162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#define DPRINTK(fmt, ...) \ 562306a36Sopenharmony_ci pr_debug("(%s:%d) " fmt "\n", \ 662306a36Sopenharmony_ci __func__, __LINE__, ##__VA_ARGS__) 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/string.h> 1162306a36Sopenharmony_ci#include <linux/ctype.h> 1262306a36Sopenharmony_ci#include <linux/fcntl.h> 1362306a36Sopenharmony_ci#include <linux/mm.h> 1462306a36Sopenharmony_ci#include <linux/proc_fs.h> 1562306a36Sopenharmony_ci#include <linux/notifier.h> 1662306a36Sopenharmony_ci#include <linux/kthread.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <asm/page.h> 2262306a36Sopenharmony_ci#include <asm/xen/hypervisor.h> 2362306a36Sopenharmony_ci#include <xen/xenbus.h> 2462306a36Sopenharmony_ci#include <xen/events.h> 2562306a36Sopenharmony_ci#include <xen/page.h> 2662306a36Sopenharmony_ci#include <xen/xen.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <xen/platform_pci.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "xenbus.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* device/<type>/<id> => <type>-<id> */ 3562306a36Sopenharmony_cistatic int frontend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci nodename = strchr(nodename, '/'); 3862306a36Sopenharmony_ci if (!nodename || strlen(nodename + 1) >= XEN_BUS_ID_SIZE) { 3962306a36Sopenharmony_ci pr_warn("bad frontend %s\n", nodename); 4062306a36Sopenharmony_ci return -EINVAL; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci strscpy(bus_id, nodename + 1, XEN_BUS_ID_SIZE); 4462306a36Sopenharmony_ci if (!strchr(bus_id, '/')) { 4562306a36Sopenharmony_ci pr_warn("bus_id %s no slash\n", bus_id); 4662306a36Sopenharmony_ci return -EINVAL; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci *strchr(bus_id, '/') = '-'; 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* device/<typename>/<name> */ 5362306a36Sopenharmony_cistatic int xenbus_probe_frontend(struct xen_bus_type *bus, const char *type, 5462306a36Sopenharmony_ci const char *name) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci char *nodename; 5762306a36Sopenharmony_ci int err; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* ignore console/0 */ 6062306a36Sopenharmony_ci if (!strncmp(type, "console", 7) && !strncmp(name, "0", 1)) { 6162306a36Sopenharmony_ci DPRINTK("Ignoring buggy device entry console/0"); 6262306a36Sopenharmony_ci return 0; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci nodename = kasprintf(GFP_KERNEL, "%s/%s/%s", bus->root, type, name); 6662306a36Sopenharmony_ci if (!nodename) 6762306a36Sopenharmony_ci return -ENOMEM; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci DPRINTK("%s", nodename); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci err = xenbus_probe_node(bus, type, nodename); 7262306a36Sopenharmony_ci kfree(nodename); 7362306a36Sopenharmony_ci return err; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int xenbus_uevent_frontend(const struct device *_dev, 7762306a36Sopenharmony_ci struct kobj_uevent_env *env) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci const struct xenbus_device *dev = to_xenbus_device(_dev); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (add_uevent_var(env, "MODALIAS=xen:%s", dev->devicetype)) 8262306a36Sopenharmony_ci return -ENOMEM; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void backend_changed(struct xenbus_watch *watch, 8962306a36Sopenharmony_ci const char *path, const char *token) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci xenbus_otherend_changed(watch, path, token, 1); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void xenbus_frontend_delayed_resume(struct work_struct *w) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct xenbus_device *xdev = container_of(w, struct xenbus_device, work); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci xenbus_dev_resume(&xdev->dev); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int xenbus_frontend_dev_resume(struct device *dev) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * If xenstored is running in this domain, we cannot access the backend 10562306a36Sopenharmony_ci * state at the moment, so we need to defer xenbus_dev_resume 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci if (xen_store_domain_type == XS_LOCAL) { 10862306a36Sopenharmony_ci struct xenbus_device *xdev = to_xenbus_device(dev); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci schedule_work(&xdev->work); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return xenbus_dev_resume(dev); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int xenbus_frontend_dev_probe(struct device *dev) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci if (xen_store_domain_type == XS_LOCAL) { 12162306a36Sopenharmony_ci struct xenbus_device *xdev = to_xenbus_device(dev); 12262306a36Sopenharmony_ci INIT_WORK(&xdev->work, xenbus_frontend_delayed_resume); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return xenbus_dev_probe(dev); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void xenbus_frontend_dev_shutdown(struct device *_dev) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct xenbus_device *dev = to_xenbus_device(_dev); 13162306a36Sopenharmony_ci unsigned long timeout = 5*HZ; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci DPRINTK("%s", dev->nodename); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci get_device(&dev->dev); 13662306a36Sopenharmony_ci if (dev->state != XenbusStateConnected) { 13762306a36Sopenharmony_ci pr_info("%s: %s: %s != Connected, skipping\n", 13862306a36Sopenharmony_ci __func__, dev->nodename, xenbus_strstate(dev->state)); 13962306a36Sopenharmony_ci goto out; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci xenbus_switch_state(dev, XenbusStateClosing); 14262306a36Sopenharmony_ci timeout = wait_for_completion_timeout(&dev->down, timeout); 14362306a36Sopenharmony_ci if (!timeout) 14462306a36Sopenharmony_ci pr_info("%s: %s timeout closing device\n", 14562306a36Sopenharmony_ci __func__, dev->nodename); 14662306a36Sopenharmony_ci out: 14762306a36Sopenharmony_ci put_device(&dev->dev); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic const struct dev_pm_ops xenbus_pm_ops = { 15162306a36Sopenharmony_ci .suspend = xenbus_dev_suspend, 15262306a36Sopenharmony_ci .resume = xenbus_frontend_dev_resume, 15362306a36Sopenharmony_ci .freeze = xenbus_dev_suspend, 15462306a36Sopenharmony_ci .thaw = xenbus_dev_cancel, 15562306a36Sopenharmony_ci .restore = xenbus_dev_resume, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic struct xen_bus_type xenbus_frontend = { 15962306a36Sopenharmony_ci .root = "device", 16062306a36Sopenharmony_ci .levels = 2, /* device/type/<id> */ 16162306a36Sopenharmony_ci .get_bus_id = frontend_bus_id, 16262306a36Sopenharmony_ci .probe = xenbus_probe_frontend, 16362306a36Sopenharmony_ci .otherend_changed = backend_changed, 16462306a36Sopenharmony_ci .bus = { 16562306a36Sopenharmony_ci .name = "xen", 16662306a36Sopenharmony_ci .match = xenbus_match, 16762306a36Sopenharmony_ci .uevent = xenbus_uevent_frontend, 16862306a36Sopenharmony_ci .probe = xenbus_frontend_dev_probe, 16962306a36Sopenharmony_ci .remove = xenbus_dev_remove, 17062306a36Sopenharmony_ci .shutdown = xenbus_frontend_dev_shutdown, 17162306a36Sopenharmony_ci .dev_groups = xenbus_dev_groups, 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci .pm = &xenbus_pm_ops, 17462306a36Sopenharmony_ci }, 17562306a36Sopenharmony_ci}; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void frontend_changed(struct xenbus_watch *watch, 17862306a36Sopenharmony_ci const char *path, const char *token) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci DPRINTK(""); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci xenbus_dev_changed(path, &xenbus_frontend); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* We watch for devices appearing and vanishing. */ 18762306a36Sopenharmony_cistatic struct xenbus_watch fe_watch = { 18862306a36Sopenharmony_ci .node = "device", 18962306a36Sopenharmony_ci .callback = frontend_changed, 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int read_backend_details(struct xenbus_device *xendev) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci return xenbus_read_otherend_details(xendev, "backend-id", "backend"); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int is_device_connecting(struct device *dev, void *data, bool ignore_nonessential) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct xenbus_device *xendev = to_xenbus_device(dev); 20062306a36Sopenharmony_ci struct device_driver *drv = data; 20162306a36Sopenharmony_ci struct xenbus_driver *xendrv; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * A device with no driver will never connect. We care only about 20562306a36Sopenharmony_ci * devices which should currently be in the process of connecting. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci if (!dev->driver) 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Is this search limited to a particular driver? */ 21162306a36Sopenharmony_ci if (drv && (dev->driver != drv)) 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci xendrv = to_xenbus_driver(dev->driver); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (ignore_nonessential && xendrv->not_essential) 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return (xendev->state < XenbusStateConnected || 22062306a36Sopenharmony_ci (xendev->state == XenbusStateConnected && 22162306a36Sopenharmony_ci xendrv->is_ready && !xendrv->is_ready(xendev))); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_cistatic int essential_device_connecting(struct device *dev, void *data) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci return is_device_connecting(dev, data, true /* ignore PV[KBB+FB] */); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_cistatic int non_essential_device_connecting(struct device *dev, void *data) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci return is_device_connecting(dev, data, false); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int exists_essential_connecting_device(struct device_driver *drv) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, 23562306a36Sopenharmony_ci essential_device_connecting); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_cistatic int exists_non_essential_connecting_device(struct device_driver *drv) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, 24062306a36Sopenharmony_ci non_essential_device_connecting); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int print_device_status(struct device *dev, void *data) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct xenbus_device *xendev = to_xenbus_device(dev); 24662306a36Sopenharmony_ci struct device_driver *drv = data; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Is this operation limited to a particular driver? */ 24962306a36Sopenharmony_ci if (drv && (dev->driver != drv)) 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (!dev->driver) { 25362306a36Sopenharmony_ci /* Information only: is this too noisy? */ 25462306a36Sopenharmony_ci pr_info("Device with no driver: %s\n", xendev->nodename); 25562306a36Sopenharmony_ci } else if (xendev->state < XenbusStateConnected) { 25662306a36Sopenharmony_ci enum xenbus_state rstate = XenbusStateUnknown; 25762306a36Sopenharmony_ci if (xendev->otherend) 25862306a36Sopenharmony_ci rstate = xenbus_read_driver_state(xendev->otherend); 25962306a36Sopenharmony_ci pr_warn("Timeout connecting to device: %s (local state %d, remote state %d)\n", 26062306a36Sopenharmony_ci xendev->nodename, xendev->state, rstate); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* We only wait for device setup after most initcalls have run. */ 26762306a36Sopenharmony_cistatic int ready_to_wait_for_devices; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic bool wait_loop(unsigned long start, unsigned int max_delay, 27062306a36Sopenharmony_ci unsigned int *seconds_waited) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci if (time_after(jiffies, start + (*seconds_waited+5)*HZ)) { 27362306a36Sopenharmony_ci if (!*seconds_waited) 27462306a36Sopenharmony_ci pr_warn("Waiting for devices to initialise: "); 27562306a36Sopenharmony_ci *seconds_waited += 5; 27662306a36Sopenharmony_ci pr_cont("%us...", max_delay - *seconds_waited); 27762306a36Sopenharmony_ci if (*seconds_waited == max_delay) { 27862306a36Sopenharmony_ci pr_cont("\n"); 27962306a36Sopenharmony_ci return true; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci schedule_timeout_interruptible(HZ/10); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return false; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci/* 28862306a36Sopenharmony_ci * On a 5-minute timeout, wait for all devices currently configured. We need 28962306a36Sopenharmony_ci * to do this to guarantee that the filesystems and / or network devices 29062306a36Sopenharmony_ci * needed for boot are available, before we can allow the boot to proceed. 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * This needs to be on a late_initcall, to happen after the frontend device 29362306a36Sopenharmony_ci * drivers have been initialised, but before the root fs is mounted. 29462306a36Sopenharmony_ci * 29562306a36Sopenharmony_ci * A possible improvement here would be to have the tools add a per-device 29662306a36Sopenharmony_ci * flag to the store entry, indicating whether it is needed at boot time. 29762306a36Sopenharmony_ci * This would allow people who knew what they were doing to accelerate their 29862306a36Sopenharmony_ci * boot slightly, but of course needs tools or manual intervention to set up 29962306a36Sopenharmony_ci * those flags correctly. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_cistatic void wait_for_devices(struct xenbus_driver *xendrv) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci unsigned long start = jiffies; 30462306a36Sopenharmony_ci struct device_driver *drv = xendrv ? &xendrv->driver : NULL; 30562306a36Sopenharmony_ci unsigned int seconds_waited = 0; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (!ready_to_wait_for_devices || !xen_domain()) 30862306a36Sopenharmony_ci return; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci while (exists_non_essential_connecting_device(drv)) 31162306a36Sopenharmony_ci if (wait_loop(start, 30, &seconds_waited)) 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Skips PVKB and PVFB check.*/ 31562306a36Sopenharmony_ci while (exists_essential_connecting_device(drv)) 31662306a36Sopenharmony_ci if (wait_loop(start, 270, &seconds_waited)) 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (seconds_waited) 32062306a36Sopenharmony_ci printk("\n"); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, 32362306a36Sopenharmony_ci print_device_status); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ciint __xenbus_register_frontend(struct xenbus_driver *drv, struct module *owner, 32762306a36Sopenharmony_ci const char *mod_name) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int ret; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci drv->read_otherend_details = read_backend_details; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci ret = xenbus_register_driver_common(drv, &xenbus_frontend, 33462306a36Sopenharmony_ci owner, mod_name); 33562306a36Sopenharmony_ci if (ret) 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* If this driver is loaded as a module wait for devices to attach. */ 33962306a36Sopenharmony_ci wait_for_devices(drv); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__xenbus_register_frontend); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(backend_state_wq); 34662306a36Sopenharmony_cistatic int backend_state; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic void xenbus_reset_backend_state_changed(struct xenbus_watch *w, 34962306a36Sopenharmony_ci const char *path, const char *token) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci if (xenbus_scanf(XBT_NIL, path, "", "%i", 35262306a36Sopenharmony_ci &backend_state) != 1) 35362306a36Sopenharmony_ci backend_state = XenbusStateUnknown; 35462306a36Sopenharmony_ci printk(KERN_DEBUG "XENBUS: backend %s %s\n", 35562306a36Sopenharmony_ci path, xenbus_strstate(backend_state)); 35662306a36Sopenharmony_ci wake_up(&backend_state_wq); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void xenbus_reset_wait_for_backend(char *be, int expected) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci long timeout; 36262306a36Sopenharmony_ci timeout = wait_event_interruptible_timeout(backend_state_wq, 36362306a36Sopenharmony_ci backend_state == expected, 5 * HZ); 36462306a36Sopenharmony_ci if (timeout <= 0) 36562306a36Sopenharmony_ci pr_info("backend %s timed out\n", be); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* 36962306a36Sopenharmony_ci * Reset frontend if it is in Connected or Closed state. 37062306a36Sopenharmony_ci * Wait for backend to catch up. 37162306a36Sopenharmony_ci * State Connected happens during kdump, Closed after kexec. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_cistatic void xenbus_reset_frontend(char *fe, char *be, int be_state) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct xenbus_watch be_watch; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci printk(KERN_DEBUG "XENBUS: backend %s %s\n", 37862306a36Sopenharmony_ci be, xenbus_strstate(be_state)); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci memset(&be_watch, 0, sizeof(be_watch)); 38162306a36Sopenharmony_ci be_watch.node = kasprintf(GFP_NOIO | __GFP_HIGH, "%s/state", be); 38262306a36Sopenharmony_ci if (!be_watch.node) 38362306a36Sopenharmony_ci return; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci be_watch.callback = xenbus_reset_backend_state_changed; 38662306a36Sopenharmony_ci backend_state = XenbusStateUnknown; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci pr_info("triggering reconnect on %s\n", be); 38962306a36Sopenharmony_ci register_xenbus_watch(&be_watch); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* fall through to forward backend to state XenbusStateInitialising */ 39262306a36Sopenharmony_ci switch (be_state) { 39362306a36Sopenharmony_ci case XenbusStateConnected: 39462306a36Sopenharmony_ci xenbus_printf(XBT_NIL, fe, "state", "%d", XenbusStateClosing); 39562306a36Sopenharmony_ci xenbus_reset_wait_for_backend(be, XenbusStateClosing); 39662306a36Sopenharmony_ci fallthrough; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci case XenbusStateClosing: 39962306a36Sopenharmony_ci xenbus_printf(XBT_NIL, fe, "state", "%d", XenbusStateClosed); 40062306a36Sopenharmony_ci xenbus_reset_wait_for_backend(be, XenbusStateClosed); 40162306a36Sopenharmony_ci fallthrough; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci case XenbusStateClosed: 40462306a36Sopenharmony_ci xenbus_printf(XBT_NIL, fe, "state", "%d", XenbusStateInitialising); 40562306a36Sopenharmony_ci xenbus_reset_wait_for_backend(be, XenbusStateInitWait); 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci unregister_xenbus_watch(&be_watch); 40962306a36Sopenharmony_ci pr_info("reconnect done on %s\n", be); 41062306a36Sopenharmony_ci kfree(be_watch.node); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic void xenbus_check_frontend(char *class, char *dev) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci int be_state, fe_state, err; 41662306a36Sopenharmony_ci char *backend, *frontend; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci frontend = kasprintf(GFP_NOIO | __GFP_HIGH, "device/%s/%s", class, dev); 41962306a36Sopenharmony_ci if (!frontend) 42062306a36Sopenharmony_ci return; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci err = xenbus_scanf(XBT_NIL, frontend, "state", "%i", &fe_state); 42362306a36Sopenharmony_ci if (err != 1) 42462306a36Sopenharmony_ci goto out; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci switch (fe_state) { 42762306a36Sopenharmony_ci case XenbusStateConnected: 42862306a36Sopenharmony_ci case XenbusStateClosed: 42962306a36Sopenharmony_ci printk(KERN_DEBUG "XENBUS: frontend %s %s\n", 43062306a36Sopenharmony_ci frontend, xenbus_strstate(fe_state)); 43162306a36Sopenharmony_ci backend = xenbus_read(XBT_NIL, frontend, "backend", NULL); 43262306a36Sopenharmony_ci if (IS_ERR_OR_NULL(backend)) 43362306a36Sopenharmony_ci goto out; 43462306a36Sopenharmony_ci err = xenbus_scanf(XBT_NIL, backend, "state", "%i", &be_state); 43562306a36Sopenharmony_ci if (err == 1) 43662306a36Sopenharmony_ci xenbus_reset_frontend(frontend, backend, be_state); 43762306a36Sopenharmony_ci kfree(backend); 43862306a36Sopenharmony_ci break; 43962306a36Sopenharmony_ci default: 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ciout: 44362306a36Sopenharmony_ci kfree(frontend); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void xenbus_reset_state(void) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci char **devclass, **dev; 44962306a36Sopenharmony_ci int devclass_n, dev_n; 45062306a36Sopenharmony_ci int i, j; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci devclass = xenbus_directory(XBT_NIL, "device", "", &devclass_n); 45362306a36Sopenharmony_ci if (IS_ERR(devclass)) 45462306a36Sopenharmony_ci return; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci for (i = 0; i < devclass_n; i++) { 45762306a36Sopenharmony_ci dev = xenbus_directory(XBT_NIL, "device", devclass[i], &dev_n); 45862306a36Sopenharmony_ci if (IS_ERR(dev)) 45962306a36Sopenharmony_ci continue; 46062306a36Sopenharmony_ci for (j = 0; j < dev_n; j++) 46162306a36Sopenharmony_ci xenbus_check_frontend(devclass[i], dev[j]); 46262306a36Sopenharmony_ci kfree(dev); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci kfree(devclass); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int frontend_probe_and_watch(struct notifier_block *notifier, 46862306a36Sopenharmony_ci unsigned long event, 46962306a36Sopenharmony_ci void *data) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci /* reset devices in Connected or Closed state */ 47262306a36Sopenharmony_ci if (xen_hvm_domain()) 47362306a36Sopenharmony_ci xenbus_reset_state(); 47462306a36Sopenharmony_ci /* Enumerate devices in xenstore and watch for changes. */ 47562306a36Sopenharmony_ci xenbus_probe_devices(&xenbus_frontend); 47662306a36Sopenharmony_ci register_xenbus_watch(&fe_watch); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return NOTIFY_DONE; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int __init xenbus_probe_frontend_init(void) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci static struct notifier_block xenstore_notifier = { 48562306a36Sopenharmony_ci .notifier_call = frontend_probe_and_watch 48662306a36Sopenharmony_ci }; 48762306a36Sopenharmony_ci int err; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci DPRINTK(""); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* Register ourselves with the kernel bus subsystem */ 49262306a36Sopenharmony_ci err = bus_register(&xenbus_frontend.bus); 49362306a36Sopenharmony_ci if (err) 49462306a36Sopenharmony_ci return err; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci register_xenstore_notifier(&xenstore_notifier); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_cisubsys_initcall(xenbus_probe_frontend_init); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci#ifndef MODULE 50362306a36Sopenharmony_cistatic int __init boot_wait_for_devices(void) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci if (!xen_has_pv_devices()) 50662306a36Sopenharmony_ci return -ENODEV; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci ready_to_wait_for_devices = 1; 50962306a36Sopenharmony_ci wait_for_devices(NULL); 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cilate_initcall(boot_wait_for_devices); 51462306a36Sopenharmony_ci#endif 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 517