Give timecounters a numeric quality field.
A timecounter will be selected when registered if its quality is not negative and no less than the current timecounters. Add a sysctl to report all available timecounters and their qualities. Give the dummy timecounter a solid negative quality of minus a million. Give the i8254 zero and the ACPI 1000. The TSC gets 800, unless APM or SMP forces it negative. Other timecounters default to zero quality and thereby retain current selection behaviour.
This commit is contained in:
parent
f6c098e569
commit
78a49a45bc
@ -98,11 +98,12 @@ DRIVER_MODULE(acpi_timer, acpi, acpi_timer_driver, acpi_timer_devclass, 0, 0);
|
||||
* Timecounter.
|
||||
*/
|
||||
static struct timecounter acpi_timer_timecounter = {
|
||||
acpi_timer_get_timecount_safe,
|
||||
0,
|
||||
0xffffff,
|
||||
0,
|
||||
"ACPI"
|
||||
acpi_timer_get_timecount_safe,
|
||||
0,
|
||||
0xffffff,
|
||||
0,
|
||||
"ACPI",
|
||||
1000
|
||||
};
|
||||
|
||||
|
||||
|
@ -60,7 +60,8 @@ static struct timecounter tsc_timecounter = {
|
||||
0, /* no poll_pps */
|
||||
~0u, /* counter_mask */
|
||||
0, /* frequency */
|
||||
"TSC" /* name */
|
||||
"TSC", /* name */
|
||||
800, /* quality (adjusted in code) */
|
||||
};
|
||||
|
||||
void
|
||||
@ -86,20 +87,6 @@ init_TSC(void)
|
||||
tsc_freq = tscval[1] - tscval[0];
|
||||
if (bootverbose)
|
||||
printf("TSC clock: %ju Hz\n", (intmax_t)tsc_freq);
|
||||
|
||||
#ifdef SMP
|
||||
/*
|
||||
* We can not use the TSC in SMP mode unless the TSCs on all CPUs
|
||||
* are somehow synchronized. Some hardware configurations do
|
||||
* this, but we have no way of determining whether this is the
|
||||
* case, so we do not use the TSC in multi-processor systems
|
||||
* unless the user indicated (by setting kern.timecounter.smp_tsc
|
||||
* to 1) that he believes that his TSCs are synchronized.
|
||||
*/
|
||||
if (mp_ncpus > 1 && !smp_tsc)
|
||||
return;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -117,17 +104,28 @@ init_TSC_tc(void)
|
||||
* or not, nor when it might be activated. Play it safe.
|
||||
*/
|
||||
if (power_pm_get_type() == POWER_PM_TYPE_APM) {
|
||||
tsc_timecounter.tc_quality = -1000;
|
||||
if (bootverbose)
|
||||
printf("TSC timecounter disabled: APM enabled.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef SMP
|
||||
/*
|
||||
* We can not use the TSC in SMP mode unless the TSCs on all CPUs
|
||||
* are somehow synchronized. Some hardware configurations do
|
||||
* this, but we have no way of determining whether this is the
|
||||
* case, so we do not use the TSC in multi-processor systems
|
||||
* unless the user indicated (by setting kern.timecounter.smp_tsc
|
||||
* to 1) that he believes that his TSCs are synchronized.
|
||||
*/
|
||||
if (mp_ncpus > 1 && !smp_tsc)
|
||||
tsc_timecounter.tc_quality = -100;
|
||||
#endif
|
||||
|
||||
if (tsc_present && tsc_freq != 0 && !tsc_is_broken) {
|
||||
tsc_timecounter.tc_frequency = tsc_freq;
|
||||
tc_init(&tsc_timecounter);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -155,7 +155,8 @@ static struct timecounter i8254_timecounter = {
|
||||
0, /* no poll_pps */
|
||||
~0u, /* counter_mask */
|
||||
0, /* frequency */
|
||||
"i8254" /* name */
|
||||
"i8254", /* name */
|
||||
0 /* quality */
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -155,7 +155,8 @@ static struct timecounter i8254_timecounter = {
|
||||
0, /* no poll_pps */
|
||||
~0u, /* counter_mask */
|
||||
0, /* frequency */
|
||||
"i8254" /* name */
|
||||
"i8254", /* name */
|
||||
0 /* quality */
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -43,7 +43,7 @@ dummy_get_timecount(struct timecounter *tc)
|
||||
}
|
||||
|
||||
static struct timecounter dummy_timecounter = {
|
||||
dummy_get_timecount, 0, ~0u, 1000000, "dummy",
|
||||
dummy_get_timecount, 0, ~0u, 1000000, "dummy", -1000000
|
||||
};
|
||||
|
||||
struct timehands {
|
||||
@ -281,29 +281,33 @@ getmicrotime(struct timeval *tvp)
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a new timecounter.
|
||||
* We should really try to rank the timecounters and intelligently determine
|
||||
* if the new timecounter is better than the current one. This is subject
|
||||
* to further study. For now always use the new timecounter.
|
||||
* Initialize a new timecounter and possibly use it.
|
||||
*/
|
||||
void
|
||||
tc_init(struct timecounter *tc)
|
||||
{
|
||||
unsigned u;
|
||||
|
||||
printf("Timecounter \"%s\" frequency %ju Hz",
|
||||
tc->tc_name, (intmax_t)tc->tc_frequency);
|
||||
if (tc->tc_quality >= 0 || bootverbose)
|
||||
printf("Timecounter \"%s\" frequency %ju Hz quality %d",
|
||||
tc->tc_name, (intmax_t)tc->tc_frequency,
|
||||
tc->tc_quality);
|
||||
|
||||
u = tc->tc_frequency / tc->tc_counter_mask;
|
||||
if (u > hz) {
|
||||
printf(" -- Insufficient hz, needs at least %u\n", u);
|
||||
return;
|
||||
}
|
||||
printf("\n");
|
||||
tc->tc_next = timecounters;
|
||||
timecounters = tc;
|
||||
printf("\n");
|
||||
(void)tc->tc_get_timecount(tc);
|
||||
(void)tc->tc_get_timecount(tc);
|
||||
/* Never automatically use a timecounter with negative quality */
|
||||
if (tc->tc_quality < 0)
|
||||
return;
|
||||
if (tc->tc_quality < timecounter->tc_quality)
|
||||
return;
|
||||
timecounter = tc;
|
||||
}
|
||||
|
||||
@ -496,6 +500,29 @@ sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS)
|
||||
SYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, CTLTYPE_STRING | CTLFLAG_RW,
|
||||
0, 0, sysctl_kern_timecounter_hardware, "A", "");
|
||||
|
||||
|
||||
/* Report or change the active timecounter hardware. */
|
||||
static int
|
||||
sysctl_kern_timecounter_choice(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
char buf[32], *spc;
|
||||
struct timecounter *tc;
|
||||
int error;
|
||||
|
||||
spc = "";
|
||||
error = 0;
|
||||
for (tc = timecounters; error == 0 && tc != NULL; tc = tc->tc_next) {
|
||||
sprintf(buf, "%s%s(%d)",
|
||||
spc, tc->tc_name, tc->tc_quality);
|
||||
error = SYSCTL_OUT(req, buf, strlen(buf));
|
||||
spc = " ";
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
SYSCTL_PROC(_kern_timecounter, OID_AUTO, choice, CTLTYPE_STRING | CTLFLAG_RD,
|
||||
0, 0, sysctl_kern_timecounter_choice, "A", "");
|
||||
|
||||
/*
|
||||
* RFC 2783 PPS-API implementation.
|
||||
*/
|
||||
|
@ -51,6 +51,13 @@ struct timecounter {
|
||||
/* Frequency of the counter in Hz. */
|
||||
char *tc_name;
|
||||
/* Name of the timecounter. */
|
||||
int tc_quality;
|
||||
/*
|
||||
* Used to determine if this timecounter is better than
|
||||
* another timecounter higher means better. Negative
|
||||
* means "only use at explicit request".
|
||||
*/
|
||||
|
||||
void *tc_priv;
|
||||
/* Pointer to the timecounter's private parts. */
|
||||
struct timecounter *tc_next;
|
||||
|
Loading…
x
Reference in New Issue
Block a user