You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer and time updates from Thomas Gleixner:
"A rather large update of timers, timekeeping & co
- Core timekeeping code is year-2038 safe now for 32bit machines.
Now we just need to fix all in kernel users and the gazillion of
user space interfaces which rely on timespec/timeval :)
- Better cache layout for the timekeeping internal data structures.
- Proper nanosecond based interfaces for in kernel users.
- Tree wide cleanup of code which wants nanoseconds but does hoops
and loops to convert back and forth from timespecs. Some of it
definitely belongs into the ugly code museum.
- Consolidation of the timekeeping interface zoo.
- A fast NMI safe accessor to clock monotonic for tracing. This is a
long standing request to support correlated user/kernel space
traces. With proper NTP frequency correction it's also suitable
for correlation of traces accross separate machines.
- Checkpoint/restart support for timerfd.
- A few NOHZ[_FULL] improvements in the [hr]timer code.
- Code move from kernel to kernel/time of all time* related code.
- New clocksource/event drivers from the ARM universe. I'm really
impressed that despite an architected timer in the newer chips SoC
manufacturers insist on inventing new and differently broken SoC
specific timers.
[ Ed. "Impressed"? I don't think that word means what you think it means ]
- Another round of code move from arch to drivers. Looks like most
of the legacy mess in ARM regarding timers is sorted out except for
a few obnoxious strongholds.
- The usual updates and fixlets all over the place"
* 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (114 commits)
timekeeping: Fixup typo in update_vsyscall_old definition
clocksource: document some basic timekeeping concepts
timekeeping: Use cached ntp_tick_length when accumulating error
timekeeping: Rework frequency adjustments to work better w/ nohz
timekeeping: Minor fixup for timespec64->timespec assignment
ftrace: Provide trace clocks monotonic
timekeeping: Provide fast and NMI safe access to CLOCK_MONOTONIC
seqcount: Add raw_write_seqcount_latch()
seqcount: Provide raw_read_seqcount()
timekeeping: Use tk_read_base as argument for timekeeping_get_ns()
timekeeping: Create struct tk_read_base and use it in struct timekeeper
timekeeping: Restructure the timekeeper some more
clocksource: Get rid of cycle_last
clocksource: Move cycle_last validation to core code
clocksource: Make delta calculation a function
wireless: ath9k: Get rid of timespec conversions
drm: vmwgfx: Use nsec based interfaces
drm: i915: Use nsec based interfaces
timekeeping: Provide ktime_get_raw()
hangcheck-timer: Use ktime_get_ns()
...
This commit is contained in:
+5
-4
@@ -12,6 +12,11 @@ config CLOCKSOURCE_WATCHDOG
|
||||
config ARCH_CLOCKSOURCE_DATA
|
||||
bool
|
||||
|
||||
# Clocksources require validation of the clocksource against the last
|
||||
# cycle update - x86/TSC misfeature
|
||||
config CLOCKSOURCE_VALIDATE_LAST_CYCLE
|
||||
bool
|
||||
|
||||
# Timekeeping vsyscall support
|
||||
config GENERIC_TIME_VSYSCALL
|
||||
bool
|
||||
@@ -20,10 +25,6 @@ config GENERIC_TIME_VSYSCALL
|
||||
config GENERIC_TIME_VSYSCALL_OLD
|
||||
bool
|
||||
|
||||
# ktime_t scalar 64bit nsec representation
|
||||
config KTIME_SCALAR
|
||||
bool
|
||||
|
||||
# Old style timekeeping
|
||||
config ARCH_USES_GETTIMEOFFSET
|
||||
bool
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
obj-y += time.o timer.o hrtimer.o itimer.o posix-timers.o posix-cpu-timers.o
|
||||
obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
|
||||
obj-y += timeconv.o posix-clock.o alarmtimer.o
|
||||
|
||||
@@ -12,3 +13,21 @@ obj-$(CONFIG_TICK_ONESHOT) += tick-oneshot.o
|
||||
obj-$(CONFIG_TICK_ONESHOT) += tick-sched.o
|
||||
obj-$(CONFIG_TIMER_STATS) += timer_stats.o
|
||||
obj-$(CONFIG_DEBUG_FS) += timekeeping_debug.o
|
||||
obj-$(CONFIG_TEST_UDELAY) += udelay_test.o
|
||||
|
||||
$(obj)/time.o: $(obj)/timeconst.h
|
||||
|
||||
quiet_cmd_hzfile = HZFILE $@
|
||||
cmd_hzfile = echo "hz=$(CONFIG_HZ)" > $@
|
||||
|
||||
targets += hz.bc
|
||||
$(obj)/hz.bc: $(objtree)/include/config/hz.h FORCE
|
||||
$(call if_changed,hzfile)
|
||||
|
||||
quiet_cmd_bc = BC $@
|
||||
cmd_bc = bc -q $(filter-out FORCE,$^) > $@
|
||||
|
||||
targets += timeconst.h
|
||||
$(obj)/timeconst.h: $(obj)/hz.bc $(src)/timeconst.bc FORCE
|
||||
$(call if_changed,bc)
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#include "tick-internal.h"
|
||||
#include "timekeeping_internal.h"
|
||||
|
||||
void timecounter_init(struct timecounter *tc,
|
||||
const struct cyclecounter *cc,
|
||||
@@ -249,7 +250,7 @@ void clocksource_mark_unstable(struct clocksource *cs)
|
||||
static void clocksource_watchdog(unsigned long data)
|
||||
{
|
||||
struct clocksource *cs;
|
||||
cycle_t csnow, wdnow;
|
||||
cycle_t csnow, wdnow, delta;
|
||||
int64_t wd_nsec, cs_nsec;
|
||||
int next_cpu, reset_pending;
|
||||
|
||||
@@ -282,11 +283,12 @@ static void clocksource_watchdog(unsigned long data)
|
||||
continue;
|
||||
}
|
||||
|
||||
wd_nsec = clocksource_cyc2ns((wdnow - cs->wd_last) & watchdog->mask,
|
||||
watchdog->mult, watchdog->shift);
|
||||
delta = clocksource_delta(wdnow, cs->wd_last, watchdog->mask);
|
||||
wd_nsec = clocksource_cyc2ns(delta, watchdog->mult,
|
||||
watchdog->shift);
|
||||
|
||||
cs_nsec = clocksource_cyc2ns((csnow - cs->cs_last) &
|
||||
cs->mask, cs->mult, cs->shift);
|
||||
delta = clocksource_delta(csnow, cs->cs_last, cs->mask);
|
||||
cs_nsec = clocksource_cyc2ns(delta, cs->mult, cs->shift);
|
||||
cs->cs_last = csnow;
|
||||
cs->wd_last = wdnow;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* linux/kernel/itimer.c
|
||||
*
|
||||
* Copyright (C) 1992 Darren Senn
|
||||
*/
|
||||
|
||||
/* These are all the functions necessary to implement itimers */
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/posix-timers.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <trace/events/timer.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/**
|
||||
* itimer_get_remtime - get remaining time for the timer
|
||||
*
|
||||
* @timer: the timer to read
|
||||
*
|
||||
* Returns the delta between the expiry time and now, which can be
|
||||
* less than zero or 1usec for an pending expired timer
|
||||
*/
|
||||
static struct timeval itimer_get_remtime(struct hrtimer *timer)
|
||||
{
|
||||
ktime_t rem = hrtimer_get_remaining(timer);
|
||||
|
||||
/*
|
||||
* Racy but safe: if the itimer expires after the above
|
||||
* hrtimer_get_remtime() call but before this condition
|
||||
* then we return 0 - which is correct.
|
||||
*/
|
||||
if (hrtimer_active(timer)) {
|
||||
if (rem.tv64 <= 0)
|
||||
rem.tv64 = NSEC_PER_USEC;
|
||||
} else
|
||||
rem.tv64 = 0;
|
||||
|
||||
return ktime_to_timeval(rem);
|
||||
}
|
||||
|
||||
static void get_cpu_itimer(struct task_struct *tsk, unsigned int clock_id,
|
||||
struct itimerval *const value)
|
||||
{
|
||||
cputime_t cval, cinterval;
|
||||
struct cpu_itimer *it = &tsk->signal->it[clock_id];
|
||||
|
||||
spin_lock_irq(&tsk->sighand->siglock);
|
||||
|
||||
cval = it->expires;
|
||||
cinterval = it->incr;
|
||||
if (cval) {
|
||||
struct task_cputime cputime;
|
||||
cputime_t t;
|
||||
|
||||
thread_group_cputimer(tsk, &cputime);
|
||||
if (clock_id == CPUCLOCK_PROF)
|
||||
t = cputime.utime + cputime.stime;
|
||||
else
|
||||
/* CPUCLOCK_VIRT */
|
||||
t = cputime.utime;
|
||||
|
||||
if (cval < t)
|
||||
/* about to fire */
|
||||
cval = cputime_one_jiffy;
|
||||
else
|
||||
cval = cval - t;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
|
||||
cputime_to_timeval(cval, &value->it_value);
|
||||
cputime_to_timeval(cinterval, &value->it_interval);
|
||||
}
|
||||
|
||||
int do_getitimer(int which, struct itimerval *value)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
|
||||
switch (which) {
|
||||
case ITIMER_REAL:
|
||||
spin_lock_irq(&tsk->sighand->siglock);
|
||||
value->it_value = itimer_get_remtime(&tsk->signal->real_timer);
|
||||
value->it_interval =
|
||||
ktime_to_timeval(tsk->signal->it_real_incr);
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
break;
|
||||
case ITIMER_VIRTUAL:
|
||||
get_cpu_itimer(tsk, CPUCLOCK_VIRT, value);
|
||||
break;
|
||||
case ITIMER_PROF:
|
||||
get_cpu_itimer(tsk, CPUCLOCK_PROF, value);
|
||||
break;
|
||||
default:
|
||||
return(-EINVAL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(getitimer, int, which, struct itimerval __user *, value)
|
||||
{
|
||||
int error = -EFAULT;
|
||||
struct itimerval get_buffer;
|
||||
|
||||
if (value) {
|
||||
error = do_getitimer(which, &get_buffer);
|
||||
if (!error &&
|
||||
copy_to_user(value, &get_buffer, sizeof(get_buffer)))
|
||||
error = -EFAULT;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The timer is automagically restarted, when interval != 0
|
||||
*/
|
||||
enum hrtimer_restart it_real_fn(struct hrtimer *timer)
|
||||
{
|
||||
struct signal_struct *sig =
|
||||
container_of(timer, struct signal_struct, real_timer);
|
||||
|
||||
trace_itimer_expire(ITIMER_REAL, sig->leader_pid, 0);
|
||||
kill_pid_info(SIGALRM, SEND_SIG_PRIV, sig->leader_pid);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static inline u32 cputime_sub_ns(cputime_t ct, s64 real_ns)
|
||||
{
|
||||
struct timespec ts;
|
||||
s64 cpu_ns;
|
||||
|
||||
cputime_to_timespec(ct, &ts);
|
||||
cpu_ns = timespec_to_ns(&ts);
|
||||
|
||||
return (cpu_ns <= real_ns) ? 0 : cpu_ns - real_ns;
|
||||
}
|
||||
|
||||
static void set_cpu_itimer(struct task_struct *tsk, unsigned int clock_id,
|
||||
const struct itimerval *const value,
|
||||
struct itimerval *const ovalue)
|
||||
{
|
||||
cputime_t cval, nval, cinterval, ninterval;
|
||||
s64 ns_ninterval, ns_nval;
|
||||
u32 error, incr_error;
|
||||
struct cpu_itimer *it = &tsk->signal->it[clock_id];
|
||||
|
||||
nval = timeval_to_cputime(&value->it_value);
|
||||
ns_nval = timeval_to_ns(&value->it_value);
|
||||
ninterval = timeval_to_cputime(&value->it_interval);
|
||||
ns_ninterval = timeval_to_ns(&value->it_interval);
|
||||
|
||||
error = cputime_sub_ns(nval, ns_nval);
|
||||
incr_error = cputime_sub_ns(ninterval, ns_ninterval);
|
||||
|
||||
spin_lock_irq(&tsk->sighand->siglock);
|
||||
|
||||
cval = it->expires;
|
||||
cinterval = it->incr;
|
||||
if (cval || nval) {
|
||||
if (nval > 0)
|
||||
nval += cputime_one_jiffy;
|
||||
set_process_cpu_timer(tsk, clock_id, &nval, &cval);
|
||||
}
|
||||
it->expires = nval;
|
||||
it->incr = ninterval;
|
||||
it->error = error;
|
||||
it->incr_error = incr_error;
|
||||
trace_itimer_state(clock_id == CPUCLOCK_VIRT ?
|
||||
ITIMER_VIRTUAL : ITIMER_PROF, value, nval);
|
||||
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
|
||||
if (ovalue) {
|
||||
cputime_to_timeval(cval, &ovalue->it_value);
|
||||
cputime_to_timeval(cinterval, &ovalue->it_interval);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the timeval is in canonical form
|
||||
*/
|
||||
#define timeval_valid(t) \
|
||||
(((t)->tv_sec >= 0) && (((unsigned long) (t)->tv_usec) < USEC_PER_SEC))
|
||||
|
||||
int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
struct hrtimer *timer;
|
||||
ktime_t expires;
|
||||
|
||||
/*
|
||||
* Validate the timevals in value.
|
||||
*/
|
||||
if (!timeval_valid(&value->it_value) ||
|
||||
!timeval_valid(&value->it_interval))
|
||||
return -EINVAL;
|
||||
|
||||
switch (which) {
|
||||
case ITIMER_REAL:
|
||||
again:
|
||||
spin_lock_irq(&tsk->sighand->siglock);
|
||||
timer = &tsk->signal->real_timer;
|
||||
if (ovalue) {
|
||||
ovalue->it_value = itimer_get_remtime(timer);
|
||||
ovalue->it_interval
|
||||
= ktime_to_timeval(tsk->signal->it_real_incr);
|
||||
}
|
||||
/* We are sharing ->siglock with it_real_fn() */
|
||||
if (hrtimer_try_to_cancel(timer) < 0) {
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
goto again;
|
||||
}
|
||||
expires = timeval_to_ktime(value->it_value);
|
||||
if (expires.tv64 != 0) {
|
||||
tsk->signal->it_real_incr =
|
||||
timeval_to_ktime(value->it_interval);
|
||||
hrtimer_start(timer, expires, HRTIMER_MODE_REL);
|
||||
} else
|
||||
tsk->signal->it_real_incr.tv64 = 0;
|
||||
|
||||
trace_itimer_state(ITIMER_REAL, value, 0);
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
break;
|
||||
case ITIMER_VIRTUAL:
|
||||
set_cpu_itimer(tsk, CPUCLOCK_VIRT, value, ovalue);
|
||||
break;
|
||||
case ITIMER_PROF:
|
||||
set_cpu_itimer(tsk, CPUCLOCK_PROF, value, ovalue);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* alarm_setitimer - set alarm in seconds
|
||||
*
|
||||
* @seconds: number of seconds until alarm
|
||||
* 0 disables the alarm
|
||||
*
|
||||
* Returns the remaining time in seconds of a pending timer or 0 when
|
||||
* the timer is not active.
|
||||
*
|
||||
* On 32 bit machines the seconds value is limited to (INT_MAX/2) to avoid
|
||||
* negative timeval settings which would cause immediate expiry.
|
||||
*/
|
||||
unsigned int alarm_setitimer(unsigned int seconds)
|
||||
{
|
||||
struct itimerval it_new, it_old;
|
||||
|
||||
#if BITS_PER_LONG < 64
|
||||
if (seconds > INT_MAX)
|
||||
seconds = INT_MAX;
|
||||
#endif
|
||||
it_new.it_value.tv_sec = seconds;
|
||||
it_new.it_value.tv_usec = 0;
|
||||
it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;
|
||||
|
||||
do_setitimer(ITIMER_REAL, &it_new, &it_old);
|
||||
|
||||
/*
|
||||
* We can't return 0 if we have an alarm pending ... And we'd
|
||||
* better return too much than too little anyway
|
||||
*/
|
||||
if ((!it_old.it_value.tv_sec && it_old.it_value.tv_usec) ||
|
||||
it_old.it_value.tv_usec >= 500000)
|
||||
it_old.it_value.tv_sec++;
|
||||
|
||||
return it_old.it_value.tv_sec;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(setitimer, int, which, struct itimerval __user *, value,
|
||||
struct itimerval __user *, ovalue)
|
||||
{
|
||||
struct itimerval set_buffer, get_buffer;
|
||||
int error;
|
||||
|
||||
if (value) {
|
||||
if(copy_from_user(&set_buffer, value, sizeof(set_buffer)))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
memset(&set_buffer, 0, sizeof(set_buffer));
|
||||
printk_once(KERN_WARNING "%s calls setitimer() with new_value NULL pointer."
|
||||
" Misfeature support will be removed\n",
|
||||
current->comm);
|
||||
}
|
||||
|
||||
error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : NULL);
|
||||
if (error || !ovalue)
|
||||
return error;
|
||||
|
||||
if (copy_to_user(ovalue, &get_buffer, sizeof(get_buffer)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
+8
-7
@@ -466,7 +466,8 @@ static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock);
|
||||
|
||||
static void sync_cmos_clock(struct work_struct *work)
|
||||
{
|
||||
struct timespec now, next;
|
||||
struct timespec64 now;
|
||||
struct timespec next;
|
||||
int fail = 1;
|
||||
|
||||
/*
|
||||
@@ -485,9 +486,9 @@ static void sync_cmos_clock(struct work_struct *work)
|
||||
return;
|
||||
}
|
||||
|
||||
getnstimeofday(&now);
|
||||
getnstimeofday64(&now);
|
||||
if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec * 5) {
|
||||
struct timespec adjust = now;
|
||||
struct timespec adjust = timespec64_to_timespec(now);
|
||||
|
||||
fail = -ENODEV;
|
||||
if (persistent_clock_is_local)
|
||||
@@ -531,7 +532,7 @@ void ntp_notify_cmos_timer(void) { }
|
||||
/*
|
||||
* Propagate a new txc->status value into the NTP state:
|
||||
*/
|
||||
static inline void process_adj_status(struct timex *txc, struct timespec *ts)
|
||||
static inline void process_adj_status(struct timex *txc, struct timespec64 *ts)
|
||||
{
|
||||
if ((time_status & STA_PLL) && !(txc->status & STA_PLL)) {
|
||||
time_state = TIME_OK;
|
||||
@@ -554,7 +555,7 @@ static inline void process_adj_status(struct timex *txc, struct timespec *ts)
|
||||
|
||||
|
||||
static inline void process_adjtimex_modes(struct timex *txc,
|
||||
struct timespec *ts,
|
||||
struct timespec64 *ts,
|
||||
s32 *time_tai)
|
||||
{
|
||||
if (txc->modes & ADJ_STATUS)
|
||||
@@ -640,7 +641,7 @@ int ntp_validate_timex(struct timex *txc)
|
||||
* adjtimex mainly allows reading (and writing, if superuser) of
|
||||
* kernel time-keeping variables. used by xntpd.
|
||||
*/
|
||||
int __do_adjtimex(struct timex *txc, struct timespec *ts, s32 *time_tai)
|
||||
int __do_adjtimex(struct timex *txc, struct timespec64 *ts, s32 *time_tai)
|
||||
{
|
||||
int result;
|
||||
|
||||
@@ -684,7 +685,7 @@ int __do_adjtimex(struct timex *txc, struct timespec *ts, s32 *time_tai)
|
||||
/* fill PPS status fields */
|
||||
pps_fill_timex(txc);
|
||||
|
||||
txc->time.tv_sec = ts->tv_sec;
|
||||
txc->time.tv_sec = (time_t)ts->tv_sec;
|
||||
txc->time.tv_usec = ts->tv_nsec;
|
||||
if (!(time_status & STA_NANO))
|
||||
txc->time.tv_usec /= NSEC_PER_USEC;
|
||||
|
||||
@@ -7,6 +7,6 @@ extern void ntp_clear(void);
|
||||
extern u64 ntp_tick_length(void);
|
||||
extern int second_overflow(unsigned long secs);
|
||||
extern int ntp_validate_timex(struct timex *);
|
||||
extern int __do_adjtimex(struct timex *, struct timespec *, s32 *);
|
||||
extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *);
|
||||
extern void __hardpps(const struct timespec *, const struct timespec *);
|
||||
#endif /* _LINUX_NTP_INTERNAL_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,8 @@
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/tick.h>
|
||||
|
||||
#include "timekeeping.h"
|
||||
|
||||
extern seqlock_t jiffies_lock;
|
||||
|
||||
#define CS_NAME_LEN 32
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
||||
scale=0
|
||||
|
||||
define gcd(a,b) {
|
||||
auto t;
|
||||
while (b) {
|
||||
t = b;
|
||||
b = a % b;
|
||||
a = t;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/* Division by reciprocal multiplication. */
|
||||
define fmul(b,n,d) {
|
||||
return (2^b*n+d-1)/d;
|
||||
}
|
||||
|
||||
/* Adjustment factor when a ceiling value is used. Use as:
|
||||
(imul * n) + (fmulxx * n + fadjxx) >> xx) */
|
||||
define fadj(b,n,d) {
|
||||
auto v;
|
||||
d = d/gcd(n,d);
|
||||
v = 2^b*(d-1)/d;
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Compute the appropriate mul/adj values as well as a shift count,
|
||||
which brings the mul value into the range 2^b-1 <= x < 2^b. Such
|
||||
a shift value will be correct in the signed integer range and off
|
||||
by at most one in the upper half of the unsigned range. */
|
||||
define fmuls(b,n,d) {
|
||||
auto s, m;
|
||||
for (s = 0; 1; s++) {
|
||||
m = fmul(s,n,d);
|
||||
if (m >= 2^(b-1))
|
||||
return s;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
define timeconst(hz) {
|
||||
print "/* Automatically generated by kernel/timeconst.bc */\n"
|
||||
print "/* Time conversion constants for HZ == ", hz, " */\n"
|
||||
print "\n"
|
||||
|
||||
print "#ifndef KERNEL_TIMECONST_H\n"
|
||||
print "#define KERNEL_TIMECONST_H\n\n"
|
||||
|
||||
print "#include <linux/param.h>\n"
|
||||
print "#include <linux/types.h>\n\n"
|
||||
|
||||
print "#if HZ != ", hz, "\n"
|
||||
print "#error \qkernel/timeconst.h has the wrong HZ value!\q\n"
|
||||
print "#endif\n\n"
|
||||
|
||||
if (hz < 2) {
|
||||
print "#error Totally bogus HZ value!\n"
|
||||
} else {
|
||||
s=fmuls(32,1000,hz)
|
||||
obase=16
|
||||
print "#define HZ_TO_MSEC_MUL32\tU64_C(0x", fmul(s,1000,hz), ")\n"
|
||||
print "#define HZ_TO_MSEC_ADJ32\tU64_C(0x", fadj(s,1000,hz), ")\n"
|
||||
obase=10
|
||||
print "#define HZ_TO_MSEC_SHR32\t", s, "\n"
|
||||
|
||||
s=fmuls(32,hz,1000)
|
||||
obase=16
|
||||
print "#define MSEC_TO_HZ_MUL32\tU64_C(0x", fmul(s,hz,1000), ")\n"
|
||||
print "#define MSEC_TO_HZ_ADJ32\tU64_C(0x", fadj(s,hz,1000), ")\n"
|
||||
obase=10
|
||||
print "#define MSEC_TO_HZ_SHR32\t", s, "\n"
|
||||
|
||||
obase=10
|
||||
cd=gcd(hz,1000)
|
||||
print "#define HZ_TO_MSEC_NUM\t\t", 1000/cd, "\n"
|
||||
print "#define HZ_TO_MSEC_DEN\t\t", hz/cd, "\n"
|
||||
print "#define MSEC_TO_HZ_NUM\t\t", hz/cd, "\n"
|
||||
print "#define MSEC_TO_HZ_DEN\t\t", 1000/cd, "\n"
|
||||
print "\n"
|
||||
|
||||
s=fmuls(32,1000000,hz)
|
||||
obase=16
|
||||
print "#define HZ_TO_USEC_MUL32\tU64_C(0x", fmul(s,1000000,hz), ")\n"
|
||||
print "#define HZ_TO_USEC_ADJ32\tU64_C(0x", fadj(s,1000000,hz), ")\n"
|
||||
obase=10
|
||||
print "#define HZ_TO_USEC_SHR32\t", s, "\n"
|
||||
|
||||
s=fmuls(32,hz,1000000)
|
||||
obase=16
|
||||
print "#define USEC_TO_HZ_MUL32\tU64_C(0x", fmul(s,hz,1000000), ")\n"
|
||||
print "#define USEC_TO_HZ_ADJ32\tU64_C(0x", fadj(s,hz,1000000), ")\n"
|
||||
obase=10
|
||||
print "#define USEC_TO_HZ_SHR32\t", s, "\n"
|
||||
|
||||
obase=10
|
||||
cd=gcd(hz,1000000)
|
||||
print "#define HZ_TO_USEC_NUM\t\t", 1000000/cd, "\n"
|
||||
print "#define HZ_TO_USEC_DEN\t\t", hz/cd, "\n"
|
||||
print "#define USEC_TO_HZ_NUM\t\t", hz/cd, "\n"
|
||||
print "#define USEC_TO_HZ_DEN\t\t", 1000000/cd, "\n"
|
||||
print "\n"
|
||||
|
||||
print "#endif /* KERNEL_TIMECONST_H */\n"
|
||||
}
|
||||
halt
|
||||
}
|
||||
|
||||
timeconst(hz)
|
||||
+618
-543
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
#ifndef _KERNEL_TIME_TIMEKEEPING_H
|
||||
#define _KERNEL_TIME_TIMEKEEPING_H
|
||||
/*
|
||||
* Internal interfaces for kernel/time/
|
||||
*/
|
||||
extern ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real,
|
||||
ktime_t *offs_boot,
|
||||
ktime_t *offs_tai);
|
||||
extern ktime_t ktime_get_update_offsets_now(ktime_t *offs_real,
|
||||
ktime_t *offs_boot,
|
||||
ktime_t *offs_tai);
|
||||
|
||||
extern int timekeeping_valid_for_hres(void);
|
||||
extern u64 timekeeping_max_deferment(void);
|
||||
extern int timekeeping_inject_offset(struct timespec *ts);
|
||||
extern s32 timekeeping_get_tai_offset(void);
|
||||
extern void timekeeping_set_tai_offset(s32 tai_offset);
|
||||
extern void timekeeping_clocktai(struct timespec *ts);
|
||||
|
||||
#endif
|
||||
@@ -67,7 +67,7 @@ static int __init tk_debug_sleep_time_init(void)
|
||||
}
|
||||
late_initcall(tk_debug_sleep_time_init);
|
||||
|
||||
void tk_debug_account_sleep_time(struct timespec *t)
|
||||
void tk_debug_account_sleep_time(struct timespec64 *t)
|
||||
{
|
||||
sleep_time_bin[fls(t->tv_sec)]++;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,27 @@
|
||||
/*
|
||||
* timekeeping debug functions
|
||||
*/
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
extern void tk_debug_account_sleep_time(struct timespec *t);
|
||||
extern void tk_debug_account_sleep_time(struct timespec64 *t);
|
||||
#else
|
||||
#define tk_debug_account_sleep_time(x)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE
|
||||
static inline cycle_t clocksource_delta(cycle_t now, cycle_t last, cycle_t mask)
|
||||
{
|
||||
cycle_t ret = (now - last) & mask;
|
||||
|
||||
return (s64) ret > 0 ? ret : 0;
|
||||
}
|
||||
#else
|
||||
static inline cycle_t clocksource_delta(cycle_t now, cycle_t last, cycle_t mask)
|
||||
{
|
||||
return (now - last) & mask;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TIMEKEEPING_INTERNAL_H */
|
||||
|
||||
+1736
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* udelay() test kernel module
|
||||
*
|
||||
* Test is executed by writing and reading to /sys/kernel/debug/udelay_test
|
||||
* Tests are configured by writing: USECS ITERATIONS
|
||||
* Tests are executed by reading from the same file.
|
||||
* Specifying usecs of 0 or negative values will run multiples tests.
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define DEFAULT_ITERATIONS 100
|
||||
|
||||
#define DEBUGFS_FILENAME "udelay_test"
|
||||
|
||||
static DEFINE_MUTEX(udelay_test_lock);
|
||||
static struct dentry *udelay_test_debugfs_file;
|
||||
static int udelay_test_usecs;
|
||||
static int udelay_test_iterations = DEFAULT_ITERATIONS;
|
||||
|
||||
static int udelay_test_single(struct seq_file *s, int usecs, uint32_t iters)
|
||||
{
|
||||
int min = 0, max = 0, fail_count = 0;
|
||||
uint64_t sum = 0;
|
||||
uint64_t avg;
|
||||
int i;
|
||||
/* Allow udelay to be up to 0.5% fast */
|
||||
int allowed_error_ns = usecs * 5;
|
||||
|
||||
for (i = 0; i < iters; ++i) {
|
||||
struct timespec ts1, ts2;
|
||||
int time_passed;
|
||||
|
||||
ktime_get_ts(&ts1);
|
||||
udelay(usecs);
|
||||
ktime_get_ts(&ts2);
|
||||
time_passed = timespec_to_ns(&ts2) - timespec_to_ns(&ts1);
|
||||
|
||||
if (i == 0 || time_passed < min)
|
||||
min = time_passed;
|
||||
if (i == 0 || time_passed > max)
|
||||
max = time_passed;
|
||||
if ((time_passed + allowed_error_ns) / 1000 < usecs)
|
||||
++fail_count;
|
||||
WARN_ON(time_passed < 0);
|
||||
sum += time_passed;
|
||||
}
|
||||
|
||||
avg = sum;
|
||||
do_div(avg, iters);
|
||||
seq_printf(s, "%d usecs x %d: exp=%d allowed=%d min=%d avg=%lld max=%d",
|
||||
usecs, iters, usecs * 1000,
|
||||
(usecs * 1000) - allowed_error_ns, min, avg, max);
|
||||
if (fail_count)
|
||||
seq_printf(s, " FAIL=%d", fail_count);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udelay_test_show(struct seq_file *s, void *v)
|
||||
{
|
||||
int usecs;
|
||||
int iters;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&udelay_test_lock);
|
||||
usecs = udelay_test_usecs;
|
||||
iters = udelay_test_iterations;
|
||||
mutex_unlock(&udelay_test_lock);
|
||||
|
||||
if (usecs > 0 && iters > 0) {
|
||||
return udelay_test_single(s, usecs, iters);
|
||||
} else if (usecs == 0) {
|
||||
struct timespec ts;
|
||||
|
||||
ktime_get_ts(&ts);
|
||||
seq_printf(s, "udelay() test (lpj=%ld kt=%ld.%09ld)\n",
|
||||
loops_per_jiffy, ts.tv_sec, ts.tv_nsec);
|
||||
seq_puts(s, "usage:\n");
|
||||
seq_puts(s, "echo USECS [ITERS] > " DEBUGFS_FILENAME "\n");
|
||||
seq_puts(s, "cat " DEBUGFS_FILENAME "\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int udelay_test_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, udelay_test_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t udelay_test_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
char lbuf[32];
|
||||
int ret;
|
||||
int usecs;
|
||||
int iters;
|
||||
|
||||
if (count >= sizeof(lbuf))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(lbuf, buf, count))
|
||||
return -EFAULT;
|
||||
lbuf[count] = '\0';
|
||||
|
||||
ret = sscanf(lbuf, "%d %d", &usecs, &iters);
|
||||
if (ret < 1)
|
||||
return -EINVAL;
|
||||
else if (ret < 2)
|
||||
iters = DEFAULT_ITERATIONS;
|
||||
|
||||
mutex_lock(&udelay_test_lock);
|
||||
udelay_test_usecs = usecs;
|
||||
udelay_test_iterations = iters;
|
||||
mutex_unlock(&udelay_test_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations udelay_test_debugfs_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = udelay_test_open,
|
||||
.read = seq_read,
|
||||
.write = udelay_test_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int __init udelay_test_init(void)
|
||||
{
|
||||
mutex_lock(&udelay_test_lock);
|
||||
udelay_test_debugfs_file = debugfs_create_file(DEBUGFS_FILENAME,
|
||||
S_IRUSR, NULL, NULL, &udelay_test_debugfs_ops);
|
||||
mutex_unlock(&udelay_test_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(udelay_test_init);
|
||||
|
||||
static void __exit udelay_test_exit(void)
|
||||
{
|
||||
mutex_lock(&udelay_test_lock);
|
||||
debugfs_remove(udelay_test_debugfs_file);
|
||||
mutex_unlock(&udelay_test_lock);
|
||||
}
|
||||
|
||||
module_exit(udelay_test_exit);
|
||||
|
||||
MODULE_AUTHOR("David Riley <davidriley@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user