mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Thomas Gleixner <tglx@linutronix.de>
To: LKML <linux-kernel@vger.kernel.org>
Cc: Anna-Maria Behnsen <anna-maria@linutronix.de>,
	Frederic Weisbecker <frederic@kernel.org>,
	Benjamin Segall <bsegall@google.com>,
	Eric Dumazet <edumazet@google.com>,
	Andrey Vagin <avagin@openvz.org>,
	Pavel Tikhomirov <ptikhomirov@virtuozzo.com>,
	Peter Zijlstra <peterz@infradead.org>,
	Cyrill Gorcunov <gorcunov@gmail.com>
Subject: [patch V3 10/18] posix-timers: Make lock_timer() use guard()
Date: Sat,  8 Mar 2025 17:48:34 +0100 (CET)	[thread overview]
Message-ID: <20250308155624.087465658@linutronix.de> (raw)
In-Reply-To: <20250308155501.391430556@linutronix.de>


From: Peter Zijlstra <peterz@infradead.org>

The lookup and locking of posix timers requires the same repeating pattern
at all usage sites:

   tmr = lock_timer(tiner_id);
   if (!tmr)
   	return -EINVAL;
   ....
   unlock_timer(tmr);

Solve this with a guard implementation, which works in most places out of
the box except for those, which need to unlock the timer inside the guard
scope.

Though the only places where this matters are timer_delete() and
timer_settime(). In both cases the timer pointer needs to be preserved
across the end of the scope, which is solved by storing the pointer in a
variable outside of the scope.

timer_settime() also has to protect the timer with RCU before unlocking,
which obviously can't use guard(rcu) before leaving the guard scope as that
guard is cleaned up before the unlock. Solve this by providing the RCU
protection open coded.

[ tglx: Made it work and added change log ]

Signed-off-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250224162103.GD11590@noisy.programming.kicks-ass.net
---
V2a: Make unlock conditional - 0day
V2: New patch
---
 include/linux/cleanup.h    |   22 ++++++----
 kernel/time/posix-timers.c |   94 +++++++++++++++++----------------------------
 2 files changed, 51 insertions(+), 65 deletions(-)

--- a/include/linux/cleanup.h
+++ b/include/linux/cleanup.h
@@ -291,11 +291,21 @@ static inline class_##_name##_t class_##
 #define __DEFINE_CLASS_IS_CONDITIONAL(_name, _is_cond)	\
 static __maybe_unused const bool class_##_name##_is_conditional = _is_cond
 
-#define DEFINE_GUARD(_name, _type, _lock, _unlock) \
+#define __DEFINE_GUARD_LOCK_PTR(_name, _exp) \
+	static inline void * class_##_name##_lock_ptr(class_##_name##_t *_T) \
+	{ return (void *)(__force unsigned long)*(_exp); }
+
+#define DEFINE_CLASS_IS_GUARD(_name) \
 	__DEFINE_CLASS_IS_CONDITIONAL(_name, false); \
+	__DEFINE_GUARD_LOCK_PTR(_name, _T)
+
+#define DEFINE_CLASS_IS_COND_GUARD(_name) \
+	__DEFINE_CLASS_IS_CONDITIONAL(_name, true); \
+	__DEFINE_GUARD_LOCK_PTR(_name, _T)
+
+#define DEFINE_GUARD(_name, _type, _lock, _unlock) \
 	DEFINE_CLASS(_name, _type, if (_T) { _unlock; }, ({ _lock; _T; }), _type _T); \
-	static inline void * class_##_name##_lock_ptr(class_##_name##_t *_T) \
-	{ return (void *)(__force unsigned long)*_T; }
+	DEFINE_CLASS_IS_GUARD(_name)
 
 #define DEFINE_GUARD_COND(_name, _ext, _condlock) \
 	__DEFINE_CLASS_IS_CONDITIONAL(_name##_ext, true); \
@@ -375,11 +385,7 @@ static inline void class_##_name##_destr
 	if (_T->lock) { _unlock; }					\
 }									\
 									\
