Re: thread on sleepqueue does not wake up after timeout
Date: Tue, 09 Nov 2021 11:52:18 UTC
On Tue, Nov 09, 2021 at 11:58:30AM +0200, Andriy Gapon wrote:
>
> Two years have passed since my original report, some things have changed,
> but we are still seeing the problem.
>
> To recap, the problem is that sometimes a callout for a sleepq timeout would
> fire to early. The code in sleepq_timeout would ignore such a wake-up.
> And, as there would not be another callout, there will never be a wake-up.
>
> This has been observed on VMWare only. We have seen it on different
> versions of ESXi (different environments, upgrades, etc) starting with 5.0,
> through 5.5 and 6.0, and now on 6.5.
>
> We use 12.2 now and are still seeing the problem occasionally.
>
> On advice from Kostik I added some diagnostic code.
> The code checks for the too early condition and panics when it happens.
> It also provides some additional information.
>
> Here is an example of such a panic.
> Unread portion of the kernel message buffer:
> panic: too early sleepq timeout, 190543869738008 > 190543869603412 (190543869798505)
> cpuid = 1
> time = 1636195325
>
> (kgdb) bt
> #0 __curthread () at /usr/src/sys/amd64/include/pcpu_aux.h:55
> #1 doadump (textdump=153080096) at /usr/src/sys/kern/kern_shutdown.c:447
> #2 0xffffffff8038f1cc in db_fncall_generic (addr=<optimized out>, nargs=0,
> args=<optimized out>, rv=<optimized out>) at
> /usr/src/sys/ddb/db_command.c:622
> #3 db_fncall (dummy1=<optimized out>, dummy2=<optimized out>,
> dummy3=<optimized out>, dummy4=<optimized out>) at
> /usr/src/sys/ddb/db_command.c:670
> #4 0xffffffff8038eaf6 in db_command (last_cmdp=<optimized out>,
> cmd_table=<optimized out>, dopager=0) at /usr/src/sys/ddb/db_command.c:494
> #5 0xffffffff80393a68 in db_script_exec (scriptname=<optimized out>,
> warnifnotfound=<optimized out>) at /usr/src/sys/ddb/db_script.c:304
> #6 0xffffffff80393892 in db_script_kdbenter (eventname=<optimized out>) at
> /usr/src/sys/ddb/db_script.c:326
> #7 0xffffffff80391ac3 in db_trap (type=<optimized out>, code=<optimized
> out>) at /usr/src/sys/ddb/db_main.c:251
> #8 0xffffffff807821e2 in kdb_trap (type=3, code=0, tf=0xfffffe01091fd410)
> at /usr/src/sys/kern/subr_kdb.c:700
> #9 0xffffffff809d870e in trap (frame=0xfffffe01091fd410) at
> /usr/src/sys/amd64/amd64/trap.c:583
> #10 <signal handler called>
> #11 kdb_enter (why=0xffffffff80b17af9 "panic", msg=0xffffffff80b17af9
> "panic") at /usr/src/sys/kern/subr_kdb.c:486
> #12 0xffffffff8073ddce in vpanic (fmt=<optimized out>, ap=<optimized out>)
> at /usr/src/sys/kern/kern_shutdown.c:975
> #13 0xffffffff8073dc23 in panic (fmt=0xffffffff81178120 <cnputs_mtx+24> "")
> at /usr/src/sys/kern/kern_shutdown.c:909
> #14 0xffffffff8078f09c in sleepq_timeout (arg=0xfffff8005b6a1000) at
> /usr/src/sys/kern/subr_sleepqueue.c:1042
> #15 0xffffffff8075594a in softclock_call_cc (c=0xfffff8005b6a1470,
> cc=0xffffffff811a0580 <cc_cpu+320>, now=190543869798505, direct=1) at
> /usr/src/sys/kern/kern_timeout.c:768
> #16 0xffffffff8075567c in callout_process (now=190543869798505) at
> /usr/src/sys/kern/kern_timeout.c:511
> #17 0xffffffff80a3d455 in handleevents (now=190543869798505, fake=0) at
> /usr/src/sys/kern/kern_clocksource.c:213
> #18 0xffffffff80a3da46 in timercb (et=0xffffffff81251238 <lapic_et>,
> arg=<optimized out>) at /usr/src/sys/kern/kern_clocksource.c:357
> #19 0xffffffff80a680cb in lapic_handle_timer (frame=0xfffffe01091fd7d0) at
> /usr/src/sys/x86/x86/local_apic.c:1339
> #20 <signal handler called>
> #21 0xffffffff81d90654 in buf_hash_find
> ...
>
> (kgdb) fr 18
> #18 0xffffffff80a3da46 in timercb (et=0xffffffff81251238 <lapic_et>,
> arg=<optimized out>) at /usr/src/sys/kern/kern_clocksource.c:357
> 357 /usr/src/sys/kern/kern_clocksource.c: No such file or directory.
> (kgdb) i loc
> state = <optimized out>
> next = 0xfffffe00015b7b80
> now = 190543869798505
> bcast = <error reading variable bcast (Cannot access memory at address 0x0)>
> cpu = <optimized out>
>
> (kgdb) fr 15
> #15 0xffffffff8075594a in softclock_call_cc (c=0xfffff8005b6a1470,
> cc=0xffffffff811a0580 <cc_cpu+320>, now=190543869798505, direct=1) at
> /usr/src/sys/kern/kern_timeout.c:768
> 768 /usr/src/sys/kern/kern_timeout.c: No such file or directory.
> (kgdb) p *c
> $1 = {c_links = {le = {le_next = 0x0, le_prev = 0xfffffe000029f398}, sle =
> {sle_next = 0x0}, tqe = {tqe_next = 0x0, tqe_prev = 0xfffffe000029f398}},
> c_time = 190543869738008, c_precision = 2684354, c_arg = 0xfffff8005b6a1000,
> c_func = 0xffffffff8078f000 <sleepq_timeout>, c_lock = 0x0, c_flags = 2,
> c_iflags = 272, c_cpu = 1, c_exec_time = 190543869798505, c_lines = {u128 =
> 5514281696343066435261632100963648521, u16 = {1033, 755, 1225, 628, 409,
> 651, 788, 1062}}}
>
> Here is an explanation for the numbers reproted in the panic message (sorted
> from earliest to latest):
> 190543869603412 - 'now' as seen in sleepq_timeout(), returned by sbinuptime();
> 190543869738008 - td_sleeptimo, also c_time in the callout;
> 190543869798505 - 'now' as captured when the LAPIC timer fired, seen in the
> stack trace and also recorded as c_exec_time in the callout.
>
> Just in case, here is the code that printed the panic message:
> panic("too early sleepq timeout, %jd > %jd (%jd)",
>
>
> (intmax_t)td->td_sleeptimo,
>
>
> (intmax_t)sbinuptime(),
>
>
> (intmax_t)td->td_slpcallout.c_exec_time);
>
> As can be seen, sbinuptime() in sleepq_timeout() returned a value smaller
> than what sbinuptime() returned in timercb().
>
> It seems that the code in callout_process() was right to fire the callout
> because now > td_sleeptimo there.
> But sleepq_timeout() thought that it was premature as sbinuptime() <
> td_sleeptimo there.
>
> In the above case the eventtimer is obviously LAPIC, the timecounter is HPET.
> But we also saw the same issue when we changed the timecounter to ACPI-fast.
> I think that we haven't tried any other choices because:
> kern.timecounter.tc.ACPI-fast.quality: 900
> kern.timecounter.tc.i8254.quality: 0
> kern.timecounter.tc.HPET.quality: 950
> kern.timecounter.tc.TSC-low.quality: -100
>
> I see three possibilities of why sbinuptime() could go backwards:
> - a bug in FreeBSD time keeping code that gets triggered only under very
> specific conditions that don't normally happen
> - a bug in VMWare, e.g. in HPET and ACPI timer emulation
> - something exotic, like correct C code miscompiled to incorrect machine
> code or weird memory model violation or etc...
>
> I'd appreciate any suggestions on additional diagnostics to narrow down /
> rule out the possibilities.
I do not have any other idea than to try and see if sbinuptime() might go
backward, by running a parallel test of it on several CPUs. Might be,
also record the raw timecounter value from the hw register.
>
> P.S.
> As a workaround I could modify sleepq_timeout() to get "current time" from
> c_exec_time (added by us) instead of sbinuptime(). c_exec_time is the value
> of 'now' in callout_process() when it decides that the callout should fire.
> But I'd like to get to the bottom of the issue.
This was done quite recently in 6df1359e5542f69179c142 by mav.