mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-11 07:03:09 -04:00
Merge branch 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull core timer updates from Thomas Gleixner:
"Timers and timekeeping updates:
- A large overhaul of the posix CPU timer code which is a preparation
for moving the CPU timer expiry out into task work so it can be
properly accounted on the task/process.
An update to the bogus permission checks will come later during the
merge window as feedback was not complete before heading of for
travel.
- Switch the timerqueue code to use cached rbtrees and get rid of the
homebrewn caching of the leftmost node.
- Consolidate hrtimer_init() + hrtimer_init_sleeper() calls into a
single function
- Implement the separation of hrtimers to be forced to expire in hard
interrupt context even when PREEMPT_RT is enabled and mark the
affected timers accordingly.
- Implement a mechanism for hrtimers and the timer wheel to protect
RT against priority inversion and live lock issues when a (hr)timer
which should be canceled is currently executing the callback.
Instead of infinitely spinning, the task which tries to cancel the
timer blocks on a per cpu base expiry lock which is held and
released by the (hr)timer expiry code.
- Enable the Hyper-V TSC page based sched_clock for Hyper-V guests
resulting in faster access to timekeeping functions.
- Updates to various clocksource/clockevent drivers and their device
tree bindings.
- The usual small improvements all over the place"
* 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (101 commits)
posix-cpu-timers: Fix permission check regression
posix-cpu-timers: Always clear head pointer on dequeue
hrtimer: Add a missing bracket and hide `migration_base' on !SMP
posix-cpu-timers: Make expiry_active check actually work correctly
posix-timers: Unbreak CONFIG_POSIX_TIMERS=n build
tick: Mark sched_timer to expire in hard interrupt context
hrtimer: Add kernel doc annotation for HRTIMER_MODE_HARD
x86/hyperv: Hide pv_ops access for CONFIG_PARAVIRT=n
posix-cpu-timers: Utilize timerqueue for storage
posix-cpu-timers: Move state tracking to struct posix_cputimers
posix-cpu-timers: Deduplicate rlimit handling
posix-cpu-timers: Remove pointless comparisons
posix-cpu-timers: Get rid of 64bit divisions
posix-cpu-timers: Consolidate timer expiry further
posix-cpu-timers: Get rid of zero checks
rlimit: Rewrite non-sensical RLIMIT_CPU comment
posix-cpu-timers: Respect INFINITY for hard RTTIME limit
posix-cpu-timers: Switch thread group sampling to array
posix-cpu-timers: Restructure expiry array
posix-cpu-timers: Remove cputime_expires
...
This commit is contained in:
commit
7f2444d38f
61 changed files with 1476 additions and 895 deletions
|
|
@ -1103,7 +1103,7 @@ static void __perf_mux_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu)
|
|||
cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * interval);
|
||||
|
||||
raw_spin_lock_init(&cpuctx->hrtimer_lock);
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
timer->function = perf_mux_hrtimer_handler;
|
||||
}
|
||||
|
||||
|
|
@ -1121,7 +1121,7 @@ static int perf_mux_hrtimer_restart(struct perf_cpu_context *cpuctx)
|
|||
if (!cpuctx->hrtimer_active) {
|
||||
cpuctx->hrtimer_active = 1;
|
||||
hrtimer_forward_now(timer, cpuctx->hrtimer_interval);
|
||||
hrtimer_start_expires(timer, HRTIMER_MODE_ABS_PINNED);
|
||||
hrtimer_start_expires(timer, HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&cpuctx->hrtimer_lock, flags);
|
||||
|
||||
|
|
@ -9574,7 +9574,7 @@ static void perf_swevent_start_hrtimer(struct perf_event *event)
|
|||
period = max_t(u64, 10000, hwc->sample_period);
|
||||
}
|
||||
hrtimer_start(&hwc->hrtimer, ns_to_ktime(period),
|
||||
HRTIMER_MODE_REL_PINNED);
|
||||
HRTIMER_MODE_REL_PINNED_HARD);
|
||||
}
|
||||
|
||||
static void perf_swevent_cancel_hrtimer(struct perf_event *event)
|
||||
|
|
@ -9596,7 +9596,7 @@ static void perf_swevent_init_hrtimer(struct perf_event *event)
|
|||
if (!is_sampling_event(event))
|
||||
return;
|
||||
|
||||
hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
|
||||
hwc->hrtimer.function = perf_swevent_hrtimer;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1519,28 +1519,17 @@ void __cleanup_sighand(struct sighand_struct *sighand)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_POSIX_TIMERS
|
||||
/*
|
||||
* Initialize POSIX timer handling for a thread group.
|
||||
*/
|
||||
static void posix_cpu_timers_init_group(struct signal_struct *sig)
|
||||
{
|
||||
struct posix_cputimers *pct = &sig->posix_cputimers;
|
||||
unsigned long cpu_limit;
|
||||
|
||||
cpu_limit = READ_ONCE(sig->rlim[RLIMIT_CPU].rlim_cur);
|
||||
if (cpu_limit != RLIM_INFINITY) {
|
||||
sig->cputime_expires.prof_exp = cpu_limit * NSEC_PER_SEC;
|
||||
sig->cputimer.running = true;
|
||||
}
|
||||
|
||||
/* The timer lists. */
|
||||
INIT_LIST_HEAD(&sig->cpu_timers[0]);
|
||||
INIT_LIST_HEAD(&sig->cpu_timers[1]);
|
||||
INIT_LIST_HEAD(&sig->cpu_timers[2]);
|
||||
posix_cputimers_group_init(pct, cpu_limit);
|
||||
}
|
||||
#else
|
||||
static inline void posix_cpu_timers_init_group(struct signal_struct *sig) { }
|
||||
#endif
|
||||
|
||||
static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
|
||||
{
|
||||
|
|
@ -1642,23 +1631,6 @@ static void rt_mutex_init_task(struct task_struct *p)
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_POSIX_TIMERS
|
||||
/*
|
||||
* Initialize POSIX timer handling for a single task.
|
||||
*/
|
||||
static void posix_cpu_timers_init(struct task_struct *tsk)
|
||||
{
|
||||
tsk->cputime_expires.prof_exp = 0;
|
||||
tsk->cputime_expires.virt_exp = 0;
|
||||
tsk->cputime_expires.sched_exp = 0;
|
||||
INIT_LIST_HEAD(&tsk->cpu_timers[0]);
|
||||
INIT_LIST_HEAD(&tsk->cpu_timers[1]);
|
||||
INIT_LIST_HEAD(&tsk->cpu_timers[2]);
|
||||
}
|
||||
#else
|
||||
static inline void posix_cpu_timers_init(struct task_struct *tsk) { }
|
||||
#endif
|
||||
|
||||
static inline void init_task_pid_links(struct task_struct *task)
|
||||
{
|
||||
enum pid_type type;
|
||||
|
|
@ -1945,7 +1917,7 @@ static __latent_entropy struct task_struct *copy_process(
|
|||
task_io_accounting_init(&p->ioac);
|
||||
acct_clear_integrals(p);
|
||||
|
||||
posix_cpu_timers_init(p);
|
||||
posix_cputimers_init(&p->posix_cputimers);
|
||||
|
||||
p->io_context = NULL;
|
||||
audit_set_context(p, NULL);
|
||||
|
|
|
|||
|
|
@ -487,11 +487,9 @@ futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout,
|
|||
if (!time)
|
||||
return NULL;
|
||||
|
||||
hrtimer_init_on_stack(&timeout->timer, (flags & FLAGS_CLOCKRT) ?
|
||||
CLOCK_REALTIME : CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_ABS);
|
||||
hrtimer_init_sleeper(timeout, current);
|
||||
|
||||
hrtimer_init_sleeper_on_stack(timeout, (flags & FLAGS_CLOCKRT) ?
|
||||
CLOCK_REALTIME : CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_ABS);
|
||||
/*
|
||||
* If range_ns is 0, calling hrtimer_set_expires_range_ns() is
|
||||
* effectively the same as calling hrtimer_set_expires().
|
||||
|
|
@ -2613,7 +2611,7 @@ static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,
|
|||
|
||||
/* Arm the timer */
|
||||
if (timeout)
|
||||
hrtimer_start_expires(&timeout->timer, HRTIMER_MODE_ABS);
|
||||
hrtimer_sleeper_start_expires(timeout, HRTIMER_MODE_ABS);
|
||||
|
||||
/*
|
||||
* If we have been removed from the hash list, then another task
|
||||
|
|
@ -2899,7 +2897,7 @@ retry_private:
|
|||
}
|
||||
|
||||
if (unlikely(to))
|
||||
hrtimer_start_expires(&to->timer, HRTIMER_MODE_ABS);
|
||||
hrtimer_sleeper_start_expires(to, HRTIMER_MODE_ABS);
|
||||
|
||||
ret = rt_mutex_wait_proxy_lock(&q.pi_state->pi_mutex, to, &rt_waiter);
|
||||
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ static void __hrtick_restart(struct rq *rq)
|
|||
{
|
||||
struct hrtimer *timer = &rq->hrtick_timer;
|
||||
|
||||
hrtimer_start_expires(timer, HRTIMER_MODE_ABS_PINNED);
|
||||
hrtimer_start_expires(timer, HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -314,7 +314,7 @@ void hrtick_start(struct rq *rq, u64 delay)
|
|||
*/
|
||||
delay = max_t(u64, delay, 10000LL);
|
||||
hrtimer_start(&rq->hrtick_timer, ns_to_ktime(delay),
|
||||
HRTIMER_MODE_REL_PINNED);
|
||||
HRTIMER_MODE_REL_PINNED_HARD);
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
|
|
@ -328,7 +328,7 @@ static void hrtick_rq_init(struct rq *rq)
|
|||
rq->hrtick_csd.info = rq;
|
||||
#endif
|
||||
|
||||
hrtimer_init(&rq->hrtick_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(&rq->hrtick_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
|
||||
rq->hrtick_timer.function = hrtick;
|
||||
}
|
||||
#else /* CONFIG_SCHED_HRTICK */
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ static void task_non_contending(struct task_struct *p)
|
|||
|
||||
dl_se->dl_non_contending = 1;
|
||||
get_task_struct(p);
|
||||
hrtimer_start(timer, ns_to_ktime(zerolag_time), HRTIMER_MODE_REL);
|
||||
hrtimer_start(timer, ns_to_ktime(zerolag_time), HRTIMER_MODE_REL_HARD);
|
||||
}
|
||||
|
||||
static void task_contending(struct sched_dl_entity *dl_se, int flags)
|
||||
|
|
@ -956,7 +956,7 @@ static int start_dl_timer(struct task_struct *p)
|
|||
*/
|
||||
if (!hrtimer_is_queued(timer)) {
|
||||
get_task_struct(p);
|
||||
hrtimer_start(timer, act, HRTIMER_MODE_ABS);
|
||||
hrtimer_start(timer, act, HRTIMER_MODE_ABS_HARD);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
|
@ -1086,7 +1086,7 @@ void init_dl_task_timer(struct sched_dl_entity *dl_se)
|
|||
{
|
||||
struct hrtimer *timer = &dl_se->dl_timer;
|
||||
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
|
||||
timer->function = dl_task_timer;
|
||||
}
|
||||
|
||||
|
|
@ -1325,7 +1325,7 @@ void init_dl_inactive_task_timer(struct sched_dl_entity *dl_se)
|
|||
{
|
||||
struct hrtimer *timer = &dl_se->inactive_timer;
|
||||
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
|
||||
timer->function = inactive_task_timer;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime)
|
|||
|
||||
raw_spin_lock_init(&rt_b->rt_runtime_lock);
|
||||
|
||||
hrtimer_init(&rt_b->rt_period_timer,
|
||||
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(&rt_b->rt_period_timer, CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_REL_HARD);
|
||||
rt_b->rt_period_timer.function = sched_rt_period_timer;
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +67,8 @@ static void start_rt_bandwidth(struct rt_bandwidth *rt_b)
|
|||
* to update the period.
|
||||
*/
|
||||
hrtimer_forward_now(&rt_b->rt_period_timer, ns_to_ktime(0));
|
||||
hrtimer_start_expires(&rt_b->rt_period_timer, HRTIMER_MODE_ABS_PINNED);
|
||||
hrtimer_start_expires(&rt_b->rt_period_timer,
|
||||
HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
}
|
||||
raw_spin_unlock(&rt_b->rt_runtime_lock);
|
||||
}
|
||||
|
|
@ -2289,8 +2290,10 @@ static void watchdog(struct rq *rq, struct task_struct *p)
|
|||
}
|
||||
|
||||
next = DIV_ROUND_UP(min(soft, hard), USEC_PER_SEC/HZ);
|
||||
if (p->rt.timeout > next)
|
||||
p->cputime_expires.sched_exp = p->se.sum_exec_runtime;
|
||||
if (p->rt.timeout > next) {
|
||||
posix_cputimers_rt_watchdog(&p->posix_cputimers,
|
||||
p->se.sum_exec_runtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
|
|
|||
16
kernel/sys.c
16
kernel/sys.c
|
|
@ -1557,15 +1557,6 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource,
|
|||
retval = -EPERM;
|
||||
if (!retval)
|
||||
retval = security_task_setrlimit(tsk, resource, new_rlim);
|
||||
if (resource == RLIMIT_CPU && new_rlim->rlim_cur == 0) {
|
||||
/*
|
||||
* The caller is asking for an immediate RLIMIT_CPU
|
||||
* expiry. But we use the zero value to mean "it was
|
||||
* never set". So let's cheat and make it one second
|
||||
* instead
|
||||
*/
|
||||
new_rlim->rlim_cur = 1;
|
||||
}
|
||||
}
|
||||
if (!retval) {
|
||||
if (old_rlim)
|
||||
|
|
@ -1576,10 +1567,9 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource,
|
|||
task_unlock(tsk->group_leader);
|
||||
|
||||
/*
|
||||
* RLIMIT_CPU handling. Note that the kernel fails to return an error
|
||||
* code if it rejected the user's attempt to set RLIMIT_CPU. This is a
|
||||
* very long-standing error, and fixing it now risks breakage of
|
||||
* applications, so we live with it
|
||||
* RLIMIT_CPU handling. Arm the posix CPU timer if the limit is not
|
||||
* infite. In case of RLIM_INFINITY the posix CPU timer code
|
||||
* ignores the rlimit.
|
||||
*/
|
||||
if (!retval && new_rlim && resource == RLIMIT_CPU &&
|
||||
new_rlim->rlim_cur != RLIM_INFINITY &&
|
||||
|
|
|
|||
|
|
@ -432,7 +432,7 @@ int alarm_cancel(struct alarm *alarm)
|
|||
int ret = alarm_try_to_cancel(alarm);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
cpu_relax();
|
||||
hrtimer_cancel_wait_running(&alarm->timer);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(alarm_cancel);
|
||||
|
|
@ -605,6 +605,19 @@ static int alarm_timer_try_to_cancel(struct k_itimer *timr)
|
|||
return alarm_try_to_cancel(&timr->it.alarm.alarmtimer);
|
||||
}
|
||||
|
||||
/**
|
||||
* alarm_timer_wait_running - Posix timer callback to wait for a timer
|
||||
* @timr: Pointer to the posixtimer data struct
|
||||
*
|
||||
* Called from the core code when timer cancel detected that the callback
|
||||
* is running. @timr is unlocked and rcu read lock is held to prevent it
|
||||
* from being freed.
|
||||
*/
|
||||
static void alarm_timer_wait_running(struct k_itimer *timr)
|
||||
{
|
||||
hrtimer_cancel_wait_running(&timr->it.alarm.alarmtimer.timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* alarm_timer_arm - Posix timer callback to arm a timer
|
||||
* @timr: Pointer to the posixtimer data struct
|
||||
|
|
@ -834,6 +847,7 @@ const struct k_clock alarm_clock = {
|
|||
.timer_forward = alarm_timer_forward,
|
||||
.timer_remaining = alarm_timer_remaining,
|
||||
.timer_try_to_cancel = alarm_timer_try_to_cancel,
|
||||
.timer_wait_running = alarm_timer_wait_running,
|
||||
.nsleep = alarm_timer_nsleep,
|
||||
};
|
||||
#endif /* CONFIG_POSIX_TIMERS */
|
||||
|
|
|
|||
|
|
@ -140,6 +140,11 @@ static struct hrtimer_cpu_base migration_cpu_base = {
|
|||
|
||||
#define migration_base migration_cpu_base.clock_base[0]
|
||||
|
||||
static inline bool is_migration_base(struct hrtimer_clock_base *base)
|
||||
{
|
||||
return base == &migration_base;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are using hashed locking: holding per_cpu(hrtimer_bases)[n].lock
|
||||
* means that all timers which are tied to this base via timer->base are
|
||||
|
|
@ -264,6 +269,11 @@ again:
|
|||
|
||||
#else /* CONFIG_SMP */
|
||||
|
||||
static inline bool is_migration_base(struct hrtimer_clock_base *base)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct hrtimer_clock_base *
|
||||
lock_hrtimer_base(const struct hrtimer *timer, unsigned long *flags)
|
||||
{
|
||||
|
|
@ -427,6 +437,17 @@ void hrtimer_init_on_stack(struct hrtimer *timer, clockid_t clock_id,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_init_on_stack);
|
||||
|
||||
static void __hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
|
||||
clockid_t clock_id, enum hrtimer_mode mode);
|
||||
|
||||
void hrtimer_init_sleeper_on_stack(struct hrtimer_sleeper *sl,
|
||||
clockid_t clock_id, enum hrtimer_mode mode)
|
||||
{
|
||||
debug_object_init_on_stack(&sl->timer, &hrtimer_debug_descr);
|
||||
__hrtimer_init_sleeper(sl, clock_id, mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_init_sleeper_on_stack);
|
||||
|
||||
void destroy_hrtimer_on_stack(struct hrtimer *timer)
|
||||
{
|
||||
debug_object_free(timer, &hrtimer_debug_descr);
|
||||
|
|
@ -1096,9 +1117,13 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
|||
|
||||
/*
|
||||
* Check whether the HRTIMER_MODE_SOFT bit and hrtimer.is_soft
|
||||
* match.
|
||||
* match on CONFIG_PREEMPT_RT = n. With PREEMPT_RT check the hard
|
||||
* expiry mode because unmarked timers are moved to softirq expiry.
|
||||
*/
|
||||
WARN_ON_ONCE(!(mode & HRTIMER_MODE_SOFT) ^ !timer->is_soft);
|
||||
if (!IS_ENABLED(CONFIG_PREEMPT_RT))
|
||||
WARN_ON_ONCE(!(mode & HRTIMER_MODE_SOFT) ^ !timer->is_soft);
|
||||
else
|
||||
WARN_ON_ONCE(!(mode & HRTIMER_MODE_HARD) ^ !timer->is_hard);
|
||||
|
||||
base = lock_hrtimer_base(timer, &flags);
|
||||
|
||||
|
|
@ -1147,6 +1172,93 @@ int hrtimer_try_to_cancel(struct hrtimer *timer)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_try_to_cancel);
|
||||
|
||||
#ifdef CONFIG_PREEMPT_RT
|
||||
static void hrtimer_cpu_base_init_expiry_lock(struct hrtimer_cpu_base *base)
|
||||
{
|
||||
spin_lock_init(&base->softirq_expiry_lock);
|
||||
}
|
||||
|
||||
static void hrtimer_cpu_base_lock_expiry(struct hrtimer_cpu_base *base)
|
||||
{
|
||||
spin_lock(&base->softirq_expiry_lock);
|
||||
}
|
||||
|
||||
static void hrtimer_cpu_base_unlock_expiry(struct hrtimer_cpu_base *base)
|
||||
{
|
||||
spin_unlock(&base->softirq_expiry_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* The counterpart to hrtimer_cancel_wait_running().
|
||||
*
|
||||
* If there is a waiter for cpu_base->expiry_lock, then it was waiting for
|
||||
* the timer callback to finish. Drop expiry_lock and reaquire it. That
|
||||
* allows the waiter to acquire the lock and make progress.
|
||||
*/
|
||||
static void hrtimer_sync_wait_running(struct hrtimer_cpu_base *cpu_base,
|
||||
unsigned long flags)
|
||||
{
|
||||
if (atomic_read(&cpu_base->timer_waiters)) {
|
||||
raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
spin_unlock(&cpu_base->softirq_expiry_lock);
|
||||
spin_lock(&cpu_base->softirq_expiry_lock);
|
||||
raw_spin_lock_irq(&cpu_base->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called on PREEMPT_RT kernels when the fast path
|
||||
* deletion of a timer failed because the timer callback function was
|
||||
* running.
|
||||
*
|
||||
* This prevents priority inversion: if the soft irq thread is preempted
|
||||
* in the middle of a timer callback, then calling del_timer_sync() can
|
||||
* lead to two issues:
|
||||
*
|
||||
* - If the caller is on a remote CPU then it has to spin wait for the timer
|
||||
* handler to complete. This can result in unbound priority inversion.
|
||||
*
|
||||
* - If the caller originates from the task which preempted the timer
|
||||
* handler on the same CPU, then spin waiting for the timer handler to
|
||||
* complete is never going to end.
|
||||
*/
|
||||
void hrtimer_cancel_wait_running(const struct hrtimer *timer)
|
||||
{
|
||||
/* Lockless read. Prevent the compiler from reloading it below */
|
||||
struct hrtimer_clock_base *base = READ_ONCE(timer->base);
|
||||
|
||||
/*
|
||||
* Just relax if the timer expires in hard interrupt context or if
|
||||
* it is currently on the migration base.
|
||||
*/
|
||||
if (!timer->is_soft || is_migration_base(base)) {
|
||||
cpu_relax();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the base as contended and grab the expiry lock, which is
|
||||
* held by the softirq across the timer callback. Drop the lock
|
||||
* immediately so the softirq can expire the next timer. In theory
|
||||
* the timer could already be running again, but that's more than
|
||||
* unlikely and just causes another wait loop.
|
||||
*/
|
||||
atomic_inc(&base->cpu_base->timer_waiters);
|
||||
spin_lock_bh(&base->cpu_base->softirq_expiry_lock);
|
||||
atomic_dec(&base->cpu_base->timer_waiters);
|
||||
spin_unlock_bh(&base->cpu_base->softirq_expiry_lock);
|
||||
}
|
||||
#else
|
||||
static inline void
|
||||
hrtimer_cpu_base_init_expiry_lock(struct hrtimer_cpu_base *base) { }
|
||||
static inline void
|
||||
hrtimer_cpu_base_lock_expiry(struct hrtimer_cpu_base *base) { }
|
||||
static inline void
|
||||
hrtimer_cpu_base_unlock_expiry(struct hrtimer_cpu_base *base) { }
|
||||
static inline void hrtimer_sync_wait_running(struct hrtimer_cpu_base *base,
|
||||
unsigned long flags) { }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* hrtimer_cancel - cancel a timer and wait for the handler to finish.
|
||||
* @timer: the timer to be cancelled
|
||||
|
|
@ -1157,13 +1269,15 @@ EXPORT_SYMBOL_GPL(hrtimer_try_to_cancel);
|
|||
*/
|
||||
int hrtimer_cancel(struct hrtimer *timer)
|
||||
{
|
||||
for (;;) {
|
||||
int ret = hrtimer_try_to_cancel(timer);
|
||||
int ret;
|
||||
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
cpu_relax();
|
||||
}
|
||||
do {
|
||||
ret = hrtimer_try_to_cancel(timer);
|
||||
|
||||
if (ret < 0)
|
||||
hrtimer_cancel_wait_running(timer);
|
||||
} while (ret < 0);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_cancel);
|
||||
|
||||
|
|
@ -1260,8 +1374,17 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
|||
enum hrtimer_mode mode)
|
||||
{
|
||||
bool softtimer = !!(mode & HRTIMER_MODE_SOFT);
|
||||
int base = softtimer ? HRTIMER_MAX_CLOCK_BASES / 2 : 0;
|
||||
struct hrtimer_cpu_base *cpu_base;
|
||||
int base;
|
||||
|
||||
/*
|
||||
* On PREEMPT_RT enabled kernels hrtimers which are not explicitely
|
||||
* marked for hard interrupt expiry mode are moved into soft
|
||||
* interrupt context for latency reasons and because the callbacks
|
||||
* can invoke functions which might sleep on RT, e.g. spin_lock().
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PREEMPT_RT) && !(mode & HRTIMER_MODE_HARD))
|
||||
softtimer = true;
|
||||
|
||||
memset(timer, 0, sizeof(struct hrtimer));
|
||||
|
||||
|
|
@ -1275,8 +1398,10 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
|||
if (clock_id == CLOCK_REALTIME && mode & HRTIMER_MODE_REL)
|
||||
clock_id = CLOCK_MONOTONIC;
|
||||
|
||||
base = softtimer ? HRTIMER_MAX_CLOCK_BASES / 2 : 0;
|
||||
base += hrtimer_clockid_to_base(clock_id);
|
||||
timer->is_soft = softtimer;
|
||||
timer->is_hard = !softtimer;
|
||||
timer->base = &cpu_base->clock_base[base];
|
||||
timerqueue_init(&timer->node);
|
||||
}
|
||||
|
|
@ -1449,6 +1574,8 @@ static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now,
|
|||
break;
|
||||
|
||||
__run_hrtimer(cpu_base, base, timer, &basenow, flags);
|
||||
if (active_mask == HRTIMER_ACTIVE_SOFT)
|
||||
hrtimer_sync_wait_running(cpu_base, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1459,6 +1586,7 @@ static __latent_entropy void hrtimer_run_softirq(struct softirq_action *h)
|
|||
unsigned long flags;
|
||||
ktime_t now;
|
||||
|
||||
hrtimer_cpu_base_lock_expiry(cpu_base);
|
||||
raw_spin_lock_irqsave(&cpu_base->lock, flags);
|
||||
|
||||
now = hrtimer_update_base(cpu_base);
|
||||
|
|
@ -1468,6 +1596,7 @@ static __latent_entropy void hrtimer_run_softirq(struct softirq_action *h)
|
|||
hrtimer_update_softirq_timer(cpu_base, true);
|
||||
|
||||
raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
hrtimer_cpu_base_unlock_expiry(cpu_base);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
|
|
@ -1639,10 +1768,75 @@ static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer)
|
|||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task)
|
||||
/**
|
||||
* hrtimer_sleeper_start_expires - Start a hrtimer sleeper timer
|
||||
* @sl: sleeper to be started
|
||||
* @mode: timer mode abs/rel
|
||||
*
|
||||
* Wrapper around hrtimer_start_expires() for hrtimer_sleeper based timers
|
||||
* to allow PREEMPT_RT to tweak the delivery mode (soft/hardirq context)
|
||||
*/
|
||||
void hrtimer_sleeper_start_expires(struct hrtimer_sleeper *sl,
|
||||
enum hrtimer_mode mode)
|
||||
{
|
||||
/*
|
||||
* Make the enqueue delivery mode check work on RT. If the sleeper
|
||||
* was initialized for hard interrupt delivery, force the mode bit.
|
||||
* This is a special case for hrtimer_sleepers because
|
||||
* hrtimer_init_sleeper() determines the delivery mode on RT so the
|
||||
* fiddling with this decision is avoided at the call sites.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PREEMPT_RT) && sl->timer.is_hard)
|
||||
mode |= HRTIMER_MODE_HARD;
|
||||
|
||||
hrtimer_start_expires(&sl->timer, mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_sleeper_start_expires);
|
||||
|
||||
static void __hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
|
||||
clockid_t clock_id, enum hrtimer_mode mode)
|
||||
{
|
||||
/*
|
||||
* On PREEMPT_RT enabled kernels hrtimers which are not explicitely
|
||||
* marked for hard interrupt expiry mode are moved into soft
|
||||
* interrupt context either for latency reasons or because the
|
||||
* hrtimer callback takes regular spinlocks or invokes other
|
||||
* functions which are not suitable for hard interrupt context on
|
||||
* PREEMPT_RT.
|
||||
*
|
||||
* The hrtimer_sleeper callback is RT compatible in hard interrupt
|
||||
* context, but there is a latency concern: Untrusted userspace can
|
||||
* spawn many threads which arm timers for the same expiry time on
|
||||
* the same CPU. That causes a latency spike due to the wakeup of
|
||||
* a gazillion threads.
|
||||
*
|
||||
* OTOH, priviledged real-time user space applications rely on the
|
||||
* low latency of hard interrupt wakeups. If the current task is in
|
||||
* a real-time scheduling class, mark the mode for hard interrupt
|
||||
* expiry.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
|
||||
if (task_is_realtime(current) && !(mode & HRTIMER_MODE_SOFT))
|
||||
mode |= HRTIMER_MODE_HARD;
|
||||
}
|
||||
|
||||
__hrtimer_init(&sl->timer, clock_id, mode);
|
||||
sl->timer.function = hrtimer_wakeup;
|
||||
sl->task = task;
|
||||
sl->task = current;
|
||||
}
|
||||
|
||||
/**
|
||||
* hrtimer_init_sleeper - initialize sleeper to the given clock
|
||||
* @sl: sleeper to be initialized
|
||||
* @clock_id: the clock to be used
|
||||
* @mode: timer mode abs/rel
|
||||
*/
|
||||
void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, clockid_t clock_id,
|
||||
enum hrtimer_mode mode)
|
||||
{
|
||||
debug_init(&sl->timer, clock_id, mode);
|
||||
__hrtimer_init_sleeper(sl, clock_id, mode);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_init_sleeper);
|
||||
|
||||
|
|
@ -1669,11 +1863,9 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod
|
|||
{
|
||||
struct restart_block *restart;
|
||||
|
||||
hrtimer_init_sleeper(t, current);
|
||||
|
||||
do {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
hrtimer_start_expires(&t->timer, mode);
|
||||
hrtimer_sleeper_start_expires(t, mode);
|
||||
|
||||
if (likely(t->task))
|
||||
freezable_schedule();
|
||||
|
|
@ -1707,10 +1899,9 @@ static long __sched hrtimer_nanosleep_restart(struct restart_block *restart)
|
|||
struct hrtimer_sleeper t;
|
||||
int ret;
|
||||
|
||||
hrtimer_init_on_stack(&t.timer, restart->nanosleep.clockid,
|
||||
HRTIMER_MODE_ABS);
|
||||
hrtimer_init_sleeper_on_stack(&t, restart->nanosleep.clockid,
|
||||
HRTIMER_MODE_ABS);
|
||||
hrtimer_set_expires_tv64(&t.timer, restart->nanosleep.expires);
|
||||
|
||||
ret = do_nanosleep(&t, HRTIMER_MODE_ABS);
|
||||
destroy_hrtimer_on_stack(&t.timer);
|
||||
return ret;
|
||||
|
|
@ -1728,7 +1919,7 @@ long hrtimer_nanosleep(const struct timespec64 *rqtp,
|
|||
if (dl_task(current) || rt_task(current))
|
||||
slack = 0;
|
||||
|
||||
hrtimer_init_on_stack(&t.timer, clockid, mode);
|
||||
hrtimer_init_sleeper_on_stack(&t, clockid, mode);
|
||||
hrtimer_set_expires_range_ns(&t.timer, timespec64_to_ktime(*rqtp), slack);
|
||||
ret = do_nanosleep(&t, mode);
|
||||
if (ret != -ERESTART_RESTARTBLOCK)
|
||||
|
|
@ -1809,6 +2000,7 @@ int hrtimers_prepare_cpu(unsigned int cpu)
|
|||
cpu_base->softirq_next_timer = NULL;
|
||||
cpu_base->expires_next = KTIME_MAX;
|
||||
cpu_base->softirq_expires_next = KTIME_MAX;
|
||||
hrtimer_cpu_base_init_expiry_lock(cpu_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1927,12 +2119,9 @@ schedule_hrtimeout_range_clock(ktime_t *expires, u64 delta,
|
|||
return -EINTR;
|
||||
}
|
||||
|
||||
hrtimer_init_on_stack(&t.timer, clock_id, mode);
|
||||
hrtimer_init_sleeper_on_stack(&t, clock_id, mode);
|
||||
hrtimer_set_expires_range_ns(&t.timer, *expires, delta);
|
||||
|
||||
hrtimer_init_sleeper(&t, current);
|
||||
|
||||
hrtimer_start_expires(&t.timer, mode);
|
||||
hrtimer_sleeper_start_expires(&t, mode);
|
||||
|
||||
if (likely(t.task))
|
||||
schedule();
|
||||
|
|
|
|||
|
|
@ -55,15 +55,10 @@ static void get_cpu_itimer(struct task_struct *tsk, unsigned int clock_id,
|
|||
val = it->expires;
|
||||
interval = it->incr;
|
||||
if (val) {
|
||||
struct task_cputime cputime;
|
||||
u64 t;
|
||||
u64 t, samples[CPUCLOCK_MAX];
|
||||
|
||||
thread_group_cputimer(tsk, &cputime);
|
||||
if (clock_id == CPUCLOCK_PROF)
|
||||
t = cputime.utime + cputime.stime;
|
||||
else
|
||||
/* CPUCLOCK_VIRT */
|
||||
t = cputime.utime;
|
||||
thread_group_sample_cputime(tsk, samples);
|
||||
t = samples[clock_id];
|
||||
|
||||
if (val < t)
|
||||
/* about to fire */
|
||||
|
|
@ -213,6 +208,7 @@ again:
|
|||
/* We are sharing ->siglock with it_real_fn() */
|
||||
if (hrtimer_try_to_cancel(timer) < 0) {
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
hrtimer_cancel_wait_running(timer);
|
||||
goto again;
|
||||
}
|
||||
expires = timeval_to_ktime(value->it_value);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -442,7 +442,7 @@ static struct k_itimer * alloc_posix_timer(void)
|
|||
|
||||
static void k_itimer_rcu_free(struct rcu_head *head)
|
||||
{
|
||||
struct k_itimer *tmr = container_of(head, struct k_itimer, it.rcu);
|
||||
struct k_itimer *tmr = container_of(head, struct k_itimer, rcu);
|
||||
|
||||
kmem_cache_free(posix_timers_cache, tmr);
|
||||
}
|
||||
|
|
@ -459,7 +459,7 @@ static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
|
|||
}
|
||||
put_pid(tmr->it_pid);
|
||||
sigqueue_free(tmr->sigq);
|
||||
call_rcu(&tmr->it.rcu, k_itimer_rcu_free);
|
||||
call_rcu(&tmr->rcu, k_itimer_rcu_free);
|
||||
}
|
||||
|
||||
static int common_timer_create(struct k_itimer *new_timer)
|
||||
|
|
@ -805,6 +805,35 @@ static int common_hrtimer_try_to_cancel(struct k_itimer *timr)
|
|||
return hrtimer_try_to_cancel(&timr->it.real.timer);
|
||||
}
|
||||
|
||||
static void common_timer_wait_running(struct k_itimer *timer)
|
||||
{
|
||||
hrtimer_cancel_wait_running(&timer->it.real.timer);
|
||||
}
|
||||
|
||||
/*
|
||||
* On PREEMPT_RT this prevent priority inversion against softirq kthread in
|
||||
* case it gets preempted while executing a timer callback. See comments in
|
||||
* hrtimer_cancel_wait_running. For PREEMPT_RT=n this just results in a
|
||||
* cpu_relax().
|
||||
*/
|
||||
static struct k_itimer *timer_wait_running(struct k_itimer *timer,
|
||||
unsigned long *flags)
|
||||
{
|
||||
const struct k_clock *kc = READ_ONCE(timer->kclock);
|
||||
timer_t timer_id = READ_ONCE(timer->it_id);
|
||||
|
||||
/* Prevent kfree(timer) after dropping the lock */
|
||||
rcu_read_lock();
|
||||
unlock_timer(timer, *flags);
|
||||
|
||||
if (!WARN_ON_ONCE(!kc->timer_wait_running))
|
||||
kc->timer_wait_running(timer);
|
||||
|
||||
rcu_read_unlock();
|
||||
/* Relock the timer. It might be not longer hashed. */
|
||||
return lock_timer(timer_id, flags);
|
||||
}
|
||||
|
||||
/* Set a POSIX.1b interval timer. */
|
||||
int common_timer_set(struct k_itimer *timr, int flags,
|
||||
struct itimerspec64 *new_setting,
|
||||
|
|
@ -844,13 +873,13 @@ int common_timer_set(struct k_itimer *timr, int flags,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int do_timer_settime(timer_t timer_id, int flags,
|
||||
static int do_timer_settime(timer_t timer_id, int tmr_flags,
|
||||
struct itimerspec64 *new_spec64,
|
||||
struct itimerspec64 *old_spec64)
|
||||
{
|
||||
const struct k_clock *kc;
|
||||
struct k_itimer *timr;
|
||||
unsigned long flag;
|
||||
unsigned long flags;
|
||||
int error = 0;
|
||||
|
||||
if (!timespec64_valid(&new_spec64->it_interval) ||
|
||||
|
|
@ -859,8 +888,9 @@ static int do_timer_settime(timer_t timer_id, int flags,
|
|||
|
||||
if (old_spec64)
|
||||
memset(old_spec64, 0, sizeof(*old_spec64));
|
||||
|
||||
timr = lock_timer(timer_id, &flags);
|
||||
retry:
|
||||
timr = lock_timer(timer_id, &flag);
|
||||
if (!timr)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -868,13 +898,16 @@ retry:
|
|||
if (WARN_ON_ONCE(!kc || !kc->timer_set))
|
||||
error = -EINVAL;
|
||||
else
|
||||
error = kc->timer_set(timr, flags, new_spec64, old_spec64);
|
||||
error = kc->timer_set(timr, tmr_flags, new_spec64, old_spec64);
|
||||
|
||||
unlock_timer(timr, flag);
|
||||
if (error == TIMER_RETRY) {
|
||||
old_spec64 = NULL; // We already got the old time...
|
||||
// We already got the old time...
|
||||
old_spec64 = NULL;
|
||||
/* Unlocks and relocks the timer if it still exists */
|
||||
timr = timer_wait_running(timr, &flags);
|
||||
goto retry;
|
||||
}
|
||||
unlock_timer(timr, flags);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
|
@ -951,13 +984,15 @@ SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)
|
|||
struct k_itimer *timer;
|
||||
unsigned long flags;
|
||||
|
||||
retry_delete:
|
||||
timer = lock_timer(timer_id, &flags);
|
||||
|
||||
retry_delete:
|
||||
if (!timer)
|
||||
return -EINVAL;
|
||||
|
||||
if (timer_delete_hook(timer) == TIMER_RETRY) {
|
||||
unlock_timer(timer, flags);
|
||||
if (unlikely(timer_delete_hook(timer) == TIMER_RETRY)) {
|
||||
/* Unlocks and relocks the timer if it still exists */
|
||||
timer = timer_wait_running(timer, &flags);
|
||||
goto retry_delete;
|
||||
}
|
||||
|
||||
|
|
@ -1238,6 +1273,7 @@ static const struct k_clock clock_realtime = {
|
|||
.timer_forward = common_hrtimer_forward,
|
||||
.timer_remaining = common_hrtimer_remaining,
|
||||
.timer_try_to_cancel = common_hrtimer_try_to_cancel,
|
||||
.timer_wait_running = common_timer_wait_running,
|
||||
.timer_arm = common_hrtimer_arm,
|
||||
};
|
||||
|
||||
|
|
@ -1253,6 +1289,7 @@ static const struct k_clock clock_monotonic = {
|
|||
.timer_forward = common_hrtimer_forward,
|
||||
.timer_remaining = common_hrtimer_remaining,
|
||||
.timer_try_to_cancel = common_hrtimer_try_to_cancel,
|
||||
.timer_wait_running = common_timer_wait_running,
|
||||
.timer_arm = common_hrtimer_arm,
|
||||
};
|
||||
|
||||
|
|
@ -1283,6 +1320,7 @@ static const struct k_clock clock_tai = {
|
|||
.timer_forward = common_hrtimer_forward,
|
||||
.timer_remaining = common_hrtimer_remaining,
|
||||
.timer_try_to_cancel = common_hrtimer_try_to_cancel,
|
||||
.timer_wait_running = common_timer_wait_running,
|
||||
.timer_arm = common_hrtimer_arm,
|
||||
};
|
||||
|
||||
|
|
@ -1298,6 +1336,7 @@ static const struct k_clock clock_boottime = {
|
|||
.timer_forward = common_hrtimer_forward,
|
||||
.timer_remaining = common_hrtimer_remaining,
|
||||
.timer_try_to_cancel = common_hrtimer_try_to_cancel,
|
||||
.timer_wait_running = common_timer_wait_running,
|
||||
.timer_arm = common_hrtimer_arm,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ struct k_clock {
|
|||
int (*timer_try_to_cancel)(struct k_itimer *timr);
|
||||
void (*timer_arm)(struct k_itimer *timr, ktime_t expires,
|
||||
bool absolute, bool sigev_none);
|
||||
void (*timer_wait_running)(struct k_itimer *timr);
|
||||
};
|
||||
|
||||
extern const struct k_clock clock_posix_cpu;
|
||||
|
|
|
|||
|
|
@ -59,11 +59,16 @@ static int bc_set_next(ktime_t expires, struct clock_event_device *bc)
|
|||
* hrtimer_{start/cancel} functions call into tracing,
|
||||
* calls to these functions must be bound within RCU_NONIDLE.
|
||||
*/
|
||||
RCU_NONIDLE({
|
||||
RCU_NONIDLE(
|
||||
{
|
||||
bc_moved = hrtimer_try_to_cancel(&bctimer) >= 0;
|
||||
if (bc_moved)
|
||||
if (bc_moved) {
|
||||
hrtimer_start(&bctimer, expires,
|
||||
HRTIMER_MODE_ABS_PINNED);});
|
||||
HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (bc_moved) {
|
||||
/* Bind the "device" to the cpu */
|
||||
bc->bound_on = smp_processor_id();
|
||||
|
|
@ -104,7 +109,7 @@ static enum hrtimer_restart bc_handler(struct hrtimer *t)
|
|||
|
||||
void tick_setup_hrtimer_broadcast(void)
|
||||
{
|
||||
hrtimer_init(&bctimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
hrtimer_init(&bctimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
|
||||
bctimer.function = bc_handler;
|
||||
clockevents_register_device(&ce_broadcast_hrtimer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -634,10 +634,12 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
|
|||
/* Forward the time to expire in the future */
|
||||
hrtimer_forward(&ts->sched_timer, now, tick_period);
|
||||
|
||||
if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
|
||||
hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED);
|
||||
else
|
||||
if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
|
||||
hrtimer_start_expires(&ts->sched_timer,
|
||||
HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
} else {
|
||||
tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset to make sure next tick stop doesn't get fooled by past
|
||||
|
|
@ -802,7 +804,8 @@ static void tick_nohz_stop_tick(struct tick_sched *ts, int cpu)
|
|||
}
|
||||
|
||||
if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
|
||||
hrtimer_start(&ts->sched_timer, tick, HRTIMER_MODE_ABS_PINNED);
|
||||
hrtimer_start(&ts->sched_timer, tick,
|
||||
HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
} else {
|
||||
hrtimer_set_expires(&ts->sched_timer, tick);
|
||||
tick_program_event(tick, 1);
|
||||
|
|
@ -1230,7 +1233,7 @@ static void tick_nohz_switch_to_nohz(void)
|
|||
* Recycle the hrtimer in ts, so we can share the
|
||||
* hrtimer_forward with the highres code.
|
||||
*/
|
||||
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
|
||||
/* Get the next period */
|
||||
next = tick_init_jiffy_update();
|
||||
|
||||
|
|
@ -1327,7 +1330,7 @@ void tick_setup_sched_timer(void)
|
|||
/*
|
||||
* Emulate tick processing via per-CPU hrtimers:
|
||||
*/
|
||||
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
|
||||
ts->sched_timer.function = tick_sched_timer;
|
||||
|
||||
/* Get the next period (per-CPU) */
|
||||
|
|
@ -1342,7 +1345,7 @@ void tick_setup_sched_timer(void)
|
|||
}
|
||||
|
||||
hrtimer_forward(&ts->sched_timer, now, tick_period);
|
||||
hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED);
|
||||
hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
tick_nohz_activate(ts, NOHZ_MODE_HIGHRES);
|
||||
}
|
||||
#endif /* HIGH_RES_TIMERS */
|
||||
|
|
|
|||
|
|
@ -196,6 +196,10 @@ EXPORT_SYMBOL(jiffies_64);
|
|||
struct timer_base {
|
||||
raw_spinlock_t lock;
|
||||
struct timer_list *running_timer;
|
||||
#ifdef CONFIG_PREEMPT_RT
|
||||
spinlock_t expiry_lock;
|
||||
atomic_t timer_waiters;
|
||||
#endif
|
||||
unsigned long clk;
|
||||
unsigned long next_expiry;
|
||||
unsigned int cpu;
|
||||
|
|
@ -1227,7 +1231,78 @@ int try_to_del_timer_sync(struct timer_list *timer)
|
|||
}
|
||||
EXPORT_SYMBOL(try_to_del_timer_sync);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
#ifdef CONFIG_PREEMPT_RT
|
||||
static __init void timer_base_init_expiry_lock(struct timer_base *base)
|
||||
{
|
||||
spin_lock_init(&base->expiry_lock);
|
||||
}
|
||||
|
||||
static inline void timer_base_lock_expiry(struct timer_base *base)
|
||||
{
|
||||
spin_lock(&base->expiry_lock);
|
||||
}
|
||||
|
||||
static inline void timer_base_unlock_expiry(struct timer_base *base)
|
||||
{
|
||||
spin_unlock(&base->expiry_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* The counterpart to del_timer_wait_running().
|
||||
*
|
||||
* If there is a waiter for base->expiry_lock, then it was waiting for the
|
||||
* timer callback to finish. Drop expiry_lock and reaquire it. That allows
|
||||
* the waiter to acquire the lock and make progress.
|
||||
*/
|
||||
static void timer_sync_wait_running(struct timer_base *base)
|
||||
{
|
||||
if (atomic_read(&base->timer_waiters)) {
|
||||
spin_unlock(&base->expiry_lock);
|
||||
spin_lock(&base->expiry_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called on PREEMPT_RT kernels when the fast path
|
||||
* deletion of a timer failed because the timer callback function was
|
||||
* running.
|
||||
*
|
||||
* This prevents priority inversion, if the softirq thread on a remote CPU
|
||||
* got preempted, and it prevents a life lock when the task which tries to
|
||||
* delete a timer preempted the softirq thread running the timer callback
|
||||
* function.
|
||||
*/
|
||||
static void del_timer_wait_running(struct timer_list *timer)
|
||||
{
|
||||
u32 tf;
|
||||
|
||||
tf = READ_ONCE(timer->flags);
|
||||
if (!(tf & TIMER_MIGRATING)) {
|
||||
struct timer_base *base = get_timer_base(tf);
|
||||
|
||||
/*
|
||||
* Mark the base as contended and grab the expiry lock,
|
||||
* which is held by the softirq across the timer
|
||||
* callback. Drop the lock immediately so the softirq can
|
||||
* expire the next timer. In theory the timer could already
|
||||
* be running again, but that's more than unlikely and just
|
||||
* causes another wait loop.
|
||||
*/
|
||||
atomic_inc(&base->timer_waiters);
|
||||
spin_lock_bh(&base->expiry_lock);
|
||||
atomic_dec(&base->timer_waiters);
|
||||
spin_unlock_bh(&base->expiry_lock);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void timer_base_init_expiry_lock(struct timer_base *base) { }
|
||||
static inline void timer_base_lock_expiry(struct timer_base *base) { }
|
||||
static inline void timer_base_unlock_expiry(struct timer_base *base) { }
|
||||
static inline void timer_sync_wait_running(struct timer_base *base) { }
|
||||
static inline void del_timer_wait_running(struct timer_list *timer) { }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT)
|
||||
/**
|
||||
* del_timer_sync - deactivate a timer and wait for the handler to finish.
|
||||
* @timer: the timer to be deactivated
|
||||
|
|
@ -1266,6 +1341,8 @@ EXPORT_SYMBOL(try_to_del_timer_sync);
|
|||
*/
|
||||
int del_timer_sync(struct timer_list *timer)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -1283,12 +1360,17 @@ int del_timer_sync(struct timer_list *timer)
|
|||
* could lead to deadlock.
|
||||
*/
|
||||
WARN_ON(in_irq() && !(timer->flags & TIMER_IRQSAFE));
|
||||
for (;;) {
|
||||
int ret = try_to_del_timer_sync(timer);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
do {
|
||||
ret = try_to_del_timer_sync(timer);
|
||||
|
||||
if (unlikely(ret < 0)) {
|
||||
del_timer_wait_running(timer);
|
||||
cpu_relax();
|
||||
}
|
||||
} while (ret < 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(del_timer_sync);
|
||||
#endif
|
||||
|
|
@ -1360,10 +1442,13 @@ static void expire_timers(struct timer_base *base, struct hlist_head *head)
|
|||
if (timer->flags & TIMER_IRQSAFE) {
|
||||
raw_spin_unlock(&base->lock);
|
||||
call_timer_fn(timer, fn, baseclk);
|
||||
base->running_timer = NULL;
|
||||
raw_spin_lock(&base->lock);
|
||||
} else {
|
||||
raw_spin_unlock_irq(&base->lock);
|
||||
call_timer_fn(timer, fn, baseclk);
|
||||
base->running_timer = NULL;
|
||||
timer_sync_wait_running(base);
|
||||
raw_spin_lock_irq(&base->lock);
|
||||
}
|
||||
}
|
||||
|
|
@ -1643,7 +1728,7 @@ void update_process_times(int user_tick)
|
|||
#endif
|
||||
scheduler_tick();
|
||||
if (IS_ENABLED(CONFIG_POSIX_TIMERS))
|
||||
run_posix_cpu_timers(p);
|
||||
run_posix_cpu_timers();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1658,6 +1743,7 @@ static inline void __run_timers(struct timer_base *base)
|
|||
if (!time_after_eq(jiffies, base->clk))
|
||||
return;
|
||||
|
||||
timer_base_lock_expiry(base);
|
||||
raw_spin_lock_irq(&base->lock);
|
||||
|
||||
/*
|
||||
|
|
@ -1684,8 +1770,8 @@ static inline void __run_timers(struct timer_base *base)
|
|||
while (levels--)
|
||||
expire_timers(base, heads + levels);
|
||||
}
|
||||
base->running_timer = NULL;
|
||||
raw_spin_unlock_irq(&base->lock);
|
||||
timer_base_unlock_expiry(base);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1930,6 +2016,7 @@ static void __init init_timer_cpu(int cpu)
|
|||
base->cpu = cpu;
|
||||
raw_spin_lock_init(&base->lock);
|
||||
base->clk = jiffies;
|
||||
timer_base_init_expiry_lock(base);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -490,10 +490,10 @@ static void watchdog_enable(unsigned int cpu)
|
|||
* Start the timer first to prevent the NMI watchdog triggering
|
||||
* before the timer has a chance to fire.
|
||||
*/
|
||||
hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
|
||||
hrtimer->function = watchdog_timer_fn;
|
||||
hrtimer_start(hrtimer, ns_to_ktime(sample_period),
|
||||
HRTIMER_MODE_REL_PINNED);
|
||||
HRTIMER_MODE_REL_PINNED_HARD);
|
||||
|
||||
/* Initialize timestamp */
|
||||
__touch_watchdog();
|
||||
|
|
|
|||
Loading…
Reference in a new issue