Merge remote-tracking branch 'bonzini/scsi-next' into staging
# By Peter Lieven (3) and others # Via Paolo Bonzini * bonzini/scsi-next: spapr-vscsi: Report error on unsupported MAD requests spapr-vscsi: Adding VSCSI capabilities iscsi: split discard requests in multiple parts iscsi: add .bdrv_get_block_status iscsi: add logical block provisioning information to iscsilun hw/scsi/lsi53c895a: Use deposit32 rather than handcoded shift/mask hw/scsi/lsi53c895a: Use sextract32 for sign-extension scsi: Fix scsi_bus_legacy_add_drive() scsi-generic with serial virtio-scsi: Make type virtio-scsi-common abstract spapr-vscsi: add task management scsi: prefer UUID to VM name for the initiator name Message-id: 1378984634-765-1-git-send-email-pbonzini@redhat.com
This commit is contained in:
commit
7d41364e71
392
block/iscsi.c
392
block/iscsi.c
@ -33,6 +33,8 @@
|
||||
#include "trace.h"
|
||||
#include "block/scsi.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qmp-commands.h"
|
||||
|
||||
#include <iscsi/iscsi.h>
|
||||
#include <iscsi/scsi-lowlevel.h>
|
||||
@ -50,8 +52,21 @@ typedef struct IscsiLun {
|
||||
uint64_t num_blocks;
|
||||
int events;
|
||||
QEMUTimer *nop_timer;
|
||||
uint8_t lbpme;
|
||||
uint8_t lbprz;
|
||||
struct scsi_inquiry_logical_block_provisioning lbp;
|
||||
struct scsi_inquiry_block_limits bl;
|
||||
} IscsiLun;
|
||||
|
||||
typedef struct IscsiTask {
|
||||
int status;
|
||||
int complete;
|
||||
int retries;
|
||||
int do_retry;
|
||||
struct scsi_task *task;
|
||||
Coroutine *co;
|
||||
} IscsiTask;
|
||||
|
||||
typedef struct IscsiAIOCB {
|
||||
BlockDriverAIOCB common;
|
||||
QEMUIOVector *qiov;
|
||||
@ -72,6 +87,7 @@ typedef struct IscsiAIOCB {
|
||||
#define NOP_INTERVAL 5000
|
||||
#define MAX_NOP_FAILURES 3
|
||||
#define ISCSI_CMD_RETRIES 5
|
||||
#define ISCSI_MAX_UNMAP 131072
|
||||
|
||||
static void
|
||||
iscsi_bh_cb(void *p)
|
||||
@ -105,6 +121,41 @@ iscsi_schedule_bh(IscsiAIOCB *acb)
|
||||
qemu_bh_schedule(acb->bh);
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
{
|
||||
struct IscsiTask *iTask = opaque;
|
||||
struct scsi_task *task = command_data;
|
||||
|
||||
iTask->complete = 1;
|
||||
iTask->status = status;
|
||||
iTask->do_retry = 0;
|
||||
iTask->task = task;
|
||||
|
||||
if (iTask->retries-- > 0 && status == SCSI_STATUS_CHECK_CONDITION
|
||||
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION) {
|
||||
iTask->do_retry = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status != SCSI_STATUS_GOOD) {
|
||||
error_report("iSCSI: Failure. %s", iscsi_get_error(iscsi));
|
||||
}
|
||||
|
||||
out:
|
||||
if (iTask->co) {
|
||||
qemu_coroutine_enter(iTask->co, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask)
|
||||
{
|
||||
*iTask = (struct IscsiTask) {
|
||||
.co = qemu_coroutine_self(),
|
||||
.retries = ISCSI_CMD_RETRIES,
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
||||
@ -568,88 +619,6 @@ iscsi_aio_flush(BlockDriverState *bs,
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static int iscsi_aio_discard_acb(IscsiAIOCB *acb);
|
||||
|
||||
static void
|
||||
iscsi_unmap_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
{
|
||||
IscsiAIOCB *acb = opaque;
|
||||
|
||||
if (acb->canceled != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
acb->status = 0;
|
||||
if (status != 0) {
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION
|
||||
&& acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
|
||||
&& acb->retries-- > 0) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
if (iscsi_aio_discard_acb(acb) == 0) {
|
||||
iscsi_set_events(acb->iscsilun);
|
||||
return;
|
||||
}
|
||||
}
|
||||
error_report("Failed to unmap data on iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
}
|
||||
|
||||
iscsi_schedule_bh(acb);
|
||||
}
|
||||
|
||||
static int iscsi_aio_discard_acb(IscsiAIOCB *acb) {
|
||||
struct iscsi_context *iscsi = acb->iscsilun->iscsi;
|
||||
struct unmap_list list[1];
|
||||
|
||||
acb->canceled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
acb->buf = NULL;
|
||||
|
||||
list[0].lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
|
||||
list[0].num = acb->nb_sectors * BDRV_SECTOR_SIZE / acb->iscsilun->block_size;
|
||||
|
||||
acb->task = iscsi_unmap_task(iscsi, acb->iscsilun->lun,
|
||||
0, 0, &list[0], 1,
|
||||
iscsi_unmap_cb,
|
||||
acb);
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to send unmap command. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
IscsiAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->sector_num = sector_num;
|
||||
acb->retries = ISCSI_CMD_RETRIES;
|
||||
|
||||
if (iscsi_aio_discard_acb(acb) != 0) {
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iscsi_set_events(iscsilun);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static void
|
||||
iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
|
||||
@ -842,6 +811,167 @@ iscsi_getlength(BlockDriverState *bs)
|
||||
return len;
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct scsi_get_lba_status *lbas = NULL;
|
||||
struct scsi_lba_status_descriptor *lbasd = NULL;
|
||||
struct IscsiTask iTask;
|
||||
int64_t ret;
|
||||
|
||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||
|
||||
if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* default to all sectors allocated */
|
||||
ret = BDRV_BLOCK_DATA;
|
||||
ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID;
|
||||
*pnum = nb_sectors;
|
||||
|
||||
/* LUN does not support logical block provisioning */
|
||||
if (iscsilun->lbpme == 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
retry:
|
||||
if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun,
|
||||
sector_qemu2lun(sector_num, iscsilun),
|
||||
8 + 16, iscsi_co_generic_cb,
|
||||
&iTask) == NULL) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (!iTask.complete) {
|
||||
iscsi_set_events(iscsilun);
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
if (iTask.do_retry) {
|
||||
if (iTask.task != NULL) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
iTask.task = NULL;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
/* in case the get_lba_status_callout fails (i.e.
|
||||
* because the device is busy or the cmd is not
|
||||
* supported) we pretend all blocks are allocated
|
||||
* for backwards compatiblity */
|
||||
goto out;
|
||||
}
|
||||
|
||||
lbas = scsi_datain_unmarshall(iTask.task);
|
||||
if (lbas == NULL) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
lbasd = &lbas->descriptors[0];
|
||||
|
||||
if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun);
|
||||
if (*pnum > nb_sectors) {
|
||||
*pnum = nb_sectors;
|
||||
}
|
||||
|
||||
if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
|
||||
lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
|
||||
ret &= ~BDRV_BLOCK_DATA;
|
||||
if (iscsilun->lbprz) {
|
||||
ret |= BDRV_BLOCK_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (iTask.task != NULL) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct IscsiTask iTask;
|
||||
struct unmap_list list;
|
||||
uint32_t nb_blocks;
|
||||
uint32_t max_unmap;
|
||||
|
||||
if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!iscsilun->lbp.lbpu) {
|
||||
/* UNMAP is not supported by the target */
|
||||
return 0;
|
||||
}
|
||||
|
||||
list.lba = sector_qemu2lun(sector_num, iscsilun);
|
||||
nb_blocks = sector_qemu2lun(nb_sectors, iscsilun);
|
||||
|
||||
max_unmap = iscsilun->bl.max_unmap;
|
||||
if (max_unmap == 0xffffffff) {
|
||||
max_unmap = ISCSI_MAX_UNMAP;
|
||||
}
|
||||
|
||||
while (nb_blocks > 0) {
|
||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||
list.num = nb_blocks;
|
||||
if (list.num > max_unmap) {
|
||||
list.num = max_unmap;
|
||||
}
|
||||
retry:
|
||||
if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1,
|
||||
iscsi_co_generic_cb, &iTask) == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
while (!iTask.complete) {
|
||||
iscsi_set_events(iscsilun);
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
if (iTask.task != NULL) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
iTask.task = NULL;
|
||||
}
|
||||
|
||||
if (iTask.do_retry) {
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (iTask.status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
/* the target might fail with a check condition if it
|
||||
is not happy with the alignment of the UNMAP request
|
||||
we silently fail in this case */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
list.lba += list.num;
|
||||
nb_blocks -= list.num;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_chap(struct iscsi_context *iscsi, const char *target)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
@ -922,8 +1052,9 @@ static char *parse_initiator_name(const char *target)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
const char *name = NULL;
|
||||
const char *iscsi_name = qemu_get_vm_name();
|
||||
const char *name;
|
||||
char *iscsi_name;
|
||||
UuidInfo *uuid_info;
|
||||
|
||||
list = qemu_find_opts("iscsi");
|
||||
if (list) {
|
||||
@ -933,16 +1064,22 @@ static char *parse_initiator_name(const char *target)
|
||||
}
|
||||
if (opts) {
|
||||
name = qemu_opt_get(opts, "initiator-name");
|
||||
if (name) {
|
||||
return g_strdup(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name) {
|
||||
return g_strdup(name);
|
||||
uuid_info = qmp_query_uuid(NULL);
|
||||
if (strcmp(uuid_info->UUID, UUID_NONE) == 0) {
|
||||
name = qemu_get_vm_name();
|
||||
} else {
|
||||
return g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s",
|
||||
iscsi_name ? ":" : "",
|
||||
iscsi_name ? iscsi_name : "");
|
||||
name = uuid_info->UUID;
|
||||
}
|
||||
iscsi_name = g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s",
|
||||
name ? ":" : "", name ? name : "");
|
||||
qapi_free_UuidInfo(uuid_info);
|
||||
return iscsi_name;
|
||||
}
|
||||
|
||||
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
|
||||
@ -990,6 +1127,8 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
|
||||
} else {
|
||||
iscsilun->block_size = rc16->block_length;
|
||||
iscsilun->num_blocks = rc16->returned_lba + 1;
|
||||
iscsilun->lbpme = rc16->lbpme;
|
||||
iscsilun->lbprz = rc16->lbprz;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1042,6 +1181,37 @@ static QemuOptsList runtime_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi,
|
||||
int lun, int evpd, int pc) {
|
||||
int full_size;
|
||||
struct scsi_task *task = NULL;
|
||||
task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
goto fail;
|
||||
}
|
||||
full_size = scsi_datain_getfullsize(task);
|
||||
if (full_size > task->datain.size) {
|
||||
scsi_free_scsi_task(task);
|
||||
|
||||
/* we need more data for the full list */
|
||||
task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return task;
|
||||
|
||||
fail:
|
||||
error_report("iSCSI: Inquiry command failed : %s",
|
||||
iscsi_get_error(iscsi));
|
||||
if (task) {
|
||||
scsi_free_scsi_task(task);
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We support iscsi url's on the form
|
||||
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
|
||||
@ -1171,6 +1341,46 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
bs->sg = 1;
|
||||
}
|
||||
|
||||
if (iscsilun->lbpme) {
|
||||
struct scsi_inquiry_logical_block_provisioning *inq_lbp;
|
||||
task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
||||
SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING);
|
||||
if (task == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
inq_lbp = scsi_datain_unmarshall(task);
|
||||
if (inq_lbp == NULL) {
|
||||
error_report("iSCSI: failed to unmarshall inquiry datain blob");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
memcpy(&iscsilun->lbp, inq_lbp,
|
||||
sizeof(struct scsi_inquiry_logical_block_provisioning));
|
||||
scsi_free_scsi_task(task);
|
||||
task = NULL;
|
||||
}
|
||||
|
||||
if (iscsilun->lbp.lbpu || iscsilun->lbp.lbpws) {
|
||||
struct scsi_inquiry_block_limits *inq_bl;
|
||||
task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
||||
SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS);
|
||||
if (task == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
inq_bl = scsi_datain_unmarshall(task);
|
||||
if (inq_bl == NULL) {
|
||||
error_report("iSCSI: failed to unmarshall inquiry datain blob");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
memcpy(&iscsilun->bl, inq_bl,
|
||||
sizeof(struct scsi_inquiry_block_limits));
|
||||
scsi_free_scsi_task(task);
|
||||
task = NULL;
|
||||
}
|
||||
|
||||
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
|
||||
/* Set up a timer for sending out iSCSI NOPs */
|
||||
iscsilun->nop_timer = timer_new_ms(QEMU_CLOCK_REALTIME, iscsi_nop_timed_event, iscsilun);
|
||||
@ -1312,11 +1522,13 @@ static BlockDriver bdrv_iscsi = {
|
||||
.bdrv_getlength = iscsi_getlength,
|
||||
.bdrv_truncate = iscsi_truncate,
|
||||
|
||||
.bdrv_co_get_block_status = iscsi_co_get_block_status,
|
||||
.bdrv_co_discard = iscsi_co_discard,
|
||||
|
||||
.bdrv_aio_readv = iscsi_aio_readv,
|
||||
.bdrv_aio_writev = iscsi_aio_writev,
|
||||
.bdrv_aio_flush = iscsi_aio_flush,
|
||||
|
||||
.bdrv_aio_discard = iscsi_aio_discard,
|
||||
.bdrv_has_zero_init = iscsi_has_zero_init,
|
||||
|
||||
#ifdef __linux__
|
||||
|
@ -998,12 +998,6 @@ bad:
|
||||
s->msg_action = 0;
|
||||
}
|
||||
|
||||
/* Sign extend a 24-bit value. */
|
||||
static inline int32_t sxt24(int32_t n)
|
||||
{
|
||||
return (n << 8) >> 8;
|
||||
}
|
||||
|
||||
#define LSI_BUF_SIZE 4096
|
||||
static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count)
|
||||
{
|
||||
@ -1083,7 +1077,7 @@ again:
|
||||
/* Table indirect addressing. */
|
||||
|
||||
/* 32-bit Table indirect */
|
||||
offset = sxt24(addr);
|
||||
offset = sextract32(addr, 0, 24);
|
||||
pci_dma_read(pci_dev, s->dsa + offset, buf, 8);
|
||||
/* byte count is stored in bits 0:23 only */
|
||||
s->dbc = cpu_to_le32(buf[0]) & 0xffffff;
|
||||
@ -1183,13 +1177,13 @@ again:
|
||||
uint32_t id;
|
||||
|
||||
if (insn & (1 << 25)) {
|
||||
id = read_dword(s, s->dsa + sxt24(insn));
|
||||
id = read_dword(s, s->dsa + sextract32(insn, 0, 24));
|
||||
} else {
|
||||
id = insn;
|
||||
}
|
||||
id = (id >> 16) & 0xf;
|
||||
if (insn & (1 << 26)) {
|
||||
addr = s->dsp + sxt24(addr);
|
||||
addr = s->dsp + sextract32(addr, 0, 24);
|
||||
}
|
||||
s->dnad = addr;
|
||||
switch (opcode) {
|
||||
@ -1385,7 +1379,7 @@ again:
|
||||
if (cond == jmp) {
|
||||
if (insn & (1 << 23)) {
|
||||
/* Relative address. */
|
||||
addr = s->dsp + sxt24(addr);
|
||||
addr = s->dsp + sextract32(addr, 0, 24);
|
||||
}
|
||||
switch ((insn >> 27) & 7) {
|
||||
case 0: /* Jump */
|
||||
@ -1438,7 +1432,7 @@ again:
|
||||
int i;
|
||||
|
||||
if (insn & (1 << 28)) {
|
||||
addr = s->dsa + sxt24(addr);
|
||||
addr = s->dsa + sextract32(addr, 0, 24);
|
||||
}
|
||||
n = (insn & 7);
|
||||
reg = (insn >> 16) & 0xff;
|
||||
@ -1876,8 +1870,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
|
||||
int shift;
|
||||
n = (offset - 0x58) >> 2;
|
||||
shift = (offset & 3) * 8;
|
||||
s->scratch[n] &= ~(0xff << shift);
|
||||
s->scratch[n] |= (val & 0xff) << shift;
|
||||
s->scratch[n] = deposit32(s->scratch[n], shift, 8, val);
|
||||
} else {
|
||||
BADF("Unhandled writeb 0x%x = 0x%x\n", offset, val);
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
|
||||
if (object_property_find(OBJECT(dev), "removable", NULL)) {
|
||||
qdev_prop_set_bit(dev, "removable", removable);
|
||||
}
|
||||
if (serial) {
|
||||
if (serial && object_property_find(OBJECT(dev), "serial", NULL)) {
|
||||
qdev_prop_set_string(dev, "serial", serial);
|
||||
}
|
||||
if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) {
|
||||
|
@ -117,6 +117,20 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag)
|
||||
{
|
||||
vscsi_req *req;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
|
||||
req = &s->reqs[i];
|
||||
if (req->iu.srp.cmd.tag == srp_tag) {
|
||||
return req;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void vscsi_put_req(vscsi_req *req)
|
||||
{
|
||||
if (req->sreq != NULL) {
|
||||
@ -755,40 +769,91 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
|
||||
static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
|
||||
{
|
||||
union viosrp_iu *iu = &req->iu;
|
||||
int fn;
|
||||
vscsi_req *tmpreq;
|
||||
int i, lun = 0, resp = SRP_TSK_MGMT_COMPLETE;
|
||||
SCSIDevice *d;
|
||||
uint64_t tag = iu->srp.rsp.tag;
|
||||
uint8_t sol_not = iu->srp.cmd.sol_not;
|
||||
|
||||
fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n",
|
||||
iu->srp.tsk_mgmt.tsk_mgmt_func);
|
||||
|
||||
switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
|
||||
#if 0 /* We really don't deal with these for now */
|
||||
case SRP_TSK_ABORT_TASK:
|
||||
fn = ABORT_TASK;
|
||||
break;
|
||||
case SRP_TSK_ABORT_TASK_SET:
|
||||
fn = ABORT_TASK_SET;
|
||||
break;
|
||||
case SRP_TSK_CLEAR_TASK_SET:
|
||||
fn = CLEAR_TASK_SET;
|
||||
break;
|
||||
case SRP_TSK_LUN_RESET:
|
||||
fn = LOGICAL_UNIT_RESET;
|
||||
break;
|
||||
case SRP_TSK_CLEAR_ACA:
|
||||
fn = CLEAR_ACA;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
fn = 0;
|
||||
}
|
||||
if (fn) {
|
||||
/* XXX Send/Handle target task management */
|
||||
;
|
||||
d = vscsi_device_find(&s->bus, be64_to_cpu(req->iu.srp.tsk_mgmt.lun), &lun);
|
||||
if (!d) {
|
||||
resp = SRP_TSK_MGMT_FIELDS_INVALID;
|
||||
} else {
|
||||
vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x20, 0);
|
||||
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
|
||||
switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
|
||||
case SRP_TSK_ABORT_TASK:
|
||||
if (d->lun != lun) {
|
||||
resp = SRP_TSK_MGMT_FIELDS_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag);
|
||||
if (tmpreq && tmpreq->sreq) {
|
||||
assert(tmpreq->sreq->hba_private);
|
||||
scsi_req_cancel(tmpreq->sreq);
|
||||
}
|
||||
break;
|
||||
|
||||
case SRP_TSK_LUN_RESET:
|
||||
if (d->lun != lun) {
|
||||
resp = SRP_TSK_MGMT_FIELDS_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
qdev_reset_all(&d->qdev);
|
||||
break;
|
||||
|
||||
case SRP_TSK_ABORT_TASK_SET:
|
||||
case SRP_TSK_CLEAR_TASK_SET:
|
||||
if (d->lun != lun) {
|
||||
resp = SRP_TSK_MGMT_FIELDS_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
|
||||
tmpreq = &s->reqs[i];
|
||||
if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) {
|
||||
continue;
|
||||
}
|
||||
if (!tmpreq->active || !tmpreq->sreq) {
|
||||
continue;
|
||||
}
|
||||
assert(tmpreq->sreq->hba_private);
|
||||
scsi_req_cancel(tmpreq->sreq);
|
||||
}
|
||||
break;
|
||||
|
||||
case SRP_TSK_CLEAR_ACA:
|
||||
resp = SRP_TSK_MGMT_NOT_SUPPORTED;
|
||||
break;
|
||||
|
||||
default:
|
||||
resp = SRP_TSK_MGMT_FIELDS_INVALID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return !fn;
|
||||
|
||||
/* Compose the response here as */
|
||||
memset(iu, 0, sizeof(struct srp_rsp) + 4);
|
||||
iu->srp.rsp.opcode = SRP_RSP;
|
||||
iu->srp.rsp.req_lim_delta = cpu_to_be32(1);
|
||||
iu->srp.rsp.tag = tag;
|
||||
iu->srp.rsp.flags |= SRP_RSP_FLAG_RSPVALID;
|
||||
iu->srp.rsp.resp_data_len = cpu_to_be32(4);
|
||||
if (resp) {
|
||||
iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2;
|
||||
} else {
|
||||
iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1;
|
||||
}
|
||||
|
||||
iu->srp.rsp.status = GOOD;
|
||||
iu->srp.rsp.data[3] = resp;
|
||||
|
||||
vscsi_send_iu(s, req, sizeof(iu->srp.rsp) + 4, VIOSRP_SRP_FORMAT);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req)
|
||||
@ -858,29 +923,97 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req)
|
||||
return vscsi_send_iu(s, req, sizeof(*sinfo), VIOSRP_MAD_FORMAT);
|
||||
}
|
||||
|
||||
static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req)
|
||||
{
|
||||
struct viosrp_capabilities *vcap;
|
||||
struct capabilities cap = { };
|
||||
uint16_t len, req_len;
|
||||
uint64_t buffer;
|
||||
int rc;
|
||||
|
||||
vcap = &req->iu.mad.capabilities;
|
||||
req_len = len = be16_to_cpu(vcap->common.length);
|
||||
buffer = be64_to_cpu(vcap->buffer);
|
||||
if (len > sizeof(cap)) {
|
||||
fprintf(stderr, "vscsi_send_capabilities: capabilities size mismatch !\n");
|
||||
|
||||
/*
|
||||
* Just read and populate the structure that is known.
|
||||
* Zero rest of the structure.
|
||||
*/
|
||||
len = sizeof(cap);
|
||||
}
|
||||
rc = spapr_vio_dma_read(&s->vdev, buffer, &cap, len);
|
||||
if (rc) {
|
||||
fprintf(stderr, "vscsi_send_capabilities: DMA read failure !\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Current implementation does not suppport any migration or
|
||||
* reservation capabilities. Construct the response telling the
|
||||
* guest not to use them.
|
||||
*/
|
||||
cap.flags = 0;
|
||||
cap.migration.ecl = 0;
|
||||
cap.reserve.type = 0;
|
||||
cap.migration.common.server_support = 0;
|
||||
cap.reserve.common.server_support = 0;
|
||||
|
||||
rc = spapr_vio_dma_write(&s->vdev, buffer, &cap, len);
|
||||
if (rc) {
|
||||
fprintf(stderr, "vscsi_send_capabilities: DMA write failure !\n");
|
||||
}
|
||||
if (req_len > len) {
|
||||
/*
|
||||
* Being paranoid and lets not worry about the error code
|
||||
* here. Actual write of the cap is done above.
|
||||
*/
|
||||
spapr_vio_dma_set(&s->vdev, (buffer + len), 0, (req_len - len));
|
||||
}
|
||||
vcap->common.status = rc ? cpu_to_be32(1) : 0;
|
||||
return vscsi_send_iu(s, req, sizeof(*vcap), VIOSRP_MAD_FORMAT);
|
||||
}
|
||||
|
||||
static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req)
|
||||
{
|
||||
union mad_iu *mad = &req->iu.mad;
|
||||
bool request_handled = false;
|
||||
uint64_t retlen = 0;
|
||||
|
||||
switch (be32_to_cpu(mad->empty_iu.common.type)) {
|
||||
case VIOSRP_EMPTY_IU_TYPE:
|
||||
fprintf(stderr, "Unsupported EMPTY MAD IU\n");
|
||||
retlen = sizeof(mad->empty_iu);
|
||||
break;
|
||||
case VIOSRP_ERROR_LOG_TYPE:
|
||||
fprintf(stderr, "Unsupported ERROR LOG MAD IU\n");
|
||||
mad->error_log.common.status = cpu_to_be16(1);
|
||||
vscsi_send_iu(s, req, sizeof(mad->error_log), VIOSRP_MAD_FORMAT);
|
||||
retlen = sizeof(mad->error_log);
|
||||
break;
|
||||
case VIOSRP_ADAPTER_INFO_TYPE:
|
||||
vscsi_send_adapter_info(s, req);
|
||||
request_handled = true;
|
||||
break;
|
||||
case VIOSRP_HOST_CONFIG_TYPE:
|
||||
mad->host_config.common.status = cpu_to_be16(1);
|
||||
vscsi_send_iu(s, req, sizeof(mad->host_config), VIOSRP_MAD_FORMAT);
|
||||
retlen = sizeof(mad->host_config);
|
||||
break;
|
||||
case VIOSRP_CAPABILITIES_TYPE:
|
||||
vscsi_send_capabilities(s, req);
|
||||
request_handled = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "VSCSI: Unknown MAD type %02x\n",
|
||||
be32_to_cpu(mad->empty_iu.common.type));
|
||||
/*
|
||||
* PAPR+ says that "The length field is set to the length
|
||||
* of the data structure(s) used in the command".
|
||||
* As we did not recognize the request type, put zero there.
|
||||
*/
|
||||
retlen = 0;
|
||||
}
|
||||
|
||||
if (!request_handled) {
|
||||
mad->empty_iu.common.status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED);
|
||||
vscsi_send_iu(s, req, retlen, VIOSRP_MAD_FORMAT);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -90,6 +90,13 @@ enum {
|
||||
SRP_REV16A_IB_IO_CLASS = 0x0100
|
||||
};
|
||||
|
||||
enum {
|
||||
SRP_TSK_MGMT_COMPLETE = 0x00,
|
||||
SRP_TSK_MGMT_FIELDS_INVALID = 0x02,
|
||||
SRP_TSK_MGMT_NOT_SUPPORTED = 0x04,
|
||||
SRP_TSK_MGMT_FAILED = 0x05
|
||||
};
|
||||
|
||||
struct srp_direct_buf {
|
||||
uint64_t va;
|
||||
uint32_t key;
|
||||
|
@ -693,6 +693,7 @@ static const TypeInfo virtio_scsi_common_info = {
|
||||
.name = TYPE_VIRTIO_SCSI_COMMON,
|
||||
.parent = TYPE_VIRTIO_DEVICE,
|
||||
.instance_size = sizeof(VirtIOSCSICommon),
|
||||
.abstract = true,
|
||||
.class_init = virtio_scsi_common_class_init,
|
||||
};
|
||||
|
||||
|
@ -17,7 +17,9 @@ extern const char *bios_name;
|
||||
extern const char *qemu_name;
|
||||
extern uint8_t qemu_uuid[];
|
||||
int qemu_uuid_parse(const char *str, uint8_t *uuid);
|
||||
|
||||
#define UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
|
||||
#define UUID_NONE "00000000-0000-0000-0000-000000000000"
|
||||
|
||||
bool runstate_check(RunState state);
|
||||
void runstate_set(RunState new_state);
|
||||
|
@ -22,6 +22,7 @@ stub-obj-y += reset.o
|
||||
stub-obj-y += set-fd-handler.o
|
||||
stub-obj-y += slirp.o
|
||||
stub-obj-y += sysbus.o
|
||||
stub-obj-y += uuid.o
|
||||
stub-obj-y += vm-stop.o
|
||||
stub-obj-y += vmstate.o
|
||||
stub-obj-$(CONFIG_WIN32) += fd-register.o
|
||||
|
12
stubs/uuid.c
Normal file
12
stubs/uuid.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qmp-commands.h"
|
||||
|
||||
UuidInfo *qmp_query_uuid(Error **errp)
|
||||
{
|
||||
UuidInfo *info = g_malloc0(sizeof(*info));
|
||||
|
||||
info->UUID = g_strdup(UUID_NONE);
|
||||
return info;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user