Strange issue after early AP startup
Hans Petter Selasky
hps at selasky.org
Tue Jan 17 19:32:04 UTC 2017
Hi,
On 01/17/17 20:00, John Baldwin wrote:
>>
>> Does this matter for the first tick? How often is configtimer() called?
>
> As I said, it is called at runtime when profclock is started / stopped, not
> just at boot. Those changes at runtime probably have existing callouts
> active and your change will not process any callouts until the next hardclock
> tick fires (but only because you are setting nextcallopt to the bogus
> 'next' value).
>> > (One odd thing is that even in your case the first call to
>> handleevents(),
>> > the 'now => state->nextcallout' check in handleevents() should be true
>> > which resets both nextcall and nextcallopt and invokes
>> callout_process().)
>>
>> Let me take you through the failure path, by code inspection:
>
> I would really appreciate it if you could add traces to find out what actually
> happens rather than what seems to happen by looking at the code. :-/
The problem is that once you add some prints, the problem goes away.
Maybe I should try to set hz to 100 or 25 ???
>
> 0) cpu_initclocks_bsp() is called and init's nextcall and nexcallopt to SBT_MAX
> similar to your change. If no callout is scheduled before configtimer()
> then they remain set to SBT_MAX. Your current patch happens to trigger a
> (bogus) call to callout_process() on the first hardclock() because it
> sets nextcallopt to 'next' even though no callout is actually scheduled to
> fire at time 'next'.
>
>> 1) configtimer() is called and we init nextcall and nextcallopt:
>>
>> > next = now + timerperiod;
>> ...
>> > state->nextcall = next;
>> > state->nextcallopt = next;
>
> These both say "the next callout() should fire at 'next' which is the time of
> the next hardclock()", even though there may be no callouts scheduled (in which
> case both of these fields should be set to SBT_MAX from the call to
> cpu_initclocks_bsp(), or there may be callouts scheduled in which case 'nextcall'
> and 'nextcallopt' will reflect the time that those callouts are already
> scheduled for and this overwrites that).
I see there are some callouts scheduled by SYSINITs, before the first
configtimer(), like NFS_TIMERINIT in nfs_init(). These are setup using
"dummy_timecounter" which means any nextcall values before the first
configtimer should be discarded.
>
>> 2) Any callout_reset() calls cpu_new_callout():
>>
>> > */
>> > state->nextcallopt = bt_opt;
>> > if (bt >= state->nextcall)
>> We follow this path, because "bt" is surely based on sbinuptime() and is
>> greater or equal to state->nextcall. Note that state->nextcallopt is
>> updated to bt_opt, which is in the future.
>
> Note, my patch should _leave_ nextcall at SBT_MAX (from cpu_initclocks_bsp())
> unless there was already an earlier call to callout_reset().
Yes, there are calls to callout_reset(). See for example NFS_TIMERINIT,
like mentioned above.
> IOW, it
> should be a NOP for the purposes of this branch compared with your change.
> (You could add a warning to print out if 'nextcall' != SBT_MAX during boot
> and see if it fires for example.)
>
>> > goto done;
>> > state->nextcall = bt;
>>
>> 3) getnextcpuevent(0) is called by the fast timercb() to setup the next
>> event:
>>
>> > state = DPCPU_PTR(timerstate);
>> > /* Handle hardclock() events, skipping some if CPU is idle. */
>> > event = state->nexthard;
>> ...
>> > /* Handle callout events. */
>> > if (event > state->nextcall)
>> We then go looping into this path, because state->nextcall is still
>> equal to "next" as in step 1) which is now in the past, until "now >=
>> state->nextcallopt" inside handleevents(), which clears this condition.
>> > event = state->nextcall;
>> ...
>> > return (event);
>
> I'm curious if there is a callout_reset() that has set 'nextcall' to a time
> that is effectively before 'now'.
>
> Maybe add a printf like this:
>
> Index: kern_clocksource.c
> ===================================================================
> --- kern_clocksource.c (revision 312301)
> +++ kern_clocksource.c (working copy)
> @@ -498,12 +498,18 @@ configtimer(int start)
> CPU_FOREACH(cpu) {
> state = DPCPU_ID_PTR(cpu, timerstate);
> state->now = now;
> + printf("%s: CPU %d: now %jd nextcall %jd nextcallopt %jd next %jd\n", __func__, cpu, state->nextcall, state->nextcall, next);
> #ifndef EARLY_AP_STARTUP
> if (!smp_started && cpu != CPU_FIRST())
> state->nextevent = SBT_MAX;
> else
> #endif
>
> In particular what I am worried about with your patch is that for
> post-boot calls to configtimer() you will delay any previously-scheduled
> callouts until the next hardclock.
>
I understand. Would a solution be to refactor callout_process(), to
accept the PCPU_GET(CPUID) as an argument and be executed for all CPUs
by configtimer(), instead of trying to guess state->nextcall and
state->nextcallopt in configtimer() ?
--HPS
More information about the freebsd-current
mailing list