Skip to content

Commit 538d710

Browse files
Peter ZijlstraKAGA-KOKO
authored andcommitted
posix-timers: Make lock_timer() use guard()
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 <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Acked-by: Frederic Weisbecker <[email protected]> Link: https://lore.kernel.org/all/[email protected] Link: https://lore.kernel.org/all/[email protected]
1 parent 1d25bdd commit 538d710

File tree

2 files changed

+50
-64
lines changed

2 files changed

+50
-64
lines changed

include/linux/cleanup.h

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,21 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
291291
#define __DEFINE_CLASS_IS_CONDITIONAL(_name, _is_cond) \
292292
static __maybe_unused const bool class_##_name##_is_conditional = _is_cond
293293

294-
#define DEFINE_GUARD(_name, _type, _lock, _unlock) \
294+
#define __DEFINE_GUARD_LOCK_PTR(_name, _exp) \
295+
static inline void * class_##_name##_lock_ptr(class_##_name##_t *_T) \
296+
{ return (void *)(__force unsigned long)*(_exp); }
297+
298+
#define DEFINE_CLASS_IS_GUARD(_name) \
295299
__DEFINE_CLASS_IS_CONDITIONAL(_name, false); \
300+
__DEFINE_GUARD_LOCK_PTR(_name, _T)
301+
302+
#define DEFINE_CLASS_IS_COND_GUARD(_name) \
303+
__DEFINE_CLASS_IS_CONDITIONAL(_name, true); \
304+
__DEFINE_GUARD_LOCK_PTR(_name, _T)
305+
306+
#define DEFINE_GUARD(_name, _type, _lock, _unlock) \
296307
DEFINE_CLASS(_name, _type, if (_T) { _unlock; }, ({ _lock; _T; }), _type _T); \
297-
static inline void * class_##_name##_lock_ptr(class_##_name##_t *_T) \
298-
{ return (void *)(__force unsigned long)*_T; }
308+
DEFINE_CLASS_IS_GUARD(_name)
299309

300310
#define DEFINE_GUARD_COND(_name, _ext, _condlock) \
301311
__DEFINE_CLASS_IS_CONDITIONAL(_name##_ext, true); \
@@ -375,11 +385,7 @@ static inline void class_##_name##_destructor(class_##_name##_t *_T) \
375385
if (_T->lock) { _unlock; } \
376386
} \
377387
\
378-
static inline void *class_##_name##_lock_ptr(class_##_name##_t *_T) \
379-
{ \
380-
return (void *)(__force unsigned long)_T->lock; \
381-
}
382-
388+
__DEFINE_GUARD_LOCK_PTR(_name, &_T->lock)
383389

384390
#define __DEFINE_LOCK_GUARD_1(_name, _type, _lock) \
385391
static inline class_##_name##_t class_##_name##_constructor(_type *l) \

kernel/time/posix-timers.c

Lines changed: 36 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,18 @@ static struct k_itimer *__lock_timer(timer_t timer_id);
6363

6464
static inline void unlock_timer(struct k_itimer *timr)
6565
{
66-
spin_unlock_irq(&timr->it_lock);
66+
if (likely((timr)))
67+
spin_unlock_irq(&timr->it_lock);
6768
}
6869

