diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2014-06-19 16:18:04 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2014-06-19 16:18:04 +0100 |
commit | 6baa963f4dcc211801608ebe1e0482b51653350c (patch) | |
tree | 5b3a7e2086b888c13cea4dafc8c18711f7302835 | |
parent | 9f6f7f1a85b118ba129620e12bd423a135c8f231 (diff) | |
parent | 91d670fbf9945ca4ecbd123affb36889e7fe8a5d (diff) |
Merge remote-tracking branch 'remotes/bonzini/scsi-next' into staging
* remotes/bonzini/scsi-next:
virtio-scsi: define dummy handle_output for vhost-scsi vqs
block/iscsi: drop obsolete pointers from iscsi_co_writev
block/iscsi: fix init value for iTask->retries
block/iscsi: bump libiscsi requirement to 1.9.0
virtio-scsi: add support for the any_layout feature
virtio-scsi: introduce virtio_scsi_complete_cmd_req
virtio-scsi: prepare sense data handling for any_layout
virtio-scsi: add extra argument and return type to qemu_sgl_concat
virtio-scsi: add target swap for VirtIOSCSICtrlTMFReq fields
virtio-scsi: start preparing for any_layout
util: add return value to qemu_iovec_concat_iov
megasas: use PCI DMA API
scsi: Print command name in debug
scsi-disk: fix bug in scsi_block_new_request() introduced by commit 137745c
scsi-disk.c: Fix compilation with -DDEBUG_SCSI
block/iscsi: use 16 byte CDBs only when necessary
block/iscsi: fix potential segfault on early callback
block/iscsi: handle BUSY condition
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | block/iscsi.c | 189 | ||||
-rwxr-xr-x | configure | 39 | ||||
-rw-r--r-- | hw/scsi/megasas.c | 16 | ||||
-rw-r--r-- | hw/scsi/scsi-bus.c | 4 | ||||
-rw-r--r-- | hw/scsi/scsi-disk.c | 7 | ||||
-rw-r--r-- | hw/scsi/spapr_vscsi.c | 5 | ||||
-rw-r--r-- | hw/scsi/vhost-scsi.c | 8 | ||||
-rw-r--r-- | hw/scsi/virtio-scsi.c | 328 | ||||
-rw-r--r-- | include/block/scsi.h | 2 | ||||
-rw-r--r-- | include/hw/i386/pc.h | 4 | ||||
-rw-r--r-- | include/hw/virtio/virtio-scsi.h | 11 | ||||
-rw-r--r-- | include/qemu-common.h | 6 | ||||
-rw-r--r-- | util/iov.c | 10 |
13 files changed, 335 insertions, 294 deletions
diff --git a/block/iscsi.c b/block/iscsi.c index 6f87605e72..84aa22a62e 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -26,6 +26,7 @@ #include "config-host.h" #include <poll.h> +#include <math.h> #include <arpa/inet.h> #include "qemu-common.h" #include "qemu/config-file.h" @@ -64,6 +65,7 @@ typedef struct IscsiLun { unsigned char *zeroblock; unsigned long *allocationmap; int cluster_sectors; + bool use_16_for_rw; } IscsiLun; typedef struct IscsiTask { @@ -75,6 +77,7 @@ typedef struct IscsiTask { Coroutine *co; QEMUBH *bh; IscsiLun *iscsilun; + QEMUTimer retry_timer; } IscsiTask; typedef struct IscsiAIOCB { @@ -86,7 +89,6 @@ typedef struct IscsiAIOCB { uint8_t *buf; int status; int canceled; - int retries; int64_t sector_num; int nb_sectors; #ifdef __linux__ @@ -96,7 +98,8 @@ typedef struct IscsiAIOCB { #define NOP_INTERVAL 5000 #define MAX_NOP_FAILURES 3 -#define ISCSI_CMD_RETRIES 5 +#define ISCSI_CMD_RETRIES ARRAY_SIZE(iscsi_retry_times) +static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048}; /* this threshhold is a trade-off knob to choose between * the potential additional overhead of an extra GET_LBA_STATUS request @@ -142,10 +145,25 @@ iscsi_schedule_bh(IscsiAIOCB *acb) static void iscsi_co_generic_bh_cb(void *opaque) { struct IscsiTask *iTask = opaque; + iTask->complete = 1; qemu_bh_delete(iTask->bh); qemu_coroutine_enter(iTask->co, NULL); } +static void iscsi_retry_timer_expired(void *opaque) +{ + struct IscsiTask *iTask = opaque; + iTask->complete = 1; + if (iTask->co) { + qemu_coroutine_enter(iTask->co, NULL); + } +} + +static inline unsigned exp_random(double mean) +{ + return -mean * log((double)rand() / RAND_MAX); +} + static void iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) @@ -153,19 +171,34 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, 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) { - error_report("iSCSI CheckCondition: %s", iscsi_get_error(iscsi)); - iTask->do_retry = 1; - goto out; - } - if (status != SCSI_STATUS_GOOD) { + if (iTask->retries++ < ISCSI_CMD_RETRIES) { + if (status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { + error_report("iSCSI CheckCondition: %s", + iscsi_get_error(iscsi)); + iTask->do_retry = 1; + goto out; + } + if (status == SCSI_STATUS_BUSY) { + unsigned retry_time = + exp_random(iscsi_retry_times[iTask->retries - 1]); + error_report("iSCSI Busy (retry #%u in %u ms): %s", + iTask->retries, retry_time, + iscsi_get_error(iscsi)); + aio_timer_init(iTask->iscsilun->aio_context, + &iTask->retry_timer, QEMU_CLOCK_REALTIME, + SCALE_MS, iscsi_retry_timer_expired, iTask); + timer_mod(&iTask->retry_timer, + qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + retry_time); + iTask->do_retry = 1; + return; + } + } error_report("iSCSI Failure: %s", iscsi_get_error(iscsi)); } @@ -174,15 +207,16 @@ out: iTask->bh = aio_bh_new(iTask->iscsilun->aio_context, iscsi_co_generic_bh_cb, iTask); qemu_bh_schedule(iTask->bh); + } else { + iTask->complete = 1; } } static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask) { *iTask = (struct IscsiTask) { - .co = qemu_coroutine_self(), - .retries = ISCSI_CMD_RETRIES, - .iscsilun = iscsilun, + .co = qemu_coroutine_self(), + .iscsilun = iscsilun, }; } @@ -325,8 +359,6 @@ static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, struct IscsiTask iTask; uint64_t lba; uint32_t num_sectors; - uint8_t *data = NULL; - uint8_t *buf = NULL; if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { return -EINVAL; @@ -334,31 +366,24 @@ static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, lba = sector_qemu2lun(sector_num, iscsilun); num_sectors = sector_qemu2lun(nb_sectors, iscsilun); -#if !defined(LIBISCSI_FEATURE_IOVECTOR) - /* if the iovec only contains one buffer we can pass it directly */ - if (iov->niov == 1) { - data = iov->iov[0].iov_base; - } else { - size_t size = MIN(nb_sectors * BDRV_SECTOR_SIZE, iov->size); - buf = g_malloc(size); - qemu_iovec_to_buf(iov, 0, buf, size); - data = buf; - } -#endif iscsi_co_init_iscsitask(iscsilun, &iTask); retry: - iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba, - data, num_sectors * iscsilun->block_size, - iscsilun->block_size, 0, 0, 0, 0, 0, - iscsi_co_generic_cb, &iTask); + if (iscsilun->use_16_for_rw) { + iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba, + NULL, num_sectors * iscsilun->block_size, + iscsilun->block_size, 0, 0, 0, 0, 0, + iscsi_co_generic_cb, &iTask); + } else { + iTask.task = iscsi_write10_task(iscsilun->iscsi, iscsilun->lun, lba, + NULL, num_sectors * iscsilun->block_size, + iscsilun->block_size, 0, 0, 0, 0, 0, + iscsi_co_generic_cb, &iTask); + } if (iTask.task == NULL) { - g_free(buf); return -ENOMEM; } -#if defined(LIBISCSI_FEATURE_IOVECTOR) scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); -#endif while (!iTask.complete) { iscsi_set_events(iscsilun); qemu_coroutine_yield(); @@ -374,8 +399,6 @@ retry: goto retry; } - g_free(buf); - if (iTask.status != SCSI_STATUS_GOOD) { return -EIO; } @@ -386,7 +409,6 @@ retry: } -#if defined(LIBISCSI_FEATURE_IOVECTOR) static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun, int64_t sector_num, int nb_sectors) { @@ -496,9 +518,6 @@ out: return ret; } -#endif /* LIBISCSI_FEATURE_IOVECTOR */ - - static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *iov) @@ -507,15 +526,11 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, struct IscsiTask iTask; uint64_t lba; uint32_t num_sectors; -#if !defined(LIBISCSI_FEATURE_IOVECTOR) - int i; -#endif if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { return -EINVAL; } -#if defined(LIBISCSI_FEATURE_IOVECTOR) if (iscsilun->lbprz && nb_sectors >= ISCSI_CHECKALLOC_THRES && !iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) { int64_t ret; @@ -529,42 +544,28 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, return 0; } } -#endif lba = sector_qemu2lun(sector_num, iscsilun); num_sectors = sector_qemu2lun(nb_sectors, iscsilun); iscsi_co_init_iscsitask(iscsilun, &iTask); retry: - switch (iscsilun->type) { - case TYPE_DISK: + if (iscsilun->use_16_for_rw) { iTask.task = iscsi_read16_task(iscsilun->iscsi, iscsilun->lun, lba, num_sectors * iscsilun->block_size, iscsilun->block_size, 0, 0, 0, 0, 0, iscsi_co_generic_cb, &iTask); - break; - default: + } else { iTask.task = iscsi_read10_task(iscsilun->iscsi, iscsilun->lun, lba, num_sectors * iscsilun->block_size, iscsilun->block_size, -#if !defined(CONFIG_LIBISCSI_1_4) /* API change from 1.4.0 to 1.5.0 */ 0, 0, 0, 0, 0, -#endif iscsi_co_generic_cb, &iTask); - break; } if (iTask.task == NULL) { return -ENOMEM; } -#if defined(LIBISCSI_FEATURE_IOVECTOR) scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); -#else - for (i = 0; i < iov->niov; i++) { - scsi_task_add_data_in_buffer(iTask.task, - iov->iov[i].iov_len, - iov->iov[i].iov_base); - } -#endif while (!iTask.complete) { iscsi_set_events(iscsilun); @@ -719,18 +720,9 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, 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 } } @@ -750,20 +742,9 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, 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 } } @@ -772,7 +753,6 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, return &acb->common; } - static void ioctl_cb(void *opaque, int status) { int *p_status = opaque; @@ -877,8 +857,6 @@ retry: return 0; } -#if defined(SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED) - static int coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) @@ -887,19 +865,27 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, struct IscsiTask iTask; uint64_t lba; uint32_t nb_blocks; + bool use_16_for_ws = iscsilun->use_16_for_rw; if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { return -EINVAL; } - if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) { - /* WRITE SAME with UNMAP is not supported by the target, - * fall back and try WRITE SAME without UNMAP */ - flags &= ~BDRV_REQ_MAY_UNMAP; + if (flags & BDRV_REQ_MAY_UNMAP) { + if (!use_16_for_ws && !iscsilun->lbp.lbpws10) { + /* WRITESAME10 with UNMAP is unsupported try WRITESAME16 */ + use_16_for_ws = true; + } + if (use_16_for_ws && !iscsilun->lbp.lbpws) { + /* WRITESAME16 with UNMAP is not supported by the target, + * fall back and try WRITESAME10/16 without UNMAP */ + flags &= ~BDRV_REQ_MAY_UNMAP; + use_16_for_ws = iscsilun->use_16_for_rw; + } } if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) { - /* WRITE SAME without UNMAP is not supported by the target */ + /* WRITESAME without UNMAP is not supported by the target */ return -ENOTSUP; } @@ -912,10 +898,18 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, iscsi_co_init_iscsitask(iscsilun, &iTask); retry: - if (iscsi_writesame16_task(iscsilun->iscsi, iscsilun->lun, lba, - iscsilun->zeroblock, iscsilun->block_size, - nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP), - 0, 0, iscsi_co_generic_cb, &iTask) == NULL) { + if (use_16_for_ws) { + iTask.task = iscsi_writesame16_task(iscsilun->iscsi, iscsilun->lun, lba, + iscsilun->zeroblock, iscsilun->block_size, + nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP), + 0, 0, iscsi_co_generic_cb, &iTask); + } else { + iTask.task = iscsi_writesame10_task(iscsilun->iscsi, iscsilun->lun, lba, + iscsilun->zeroblock, iscsilun->block_size, + nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP), + 0, 0, iscsi_co_generic_cb, &iTask); + } + if (iTask.task == NULL) { return -ENOMEM; } @@ -957,8 +951,6 @@ retry: return 0; } -#endif /* SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED */ - static void parse_chap(struct iscsi_context *iscsi, const char *target, Error **errp) { @@ -1068,7 +1060,6 @@ static char *parse_initiator_name(const char *target) return iscsi_name; } -#if defined(LIBISCSI_FEATURE_NOP_COUNTER) static void iscsi_nop_timed_event(void *opaque) { IscsiLun *iscsilun = opaque; @@ -1086,7 +1077,6 @@ static void iscsi_nop_timed_event(void *opaque) timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); iscsi_set_events(iscsilun); } -#endif static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) { @@ -1113,6 +1103,7 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) iscsilun->num_blocks = rc16->returned_lba + 1; iscsilun->lbpme = rc16->lbpme; iscsilun->lbprz = rc16->lbprz; + iscsilun->use_16_for_rw = (rc16->returned_lba > 0xffffffff); } } break; @@ -1224,14 +1215,12 @@ static void iscsi_attach_aio_context(BlockDriverState *bs, iscsilun->aio_context = new_context; iscsi_set_events(iscsilun); -#if defined(LIBISCSI_FEATURE_NOP_COUNTER) /* Set up a timer for sending out iSCSI NOPs */ iscsilun->nop_timer = aio_timer_new(iscsilun->aio_context, QEMU_CLOCK_REALTIME, SCALE_MS, iscsi_nop_timed_event, iscsilun); timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); -#endif } /* @@ -1423,13 +1412,11 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) { iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran * iscsilun->block_size) >> BDRV_SECTOR_BITS; -#if defined(LIBISCSI_FEATURE_IOVECTOR) if (iscsilun->lbprz && !(bs->open_flags & BDRV_O_NOCACHE)) { iscsilun->allocationmap = bitmap_new(DIV_ROUND_UP(bs->total_sectors, iscsilun->cluster_sectors)); } -#endif } out: @@ -1614,13 +1601,9 @@ static BlockDriver bdrv_iscsi = { .bdrv_truncate = iscsi_truncate, .bdrv_refresh_limits = iscsi_refresh_limits, -#if defined(LIBISCSI_FEATURE_IOVECTOR) .bdrv_co_get_block_status = iscsi_co_get_block_status, -#endif .bdrv_co_discard = iscsi_co_discard, -#if defined(SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED) .bdrv_co_write_zeroes = iscsi_co_write_zeroes, -#endif .bdrv_co_readv = iscsi_co_readv, .bdrv_co_writev = iscsi_co_writev, .bdrv_co_flush_to_disk = iscsi_co_flush, @@ -3405,46 +3405,20 @@ if compile_prog "" "" ; then fi ########################################## -# Do we have libiscsi -# We check for iscsi_write16_sync() to make sure we have a -# at least version 1.4.0 of libiscsi. +# Do we have libiscsi >= 1.9.0 if test "$libiscsi" != "no" ; then - cat > $TMPC << EOF -#include <stdio.h> -#include <iscsi/iscsi.h> -int main(void) { iscsi_write16_sync(NULL,0,0,NULL,0,0,0,0,0,0,0); return 0; } -EOF - if $pkg_config --atleast-version=1.7.0 libiscsi; then + if $pkg_config --atleast-version=1.9.0 libiscsi; then libiscsi="yes" libiscsi_cflags=$($pkg_config --cflags libiscsi) libiscsi_libs=$($pkg_config --libs libiscsi) - elif compile_prog "" "-liscsi" ; then - libiscsi="yes" - libiscsi_libs="-liscsi" else if test "$libiscsi" = "yes" ; then - feature_not_found "libiscsi" "Install libiscsi devel" + feature_not_found "libiscsi" "Install libiscsi >= 1.9.0" fi libiscsi="no" fi fi -# We also need to know the API version because there was an -# API change from 1.4.0 to 1.5.0. -if test "$libiscsi" = "yes"; then - cat >$TMPC <<EOF -#include <iscsi/iscsi.h> -int main(void) -{ - iscsi_read10_task(0, 0, 0, 0, 0, 0, 0); - return 0; -} -EOF - if compile_prog "" "-liscsi"; then - libiscsi_version="1.4.0" - fi -fi - ########################################## # Do we need libm cat > $TMPC << EOF @@ -4218,11 +4192,7 @@ echo "nss used $smartcard_nss" echo "libusb $libusb" echo "usb net redir $usb_redir" echo "GLX support $glx" -if test "$libiscsi_version" = "1.4.0"; then -echo "libiscsi support $libiscsi (1.4.0)" -else echo "libiscsi support $libiscsi" -fi echo "libnfs support $libnfs" echo "build guest agent $guest_agent" echo "QGA VSS support $guest_agent_with_vss" @@ -4579,9 +4549,6 @@ fi if test "$libiscsi" = "yes" ; then echo "CONFIG_LIBISCSI=m" >> $config_host_mak - if test "$libiscsi_version" = "1.4.0"; then - echo "CONFIG_LIBISCSI_1_4=y" >> $config_host_mak - fi echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak fi diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index b05c47abdc..c68a873f18 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -294,6 +294,7 @@ static void megasas_unmap_sgl(MegasasCmd *cmd) static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr, uint8_t sense_len) { + PCIDevice *pcid = PCI_DEVICE(cmd->state); uint32_t pa_hi = 0, pa_lo; hwaddr pa; @@ -306,7 +307,7 @@ static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr, pa_hi = le32_to_cpu(cmd->frame->pass.sense_addr_hi); } pa = ((uint64_t) pa_hi << 32) | pa_lo; - cpu_physical_memory_write(pa, sense_ptr, sense_len); + pci_dma_write(pcid, pa, sense_ptr, sense_len); cmd->frame->header.sense_len = sense_len; } return sense_len; @@ -472,6 +473,7 @@ static MegasasCmd *megasas_next_frame(MegasasState *s, static MegasasCmd *megasas_enqueue_frame(MegasasState *s, hwaddr frame, uint64_t context, int count) { + PCIDevice *pcid = PCI_DEVICE(s); MegasasCmd *cmd = NULL; int frame_size = MFI_FRAME_SIZE * 16; hwaddr frame_size_p = frame_size; @@ -484,11 +486,11 @@ static MegasasCmd *megasas_enqueue_frame(MegasasState *s, if (!cmd->pa) { cmd->pa = frame; /* Map all possible frames */ - cmd->frame = cpu_physical_memory_map(frame, &frame_size_p, 0); + cmd->frame = pci_dma_map(pcid, frame, &frame_size_p, 0); if (frame_size_p != frame_size) { trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame); if (cmd->frame) { - cpu_physical_memory_unmap(cmd->frame, frame_size_p, 0, 0); + pci_dma_unmap(pcid, cmd->frame, frame_size_p, 0, 0); cmd->frame = NULL; cmd->pa = 0; } @@ -561,13 +563,14 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context) static void megasas_reset_frames(MegasasState *s) { + PCIDevice *pcid = PCI_DEVICE(s); int i; MegasasCmd *cmd; for (i = 0; i < s->fw_cmds; i++) { cmd = &s->frames[i]; if (cmd->pa) { - cpu_physical_memory_unmap(cmd->frame, cmd->pa_size, 0, 0); + pci_dma_unmap(pcid, cmd->frame, cmd->pa_size, 0, 0); cmd->frame = NULL; cmd->pa = 0; } @@ -584,6 +587,7 @@ static void megasas_abort_command(MegasasCmd *cmd) static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd) { + PCIDevice *pcid = PCI_DEVICE(s); uint32_t pa_hi, pa_lo; hwaddr iq_pa, initq_size; struct mfi_init_qinfo *initq; @@ -595,7 +599,7 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd) iq_pa = (((uint64_t) pa_hi << 32) | pa_lo); trace_megasas_init_firmware((uint64_t)iq_pa); initq_size = sizeof(*initq); - initq = cpu_physical_memory_map(iq_pa, &initq_size, 0); + initq = pci_dma_map(pcid, iq_pa, &initq_size, 0); if (!initq || initq_size != sizeof(*initq)) { trace_megasas_initq_map_failed(cmd->index); s->event_count++; @@ -631,7 +635,7 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd) s->fw_state = MFI_FWSTATE_OPERATIONAL; out: if (initq) { - cpu_physical_memory_unmap(initq, initq_size, 0, 0); + pci_dma_unmap(pcid, initq, initq_size, 0, 0); } return ret; } diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 003d2843df..ea1ac09c8a 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1429,7 +1429,7 @@ int scsi_build_sense(uint8_t *in_buf, int in_len, } } -static const char *scsi_command_name(uint8_t cmd) +const char *scsi_command_name(uint8_t cmd) { static const char *names[] = { [ TEST_UNIT_READY ] = "TEST_UNIT_READY", @@ -1545,6 +1545,8 @@ static const char *scsi_command_name(uint8_t cmd) [ SET_READ_AHEAD ] = "SET_READ_AHEAD", [ ALLOW_OVERWRITE ] = "ALLOW_OVERWRITE", [ MECHANISM_STATUS ] = "MECHANISM_STATUS", + [ GET_EVENT_STATUS_NOTIFICATION ] = "GET_EVENT_STATUS_NOTIFICATION", + [ READ_DISC_INFORMATION ] = "READ_DISC_INFORMATION", }; if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index fc6e32ada5..a529ad24c7 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2015,7 +2015,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) case VERIFY_10: case VERIFY_12: case VERIFY_16: - DPRINTF("Verify (bytchk %lu)\n", (r->req.buf[1] >> 1) & 3); + DPRINTF("Verify (bytchk %d)\n", (req->cmd.buf[1] >> 1) & 3); if (req->cmd.buf[1] & 6) { goto illegal_request; } @@ -2027,7 +2027,8 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) (long)r->req.cmd.xfer); break; default: - DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); + DPRINTF("Unknown SCSI command (%2.2x=%s)\n", buf[0], + scsi_command_name(buf[0])); scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); return 0; } @@ -2526,7 +2527,7 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, * ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without * O_DIRECT everything must go through SG_IO. */ - if (bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE) { + if (!(bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE)) { break; } diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index f96b7af791..048cfc7b05 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -799,8 +799,9 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) req->sreq = scsi_req_new(sdev, req->qtag, lun, srp->cmd.cdb, req); n = scsi_req_enqueue(req->sreq); - DPRINTF("VSCSI: Queued command tag 0x%x CMD 0x%x LUN %d ret: %d\n", - req->qtag, srp->cmd.cdb[0], lun, n); + DPRINTF("VSCSI: Queued command tag 0x%x CMD 0x%x=%s LUN %d ret: %d\n", + req->qtag, srp->cmd.cdb[0], scsi_command_name(srp->cmd.cdb[0]), + lun, n); if (n) { /* Transfer direction must be set before preprocessing the diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 668bafa72a..cc8df4ef03 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -196,6 +196,10 @@ static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) } } +static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ +} + static void vhost_scsi_realize(DeviceState *dev, Error **errp) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); @@ -217,7 +221,9 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) } } - virtio_scsi_common_realize(dev, &err); + virtio_scsi_common_realize(dev, &err, vhost_dummy_handle_output, + vhost_dummy_handle_output, + vhost_dummy_handle_output); if (err != NULL) { error_propagate(errp, err); return; diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index b39880a9cd..8c8c9d1f61 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -15,6 +15,7 @@ #include "hw/virtio/virtio-scsi.h" #include "qemu/error-report.h" +#include "qemu/iov.h" #include <hw/scsi/scsi.h> #include <block/scsi.h> #include <hw/virtio/virtio-bus.h> @@ -25,21 +26,28 @@ typedef struct VirtIOSCSIReq { VirtQueueElement elem; QEMUSGList qsgl; SCSIRequest *sreq; + size_t resp_size; + enum SCSIXferMode mode; + QEMUIOVector resp_iov; union { - char *buf; - VirtIOSCSICmdReq *cmd; - VirtIOSCSICtrlTMFReq *tmf; - VirtIOSCSICtrlANReq *an; - } req; - union { - char *buf; - VirtIOSCSICmdResp *cmd; - VirtIOSCSICtrlTMFResp *tmf; - VirtIOSCSICtrlANResp *an; - VirtIOSCSIEvent *event; + VirtIOSCSICmdResp cmd; + VirtIOSCSICtrlTMFResp tmf; + VirtIOSCSICtrlANResp an; + VirtIOSCSIEvent event; } resp; + union { + struct { + VirtIOSCSICmdReq cmd; + uint8_t cdb[]; + } QEMU_PACKED; + VirtIOSCSICtrlTMFReq tmf; + VirtIOSCSICtrlANReq an; + } req; } VirtIOSCSIReq; +QEMU_BUILD_BUG_ON(offsetof(VirtIOSCSIReq, req.cdb) != + offsetof(VirtIOSCSIReq, req.cmd) + sizeof(VirtIOSCSICmdReq)); + static inline int virtio_scsi_get_lun(uint8_t *lun) { return ((lun[2] << 8) | lun[3]) & 0x3FFF; @@ -56,18 +64,41 @@ static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun) return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun)); } +static VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq) +{ + VirtIOSCSIReq *req; + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + + req = g_malloc0(sizeof(*req) + vs->cdb_size); + + req->vq = vq; + req->dev = s; + req->sreq = NULL; + qemu_sglist_init(&req->qsgl, DEVICE(s), 8, &address_space_memory); + qemu_iovec_init(&req->resp_iov, 1); + return req; +} + +static void virtio_scsi_free_req(VirtIOSCSIReq *req) +{ + qemu_iovec_destroy(&req->resp_iov); + qemu_sglist_destroy(&req->qsgl); + g_free(req); +} + static void virtio_scsi_complete_req(VirtIOSCSIReq *req) { VirtIOSCSI *s = req->dev; VirtQueue *vq = req->vq; VirtIODevice *vdev = VIRTIO_DEVICE(s); - virtqueue_push(vq, &req->elem, req->qsgl.size + req->elem.in_sg[0].iov_len); - qemu_sglist_destroy(&req->qsgl); + + qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size); + virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size); if (req->sreq) { req->sreq->hba_private = NULL; scsi_req_unref(req->sreq); } - g_free(req); + virtio_scsi_free_req(req); virtio_notify(vdev, vq); } @@ -77,50 +108,73 @@ static void virtio_scsi_bad_req(void) exit(1); } -static void qemu_sgl_init_external(VirtIOSCSIReq *req, struct iovec *sg, - hwaddr *addr, int num) +static size_t qemu_sgl_concat(VirtIOSCSIReq *req, struct iovec *iov, + hwaddr *addr, int num, size_t skip) { QEMUSGList *qsgl = &req->qsgl; - - qemu_sglist_init(qsgl, DEVICE(req->dev), num, &address_space_memory); - while (num--) { - qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len); + size_t copied = 0; + + while (num) { + if (skip >= iov->iov_len) { + skip -= iov->iov_len; + } else { + qemu_sglist_add(qsgl, *addr + skip, iov->iov_len - skip); + copied += iov->iov_len - skip; + skip = 0; + } + iov++; + addr++; + num--; } + + assert(skip == 0); + return copied; } -static void virtio_scsi_parse_req(VirtIOSCSI *s, VirtQueue *vq, - VirtIOSCSIReq *req) +static int virtio_scsi_parse_req(VirtIOSCSIReq *req, + unsigned req_size, unsigned resp_size) { - assert(req->elem.in_num); - req->vq = vq; - req->dev = s; - req->sreq = NULL; - if (req->elem.out_num) { - req->req.buf = req->elem.out_sg[0].iov_base; + size_t in_size, out_size; + + if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0, + &req->req, req_size) < req_size) { + return -EINVAL; } - req->resp.buf = req->elem.in_sg[0].iov_base; - if (req->elem.out_num > 1) { - qemu_sgl_init_external(req, &req->elem.out_sg[1], - &req->elem.out_addr[1], - req->elem.out_num - 1); - } else { - qemu_sgl_init_external(req, &req->elem.in_sg[1], - &req->elem.in_addr[1], - req->elem.in_num - 1); + if (qemu_iovec_concat_iov(&req->resp_iov, + req->elem.in_sg, req->elem.in_num, 0, + resp_size) < resp_size) { + return -EINVAL; + } + req->resp_size = resp_size; + + out_size = qemu_sgl_concat(req, req->elem.out_sg, + &req->elem.out_addr[0], req->elem.out_num, + req_size); + in_size = qemu_sgl_concat(req, req->elem.in_sg, + &req->elem.in_addr[0], req->elem.in_num, + resp_size); + + if (out_size && in_size) { + return -ENOTSUP; + } + + if (out_size) { + req->mode = SCSI_XFER_TO_DEV; + } else if (in_size) { + req->mode = SCSI_XFER_FROM_DEV; } + + return 0; } static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq) { - VirtIOSCSIReq *req; - req = g_malloc(sizeof(*req)); + VirtIOSCSIReq *req = virtio_scsi_init_req(s, vq); if (!virtqueue_pop(vq, &req->elem)) { - g_free(req); + virtio_scsi_free_req(req); return NULL; } - - virtio_scsi_parse_req(s, vq, req); return req; } @@ -143,9 +197,9 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) VirtIOSCSIReq *req; uint32_t n; - req = g_malloc(sizeof(*req)); qemu_get_be32s(f, &n); assert(n < vs->conf.num_queues); + req = virtio_scsi_init_req(s, vs->cmd_vqs[n]); qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); /* TODO: add a way for SCSIBusInfo's load_request to fail, * and fail migration instead of asserting here. @@ -156,41 +210,44 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) #endif assert(req->elem.in_num <= ARRAY_SIZE(req->elem.in_sg)); assert(req->elem.out_num <= ARRAY_SIZE(req->elem.out_sg)); - virtio_scsi_parse_req(s, vs->cmd_vqs[n], req); + + if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, + sizeof(VirtIOSCSICmdResp) + vs->sense_size) < 0) { + error_report("invalid SCSI request migration data"); + exit(1); + } scsi_req_ref(sreq); req->sreq = sreq; if (req->sreq->cmd.mode != SCSI_XFER_NONE) { - int req_mode = - (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV); - - assert(req->sreq->cmd.mode == req_mode); + assert(req->sreq->cmd.mode == req->mode); } return req; } static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) { - SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf->lun); + SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf.lun); SCSIRequest *r, *next; BusChild *kid; int target; /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ - req->resp.tmf->response = VIRTIO_SCSI_S_OK; + req->resp.tmf.response = VIRTIO_SCSI_S_OK; - switch (req->req.tmf->subtype) { + tswap32s(&req->req.tmf.subtype); + switch (req->req.tmf.subtype) { case VIRTIO_SCSI_T_TMF_ABORT_TASK: case VIRTIO_SCSI_T_TMF_QUERY_TASK: if (!d) { goto fail; } - if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { goto incorrect_lun; } QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { VirtIOSCSIReq *cmd_req = r->hba_private; - if (cmd_req && cmd_req->req.cmd->tag == req->req.tmf->tag) { + if (cmd_req && cmd_req->req.cmd.tag == req->req.tmf.tag) { break; } } @@ -200,11 +257,11 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) * check for it in the loop above. */ assert(r->hba_private); - if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { + if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { /* "If the specified command is present in the task set, then * return a service response set to FUNCTION SUCCEEDED". */ - req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; + req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; } else { scsi_req_cancel(r); } @@ -215,7 +272,7 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) if (!d) { goto fail; } - if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { goto incorrect_lun; } s->resetting++; @@ -229,16 +286,16 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) if (!d) { goto fail; } - if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { goto incorrect_lun; } QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { if (r->hba_private) { - if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) { + if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) { /* "If there is any command present in the task set, then * return a service response set to FUNCTION SUCCEEDED". */ - req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; + req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; break; } else { scsi_req_cancel(r); @@ -248,7 +305,7 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) break; case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: - target = req->req.tmf->lun[1]; + target = req->req.tmf.lun[1]; s->resetting++; QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { d = DO_UPCAST(SCSIDevice, qdev, kid->child); @@ -261,18 +318,18 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) case VIRTIO_SCSI_T_TMF_CLEAR_ACA: default: - req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_REJECTED; + req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_REJECTED; break; } return; incorrect_lun: - req->resp.tmf->response = VIRTIO_SCSI_S_INCORRECT_LUN; + req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN; return; fail: - req->resp.tmf->response = VIRTIO_SCSI_S_BAD_TARGET; + req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET; } static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) @@ -281,57 +338,70 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) VirtIOSCSIReq *req; while ((req = virtio_scsi_pop_req(s, vq))) { - int out_size, in_size; - if (req->elem.out_num < 1 || req->elem.in_num < 1) { + int type; + + if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0, + &type, sizeof(type)) < sizeof(type)) { virtio_scsi_bad_req(); continue; } - out_size = req->elem.out_sg[0].iov_len; - in_size = req->elem.in_sg[0].iov_len; - if (req->req.tmf->type == VIRTIO_SCSI_T_TMF) { - if (out_size < sizeof(VirtIOSCSICtrlTMFReq) || - in_size < sizeof(VirtIOSCSICtrlTMFResp)) { + tswap32s(&req->req.tmf.type); + if (req->req.tmf.type == VIRTIO_SCSI_T_TMF) { + if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlTMFReq), + sizeof(VirtIOSCSICtrlTMFResp)) < 0) { virtio_scsi_bad_req(); + } else { + virtio_scsi_do_tmf(s, req); } - virtio_scsi_do_tmf(s, req); - } else if (req->req.tmf->type == VIRTIO_SCSI_T_AN_QUERY || - req->req.tmf->type == VIRTIO_SCSI_T_AN_SUBSCRIBE) { - if (out_size < sizeof(VirtIOSCSICtrlANReq) || - in_size < sizeof(VirtIOSCSICtrlANResp)) { + } else if (req->req.tmf.type == VIRTIO_SCSI_T_AN_QUERY || + req->req.tmf.type == VIRTIO_SCSI_T_AN_SUBSCRIBE) { + if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlANReq), + sizeof(VirtIOSCSICtrlANResp)) < 0) { virtio_scsi_bad_req(); + } else { + req->resp.an.event_actual = 0; + req->resp.an.response = VIRTIO_SCSI_S_OK; } - req->resp.an->event_actual = 0; - req->resp.an->response = VIRTIO_SCSI_S_OK; } virtio_scsi_complete_req(req); } } +static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req) +{ + /* Sense data is not in req->resp and is copied separately + * in virtio_scsi_command_complete. + */ + req->resp_size = sizeof(VirtIOSCSICmdResp); + virtio_scsi_complete_req(req); +} + static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, size_t resid) { VirtIOSCSIReq *req = r->hba_private; - VirtIOSCSI *s = req->dev; - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + uint8_t sense[SCSI_SENSE_BUF_SIZE]; uint32_t sense_len; if (r->io_canceled) { return; } - req->resp.cmd->response = VIRTIO_SCSI_S_OK; - req->resp.cmd->status = status; - if (req->resp.cmd->status == GOOD) { - req->resp.cmd->resid = tswap32(resid); + req->resp.cmd.response = VIRTIO_SCSI_S_OK; + req->resp.cmd.status = status; + if (req->resp.cmd.status == GOOD) { + req->resp.cmd.resid = tswap32(resid); } else { - req->resp.cmd->resid = 0; - sense_len = scsi_req_get_sense(r, req->resp.cmd->sense, - vs->sense_size); - req->resp.cmd->sense_len = tswap32(sense_len); + req->resp.cmd.resid = 0; + sense_len = scsi_req_get_sense(r, sense, sizeof(sense)); + sense_len = MIN(sense_len, req->resp_iov.size - sizeof(req->resp.cmd)); + qemu_iovec_from_buf(&req->resp_iov, sizeof(req->resp.cmd), + &req->resp, sense_len); + req->resp.cmd.sense_len = tswap32(sense_len); } - virtio_scsi_complete_req(req); + virtio_scsi_complete_cmd_req(req); } static QEMUSGList *virtio_scsi_get_sg_list(SCSIRequest *r) @@ -349,17 +419,17 @@ static void virtio_scsi_request_cancelled(SCSIRequest *r) return; } if (req->dev->resetting) { - req->resp.cmd->response = VIRTIO_SCSI_S_RESET; + req->resp.cmd.response = VIRTIO_SCSI_S_RESET; } else { - req->resp.cmd->response = VIRTIO_SCSI_S_ABORTED; + req->resp.cmd.response = VIRTIO_SCSI_S_ABORTED; } - virtio_scsi_complete_req(req); + virtio_scsi_complete_cmd_req(req); } static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req) { - req->resp.cmd->response = VIRTIO_SCSI_S_FAILURE; - virtio_scsi_complete_req(req); + req->resp.cmd.response = VIRTIO_SCSI_S_FAILURE; + virtio_scsi_complete_cmd_req(req); } static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) @@ -373,43 +443,35 @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) while ((req = virtio_scsi_pop_req(s, vq))) { SCSIDevice *d; - int out_size, in_size; - if (req->elem.out_num < 1 || req->elem.in_num < 1) { - virtio_scsi_bad_req(); - } + int rc; - out_size = req->elem.out_sg[0].iov_len; - in_size = req->elem.in_sg[0].iov_len; - if (out_size < sizeof(VirtIOSCSICmdReq) + vs->cdb_size || - in_size < sizeof(VirtIOSCSICmdResp) + vs->sense_size) { - virtio_scsi_bad_req(); - } - - if (req->elem.out_num > 1 && req->elem.in_num > 1) { - virtio_scsi_fail_cmd_req(req); + rc = virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, + sizeof(VirtIOSCSICmdResp) + vs->sense_size); + if (rc < 0) { + if (rc == -ENOTSUP) { + virtio_scsi_fail_cmd_req(req); + } else { + virtio_scsi_bad_req(); + } continue; } - d = virtio_scsi_device_find(s, req->req.cmd->lun); + d = virtio_scsi_device_find(s, req->req.cmd.lun); if (!d) { - req->resp.cmd->response = VIRTIO_SCSI_S_BAD_TARGET; - virtio_scsi_complete_req(req); + req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET; + virtio_scsi_complete_cmd_req(req); continue; } - req->sreq = scsi_req_new(d, req->req.cmd->tag, - virtio_scsi_get_lun(req->req.cmd->lun), - req->req.cmd->cdb, req); - - if (req->sreq->cmd.mode != SCSI_XFER_NONE) { - int req_mode = - (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV); - - if (req->sreq->cmd.mode != req_mode || - req->sreq->cmd.xfer > req->qsgl.size) { - req->resp.cmd->response = VIRTIO_SCSI_S_OVERRUN; - virtio_scsi_complete_req(req); - continue; - } + req->sreq = scsi_req_new(d, req->req.cmd.tag, + virtio_scsi_get_lun(req->req.cmd.lun), + req->req.cdb, req); + + if (req->sreq->cmd.mode != SCSI_XFER_NONE + && (req->sreq->cmd.mode != req->mode || + req->sreq->cmd.xfer > req->qsgl.size)) { + req->resp.cmd.response = VIRTIO_SCSI_S_OVERRUN; + virtio_scsi_complete_cmd_req(req); + continue; } n = scsi_req_enqueue(req->sreq); @@ -513,7 +575,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, return; } - if (req->elem.out_num || req->elem.in_num != 1) { + if (req->elem.out_num) { virtio_scsi_bad_req(); } @@ -522,12 +584,12 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, s->events_dropped = false; } - in_size = req->elem.in_sg[0].iov_len; + in_size = iov_size(req->elem.in_sg, req->elem.in_num); if (in_size < sizeof(VirtIOSCSIEvent)) { virtio_scsi_bad_req(); } - evt = req->resp.event; + evt = &req->resp.event; memset(evt, 0, sizeof(VirtIOSCSIEvent)); evt->event = event; evt->reason = reason; @@ -605,7 +667,9 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = { .load_request = virtio_scsi_load_request, }; -void virtio_scsi_common_realize(DeviceState *dev, Error **errp) +void virtio_scsi_common_realize(DeviceState *dev, Error **errp, + HandleOutput ctrl, HandleOutput evt, + HandleOutput cmd) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(dev); @@ -619,12 +683,12 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp) s->cdb_size = VIRTIO_SCSI_CDB_SIZE; s->ctrl_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - virtio_scsi_handle_ctrl); + ctrl); s->event_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - virtio_scsi_handle_event); + evt); for (i = 0; i < s->conf.num_queues; i++) { s->cmd_vqs[i] = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - virtio_scsi_handle_cmd); + cmd); } } @@ -635,7 +699,9 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) static int virtio_scsi_id; Error *err = NULL; - virtio_scsi_common_realize(dev, &err); + virtio_scsi_common_realize(dev, &err, virtio_scsi_handle_ctrl, + virtio_scsi_handle_event, + virtio_scsi_handle_cmd); if (err != NULL) { error_propagate(errp, err); return; diff --git a/include/block/scsi.h b/include/block/scsi.h index 9ab045b613..edde960d18 100644 --- a/include/block/scsi.h +++ b/include/block/scsi.h @@ -143,6 +143,8 @@ #define READ_CD 0xbe #define SEND_DVD_STRUCTURE 0xbf +const char *scsi_command_name(uint8_t cmd); + /* * SERVICE ACTION IN subcodes */ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index fa9d99792a..ca7a0bdd1a 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -268,6 +268,10 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); #define PC_COMPAT_2_0 \ {\ + .driver = "virtio-scsi-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ .driver = "apic",\ .property = "version",\ .value = stringify(0x11),\ diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index 42b102487a..a8f618578b 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -84,14 +84,13 @@ #define VIRTIO_SCSI_EVT_RESET_RESCAN 1 #define VIRTIO_SCSI_EVT_RESET_REMOVED 2 -/* SCSI command request, followed by data-out */ +/* SCSI command request, followed by CDB and data-out */ typedef struct { uint8_t lun[8]; /* Logical Unit Number */ uint64_t tag; /* Command identifier */ uint8_t task_attr; /* Task attribute */ uint8_t prio; uint8_t crn; - uint8_t cdb[]; } QEMU_PACKED VirtIOSCSICmdReq; /* Response, followed by sense data and data-in */ @@ -101,7 +100,6 @@ typedef struct { uint16_t status_qualifier; /* Status qualifier */ uint8_t status; /* Command completion status */ uint8_t response; /* Response values */ - uint8_t sense[]; } QEMU_PACKED VirtIOSCSICmdResp; /* Task Management Request */ @@ -186,7 +184,12 @@ typedef struct { DEFINE_PROP_BIT("param_change", _state, _feature_field, \ VIRTIO_SCSI_F_CHANGE, true) -void virtio_scsi_common_realize(DeviceState *dev, Error **errp); +typedef void (*HandleOutput)(VirtIODevice *, VirtQueue *); + +void virtio_scsi_common_realize(DeviceState *dev, Error **errp, + HandleOutput ctrl, HandleOutput evt, + HandleOutput cmd); + void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp); #endif /* _QEMU_VIRTIO_SCSI_H */ diff --git a/include/qemu-common.h b/include/qemu-common.h index 66ceceb2ad..ae76197532 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -315,9 +315,9 @@ void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t soffset, size_t sbytes); -void qemu_iovec_concat_iov(QEMUIOVector *dst, - struct iovec *src_iov, unsigned int src_cnt, - size_t soffset, size_t sbytes); +size_t qemu_iovec_concat_iov(QEMUIOVector *dst, + struct iovec *src_iov, unsigned int src_cnt, + size_t soffset, size_t sbytes); bool qemu_iovec_is_zero(QEMUIOVector *qiov); void qemu_iovec_destroy(QEMUIOVector *qiov); void qemu_iovec_reset(QEMUIOVector *qiov); diff --git a/util/iov.c b/util/iov.c index 49f88388f8..2b4f46da75 100644 --- a/util/iov.c +++ b/util/iov.c @@ -295,15 +295,15 @@ void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len) * of src". * Only vector pointers are processed, not the actual data buffers. */ -void qemu_iovec_concat_iov(QEMUIOVector *dst, - struct iovec *src_iov, unsigned int src_cnt, - size_t soffset, size_t sbytes) +size_t qemu_iovec_concat_iov(QEMUIOVector *dst, + struct iovec *src_iov, unsigned int src_cnt, + size_t soffset, size_t sbytes) { int i; size_t done; if (!sbytes) { - return; + return 0; } assert(dst->nalloc != -1); for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) { @@ -317,6 +317,8 @@ void qemu_iovec_concat_iov(QEMUIOVector *dst, } } assert(soffset == 0); /* offset beyond end of src */ + + return done; } /* |