Commit 3066616c authored by Konrad Rzeszutek Wilk's avatar Konrad Rzeszutek Wilk

xen/xenbus: Add quirk to deal with misconfigured backends.

A rather annoying and common case is when booting a PVonHVM guest
and exposing the PV KBD and PV VFB - as broken toolstacks don't
always initialize the backends correctly.

Normally The HVM guest is using the VGA driver and the emulated
keyboard for this (though upstream version of QEMU implements
PV KBD, but still uses a VGA driver). We provide a very basic
two-stage wait mechanism - where we wait for 30 seconds for all
devices, and then for 270 for all them except the two mentioned.

That allows us to wait for the essential devices, like network
or disk for the full 6 minutes.

To trigger this, put this in your guest config:

vfb = [ 'vnc=1, vnclisten=0.0.0.0 ,vncunused=1']

instead of this:
vnc=1
vnclisten="0.0.0.0"

CC: stable@kernel.org
Acked-by: default avatarStefano Stabellini <stefano.stabellini@eu.citrix.com>
[v3: Split delay in non-essential (30 seconds) and essential
 devices per Ian and Stefano suggestion]
[v4: Added comments per Stefano suggestion]
Signed-off-by: default avatarKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
parent a71e23d9
...@@ -135,7 +135,7 @@ static int read_backend_details(struct xenbus_device *xendev) ...@@ -135,7 +135,7 @@ static int read_backend_details(struct xenbus_device *xendev)
return xenbus_read_otherend_details(xendev, "backend-id", "backend"); return xenbus_read_otherend_details(xendev, "backend-id", "backend");
} }
static int is_device_connecting(struct device *dev, void *data) static int is_device_connecting(struct device *dev, void *data, bool ignore_nonessential)
{ {
struct xenbus_device *xendev = to_xenbus_device(dev); struct xenbus_device *xendev = to_xenbus_device(dev);
struct device_driver *drv = data; struct device_driver *drv = data;
...@@ -152,16 +152,41 @@ static int is_device_connecting(struct device *dev, void *data) ...@@ -152,16 +152,41 @@ static int is_device_connecting(struct device *dev, void *data)
if (drv && (dev->driver != drv)) if (drv && (dev->driver != drv))
return 0; return 0;
if (ignore_nonessential) {
/* With older QEMU, for PVonHVM guests the guest config files
* could contain: vfb = [ 'vnc=1, vnclisten=0.0.0.0']
* which is nonsensical as there is no PV FB (there can be
* a PVKB) running as HVM guest. */
if ((strncmp(xendev->nodename, "device/vkbd", 11) == 0))
return 0;
if ((strncmp(xendev->nodename, "device/vfb", 10) == 0))
return 0;
}
xendrv = to_xenbus_driver(dev->driver); xendrv = to_xenbus_driver(dev->driver);
return (xendev->state < XenbusStateConnected || return (xendev->state < XenbusStateConnected ||
(xendev->state == XenbusStateConnected && (xendev->state == XenbusStateConnected &&
xendrv->is_ready && !xendrv->is_ready(xendev))); xendrv->is_ready && !xendrv->is_ready(xendev)));
} }
static int essential_device_connecting(struct device *dev, void *data)
{
return is_device_connecting(dev, data, true /* ignore PV[KBB+FB] */);
}
static int non_essential_device_connecting(struct device *dev, void *data)
{
return is_device_connecting(dev, data, false);
}
static int exists_connecting_device(struct device_driver *drv) static int exists_essential_connecting_device(struct device_driver *drv)
{ {
return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
is_device_connecting); essential_device_connecting);
}
static int exists_non_essential_connecting_device(struct device_driver *drv)
{
return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
non_essential_device_connecting);
} }
static int print_device_status(struct device *dev, void *data) static int print_device_status(struct device *dev, void *data)
...@@ -192,6 +217,23 @@ static int print_device_status(struct device *dev, void *data) ...@@ -192,6 +217,23 @@ static int print_device_status(struct device *dev, void *data)
/* We only wait for device setup after most initcalls have run. */ /* We only wait for device setup after most initcalls have run. */
static int ready_to_wait_for_devices; static int ready_to_wait_for_devices;
static bool wait_loop(unsigned long start, unsigned int max_delay,
unsigned int *seconds_waited)
{
if (time_after(jiffies, start + (*seconds_waited+5)*HZ)) {
if (!*seconds_waited)
printk(KERN_WARNING "XENBUS: Waiting for "
"devices to initialise: ");
*seconds_waited += 5;
printk("%us...", max_delay - *seconds_waited);
if (*seconds_waited == max_delay)
return true;
}
schedule_timeout_interruptible(HZ/10);
return false;
}
/* /*
* On a 5-minute timeout, wait for all devices currently configured. We need * On a 5-minute timeout, wait for all devices currently configured. We need
* to do this to guarantee that the filesystems and / or network devices * to do this to guarantee that the filesystems and / or network devices
...@@ -215,19 +257,14 @@ static void wait_for_devices(struct xenbus_driver *xendrv) ...@@ -215,19 +257,14 @@ static void wait_for_devices(struct xenbus_driver *xendrv)
if (!ready_to_wait_for_devices || !xen_domain()) if (!ready_to_wait_for_devices || !xen_domain())
return; return;
while (exists_connecting_device(drv)) { while (exists_non_essential_connecting_device(drv))
if (time_after(jiffies, start + (seconds_waited+5)*HZ)) { if (wait_loop(start, 30, &seconds_waited))
if (!seconds_waited) break;
printk(KERN_WARNING "XENBUS: Waiting for "
"devices to initialise: "); /* Skips PVKB and PVFB check.*/
seconds_waited += 5; while (exists_essential_connecting_device(drv))
printk("%us...", 300 - seconds_waited); if (wait_loop(start, 270, &seconds_waited))
if (seconds_waited == 300) break;
break;
}
schedule_timeout_interruptible(HZ/10);
}
if (seconds_waited) if (seconds_waited)
printk("\n"); printk("\n");
......
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