From ea389aaaddefd1606632843e38923ddba098574b Mon Sep 17 00:00:00 2001 From: Mark Johnston Date: Sat, 16 Oct 2021 09:46:55 -0400 Subject: timecounter: Lock the timecounter list Timecounter registration is dynamic, i.e., there is no requirement that timecounters must be registered during single-threaded boot. Loadable drivers may in principle register timecounters (which can be switched to automatically). Timecounters cannot be unregistered, though this could be implemented. Registered timecounters belong to a global linked list. Add a mutex to synchronize insertions and the traversals done by (mpsafe) sysctl handlers. No functional change intended. Reviewed by: imp, kib MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D32511 --- cpukit/score/src/kern_tc.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/cpukit/score/src/kern_tc.c b/cpukit/score/src/kern_tc.c index e8c773a890..d8f92e11dd 100644 --- a/cpukit/score/src/kern_tc.c +++ b/cpukit/score/src/kern_tc.c @@ -199,6 +199,10 @@ static struct timehands *volatile timehands = &ths[0]; struct timecounter *timecounter = &dummy_timecounter; static struct timecounter *timecounters = &dummy_timecounter; +/* Mutex to protect the timecounter list. */ +static struct mtx tc_lock; +MTX_SYSINIT(tc_lock, &tc_lock, "tc", MTX_DEF); + int tc_min_ticktock_freq = 1; #else /* __rtems__ */ /* @@ -1373,8 +1377,6 @@ tc_init(struct timecounter *tc) tc->tc_quality); } - tc->tc_next = timecounters; - timecounters = tc; /* * Set up sysctl tree for this counter. */ @@ -1396,6 +1398,11 @@ tc_init(struct timecounter *tc) SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, "quality", CTLFLAG_RD, &(tc->tc_quality), 0, "goodness of time counter"); + + mtx_lock(&tc_lock); + tc->tc_next = timecounters; + timecounters = tc; + /* * Do not automatically switch if the current tc was specifically * chosen. Never automatically use a timecounter with negative quality. @@ -1403,22 +1410,24 @@ tc_init(struct timecounter *tc) * worse since this timecounter may not be monotonic. */ if (tc_chosen) - return; + goto unlock; if (tc->tc_quality < 0) - return; + goto unlock; if (tc_from_tunable[0] != '\0' && strcmp(tc->tc_name, tc_from_tunable) == 0) { tc_chosen = 1; tc_from_tunable[0] = '\0'; } else { if (tc->tc_quality < timecounter->tc_quality) - return; + goto unlock; if (tc->tc_quality == timecounter->tc_quality && tc->tc_frequency < timecounter->tc_frequency) - return; + goto unlock; } (void)tc->tc_get_timecount(tc); timecounter = tc; +unlock: + mtx_unlock(&tc_lock); #else /* __rtems__ */ if (tc->tc_quality < timecounter->tc_quality) return; @@ -1722,16 +1731,22 @@ sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS) struct timecounter *newtc, *tc; int error; + mtx_lock(&tc_lock); tc = timecounter; strlcpy(newname, tc->tc_name, sizeof(newname)); + mtx_unlock(&tc_lock); error = sysctl_handle_string(oidp, &newname[0], sizeof(newname), req); if (error != 0 || req->newptr == NULL) return (error); + + mtx_lock(&tc_lock); /* Record that the tc in use now was specifically chosen. */ tc_chosen = 1; - if (strcmp(newname, tc->tc_name) == 0) + if (strcmp(newname, tc->tc_name) == 0) { + mtx_unlock(&tc_lock); return (0); + } for (newtc = timecounters; newtc != NULL; newtc = newtc->tc_next) { if (strcmp(newname, newtc->tc_name) != 0) continue; @@ -1749,11 +1764,11 @@ sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS) * use any locking and that it can be called in hard interrupt * context via 'tc_windup()'. */ - return (0); + break; } - return (EINVAL); + mtx_unlock(&tc_lock); + return (newtc != NULL ? 0 : EINVAL); } - SYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE, 0, 0, sysctl_kern_timecounter_hardware, "A", @@ -1767,12 +1782,17 @@ sysctl_kern_timecounter_choice(SYSCTL_HANDLER_ARGS) struct timecounter *tc; int error; + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + return (error); sbuf_new_for_sysctl(&sb, NULL, 0, req); + mtx_lock(&tc_lock); for (tc = timecounters; tc != NULL; tc = tc->tc_next) { if (tc != timecounters) sbuf_putc(&sb, ' '); sbuf_printf(&sb, "%s(%d)", tc->tc_name, tc->tc_quality); } + mtx_unlock(&tc_lock); error = sbuf_finish(&sb); sbuf_delete(&sb); return (error); -- cgit v1.2.3