Skip to content

Commit 523748e

Browse files
committed
Fix SMP=4 boot hang regression
After the introduction of #110, the adaptive timer/UART fd registration logic would exclude timer fd monitoring when all harts entered WFI state during early boot. This created a deadlock: harts waited for timer interrupts, but the timer fd was not being polled, preventing wakeup. Symptom: - SMP=4 hung after "smp: Brought up 1 node, 4 CPUs" (49 lines of output) - Never reached "clocksource: Switched to clocksource" or login prompt - SMP=1 continued to work correctly This commit introduces boot completion heuristic using peripheral_update_ctr. Consider boot "incomplete" for the first 5000 scheduler iterations after all harts start. During this period, always keep timer and UART fds active to ensure harts can receive timer interrupts even when temporarily in WFI.
1 parent a56f5bd commit 523748e

File tree

1 file changed

+13
-15
lines changed

1 file changed

+13
-15
lines changed

main.c

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,22 +1191,20 @@ static int semu_run(emu_state_t *emu)
11911191
size_t pfd_count = 0;
11921192
int timer_index = -1;
11931193

1194-
/* Add periodic timer fd (1ms interval for guest timer emulation).
1195-
* Only add timer when ALL harts are active (none idle) to allow
1196-
* poll() to sleep when any harts are in WFI. When harts are idle,
1197-
* timer updates can be deferred until they wake up.
1198-
*
1199-
* During SMP boot (started_harts < vm->n_hart), always include the
1200-
* timer to ensure secondary harts can complete initialization. Only
1201-
* apply conditional exclusion after all harts have started.
1202-
*
1203-
* For single-hart configurations (n_hart == 1), disable
1204-
* optimization entirely to avoid boot issues, as the first hart
1205-
* starts immediately.
1194+
/* Add periodic timer fd (1ms interval). Excluded when harts are
1195+
* idle to allow poll() sleep, but always included during:
1196+
* 1. Single-hart mode (n_hart == 1)
1197+
* 2. Boot phase (!boot_complete) - prevents deadlock when kernel
1198+
* briefly puts all harts in WFI while waiting for timer IRQ
1199+
* 3. Active execution (idle_harts == 0)
12061200
*/
12071201
bool all_harts_started = (started_harts >= vm->n_hart);
1202+
const uint64_t BOOT_SETTLE_ITERATIONS = 5000;
1203+
bool boot_complete =
1204+
all_harts_started &&
1205+
(emu->peripheral_update_ctr > BOOT_SETTLE_ITERATIONS);
12081206
bool harts_active =
1209-
(vm->n_hart == 1) || !all_harts_started || (idle_harts == 0);
1207+
(vm->n_hart == 1) || !boot_complete || (idle_harts == 0);
12101208
#ifdef __APPLE__
12111209
/* macOS: use kqueue with EVFILT_TIMER */
12121210
if (kq >= 0 && pfd_count < poll_capacity && harts_active) {
@@ -1227,7 +1225,7 @@ static int semu_run(emu_state_t *emu)
12271225
/* Add UART input fd (stdin for keyboard input).
12281226
* Only add UART when:
12291227
* 1. Single-hart configuration (n_hart == 1), OR
1230-
* 2. Not all harts started (!all_harts_started), OR
1228+
* 2. Boot not complete (!boot_complete), OR
12311229
* 3. All harts are active (idle_harts == 0), OR
12321230
* 4. A hart is actively waiting for UART input
12331231
*
@@ -1236,7 +1234,7 @@ static int semu_run(emu_state_t *emu)
12361234
* input (Ctrl+A x) may be delayed by up to poll_timeout (10ms)
12371235
* when harts are idle, which is acceptable for an emulator.
12381236
*/
1239-
bool need_uart = (vm->n_hart == 1) || !all_harts_started ||
1237+
bool need_uart = (vm->n_hart == 1) || !boot_complete ||
12401238
(idle_harts == 0) || emu->uart.has_waiting_hart;
12411239
if (emu->uart.in_fd >= 0 && pfd_count < poll_capacity &&
12421240
need_uart) {

0 commit comments

Comments
 (0)