opnsense-src/sys/sys/time.h
Davide Italiano 5b999a6be0 - Make callout(9) tickless, relying on eventtimers(4) as backend for
precise time event generation. This greatly improves granularity of
callouts which are not anymore constrained to wait next tick to be
scheduled.
- Extend the callout KPI introducing a set of callout_reset_sbt* functions,
which take a sbintime_t as timeout argument. The new KPI also offers a
way for consumers to specify precision tolerance they allow, so that
callout can coalesce events and reduce number of interrupts as well as
potentially avoid scheduling a SWI thread.
- Introduce support for dispatching callouts directly from hardware
interrupt context, specifying an additional flag. This feature should be
used carefully, as long as interrupt context has some limitations
(e.g. no sleeping locks can be held).
- Enhance mechanisms to gather informations about callwheel, introducing
a new sysctl to obtain stats.

This change breaks the KBI. struct callout fields has been changed, in
particular 'int ticks' (4 bytes) has been replaced with 'sbintime_t'
(8 bytes) and another 'sbintime_t' field was added for precision.

Together with:	mav
Reviewed by:	attilio, bde, luigi, phk
Sponsored by:	Google Summer of Code 2012, iXsystems inc.
Tested by:	flo (amd64, sparc64), marius (sparc64), ian (arm),
		markj (amd64), mav, Fabian Keil
2013-03-04 11:09:56 +00:00

496 lines
13 KiB
C

