summary refs log tree commit diff stats
path: root/hw/scsi-bus.c
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2011-10-25 12:53:36 +0200
committerKevin Wolf <kwolf@redhat.com>2011-10-28 19:25:52 +0200
commit71544d30a6f8b91574552a61cd5bd122a77e82d1 (patch)
tree7c39af913f1105f9561b75895873b69d72e6b4bb /hw/scsi-bus.c
parentc9501c951c3dbe007dfba9328156be2d931f6d94 (diff)
downloadfocaccia-qemu-71544d30a6f8b91574552a61cd5bd122a77e82d1.tar.gz
focaccia-qemu-71544d30a6f8b91574552a61cd5bd122a77e82d1.zip
scsi: push request restart to SCSIDevice
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 <pbonzini@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Diffstat (limited to 'hw/scsi-bus.c')
-rw-r--r--hw/scsi-bus.c56
1 files changed, 56 insertions, 0 deletions
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index dfce5fbeeb..e6ebbd594e 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);
     }
@@ -586,6 +641,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;