Re: Dynamic timecounter changes
- In reply to: Mark Johnston : "Re: Dynamic timecounter changes"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 28 Oct 2021 18:15:50 UTC
On Thu, Oct 28, 2021 at 12:43:55PM -0400, Mark Johnston wrote:
> On Thu, Oct 28, 2021 at 10:52:59AM +0200, Sebastian Huber wrote:
> > Hello,
> >
> > there was a recent change which protected timecounter changes with a mutex:
> >
> > https://github.com/freebsd/freebsd-src/commit/621fd9dcb2d83daab477c130bc99b905f6fc27dc
> >
> > If the timecounter can change dynamically, could tc_windup() see
> > different timercounter here:
> >
> > /*
> > * Capture a timecounter delta on the current timecounter and if
> > * changing timecounters, a counter value from the new timecounter.
> > * Update the offset fields accordingly.
> > */
> > delta = tc_delta(th);
> > if (th->th_counter != timecounter)
> > ncount = timecounter->tc_get_timecount(timecounter);
> > else
> > ncount = 0;
> >
> > and here:
> >
> > /* Now is a good time to change timecounters. */
> > if (th->th_counter != timecounter) {
> > #ifndef __arm__
> > if ((timecounter->tc_flags & TC_FLAGS_C2STOP) != 0)
> > cpu_disable_c2_sleep++;
> > if ((th->th_counter->tc_flags & TC_FLAGS_C2STOP) != 0)
> > cpu_disable_c2_sleep--;
> > #endif
> > th->th_counter = timecounter;
> > th->th_offset_count = ncount;
> > tc_min_ticktock_freq = max(1, timecounter->tc_frequency /
> > (((uint64_t)timecounter->tc_counter_mask + 1) / 3));
> > recalculate_scaling_factor_and_large_delta(th);
> > #ifdef FFCLOCK
> > ffclock_change_tc(th);
> > #endif
> > }
> >
> > An ncount value from two different timecounter would be used in this
> > case. Maybe the "timecounter" global variable should be just read once
> > into a local variable.
>
> I think you are right. An alternate solution would be to synchronize
> updates of the global "timecounter" variable with tc_setclock_mtx. That
> lock can't trivially be used to protect the list since it's a spin mutex
> and sysctl_kern_timecounter_choice() can't hold it across sbuf_printf()
> calls.
You can protect list update both with tc_lock and tc_setclock spinlock.
Then the iteration can use tc_lock as it does now.
>
> Hmm, but the ACPI timer driver also updates the selected timecounter.
> Perhaps loading "timecounter" once is the better solution for now.
>
> diff --git a/sys/kern/kern_tc.c b/sys/kern/kern_tc.c
> index 9a928ca37aff..611d81b3280b 100644
> --- a/sys/kern/kern_tc.c
> +++ b/sys/kern/kern_tc.c
> @@ -1319,6 +1319,7 @@ static void
> tc_windup(struct bintime *new_boottimebin)
> {
> struct bintime bt;
> + struct timecounter *tc;
> struct timehands *th, *tho;
> uint64_t scale;
> u_int delta, ncount, ogen;
> @@ -1348,9 +1349,10 @@ tc_windup(struct bintime *new_boottimebin)
> * changing timecounters, a counter value from the new timecounter.
> * Update the offset fields accordingly.
> */
> + tc = atomic_load_ptr(&timecounter);
> delta = tc_delta(th);
> - if (th->th_counter != timecounter)
> - ncount = timecounter->tc_get_timecount(timecounter);
> + if (th->th_counter != tc)
> + ncount = tc->tc_get_timecount(tc);
> else
> ncount = 0;
> #ifdef FFCLOCK
> @@ -1407,17 +1409,17 @@ tc_windup(struct bintime *new_boottimebin)
> bintime2timespec(&bt, &th->th_nanotime);
>
> /* Now is a good time to change timecounters. */
> - if (th->th_counter != timecounter) {
> + if (th->th_counter != tc) {
> #ifndef __arm__
> - if ((timecounter->tc_flags & TC_FLAGS_C2STOP) != 0)
> + if ((tc->tc_flags & TC_FLAGS_C2STOP) != 0)
> cpu_disable_c2_sleep++;
> if ((th->th_counter->tc_flags & TC_FLAGS_C2STOP) != 0)
> cpu_disable_c2_sleep--;
> #endif
> - th->th_counter = timecounter;
> + th->th_counter = tc;
> th->th_offset_count = ncount;
> - tc_min_ticktock_freq = max(1, timecounter->tc_frequency /
> - (((uint64_t)timecounter->tc_counter_mask + 1) / 3));
> + tc_min_ticktock_freq = max(1, tc->tc_frequency /
> + (((uint64_t)tc->tc_counter_mask + 1) / 3));
> #ifdef FFCLOCK
> ffclock_change_tc(th);
> #endif
I think this would be useful regardless of the locking.