From 23a1a4590594555cc59d12adaaa669ac14cb5390 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sat, 22 Aug 2026 14:07:55 -0700 Subject: [PATCH 1/2] x86/xen: take the early CPU frequency from the hypervisor xen_init_time_common() points x86_platform.calibrate_tsc at xen_tsc_khz() but leaves calibrate_cpu native, so tsc_early_init() -> determine_cpu_tsc_frequencies() still calls native_calibrate_cpu_early() for cpu_khz. That tries CPUID 0x16, then MSR_PLATFORM_INFO, and then falls through to quick_pit_calibrate() -- which on a PVH guest waits out its full 50ms timeout on a PIT that is not there, prints "Fast TSC calibration failed", and arrives at a value the hypervisor had already been asked for a moment later. It is 48ms of every boot, and it is the largest single item in one: a 512 MiB PVH guest reached "Run /init as init process" at 0.330s, and 0.118s once this and the xenbus boot wait were out of the way. There is nothing for the calibration to find out here. Xen reports the TSC frequency, tsc_khz already comes from it, and cpu_khz differing from tsc_khz under a hypervisor is not information -- determine_cpu_tsc_frequencies() would overwrite cpu_khz with tsc_khz anyway once they disagree by more than 10%. Note that tsc_early_khz= does not help: it only short-circuits calibrate_tsc, and the 50ms is spent in calibrate_cpu. Set in xen_init_time_common() so PV and PVH/HVM guests both get it, which is where calibrate_tsc is already set for the same reason. Signed-off-by: Alex Zenla --- arch/x86/xen/time.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 96521b1874acfc..493c46190d7c20 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -567,6 +567,7 @@ static void __init xen_init_time_common(void) paravirt_set_sched_clock(xen_sched_clock); x86_platform.calibrate_tsc = xen_tsc_khz; + x86_platform.calibrate_cpu = xen_tsc_khz; x86_platform.get_wallclock = xen_get_wallclock; } From 5fa9fe16e9ae3a8ee2b1a0c5efc6f37b2c350b08 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sat, 22 Aug 2026 14:08:06 -0700 Subject: [PATCH 2/2] xen/xenbus: wake the boot device wait when a backend changes state boot_wait_for_devices() sleeps HZ/10 between checks, so on a CONFIG_HZ=100 kernel it waits a tenth of a second to notice something that has already happened. What it is waiting for is not slow: a toolstack has every backend sitting in XenbusStateInitWait before it unpauses the guest, so the wait covers the frontend half of the handshake, which finishes a few milliseconds after the late_initcall starts looking. Measured on a PVH guest, the last frontend connected at 0.232s and init started at 0.311s -- 79ms of sleeping past the event. Frontends move to XenbusStateConnected (and blkfront sets is_ready) from their otherend_changed callback, which runs from the frontend bus's backend_changed() watch. Bump a sequence count and wake a wait queue there, and have wait_for_devices() sleep on it instead of for a fixed interval. The sequence is sampled before each check, so a change that lands between the check and the sleep still wakes the waiter. The HZ/10 interruptible sleep stays as the timeout, so state changes that do not come through the watch are noticed no later than before, and the 30/270-second budgets and their reporting are unchanged. Signed-off-by: Alex Zenla --- drivers/xen/xenbus/xenbus_probe_frontend.c | 30 +++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index 199917b6f77ca9..9d14e3808d949c 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -85,10 +85,17 @@ static int xenbus_uevent_frontend(const struct device *_dev, } +/* Condition for wait_for_devices(): bumped on every backend state change. */ +static atomic_t frontend_wait_seq = ATOMIC_INIT(0); +static DECLARE_WAIT_QUEUE_HEAD(frontend_wait_wq); + static void backend_changed(struct xenbus_watch *watch, const char *path, const char *token) { xenbus_otherend_changed(watch, path, token, 1); + + atomic_inc(&frontend_wait_seq); + wake_up(&frontend_wait_wq); } static void xenbus_frontend_delayed_resume(struct work_struct *w) @@ -265,7 +272,7 @@ static int print_device_status(struct device *dev, void *data) static int ready_to_wait_for_devices; static bool wait_loop(unsigned long start, unsigned int max_delay, - unsigned int *seconds_waited) + unsigned int *seconds_waited, unsigned int seq) { if (time_after(jiffies, start + (*seconds_waited+5)*HZ)) { if (!*seconds_waited) @@ -278,7 +285,8 @@ static bool wait_loop(unsigned long start, unsigned int max_delay, } } - schedule_timeout_interruptible(HZ/10); + wait_event_interruptible_timeout(frontend_wait_wq, + atomic_read(&frontend_wait_seq) != seq, HZ/10); return false; } @@ -301,18 +309,28 @@ static void wait_for_devices(struct xenbus_driver *xendrv) unsigned long start = jiffies; struct device_driver *drv = xendrv ? &xendrv->driver : NULL; unsigned int seconds_waited = 0; + unsigned int seq; if (!ready_to_wait_for_devices || !xen_domain()) return; - while (exists_non_essential_connecting_device(drv)) - if (wait_loop(start, 30, &seconds_waited)) + /* Sample the sequence before checking, so a change in between wakes us. */ + for (;;) { + seq = atomic_read(&frontend_wait_seq); + if (!exists_non_essential_connecting_device(drv)) + break; + if (wait_loop(start, 30, &seconds_waited, seq)) break; + } /* Skips PVKB and PVFB check.*/ - while (exists_essential_connecting_device(drv)) - if (wait_loop(start, 270, &seconds_waited)) + for (;;) { + seq = atomic_read(&frontend_wait_seq); + if (!exists_essential_connecting_device(drv)) + break; + if (wait_loop(start, 270, &seconds_waited, seq)) break; + } if (seconds_waited) printk("\n");