mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-05 15:15:44 +00:00
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
This commit is contained in:
committed by
Moyano, Gabriel
parent
f03147f172
commit
e27dc9d6ac
@@ -182,6 +182,10 @@ static struct timehands *volatile timehands = &ths[0];
|
|||||||
struct timecounter *timecounter = &dummy_timecounter;
|
struct timecounter *timecounter = &dummy_timecounter;
|
||||||
static struct timecounter *timecounters = &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;
|
int tc_min_ticktock_freq = 1;
|
||||||
#else /* __rtems__ */
|
#else /* __rtems__ */
|
||||||
/*
|
/*
|
||||||
@@ -1356,8 +1360,6 @@ tc_init(struct timecounter *tc)
|
|||||||
tc->tc_quality);
|
tc->tc_quality);
|
||||||
}
|
}
|
||||||
|
|
||||||
tc->tc_next = timecounters;
|
|
||||||
timecounters = tc;
|
|
||||||
/*
|
/*
|
||||||
* Set up sysctl tree for this counter.
|
* Set up sysctl tree for this counter.
|
||||||
*/
|
*/
|
||||||
@@ -1379,6 +1381,11 @@ tc_init(struct timecounter *tc)
|
|||||||
SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO,
|
SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO,
|
||||||
"quality", CTLFLAG_RD, &(tc->tc_quality), 0,
|
"quality", CTLFLAG_RD, &(tc->tc_quality), 0,
|
||||||
"goodness of time counter");
|
"goodness of time counter");
|
||||||
|
|
||||||
|
mtx_lock(&tc_lock);
|
||||||
|
tc->tc_next = timecounters;
|
||||||
|
timecounters = tc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do not automatically switch if the current tc was specifically
|
* Do not automatically switch if the current tc was specifically
|
||||||
* chosen. Never automatically use a timecounter with negative quality.
|
* chosen. Never automatically use a timecounter with negative quality.
|
||||||
@@ -1386,22 +1393,24 @@ tc_init(struct timecounter *tc)
|
|||||||
* worse since this timecounter may not be monotonic.
|
* worse since this timecounter may not be monotonic.
|
||||||
*/
|
*/
|
||||||
if (tc_chosen)
|
if (tc_chosen)
|
||||||
return;
|
goto unlock;
|
||||||
if (tc->tc_quality < 0)
|
if (tc->tc_quality < 0)
|
||||||
return;
|
goto unlock;
|
||||||
if (tc_from_tunable[0] != '\0' &&
|
if (tc_from_tunable[0] != '\0' &&
|
||||||
strcmp(tc->tc_name, tc_from_tunable) == 0) {
|
strcmp(tc->tc_name, tc_from_tunable) == 0) {
|
||||||
tc_chosen = 1;
|
tc_chosen = 1;
|
||||||
tc_from_tunable[0] = '\0';
|
tc_from_tunable[0] = '\0';
|
||||||
} else {
|
} else {
|
||||||
if (tc->tc_quality < timecounter->tc_quality)
|
if (tc->tc_quality < timecounter->tc_quality)
|
||||||
return;
|
goto unlock;
|
||||||
if (tc->tc_quality == timecounter->tc_quality &&
|
if (tc->tc_quality == timecounter->tc_quality &&
|
||||||
tc->tc_frequency < timecounter->tc_frequency)
|
tc->tc_frequency < timecounter->tc_frequency)
|
||||||
return;
|
goto unlock;
|
||||||
}
|
}
|
||||||
(void)tc->tc_get_timecount(tc);
|
(void)tc->tc_get_timecount(tc);
|
||||||
timecounter = tc;
|
timecounter = tc;
|
||||||
|
unlock:
|
||||||
|
mtx_unlock(&tc_lock);
|
||||||
#else /* __rtems__ */
|
#else /* __rtems__ */
|
||||||
if (tc->tc_quality < timecounter->tc_quality)
|
if (tc->tc_quality < timecounter->tc_quality)
|
||||||
return;
|
return;
|
||||||
@@ -1705,16 +1714,22 @@ sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS)
|
|||||||
struct timecounter *newtc, *tc;
|
struct timecounter *newtc, *tc;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
mtx_lock(&tc_lock);
|
||||||
tc = timecounter;
|
tc = timecounter;
|
||||||
strlcpy(newname, tc->tc_name, sizeof(newname));
|
strlcpy(newname, tc->tc_name, sizeof(newname));
|
||||||
|
mtx_unlock(&tc_lock);
|
||||||
|
|
||||||
error = sysctl_handle_string(oidp, &newname[0], sizeof(newname), req);
|
error = sysctl_handle_string(oidp, &newname[0], sizeof(newname), req);
|
||||||
if (error != 0 || req->newptr == NULL)
|
if (error != 0 || req->newptr == NULL)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
|
mtx_lock(&tc_lock);
|
||||||
/* Record that the tc in use now was specifically chosen. */
|
/* Record that the tc in use now was specifically chosen. */
|
||||||
tc_chosen = 1;
|
tc_chosen = 1;
|
||||||
if (strcmp(newname, tc->tc_name) == 0)
|
if (strcmp(newname, tc->tc_name) == 0) {
|
||||||
|
mtx_unlock(&tc_lock);
|
||||||
return (0);
|
return (0);
|
||||||
|
}
|
||||||
for (newtc = timecounters; newtc != NULL; newtc = newtc->tc_next) {
|
for (newtc = timecounters; newtc != NULL; newtc = newtc->tc_next) {
|
||||||
if (strcmp(newname, newtc->tc_name) != 0)
|
if (strcmp(newname, newtc->tc_name) != 0)
|
||||||
continue;
|
continue;
|
||||||
@@ -1732,11 +1747,11 @@ sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS)
|
|||||||
* use any locking and that it can be called in hard interrupt
|
* use any locking and that it can be called in hard interrupt
|
||||||
* context via 'tc_windup()'.
|
* 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,
|
SYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware,
|
||||||
CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE, 0, 0,
|
CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE, 0, 0,
|
||||||
sysctl_kern_timecounter_hardware, "A",
|
sysctl_kern_timecounter_hardware, "A",
|
||||||
@@ -1750,12 +1765,17 @@ sysctl_kern_timecounter_choice(SYSCTL_HANDLER_ARGS)
|
|||||||
struct timecounter *tc;
|
struct timecounter *tc;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
error = sysctl_wire_old_buffer(req, 0);
|
||||||
|
if (error != 0)
|
||||||
|
return (error);
|
||||||
sbuf_new_for_sysctl(&sb, NULL, 0, req);
|
sbuf_new_for_sysctl(&sb, NULL, 0, req);
|
||||||
|
mtx_lock(&tc_lock);
|
||||||
for (tc = timecounters; tc != NULL; tc = tc->tc_next) {
|
for (tc = timecounters; tc != NULL; tc = tc->tc_next) {
|
||||||
if (tc != timecounters)
|
if (tc != timecounters)
|
||||||
sbuf_putc(&sb, ' ');
|
sbuf_putc(&sb, ' ');
|
||||||
sbuf_printf(&sb, "%s(%d)", tc->tc_name, tc->tc_quality);
|
sbuf_printf(&sb, "%s(%d)", tc->tc_name, tc->tc_quality);
|
||||||
}
|
}
|
||||||
|
mtx_unlock(&tc_lock);
|
||||||
error = sbuf_finish(&sb);
|
error = sbuf_finish(&sb);
|
||||||
sbuf_delete(&sb);
|
sbuf_delete(&sb);
|
||||||
return (error);
|
return (error);
|
||||||
|
|||||||
Reference in New Issue
Block a user