diff options
Diffstat (limited to 'kernel/posix-cpu-timers.c')
-rw-r--r-- | kernel/posix-cpu-timers.c | 327 |
1 files changed, 192 insertions, 135 deletions
diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index 3b8946416a5f..c7f31aa272f7 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -233,8 +233,7 @@ void thread_group_cputimer(struct task_struct *tsk, struct task_cputime *times) /* * Sample a process (thread group) clock for the given group_leader task. - * Must be called with task sighand lock held for safe while_each_thread() - * traversal. + * Must be called with tasklist_lock held for reading. */ static int cpu_clock_sample_group(const clockid_t which_clock, struct task_struct *p, @@ -261,53 +260,30 @@ static int cpu_clock_sample_group(const clockid_t which_clock, return 0; } -static int posix_cpu_clock_get_task(struct task_struct *tsk, - const clockid_t which_clock, - struct timespec *tp) -{ - int err = -EINVAL; - unsigned long long rtn; - - if (CPUCLOCK_PERTHREAD(which_clock)) { - if (same_thread_group(tsk, current)) - err = cpu_clock_sample(which_clock, tsk, &rtn); - } else { - unsigned long flags; - struct sighand_struct *sighand; - - /* - * while_each_thread() is not yet entirely RCU safe, - * keep locking the group while sampling process - * clock for now. - */ - sighand = lock_task_sighand(tsk, &flags); - if (!sighand) - return err; - - if (tsk == current || thread_group_leader(tsk)) - err = cpu_clock_sample_group(which_clock, tsk, &rtn); - - unlock_task_sighand(tsk, &flags); - } - - if (!err) - sample_to_timespec(which_clock, rtn, tp); - - return err; -} - static int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *tp) { const pid_t pid = CPUCLOCK_PID(which_clock); - int err = -EINVAL; + int error = -EINVAL; + unsigned long long rtn; if (pid == 0) { /* * Special case constant value for our own clocks. * We don't have to do any lookup to find ourselves. */ - err = posix_cpu_clock_get_task(current, which_clock, tp); + if (CPUCLOCK_PERTHREAD(which_clock)) { + /* + * Sampling just ourselves we can do with no locking. + */ + error = cpu_clock_sample(which_clock, + current, &rtn); + } else { + read_lock(&tasklist_lock); + error = cpu_clock_sample_group(which_clock, + current, &rtn); + read_unlock(&tasklist_lock); + } } else { /* * Find the given PID, and validate that the caller @@ -316,12 +292,29 @@ static int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *tp) struct task_struct *p; rcu_read_lock(); p = find_task_by_vpid(pid); - if (p) - err = posix_cpu_clock_get_task(p, which_clock, tp); + if (p) { + if (CPUCLOCK_PERTHREAD(which_clock)) { + if (same_thread_group(p, current)) { + error = cpu_clock_sample(which_clock, + p, &rtn); + } + } else { + read_lock(&tasklist_lock); + if (thread_group_leader(p) && p->sighand) { + error = + cpu_clock_sample_group(which_clock, + p, &rtn); + } + read_unlock(&tasklist_lock); + } + } rcu_read_unlock(); } - return err; + if (error) + return error; + sample_to_timespec(which_clock, rtn, tp); + return 0; } @@ -378,40 +371,36 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer) */ static int posix_cpu_timer_del(struct k_itimer *timer) { - int ret = 0; - unsigned long flags; - struct sighand_struct *sighand; struct task_struct *p = timer->it.cpu.task; + int ret = 0; - WARN_ON_ONCE(p == NULL); - - /* - * Protect against sighand release/switch in exit/exec and process/ - * thread timer list entry concurrent read/writes. - */ - sighand = lock_task_sighand(p, &flags); - if (unlikely(sighand == NULL)) { - /* - * We raced with the reaping of the task. - * The deletion should have cleared us off the list. - */ - WARN_ON_ONCE(!list_empty(&timer->it.cpu.entry)); - } else { - if (timer->it.cpu.firing) - ret = TIMER_RETRY; - else - list_del(&timer->it.cpu.entry); + if (likely(p != NULL)) { + read_lock(&tasklist_lock); + if (unlikely(p->sighand == NULL)) { + /* + * We raced with the reaping of the task. + * The deletion should have cleared us off the list. + */ + BUG_ON(!list_empty(&timer->it.cpu.entry)); + } else { + spin_lock(&p->sighand->siglock); + if (timer->it.cpu.firing) + ret = TIMER_RETRY; + else + list_del(&timer->it.cpu.entry); + spin_unlock(&p->sighand->siglock); + } + read_unlock(&tasklist_lock); - unlock_task_sighand(p, &flags); + if (!ret) + put_task_struct(p); } - if (!ret) - put_task_struct(p); - return ret; } -static void cleanup_timers_list(struct list_head *head) +static void cleanup_timers_list(struct list_head *head, + unsigned long long curr) { struct cpu_timer_list *timer, *next; @@ -425,11 +414,16 @@ static void cleanup_timers_list(struct list_head *head) * time for later timer_gettime calls to return. * This must be called with the siglock held. */ -static void cleanup_timers(struct list_head *head) +static void cleanup_timers(struct list_head *head, + cputime_t utime, cputime_t stime, + unsigned long long sum_exec_runtime) { - cleanup_timers_list(head); - cleanup_timers_list(++head); - cleanup_timers_list(++head); + + cputime_t ptime = utime + stime; + + cleanup_timers_list(head, cputime_to_expires(ptime)); + cleanup_timers_list(++head, cputime_to_expires(utime)); + cleanup_timers_list(++head, sum_exec_runtime); } /* @@ -439,14 +433,41 @@ static void cleanup_timers(struct list_head *head) */ void posix_cpu_timers_exit(struct task_struct *tsk) { + cputime_t utime, stime; + add_device_randomness((const void*) &tsk->se.sum_exec_runtime, sizeof(unsigned long long)); - cleanup_timers(tsk->cpu_timers); + task_cputime(tsk, &utime, &stime); + cleanup_timers(tsk->cpu_timers, + utime, stime, tsk->se.sum_exec_runtime); } void posix_cpu_timers_exit_group(struct task_struct *tsk) { - cleanup_timers(tsk->signal->cpu_timers); + struct signal_struct *const sig = tsk->signal; + cputime_t utime, stime; + + task_cputime(tsk, &utime, &stime); + cleanup_timers(tsk->signal->cpu_timers, + utime + sig->utime, stime + sig->stime, + tsk->se.sum_exec_runtime + sig->sum_sched_runtime); +} + +static void clear_dead_task(struct k_itimer *itimer, unsigned long long now) +{ + struct cpu_timer_list *timer = &itimer->it.cpu; + + /* + * That's all for this thread or process. + * We leave our residual in expires to be reported. + */ + put_task_struct(timer->task); + timer->task = NULL; + if (timer->expires < now) { + timer->expires = 0; + } else { + timer->expires -= now; + } } static inline int expires_gt(cputime_t expires, cputime_t new_exp) @@ -456,7 +477,8 @@ static inline int expires_gt(cputime_t expires, cputime_t new_exp) /* * Insert the timer on the appropriate list before any timers that - * expire later. This must be called with the sighand lock held. + * expire later. This must be called with the tasklist_lock held + * for reading, interrupts disabled and p->sighand->siglock taken. */ static void arm_timer(struct k_itimer *timer) { @@ -547,8 +569,7 @@ static void cpu_timer_fire(struct k_itimer *timer) /* * Sample a process (thread group) timer for the given group_leader task. - * Must be called with task sighand lock held for safe while_each_thread() - * traversal. + * Must be called with tasklist_lock held for reading. */ static int cpu_timer_sample_group(const clockid_t which_clock, struct task_struct *p, @@ -587,8 +608,7 @@ static DECLARE_WORK(nohz_kick_work, nohz_kick_work_fn); */ static void posix_cpu_timer_kick_nohz(void) { - if (context_tracking_is_enabled()) - schedule_work(&nohz_kick_work); + schedule_work(&nohz_kick_work); } bool posix_cpu_timers_can_stop_tick(struct task_struct *tsk) @@ -611,39 +631,43 @@ static inline void posix_cpu_timer_kick_nohz(void) { } * If we return TIMER_RETRY, it's necessary to release the timer's lock * and try again. (This happens when the timer is in the middle of firing.) */ -static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, +static int posix_cpu_timer_set(struct k_itimer *timer, int flags, struct itimerspec *new, struct itimerspec *old) { - unsigned long flags; - struct sighand_struct *sighand; struct task_struct *p = timer->it.cpu.task; unsigned long long old_expires, new_expires, old_incr, val; int ret; - WARN_ON_ONCE(p == NULL); + if (unlikely(p == NULL)) { + /* + * Timer refers to a dead task's clock. + */ + return -ESRCH; + } new_expires = timespec_to_sample(timer->it_clock, &new->it_value); + read_lock(&tasklist_lock); /* - * Protect against sighand release/switch in exit/exec and p->cpu_timers - * and p->signal->cpu_timers read/write in arm_timer() - */ - sighand = lock_task_sighand(p, &flags); - /* - * If p has just been reaped, we can no + * We need the tasklist_lock to protect against reaping that + * clears p->sighand. If p has just been reaped, we can no * longer get any information about it at all. */ - if (unlikely(sighand == NULL)) { + if (unlikely(p->sighand == NULL)) { + read_unlock(&tasklist_lock); + put_task_struct(p); + timer->it.cpu.task = NULL; return -ESRCH; } /* * Disarm any old timer after extracting its expiry time. */ - WARN_ON_ONCE(!irqs_disabled()); + BUG_ON(!irqs_disabled()); ret = 0; old_incr = timer->it.cpu.incr; + spin_lock(&p->sighand->siglock); old_expires = timer->it.cpu.expires; if (unlikely(timer->it.cpu.firing)) { timer->it.cpu.firing = -1; @@ -700,11 +724,12 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, * disable this firing since we are already reporting * it as an overrun (thanks to bump_cpu_timer above). */ - unlock_task_sighand(p, &flags); + spin_unlock(&p->sighand->siglock); + read_unlock(&tasklist_lock); goto out; } - if (new_expires != 0 && !(timer_flags & TIMER_ABSTIME)) { + if (new_expires != 0 && !(flags & TIMER_ABSTIME)) { new_expires += val; } @@ -718,7 +743,9 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, arm_timer(timer); } - unlock_task_sighand(p, &flags); + spin_unlock(&p->sighand->siglock); + read_unlock(&tasklist_lock); + /* * Install the new reload setting, and * set up the signal and overrun bookkeeping. @@ -760,8 +787,7 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp) { unsigned long long now; struct task_struct *p = timer->it.cpu.task; - - WARN_ON_ONCE(p == NULL); + int clear_dead; /* * Easy part: convert the reload time. @@ -774,34 +800,52 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp) return; } + if (unlikely(p == NULL)) { + /* + * This task already died and the timer will never fire. + * In this case, expires is actually the dead value. + */ + dead: + sample_to_timespec(timer->it_clock, timer->it.cpu.expires, + &itp->it_value); + return; + } + /* * Sample the clock to take the difference with the expiry time. */ if (CPUCLOCK_PERTHREAD(timer->it_clock)) { cpu_clock_sample(timer->it_clock, p, &now); + clear_dead = p->exit_state; } else { - struct sighand_struct *sighand; - unsigned long flags; - - /* - * Protect against sighand release/switch in exit/exec and - * also make timer sampling safe if it ends up calling - * thread_group_cputime(). - */ - sighand = lock_task_sighand(p, &flags); - if (unlikely(sighand == NULL)) { + read_lock(&tasklist_lock); + if (unlikely(p->sighand == NULL)) { /* * The process has been reaped. * We can't even collect a sample any more. * Call the timer disarmed, nothing else to do. */ + put_task_struct(p); + timer->it.cpu.task = NULL; timer->it.cpu.expires = 0; - sample_to_timespec(timer->it_clock, timer->it.cpu.expires, - &itp->it_value); + read_unlock(&tasklist_lock); + goto dead; } else { cpu_timer_sample_group(timer->it_clock, p, &now); - unlock_task_sighand(p, &flags); + clear_dead = (unlikely(p->exit_state) && + thread_group_empty(p)); } + read_unlock(&tasklist_lock); + } + + if (unlikely(clear_dead)) { + /* + * We've noticed that the thread is dead, but + * not yet reaped. Take this opportunity to + * drop our task ref. + */ + clear_dead_task(timer, now); + goto dead; } if (now < timer->it.cpu.expires) { @@ -1015,12 +1059,14 @@ static void check_process_timers(struct task_struct *tsk, */ void posix_cpu_timer_schedule(struct k_itimer *timer) { - struct sighand_struct *sighand; - unsigned long flags; struct task_struct *p = timer->it.cpu.task; unsigned long long now; - WARN_ON_ONCE(p == NULL); + if (unlikely(p == NULL)) + /* + * The task was cleaned up already, no future firings. + */ + goto out; /* * Fetch the current sample and update the timer's expiry time. @@ -1028,45 +1074,49 @@ void posix_cpu_timer_schedule(struct k_itimer *timer) if (CPUCLOCK_PERTHREAD(timer->it_clock)) { cpu_clock_sample(timer->it_clock, p, &now); bump_cpu_timer(timer, now); - if (unlikely(p->exit_state)) - goto out; - - /* Protect timer list r/w in arm_timer() */ - sighand = lock_task_sighand(p, &flags); - if (!sighand) + if (unlikely(p->exit_state)) { + clear_dead_task(timer, now); goto out; + } + read_lock(&tasklist_lock); /* arm_timer needs it. */ + spin_lock(&p->sighand->siglock); } else { - /* - * Protect arm_timer() and timer sampling in case of call to - * thread_group_cputime(). - */ - sighand = lock_task_sighand(p, &flags); - if (unlikely(sighand == NULL)) { + read_lock(&tasklist_lock); + if (unlikely(p->sighand == NULL)) { /* * The process has been reaped. * We can't even collect a sample any more. */ + put_task_struct(p); + timer->it.cpu.task = p = NULL; timer->it.cpu.expires = 0; - goto out; + goto out_unlock; } else if (unlikely(p->exit_state) && thread_group_empty(p)) { - unlock_task_sighand(p, &flags); - /* Optimizations: if the process is dying, no need to rearm */ - goto out; + /* + * We've noticed that the thread is dead, but + * not yet reaped. Take this opportunity to + * drop our task ref. + */ + cpu_timer_sample_group(timer->it_clock, p, &now); + clear_dead_task(timer, now); + goto out_unlock; } + spin_lock(&p->sighand->siglock); cpu_timer_sample_group(timer->it_clock, p, &now); bump_cpu_timer(timer, now); - /* Leave the sighand locked for the call below. */ + /* Leave the tasklist_lock locked for the call below. */ } /* * Now re-arm for the new expiry time. */ - WARN_ON_ONCE(!irqs_disabled()); + BUG_ON(!irqs_disabled()); arm_timer(timer); - unlock_task_sighand(p, &flags); + spin_unlock(&p->sighand->siglock); + +out_unlock: + read_unlock(&tasklist_lock); - /* Kick full dynticks CPUs in case they need to tick on the new timer */ - posix_cpu_timer_kick_nohz(); out: timer->it_overrun_last = timer->it_overrun; timer->it_overrun = -1; @@ -1150,7 +1200,7 @@ void run_posix_cpu_timers(struct task_struct *tsk) struct k_itimer *timer, *next; unsigned long flags; - WARN_ON_ONCE(!irqs_disabled()); + BUG_ON(!irqs_disabled()); /* * The fast path checks that there are no expired thread or thread @@ -1206,6 +1256,13 @@ void run_posix_cpu_timers(struct task_struct *tsk) cpu_timer_fire(timer); spin_unlock(&timer->it_lock); } + + /* + * In case some timers were rescheduled after the queue got emptied, + * wake up full dynticks CPUs. + */ + if (tsk->signal->cputimer.running) + posix_cpu_timer_kick_nohz(); } /* @@ -1217,7 +1274,7 @@ void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx, { unsigned long long now; - WARN_ON_ONCE(clock_idx == CPUCLOCK_SCHED); + BUG_ON(clock_idx == CPUCLOCK_SCHED); cpu_timer_sample_group(clock_idx, tsk, &now); if (oldval) { |