History

posix-cpu-timers: Store a reference to a pid not a task

posix cpu timers do not handle the death of a process well. This is most clearly seen when a multi-threaded process calls exec from a thread that is not the leader of the thread group. The posix cpu timer code continues to pin the old thread group leader and is unable to find the siglock from there. This results in posix_cpu_timer_del being unable to delete a timer, posix_cpu_timer_set being unable to set a timer. Further to compensate for the problems in posix_cpu_timer_del on a multi-threaded exec all timers that point at the multi-threaded task are stopped. The code for the timers fundamentally needs to check if the target process/thread is alive. This needs an extra level of indirection. This level of indirection is already available in struct pid. So replace cpu.task with cpu.pid to get the needed extra layer of indirection. In addition to handling things more cleanly this reduces the amount of memory a timer can pin when a process exits and then is reaped from a task_struct to the vastly smaller struct pid.

Eric W. Biederman committed 6 years ago

Commit 55e8c8e1 parent beb41d92 files · +56 −19Browse files

Unified · Split

2 files
  1. include/linux/posix-timers.h +1 −1
  2. kernel/time/posix-cpu-timers.c +55 −18
include/linux/posix-timers.h+1−1
@@ -69,7 +69,7 @@ static inline int clockid_to_fd(const clockid_t clk) · 68 unmodified lines
69struct cpu_timer {69struct cpu_timer {
70 struct timerqueue_node node;70 struct timerqueue_node node;
71 struct timerqueue_head *head;71 struct timerqueue_head *head;
72 struct task_struct *task;72 struct pid *pid;
73 struct list_head elist;73 struct list_head elist;
74 int firing;74 int firing;
75};75};
kernel/time/posix-cpu-timers.c+55−18
@@ -118,6 +118,16 @@ static inline int validate_clock_permissions(const clockid_t clock) · 117 unmodified lines
118 return __get_task_for_clock(clock, false, false) ? 0 : -EINVAL;118 return __get_task_for_clock(clock, false, false) ? 0 : -EINVAL;
119}119}
120 120
121static inline enum pid_type cpu_timer_pid_type(struct k_itimer *timer)
122{
123 return CPUCLOCK_PERTHREAD(timer->it_clock) ? PIDTYPE_PID : PIDTYPE_TGID;
124}
125
126static inline struct task_struct *cpu_timer_task_rcu(struct k_itimer *timer)
127{
128 return pid_task(timer->it.cpu.pid, cpu_timer_pid_type(timer));
129}
130
121/*131/*
122 * Update expiry time from increment, and increase overrun count,132 * Update expiry time from increment, and increase overrun count,
123 * given the current clock sample.133 * given the current clock sample.
@@ -391,7 +401,12 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer) · 267 unmodified lines
391 401
392 new_timer->kclock = &clock_posix_cpu;402 new_timer->kclock = &clock_posix_cpu;
393 timerqueue_init(&new_timer->it.cpu.node);403 timerqueue_init(&new_timer->it.cpu.node);
394 new_timer->it.cpu.task = p;404 new_timer->it.cpu.pid = get_task_pid(p, cpu_timer_pid_type(new_timer));
405 /*
406 * get_task_for_clock() took a reference on @p. Drop it as the timer
407 * holds a reference on the pid of @p.
408 */
409 put_task_struct(p);
395 return 0;410 return 0;
396}411}
397 412
@@ -404,13 +419,15 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer) · 6 unmodified lines
404static int posix_cpu_timer_del(struct k_itimer *timer)419static int posix_cpu_timer_del(struct k_itimer *timer)
405{420{
406 struct cpu_timer *ctmr = &timer->it.cpu;421 struct cpu_timer *ctmr = &timer->it.cpu;
407 struct task_struct *p = ctmr->task;
408 struct sighand_struct *sighand;422 struct sighand_struct *sighand;
423 struct task_struct *p;
409 unsigned long flags;424 unsigned long flags;
410 int ret = 0;425 int ret = 0;
411 426
412 if (WARN_ON_ONCE(!p))427 rcu_read_lock();
413 return -EINVAL;428 p = cpu_timer_task_rcu(timer);
429 if (!p)
430 goto out;
414 431
415 /*432 /*
416 * Protect against sighand release/switch in exit/exec and process/433 * Protect against sighand release/switch in exit/exec and process/
@@ -432,8 +449,10 @@ static int posix_cpu_timer_del(struct k_itimer *timer) · 15 unmodified lines
432 unlock_task_sighand(p, &flags);449 unlock_task_sighand(p, &flags);
433 }450 }
434 451
452out:
453 rcu_read_unlock();
435 if (!ret)454 if (!ret)
436 put_task_struct(p);455 put_pid(ctmr->pid);
437 456
438 return ret;457 return ret;
439}458}
@@ -561,13 +580,21 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, · 121 unmodified lines
561 clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);580 clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
562 u64 old_expires, new_expires, old_incr, val;581 u64 old_expires, new_expires, old_incr, val;
563 struct cpu_timer *ctmr = &timer->it.cpu;582 struct cpu_timer *ctmr = &timer->it.cpu;
564 struct task_struct *p = ctmr->task;
565 struct sighand_struct *sighand;583 struct sighand_struct *sighand;
584 struct task_struct *p;
566 unsigned long flags;585 unsigned long flags;
567 int ret = 0;586 int ret = 0;
568 587
569 if (WARN_ON_ONCE(!p))588 rcu_read_lock();
570 return -EINVAL;589 p = cpu_timer_task_rcu(timer);
590 if (!p) {
591 /*
592 * If p has just been reaped, we can no
593 * longer get any information about it at all.
594 */
595 rcu_read_unlock();
596 return -ESRCH;
597 }
571 598
572 /*599 /*
573 * Use the to_ktime conversion because that clamps the maximum600 * Use the to_ktime conversion because that clamps the maximum
@@ -584,8 +611,10 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, · 10 unmodified lines
584 * If p has just been reaped, we can no611 * If p has just been reaped, we can no
585 * longer get any information about it at all.612 * longer get any information about it at all.
586 */613 */
587 if (unlikely(sighand == NULL))614 if (unlikely(sighand == NULL)) {
615 rcu_read_unlock();
588 return -ESRCH;616 return -ESRCH;
617 }
589 618
590 /*619 /*
591 * Disarm any old timer after extracting its expiry time.620 * Disarm any old timer after extracting its expiry time.
@@ -690,6 +719,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, · 98 unmodified lines
690 719
691 ret = 0;720 ret = 0;
692 out:721 out:
722 rcu_read_unlock();
693 if (old)723 if (old)
694 old->it_interval = ns_to_timespec64(old_incr);724 old->it_interval = ns_to_timespec64(old_incr);
695 725
@@ -701,10 +731,12 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp · 5 unmodified lines
701 clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);731 clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
702 struct cpu_timer *ctmr = &timer->it.cpu;732 struct cpu_timer *ctmr = &timer->it.cpu;
703 u64 now, expires = cpu_timer_getexpires(ctmr);733 u64 now, expires = cpu_timer_getexpires(ctmr);
704 struct task_struct *p = ctmr->task;734 struct task_struct *p;
705 735
706 if (WARN_ON_ONCE(!p))736 rcu_read_lock();
707 return;737 p = cpu_timer_task_rcu(timer);
738 if (!p)
739 goto out;
708 740
709 /*741 /*
710 * Easy part: convert the reload time.742 * Easy part: convert the reload time.
@@ -712,7 +744,7 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp · 1 unmodified line
712 itp->it_interval = ktime_to_timespec64(timer->it_interval);744 itp->it_interval = ktime_to_timespec64(timer->it_interval);
713 745
714 if (!expires)746 if (!expires)
715 return;747 goto out;
716 748
717 /*749 /*
718 * Sample the clock to take the difference with the expiry time.750 * Sample the clock to take the difference with the expiry time.
@@ -732,6 +764,8 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp · 13 unmodified lines
732 itp->it_value.tv_nsec = 1;764 itp->it_value.tv_nsec = 1;
733 itp->it_value.tv_sec = 0;765 itp->it_value.tv_sec = 0;
734 }766 }
767out:
768 rcu_read_unlock();
735}769}
736 770
737#define MAX_COLLECTED 20771#define MAX_COLLECTED 20
@@ -952,14 +986,15 @@ static void check_process_timers(struct task_struct *tsk, · 214 unmodified lines
952static void posix_cpu_timer_rearm(struct k_itimer *timer)986static void posix_cpu_timer_rearm(struct k_itimer *timer)
953{987{
954 clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);988 clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
955 struct cpu_timer *ctmr = &timer->it.cpu;989 struct task_struct *p;
956 struct task_struct *p = ctmr->task;
957 struct sighand_struct *sighand;990 struct sighand_struct *sighand;
958 unsigned long flags;991 unsigned long flags;
959 u64 now;992 u64 now;
960 993
961 if (WARN_ON_ONCE(!p))994 rcu_read_lock();
962 return;995 p = cpu_timer_task_rcu(timer);
996 if (!p)
997 goto out;
963 998
964 /*999 /*
965 * Fetch the current sample and update the timer's expiry time.1000 * Fetch the current sample and update the timer's expiry time.
@@ -974,13 +1009,15 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer) · 8 unmodified lines
974 /* Protect timer list r/w in arm_timer() */1009 /* Protect timer list r/w in arm_timer() */
975 sighand = lock_task_sighand(p, &flags);1010 sighand = lock_task_sighand(p, &flags);
976 if (unlikely(sighand == NULL))1011 if (unlikely(sighand == NULL))
977 return;1012 goto out;
978 1013
979 /*1014 /*
980 * Now re-arm for the new expiry time.1015 * Now re-arm for the new expiry time.
981 */1016 */
982 arm_timer(timer, p);1017 arm_timer(timer, p);
983 unlock_task_sighand(p, &flags);1018 unlock_task_sighand(p, &flags);
1019out:
1020 rcu_read_unlock();
984}1021}
985 1022
986/**1023/**