|
[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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |