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:
Poul-Henning Kamp 2003-08-16 08:23:53 +00:00
parent f6c098e569
commit 78a49a45bc
6 changed files with 68 additions and 33 deletions

View File

@ -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
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.
*/

View File

@ -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;