Commit 2abb2746 authored by Aurelien Chartier's avatar Aurelien Chartier Committed by Konrad Rzeszutek Wilk

xenbus: delay xenbus frontend resume if xenstored is not running

If the xenbus frontend is located in a domain running xenstored, the device
resume is hanging because it is happening before the process resume. This
patch adds extra logic to the resume code to check if we are the domain
running xenstored and delay the resume if needed.
Signed-off-by: default avatarAurelien Chartier <aurelien.chartier@citrix.com>
[Changes in v2:
- Instead of bypassing the resume, process it in a workqueue]
[Changes in v3:
- Add a struct work in xenbus_device to avoid dynamic allocation
- Several small code fixes]
[Changes in v4:
- Use a dedicated workqueue]
[Changes in v5:
- Move create_workqueue error handling to xenbus_frontend_dev_resume]
Acked-by: default avatarJan Beulich <jbeulich@suse.com>
Signed-off-by: default avatarKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
parent 1d7004f0
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include "xenbus_probe.h" #include "xenbus_probe.h"
static struct workqueue_struct *xenbus_frontend_wq;
/* device/<type>/<id> => <type>-<id> */ /* device/<type>/<id> => <type>-<id> */
static int frontend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename) static int frontend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename)
{ {
...@@ -89,9 +91,40 @@ static void backend_changed(struct xenbus_watch *watch, ...@@ -89,9 +91,40 @@ static void backend_changed(struct xenbus_watch *watch,
xenbus_otherend_changed(watch, vec, len, 1); xenbus_otherend_changed(watch, vec, len, 1);
} }
static void xenbus_frontend_delayed_resume(struct work_struct *w)
{
struct xenbus_device *xdev = container_of(w, struct xenbus_device, work);
xenbus_dev_resume(&xdev->dev);
}
static int xenbus_frontend_dev_resume(struct device *dev)
{
/*
* If xenstored is running in this domain, we cannot access the backend
* state at the moment, so we need to defer xenbus_dev_resume
*/
if (xen_store_domain_type == XS_LOCAL) {
struct xenbus_device *xdev = to_xenbus_device(dev);
if (!xenbus_frontend_wq) {
pr_err("%s: no workqueue to process delayed resume\n",
xdev->nodename);
return -EFAULT;
}
INIT_WORK(&xdev->work, xenbus_frontend_delayed_resume);
queue_work(xenbus_frontend_wq, &xdev->work);
return 0;
}
return xenbus_dev_resume(dev);
}
static const struct dev_pm_ops xenbus_pm_ops = { static const struct dev_pm_ops xenbus_pm_ops = {
.suspend = xenbus_dev_suspend, .suspend = xenbus_dev_suspend,
.resume = xenbus_dev_resume, .resume = xenbus_frontend_dev_resume,
.freeze = xenbus_dev_suspend, .freeze = xenbus_dev_suspend,
.thaw = xenbus_dev_cancel, .thaw = xenbus_dev_cancel,
.restore = xenbus_dev_resume, .restore = xenbus_dev_resume,
...@@ -440,6 +473,8 @@ static int __init xenbus_probe_frontend_init(void) ...@@ -440,6 +473,8 @@ static int __init xenbus_probe_frontend_init(void)
register_xenstore_notifier(&xenstore_notifier); register_xenstore_notifier(&xenstore_notifier);
xenbus_frontend_wq = create_workqueue("xenbus_frontend");
return 0; return 0;
} }
subsys_initcall(xenbus_probe_frontend_init); subsys_initcall(xenbus_probe_frontend_init);
......
...@@ -70,6 +70,7 @@ struct xenbus_device { ...@@ -70,6 +70,7 @@ struct xenbus_device {
struct device dev; struct device dev;
enum xenbus_state state; enum xenbus_state state;
struct completion down; struct completion down;
struct work_struct work;
}; };
static inline struct xenbus_device *to_xenbus_device(struct device *dev) static inline struct xenbus_device *to_xenbus_device(struct device *dev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment