From 12badfc2385ae6822cbcbcc8d1c0ed71c9e0ff09 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Mon, 21 May 2012 12:03:10 +0200 Subject: [PATCH 1/6] scsi: declare vmstate_info_scsi_requests to be static Signed-off-by: Jim Meyering --- hw/scsi-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 8ab9bcda86..f10f3ec25c 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -1561,7 +1561,7 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size) return 0; } -const VMStateInfo vmstate_info_scsi_requests = { +static const VMStateInfo vmstate_info_scsi_requests = { .name = "scsi-requests", .get = get_scsi_requests, .put = put_scsi_requests, From c9b9f6824fd82058f6918b64d8fd9b1578fac353 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 22 May 2012 19:56:36 +1000 Subject: [PATCH 2/6] ISCSI: redo how we set up the events Call qemu_notify_event() after updating events. Otherwise, If we add an event for -is-writeable but the socket is already writeable there may be a delay before the event callback is actually triggered. Those delays would in particular hurt performance during BIOS boot and when the GRUB bootloader reads the kernel and initrd. But first call out to the socket write functions directly, and only set up the write event if the socket is full. This will happen very rarely and this improves performance. Signed-off-by: Ronnie Sahlberg --- block/iscsi.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/block/iscsi.c b/block/iscsi.c index d37c4ee171..db41bb7582 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -39,6 +39,7 @@ typedef struct IscsiLun { int lun; int block_size; unsigned long num_blocks; + int events; } IscsiLun; typedef struct IscsiAIOCB { @@ -104,11 +105,27 @@ static void iscsi_set_events(IscsiLun *iscsilun) { struct iscsi_context *iscsi = iscsilun->iscsi; + int ev; - qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read, - (iscsi_which_events(iscsi) & POLLOUT) - ? iscsi_process_write : NULL, - iscsi_process_flush, iscsilun); + /* We always register a read handler. */ + ev = POLLIN; + ev |= iscsi_which_events(iscsi); + if (ev != iscsilun->events) { + qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), + iscsi_process_read, + (ev & POLLOUT) ? iscsi_process_write : NULL, + iscsi_process_flush, + iscsilun); + + } + + /* If we just added an event, the callback might be delayed + * unless we call qemu_notify_event(). + */ + if (ev & ~iscsilun->events) { + qemu_notify_event(); + } + iscsilun->events = ev; } static void From c7b4a95202032bf1a9e13dd9695389c0ed246eec Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 26 May 2012 09:41:13 +0200 Subject: [PATCH 3/6] ISCSI: change num_blocks to 64-bit Signed-off-by: Paolo Bonzini --- block/iscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/iscsi.c b/block/iscsi.c index db41bb7582..9cd258ff6f 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -38,7 +38,7 @@ typedef struct IscsiLun { struct iscsi_context *iscsi; int lun; int block_size; - unsigned long num_blocks; + uint64_t num_blocks; int events; } IscsiLun; From dbfff6d776670cca751b904063c9173a23ae8c75 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Fri, 25 May 2012 21:59:01 +1000 Subject: [PATCH 4/6] ISCSI: get device type at connection time This is needed to avoid READ CAPACITY(16) for MMC devices. Signed-off-by: Ronnie Sahlberg Signed-off-by: Paolo Bonzini --- block/iscsi.c | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/block/iscsi.c b/block/iscsi.c index 9cd258ff6f..91cca83c5b 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -29,6 +29,7 @@ #include "qemu-error.h" #include "block_int.h" #include "trace.h" +#include "hw/scsi-defs.h" #include #include @@ -37,6 +38,7 @@ typedef struct IscsiLun { struct iscsi_context *iscsi; int lun; + enum scsi_inquiry_peripheral_device_type type; int block_size; uint64_t num_blocks; int events; @@ -507,6 +509,44 @@ iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status, 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); + + 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; + } +} + static void iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) @@ -520,10 +560,11 @@ iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data, return; } - task = iscsi_readcapacity16_task(iscsi, itask->iscsilun->lun, - iscsi_readcapacity16_cb, opaque); + task = iscsi_inquiry_task(iscsi, itask->iscsilun->lun, + 0, 0, 36, + iscsi_inquiry_cb, opaque); if (task == NULL) { - error_report("iSCSI: failed to send readcapacity16 command."); + error_report("iSCSI: failed to send inquiry command."); itask->status = 1; itask->complete = 1; return; From 6bcd1346bbfb051820e1c55bd4f04766f556bcdf Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sat, 26 May 2012 14:56:38 +1000 Subject: [PATCH 5/6] ISCSI: Only call READCAPACITY16 for SBC devices, use READCAPACITY10 for MMC Signed-off-by: Ronnie Sahlberg --- block/iscsi.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/block/iscsi.c b/block/iscsi.c index 91cca83c5b..472c8539cb 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -509,6 +509,42 @@ iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status, 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; + 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) @@ -537,13 +573,31 @@ iscsi_inquiry_cb(struct iscsi_context *iscsi, int status, void *command_data, scsi_free_scsi_task(task); - task = iscsi_readcapacity16_task(iscsi, itask->iscsilun->lun, + 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; + 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; - return; } } From f4dfa67f04037c1b1a8f4e4ddc944c5ab308f35b Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 22 May 2012 20:10:05 +1000 Subject: [PATCH 6/6] ISCSI: Switch to using READ16/WRITE16 for I/O to the LUN This allows using LUNs bigger than 2TB. Keep using READ10 for other device types such as MMC. Signed-off-by: Ronnie Sahlberg --- block/iscsi.c | 112 +++++++++++++++++++++++++++++++++++++------------- trace-events | 4 +- 2 files changed, 85 insertions(+), 31 deletions(-) diff --git a/block/iscsi.c b/block/iscsi.c index 472c8539cb..22888a0845 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -25,6 +25,7 @@ #include "config-host.h" #include +#include #include "qemu-common.h" #include "qemu-error.h" #include "block_int.h" @@ -180,12 +181,12 @@ iscsi_readv_writev_bh_cb(void *p) static void -iscsi_aio_write10_cb(struct iscsi_context *iscsi, int status, +iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) { IscsiAIOCB *acb = opaque; - trace_iscsi_aio_write10_cb(iscsi, status, acb, acb->canceled); + trace_iscsi_aio_write16_cb(iscsi, status, acb, acb->canceled); g_free(acb->buf); @@ -198,7 +199,7 @@ iscsi_aio_write10_cb(struct iscsi_context *iscsi, int status, acb->status = 0; if (status < 0) { - error_report("Failed to write10 data to iSCSI lun. %s", + error_report("Failed to write16 data to iSCSI lun. %s", iscsi_get_error(iscsi)); acb->status = -EIO; } @@ -223,12 +224,9 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num, struct iscsi_context *iscsi = iscsilun->iscsi; IscsiAIOCB *acb; size_t size; - int fua = 0; - - /* set FUA on writes when cache mode is write through */ - if (!(bs->open_flags & BDRV_O_CACHE_WB)) { - fua = 1; - } + uint32_t num_sectors; + uint64_t lba; + struct iscsi_data data; acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque); trace_iscsi_aio_writev(iscsi, sector_num, nb_sectors, opaque, acb); @@ -238,18 +236,44 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num, acb->canceled = 0; - /* XXX we should pass the iovec to write10 to avoid the extra copy */ + /* XXX we should pass the iovec to write16 to avoid the extra copy */ /* this will allow us to get rid of 'buf' completely */ size = nb_sectors * BDRV_SECTOR_SIZE; acb->buf = g_malloc(size); qemu_iovec_to_buffer(acb->qiov, acb->buf); - acb->task = iscsi_write10_task(iscsi, iscsilun->lun, acb->buf, size, - sector_qemu2lun(sector_num, iscsilun), - fua, 0, iscsilun->block_size, - iscsi_aio_write10_cb, acb); + + + acb->task = malloc(sizeof(struct scsi_task)); if (acb->task == NULL) { - error_report("iSCSI: Failed to send write10 command. %s", - iscsi_get_error(iscsi)); + error_report("iSCSI: Failed to allocate task for scsi WRITE16 " + "command. %s", iscsi_get_error(iscsi)); + qemu_aio_release(acb); + return NULL; + } + memset(acb->task, 0, sizeof(struct scsi_task)); + + acb->task->xfer_dir = SCSI_XFER_WRITE; + acb->task->cdb_size = 16; + acb->task->cdb[0] = 0x8a; + if (!(bs->open_flags & BDRV_O_CACHE_WB)) { + /* set FUA on writes when cache mode is write through */ + acb->task->cdb[1] |= 0x04; + } + lba = sector_qemu2lun(sector_num, iscsilun); + *(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32); + *(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff); + num_sectors = size / iscsilun->block_size; + *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors); + acb->task->expxferlen = size; + + data.data = acb->buf; + data.size = size; + + if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task, + iscsi_aio_write16_cb, + &data, + acb) != 0) { + scsi_free_scsi_task(acb->task); g_free(acb->buf); qemu_aio_release(acb); return NULL; @@ -261,12 +285,12 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num, } static void -iscsi_aio_read10_cb(struct iscsi_context *iscsi, int status, +iscsi_aio_read16_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) { IscsiAIOCB *acb = opaque; - trace_iscsi_aio_read10_cb(iscsi, status, acb, acb->canceled); + trace_iscsi_aio_read16_cb(iscsi, status, acb, acb->canceled); if (acb->canceled != 0) { qemu_aio_release(acb); @@ -277,7 +301,7 @@ iscsi_aio_read10_cb(struct iscsi_context *iscsi, int status, acb->status = 0; if (status != 0) { - error_report("Failed to read10 data from iSCSI lun. %s", + error_report("Failed to read16 data from iSCSI lun. %s", iscsi_get_error(iscsi)); acb->status = -EIO; } @@ -296,8 +320,10 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, IscsiLun *iscsilun = bs->opaque; struct iscsi_context *iscsi = iscsilun->iscsi; IscsiAIOCB *acb; - size_t qemu_read_size, lun_read_size; + size_t qemu_read_size; int i; + uint64_t lba; + uint32_t num_sectors; qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors; @@ -322,16 +348,44 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, acb->read_offset = bdrv_offset % iscsilun->block_size; } - lun_read_size = (qemu_read_size + iscsilun->block_size - + acb->read_offset - 1) - / iscsilun->block_size * iscsilun->block_size; - acb->task = iscsi_read10_task(iscsi, iscsilun->lun, - sector_qemu2lun(sector_num, iscsilun), - lun_read_size, iscsilun->block_size, - iscsi_aio_read10_cb, acb); + num_sectors = (qemu_read_size + iscsilun->block_size + + acb->read_offset - 1) + / iscsilun->block_size; + + acb->task = malloc(sizeof(struct scsi_task)); if (acb->task == NULL) { - error_report("iSCSI: Failed to send read10 command. %s", - iscsi_get_error(iscsi)); + error_report("iSCSI: Failed to allocate task for scsi READ16 " + "command. %s", iscsi_get_error(iscsi)); + qemu_aio_release(acb); + return NULL; + } + memset(acb->task, 0, sizeof(struct scsi_task)); + + acb->task->xfer_dir = SCSI_XFER_READ; + lba = sector_qemu2lun(sector_num, iscsilun); + acb->task->expxferlen = qemu_read_size; + + switch (iscsilun->type) { + case TYPE_DISK: + acb->task->cdb_size = 16; + acb->task->cdb[0] = 0x88; + *(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32); + *(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff); + *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors); + break; + default: + acb->task->cdb_size = 10; + acb->task->cdb[0] = 0x28; + *(uint32_t *)&acb->task->cdb[2] = htonl(lba); + *(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, + acb) != 0) { + scsi_free_scsi_task(acb->task); qemu_aio_release(acb); return NULL; } diff --git a/trace-events b/trace-events index 87cb96cabf..45c6bc1271 100644 --- a/trace-events +++ b/trace-events @@ -602,9 +602,9 @@ escc_kbd_command(int val) "Command %d" escc_sunmouse_event(int dx, int dy, int buttons_state) "dx=%d dy=%d buttons=%01x" # block/iscsi.c -iscsi_aio_write10_cb(void *iscsi, int status, void *acb, int canceled) "iscsi %p status %d acb %p canceled %d" +iscsi_aio_write16_cb(void *iscsi, int status, void *acb, int canceled) "iscsi %p status %d acb %p canceled %d" iscsi_aio_writev(void *iscsi, int64_t sector_num, int nb_sectors, void *opaque, void *acb) "iscsi %p sector_num %"PRId64" nb_sectors %d opaque %p acb %p" -iscsi_aio_read10_cb(void *iscsi, int status, void *acb, int canceled) "iscsi %p status %d acb %p canceled %d" +iscsi_aio_read16_cb(void *iscsi, int status, void *acb, int canceled) "iscsi %p status %d acb %p canceled %d" iscsi_aio_readv(void *iscsi, int64_t sector_num, int nb_sectors, void *opaque, void *acb) "iscsi %p sector_num %"PRId64" nb_sectors %d opaque %p acb %p" # hw/esp.c