-static inline void *class_##_name##_lock_ptr(class_##_name##_t *_T)	\
-{									\
-	return (void *)(__force unsigned long)_T->lock;			\
-}
-
+__DEFINE_GUARD_LOCK_PTR(_name, &_T->lock)
 
 #define __DEFINE_LOCK_GUARD_1(_name, _type, _lock)			\
 static inline class_##_name##_t class_##_name##_constructor(_type *l)	\
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -63,9 +63,18 @@ static struct k_itimer *__lock_timer(tim
 
 static inline void unlock_timer(struct k_itimer *timr)
 {
-	spin_unlock_irq(&timr->it_lock);
+	if (likely((timr)))
+		spin_unlock_irq(&timr->it_lock);
 }
 
+#define scoped_timer_get_or_fail(_id)					\
+	scoped_cond_guard(lock_timer, return -EINVAL, _id)
+
+#define scoped_timer				(scope)
+
+DEFINE_CLASS(lock_timer, struct k_itimer *, unlock_timer(_T), __lock_timer(id), timer_t id);
+DEFINE_CLASS_IS_COND_GUARD(lock_timer);
+
 static int hash(struct signal_struct *sig, unsigned int nr)
 {
 	return hash_32(hash32_ptr(sig) ^ nr, HASH_BITS(posix_timers_hashtable));
@@ -682,18 +691,10 @@ void common_timer_get(struct k_itimer *t
 
 static int do_timer_gettime(timer_t timer_id,  struct itimerspec64 *setting)
 {
-	struct k_itimer *timr;
-	int ret = 0;
-
-	timr = lock_timer(timer_id);
-	if (!timr)
-		return -EINVAL;
-
 	memset(setting, 0, sizeof(*setting));
-	timr->kclock->timer_get(timr, setting);
-
-	unlock_timer(timr);
-	return ret;
+	scoped_timer_get_or_fail(timer_id)
+		scoped_timer->kclock->timer_get(scoped_timer, setting);
+	return 0;
 }
 
 /* Get the time remaining on a POSIX.1b interval timer. */
@@ -747,17 +748,8 @@ SYSCALL_DEFINE2(timer_gettime32, timer_t
  */
 SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
 {
-	struct k_itimer *timr;
-	int overrun;
-
-	timr = lock_timer(timer_id);
-	if (!timr)
-		return -EINVAL;
-
-	overrun = timer_overrun_to_int(timr);
-	unlock_timer(timr);
-
-	return overrun;
+	scoped_timer_get_or_fail(timer_id)
+		return timer_overrun_to_int(scoped_timer);
 }
 
 static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires,
@@ -875,12 +867,9 @@ int common_timer_set(struct k_itimer *ti
 	return 0;
 }
 
-static int do_timer_settime(timer_t timer_id, int tmr_flags,
-			    struct itimerspec64 *new_spec64,
+static int do_timer_settime(timer_t timer_id, int tmr_flags, struct itimerspec64 *new_spec64,
 			    struct itimerspec64 *old_spec64)
 {
-	int ret;
-
 	if (!timespec64_valid(&new_spec64->it_interval) ||
 	    !timespec64_valid(&new_spec64->it_value))
 		return -EINVAL;
@@ -888,36 +877,28 @@ static int do_timer_settime(timer_t time
 	if (old_spec64)
 		memset(old_spec64, 0, sizeof(*old_spec64));
 
-	for (;;) {
-		struct k_itimer *timr = lock_timer(timer_id);
+	for (; ; old_spec64 = NULL) {
+		struct k_itimer *timr;
 
-		if (!timr)
-			return -EINVAL;
+		scoped_timer_get_or_fail(timer_id) {
+			timr = scoped_timer;
 
-		if (old_spec64)
-			old_spec64->it_interval = ktime_to_timespec64(timr->it_interval);
+			if (old_spec64)
+				old_spec64->it_interval = ktime_to_timespec64(timr->it_interval);
 
-		/* Prevent signal delivery and rearming. */
-		timr->it_signal_seq++;
-
-		ret = timr->kclock->timer_set(timr, tmr_flags, new_spec64, old_spec64);
-		if (ret != TIMER_RETRY) {
-			unlock_timer(timr);
-			break;
-		}
+			/* Prevent signal delivery and rearming. */
+			timr->it_signal_seq++;
 
-		/* Read the old time only once */
-		old_spec64 = NULL;
-		/* Protect the timer from being freed after the lock is dropped */
-		guard(rcu)();
-		unlock_timer(timr);
-		/*
-		 * timer_wait_running() might drop RCU read side protection
-		 * so the timer has to be looked up again!
-		 */
+			int ret = timr->kclock->timer_set(timr, tmr_flags, new_spec64, old_spec64);
+			if (ret != TIMER_RETRY)
+				return ret;
+
+			/* Protect the timer from being freed when leaving the lock scope */
+			rcu_read_lock();
+		}
 		timer_wait_running(timr);
+		rcu_read_unlock();
 	}
-	return ret;
 }
 
 /* Set a POSIX.1b interval timer */
@@ -1028,13 +1009,12 @@ static void posix_timer_delete(struct k_
 /* Delete a POSIX.1b interval timer. */
 SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)
 {
-	struct k_itimer *timer = lock_timer(timer_id);
-
-	if (!timer)
-		return -EINVAL;
+	struct k_itimer *timer;
 
-	posix_timer_delete(timer);
-	unlock_timer(timer);
+	scoped_timer_get_or_fail(timer_id) {
+		timer = scoped_timer;
+		posix_timer_delete(timer);
+	}
 	/* Remove it from the hash, which frees up the timer ID */
 	posix_timer_unhash_and_free(timer);
 	return 0;


  parent reply	other threads:[~2025-03-08 16:48 UTC|newest]

Thread overview: 68+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-03-08 16:48 [patch V3 00/18] posix-timers: Rework the global hash table and provide a sane mechanism for CRIU Thomas Gleixner
2025-03-08 16:48 ` [patch V3 01/18] posix-timers: Ensure that timer initialization is fully visible Thomas Gleixner
2025-03-08 21:39   ` Frederic Weisbecker
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-08 16:48 ` [patch V3 02/18] posix-timers: Initialise timer before adding it to the hash table Thomas Gleixner
2025-03-11 13:25   ` Frederic Weisbecker
2025-03-11 14:16     ` Thomas Gleixner
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Eric Dumazet
2025-03-08 16:48 ` [patch V3 03/18] posix-timers: Add cond_resched() to posix_timer_add() search loop Thomas Gleixner
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Eric Dumazet
2025-03-08 16:48 ` [patch V3 04/18] posix-timers: Cleanup includes Thomas Gleixner
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-08 16:48 ` [patch V3 05/18] posix-timers: Remove a few paranoid warnings Thomas Gleixner
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-08 16:48 ` [patch V3 06/18] posix-timers: Remove SLAB_PANIC from kmem cache Thomas Gleixner
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-08 16:48 ` [patch V3 07/18] posix-timers: Use guards in a few places Thomas Gleixner
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-08 16:48 ` [patch V3 08/18] posix-timers: Simplify lock/unlock_timer() Thomas Gleixner
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-08 16:48 ` [patch V3 09/18] posix-timers: Rework timer removal Thomas Gleixner
2025-03-09 23:17   ` Frederic Weisbecker
2025-03-10  6:33     ` Thomas Gleixner
2025-03-10  8:13   ` [patch V3a " Thomas Gleixner
2025-03-13 11:31     ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-08 16:48 ` Thomas Gleixner [this message]
2025-03-10 11:57   ` [patch V3 10/18] posix-timers: Make lock_timer() use guard() Frederic Weisbecker
2025-03-10 17:36     ` Thomas Gleixner
2025-03-10 22:16       ` Frederic Weisbecker
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Peter Zijlstra
2025-03-08 16:48 ` [patch V3 11/18] posix-timers: Make signal_struct:: Next_posix_timer_id an atomic_t Thomas Gleixner
2025-03-10 22:57   ` Frederic Weisbecker
2025-03-11 13:41   ` Frederic Weisbecker
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Eric Dumazet
2025-03-08 16:48 ` [patch V3 12/18] posix-timers: Improve hash table performance Thomas Gleixner
2025-03-11 13:44   ` Frederic Weisbecker
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-08 16:48 ` [patch V3 13/18] posix-timers: Switch to jhash32() Thomas Gleixner
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-08 16:48 ` [patch V3 14/18] posix-timers: Avoid false cacheline sharing Thomas Gleixner
2025-03-11 13:53   ` Frederic Weisbecker
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-13 22:13   ` [patch V3 14/18] " David Laight
2025-03-17  6:20   ` Nysal Jan K.A.
2025-03-08 16:48 ` [patch V3 15/18] posix-timers: Make per process list RCU safe Thomas Gleixner
2025-03-11 15:29   ` Frederic Weisbecker
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-08 16:48 ` [patch V3 16/18] posix-timers: Dont iterate /proc/$PID/timers with sighand:: Siglock held Thomas Gleixner
2025-03-08 22:38   ` Cyrill Gorcunov
2025-03-11 15:26   ` Frederic Weisbecker
2025-03-13 11:31   ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-08 16:48 ` [patch V3 17/18] posix-timers: Provide a mechanism to allocate a given timer ID Thomas Gleixner
2025-03-08 22:25   ` Cyrill Gorcunov
2025-03-11 21:35   ` Frederic Weisbecker
2025-03-11 22:05     ` Thomas Gleixner
2025-03-11 22:07       ` [patch V3a " Thomas Gleixner
2025-03-11 22:32         ` Frederic Weisbecker
2025-03-12  7:56           ` Cyrill Gorcunov
2025-03-12 11:24             ` Thomas Gleixner
2025-03-12 11:31               ` Thomas Gleixner
2025-03-12 12:41               ` Cyrill Gorcunov
2025-03-12 17:45                 ` Thomas Gleixner
2025-03-13 11:31         ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2025-03-12 12:59     ` [patch V3 17/18] " Cyrill Gorcunov
2025-03-08 16:48 ` [patch V3 18/18] selftests/timers/posix-timers: Add a test for exact allocation mode Thomas Gleixner
2025-03-10  8:11   ` [patch V3a " Thomas Gleixner
2025-03-11 21:44     ` Frederic Weisbecker
2025-03-13 11:31     ` [tip: timers/core] " tip-bot2 for Thomas Gleixner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250308155624.087465658@linutronix.de \
    --to=tglx@linutronix.de \
    --cc=anna-maria@linutronix.de \
    --cc=avagin@openvz.org \
    --cc=bsegall@google.com \
    --cc=edumazet@google.com \
    --cc=frederic@kernel.org \
    --cc=gorcunov@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=peterz@infradead.org \
    --cc=ptikhomirov@virtuozzo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®