1680854946
Right now, userspace (fast) gettimeofday(2) on x86 only works for RDTSC. For older machines, like Core2, where RDTSC is not C2/C3 invariant, and which fall to HPET hardware, this means that the call has both the penalty of the syscall and of the uncached hw behind the QPI or PCIe connection to the sought bridge. Nothing can me done against the access latency, but the syscall overhead can be removed. System already provides mappable /dev/hpetX devices, which gives straight access to the HPET registers page. Add yet another algorithm to the x86 'vdso' timehands. Libc is updated to handle both RDTSC and HPET. For HPET, the index of the hpet device to mmap is passed from kernel to userspace, index might be changed and libc invalidates its mapping as needed. Remove cpu_fill_vdso_timehands() KPI, instead require that timecounters which can be used from userspace, to provide tc_fill_vdso_timehands{,32}() methods. Merge i386 and amd64 libc/<arch>/sys/__vdso_gettc.c into one source file in the new libc/x86/sys location. __vdso_gettc() internal interface is changed to move timecounter algorithm detection into the MD code. Measurements show that RDTSC even with the syscall overhead is faster than userspace HPET access. But still, userspace HPET is three-four times faster than syscall HPET on several Core2 and SandyBridge machines. Tested by: Howard Su <howard0su@gmail.com> Sponsored by: The FreeBSD Foundation MFC after: 1 month Differential revision: https://reviews.freebsd.org/D7473
98 lines
3.1 KiB
C
98 lines
3.1 KiB
C
/*-
|
|
* ----------------------------------------------------------------------------
|
|
* "THE BEER-WARE LICENSE" (Revision 42):
|
|
* <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
|
* can do whatever you want with this stuff. If we meet some day, and you think
|
|
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#ifndef _SYS_TIMETC_H_
|
|
#define _SYS_TIMETC_H_
|
|
|
|
#ifndef _KERNEL
|
|
#error "no user-serviceable parts inside"
|
|
#endif
|
|
|
|
/*-
|
|
* `struct timecounter' is the interface between the hardware which implements
|
|
* a timecounter and the MI code which uses this to keep track of time.
|
|
*
|
|
* A timecounter is a binary counter which has two properties:
|
|
* * it runs at a fixed, known frequency.
|
|
* * it has sufficient bits to not roll over in less than approximately
|
|
* max(2 msec, 2/HZ seconds). (The value 2 here is really 1 + delta,
|
|
* for some indeterminate value of delta.)
|
|
*/
|
|
|
|
struct timecounter;
|
|
struct vdso_timehands;
|
|
struct vdso_timehands32;
|
|
typedef u_int timecounter_get_t(struct timecounter *);
|
|
typedef void timecounter_pps_t(struct timecounter *);
|
|
typedef uint32_t timecounter_fill_vdso_timehands_t(struct vdso_timehands *,
|
|
struct timecounter *);
|
|
typedef uint32_t timecounter_fill_vdso_timehands32_t(struct vdso_timehands32 *,
|
|
struct timecounter *);
|
|
|
|
struct timecounter {
|
|
timecounter_get_t *tc_get_timecount;
|
|
/*
|
|
* This function reads the counter. It is not required to
|
|
* mask any unimplemented bits out, as long as they are
|
|
* constant.
|
|
*/
|
|
timecounter_pps_t *tc_poll_pps;
|
|
/*
|
|
* This function is optional. It will be called whenever the
|
|
* timecounter is rewound, and is intended to check for PPS
|
|
* events. Normal hardware does not need it but timecounters
|
|
* which latch PPS in hardware (like sys/pci/xrpu.c) do.
|
|
*/
|
|
u_int tc_counter_mask;
|
|
/* This mask should mask off any unimplemented bits. */
|
|
uint64_t tc_frequency;
|
|
/* Frequency of the counter in Hz. */
|
|
const 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".
|
|
*/
|
|
u_int tc_flags;
|
|
#define TC_FLAGS_C2STOP 1 /* Timer dies in C2+. */
|
|
#define TC_FLAGS_SUSPEND_SAFE 2 /*
|
|
* Timer functional across
|
|
* suspend/resume.
|
|
*/
|
|
|
|
void *tc_priv;
|
|
/* Pointer to the timecounter's private parts. */
|
|
struct timecounter *tc_next;
|
|
/* Pointer to the next timecounter. */
|
|
timecounter_fill_vdso_timehands_t *tc_fill_vdso_timehands;
|
|
timecounter_fill_vdso_timehands32_t *tc_fill_vdso_timehands32;
|
|
};
|
|
|
|
extern struct timecounter *timecounter;
|
|
extern int tc_min_ticktock_freq; /*
|
|
* Minimal tc_ticktock() call frequency,
|
|
* required to handle counter wraps.
|
|
*/
|
|
|
|
u_int64_t tc_getfrequency(void);
|
|
void tc_init(struct timecounter *tc);
|
|
void tc_setclock(struct timespec *ts);
|
|
void tc_ticktock(int cnt);
|
|
void cpu_tick_calibration(void);
|
|
|
|
#ifdef SYSCTL_DECL
|
|
SYSCTL_DECL(_kern_timecounter);
|
|
#endif
|
|
|
|
#endif /* !_SYS_TIMETC_H_ */
|