/*-
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)time.h 8.5 (Berkeley) 5/4/95
* $FreeBSD$
*/
#ifndef _SYS_TIME_H_
#define _SYS_TIME_H_
#include <sys/_timeval.h>
#include <sys/types.h>
#include <sys/timespec.h>
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of dst correction */
};
#define DST_NONE 0 /* not on dst */
#define DST_USA 1 /* USA style dst */
#define DST_AUST 2 /* Australian style dst */
#define DST_WET 3 /* Western European dst */
#define DST_MET 4 /* Middle European dst */
#define DST_EET 5 /* Eastern European dst */
#define DST_CAN 6 /* Canada */
#if __BSD_VISIBLE
struct bintime {
time_t sec;
uint64_t frac;
};
static __inline void
bintime_addx(struct bintime *bt, uint64_t x)
{
uint64_t u;
u = bt->frac;
bt->frac += x;
if (u > bt->frac)
bt->sec++;
}
static __inline void
bintime_add(struct bintime *bt, const struct bintime *bt2)
{
uint64_t u;
u = bt->frac;
bt->frac += bt2->frac;
if (u > bt->frac)
bt->sec++;
bt->sec += bt2->sec;
}
static __inline void
bintime_sub(struct bintime *bt, const struct bintime *bt2)
{
uint64_t u;
u = bt->frac;
bt->frac -= bt2->frac;
if (u < bt->frac)
bt->sec--;
bt->sec -= bt2->sec;
}
static __inline void
bintime_mul(struct bintime *bt, u_int x)
{
uint64_t p1, p2;
p1 = (bt->frac & 0xffffffffull) * x;
p2 = (bt->frac >> 32) * x + (p1 >> 32);
bt->sec *= x;
bt->sec += (p2 >> 32);
bt->frac = (p2 << 32) | (p1 & 0xffffffffull);
}
static __inline void
bintime_shift(struct bintime *bt, int exp)
{
if (exp > 0) {
bt->sec <<= exp;
bt->sec |= bt->frac >> (64 - exp);
bt->frac <<= exp;
} else if (exp < 0) {
bt->frac >>= -exp;
bt->frac |= (uint64_t)bt->sec << (64 + exp);
bt->sec >>= -exp;
}
}
#define bintime_clear(a) ((a)->sec = (a)->frac = 0)
#define bintime_isset(a) ((a)->sec || (a)->frac)
#define bintime_cmp(a, b, cmp) \
(((a)->sec == (b)->sec) ? \
((a)->frac cmp (b)->frac) : \
((a)->sec cmp (b)->sec))
#define SBT_1S ((sbintime_t)1 << 32)
#define SBT_1M (SBT_1S * 60)
#define SBT_1MS (SBT_1S / 1000)
#define SBT_1US (SBT_1S / 1000000)
#define SBT_1NS (SBT_1S / 1000000000)
static __inline int
sbintime_getsec(sbintime_t sbt)
{
return (sbt >> 32);
}
static __inline sbintime_t
bttosbt(const struct bintime bt)
{
return (((sbintime_t)bt.sec << 32) + (bt.frac >> 32));
}
static __inline struct bintime
sbttobt(sbintime_t sbt)
{
struct bintime bt;
bt.sec = sbt >> 32;
bt.frac = sbt << 32;
return (bt);
}
/*-
* Background information:
*
* When converting between timestamps on parallel timescales of differing
* resolutions it is historical and scientific practice to round down rather
* than doing 4/5 rounding.
*
* The date changes at midnight, not at noon.
*
* Even at 15:59:59.999999999 it's not four'o'clock.
*
* time_second ticks after N.999999999 not after N.4999999999
*/
static __inline void
bintime2timespec(const struct bintime *bt, struct timespec *ts)
{
ts->tv_sec = bt->sec;
ts->tv_nsec = ((uint64_t)1000000000 * (uint32_t)(bt->frac >> 32)) >> 32;
}
static __inline void
timespec2bintime(const struct timespec *ts, struct bintime *bt)
{
bt->sec = ts->tv_sec;
/* 18446744073 = int(2^64 / 1000000000) */
bt->frac = ts->tv_nsec * (uint64_t)18446744073LL;
}
static __inline void
bintime2timeval(const struct bintime *bt, struct timeval *tv)
{
tv->tv_sec = bt->sec;
tv->tv_usec = ((uint64_t)1000000 * (uint32_t)(bt->frac >> 32)) >> 32;
}
static __inline void
timeval2bintime(const struct timeval *tv, struct bintime *bt)
{
bt->sec = tv->tv_sec;
/* 18446744073709 = int(2^64 / 1000000) */
bt->frac = tv->tv_usec * (uint64_t)18446744073709LL;
}
static __inline struct timespec
sbttots(sbintime_t sbt)
{
struct timespec ts;
ts.tv_sec = sbt >> 32;
ts.tv_nsec = ((uint64_t)1000000000 * (uint32_t)sbt) >> 32;
return (ts);
}
static __inline sbintime_t
tstosbt(struct timespec ts)
{
return (((sbintime_t)ts.tv_sec << 32) +
(ts.tv_nsec * (((uint64_t)1 << 63) / 500000000) >> 32));
}
static __inline struct timeval
sbttotv(sbintime_t sbt)
{
struct timeval tv;
tv.tv_sec = sbt >> 32;
tv.tv_usec = ((uint64_t)1000000 * (uint32_t)sbt) >> 32;
return (tv);
}
static __inline sbintime_t
tvtosbt(struct timeval tv)
{
return (((sbintime_t)tv.tv_sec << 32) +
(tv.tv_usec * (((uint64_t)1 << 63) / 500000) >> 32));
}
#endif /* __BSD_VISIBLE */
#ifdef _KERNEL
/* Operations on timespecs */
#define timespecclear(tvp) ((tvp)->tv_sec = (tvp)->tv_nsec = 0)
#define timespecisset(tvp) ((tvp)->tv_sec || (tvp)->tv_nsec)
#define timespeccmp(tvp, uvp, cmp) \
(((tvp)->tv_sec == (uvp)->tv_sec) ? \
((tvp)->tv_nsec cmp (uvp)->tv_nsec) : \
((tvp)->tv_sec cmp (uvp)->tv_sec))
#define timespecadd(vvp, uvp) \
do { \
(vvp)->tv_sec += (uvp)->tv_sec; \
(vvp)->tv_nsec += (uvp)->tv_nsec; \
if ((vvp)->tv_nsec >= 1000000000) { \
(vvp)->tv_sec++; \
(vvp)->tv_nsec -= 1000000000; \
} \
} while (0)
#define timespecsub(vvp, uvp) \
do { \
(vvp)->tv_sec -= (uvp)->tv_sec; \
(vvp)->tv_nsec -= (uvp)->tv_nsec; \
if ((vvp)->tv_nsec < 0) { \
(vvp)->tv_sec--; \
(vvp)->tv_nsec += 1000000000; \
} \
} while (0)
/* Operations on timevals. */
#define timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
#define timevalisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
#define timevalcmp(tvp, uvp, cmp) \
(((tvp)->tv_sec == (uvp)->tv_sec) ? \
((tvp)->tv_usec cmp (uvp)->tv_usec) : \
((tvp)->tv_sec cmp (uvp)->tv_sec))
/* timevaladd and timevalsub are not inlined */
#endif /* _KERNEL */
#ifndef _KERNEL /* NetBSD/OpenBSD compatible interfaces */
#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
#define timercmp(tvp, uvp, cmp) \
(((tvp)->tv_sec == (uvp)->tv_sec) ? \
((tvp)->tv_usec cmp (uvp)->tv_usec) : \
((tvp)->tv_sec cmp (uvp)->tv_sec))
#define timeradd(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
if ((vvp)->tv_usec >= 1000000) { \
(vvp)->tv_sec++; \
(vvp)->tv_usec -= 1000000; \
} \
} while (0)
#define timersub(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
if ((vvp)->tv_usec < 0) { \
(vvp)->tv_sec--; \
(vvp)->tv_usec += 1000000; \
} \
} while (0)
#endif
/*
* Names of the interval timers, and structure
* defining a timer setting.
*/
#define ITIMER_REAL 0
#define ITIMER_VIRTUAL 1
#define ITIMER_PROF 2
struct itimerval {
struct timeval it_interval; /* timer interval */
struct timeval it_value; /* current value */
};
/*
* Getkerninfo clock information structure
*/
struct clockinfo {
int hz; /* clock frequency */
int tick; /* micro-seconds per hz tick */
int spare;
int stathz; /* statistics clock frequency */
int profhz; /* profiling clock frequency */
};
/* These macros are also in time.h. */
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
#define CLOCK_VIRTUAL 1
#define CLOCK_PROF 2
#define CLOCK_MONOTONIC 4
#define CLOCK_UPTIME 5 /* FreeBSD-specific. */
#define CLOCK_UPTIME_PRECISE 7 /* FreeBSD-specific. */
#define CLOCK_UPTIME_FAST 8 /* FreeBSD-specific. */
#define CLOCK_REALTIME_PRECISE 9 /* FreeBSD-specific. */
#define CLOCK_REALTIME_FAST 10 /* FreeBSD-specific. */
#define CLOCK_MONOTONIC_PRECISE 11 /* FreeBSD-specific. */
#define CLOCK_MONOTONIC_FAST 12 /* FreeBSD-specific. */
#define CLOCK_SECOND 13 /* FreeBSD-specific. */
#define CLOCK_THREAD_CPUTIME_ID 14
#define CLOCK_PROCESS_CPUTIME_ID 15
#endif
#ifndef TIMER_ABSTIME
#define TIMER_RELTIME 0x0 /* relative timer */
#define TIMER_ABSTIME 0x1 /* absolute timer */
#endif
#if __BSD_VISIBLE
#define CPUCLOCK_WHICH_PID 0
#define CPUCLOCK_WHICH_TID 1
#endif
#ifdef _KERNEL
/*
* Kernel to clock driver interface.
*/
void inittodr(time_t base);
void resettodr(void);
extern volatile time_t time_second;
extern volatile time_t time_uptime;
extern struct bintime boottimebin;
extern struct timeval boottime;
extern struct bintime tc_tick_bt;
extern sbintime_t tc_tick_sbt;
extern struct bintime tick_bt;
extern sbintime_t tick_sbt;
extern int tc_precexp;
extern int tc_timepercentage;
extern struct bintime bt_timethreshold;
extern struct bintime bt_tickthreshold;
extern sbintime_t sbt_timethreshold;
extern sbintime_t sbt_tickthreshold;
/*
* Functions for looking at our clock: [get]{bin,nano,micro}[up]time()
*
* Functions without the "get" prefix returns the best timestamp
* we can produce in the given format.
*
* "bin" == struct bintime == seconds + 64 bit fraction of seconds.
* "nano" == struct timespec == seconds + nanoseconds.
* "micro" == struct timeval == seconds + microseconds.
*
* Functions containing "up" returns time relative to boot and
* should be used for calculating time intervals.
*
* Functions without "up" returns GMT time.
*
* Functions with the "get" prefix returns a less precise result
* much faster than the functions without "get" prefix and should
* be used where a precision of 1/hz seconds is acceptable or where
* performance is priority. (NB: "precision", _not_ "resolution" !)
*/
void binuptime(struct bintime *bt);
void nanouptime(struct timespec *tsp);
void microuptime(struct timeval *tvp);
static __inline sbintime_t
sbinuptime(void)
{
struct bintime bt;
binuptime(&bt);
return (bttosbt(bt));
}
void bintime(struct bintime *bt);
void nanotime(struct timespec *tsp);
void microtime(struct timeval *tvp);
void getbinuptime(struct bintime *bt);
void getnanouptime(struct timespec *tsp);
void getmicrouptime(struct timeval *tvp);
static __inline sbintime_t
getsbinuptime(void)
{
struct bintime bt;
getbinuptime(&bt);
return (bttosbt(bt));
}
void getbintime(struct bintime *bt);
void getnanotime(struct timespec *tsp);
void getmicrotime(struct timeval *tvp);
/* Other functions */
int itimerdecr(struct itimerval *itp, int usec);
int itimerfix(struct timeval *tv);
int ppsratecheck(struct timeval *, int *, int);
int ratecheck(struct timeval *, const struct timeval *);
void timevaladd(struct timeval *t1, const struct timeval *t2);
void timevalsub(struct timeval *t1, const struct timeval *t2);
int tvtohz(struct timeval *tv);
#define TC_DEFAULTPERC 5
#define BT2FREQ(bt) \
(((uint64_t)0x8000000000000000 + ((bt)->frac >> 2)) / \
((bt)->frac >> 1))
#define SBT2FREQ(sbt) ((SBT_1S + ((sbt) >> 1)) / (sbt))
#define FREQ2BT(freq, bt) \
{ \
(bt)->sec = 0; \
(bt)->frac = ((uint64_t)0x8000000000000000 / (freq)) << 1; \
}
#define TIMESEL(sbt, sbt2) \
(((sbt2) >= sbt_timethreshold) ? \
((*(sbt) = getsbinuptime()), 1) : ((*(sbt) = sbinuptime()), 0))
#else /* !_KERNEL */
#include <time.h>
#include <sys/cdefs.h>
#include <sys/select.h>
__BEGIN_DECLS
int setitimer(int, const struct itimerval *, struct itimerval *);
int utimes(const char *, const struct timeval *);
#if __BSD_VISIBLE
int adjtime(const struct timeval *, struct timeval *);
int clock_getcpuclockid2(id_t, int, clockid_t *);
int futimes(int, const struct timeval *);
int futimesat(int, const char *, const struct timeval [2]);
int lutimes(const char *, const struct timeval *);
int settimeofday(const struct timeval *, const struct timezone *);
#endif
#if __XSI_VISIBLE
int getitimer(int, struct itimerval *);
int gettimeofday(struct timeval *, struct timezone *);
#endif
__END_DECLS
#endif /* !_KERNEL */
#endif /* !_SYS_TIME_H_ */