From f9e68779ee7facbebe890282b90fc030aed2f83c Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Sun, 13 Sep 2026 14:43:35 +0200 Subject: [PATCH] ide: idewait spun for a minute on any machine with no legacy drive The bound was 20,000,000 iterations, on the reasoning that a real drive answers in microseconds so only an empty channel could reach the end. True, and it misses what an iteration costs: every read here is a port access, and under a hypervisor a port access is a VM exit at a microsecond or two. Twenty million of those is a minute of the machine apparently doing nothing, on every boot, on any machine with nothing on the primary IDE channel -- which is every machine built in the last decade, where storage is AHCI or NVMe and the legacy ports answer nobody. Booting the ISO looked like a hang for exactly that reason: the line after "time:" took longer than anyone waits. Bounded by time now, one second: four orders of magnitude more than a drive needs, and short enough that an absent one costs nothing. "ide: no drive on the primary channel" prints immediately. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2 --- kernel/dev/ide.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/kernel/dev/ide.c b/kernel/dev/ide.c index 596f0b0..0a2c602 100644 --- a/kernel/dev/ide.c +++ b/kernel/dev/ide.c @@ -55,22 +55,46 @@ static void idestart(struct buf*); // dead machine right after the VGA banner. That is not an exotic // configuration: it is every CD boot, and most hardware newer than IDE. // -// The bound is deliberately enormous. A real drive answers in microseconds; -// this only expires when there is nothing there at all. +// Bounded by *time*, not by a count of port reads. +// +// The bound used to be 20,000,000 iterations, on the reasoning that a real +// drive answers in microseconds so only an empty channel could reach the end. +// True, and it misses what an iteration costs: every `inb` here is a port +// access, and under a hypervisor a port access is a VM exit at a microsecond +// or two. Twenty million of them is a minute of the machine apparently doing +// nothing, on every boot, on any machine with no drive on the primary channel. +// +// That is not a rare configuration -- it is every machine built in the last +// decade, where storage is AHCI or NVMe and the legacy ports answer nothing. +// A SchOS booted from a USB stick on real hardware spent that minute staring +// at an empty ISA port before it printed its next line. +// +// One second is four orders of magnitude more than a drive needs and short +// enough that an absent one costs nothing worth measuring. static int idewait(int checkerr) { + uint64 deadline = tsc_hz ? rdtsc() + tsc_hz : 0; int r = 0; - for(int i = 0; i < 20000000; i++){ + for(;;){ r = inb(0x1f7); if((r & (IDE_BSY|IDE_DRDY)) == IDE_DRDY){ if(checkerr && (r & (IDE_DF|IDE_ERR)) != 0) return -1; return 0; } + // No calibrated TSC yet (which cannot happen from ideinit, but this + // function has other callers and one of them may move): fall back to a + // count, small enough not to cost a minute. + if (deadline == 0) { + static int spins; + if (++spins > 100000) { spins = 0; return -1; } + continue; + } + if (rdtsc() >= deadline) + return -1; } - return -1; } static void