From 8da1e18b0cf46b6c95c88bbad1cc50d6dd1bef4b Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 15 Nov 2012 15:42:06 +0100 Subject: [PATCH 1/5] iscsi: fix segfault in url parsing If an invalid URL is specified iscsi_get_error(iscsi) is called with iscsi == NULL. Signed-off-by: Peter Lieven Signed-off-by: Paolo Bonzini --- block/iscsi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/block/iscsi.c b/block/iscsi.c index a6a819d68f..5cd8b49a3f 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -947,8 +947,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) iscsi_url = iscsi_parse_full_url(iscsi, filename); if (iscsi_url == NULL) { - error_report("Failed to parse URL : %s %s", filename, - iscsi_get_error(iscsi)); + error_report("Failed to parse URL : %s", filename); ret = -EINVAL; goto out; } From e829b0bb054ed3389e5b22dad61875e51674e629 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sat, 17 Nov 2012 14:37:39 +0100 Subject: [PATCH 2/5] iscsi: fix deadlock during login If the connection is interrupted before the first login is successfully completed qemu-kvm is waiting forever in qemu_aio_wait(). This is fixed by performing an sync login to the target. If the connection breaks after the first successful login errors are handled internally by libiscsi. Signed-off-by: Peter Lieven Signed-off-by: Paolo Bonzini --- block/iscsi.c | 251 ++++++++++++++------------------------------------ 1 file changed, 70 insertions(+), 181 deletions(-) diff --git a/block/iscsi.c b/block/iscsi.c index 5cd8b49a3f..01340e1167 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -65,13 +65,6 @@ typedef struct IscsiAIOCB { #endif } IscsiAIOCB; -struct IscsiTask { - IscsiLun *iscsilun; - BlockDriverState *bs; - int status; - int complete; -}; - static void iscsi_bh_cb(void *p) { @@ -380,7 +373,7 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, *(uint16_t *)&acb->task->cdb[7] = htons(num_sectors); break; } - + if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task, iscsi_aio_read16_cb, NULL, @@ -665,163 +658,6 @@ iscsi_getlength(BlockDriverState *bs) return len; } -static void -iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status, - void *command_data, void *opaque) -{ - struct IscsiTask *itask = opaque; - struct scsi_readcapacity16 *rc16; - struct scsi_task *task = command_data; - - if (status != 0) { - error_report("iSCSI: Failed to read capacity of iSCSI lun. %s", - iscsi_get_error(iscsi)); - itask->status = 1; - itask->complete = 1; - scsi_free_scsi_task(task); - return; - } - - rc16 = scsi_datain_unmarshall(task); - if (rc16 == NULL) { - error_report("iSCSI: Failed to unmarshall readcapacity16 data."); - itask->status = 1; - itask->complete = 1; - scsi_free_scsi_task(task); - return; - } - - itask->iscsilun->block_size = rc16->block_length; - itask->iscsilun->num_blocks = rc16->returned_lba + 1; - itask->bs->total_sectors = itask->iscsilun->num_blocks * - itask->iscsilun->block_size / BDRV_SECTOR_SIZE ; - - itask->status = 0; - itask->complete = 1; - scsi_free_scsi_task(task); -} - -static void -iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status, - void *command_data, void *opaque) -{ - struct IscsiTask *itask = opaque; - struct scsi_readcapacity10 *rc10; - struct scsi_task *task = command_data; - - if (status != 0) { - error_report("iSCSI: Failed to read capacity of iSCSI lun. %s", - iscsi_get_error(iscsi)); - itask->status = 1; - itask->complete = 1; - scsi_free_scsi_task(task); - return; - } - - rc10 = scsi_datain_unmarshall(task); - if (rc10 == NULL) { - error_report("iSCSI: Failed to unmarshall readcapacity10 data."); - itask->status = 1; - itask->complete = 1; - scsi_free_scsi_task(task); - return; - } - - itask->iscsilun->block_size = rc10->block_size; - if (rc10->lba == 0) { - /* blank disk loaded */ - itask->iscsilun->num_blocks = 0; - } else { - itask->iscsilun->num_blocks = rc10->lba + 1; - } - itask->bs->total_sectors = itask->iscsilun->num_blocks * - itask->iscsilun->block_size / BDRV_SECTOR_SIZE ; - - itask->status = 0; - itask->complete = 1; - scsi_free_scsi_task(task); -} - -static void -iscsi_inquiry_cb(struct iscsi_context *iscsi, int status, void *command_data, - void *opaque) -{ - struct IscsiTask *itask = opaque; - struct scsi_task *task = command_data; - struct scsi_inquiry_standard *inq; - - if (status != 0) { - itask->status = 1; - itask->complete = 1; - scsi_free_scsi_task(task); - return; - } - - inq = scsi_datain_unmarshall(task); - if (inq == NULL) { - error_report("iSCSI: Failed to unmarshall inquiry data."); - itask->status = 1; - itask->complete = 1; - scsi_free_scsi_task(task); - return; - } - - itask->iscsilun->type = inq->periperal_device_type; - - scsi_free_scsi_task(task); - - switch (itask->iscsilun->type) { - case TYPE_DISK: - task = iscsi_readcapacity16_task(iscsi, itask->iscsilun->lun, - iscsi_readcapacity16_cb, opaque); - if (task == NULL) { - error_report("iSCSI: failed to send readcapacity16 command."); - itask->status = 1; - itask->complete = 1; - return; - } - break; - case TYPE_ROM: - task = iscsi_readcapacity10_task(iscsi, itask->iscsilun->lun, - 0, 0, - iscsi_readcapacity10_cb, opaque); - if (task == NULL) { - error_report("iSCSI: failed to send readcapacity16 command."); - itask->status = 1; - itask->complete = 1; - return; - } - break; - default: - itask->status = 0; - itask->complete = 1; - } -} - -static void -iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data, - void *opaque) -{ - struct IscsiTask *itask = opaque; - struct scsi_task *task; - - if (status != 0) { - itask->status = 1; - itask->complete = 1; - return; - } - - task = iscsi_inquiry_task(iscsi, itask->iscsilun->lun, - 0, 0, 36, - iscsi_inquiry_cb, opaque); - if (task == NULL) { - error_report("iSCSI: failed to send inquiry command."); - itask->status = 1; - itask->complete = 1; - return; - } -} - static int parse_chap(struct iscsi_context *iscsi, const char *target) { QemuOptsList *list; @@ -934,7 +770,10 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) IscsiLun *iscsilun = bs->opaque; struct iscsi_context *iscsi = NULL; struct iscsi_url *iscsi_url = NULL; - struct IscsiTask task; + struct scsi_task *task = NULL; + struct scsi_inquiry_standard *inq = NULL; + struct scsi_readcapacity10 *rc10 = NULL; + struct scsi_readcapacity16 *rc16 = NULL; char *initiator_name = NULL; int ret; @@ -997,33 +836,80 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) /* check if we got HEADER_DIGEST via the options */ parse_header_digest(iscsi, iscsi_url->target); - task.iscsilun = iscsilun; - task.status = 0; - task.complete = 0; - task.bs = bs; + if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { + error_report("iSCSI: Failed to connect to LUN : %s", + iscsi_get_error(iscsi)); + ret = -EINVAL; + goto out; + } iscsilun->iscsi = iscsi; iscsilun->lun = iscsi_url->lun; - if (iscsi_full_connect_async(iscsi, iscsi_url->portal, iscsi_url->lun, - iscsi_connect_cb, &task) - != 0) { - error_report("iSCSI: Failed to start async connect."); + task = iscsi_inquiry_sync(iscsi, iscsilun->lun, 0, 0, 36); + + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + error_report("iSCSI: failed to send inquiry command."); ret = -EINVAL; goto out; } - while (!task.complete) { - iscsi_set_events(iscsilun); - qemu_aio_wait(); - } - if (task.status != 0) { - error_report("iSCSI: Failed to connect to LUN : %s", - iscsi_get_error(iscsi)); + inq = scsi_datain_unmarshall(task); + if (inq == NULL) { + error_report("iSCSI: Failed to unmarshall inquiry data."); ret = -EINVAL; goto out; } + iscsilun->type = inq->periperal_device_type; + + scsi_free_scsi_task(task); + + switch (iscsilun->type) { + case TYPE_DISK: + task = iscsi_readcapacity16_sync(iscsi, iscsilun->lun); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + error_report("iSCSI: failed to send readcapacity16 command."); + ret = -EINVAL; + goto out; + } + rc16 = scsi_datain_unmarshall(task); + if (rc16 == NULL) { + error_report("iSCSI: Failed to unmarshall readcapacity16 data."); + ret = -EINVAL; + goto out; + } + iscsilun->block_size = rc16->block_length; + iscsilun->num_blocks = rc16->returned_lba + 1; + break; + case TYPE_ROM: + task = iscsi_readcapacity10_sync(iscsi, iscsilun->lun, 0, 0); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + error_report("iSCSI: failed to send readcapacity10 command."); + ret = -EINVAL; + goto out; + } + rc10 = scsi_datain_unmarshall(task); + if (rc10 == NULL) { + error_report("iSCSI: Failed to unmarshall readcapacity10 data."); + ret = -EINVAL; + goto out; + } + iscsilun->block_size = rc10->block_size; + if (rc10->lba == 0) { + /* blank disk loaded */ + iscsilun->num_blocks = 0; + } else { + iscsilun->num_blocks = rc10->lba + 1; + } + break; + default: + break; + } + + bs->total_sectors = iscsilun->num_blocks * + iscsilun->block_size / BDRV_SECTOR_SIZE ; + /* Medium changer or tape. We dont have any emulation for this so this must * be sg ioctl compatible. We force it to be sg, otherwise qemu will try * to read from the device to guess the image format. @@ -1042,6 +928,9 @@ out: if (iscsi_url != NULL) { iscsi_destroy_url(iscsi_url); } + if (task != NULL) { + scsi_free_scsi_task(task); + } if (ret) { if (iscsi != NULL) { From f807ecd5741325fe0d281199ff22cdda0acb6a7a Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sat, 17 Nov 2012 16:20:28 +0100 Subject: [PATCH 3/5] iscsi: do not assume device is zero initialized Without any complex checks we can't assume that an iscsi target is initialized to zero. Signed-off-by: Peter Lieven Signed-off-by: Paolo Bonzini --- block/iscsi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/block/iscsi.c b/block/iscsi.c index 01340e1167..c0b70b3d32 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -951,6 +951,11 @@ static void iscsi_close(BlockDriverState *bs) memset(iscsilun, 0, sizeof(IscsiLun)); } +static int iscsi_has_zero_init(BlockDriverState *bs) +{ + return 0; +} + static BlockDriver bdrv_iscsi = { .format_name = "iscsi", .protocol_name = "iscsi", @@ -966,6 +971,7 @@ static BlockDriver bdrv_iscsi = { .bdrv_aio_flush = iscsi_aio_flush, .bdrv_aio_discard = iscsi_aio_discard, + .bdrv_has_zero_init = iscsi_has_zero_init, #ifdef __linux__ .bdrv_ioctl = iscsi_ioctl, From 474ee55a18765e7de8f0b2cc00db5d26286bb24d Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 23 Nov 2012 16:08:44 +1100 Subject: [PATCH 4/5] virtio-scsi: Fix some endian bugs with virtio-scsi The virtio-scsi specification does not specify the correct endianness for fields in the request structure. It's therefore best to assume that it is "guest native" endian since that's the (stupid and poorly defined) norm in virtio. However, the qemu device for virtio-scsi has no byteswaps at all, and so will break if the guest has different endianness from the host. This patch fixes it by adding tswap() calls for the sense_len and resid fields in the request structure. In theory status_qualifier needs swaps as well, but that field is never actually touched. The tag field is a uint64_t, but since its value is completely arbitrary, it might as well be uint8_t[8] and so it does not need swapping. Cc: Paolo Bonzini Cc: Paul 'Rusty' Russell Signed-off-by: David Gibson Signed-off-by: Paolo Bonzini --- hw/virtio-scsi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index 7d546f6ca7..924fc69b73 100644 --- a/hw/virtio-scsi.c +++ b/hw/virtio-scsi.c @@ -424,15 +424,17 @@ static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, size_t resid) { VirtIOSCSIReq *req = r->hba_private; + uint32_t sense_len; req->resp.cmd->response = VIRTIO_SCSI_S_OK; req->resp.cmd->status = status; if (req->resp.cmd->status == GOOD) { - req->resp.cmd->resid = resid; + req->resp.cmd->resid = tswap32(resid); } else { req->resp.cmd->resid = 0; - req->resp.cmd->sense_len = - scsi_req_get_sense(r, req->resp.cmd->sense, VIRTIO_SCSI_SENSE_SIZE); + sense_len = scsi_req_get_sense(r, req->resp.cmd->sense, + VIRTIO_SCSI_SENSE_SIZE); + req->resp.cmd->sense_len = tswap32(sense_len); } virtio_scsi_complete_req(req); } From 863d1050c96cff91dd478767c0da9cc288575919 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 26 Nov 2012 12:33:52 +1100 Subject: [PATCH 5/5] virtio-scsi: Fix subtle (guest) endian bug The virtio-scsi config space is, by specification, in guest endian (which is ill-defined, but there you go). In virtio_scsi_get_config() we set up all the fields in there, using stl_raw(). Which is a problem for the max_channel and max_target fields, which are 16-bit, not 32-bit. For little-endian targets we get away with it by accident, since the first two bytes will still be correct, and the extra two bytes written (with zeroes) will be overwritten correctly by the next store. But for big-endian guests, this means the max_target field ends up as zero, which means the guest will only recognize a single disk on the virtio-scsi bus. This patch fixes the problem. Cc: Paolo Bonzini Cc: Paul 'Rusty' Russell Signed-off-by: David Gibson Signed-off-by: Paolo Bonzini --- hw/virtio-scsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index 924fc69b73..bfe1860505 100644 --- a/hw/virtio-scsi.c +++ b/hw/virtio-scsi.c @@ -534,8 +534,8 @@ static void virtio_scsi_get_config(VirtIODevice *vdev, stl_raw(&scsiconf->event_info_size, sizeof(VirtIOSCSIEvent)); stl_raw(&scsiconf->sense_size, s->sense_size); stl_raw(&scsiconf->cdb_size, s->cdb_size); - stl_raw(&scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL); - stl_raw(&scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET); + stw_raw(&scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL); + stw_raw(&scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET); stl_raw(&scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN); }