[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH linux-2.6.18] support suspend/resume in pvscsi drivers



Up to now the pvscsi drivers haven't supported domain suspend and
resume. When a domain with an assigned pvscsi device was suspended
and resumed again, it was not able to use the device any more: trying
to do so resulted in hanging processes.

Support suspend and resume of pvscsi devices.

Signed-off-by: Juergen Gross <jgross@xxxxxxxx>

diff -r 578e5aea3cbb drivers/xen/scsiback/xenbus.c
--- a/drivers/xen/scsiback/xenbus.c     Mon Jan 19 11:51:46 2015 +0100
+++ b/drivers/xen/scsiback/xenbus.c     Fri Jan 30 13:57:29 2015 +0100
@@ -167,33 +167,48 @@ static void scsiback_do_lun_hotplug(stru
 
                switch (op) {
                case VSCSIBACK_OP_ADD_OR_DEL_LUN:
-                       if (device_state == XenbusStateInitialising) {
+                       switch (device_state) {
+                       case XenbusStateInitialising:
+                       case XenbusStateConnected:
                                sdev = scsiback_get_scsi_device(&phy);
-                               if (!sdev)
-                                       xenbus_printf(XBT_NIL, dev->nodename, 
state_str, 
-                                                           "%d", 
XenbusStateClosed);
-                               else {
-                                       err = 
scsiback_add_translation_entry(be->info, sdev, &vir);
-                                       if (!err) {
-                                               if (xenbus_printf(XBT_NIL, 
dev->nodename, state_str, 
-                                                                   "%d", 
XenbusStateInitialised)) {
-                                                       printk(KERN_ERR 
"scsiback: xenbus_printf error %s\n", state_str);
-                                                       
scsiback_del_translation_entry(be->info, &vir);
-                                               }
-                                       } else {
-                                               scsi_device_put(sdev);
-                                               xenbus_printf(XBT_NIL, 
dev->nodename, state_str, 
-                                                                   "%d", 
XenbusStateClosed);
-                                       }
+                               if (!sdev) {
+                                       xenbus_printf(XBT_NIL, dev->nodename,
+                                                     state_str,
+                                                     "%d", XenbusStateClosed);
+                                       break;
                                }
-                       }
+                               if (scsiback_add_translation_entry(be->info,
+                                                               sdev, &vir)) {
+                                       scsi_device_put(sdev);
+                                       if (device_state == 
XenbusStateConnected)
+                                               break;
+                                       xenbus_printf(XBT_NIL, dev->nodename,
+                                                     state_str,
+                                                     "%d", XenbusStateClosed);
+                                       break;
+                               }
+                               if (!xenbus_printf(XBT_NIL, dev->nodename,
+                                                 state_str, "%d",
+                                                 XenbusStateInitialised))
+                                       break;
+                               printk(KERN_ERR "scsiback: xenbus_printf error 
%s\n",
+                                      state_str);
+                               scsiback_del_translation_entry(be->info, &vir);
+                               break;
 
-                       if (device_state == XenbusStateClosing) {
-                               if (!scsiback_del_translation_entry(be->info, 
&vir)) {
-                                       if (xenbus_printf(XBT_NIL, 
dev->nodename, state_str, 
-                                                           "%d", 
XenbusStateClosed))
-                                               printk(KERN_ERR "scsiback: 
xenbus_printf error %s\n", state_str);
-                               }
+                       case XenbusStateClosing:
+                               if (scsiback_del_translation_entry(be->info,
+                                                                  &vir))
+                                       break;
+                               if (xenbus_printf(XBT_NIL, dev->nodename,
+                                                 state_str, "%d",
+                                                 XenbusStateClosed))
+                                       printk(KERN_ERR "scsiback: 
xenbus_printf error %s\n",
+                                              state_str);
+                               break;
+
+                       default:
+                               break;
                        }
                        break;
 
diff -r 578e5aea3cbb drivers/xen/scsifront/common.h
--- a/drivers/xen/scsifront/common.h    Mon Jan 19 11:51:46 2015 +0100
+++ b/drivers/xen/scsifront/common.h    Fri Jan 30 13:57:29 2015 +0100
@@ -75,7 +75,9 @@
 
 struct vscsifrnt_shadow {
        uint16_t next_free;
-       
+#define VSCSIIF_IN_USE         0x0fff
+#define VSCSIIF_NONE           0x0ffe
+
        /* command between backend and frontend
         * VSCSIIF_ACT_SCSI_CDB or VSCSIIF_ACT_SCSI_RESET */
        unsigned char act;
@@ -113,8 +115,12 @@ struct vscsifrnt_info {
        struct task_struct *kthread;
        wait_queue_head_t wq;
        wait_queue_head_t wq_sync;
+       wait_queue_head_t wq_pause;
        unsigned char waiting_resp;
        unsigned char waiting_sync;
+       unsigned char waiting_pause;
+       unsigned char pause;
+       unsigned callers;
 };
 
 #define DPRINTK(_f, _a...)                             \
@@ -124,6 +130,7 @@ struct vscsifrnt_info {
 int scsifront_xenbus_init(void);
 void scsifront_xenbus_unregister(void);
 int scsifront_schedule(void *data);
+void scsifront_finish_all(struct vscsifrnt_info *info);
 irqreturn_t scsifront_intr(int irq, void *dev_id, struct pt_regs *ptregs);
 
 
diff -r 578e5aea3cbb drivers/xen/scsifront/scsifront.c
--- a/drivers/xen/scsifront/scsifront.c Mon Jan 19 11:51:46 2015 +0100
+++ b/drivers/xen/scsifront/scsifront.c Fri Jan 30 13:57:29 2015 +0100
@@ -40,9 +40,9 @@ static int get_id_from_freelist(struct v
        spin_lock_irqsave(&info->shadow_lock, flags);
 
        free = info->shadow_free;
-       BUG_ON(free > VSCSIIF_MAX_REQS);
+       BUG_ON(free >= VSCSIIF_MAX_REQS);
        info->shadow_free = info->shadow[free].next_free;
-       info->shadow[free].next_free = 0x0fff;
+       info->shadow[free].next_free = VSCSIIF_IN_USE;
 
        info->shadow[free].wait_reset = 0;
 
@@ -188,27 +188,26 @@ static void scsifront_sync_cmd_done(stru
        wake_up(&(info->shadow[id].wq_reset));
 }
 
+static void scsifront_do_response(struct vscsifrnt_info *info,
+                                 vscsiif_response_t *ring_res)
+{
+       if (info->shadow[ring_res->rqid].act == VSCSIIF_ACT_SCSI_CDB)
+               scsifront_cdb_cmd_done(info, ring_res);
+       else
+               scsifront_sync_cmd_done(info, ring_res);
+}
 
-static int scsifront_cmd_done(struct vscsifrnt_info *info)
+static int scsifront_ring_drain(struct vscsifrnt_info *info)
 {
        vscsiif_response_t *ring_res;
-
        RING_IDX i, rp;
        int more_to_do = 0;
-       unsigned long flags;
-
-       spin_lock_irqsave(info->host->host_lock, flags);
 
        rp = info->ring.sring->rsp_prod;
        rmb();
        for (i = info->ring.rsp_cons; i != rp; i++) {
-               
                ring_res = RING_GET_RESPONSE(&info->ring, i);
-
-               if (info->shadow[ring_res->rqid].act == VSCSIIF_ACT_SCSI_CDB)
-                       scsifront_cdb_cmd_done(info, ring_res);
-               else
-                       scsifront_sync_cmd_done(info, ring_res);
+               scsifront_do_response(info, ring_res);
        }
 
        info->ring.rsp_cons = i;
@@ -219,6 +218,18 @@ static int scsifront_cmd_done(struct vsc
                info->ring.sring->rsp_event = i + 1;
        }
 
+       return more_to_do;
+}
+
+static int scsifront_cmd_done(struct vscsifrnt_info *info)
+{
+       int more_to_do;
+       unsigned long flags;
+
+       spin_lock_irqsave(info->host->host_lock, flags);
+
+       more_to_do = scsifront_ring_drain(info);
+
        info->waiting_sync = 0;
 
        spin_unlock_irqrestore(info->host->host_lock, flags);
@@ -231,8 +242,23 @@ static int scsifront_cmd_done(struct vsc
        return more_to_do;
 }
 
+void scsifront_finish_all(struct vscsifrnt_info *info)
+{
+       unsigned i;
+       struct vscsiif_response resp;
 
+       scsifront_ring_drain(info);
 
+       for (i = 0; i < VSCSIIF_MAX_REQS; i++) {
+               if (info->shadow[i].next_free != VSCSIIF_IN_USE)
+                       continue;
+               resp.rqid = i;
+               resp.sense_len = 0;
+               resp.rslt = DID_RESET << 16;
+               resp.residual_len = 0;
+               scsifront_do_response(info, &resp);
+       }
+}
 
 int scsifront_schedule(void *data)
 {
@@ -359,6 +385,27 @@ big_to_sg:
        return ref_cnt;
 }
 
+static int scsifront_enter(struct vscsifrnt_info *info)
+{
+       if (info->pause)
+               return 1;
+       info->callers++;
+       return 0;
+}
+
+static void scsifront_return(struct vscsifrnt_info *info)
+{
+       info->callers--;
+       if (info->callers)
+               return;
+
+       if (!info->waiting_pause)
+               return;
+
+       info->waiting_pause = 0;
+       wake_up(&info->wq_pause);
+}
+
 static int scsifront_queuecommand(struct scsi_cmnd *sc,
                                  void (*done)(struct scsi_cmnd *))
 {
@@ -368,6 +415,9 @@ static int scsifront_queuecommand(struct
        int ref_cnt;
        uint16_t rqid;
 
+       if (scsifront_enter(info))
+               return SCSI_MLQUEUE_HOST_BUSY;
+
 /* debug printk to identify more missing scsi commands
        shost_printk(KERN_INFO "scsicmd: ", sc->device->host,
                     "len=%u %#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x\n",
@@ -420,13 +470,16 @@ static int scsifront_queuecommand(struct
 
        scsifront_do_request(info);
 
+       scsifront_return(info);
        return 0;
 
 out_host_busy:
+       scsifront_return(info);
        return SCSI_MLQUEUE_HOST_BUSY;
 
 out_fail_command:
        done(sc);
+       scsifront_return(info);
        return 0;
 }
 
@@ -451,7 +504,7 @@ static int scsifront_dev_reset_handler(s
        spin_lock_irq(host->host_lock);
 #endif
        while (RING_FULL(&info->ring)) {
-               if (err) {
+               if (err || info->pause) {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
                        spin_unlock_irq(host->host_lock);
 #endif
@@ -464,6 +517,11 @@ static int scsifront_dev_reset_handler(s
                spin_lock_irq(host->host_lock);
        }
 
+       if (scsifront_enter(info)) {
+               spin_unlock_irq(host->host_lock);
+               return FAILED;
+       }
+
        ring_req      = scsifront_pre_request(info);
        ring_req->act = VSCSIIF_ACT_SCSI_RESET;
 
@@ -502,6 +560,8 @@ static int scsifront_dev_reset_handler(s
                err = FAILED;
        }
 
+       scsifront_return(info);
+
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
        spin_unlock_irq(host->host_lock);
 #endif
diff -r 578e5aea3cbb drivers/xen/scsifront/xenbus.c
--- a/drivers/xen/scsifront/xenbus.c    Mon Jan 19 11:51:46 2015 +0100
+++ b/drivers/xen/scsifront/xenbus.c    Fri Jan 30 13:57:29 2015 +0100
@@ -43,6 +43,20 @@
 
 extern struct scsi_host_template scsifront_sht;
 
+static void scsifront_free_ring(struct vscsifrnt_info *info)
+{
+       if (info->ring_ref != GRANT_INVALID_REF) {
+               gnttab_end_foreign_access(info->ring_ref,
+                                       (unsigned long)info->ring.sring);
+               info->ring_ref = GRANT_INVALID_REF;
+               info->ring.sring = NULL;
+       }
+
+       if (info->irq)
+               unbind_from_irqhandler(info->irq, info);
+       info->irq = 0;
+}
+
 static void scsifront_free(struct vscsifrnt_info *info)
 {
        struct Scsi_Host *host = info->host;
@@ -55,16 +69,7 @@ static void scsifront_free(struct vscsif
                scsi_remove_host(info->host);
        }
 
-       if (info->ring_ref != GRANT_INVALID_REF) {
-               gnttab_end_foreign_access(info->ring_ref,
-                                       (unsigned long)info->ring.sring);
-               info->ring_ref = GRANT_INVALID_REF;
-               info->ring.sring = NULL;
-       }
-
-       if (info->irq)
-               unbind_from_irqhandler(info->irq, info);
-       info->irq = 0;
+       scsifront_free_ring(info);
 
        scsi_host_put(info->host);
 }
@@ -196,7 +201,7 @@ static int scsifront_probe(struct xenbus
                init_waitqueue_head(&(info->shadow[i].wq_reset));
                info->shadow[i].wait_reset = 0;
        }
-       info->shadow[VSCSIIF_MAX_REQS - 1].next_free = 0x0fff;
+       info->shadow[VSCSIIF_MAX_REQS - 1].next_free = VSCSIIF_NONE;
 
        err = scsifront_init_ring(info);
        if (err) {
@@ -208,6 +213,11 @@ static int scsifront_probe(struct xenbus
        init_waitqueue_head(&info->wq_sync);
        spin_lock_init(&info->shadow_lock);
 
+       info->pause = 0;
+       info->callers = 0;
+       info->waiting_pause = 0;
+       init_waitqueue_head(&info->wq_pause);
+
        snprintf(name, DEFAULT_TASK_COMM_LEN, "vscsiif.%d", 
info->host->host_no);
 
        info->kthread = kthread_run(scsifront_schedule, info, name);
@@ -240,6 +250,66 @@ free_sring:
        return err;
 }
 
+static int scsifront_resume(struct xenbus_device *dev)
+{
+       struct vscsifrnt_info *info = dev->dev.driver_data;
+       struct Scsi_Host *host = info->host;
+       int err;
+
+       spin_lock_irq(host->host_lock);
+
+       /* finish all still pending commands */
+       scsifront_finish_all(info);
+
+       spin_unlock_irq(host->host_lock);
+
+       /* reconnect to dom0 */
+       scsifront_free_ring(info);
+       err = scsifront_init_ring(info);
+       if (err) {
+               dev_err(&dev->dev, "fail to resume %d\n", err);
+               scsifront_free(info);
+               return err;
+       }
+
+       xenbus_switch_state(dev, XenbusStateInitialised);
+
+       return 0;
+}
+
+static int scsifront_suspend(struct xenbus_device *dev)
+{
+       struct vscsifrnt_info *info = dev->dev.driver_data;
+       struct Scsi_Host *host = info->host;
+       int err = 0;
+
+       /* no new commands for the backend */
+       spin_lock_irq(host->host_lock);
+       info->pause = 1;
+       while (info->callers && !err) {
+               info->waiting_pause = 1;
+               info->waiting_sync = 0;
+               spin_unlock_irq(host->host_lock);
+               wake_up(&info->wq_sync);
+               err = wait_event_interruptible(info->wq_pause,
+                                              !info->waiting_pause);
+               spin_lock_irq(host->host_lock);
+       }
+       spin_unlock_irq(host->host_lock);
+       return err;
+}
+
+static int scsifront_suspend_cancel(struct xenbus_device *dev)
+{
+       struct vscsifrnt_info *info = dev->dev.driver_data;
+       struct Scsi_Host *host = info->host;
+
+       spin_lock_irq(host->host_lock);
+       info->pause = 0;
+       spin_unlock_irq(host->host_lock);
+       return 0;
+}
+
 static int scsifront_remove(struct xenbus_device *dev)
 {
        struct vscsifrnt_info *info = dev->dev.driver_data;
@@ -278,6 +348,7 @@ static int scsifront_disconnect(struct v
 
 #define VSCSIFRONT_OP_ADD_LUN  1
 #define VSCSIFRONT_OP_DEL_LUN  2
+#define VSCSIFRONT_OP_READD_LUN        3
 
 static void scsifront_do_lun_hotplug(struct vscsifrnt_info *info, int op)
 {
@@ -339,6 +410,11 @@ static void scsifront_do_lun_hotplug(str
                                }
                        }
                        break;
+               case VSCSIFRONT_OP_READD_LUN:
+                       if (device_state == XenbusStateConnected)
+                               xenbus_printf(XBT_NIL, dev->nodename, state_str,
+                                             "%d", XenbusStateConnected);
+                       break;
                default:
                        break;
                }
@@ -366,6 +442,13 @@ static void scsifront_backend_changed(st
                break;
 
        case XenbusStateConnected:
+               if (info->pause) {
+                       scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_READD_LUN);
+                       xenbus_switch_state(dev, XenbusStateConnected);
+                       info->pause = 0;
+                       return;
+               }
+
                if (xenbus_read_driver_state(dev->nodename) ==
                        XenbusStateInitialised) {
                        scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN);
@@ -407,7 +490,9 @@ MODULE_ALIAS("xen:vscsi");
 static DEFINE_XENBUS_DRIVER(scsifront, ,
        .probe                  = scsifront_probe,
        .remove                 = scsifront_remove,
-/*     .resume                 = scsifront_resume, */
+       .resume                 = scsifront_resume,
+       .suspend                = scsifront_suspend,
+       .suspend_cancel         = scsifront_suspend_cancel,
        .otherend_changed       = scsifront_backend_changed,
 );
 

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.