summaryrefslogtreecommitdiffstats
path: root/debian/patches-rt/posix-timers-expiry-lock.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches-rt/posix-timers-expiry-lock.patch')
-rw-r--r--debian/patches-rt/posix-timers-expiry-lock.patch270
1 files changed, 270 insertions, 0 deletions
diff --git a/debian/patches-rt/posix-timers-expiry-lock.patch b/debian/patches-rt/posix-timers-expiry-lock.patch
new file mode 100644
index 000000000..9a37dc334
--- /dev/null
+++ b/debian/patches-rt/posix-timers-expiry-lock.patch
@@ -0,0 +1,270 @@
+From: Anna-Maria Gleixner <anna-maria@linutronix.de>
+Date: Mon, 27 May 2019 16:54:06 +0200
+Subject: [PATCH] posix-timers: Add expiry lock
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/5.2/older/patches-5.2-rt1.tar.xz
+
+If a about to be removed posix timer is active then the code will retry the
+delete operation until it succeeds / the timer callback completes.
+
+Use hrtimer_grab_expiry_lock() for posix timers which use a hrtimer underneath
+to spin on a lock until the callback finished.
+
+Introduce cpu_timers_grab_expiry_lock() for the posix-cpu-timer. This will
+acquire the proper per-CPU spin_lock which is acquired by the CPU which is
+expirering the timer.
+
+Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+---
+ fs/timerfd.c | 6 +++++-
+ include/linux/hrtimer.h | 1 +
+ include/linux/posix-timers.h | 1 +
+ kernel/time/alarmtimer.c | 2 +-
+ kernel/time/hrtimer.c | 2 +-
+ kernel/time/itimer.c | 1 +
+ kernel/time/posix-cpu-timers.c | 23 +++++++++++++++++++++++
+ kernel/time/posix-timers.c | 38 +++++++++++++++++++++++++++++---------
+ kernel/time/posix-timers.h | 2 ++
+ 9 files changed, 64 insertions(+), 12 deletions(-)
+
+--- a/fs/timerfd.c
++++ b/fs/timerfd.c
+@@ -471,7 +471,11 @@ static int do_timerfd_settime(int ufd, i
+ break;
+ }
+ spin_unlock_irq(&ctx->wqh.lock);
+- cpu_relax();
++
++ if (isalarm(ctx))
++ hrtimer_grab_expiry_lock(&ctx->t.alarm.timer);
++ else
++ hrtimer_grab_expiry_lock(&ctx->t.tmr);
+ }
+
+ /*
+--- a/include/linux/hrtimer.h
++++ b/include/linux/hrtimer.h
+@@ -400,6 +400,7 @@ static inline void hrtimer_start(struct
+
+ extern int hrtimer_cancel(struct hrtimer *timer);
+ extern int hrtimer_try_to_cancel(struct hrtimer *timer);
++extern void hrtimer_grab_expiry_lock(const struct hrtimer *timer);
+
+ static inline void hrtimer_start_expires(struct hrtimer *timer,
+ enum hrtimer_mode mode)
+--- a/include/linux/posix-timers.h
++++ b/include/linux/posix-timers.h
+@@ -15,6 +15,7 @@ struct cpu_timer_list {
+ u64 expires;
+ struct task_struct *task;
+ int firing;
++ int firing_cpu;
+ };
+
+ /*
+--- a/kernel/time/alarmtimer.c
++++ b/kernel/time/alarmtimer.c
+@@ -433,7 +433,7 @@ int alarm_cancel(struct alarm *alarm)
+ int ret = alarm_try_to_cancel(alarm);
+ if (ret >= 0)
+ return ret;
+- cpu_relax();
++ hrtimer_grab_expiry_lock(&alarm->timer);
+ }
+ }
+ EXPORT_SYMBOL_GPL(alarm_cancel);
+--- a/kernel/time/hrtimer.c
++++ b/kernel/time/hrtimer.c
+@@ -930,7 +930,7 @@ u64 hrtimer_forward(struct hrtimer *time
+ }
+ EXPORT_SYMBOL_GPL(hrtimer_forward);
+
+-static void hrtimer_grab_expiry_lock(const struct hrtimer *timer)
++void hrtimer_grab_expiry_lock(const struct hrtimer *timer)
+ {
+ struct hrtimer_clock_base *base = timer->base;
+
+--- a/kernel/time/itimer.c
++++ b/kernel/time/itimer.c
+@@ -213,6 +213,7 @@ int do_setitimer(int which, struct itime
+ /* We are sharing ->siglock with it_real_fn() */
+ if (hrtimer_try_to_cancel(timer) < 0) {
+ spin_unlock_irq(&tsk->sighand->siglock);
++ hrtimer_grab_expiry_lock(timer);
+ goto again;
+ }
+ expires = timeval_to_ktime(value->it_value);
+--- a/kernel/time/posix-cpu-timers.c
++++ b/kernel/time/posix-cpu-timers.c
+@@ -785,6 +785,7 @@ check_timers_list(struct list_head *time
+ return t->expires;
+
+ t->firing = 1;
++ t->firing_cpu = smp_processor_id();
+ list_move_tail(&t->entry, firing);
+ }
+
+@@ -1127,6 +1128,20 @@ static inline int fastpath_timer_check(s
+ return 0;
+ }
+
++static DEFINE_PER_CPU(spinlock_t, cpu_timer_expiry_lock) = __SPIN_LOCK_UNLOCKED(cpu_timer_expiry_lock);
++
++void cpu_timers_grab_expiry_lock(struct k_itimer *timer)
++{
++ int cpu = timer->it.cpu.firing_cpu;
++
++ if (cpu >= 0) {
++ spinlock_t *expiry_lock = per_cpu_ptr(&cpu_timer_expiry_lock, cpu);
++
++ spin_lock_irq(expiry_lock);
++ spin_unlock_irq(expiry_lock);
++ }
++}
++
+ /*
+ * This is called from the timer interrupt handler. The irq handler has
+ * already updated our counts. We need to check if any timers fire now.
+@@ -1137,6 +1152,7 @@ void run_posix_cpu_timers(struct task_st
+ LIST_HEAD(firing);
+ struct k_itimer *timer, *next;
+ unsigned long flags;
++ spinlock_t *expiry_lock;
+
+ lockdep_assert_irqs_disabled();
+
+@@ -1147,6 +1163,9 @@ void run_posix_cpu_timers(struct task_st
+ if (!fastpath_timer_check(tsk))
+ return;
+
++ expiry_lock = this_cpu_ptr(&cpu_timer_expiry_lock);
++ spin_lock(expiry_lock);
++
+ if (!lock_task_sighand(tsk, &flags))
+ return;
+ /*
+@@ -1181,6 +1200,7 @@ void run_posix_cpu_timers(struct task_st
+ list_del_init(&timer->it.cpu.entry);
+ cpu_firing = timer->it.cpu.firing;
+ timer->it.cpu.firing = 0;
++ timer->it.cpu.firing_cpu = -1;
+ /*
+ * The firing flag is -1 if we collided with a reset
+ * of the timer, which already reported this
+@@ -1190,6 +1210,7 @@ void run_posix_cpu_timers(struct task_st
+ cpu_timer_fire(timer);
+ spin_unlock(&timer->it_lock);
+ }
++ spin_unlock(expiry_lock);
+ }
+
+ /*
+@@ -1308,6 +1329,8 @@ static int do_cpu_nanosleep(const clocki
+ spin_unlock_irq(&timer.it_lock);
+
+ while (error == TIMER_RETRY) {
++
++ cpu_timers_grab_expiry_lock(&timer);
+ /*
+ * We need to handle case when timer was or is in the
+ * middle of firing. In other cases we already freed
+--- a/kernel/time/posix-timers.c
++++ b/kernel/time/posix-timers.c
+@@ -805,6 +805,17 @@ static int common_hrtimer_try_to_cancel(
+ return hrtimer_try_to_cancel(&timr->it.real.timer);
+ }
+
++static void timer_wait_for_callback(const struct k_clock *kc, struct k_itimer *timer)
++{
++ if (kc->timer_arm == common_hrtimer_arm)
++ hrtimer_grab_expiry_lock(&timer->it.real.timer);
++ else if (kc == &alarm_clock)
++ hrtimer_grab_expiry_lock(&timer->it.alarm.alarmtimer.timer);
++ else
++ /* posix-cpu-timers */
++ cpu_timers_grab_expiry_lock(timer);
++}
++
+ /* Set a POSIX.1b interval timer. */
+ int common_timer_set(struct k_itimer *timr, int flags,
+ struct itimerspec64 *new_setting,
+@@ -870,11 +881,15 @@ static int do_timer_settime(timer_t time
+ else
+ error = kc->timer_set(timr, flags, new_spec64, old_spec64);
+
+- unlock_timer(timr, flag);
+ if (error == TIMER_RETRY) {
++ rcu_read_lock();
++ unlock_timer(timr, flag);
++ timer_wait_for_callback(kc, timr);
++ rcu_read_unlock();
+ old_spec64 = NULL; // We already got the old time...
+ goto retry;
+ }
++ unlock_timer(timr, flag);
+
+ return error;
+ }
+@@ -936,13 +951,21 @@ int common_timer_del(struct k_itimer *ti
+ return 0;
+ }
+
+-static inline int timer_delete_hook(struct k_itimer *timer)
++static int timer_delete_hook(struct k_itimer *timer)
+ {
+ const struct k_clock *kc = timer->kclock;
++ int ret;
+
+ if (WARN_ON_ONCE(!kc || !kc->timer_del))
+ return -EINVAL;
+- return kc->timer_del(timer);
++ ret = kc->timer_del(timer);
++ if (ret == TIMER_RETRY) {
++ rcu_read_lock();
++ spin_unlock_irq(&timer->it_lock);
++ timer_wait_for_callback(kc, timer);
++ rcu_read_unlock();
++ }
++ return ret;
+ }
+
+ /* Delete a POSIX.1b interval timer. */
+@@ -956,10 +979,8 @@ SYSCALL_DEFINE1(timer_delete, timer_t, t
+ if (!timer)
+ return -EINVAL;
+
+- if (timer_delete_hook(timer) == TIMER_RETRY) {
+- unlock_timer(timer, flags);
++ if (timer_delete_hook(timer) == TIMER_RETRY)
+ goto retry_delete;
+- }
+
+ spin_lock(&current->sighand->siglock);
+ list_del(&timer->list);
+@@ -985,10 +1006,9 @@ static void itimer_delete(struct k_itime
+ retry_delete:
+ spin_lock_irqsave(&timer->it_lock, flags);
+
+- if (timer_delete_hook(timer) == TIMER_RETRY) {
+- unlock_timer(timer, flags);
++ if (timer_delete_hook(timer) == TIMER_RETRY)
+ goto retry_delete;
+- }
++
+ list_del(&timer->list);
+ /*
+ * This keeps any tasks waiting on the spin lock from thinking
+--- a/kernel/time/posix-timers.h
++++ b/kernel/time/posix-timers.h
+@@ -32,6 +32,8 @@ extern const struct k_clock clock_proces
+ extern const struct k_clock clock_thread;
+ extern const struct k_clock alarm_clock;
+
++extern void cpu_timers_grab_expiry_lock(struct k_itimer *timer);
++
+ int posix_timer_event(struct k_itimer *timr, int si_private);
+
+ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting);