diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2013-07-18 08:13:50 -0500 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2013-07-18 08:13:50 -0500 |
commit | 5ab4dba4dac1932f8b14561772d3473d02ccd259 (patch) | |
tree | 88e5247c43943c3196b6632bce7268e273d1b841 | |
parent | e9acb8cea9b11679cab8cb3c20c4348803bbb58a (diff) | |
parent | 0777b5dde42f4f453abc8bb2c4e145fb8749415d (diff) | |
download | hqemu-5ab4dba4dac1932f8b14561772d3473d02ccd259.zip hqemu-5ab4dba4dac1932f8b14561772d3473d02ccd259.tar.gz |
Merge remote-tracking branch 'bonzini/scsi-next' into staging
# By Peter Lieven (4) and Ronnie Sahlberg (1)
# Via Paolo Bonzini
* bonzini/scsi-next:
iscsi: factor out sector conversions
iscsi: assert that sectors are aligned to LUN blocksize
iscsi: remove support for misaligned nb_sectors in aio_readv
iscsi: fix -ENOSPC in iscsi_create()
Fix iSCSI crash on SG_IO with an iovector
Message-id: 1374073524-8469-1-git-send-email-pbonzini@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
-rw-r--r-- | block/iscsi.c | 104 |
1 files changed, 76 insertions, 28 deletions
diff --git a/block/iscsi.c b/block/iscsi.c index 0bbf0b1..5f28c6a 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -32,6 +32,7 @@ #include "block/block_int.h" #include "trace.h" #include "block/scsi.h" +#include "qemu/iov.h" #include <iscsi/iscsi.h> #include <iscsi/scsi-lowlevel.h> @@ -61,8 +62,6 @@ typedef struct IscsiAIOCB { int status; int canceled; int retries; - size_t read_size; - size_t read_offset; int64_t sector_num; int nb_sectors; #ifdef __linux__ @@ -233,11 +232,28 @@ iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status, iscsi_schedule_bh(acb); } +static int64_t sector_lun2qemu(int64_t sector, IscsiLun *iscsilun) +{ + return sector * iscsilun->block_size / BDRV_SECTOR_SIZE; +} + static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun) { return sector * BDRV_SECTOR_SIZE / iscsilun->block_size; } +static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors, + IscsiLun *iscsilun) +{ + if ((sector_num * BDRV_SECTOR_SIZE) % iscsilun->block_size || + (nb_sectors * BDRV_SECTOR_SIZE) % iscsilun->block_size) { + error_report("iSCSI misaligned request: iscsilun->block_size %u, sector_num %ld, nb_sectors %d", + iscsilun->block_size, sector_num, nb_sectors); + return 0; + } + return 1; +} + static int iscsi_aio_writev_acb(IscsiAIOCB *acb) { @@ -285,7 +301,7 @@ iscsi_aio_writev_acb(IscsiAIOCB *acb) lba = sector_qemu2lun(acb->sector_num, acb->iscsilun); *(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32); *(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff); - num_sectors = size / acb->iscsilun->block_size; + num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun); *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors); acb->task->expxferlen = size; @@ -322,6 +338,10 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num, IscsiLun *iscsilun = bs->opaque; IscsiAIOCB *acb; + if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { + return NULL; + } + acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); trace_iscsi_aio_writev(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb); @@ -379,6 +399,7 @@ static int iscsi_aio_readv_acb(IscsiAIOCB *acb) { struct iscsi_context *iscsi = acb->iscsilun->iscsi; + size_t size; uint64_t lba; uint32_t num_sectors; int ret; @@ -391,20 +412,7 @@ iscsi_aio_readv_acb(IscsiAIOCB *acb) acb->status = -EINPROGRESS; acb->buf = NULL; - /* If LUN blocksize is bigger than BDRV_BLOCK_SIZE a read from QEMU - * may be misaligned to the LUN, so we may need to read some extra - * data. - */ - acb->read_offset = 0; - if (acb->iscsilun->block_size > BDRV_SECTOR_SIZE) { - uint64_t bdrv_offset = BDRV_SECTOR_SIZE * acb->sector_num; - - acb->read_offset = bdrv_offset % acb->iscsilun->block_size; - } - - num_sectors = (acb->read_size + acb->iscsilun->block_size - + acb->read_offset - 1) - / acb->iscsilun->block_size; + size = acb->nb_sectors * BDRV_SECTOR_SIZE; acb->task = malloc(sizeof(struct scsi_task)); if (acb->task == NULL) { @@ -415,8 +423,9 @@ iscsi_aio_readv_acb(IscsiAIOCB *acb) memset(acb->task, 0, sizeof(struct scsi_task)); acb->task->xfer_dir = SCSI_XFER_READ; + acb->task->expxferlen = size; lba = sector_qemu2lun(acb->sector_num, acb->iscsilun); - acb->task->expxferlen = acb->read_size; + num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun); switch (acb->iscsilun->type) { case TYPE_DISK: @@ -464,6 +473,10 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, IscsiLun *iscsilun = bs->opaque; IscsiAIOCB *acb; + if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { + return NULL; + } + acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); trace_iscsi_aio_readv(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb); @@ -471,7 +484,6 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, acb->sector_num = sector_num; acb->iscsilun = iscsilun; acb->qiov = qiov; - acb->read_size = BDRV_SECTOR_SIZE * (size_t)acb->nb_sectors; acb->retries = ISCSI_CMD_RETRIES; if (iscsi_aio_readv_acb(acb) != 0) { @@ -651,6 +663,9 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, { IscsiAIOCB *acb = opaque; + g_free(acb->buf); + acb->buf = NULL; + if (acb->canceled != 0) { return; } @@ -727,14 +742,30 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, memcpy(&acb->task->cdb[0], acb->ioh->cmdp, acb->ioh->cmd_len); acb->task->expxferlen = acb->ioh->dxfer_len; + data.size = 0; if (acb->task->xfer_dir == SCSI_XFER_WRITE) { - data.data = acb->ioh->dxferp; - data.size = acb->ioh->dxfer_len; + if (acb->ioh->iovec_count == 0) { + data.data = acb->ioh->dxferp; + data.size = acb->ioh->dxfer_len; + } else { +#if defined(LIBISCSI_FEATURE_IOVECTOR) + scsi_task_set_iov_out(acb->task, + (struct scsi_iovec *) acb->ioh->dxferp, + acb->ioh->iovec_count); +#else + struct iovec *iov = (struct iovec *)acb->ioh->dxferp; + + acb->buf = g_malloc(acb->ioh->dxfer_len); + data.data = acb->buf; + data.size = iov_to_buf(iov, acb->ioh->iovec_count, 0, + acb->buf, acb->ioh->dxfer_len); +#endif + } } + if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task, iscsi_aio_ioctl_cb, - (acb->task->xfer_dir == SCSI_XFER_WRITE) ? - &data : NULL, + (data.size > 0) ? &data : NULL, acb) != 0) { scsi_free_scsi_task(acb->task); qemu_aio_release(acb); @@ -743,9 +774,26 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, /* tell libiscsi to read straight into the buffer we got from ioctl */ if (acb->task->xfer_dir == SCSI_XFER_READ) { - scsi_task_add_data_in_buffer(acb->task, - acb->ioh->dxfer_len, - acb->ioh->dxferp); + if (acb->ioh->iovec_count == 0) { + scsi_task_add_data_in_buffer(acb->task, + acb->ioh->dxfer_len, + acb->ioh->dxferp); + } else { +#if defined(LIBISCSI_FEATURE_IOVECTOR) + scsi_task_set_iov_in(acb->task, + (struct scsi_iovec *) acb->ioh->dxferp, + acb->ioh->iovec_count); +#else + int i; + for (i = 0; i < acb->ioh->iovec_count; i++) { + struct iovec *iov = (struct iovec *)acb->ioh->dxferp; + + scsi_task_add_data_in_buffer(acb->task, + iov[i].iov_len, + iov[i].iov_base); + } +#endif + } } iscsi_set_events(iscsilun); @@ -1118,8 +1166,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags) if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) { goto out; } - bs->total_sectors = iscsilun->num_blocks * - iscsilun->block_size / BDRV_SECTOR_SIZE ; + bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun); /* 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 @@ -1235,6 +1282,7 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options) } if (bs.total_sectors < total_size) { ret = -ENOSPC; + goto out; } ret = 0; |