Merge branch 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (62 commits) posix-clocks: Check write permissions in posix syscalls hrtimer: Remove empty hrtimer_init_hres_timer() hrtimer: Update hrtimer->state documentation hrtimer: Update base[CLOCK_BOOTTIME].offset correctly timers: Export CLOCK_BOOTTIME via the posix timers interface timers: Add CLOCK_BOOTTIME hrtimer base time: Extend get_xtime_and_monotonic_offset() to also return sleep time: Introduce get_monotonic_boottime and ktime_get_boottime hrtimers: extend hrtimer base code to handle more then 2 clockids ntp: Remove redundant and incorrect parameter check mn10300: Switch do_timer() to xtimer_update() posix clocks: Introduce dynamic clocks posix-timers: Cleanup namespace posix-timers: Add support for fd based clocks x86: Add clock_adjtime for x86 posix-timers: Introduce a syscall for clock tuning. time: Splitout compat timex accessors ntp: Add ADJ_SETOFFSET mode bit time: Introduce timekeeping_inject_offset posix-timer: Update comment ... Fix up new system-call-related conflicts in arch/x86/ia32/ia32entry.S arch/x86/include/asm/unistd_32.h arch/x86/include/asm/unistd_64.h arch/x86/kernel/syscall_table_32.S (name_to_handle_at()/open_by_handle_at() vs clock_adjtime()), and some due to movement of get_jiffies_64() in: kernel/time.c
这个提交包含在:
136
kernel/compat.c
136
kernel/compat.c
@@ -52,6 +52,64 @@ static int compat_put_timeval(struct compat_timeval __user *o,
|
||||
put_user(i->tv_usec, &o->tv_usec)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int compat_get_timex(struct timex *txc, struct compat_timex __user *utp)
|
||||
{
|
||||
memset(txc, 0, sizeof(struct timex));
|
||||
|
||||
if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) ||
|
||||
__get_user(txc->modes, &utp->modes) ||
|
||||
__get_user(txc->offset, &utp->offset) ||
|
||||
__get_user(txc->freq, &utp->freq) ||
|
||||
__get_user(txc->maxerror, &utp->maxerror) ||
|
||||
__get_user(txc->esterror, &utp->esterror) ||
|
||||
__get_user(txc->status, &utp->status) ||
|
||||
__get_user(txc->constant, &utp->constant) ||
|
||||
__get_user(txc->precision, &utp->precision) ||
|
||||
__get_user(txc->tolerance, &utp->tolerance) ||
|
||||
__get_user(txc->time.tv_sec, &utp->time.tv_sec) ||
|
||||
__get_user(txc->time.tv_usec, &utp->time.tv_usec) ||
|
||||
__get_user(txc->tick, &utp->tick) ||
|
||||
__get_user(txc->ppsfreq, &utp->ppsfreq) ||
|
||||
__get_user(txc->jitter, &utp->jitter) ||
|
||||
__get_user(txc->shift, &utp->shift) ||
|
||||
__get_user(txc->stabil, &utp->stabil) ||
|
||||
__get_user(txc->jitcnt, &utp->jitcnt) ||
|
||||
__get_user(txc->calcnt, &utp->calcnt) ||
|
||||
__get_user(txc->errcnt, &utp->errcnt) ||
|
||||
__get_user(txc->stbcnt, &utp->stbcnt))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compat_put_timex(struct compat_timex __user *utp, struct timex *txc)
|
||||
{
|
||||
if (!access_ok(VERIFY_WRITE, utp, sizeof(struct compat_timex)) ||
|
||||
__put_user(txc->modes, &utp->modes) ||
|
||||
__put_user(txc->offset, &utp->offset) ||
|
||||
__put_user(txc->freq, &utp->freq) ||
|
||||
__put_user(txc->maxerror, &utp->maxerror) ||
|
||||
__put_user(txc->esterror, &utp->esterror) ||
|
||||
__put_user(txc->status, &utp->status) ||
|
||||
__put_user(txc->constant, &utp->constant) ||
|
||||
__put_user(txc->precision, &utp->precision) ||
|
||||
__put_user(txc->tolerance, &utp->tolerance) ||
|
||||
__put_user(txc->time.tv_sec, &utp->time.tv_sec) ||
|
||||
__put_user(txc->time.tv_usec, &utp->time.tv_usec) ||
|
||||
__put_user(txc->tick, &utp->tick) ||
|
||||
__put_user(txc->ppsfreq, &utp->ppsfreq) ||
|
||||
__put_user(txc->jitter, &utp->jitter) ||
|
||||
__put_user(txc->shift, &utp->shift) ||
|
||||
__put_user(txc->stabil, &utp->stabil) ||
|
||||
__put_user(txc->jitcnt, &utp->jitcnt) ||
|
||||
__put_user(txc->calcnt, &utp->calcnt) ||
|
||||
__put_user(txc->errcnt, &utp->errcnt) ||
|
||||
__put_user(txc->stbcnt, &utp->stbcnt) ||
|
||||
__put_user(txc->tai, &utp->tai))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
asmlinkage long compat_sys_gettimeofday(struct compat_timeval __user *tv,
|
||||
struct timezone __user *tz)
|
||||
{
|
||||
@@ -617,6 +675,29 @@ long compat_sys_clock_gettime(clockid_t which_clock,
|
||||
return err;
|
||||
}
|
||||
|
||||
long compat_sys_clock_adjtime(clockid_t which_clock,
|
||||
struct compat_timex __user *utp)
|
||||
{
|
||||
struct timex txc;
|
||||
mm_segment_t oldfs;
|
||||
int err, ret;
|
||||
|
||||
err = compat_get_timex(&txc, utp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
oldfs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
ret = sys_clock_adjtime(which_clock, (struct timex __user *) &txc);
|
||||
set_fs(oldfs);
|
||||
|
||||
err = compat_put_timex(utp, &txc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
long compat_sys_clock_getres(clockid_t which_clock,
|
||||
struct compat_timespec __user *tp)
|
||||
{
|
||||
@@ -951,58 +1032,17 @@ asmlinkage long compat_sys_rt_sigsuspend(compat_sigset_t __user *unewset, compat
|
||||
asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp)
|
||||
{
|
||||
struct timex txc;
|
||||
int ret;
|
||||
int err, ret;
|
||||
|
||||
memset(&txc, 0, sizeof(struct timex));
|
||||
|
||||
if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) ||
|
||||
__get_user(txc.modes, &utp->modes) ||
|
||||
__get_user(txc.offset, &utp->offset) ||
|
||||
__get_user(txc.freq, &utp->freq) ||
|
||||
__get_user(txc.maxerror, &utp->maxerror) ||
|
||||
__get_user(txc.esterror, &utp->esterror) ||
|
||||
__get_user(txc.status, &utp->status) ||
|
||||
__get_user(txc.constant, &utp->constant) ||
|
||||
__get_user(txc.precision, &utp->precision) ||
|
||||
__get_user(txc.tolerance, &utp->tolerance) ||
|
||||
__get_user(txc.time.tv_sec, &utp->time.tv_sec) ||
|
||||
__get_user(txc.time.tv_usec, &utp->time.tv_usec) ||
|
||||
__get_user(txc.tick, &utp->tick) ||
|
||||
__get_user(txc.ppsfreq, &utp->ppsfreq) ||
|
||||
__get_user(txc.jitter, &utp->jitter) ||
|
||||
__get_user(txc.shift, &utp->shift) ||
|
||||
__get_user(txc.stabil, &utp->stabil) ||
|
||||
__get_user(txc.jitcnt, &utp->jitcnt) ||
|
||||
__get_user(txc.calcnt, &utp->calcnt) ||
|
||||
__get_user(txc.errcnt, &utp->errcnt) ||
|
||||
__get_user(txc.stbcnt, &utp->stbcnt))
|
||||
return -EFAULT;
|
||||
err = compat_get_timex(&txc, utp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ret = do_adjtimex(&txc);
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, utp, sizeof(struct compat_timex)) ||
|
||||
__put_user(txc.modes, &utp->modes) ||
|
||||
__put_user(txc.offset, &utp->offset) ||
|
||||
__put_user(txc.freq, &utp->freq) ||
|
||||
__put_user(txc.maxerror, &utp->maxerror) ||
|
||||
__put_user(txc.esterror, &utp->esterror) ||
|
||||
__put_user(txc.status, &utp->status) ||
|
||||
__put_user(txc.constant, &utp->constant) ||
|
||||
__put_user(txc.precision, &utp->precision) ||
|
||||
__put_user(txc.tolerance, &utp->tolerance) ||
|
||||
__put_user(txc.time.tv_sec, &utp->time.tv_sec) ||
|
||||
__put_user(txc.time.tv_usec, &utp->time.tv_usec) ||
|
||||
__put_user(txc.tick, &utp->tick) ||
|
||||
__put_user(txc.ppsfreq, &utp->ppsfreq) ||
|
||||
__put_user(txc.jitter, &utp->jitter) ||
|
||||
__put_user(txc.shift, &utp->shift) ||
|
||||
__put_user(txc.stabil, &utp->stabil) ||
|
||||
__put_user(txc.jitcnt, &utp->jitcnt) ||
|
||||
__put_user(txc.calcnt, &utp->calcnt) ||
|
||||
__put_user(txc.errcnt, &utp->errcnt) ||
|
||||
__put_user(txc.stbcnt, &utp->stbcnt) ||
|
||||
__put_user(txc.tai, &utp->tai))
|
||||
ret = -EFAULT;
|
||||
err = compat_put_timex(utp, &txc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -53,11 +53,10 @@
|
||||
/*
|
||||
* The timer bases:
|
||||
*
|
||||
* Note: If we want to add new timer bases, we have to skip the two
|
||||
* clock ids captured by the cpu-timers. We do this by holding empty
|
||||
* entries rather than doing math adjustment of the clock ids.
|
||||
* This ensures that we capture erroneous accesses to these clock ids
|
||||
* rather than moving them into the range of valid clock id's.
|
||||
* There are more clockids then hrtimer bases. Thus, we index
|
||||
* into the timer bases by the hrtimer_base_type enum. When trying
|
||||
* to reach a base using a clockid, hrtimer_clockid_to_base()
|
||||
* is used to convert from clockid to the proper hrtimer_base_type.
|
||||
*/
|
||||
DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =
|
||||
{
|
||||
@@ -74,30 +73,39 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =
|
||||
.get_time = &ktime_get,
|
||||
.resolution = KTIME_LOW_RES,
|
||||
},
|
||||
{
|
||||
.index = CLOCK_BOOTTIME,
|
||||
.get_time = &ktime_get_boottime,
|
||||
.resolution = KTIME_LOW_RES,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static int hrtimer_clock_to_base_table[MAX_CLOCKS];
|
||||
|
||||
static inline int hrtimer_clockid_to_base(clockid_t clock_id)
|
||||
{
|
||||
return hrtimer_clock_to_base_table[clock_id];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the coarse grained time at the softirq based on xtime and
|
||||
* wall_to_monotonic.
|
||||
*/
|
||||
static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base)
|
||||
{
|
||||
ktime_t xtim, tomono;
|
||||
struct timespec xts, tom;
|
||||
unsigned long seq;
|
||||
ktime_t xtim, mono, boot;
|
||||
struct timespec xts, tom, slp;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
xts = __current_kernel_time();
|
||||
tom = __get_wall_to_monotonic();
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
get_xtime_and_monotonic_and_sleep_offset(&xts, &tom, &slp);
|
||||
|
||||
xtim = timespec_to_ktime(xts);
|
||||
tomono = timespec_to_ktime(tom);
|
||||
base->clock_base[CLOCK_REALTIME].softirq_time = xtim;
|
||||
base->clock_base[CLOCK_MONOTONIC].softirq_time =
|
||||
ktime_add(xtim, tomono);
|
||||
mono = ktime_add(xtim, timespec_to_ktime(tom));
|
||||
boot = ktime_add(mono, timespec_to_ktime(slp));
|
||||
base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim;
|
||||
base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono;
|
||||
base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -184,10 +192,11 @@ switch_hrtimer_base(struct hrtimer *timer, struct hrtimer_clock_base *base,
|
||||
struct hrtimer_cpu_base *new_cpu_base;
|
||||
int this_cpu = smp_processor_id();
|
||||
int cpu = hrtimer_get_target(this_cpu, pinned);
|
||||
int basenum = hrtimer_clockid_to_base(base->index);
|
||||
|
||||
again:
|
||||
new_cpu_base = &per_cpu(hrtimer_bases, cpu);
|
||||
new_base = &new_cpu_base->clock_base[base->index];
|
||||
new_base = &new_cpu_base->clock_base[basenum];
|
||||
|
||||
if (base != new_base) {
|
||||
/*
|
||||
@@ -617,24 +626,23 @@ static int hrtimer_reprogram(struct hrtimer *timer,
|
||||
static void retrigger_next_event(void *arg)
|
||||
{
|
||||
struct hrtimer_cpu_base *base;
|
||||
struct timespec realtime_offset, wtm;
|
||||
unsigned long seq;
|
||||
struct timespec realtime_offset, wtm, sleep;
|
||||
|
||||
if (!hrtimer_hres_active())
|
||||
return;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
wtm = __get_wall_to_monotonic();
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
get_xtime_and_monotonic_and_sleep_offset(&realtime_offset, &wtm,
|
||||
&sleep);
|
||||
set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec);
|
||||
|
||||
base = &__get_cpu_var(hrtimer_bases);
|
||||
|
||||
/* Adjust CLOCK_REALTIME offset */
|
||||
raw_spin_lock(&base->lock);
|
||||
base->clock_base[CLOCK_REALTIME].offset =
|
||||
base->clock_base[HRTIMER_BASE_REALTIME].offset =
|
||||
timespec_to_ktime(realtime_offset);
|
||||
base->clock_base[HRTIMER_BASE_BOOTTIME].offset =
|
||||
timespec_to_ktime(sleep);
|
||||
|
||||
hrtimer_force_reprogram(base, 0);
|
||||
raw_spin_unlock(&base->lock);
|
||||
@@ -678,14 +686,6 @@ static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base)
|
||||
base->hres_active = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the high resolution related parts of a hrtimer
|
||||
*/
|
||||
static inline void hrtimer_init_timer_hres(struct hrtimer *timer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* When High resolution timers are active, try to reprogram. Note, that in case
|
||||
* the state has HRTIMER_STATE_CALLBACK set, no reprogramming and no expiry
|
||||
@@ -731,8 +731,9 @@ static int hrtimer_switch_to_hres(void)
|
||||
return 0;
|
||||
}
|
||||
base->hres_active = 1;
|
||||
base->clock_base[CLOCK_REALTIME].resolution = KTIME_HIGH_RES;
|
||||
base->clock_base[CLOCK_MONOTONIC].resolution = KTIME_HIGH_RES;
|
||||
base->clock_base[HRTIMER_BASE_REALTIME].resolution = KTIME_HIGH_RES;
|
||||
base->clock_base[HRTIMER_BASE_MONOTONIC].resolution = KTIME_HIGH_RES;
|
||||
base->clock_base[HRTIMER_BASE_BOOTTIME].resolution = KTIME_HIGH_RES;
|
||||
|
||||
tick_setup_sched_timer();
|
||||
|
||||
@@ -756,7 +757,6 @@ static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer,
|
||||
return 0;
|
||||
}
|
||||
static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) { }
|
||||
static inline void hrtimer_init_timer_hres(struct hrtimer *timer) { }
|
||||
|
||||
#endif /* CONFIG_HIGH_RES_TIMERS */
|
||||
|
||||
@@ -1127,6 +1127,7 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
||||
enum hrtimer_mode mode)
|
||||
{
|
||||
struct hrtimer_cpu_base *cpu_base;
|
||||
int base;
|
||||
|
||||
memset(timer, 0, sizeof(struct hrtimer));
|
||||
|
||||
@@ -1135,8 +1136,8 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
||||
if (clock_id == CLOCK_REALTIME && mode != HRTIMER_MODE_ABS)
|
||||
clock_id = CLOCK_MONOTONIC;
|
||||
|
||||
timer->base = &cpu_base->clock_base[clock_id];
|
||||
hrtimer_init_timer_hres(timer);
|
||||
base = hrtimer_clockid_to_base(clock_id);
|
||||
timer->base = &cpu_base->clock_base[base];
|
||||
timerqueue_init(&timer->node);
|
||||
|
||||
#ifdef CONFIG_TIMER_STATS
|
||||
@@ -1171,9 +1172,10 @@ EXPORT_SYMBOL_GPL(hrtimer_init);
|
||||
int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp)
|
||||
{
|
||||
struct hrtimer_cpu_base *cpu_base;
|
||||
int base = hrtimer_clockid_to_base(which_clock);
|
||||
|
||||
cpu_base = &__raw_get_cpu_var(hrtimer_bases);
|
||||
*tp = ktime_to_timespec(cpu_base->clock_base[which_clock].resolution);
|
||||
*tp = ktime_to_timespec(cpu_base->clock_base[base].resolution);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1720,6 +1722,10 @@ static struct notifier_block __cpuinitdata hrtimers_nb = {
|
||||
|
||||
void __init hrtimers_init(void)
|
||||
{
|
||||
hrtimer_clock_to_base_table[CLOCK_REALTIME] = HRTIMER_BASE_REALTIME;
|
||||
hrtimer_clock_to_base_table[CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC;
|
||||
hrtimer_clock_to_base_table[CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME;
|
||||
|
||||
hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE,
|
||||
(void *)(long)smp_processor_id());
|
||||
register_cpu_notifier(&hrtimers_nb);
|
||||
|
@@ -176,7 +176,8 @@ static inline cputime_t virt_ticks(struct task_struct *p)
|
||||
return p->utime;
|
||||
}
|
||||
|
||||
int posix_cpu_clock_getres(const clockid_t which_clock, struct timespec *tp)
|
||||
static int
|
||||
posix_cpu_clock_getres(const clockid_t which_clock, struct timespec *tp)
|
||||
{
|
||||
int error = check_clock(which_clock);
|
||||
if (!error) {
|
||||
@@ -194,7 +195,8 @@ int posix_cpu_clock_getres(const clockid_t which_clock, struct timespec *tp)
|
||||
return error;
|
||||
}
|
||||
|
||||
int posix_cpu_clock_set(const clockid_t which_clock, const struct timespec *tp)
|
||||
static int
|
||||
posix_cpu_clock_set(const clockid_t which_clock, const struct timespec *tp)
|
||||
{
|
||||
/*
|
||||
* You can never reset a CPU clock, but we check for other errors
|
||||
@@ -317,7 +319,7 @@ static int cpu_clock_sample_group(const clockid_t which_clock,
|
||||
}
|
||||
|
||||
|
||||
int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *tp)
|
||||
static int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *tp)
|
||||
{
|
||||
const pid_t pid = CPUCLOCK_PID(which_clock);
|
||||
int error = -EINVAL;
|
||||
@@ -379,7 +381,7 @@ int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *tp)
|
||||
* This is called from sys_timer_create() and do_cpu_nanosleep() with the
|
||||
* new timer already all-zeros initialized.
|
||||
*/
|
||||
int posix_cpu_timer_create(struct k_itimer *new_timer)
|
||||
static int posix_cpu_timer_create(struct k_itimer *new_timer)
|
||||
{
|
||||
int ret = 0;
|
||||
const pid_t pid = CPUCLOCK_PID(new_timer->it_clock);
|
||||
@@ -425,7 +427,7 @@ int posix_cpu_timer_create(struct k_itimer *new_timer)
|
||||
* If we return TIMER_RETRY, it's necessary to release the timer's lock
|
||||
* and try again. (This happens when the timer is in the middle of firing.)
|
||||
*/
|
||||
int posix_cpu_timer_del(struct k_itimer *timer)
|
||||
static int posix_cpu_timer_del(struct k_itimer *timer)
|
||||
{
|
||||
struct task_struct *p = timer->it.cpu.task;
|
||||
int ret = 0;
|
||||
@@ -665,8 +667,8 @@ static int cpu_timer_sample_group(const clockid_t which_clock,
|
||||
* If we return TIMER_RETRY, it's necessary to release the timer's lock
|
||||
* and try again. (This happens when the timer is in the middle of firing.)
|
||||
*/
|
||||
int posix_cpu_timer_set(struct k_itimer *timer, int flags,
|
||||
struct itimerspec *new, struct itimerspec *old)
|
||||
static int posix_cpu_timer_set(struct k_itimer *timer, int flags,
|
||||
struct itimerspec *new, struct itimerspec *old)
|
||||
{
|
||||
struct task_struct *p = timer->it.cpu.task;
|
||||
union cpu_time_count old_expires, new_expires, old_incr, val;
|
||||
@@ -820,7 +822,7 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,
|
||||
return ret;
|
||||
}
|
||||
|
||||
void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp)
|
||||
static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp)
|
||||
{
|
||||
union cpu_time_count now;
|
||||
struct task_struct *p = timer->it.cpu.task;
|
||||
@@ -1481,11 +1483,13 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
|
||||
return error;
|
||||
}
|
||||
|
||||
int posix_cpu_nsleep(const clockid_t which_clock, int flags,
|
||||
struct timespec *rqtp, struct timespec __user *rmtp)
|
||||
static long posix_cpu_nsleep_restart(struct restart_block *restart_block);
|
||||
|
||||
static int posix_cpu_nsleep(const clockid_t which_clock, int flags,
|
||||
struct timespec *rqtp, struct timespec __user *rmtp)
|
||||
{
|
||||
struct restart_block *restart_block =
|
||||
¤t_thread_info()->restart_block;
|
||||
¤t_thread_info()->restart_block;
|
||||
struct itimerspec it;
|
||||
int error;
|
||||
|
||||
@@ -1501,56 +1505,47 @@ int posix_cpu_nsleep(const clockid_t which_clock, int flags,
|
||||
|
||||
if (error == -ERESTART_RESTARTBLOCK) {
|
||||
|
||||
if (flags & TIMER_ABSTIME)
|
||||
if (flags & TIMER_ABSTIME)
|
||||
return -ERESTARTNOHAND;
|
||||
/*
|
||||
* Report back to the user the time still remaining.
|
||||
*/
|
||||
if (rmtp != NULL && copy_to_user(rmtp, &it.it_value, sizeof *rmtp))
|
||||
* Report back to the user the time still remaining.
|
||||
*/
|
||||
if (rmtp && copy_to_user(rmtp, &it.it_value, sizeof *rmtp))
|
||||
return -EFAULT;
|
||||
|
||||
restart_block->fn = posix_cpu_nsleep_restart;
|
||||
restart_block->arg0 = which_clock;
|
||||
restart_block->arg1 = (unsigned long) rmtp;
|
||||
restart_block->arg2 = rqtp->tv_sec;
|
||||
restart_block->arg3 = rqtp->tv_nsec;
|
||||
restart_block->nanosleep.index = which_clock;
|
||||
restart_block->nanosleep.rmtp = rmtp;
|
||||
restart_block->nanosleep.expires = timespec_to_ns(rqtp);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
long posix_cpu_nsleep_restart(struct restart_block *restart_block)
|
||||
static long posix_cpu_nsleep_restart(struct restart_block *restart_block)
|
||||
{
|
||||
clockid_t which_clock = restart_block->arg0;
|
||||
struct timespec __user *rmtp;
|
||||
clockid_t which_clock = restart_block->nanosleep.index;
|
||||
struct timespec t;
|
||||
struct itimerspec it;
|
||||
int error;
|
||||
|
||||
rmtp = (struct timespec __user *) restart_block->arg1;
|
||||
t.tv_sec = restart_block->arg2;
|
||||
t.tv_nsec = restart_block->arg3;
|
||||
t = ns_to_timespec(restart_block->nanosleep.expires);
|
||||
|
||||
restart_block->fn = do_no_restart_syscall;
|
||||
error = do_cpu_nanosleep(which_clock, TIMER_ABSTIME, &t, &it);
|
||||
|
||||
if (error == -ERESTART_RESTARTBLOCK) {
|
||||
struct timespec __user *rmtp = restart_block->nanosleep.rmtp;
|
||||
/*
|
||||
* Report back to the user the time still remaining.
|
||||
*/
|
||||
if (rmtp != NULL && copy_to_user(rmtp, &it.it_value, sizeof *rmtp))
|
||||
* Report back to the user the time still remaining.
|
||||
*/
|
||||
if (rmtp && copy_to_user(rmtp, &it.it_value, sizeof *rmtp))
|
||||
return -EFAULT;
|
||||
|
||||
restart_block->fn = posix_cpu_nsleep_restart;
|
||||
restart_block->arg0 = which_clock;
|
||||
restart_block->arg1 = (unsigned long) rmtp;
|
||||
restart_block->arg2 = t.tv_sec;
|
||||
restart_block->arg3 = t.tv_nsec;
|
||||
restart_block->nanosleep.expires = timespec_to_ns(&t);
|
||||
}
|
||||
return error;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#define PROCESS_CLOCK MAKE_PROCESS_CPUCLOCK(0, CPUCLOCK_SCHED)
|
||||
#define THREAD_CLOCK MAKE_THREAD_CPUCLOCK(0, CPUCLOCK_SCHED)
|
||||
|
||||
@@ -1594,38 +1589,37 @@ static int thread_cpu_timer_create(struct k_itimer *timer)
|
||||
timer->it_clock = THREAD_CLOCK;
|
||||
return posix_cpu_timer_create(timer);
|
||||
}
|
||||
static int thread_cpu_nsleep(const clockid_t which_clock, int flags,
|
||||
struct timespec *rqtp, struct timespec __user *rmtp)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static long thread_cpu_nsleep_restart(struct restart_block *restart_block)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct k_clock clock_posix_cpu = {
|
||||
.clock_getres = posix_cpu_clock_getres,
|
||||
.clock_set = posix_cpu_clock_set,
|
||||
.clock_get = posix_cpu_clock_get,
|
||||
.timer_create = posix_cpu_timer_create,
|
||||
.nsleep = posix_cpu_nsleep,
|
||||
.nsleep_restart = posix_cpu_nsleep_restart,
|
||||
.timer_set = posix_cpu_timer_set,
|
||||
.timer_del = posix_cpu_timer_del,
|
||||
.timer_get = posix_cpu_timer_get,
|
||||
};
|
||||
|
||||
static __init int init_posix_cpu_timers(void)
|
||||
{
|
||||
struct k_clock process = {
|
||||
.clock_getres = process_cpu_clock_getres,
|
||||
.clock_get = process_cpu_clock_get,
|
||||
.clock_set = do_posix_clock_nosettime,
|
||||
.timer_create = process_cpu_timer_create,
|
||||
.nsleep = process_cpu_nsleep,
|
||||
.nsleep_restart = process_cpu_nsleep_restart,
|
||||
.clock_getres = process_cpu_clock_getres,
|
||||
.clock_get = process_cpu_clock_get,
|
||||
.timer_create = process_cpu_timer_create,
|
||||
.nsleep = process_cpu_nsleep,
|
||||
.nsleep_restart = process_cpu_nsleep_restart,
|
||||
};
|
||||
struct k_clock thread = {
|
||||
.clock_getres = thread_cpu_clock_getres,
|
||||
.clock_get = thread_cpu_clock_get,
|
||||
.clock_set = do_posix_clock_nosettime,
|
||||
.timer_create = thread_cpu_timer_create,
|
||||
.nsleep = thread_cpu_nsleep,
|
||||
.nsleep_restart = thread_cpu_nsleep_restart,
|
||||
.clock_getres = thread_cpu_clock_getres,
|
||||
.clock_get = thread_cpu_clock_get,
|
||||
.timer_create = thread_cpu_timer_create,
|
||||
};
|
||||
struct timespec ts;
|
||||
|
||||
register_posix_clock(CLOCK_PROCESS_CPUTIME_ID, &process);
|
||||
register_posix_clock(CLOCK_THREAD_CPUTIME_ID, &thread);
|
||||
posix_timers_register_clock(CLOCK_PROCESS_CPUTIME_ID, &process);
|
||||
posix_timers_register_clock(CLOCK_THREAD_CPUTIME_ID, &thread);
|
||||
|
||||
cputime_to_timespec(cputime_one_jiffy, &ts);
|
||||
onecputick = ts.tv_nsec;
|
||||
|
@@ -41,6 +41,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/posix-clock.h>
|
||||
#include <linux/posix-timers.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/wait.h>
|
||||
@@ -81,6 +82,14 @@ static DEFINE_SPINLOCK(idr_lock);
|
||||
#error "SIGEV_THREAD_ID must not share bit with other SIGEV values!"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* parisc wants ENOTSUP instead of EOPNOTSUPP
|
||||
*/
|
||||
#ifndef ENOTSUP
|
||||
# define ENANOSLEEP_NOTSUP EOPNOTSUPP
|
||||
#else
|
||||
# define ENANOSLEEP_NOTSUP ENOTSUP
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The timer ID is turned into a timer address by idr_find().
|
||||
@@ -94,11 +103,7 @@ static DEFINE_SPINLOCK(idr_lock);
|
||||
/*
|
||||
* CLOCKs: The POSIX standard calls for a couple of clocks and allows us
|
||||
* to implement others. This structure defines the various
|
||||
* clocks and allows the possibility of adding others. We
|
||||
* provide an interface to add clocks to the table and expect
|
||||
* the "arch" code to add at least one clock that is high
|
||||
* resolution. Here we define the standard CLOCK_REALTIME as a
|
||||
* 1/HZ resolution clock.
|
||||
* clocks.
|
||||
*
|
||||
* RESOLUTION: Clock resolution is used to round up timer and interval
|
||||
* times, NOT to report clock times, which are reported with as
|
||||
@@ -108,20 +113,13 @@ static DEFINE_SPINLOCK(idr_lock);
|
||||
* necessary code is written. The standard says we should say
|
||||
* something about this issue in the documentation...
|
||||
*
|
||||
* FUNCTIONS: The CLOCKs structure defines possible functions to handle
|
||||
* various clock functions. For clocks that use the standard
|
||||
* system timer code these entries should be NULL. This will
|
||||
* allow dispatch without the overhead of indirect function
|
||||
* calls. CLOCKS that depend on other sources (e.g. WWV or GPS)
|
||||
* must supply functions here, even if the function just returns
|
||||
* ENOSYS. The standard POSIX timer management code assumes the
|
||||
* following: 1.) The k_itimer struct (sched.h) is used for the
|
||||
* timer. 2.) The list, it_lock, it_clock, it_id and it_pid
|
||||
* fields are not modified by timer code.
|
||||
* FUNCTIONS: The CLOCKs structure defines possible functions to
|
||||
* handle various clock functions.
|
||||
*
|
||||
* At this time all functions EXCEPT clock_nanosleep can be
|
||||
* redirected by the CLOCKS structure. Clock_nanosleep is in
|
||||
* there, but the code ignores it.
|
||||
* The standard POSIX timer management code assumes the
|
||||
* following: 1.) The k_itimer struct (sched.h) is used for
|
||||
* the timer. 2.) The list, it_lock, it_clock, it_id and
|
||||
* it_pid fields are not modified by timer code.
|
||||
*
|
||||
* Permissions: It is assumed that the clock_settime() function defined
|
||||
* for each clock will take care of permission checks. Some
|
||||
@@ -138,6 +136,7 @@ static struct k_clock posix_clocks[MAX_CLOCKS];
|
||||
*/
|
||||
static int common_nsleep(const clockid_t, int flags, struct timespec *t,
|
||||
struct timespec __user *rmtp);
|
||||
static int common_timer_create(struct k_itimer *new_timer);
|
||||
static void common_timer_get(struct k_itimer *, struct itimerspec *);
|
||||
static int common_timer_set(struct k_itimer *, int,
|
||||
struct itimerspec *, struct itimerspec *);
|
||||
@@ -158,76 +157,24 @@ static inline void unlock_timer(struct k_itimer *timr, unsigned long flags)
|
||||
spin_unlock_irqrestore(&timr->it_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call the k_clock hook function if non-null, or the default function.
|
||||
*/
|
||||
#define CLOCK_DISPATCH(clock, call, arglist) \
|
||||
((clock) < 0 ? posix_cpu_##call arglist : \
|
||||
(posix_clocks[clock].call != NULL \
|
||||
? (*posix_clocks[clock].call) arglist : common_##call arglist))
|
||||
|
||||
/*
|
||||
* Default clock hook functions when the struct k_clock passed
|
||||
* to register_posix_clock leaves a function pointer null.
|
||||
*
|
||||
* The function common_CALL is the default implementation for
|
||||
* the function pointer CALL in struct k_clock.
|
||||
*/
|
||||
|
||||
static inline int common_clock_getres(const clockid_t which_clock,
|
||||
struct timespec *tp)
|
||||
{
|
||||
tp->tv_sec = 0;
|
||||
tp->tv_nsec = posix_clocks[which_clock].res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get real time for posix timers
|
||||
*/
|
||||
static int common_clock_get(clockid_t which_clock, struct timespec *tp)
|
||||
/* Get clock_realtime */
|
||||
static int posix_clock_realtime_get(clockid_t which_clock, struct timespec *tp)
|
||||
{
|
||||
ktime_get_real_ts(tp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int common_clock_set(const clockid_t which_clock,
|
||||
struct timespec *tp)
|
||||
/* Set clock_realtime */
|
||||
static int posix_clock_realtime_set(const clockid_t which_clock,
|
||||
const struct timespec *tp)
|
||||
{
|
||||
return do_sys_settimeofday(tp, NULL);
|
||||
}
|
||||
|
||||
static int common_timer_create(struct k_itimer *new_timer)
|
||||
static int posix_clock_realtime_adj(const clockid_t which_clock,
|
||||
struct timex *t)
|
||||
{
|
||||
hrtimer_init(&new_timer->it.real.timer, new_timer->it_clock, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int no_timer_create(struct k_itimer *new_timer)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int no_nsleep(const clockid_t which_clock, int flags,
|
||||
struct timespec *tsave, struct timespec __user *rmtp)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return nonzero if we know a priori this clockid_t value is bogus.
|
||||
*/
|
||||
static inline int invalid_clockid(const clockid_t which_clock)
|
||||
{
|
||||
if (which_clock < 0) /* CPU clock, posix_cpu_* will check it */
|
||||
return 0;
|
||||
if ((unsigned) which_clock >= MAX_CLOCKS)
|
||||
return 1;
|
||||
if (posix_clocks[which_clock].clock_getres != NULL)
|
||||
return 0;
|
||||
if (posix_clocks[which_clock].res != 0)
|
||||
return 0;
|
||||
return 1;
|
||||
return do_adjtimex(t);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -240,7 +187,7 @@ static int posix_ktime_get_ts(clockid_t which_clock, struct timespec *tp)
|
||||
}
|
||||
|
||||
/*
|
||||
* Get monotonic time for posix timers
|
||||
* Get monotonic-raw time for posix timers
|
||||
*/
|
||||
static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec *tp)
|
||||
{
|
||||
@@ -267,46 +214,70 @@ static int posix_get_coarse_res(const clockid_t which_clock, struct timespec *tp
|
||||
*tp = ktime_to_timespec(KTIME_LOW_RES);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int posix_get_boottime(const clockid_t which_clock, struct timespec *tp)
|
||||
{
|
||||
get_monotonic_boottime(tp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize everything, well, just everything in Posix clocks/timers ;)
|
||||
*/
|
||||
static __init int init_posix_timers(void)
|
||||
{
|
||||
struct k_clock clock_realtime = {
|
||||
.clock_getres = hrtimer_get_res,
|
||||
.clock_getres = hrtimer_get_res,
|
||||
.clock_get = posix_clock_realtime_get,
|
||||
.clock_set = posix_clock_realtime_set,
|
||||
.clock_adj = posix_clock_realtime_adj,
|
||||
.nsleep = common_nsleep,
|
||||
.nsleep_restart = hrtimer_nanosleep_restart,
|
||||
.timer_create = common_timer_create,
|
||||
.timer_set = common_timer_set,
|
||||
.timer_get = common_timer_get,
|
||||
.timer_del = common_timer_del,
|
||||
};
|
||||
struct k_clock clock_monotonic = {
|
||||
.clock_getres = hrtimer_get_res,
|
||||
.clock_get = posix_ktime_get_ts,
|
||||
.clock_set = do_posix_clock_nosettime,
|
||||
.clock_getres = hrtimer_get_res,
|
||||
.clock_get = posix_ktime_get_ts,
|
||||
.nsleep = common_nsleep,
|
||||
.nsleep_restart = hrtimer_nanosleep_restart,
|
||||
.timer_create = common_timer_create,
|
||||
.timer_set = common_timer_set,
|
||||
.timer_get = common_timer_get,
|
||||
.timer_del = common_timer_del,
|
||||
};
|
||||
struct k_clock clock_monotonic_raw = {
|
||||
.clock_getres = hrtimer_get_res,
|
||||
.clock_get = posix_get_monotonic_raw,
|
||||
.clock_set = do_posix_clock_nosettime,
|
||||
.timer_create = no_timer_create,
|
||||
.nsleep = no_nsleep,
|
||||
.clock_getres = hrtimer_get_res,
|
||||
.clock_get = posix_get_monotonic_raw,
|
||||
};
|
||||
struct k_clock clock_realtime_coarse = {
|
||||
.clock_getres = posix_get_coarse_res,
|
||||
.clock_get = posix_get_realtime_coarse,
|
||||
.clock_set = do_posix_clock_nosettime,
|
||||
.timer_create = no_timer_create,
|
||||
.nsleep = no_nsleep,
|
||||
.clock_getres = posix_get_coarse_res,
|
||||
.clock_get = posix_get_realtime_coarse,
|
||||
};
|
||||
struct k_clock clock_monotonic_coarse = {
|
||||
.clock_getres = posix_get_coarse_res,
|
||||
.clock_get = posix_get_monotonic_coarse,
|
||||
.clock_set = do_posix_clock_nosettime,
|
||||
.timer_create = no_timer_create,
|
||||
.nsleep = no_nsleep,
|
||||
.clock_getres = posix_get_coarse_res,
|
||||
.clock_get = posix_get_monotonic_coarse,
|
||||
};
|
||||
struct k_clock clock_boottime = {
|
||||
.clock_getres = hrtimer_get_res,
|
||||
.clock_get = posix_get_boottime,
|
||||
.nsleep = common_nsleep,
|
||||
.nsleep_restart = hrtimer_nanosleep_restart,
|
||||
.timer_create = common_timer_create,
|
||||
.timer_set = common_timer_set,
|
||||
.timer_get = common_timer_get,
|
||||
.timer_del = common_timer_del,
|
||||
};
|
||||
|
||||
register_posix_clock(CLOCK_REALTIME, &clock_realtime);
|
||||
register_posix_clock(CLOCK_MONOTONIC, &clock_monotonic);
|
||||
register_posix_clock(CLOCK_MONOTONIC_RAW, &clock_monotonic_raw);
|
||||
register_posix_clock(CLOCK_REALTIME_COARSE, &clock_realtime_coarse);
|
||||
register_posix_clock(CLOCK_MONOTONIC_COARSE, &clock_monotonic_coarse);
|
||||
posix_timers_register_clock(CLOCK_REALTIME, &clock_realtime);
|
||||
posix_timers_register_clock(CLOCK_MONOTONIC, &clock_monotonic);
|
||||
posix_timers_register_clock(CLOCK_MONOTONIC_RAW, &clock_monotonic_raw);
|
||||
posix_timers_register_clock(CLOCK_REALTIME_COARSE, &clock_realtime_coarse);
|
||||
posix_timers_register_clock(CLOCK_MONOTONIC_COARSE, &clock_monotonic_coarse);
|
||||
posix_timers_register_clock(CLOCK_BOOTTIME, &clock_boottime);
|
||||
|
||||
posix_timers_cache = kmem_cache_create("posix_timers_cache",
|
||||
sizeof (struct k_itimer), 0, SLAB_PANIC,
|
||||
@@ -482,17 +453,29 @@ static struct pid *good_sigevent(sigevent_t * event)
|
||||
return task_pid(rtn);
|
||||
}
|
||||
|
||||
void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock)
|
||||
void posix_timers_register_clock(const clockid_t clock_id,
|
||||
struct k_clock *new_clock)
|
||||
{
|
||||
if ((unsigned) clock_id >= MAX_CLOCKS) {
|
||||
printk("POSIX clock register failed for clock_id %d\n",
|
||||
printk(KERN_WARNING "POSIX clock register failed for clock_id %d\n",
|
||||
clock_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!new_clock->clock_get) {
|
||||
printk(KERN_WARNING "POSIX clock id %d lacks clock_get()\n",
|
||||
clock_id);
|
||||
return;
|
||||
}
|
||||
if (!new_clock->clock_getres) {
|
||||
printk(KERN_WARNING "POSIX clock id %d lacks clock_getres()\n",
|
||||
clock_id);
|
||||
return;
|
||||
}
|
||||
|
||||
posix_clocks[clock_id] = *new_clock;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_posix_clock);
|
||||
EXPORT_SYMBOL_GPL(posix_timers_register_clock);
|
||||
|
||||
static struct k_itimer * alloc_posix_timer(void)
|
||||
{
|
||||
@@ -523,19 +506,39 @@ static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
|
||||
kmem_cache_free(posix_timers_cache, tmr);
|
||||
}
|
||||
|
||||
static struct k_clock *clockid_to_kclock(const clockid_t id)
|
||||
{
|
||||
if (id < 0)
|
||||
return (id & CLOCKFD_MASK) == CLOCKFD ?
|
||||
&clock_posix_dynamic : &clock_posix_cpu;
|
||||
|
||||
if (id >= MAX_CLOCKS || !posix_clocks[id].clock_getres)
|
||||
return NULL;
|
||||
return &posix_clocks[id];
|
||||
}
|
||||
|
||||
static int common_timer_create(struct k_itimer *new_timer)
|
||||
{
|
||||
hrtimer_init(&new_timer->it.real.timer, new_timer->it_clock, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a POSIX.1b interval timer. */
|
||||
|
||||
SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
|
||||
struct sigevent __user *, timer_event_spec,
|
||||
timer_t __user *, created_timer_id)
|
||||
{
|
||||
struct k_clock *kc = clockid_to_kclock(which_clock);
|
||||
struct k_itimer *new_timer;
|
||||
int error, new_timer_id;
|
||||
sigevent_t event;
|
||||
int it_id_set = IT_ID_NOT_SET;
|
||||
|
||||
if (invalid_clockid(which_clock))
|
||||
if (!kc)
|
||||
return -EINVAL;
|
||||
if (!kc->timer_create)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
new_timer = alloc_posix_timer();
|
||||
if (unlikely(!new_timer))
|
||||
@@ -597,7 +600,7 @@ SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = CLOCK_DISPATCH(which_clock, timer_create, (new_timer));
|
||||
error = kc->timer_create(new_timer);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
@@ -607,7 +610,7 @@ SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
return 0;
|
||||
/*
|
||||
/*
|
||||
* In the case of the timer belonging to another task, after
|
||||
* the task is unlocked, the timer is owned by the other task
|
||||
* and may cease to exist at any time. Don't use or modify
|
||||
@@ -709,22 +712,28 @@ common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting)
|
||||
SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,
|
||||
struct itimerspec __user *, setting)
|
||||
{
|
||||
struct k_itimer *timr;
|
||||
struct itimerspec cur_setting;
|
||||
struct k_itimer *timr;
|
||||
struct k_clock *kc;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
timr = lock_timer(timer_id, &flags);
|
||||
if (!timr)
|
||||
return -EINVAL;
|
||||
|
||||
CLOCK_DISPATCH(timr->it_clock, timer_get, (timr, &cur_setting));
|
||||
kc = clockid_to_kclock(timr->it_clock);
|
||||
if (WARN_ON_ONCE(!kc || !kc->timer_get))
|
||||
ret = -EINVAL;
|
||||
else
|
||||
kc->timer_get(timr, &cur_setting);
|
||||
|
||||
unlock_timer(timr, flags);
|
||||
|
||||
if (copy_to_user(setting, &cur_setting, sizeof (cur_setting)))
|
||||
if (!ret && copy_to_user(setting, &cur_setting, sizeof (cur_setting)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -813,6 +822,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
|
||||
int error = 0;
|
||||
unsigned long flag;
|
||||
struct itimerspec *rtn = old_setting ? &old_spec : NULL;
|
||||
struct k_clock *kc;
|
||||
|
||||
if (!new_setting)
|
||||
return -EINVAL;
|
||||
@@ -828,8 +838,11 @@ retry:
|
||||
if (!timr)
|
||||
return -EINVAL;
|
||||
|
||||
error = CLOCK_DISPATCH(timr->it_clock, timer_set,
|
||||
(timr, flags, &new_spec, rtn));
|
||||
kc = clockid_to_kclock(timr->it_clock);
|
||||
if (WARN_ON_ONCE(!kc || !kc->timer_set))
|
||||
error = -EINVAL;
|
||||
else
|
||||
error = kc->timer_set(timr, flags, &new_spec, rtn);
|
||||
|
||||
unlock_timer(timr, flag);
|
||||
if (error == TIMER_RETRY) {
|
||||
@@ -844,7 +857,7 @@ retry:
|
||||
return error;
|
||||
}
|
||||
|
||||
static inline int common_timer_del(struct k_itimer *timer)
|
||||
static int common_timer_del(struct k_itimer *timer)
|
||||
{
|
||||
timer->it.real.interval.tv64 = 0;
|
||||
|
||||
@@ -855,7 +868,11 @@ static inline int common_timer_del(struct k_itimer *timer)
|
||||
|
||||
static inline int timer_delete_hook(struct k_itimer *timer)
|
||||
{
|
||||
return CLOCK_DISPATCH(timer->it_clock, timer_del, (timer));
|
||||
struct k_clock *kc = clockid_to_kclock(timer->it_clock);
|
||||
|
||||
if (WARN_ON_ONCE(!kc || !kc->timer_del))
|
||||
return -EINVAL;
|
||||
return kc->timer_del(timer);
|
||||
}
|
||||
|
||||
/* Delete a POSIX.1b interval timer. */
|
||||
@@ -927,69 +944,76 @@ void exit_itimers(struct signal_struct *sig)
|
||||
}
|
||||
}
|
||||
|
||||
/* Not available / possible... functions */
|
||||
int do_posix_clock_nosettime(const clockid_t clockid, struct timespec *tp)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(do_posix_clock_nosettime);
|
||||
|
||||
int do_posix_clock_nonanosleep(const clockid_t clock, int flags,
|
||||
struct timespec *t, struct timespec __user *r)
|
||||
{
|
||||
#ifndef ENOTSUP
|
||||
return -EOPNOTSUPP; /* aka ENOTSUP in userland for POSIX */
|
||||
#else /* parisc does define it separately. */
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(do_posix_clock_nonanosleep);
|
||||
|
||||
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
|
||||
const struct timespec __user *, tp)
|
||||
{
|
||||
struct k_clock *kc = clockid_to_kclock(which_clock);
|
||||
struct timespec new_tp;
|
||||
|
||||
if (invalid_clockid(which_clock))
|
||||
if (!kc || !kc->clock_set)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&new_tp, tp, sizeof (*tp)))
|
||||
return -EFAULT;
|
||||
|
||||
return CLOCK_DISPATCH(which_clock, clock_set, (which_clock, &new_tp));
|
||||
return kc->clock_set(which_clock, &new_tp);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
|
||||
struct timespec __user *,tp)
|
||||
{
|
||||
struct k_clock *kc = clockid_to_kclock(which_clock);
|
||||
struct timespec kernel_tp;
|
||||
int error;
|
||||
|
||||
if (invalid_clockid(which_clock))
|
||||
if (!kc)
|
||||
return -EINVAL;
|
||||
error = CLOCK_DISPATCH(which_clock, clock_get,
|
||||
(which_clock, &kernel_tp));
|
||||
|
||||
error = kc->clock_get(which_clock, &kernel_tp);
|
||||
|
||||
if (!error && copy_to_user(tp, &kernel_tp, sizeof (kernel_tp)))
|
||||
error = -EFAULT;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
|
||||
struct timex __user *, utx)
|
||||
{
|
||||
struct k_clock *kc = clockid_to_kclock(which_clock);
|
||||
struct timex ktx;
|
||||
int err;
|
||||
|
||||
if (!kc)
|
||||
return -EINVAL;
|
||||
if (!kc->clock_adj)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (copy_from_user(&ktx, utx, sizeof(ktx)))
|
||||
return -EFAULT;
|
||||
|
||||
err = kc->clock_adj(which_clock, &ktx);
|
||||
|
||||
if (!err && copy_to_user(utx, &ktx, sizeof(ktx)))
|
||||
return -EFAULT;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
|
||||
struct timespec __user *, tp)
|
||||
{
|
||||
struct k_clock *kc = clockid_to_kclock(which_clock);
|
||||
struct timespec rtn_tp;
|
||||
int error;
|
||||
|
||||
if (invalid_clockid(which_clock))
|
||||
if (!kc)
|
||||
return -EINVAL;
|
||||
|
||||
error = CLOCK_DISPATCH(which_clock, clock_getres,
|
||||
(which_clock, &rtn_tp));
|
||||
error = kc->clock_getres(which_clock, &rtn_tp);
|
||||
|
||||
if (!error && tp && copy_to_user(tp, &rtn_tp, sizeof (rtn_tp))) {
|
||||
if (!error && tp && copy_to_user(tp, &rtn_tp, sizeof (rtn_tp)))
|
||||
error = -EFAULT;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -1009,10 +1033,13 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
|
||||
const struct timespec __user *, rqtp,
|
||||
struct timespec __user *, rmtp)
|
||||
{
|
||||
struct k_clock *kc = clockid_to_kclock(which_clock);
|
||||
struct timespec t;
|
||||
|
||||
if (invalid_clockid(which_clock))
|
||||
if (!kc)
|
||||
return -EINVAL;
|
||||
if (!kc->nsleep)
|
||||
return -ENANOSLEEP_NOTSUP;
|
||||
|
||||
if (copy_from_user(&t, rqtp, sizeof (struct timespec)))
|
||||
return -EFAULT;
|
||||
@@ -1020,27 +1047,20 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
|
||||
if (!timespec_valid(&t))
|
||||
return -EINVAL;
|
||||
|
||||
return CLOCK_DISPATCH(which_clock, nsleep,
|
||||
(which_clock, flags, &t, rmtp));
|
||||
}
|
||||
|
||||
/*
|
||||
* nanosleep_restart for monotonic and realtime clocks
|
||||
*/
|
||||
static int common_nsleep_restart(struct restart_block *restart_block)
|
||||
{
|
||||
return hrtimer_nanosleep_restart(restart_block);
|
||||
return kc->nsleep(which_clock, flags, &t, rmtp);
|
||||
}
|
||||
|
||||
/*
|
||||
* This will restart clock_nanosleep. This is required only by
|
||||
* compat_clock_nanosleep_restart for now.
|
||||
*/
|
||||
long
|
||||
clock_nanosleep_restart(struct restart_block *restart_block)
|
||||
long clock_nanosleep_restart(struct restart_block *restart_block)
|
||||
{
|
||||
clockid_t which_clock = restart_block->arg0;
|
||||
clockid_t which_clock = restart_block->nanosleep.index;
|
||||
struct k_clock *kc = clockid_to_kclock(which_clock);
|
||||
|
||||
return CLOCK_DISPATCH(which_clock, nsleep_restart,
|
||||
(restart_block));
|
||||
if (WARN_ON_ONCE(!kc || !kc->nsleep_restart))
|
||||
return -EINVAL;
|
||||
|
||||
return kc->nsleep_restart(restart_block);
|
||||
}
|
||||
|
@@ -150,7 +150,7 @@ static inline void warp_clock(void)
|
||||
* various programs will get confused when the clock gets warped.
|
||||
*/
|
||||
|
||||
int do_sys_settimeofday(struct timespec *tv, struct timezone *tz)
|
||||
int do_sys_settimeofday(const struct timespec *tv, const struct timezone *tz)
|
||||
{
|
||||
static int firsttime = 1;
|
||||
int error = 0;
|
||||
@@ -674,7 +674,6 @@ u64 nsecs_to_jiffies64(u64 n)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* nsecs_to_jiffies - Convert nsecs in u64 to jiffies
|
||||
*
|
||||
@@ -693,23 +692,6 @@ unsigned long nsecs_to_jiffies(u64 n)
|
||||
return (unsigned long)nsecs_to_jiffies64(n);
|
||||
}
|
||||
|
||||
#if (BITS_PER_LONG < 64)
|
||||
u64 get_jiffies_64(void)
|
||||
{
|
||||
unsigned long seq;
|
||||
u64 ret;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
ret = jiffies_64;
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(get_jiffies_64);
|
||||
#endif
|
||||
|
||||
EXPORT_SYMBOL(jiffies);
|
||||
|
||||
/*
|
||||
* Add two timespec values and do a safety check for overflow.
|
||||
* It's assumed that both values are valid (>= 0)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o timecompare.o timeconv.o
|
||||
obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o timecompare.o
|
||||
obj-y += timeconv.o posix-clock.o
|
||||
|
||||
obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o
|
||||
obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o
|
||||
|
@@ -18,7 +18,6 @@
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/sysdev.h>
|
||||
#include <linux/tick.h>
|
||||
|
||||
#include "tick-internal.h"
|
||||
|
||||
|
@@ -22,8 +22,11 @@
|
||||
************************************************************************/
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include "tick-internal.h"
|
||||
|
||||
/* The Jiffies based clocksource is the lowest common
|
||||
* denominator clock source which should function on
|
||||
* all systems. It has the same coarse resolution as
|
||||
@@ -64,6 +67,23 @@ struct clocksource clocksource_jiffies = {
|
||||
.shift = JIFFIES_SHIFT,
|
||||
};
|
||||
|
||||
#if (BITS_PER_LONG < 64)
|
||||
u64 get_jiffies_64(void)
|
||||
{
|
||||
unsigned long seq;
|
||||
u64 ret;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
ret = jiffies_64;
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(get_jiffies_64);
|
||||
#endif
|
||||
|
||||
EXPORT_SYMBOL(jiffies);
|
||||
|
||||
static int __init init_jiffies_clocksource(void)
|
||||
{
|
||||
return clocksource_register(&clocksource_jiffies);
|
||||
|
@@ -16,6 +16,8 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "tick-internal.h"
|
||||
|
||||
/*
|
||||
* NTP timekeeping variables:
|
||||
*/
|
||||
@@ -646,6 +648,17 @@ int do_adjtimex(struct timex *txc)
|
||||
hrtimer_cancel(&leap_timer);
|
||||
}
|
||||
|
||||
if (txc->modes & ADJ_SETOFFSET) {
|
||||
struct timespec delta;
|
||||
delta.tv_sec = txc->time.tv_sec;
|
||||
delta.tv_nsec = txc->time.tv_usec;
|
||||
if (!(txc->modes & ADJ_NANO))
|
||||
delta.tv_nsec *= 1000;
|
||||
result = timekeeping_inject_offset(&delta);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
getnstimeofday(&ts);
|
||||
|
||||
write_seqlock_irq(&xtime_lock);
|
||||
|
451
kernel/time/posix-clock.c
普通文件
451
kernel/time/posix-clock.c
普通文件
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
* posix-clock.c - support for dynamic clock devices
|
||||
*
|
||||
* Copyright (C) 2010 OMICRON electronics GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/posix-clock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
static void delete_clock(struct kref *kref);
|
||||
|
||||
/*
|
||||
* Returns NULL if the posix_clock instance attached to 'fp' is old and stale.
|
||||
*/
|
||||
static struct posix_clock *get_posix_clock(struct file *fp)
|
||||
{
|
||||
struct posix_clock *clk = fp->private_data;
|
||||
|
||||
mutex_lock(&clk->mutex);
|
||||
|
||||
if (!clk->zombie)
|
||||
return clk;
|
||||
|
||||
mutex_unlock(&clk->mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void put_posix_clock(struct posix_clock *clk)
|
||||
{
|
||||
mutex_unlock(&clk->mutex);
|
||||
}
|
||||
|
||||
static ssize_t posix_clock_read(struct file *fp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct posix_clock *clk = get_posix_clock(fp);
|
||||
int err = -EINVAL;
|
||||
|
||||
if (!clk)
|
||||
return -ENODEV;
|
||||
|
||||
if (clk->ops.read)
|
||||
err = clk->ops.read(clk, fp->f_flags, buf, count);
|
||||
|
||||
put_posix_clock(clk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static unsigned int posix_clock_poll(struct file *fp, poll_table *wait)
|
||||
{
|
||||
struct posix_clock *clk = get_posix_clock(fp);
|
||||
int result = 0;
|
||||
|
||||
if (!clk)
|
||||
return -ENODEV;
|
||||
|
||||
if (clk->ops.poll)
|
||||
result = clk->ops.poll(clk, fp, wait);
|
||||
|
||||
put_posix_clock(clk);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int posix_clock_fasync(int fd, struct file *fp, int on)
|
||||
{
|
||||
struct posix_clock *clk = get_posix_clock(fp);
|
||||
int err = 0;
|
||||
|
||||
if (!clk)
|
||||
return -ENODEV;
|
||||
|
||||
if (clk->ops.fasync)
|
||||
err = clk->ops.fasync(clk, fd, fp, on);
|
||||
|
||||
put_posix_clock(clk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int posix_clock_mmap(struct file *fp, struct vm_area_struct *vma)
|
||||
{
|
||||
struct posix_clock *clk = get_posix_clock(fp);
|
||||
int err = -ENODEV;
|
||||
|
||||
if (!clk)
|
||||
return -ENODEV;
|
||||
|
||||
if (clk->ops.mmap)
|
||||
err = clk->ops.mmap(clk, vma);
|
||||
|
||||
put_posix_clock(clk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static long posix_clock_ioctl(struct file *fp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct posix_clock *clk = get_posix_clock(fp);
|
||||
int err = -ENOTTY;
|
||||
|
||||
if (!clk)
|
||||
return -ENODEV;
|
||||
|
||||
if (clk->ops.ioctl)
|
||||
err = clk->ops.ioctl(clk, cmd, arg);
|
||||
|
||||
put_posix_clock(clk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long posix_clock_compat_ioctl(struct file *fp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct posix_clock *clk = get_posix_clock(fp);
|
||||
int err = -ENOTTY;
|
||||
|
||||
if (!clk)
|
||||
return -ENODEV;
|
||||
|
||||
if (clk->ops.ioctl)
|
||||
err = clk->ops.ioctl(clk, cmd, arg);
|
||||
|
||||
put_posix_clock(clk);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int posix_clock_open(struct inode *inode, struct file *fp)
|
||||
{
|
||||
int err;
|
||||
struct posix_clock *clk =
|
||||
container_of(inode->i_cdev, struct posix_clock, cdev);
|
||||
|
||||
mutex_lock(&clk->mutex);
|
||||
|
||||
if (clk->zombie) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (clk->ops.open)
|
||||
err = clk->ops.open(clk, fp->f_mode);
|
||||
else
|
||||
err = 0;
|
||||
|
||||
if (!err) {
|
||||
kref_get(&clk->kref);
|
||||
fp->private_data = clk;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&clk->mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int posix_clock_release(struct inode *inode, struct file *fp)
|
||||
{
|
||||
struct posix_clock *clk = fp->private_data;
|
||||
int err = 0;
|
||||
|
||||
if (clk->ops.release)
|
||||
err = clk->ops.release(clk);
|
||||
|
||||
kref_put(&clk->kref, delete_clock);
|
||||
|
||||
fp->private_data = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct file_operations posix_clock_file_operations = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = posix_clock_read,
|
||||
.poll = posix_clock_poll,
|
||||
.unlocked_ioctl = posix_clock_ioctl,
|
||||
.open = posix_clock_open,
|
||||
.release = posix_clock_release,
|
||||
.fasync = posix_clock_fasync,
|
||||
.mmap = posix_clock_mmap,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = posix_clock_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
int posix_clock_register(struct posix_clock *clk, dev_t devid)
|
||||
{
|
||||
int err;
|
||||
|
||||
kref_init(&clk->kref);
|
||||
mutex_init(&clk->mutex);
|
||||
|
||||
cdev_init(&clk->cdev, &posix_clock_file_operations);
|
||||
clk->cdev.owner = clk->ops.owner;
|
||||
err = cdev_add(&clk->cdev, devid, 1);
|
||||
if (err)
|
||||
goto no_cdev;
|
||||
|
||||
return err;
|
||||
no_cdev:
|
||||
mutex_destroy(&clk->mutex);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(posix_clock_register);
|
||||
|
||||
static void delete_clock(struct kref *kref)
|
||||
{
|
||||
struct posix_clock *clk = container_of(kref, struct posix_clock, kref);
|
||||
mutex_destroy(&clk->mutex);
|
||||
if (clk->release)
|
||||
clk->release(clk);
|
||||
}
|
||||
|
||||
void posix_clock_unregister(struct posix_clock *clk)
|
||||
{
|
||||
cdev_del(&clk->cdev);
|
||||
|
||||
mutex_lock(&clk->mutex);
|
||||
clk->zombie = true;
|
||||
mutex_unlock(&clk->mutex);
|
||||
|
||||
kref_put(&clk->kref, delete_clock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(posix_clock_unregister);
|
||||
|
||||
struct posix_clock_desc {
|
||||
struct file *fp;
|
||||
struct posix_clock *clk;
|
||||
};
|
||||
|
||||
static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd)
|
||||
{
|
||||
struct file *fp = fget(CLOCKID_TO_FD(id));
|
||||
int err = -EINVAL;
|
||||
|
||||
if (!fp)
|
||||
return err;
|
||||
|
||||
if (fp->f_op->open != posix_clock_open || !fp->private_data)
|
||||
goto out;
|
||||
|
||||
cd->fp = fp;
|
||||
cd->clk = get_posix_clock(fp);
|
||||
|
||||
err = cd->clk ? 0 : -ENODEV;
|
||||
out:
|
||||
if (err)
|
||||
fput(fp);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void put_clock_desc(struct posix_clock_desc *cd)
|
||||
{
|
||||
put_posix_clock(cd->clk);
|
||||
fput(cd->fp);
|
||||
}
|
||||
|
||||
static int pc_clock_adjtime(clockid_t id, struct timex *tx)
|
||||
{
|
||||
struct posix_clock_desc cd;
|
||||
int err;
|
||||
|
||||
err = get_clock_desc(id, &cd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
|
||||
err = -EACCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cd.clk->ops.clock_adjtime)
|
||||
err = cd.clk->ops.clock_adjtime(cd.clk, tx);
|
||||
else
|
||||
err = -EOPNOTSUPP;
|
||||
out:
|
||||
put_clock_desc(&cd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pc_clock_gettime(clockid_t id, struct timespec *ts)
|
||||
{
|
||||
struct posix_clock_desc cd;
|
||||
int err;
|
||||
|
||||
err = get_clock_desc(id, &cd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cd.clk->ops.clock_gettime)
|
||||
err = cd.clk->ops.clock_gettime(cd.clk, ts);
|
||||
else
|
||||
err = -EOPNOTSUPP;
|
||||
|
||||
put_clock_desc(&cd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pc_clock_getres(clockid_t id, struct timespec *ts)
|
||||
{
|
||||
struct posix_clock_desc cd;
|
||||
int err;
|
||||
|
||||
err = get_clock_desc(id, &cd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cd.clk->ops.clock_getres)
|
||||
err = cd.clk->ops.clock_getres(cd.clk, ts);
|
||||
else
|
||||
err = -EOPNOTSUPP;
|
||||
|
||||
put_clock_desc(&cd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pc_clock_settime(clockid_t id, const struct timespec *ts)
|
||||
{
|
||||
struct posix_clock_desc cd;
|
||||
int err;
|
||||
|
||||
err = get_clock_desc(id, &cd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
|
||||
err = -EACCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cd.clk->ops.clock_settime)
|
||||
err = cd.clk->ops.clock_settime(cd.clk, ts);
|
||||
else
|
||||
err = -EOPNOTSUPP;
|
||||
out:
|
||||
put_clock_desc(&cd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pc_timer_create(struct k_itimer *kit)
|
||||
{
|
||||
clockid_t id = kit->it_clock;
|
||||
struct posix_clock_desc cd;
|
||||
int err;
|
||||
|
||||
err = get_clock_desc(id, &cd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cd.clk->ops.timer_create)
|
||||
err = cd.clk->ops.timer_create(cd.clk, kit);
|
||||
else
|
||||
err = -EOPNOTSUPP;
|
||||
|
||||
put_clock_desc(&cd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pc_timer_delete(struct k_itimer *kit)
|
||||
{
|
||||
clockid_t id = kit->it_clock;
|
||||
struct posix_clock_desc cd;
|
||||
int err;
|
||||
|
||||
err = get_clock_desc(id, &cd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cd.clk->ops.timer_delete)
|
||||
err = cd.clk->ops.timer_delete(cd.clk, kit);
|
||||
else
|
||||
err = -EOPNOTSUPP;
|
||||
|
||||
put_clock_desc(&cd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void pc_timer_gettime(struct k_itimer *kit, struct itimerspec *ts)
|
||||
{
|
||||
clockid_t id = kit->it_clock;
|
||||
struct posix_clock_desc cd;
|
||||
|
||||
if (get_clock_desc(id, &cd))
|
||||
return;
|
||||
|
||||
if (cd.clk->ops.timer_gettime)
|
||||
cd.clk->ops.timer_gettime(cd.clk, kit, ts);
|
||||
|
||||
put_clock_desc(&cd);
|
||||
}
|
||||
|
||||
static int pc_timer_settime(struct k_itimer *kit, int flags,
|
||||
struct itimerspec *ts, struct itimerspec *old)
|
||||
{
|
||||
clockid_t id = kit->it_clock;
|
||||
struct posix_clock_desc cd;
|
||||
int err;
|
||||
|
||||
err = get_clock_desc(id, &cd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cd.clk->ops.timer_settime)
|
||||
err = cd.clk->ops.timer_settime(cd.clk, kit, flags, ts, old);
|
||||
else
|
||||
err = -EOPNOTSUPP;
|
||||
|
||||
put_clock_desc(&cd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
struct k_clock clock_posix_dynamic = {
|
||||
.clock_getres = pc_clock_getres,
|
||||
.clock_set = pc_clock_settime,
|
||||
.clock_get = pc_clock_gettime,
|
||||
.clock_adj = pc_clock_adjtime,
|
||||
.timer_create = pc_timer_create,
|
||||
.timer_set = pc_timer_settime,
|
||||
.timer_del = pc_timer_delete,
|
||||
.timer_get = pc_timer_gettime,
|
||||
};
|
@@ -18,7 +18,6 @@
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tick.h>
|
||||
|
||||
#include "tick-internal.h"
|
||||
|
||||
|
@@ -18,7 +18,6 @@
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tick.h>
|
||||
|
||||
#include <asm/irq_regs.h>
|
||||
|
||||
|
@@ -1,6 +1,10 @@
|
||||
/*
|
||||
* tick internal variable and functions used by low/high res code
|
||||
*/
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/tick.h>
|
||||
|
||||
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BUILD
|
||||
|
||||
#define TICK_DO_TIMER_NONE -1
|
||||
#define TICK_DO_TIMER_BOOT -2
|
||||
@@ -135,3 +139,8 @@ static inline int tick_device_is_functional(struct clock_event_device *dev)
|
||||
{
|
||||
return !(dev->features & CLOCK_EVT_FEAT_DUMMY);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern void do_timer(unsigned long ticks);
|
||||
extern seqlock_t xtime_lock;
|
||||
|
@@ -18,7 +18,6 @@
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tick.h>
|
||||
|
||||
#include "tick-internal.h"
|
||||
|
||||
|
@@ -19,7 +19,6 @@
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/irq_regs.h>
|
||||
|
@@ -353,7 +353,7 @@ EXPORT_SYMBOL(do_gettimeofday);
|
||||
*
|
||||
* Sets the time of day to the new time and update NTP and notify hrtimers
|
||||
*/
|
||||
int do_settimeofday(struct timespec *tv)
|
||||
int do_settimeofday(const struct timespec *tv)
|
||||
{
|
||||
struct timespec ts_delta;
|
||||
unsigned long flags;
|
||||
@@ -387,6 +387,42 @@ int do_settimeofday(struct timespec *tv)
|
||||
|
||||
EXPORT_SYMBOL(do_settimeofday);
|
||||
|
||||
|
||||
/**
|
||||
* timekeeping_inject_offset - Adds or subtracts from the current time.
|
||||
* @tv: pointer to the timespec variable containing the offset
|
||||
*
|
||||
* Adds or subtracts an offset value from the current time.
|
||||
*/
|
||||
int timekeeping_inject_offset(struct timespec *ts)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
|
||||
return -EINVAL;
|
||||
|
||||
write_seqlock_irqsave(&xtime_lock, flags);
|
||||
|
||||
timekeeping_forward_now();
|
||||
|
||||
xtime = timespec_add(xtime, *ts);
|
||||
wall_to_monotonic = timespec_sub(wall_to_monotonic, *ts);
|
||||
|
||||
timekeeper.ntp_error = 0;
|
||||
ntp_clear();
|
||||
|
||||
update_vsyscall(&xtime, &wall_to_monotonic, timekeeper.clock,
|
||||
timekeeper.mult);
|
||||
|
||||
write_sequnlock_irqrestore(&xtime_lock, flags);
|
||||
|
||||
/* signal hrtimers about time change */
|
||||
clock_was_set();
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(timekeeping_inject_offset);
|
||||
|
||||
/**
|
||||
* change_clocksource - Swaps clocksources if a new one is available
|
||||
*
|
||||
@@ -779,7 +815,7 @@ static cycle_t logarithmic_accumulation(cycle_t offset, int shift)
|
||||
*
|
||||
* Called from the timer interrupt, must hold a write on xtime_lock.
|
||||
*/
|
||||
void update_wall_time(void)
|
||||
static void update_wall_time(void)
|
||||
{
|
||||
struct clocksource *clock;
|
||||
cycle_t offset;
|
||||
@@ -871,7 +907,7 @@ void update_wall_time(void)
|
||||
* getboottime - Return the real time of system boot.
|
||||
* @ts: pointer to the timespec to be set
|
||||
*
|
||||
* Returns the time of day in a timespec.
|
||||
* Returns the wall-time of boot in a timespec.
|
||||
*
|
||||
* This is based on the wall_to_monotonic offset and the total suspend
|
||||
* time. Calls to settimeofday will affect the value returned (which
|
||||
@@ -889,6 +925,55 @@ void getboottime(struct timespec *ts)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(getboottime);
|
||||
|
||||
|
||||
/**
|
||||
* get_monotonic_boottime - Returns monotonic time since boot
|
||||
* @ts: pointer to the timespec to be set
|
||||
*
|
||||
* Returns the monotonic time since boot in a timespec.
|
||||
*
|
||||
* This is similar to CLOCK_MONTONIC/ktime_get_ts, but also
|
||||
* includes the time spent in suspend.
|
||||
*/
|
||||
void get_monotonic_boottime(struct timespec *ts)
|
||||
{
|
||||
struct timespec tomono, sleep;
|
||||
unsigned int seq;
|
||||
s64 nsecs;
|
||||
|
||||
WARN_ON(timekeeping_suspended);
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
*ts = xtime;
|
||||
tomono = wall_to_monotonic;
|
||||
sleep = total_sleep_time;
|
||||
nsecs = timekeeping_get_ns();
|
||||
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
|
||||
set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec,
|
||||
ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec + nsecs);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_monotonic_boottime);
|
||||
|
||||
/**
|
||||
* ktime_get_boottime - Returns monotonic time since boot in a ktime
|
||||
*
|
||||
* Returns the monotonic time since boot in a ktime
|
||||
*
|
||||
* This is similar to CLOCK_MONTONIC/ktime_get, but also
|
||||
* includes the time spent in suspend.
|
||||
*/
|
||||
ktime_t ktime_get_boottime(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
get_monotonic_boottime(&ts);
|
||||
return timespec_to_ktime(ts);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ktime_get_boottime);
|
||||
|
||||
/**
|
||||
* monotonic_to_bootbased - Convert the monotonic time to boot based.
|
||||
* @ts: pointer to the timespec to be converted
|
||||
@@ -910,11 +995,6 @@ struct timespec __current_kernel_time(void)
|
||||
return xtime;
|
||||
}
|
||||
|
||||
struct timespec __get_wall_to_monotonic(void)
|
||||
{
|
||||
return wall_to_monotonic;
|
||||
}
|
||||
|
||||
struct timespec current_kernel_time(void)
|
||||
{
|
||||
struct timespec now;
|
||||
@@ -946,3 +1026,48 @@ struct timespec get_monotonic_coarse(void)
|
||||
now.tv_nsec + mono.tv_nsec);
|
||||
return now;
|
||||
}
|
||||
|
||||
/*
|
||||
* The 64-bit jiffies value is not atomic - you MUST NOT read it
|
||||
* without sampling the sequence number in xtime_lock.
|
||||
* jiffies is defined in the linker script...
|
||||
*/
|
||||
void do_timer(unsigned long ticks)
|
||||
{
|
||||
jiffies_64 += ticks;
|
||||
update_wall_time();
|
||||
calc_global_load(ticks);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_xtime_and_monotonic_and_sleep_offset() - get xtime, wall_to_monotonic,
|
||||
* and sleep offsets.
|
||||
* @xtim: pointer to timespec to be set with xtime
|
||||
* @wtom: pointer to timespec to be set with wall_to_monotonic
|
||||
* @sleep: pointer to timespec to be set with time in suspend
|
||||
*/
|
||||
void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim,
|
||||
struct timespec *wtom, struct timespec *sleep)
|
||||
{
|
||||
unsigned long seq;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
*xtim = xtime;
|
||||
*wtom = wall_to_monotonic;
|
||||
*sleep = total_sleep_time;
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
}
|
||||
|
||||
/**
|
||||
* xtime_update() - advances the timekeeping infrastructure
|
||||
* @ticks: number of ticks, that have elapsed since the last call.
|
||||
*
|
||||
* Must be called with interrupts disabled.
|
||||
*/
|
||||
void xtime_update(unsigned long ticks)
|
||||
{
|
||||
write_seqlock(&xtime_lock);
|
||||
do_timer(ticks);
|
||||
write_sequnlock(&xtime_lock);
|
||||
}
|
||||
|
@@ -1324,19 +1324,6 @@ void run_local_timers(void)
|
||||
raise_softirq(TIMER_SOFTIRQ);
|
||||
}
|
||||
|
||||
/*
|
||||
* The 64-bit jiffies value is not atomic - you MUST NOT read it
|
||||
* without sampling the sequence number in xtime_lock.
|
||||
* jiffies is defined in the linker script...
|
||||
*/
|
||||
|
||||
void do_timer(unsigned long ticks)
|
||||
{
|
||||
jiffies_64 += ticks;
|
||||
update_wall_time();
|
||||
calc_global_load(ticks);
|
||||
}
|
||||
|
||||
#ifdef __ARCH_WANT_SYS_ALARM
|
||||
|
||||
/*
|
||||
|
在新工单中引用
屏蔽一个用户