70+
#define scoped_timer_get_or_fail(_id) \
71+
scoped_cond_guard(lock_timer, return -EINVAL, _id)
72+
73+
#define scoped_timer (scope)
74+
75+
DEFINE_CLASS(lock_timer, struct k_itimer *, unlock_timer(_T), __lock_timer(id), timer_t id);
76+
DEFINE_CLASS_IS_COND_GUARD(lock_timer);
77+
6978
static int hash(struct signal_struct *sig, unsigned int nr)
7079
{
7180
return hash_32(hash32_ptr(sig) ^ nr, HASH_BITS(posix_timers_hashtable));
@@ -682,18 +691,10 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
682691

683692
static int do_timer_gettime(timer_t timer_id, struct itimerspec64 *setting)
684693
{
685-
struct k_itimer *timr;
686-
int ret = 0;
687-
688-
timr = lock_timer(timer_id);
689-
if (!timr)
690-
return -EINVAL;
691-
692694
memset(setting, 0, sizeof(*setting));
693-
timr->kclock->timer_get(timr, setting);
694-
695-
unlock_timer(timr);
696-
return ret;
695+
scoped_timer_get_or_fail(timer_id)
696+
scoped_timer->kclock->timer_get(scoped_timer, setting);
697+
return 0;
697698
}
698699

699700
/* Get the time remaining on a POSIX.1b interval timer. */
@@ -747,17 +748,8 @@ SYSCALL_DEFINE2(timer_gettime32, timer_t, timer_id,
747748
*/
748749
SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
749750
{
750-
struct k_itimer *timr;
751-
int overrun;
752-
753-
timr = lock_timer(timer_id);
754-
if (!timr)
755-
return -EINVAL;
756-
757-
overrun = timer_overrun_to_int(timr);
758-
unlock_timer(timr);
759-
760-
return overrun;
751+
scoped_timer_get_or_fail(timer_id)
752+
return timer_overrun_to_int(scoped_timer);
761753
}
762754

763755
static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires,
@@ -875,49 +867,38 @@ int common_timer_set(struct k_itimer *timr, int flags,
875867
return 0;
876868
}
877869

878-
static int do_timer_settime(timer_t timer_id, int tmr_flags,
879-
struct itimerspec64 *new_spec64,
870+
static int do_timer_settime(timer_t timer_id, int tmr_flags, struct itimerspec64 *new_spec64,
880871
struct itimerspec64 *old_spec64)
881872
{
882-
int ret;
883-
884873
if (!timespec64_valid(&new_spec64->it_interval) ||
885874
!timespec64_valid(&new_spec64->it_value))
886875
return -EINVAL;
887876

888877
if (old_spec64)
889878
memset(old_spec64, 0, sizeof(*old_spec64));
890879

891-
for (;;) {
892-
struct k_itimer *timr = lock_timer(timer_id);
880+
for (; ; old_spec64 = NULL) {
881+
struct k_itimer *timr;
893882

894-
if (!timr)
895-
return -EINVAL;
883+
scoped_timer_get_or_fail(timer_id) {
884+
timr = scoped_timer;
896885

897-
if (old_spec64)
898-
old_spec64->it_interval = ktime_to_timespec64(timr->it_interval);
886+
if (old_spec64)
887+
old_spec64->it_interval = ktime_to_timespec64(timr->it_interval);
899888

900-
/* Prevent signal delivery and rearming. */
901-
timr->it_signal_seq++;
889+
/* Prevent signal delivery and rearming. */
890+
timr->it_signal_seq++;
902891

903-
ret = timr->kclock->timer_set(timr, tmr_flags, new_spec64, old_spec64);
904-
if (ret != TIMER_RETRY) {
905-
unlock_timer(timr);
906-
break;
907-
}
892+
int ret = timr->kclock->timer_set(timr, tmr_flags, new_spec64, old_spec64);
893+
if (ret != TIMER_RETRY)
894+
return ret;
908895

909-
/* Read the old time only once */
910-
old_spec64 = NULL;
911-
/* Protect the timer from being freed after the lock is dropped */
912-
guard(rcu)();
913-
unlock_timer(timr);
914-
/*
915-
* timer_wait_running() might drop RCU read side protection
916-
* so the timer has to be looked up again!
917-
*/
896+
/* Protect the timer from being freed when leaving the lock scope */
897+
rcu_read_lock();
898+
}
918899
timer_wait_running(timr);
900+
rcu_read_unlock();
919901
}
920-
return ret;
921902
}
922903

923904
/* Set a POSIX.1b interval timer */
@@ -1028,13 +1009,12 @@ static void posix_timer_delete(struct k_itimer *timer)
10281009
/* Delete a POSIX.1b interval timer. */
10291010
SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)
10301011
{
1031-
struct k_itimer *timer = lock_timer(timer_id);
1032-
1033-
if (!timer)
1034-
return -EINVAL;
1012+
struct k_itimer *timer;
10351013

1036-
posix_timer_delete(timer);
1037-
unlock_timer(timer);
1014+
scoped_timer_get_or_fail(timer_id) {
1015+
timer = scoped_timer;
1016+
posix_timer_delete(timer);
1017+
}
10381018
/* Remove it from the hash, which frees up the timer ID */
10391019
posix_timer_unhash_and_free(timer);
10401020
return 0;

0 commit comments

Comments
 (0)