From d7a4b5a1360418b94d67bde2ba2d450803ac9066 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 22 Feb 2012 14:12:27 +0100 Subject: [PATCH 071/109] scsi: push request restart to SCSIDevice RH-Author: Paolo Bonzini Message-id: <1329919979-20948-71-git-send-email-pbonzini@redhat.com> Patchwork-id: 37551 O-Subject: [RHEL 6.3 qemu-kvm PATCH v2 070/102] scsi: push request restart to SCSIDevice Bugzilla: 782029 RH-Acked-by: Laszlo Ersek RH-Acked-by: Orit Wasserman RH-Acked-by: Gerd Hoffmann The request restart mechanism is generic and could be reused for scsi-generic. In the meanwhile, pushing it to SCSIDevice avoids that scsi_dma_restart_bh looks at SCSIGenericReqs when working on a scsi-block device. The code is the same that is already in hw/scsi-disk.c, with the type flags replaced by req->cmd.mode and a more generic way to requeue SCSI_XFER_NONE commands. I also added a missing call to qemu_del_vm_change_state_handler. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf (cherry picked from 71544d30a6f8b91574552a61cd5bd122a77e82d1) Conflicts: hw/scsi-disk.c (reconcile differences between previous RHEL commits and what actually had gone in upstream) hw/scsi.h (trivial) --- hw/scsi-bus.c | 56 +++++++++++++++++++++++++++++++++++++++++ hw/scsi-disk.c | 76 +++++-------------------------------------------------- hw/scsi.h | 5 +++ 3 files changed, 68 insertions(+), 69 deletions(-) Signed-off-by: Michal Novotny --- hw/scsi-bus.c | 56 +++++++++++++++++++++++++++++++++++++++++ hw/scsi-disk.c | 76 +++++-------------------------------------------------- hw/scsi.h | 5 +++ 3 files changed, 68 insertions(+), 69 deletions(-) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index fd41f42..9617fde 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -8,6 +8,7 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev); static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf); +static void scsi_req_dequeue(SCSIRequest *req); static int scsi_build_sense(uint8_t *in_buf, int in_len, uint8_t *buf, int len, bool fixed); @@ -33,6 +34,53 @@ void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info) bus->qbus.allow_hotplug = 1; } +static void scsi_dma_restart_bh(void *opaque) +{ + SCSIDevice *s = opaque; + SCSIRequest *req, *next; + + qemu_bh_delete(s->bh); + s->bh = NULL; + + QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { + scsi_req_ref(req); + if (req->retry) { + req->retry = false; + switch (req->cmd.mode) { + case SCSI_XFER_FROM_DEV: + case SCSI_XFER_TO_DEV: + scsi_req_continue(req); + break; + case SCSI_XFER_NONE: + scsi_req_dequeue(req); + scsi_req_enqueue(req); + break; + } + } + scsi_req_unref(req); + } +} + +void scsi_req_retry(SCSIRequest *req) +{ + /* No need to save a reference, because scsi_dma_restart_bh just + * looks at the request list. */ + req->retry = true; +} + +static void scsi_dma_restart_cb(void *opaque, int running, RunState state) +{ + SCSIDevice *s = opaque; + + if (!running) { + return; + } + if (!s->bh) { + s->bh = qemu_bh_new(scsi_dma_restart_bh, s); + qemu_bh_schedule(s->bh); + } +} + static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base) { SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev); @@ -83,6 +131,10 @@ static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base) dev->info = info; QTAILQ_INIT(&dev->requests); rc = dev->info->init(dev); + if (rc == 0) { + dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb, + dev); + } err: return rc; @@ -92,6 +144,9 @@ static int scsi_qdev_exit(DeviceState *qdev) { SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev); + if (dev->vmsentry) { + qemu_del_vm_change_state_handler(dev->vmsentry); + } if (dev->info->destroy) { dev->info->destroy(dev); } @@ -589,6 +644,7 @@ int32_t scsi_req_enqueue(SCSIRequest *req) static void scsi_req_dequeue(SCSIRequest *req) { trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); + req->retry = false; if (req->enqueued) { QTAILQ_REMOVE(&req->dev->requests, req, next); req->enqueued = false; diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 0b94f9e..59d1a79 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -41,12 +41,6 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #define SCSI_DMA_BUF_SIZE 131072 #define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_REQ_STATUS_RETRY 0x01 -#define SCSI_REQ_STATUS_RETRY_TYPE_MASK 0x06 -#define SCSI_REQ_STATUS_RETRY_READ 0x00 -#define SCSI_REQ_STATUS_RETRY_WRITE 0x02 -#define SCSI_REQ_STATUS_RETRY_FLUSH 0x04 - typedef struct SCSIDiskState SCSIDiskState; typedef struct SCSIDiskReq { @@ -57,7 +51,6 @@ typedef struct SCSIDiskReq { uint32_t buflen; struct iovec iov; QEMUIOVector qiov; - uint32_t status; BlockAcctCookie acct; } SCSIDiskReq; @@ -75,8 +68,7 @@ struct SCSIDiskState bool tray_locked; }; -static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); -static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf); +static int scsi_handle_rw_error(SCSIDiskReq *r, int error); static void scsi_free_request(SCSIRequest *req) { @@ -102,7 +94,6 @@ static void scsi_cancel_io(SCSIRequest *req) SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); DPRINTF("Cancel tag=0x%x\n", req->tag); - r->status &= ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK); if (r->req.aiocb) { bdrv_aio_cancel(r->req.aiocb); @@ -139,7 +130,7 @@ static void scsi_read_complete(void * opaque, int ret) } if (ret) { - if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_READ)) { + if (scsi_handle_rw_error(r, -ret)) { goto done; } } @@ -168,7 +159,7 @@ static void scsi_flush_complete(void * opaque, int ret) } if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_FLUSH)) { + if (scsi_handle_rw_error(r, -ret)) { goto done; } } @@ -232,9 +223,9 @@ static void scsi_read_data(SCSIRequest *req) * scsi_handle_rw_error always manages its reference counts, independent * of the return value. */ -static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) +static int scsi_handle_rw_error(SCSIDiskReq *r, int error) { - int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); + int is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); BlockErrorAction action = bdrv_get_on_error(s->qdev.conf.bs, is_read); @@ -246,15 +237,9 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC) || action == BLOCK_ERR_STOP_ANY) { - type &= SCSI_REQ_STATUS_RETRY_TYPE_MASK; - r->status |= SCSI_REQ_STATUS_RETRY | type; - bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_STOP, error, is_read); vm_stop(RUN_STATE_IO_ERROR); - - /* Keep a reference until the request completes; the corresponding - * unref is in scsi_dma_restart_bh and scsi_cancel_io. */ - scsi_req_ref(&r->req); + scsi_req_retry(&r->req); } else { switch (error) { case ENOMEDIUM: @@ -287,7 +272,7 @@ static void scsi_write_complete(void * opaque, int ret) } if (ret) { - if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_WRITE)) { + if (scsi_handle_rw_error(r, -ret)) { goto done; } } @@ -344,52 +329,6 @@ static void scsi_write_data(SCSIRequest *req) } } -static void scsi_dma_restart_bh(void *opaque) -{ - SCSIDiskState *s = opaque; - SCSIRequest *req; - SCSIDiskReq *r; - - qemu_bh_delete(s->bh); - s->bh = NULL; - - QTAILQ_FOREACH(req, &s->qdev.requests, next) { - r = DO_UPCAST(SCSIDiskReq, req, req); - if (r->status & SCSI_REQ_STATUS_RETRY) { - int status = r->status; - - r->status &= - ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK); - - switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) { - case SCSI_REQ_STATUS_RETRY_READ: - scsi_read_data(&r->req); - break; - case SCSI_REQ_STATUS_RETRY_WRITE: - scsi_write_data(&r->req); - break; - case SCSI_REQ_STATUS_RETRY_FLUSH: - scsi_send_command(&r->req, r->req.cmd.buf); - break; - } - scsi_req_unref(&r->req); - } - } -} - -static void scsi_dma_restart_cb(void *opaque, int running, RunState state) -{ - SCSIDiskState *s = opaque; - - if (!running) { - return; - } - if (!s->bh) { - s->bh = qemu_bh_new(scsi_dma_restart_bh, s); - qemu_bh_schedule(s->bh); - } -} - /* Return a pointer to the data buffer. */ static uint8_t *scsi_get_buf(SCSIRequest *req) { @@ -1608,7 +1547,6 @@ static int scsi_initfn(SCSIDevice *dev) } s->qdev.conf.bs->buffer_alignment = s->qdev.blocksize; - qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0"); return 0; } diff --git a/hw/scsi.h b/hw/scsi.h index 86f3870..801e36d 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -4,6 +4,7 @@ #include "qdev.h" #include "block.h" #include "block_int.h" +#include "sysemu.h" #define MAX_SCSI_DEVS 255 @@ -53,6 +54,7 @@ struct SCSIRequest { uint32_t sense_len; bool enqueued; bool io_canceled; + bool retry; void *hba_private; QTAILQ_ENTRY(SCSIRequest) next; }; @@ -60,6 +62,8 @@ struct SCSIRequest { struct SCSIDevice { DeviceState qdev; + VMChangeStateEntry *vmsentry; + QEMUBH *bh; uint32_t id; BlockConf conf; SCSIDeviceInfo *info; @@ -195,6 +199,7 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req); int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len); void scsi_req_abort(SCSIRequest *req, int status); void scsi_req_cancel(SCSIRequest *req); +void scsi_req_retry(SCSIRequest *req); void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense); int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); -- 1.7.7.6