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