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: Stefano 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: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
This commit is contained in:
Родитель
a71e23d992
Коммит
3066616ce2
|
@ -135,7 +135,7 @@ static int read_backend_details(struct xenbus_device *xendev)
|
|||
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 device_driver *drv = data;
|
||||
|
@ -152,16 +152,41 @@ static int is_device_connecting(struct device *dev, void *data)
|
|||
if (drv && (dev->driver != drv))
|
||||
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);
|
||||
return (xendev->state < XenbusStateConnected ||
|
||||
(xendev->state == XenbusStateConnected &&
|
||||
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,
|
||||
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)
|
||||
|
@ -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. */
|
||||
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
|
||||
* 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)
|
|||
if (!ready_to_wait_for_devices || !xen_domain())
|
||||
return;
|
||||
|
||||
while (exists_connecting_device(drv)) {
|
||||
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...", 300 - seconds_waited);
|
||||
if (seconds_waited == 300)
|
||||
break;
|
||||
}
|
||||
while (exists_non_essential_connecting_device(drv))
|
||||
if (wait_loop(start, 30, &seconds_waited))
|
||||
break;
|
||||
|
||||
schedule_timeout_interruptible(HZ/10);
|
||||
}
|
||||
/* Skips PVKB and PVFB check.*/
|
||||
while (exists_essential_connecting_device(drv))
|
||||
if (wait_loop(start, 270, &seconds_waited))
|
||||
break;
|
||||
|
||||
if (seconds_waited)
|
||||
printk("\n");
|
||||
|
|
Загрузка…
Ссылка в новой задаче