diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/scsi/scsi-disk.c | 23 | ||||
-rw-r--r-- | hw/scsi/scsi-generic.c | 11 | ||||
-rw-r--r-- | hw/scsi/virtio-scsi.c | 169 |
3 files changed, 154 insertions, 49 deletions
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index d4e360850f..97c9b1c8cd 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -273,9 +273,11 @@ static void scsi_aio_complete(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + if (scsi_disk_req_check_error(r, ret, true)) { goto done; } @@ -352,6 +354,7 @@ done: scsi_req_unref(&r->req); } +/* Called with AioContext lock held */ static void scsi_dma_complete(void *opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -360,14 +363,12 @@ static void scsi_dma_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); } else { block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); } scsi_dma_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); } static void scsi_read_complete_noio(SCSIDiskReq *r, int ret) @@ -393,10 +394,11 @@ static void scsi_read_complete(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); } else { @@ -446,10 +448,11 @@ static void scsi_do_read_cb(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + assert (r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); } else { @@ -530,10 +533,11 @@ static void scsi_write_complete(void * opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + assert (r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); } else { @@ -1737,10 +1741,11 @@ static void scsi_unmap_complete(void *opaque, int ret) SCSIDiskReq *r = data->r; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); if (scsi_disk_req_check_error(r, ret, true)) { scsi_req_unref(&r->req); g_free(data); @@ -1816,9 +1821,11 @@ static void scsi_write_same_complete(void *opaque, int ret) SCSIDiskReq *r = data->r; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + if (scsi_disk_req_check_error(r, ret, true)) { goto done; } diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 92cce20a4d..ac9fa662b4 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -111,10 +111,11 @@ static void scsi_command_complete(void *opaque, int ret) SCSIGenericReq *r = (SCSIGenericReq *)opaque; SCSIDevice *s = r->req.dev; + aio_context_acquire(blk_get_aio_context(s->conf.blk)); + assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->conf.blk)); scsi_command_complete_noio(r, ret); aio_context_release(blk_get_aio_context(s->conf.blk)); } @@ -269,11 +270,11 @@ static void scsi_read_complete(void * opaque, int ret) SCSIDevice *s = r->req.dev; int len; + aio_context_acquire(blk_get_aio_context(s->conf.blk)); + assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->conf.blk)); - if (ret || r->req.io_canceled) { scsi_command_complete_noio(r, ret); goto done; @@ -386,11 +387,11 @@ static void scsi_write_complete(void * opaque, int ret) trace_scsi_generic_write_complete(ret); + aio_context_acquire(blk_get_aio_context(s->conf.blk)); + assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->conf.blk)); - if (ret || r->req.io_canceled) { scsi_command_complete_noio(r, ret); goto done; diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 2b649ca976..612c525d9d 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -43,13 +43,11 @@ typedef struct VirtIOSCSIReq { QEMUSGList qsgl; QEMUIOVector resp_iov; - union { - /* Used for two-stage request submission */ - QTAILQ_ENTRY(VirtIOSCSIReq) next; + /* Used for two-stage request submission and TMFs deferred to BH */ + QTAILQ_ENTRY(VirtIOSCSIReq) next; - /* Used for cancellation of request during TMFs */ - int remaining; - }; + /* Used for cancellation of request during TMFs */ + int remaining; SCSIRequest *sreq; size_t resp_size; @@ -294,6 +292,122 @@ static inline void virtio_scsi_ctx_check(VirtIOSCSI *s, SCSIDevice *d) } } +static void virtio_scsi_do_one_tmf_bh(VirtIOSCSIReq *req) +{ + VirtIOSCSI *s = req->dev; + SCSIDevice *d = virtio_scsi_device_get(s, req->req.tmf.lun); + BusChild *kid; + int target; + + switch (req->req.tmf.subtype) { + case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: + if (!d) { + req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET; + goto out; + } + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { + req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN; + goto out; + } + qatomic_inc(&s->resetting); + device_cold_reset(&d->qdev); + qatomic_dec(&s->resetting); + break; + + case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: + target = req->req.tmf.lun[1]; + qatomic_inc(&s->resetting); + + rcu_read_lock(); + QTAILQ_FOREACH_RCU(kid, &s->bus.qbus.children, sibling) { + SCSIDevice *d1 = SCSI_DEVICE(kid->child); + if (d1->channel == 0 && d1->id == target) { + device_cold_reset(&d1->qdev); + } + } + rcu_read_unlock(); + + qatomic_dec(&s->resetting); + break; + + default: + g_assert_not_reached(); + break; + } + +out: + object_unref(OBJECT(d)); + + virtio_scsi_acquire(s); + virtio_scsi_complete_req(req); + virtio_scsi_release(s); +} + +/* Some TMFs must be processed from the main loop thread */ +static void virtio_scsi_do_tmf_bh(void *opaque) +{ + VirtIOSCSI *s = opaque; + QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); + VirtIOSCSIReq *req; + VirtIOSCSIReq *tmp; + + GLOBAL_STATE_CODE(); + + virtio_scsi_acquire(s); + + QTAILQ_FOREACH_SAFE(req, &s->tmf_bh_list, next, tmp) { + QTAILQ_REMOVE(&s->tmf_bh_list, req, next); + QTAILQ_INSERT_TAIL(&reqs, req, next); + } + + qemu_bh_delete(s->tmf_bh); + s->tmf_bh = NULL; + + virtio_scsi_release(s); + + QTAILQ_FOREACH_SAFE(req, &reqs, next, tmp) { + QTAILQ_REMOVE(&reqs, req, next); + virtio_scsi_do_one_tmf_bh(req); + } +} + +static void virtio_scsi_reset_tmf_bh(VirtIOSCSI *s) +{ + VirtIOSCSIReq *req; + VirtIOSCSIReq *tmp; + + GLOBAL_STATE_CODE(); + + virtio_scsi_acquire(s); + + if (s->tmf_bh) { + qemu_bh_delete(s->tmf_bh); + s->tmf_bh = NULL; + } + + QTAILQ_FOREACH_SAFE(req, &s->tmf_bh_list, next, tmp) { + QTAILQ_REMOVE(&s->tmf_bh_list, req, next); + + /* SAM-6 6.3.2 Hard reset */ + req->resp.tmf.response = VIRTIO_SCSI_S_TARGET_FAILURE; + virtio_scsi_complete_req(req); + } + + virtio_scsi_release(s); +} + +static void virtio_scsi_defer_tmf_to_bh(VirtIOSCSIReq *req) +{ + VirtIOSCSI *s = req->dev; + + QTAILQ_INSERT_TAIL(&s->tmf_bh_list, req, next); + + if (!s->tmf_bh) { + s->tmf_bh = qemu_bh_new(virtio_scsi_do_tmf_bh, s); + qemu_bh_schedule(s->tmf_bh); + } +} + /* Return 0 if the request is ready to be completed and return to guest; * -EINPROGRESS if the request is submitted and will be completed later, in the * case of async cancellation. */ @@ -301,8 +415,6 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) { SCSIDevice *d = virtio_scsi_device_get(s, req->req.tmf.lun); SCSIRequest *r, *next; - BusChild *kid; - int target; int ret = 0; virtio_scsi_ctx_check(s, d); @@ -359,15 +471,9 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) break; case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: - if (!d) { - goto fail; - } - if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { - goto incorrect_lun; - } - s->resetting++; - device_cold_reset(&d->qdev); - s->resetting--; + case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: + virtio_scsi_defer_tmf_to_bh(req); + ret = -EINPROGRESS; break; case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: @@ -410,22 +516,6 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) } break; - case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: - target = req->req.tmf.lun[1]; - s->resetting++; - - rcu_read_lock(); - QTAILQ_FOREACH_RCU(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *d1 = SCSI_DEVICE(kid->child); - if (d1->channel == 0 && d1->id == target) { - device_cold_reset(&d1->qdev); - } - } - rcu_read_unlock(); - - s->resetting--; - break; - case VIRTIO_SCSI_T_TMF_CLEAR_ACA: default: req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_REJECTED; @@ -655,7 +745,7 @@ static void virtio_scsi_request_cancelled(SCSIRequest *r) if (!req) { return; } - if (req->dev->resetting) { + if (qatomic_read(&req->dev->resetting)) { req->resp.cmd.response = VIRTIO_SCSI_S_RESET; } else { req->resp.cmd.response = VIRTIO_SCSI_S_ABORTED; @@ -831,9 +921,12 @@ static void virtio_scsi_reset(VirtIODevice *vdev) VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); assert(!s->dataplane_started); - s->resetting++; + + virtio_scsi_reset_tmf_bh(s); + + qatomic_inc(&s->resetting); bus_cold_reset(BUS(&s->bus)); - s->resetting--; + qatomic_dec(&s->resetting); vs->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE; vs->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE; @@ -1053,6 +1146,8 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) VirtIOSCSI *s = VIRTIO_SCSI(dev); Error *err = NULL; + QTAILQ_INIT(&s->tmf_bh_list); + virtio_scsi_common_realize(dev, virtio_scsi_handle_ctrl, virtio_scsi_handle_event, @@ -1090,6 +1185,8 @@ static void virtio_scsi_device_unrealize(DeviceState *dev) { VirtIOSCSI *s = VIRTIO_SCSI(dev); + virtio_scsi_reset_tmf_bh(s); + qbus_set_hotplug_handler(BUS(&s->bus), NULL); virtio_scsi_common_unrealize(dev); } |