History

sched/core: Add function to sample state of locked-down task

A running task's state can be sampled in a consistent manner (for example, for diagnostic purposes) simply by invoking smp_call_function_single() on its CPU, which may be obtained using task_cpu(), then having the IPI handler verify that the desired task is in fact still running. However, if the task is not running, this sampling can in theory be done immediately and directly. In practice, the task might start running at any time, including during the sampling period. Gaining a consistent sample of a not-running task therefore requires that something be done to lock down the target task's state. This commit therefore adds a try_invoke_on_locked_down_task() function that invokes a specified function if the specified task can be locked down, returning true if successful and if the specified function returns true. Otherwise this function simply returns false. Given that the function passed to try_invoke_on_nonrunning_task() might be invoked with a runqueue lock held, that function had better be quite lightweight. The function is passed the target task's task_struct pointer and the argument passed to try_invoke_on_locked_down_task(), allowing easy access to task state and to a location for further variables to be passed in and out. Note that the specified function will be called even if the specified task is currently running. The function can use ->on_rq and task_curr() to quickly and easily determine the task's state, and can return false if this state is not to the function's liking. The caller of the try_invoke_on_locked_down_task() would then see the false return value, and could take appropriate action, for example, trying again later or sending an IPI if matters are more urgent. It is expected that use cases such as the RCU CPU stall warning code will simply return false if the task is currently running. However, there are use cases involving nohz_full CPUs where the specified function might instead fall back to an alternative sampling scheme that relies on heavier synchronization (such as memory barriers) in the target task.

Paul E. McKenney committed 6 years ago

Commit 2beaf321 parent 66777e52 files · +50 −0Browse files

Unified · Split

2 files
  1. include/linux/wait.h +2 −0
  2. kernel/sched/core.c +48 −0
include/linux/wait.h+2−0
@@ -1149,4 +1149,6 @@ int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, i · 1148 unmodified lines
1149 (wait)->flags = 0; \1149 (wait)->flags = 0; \
1150 } while (0)1150 } while (0)
1151 1151
1152bool try_invoke_on_locked_down_task(struct task_struct *p, bool (*func)(struct task_struct *t, void *arg), void *arg);
1153
1152#endif /* _LINUX_WAIT_H */1154#endif /* _LINUX_WAIT_H */
kernel/sched/core.c+48−0
@@ -2566,6 +2566,8 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) · 2565 unmodified lines
2566 *2566 *
2567 * Pairs with the LOCK+smp_mb__after_spinlock() on rq->lock in2567 * Pairs with the LOCK+smp_mb__after_spinlock() on rq->lock in
2568 * __schedule(). See the comment for smp_mb__after_spinlock().2568 * __schedule(). See the comment for smp_mb__after_spinlock().
2569 *
2570 * A similar smb_rmb() lives in try_invoke_on_locked_down_task().
2569 */2571 */
2570 smp_rmb();2572 smp_rmb();
2571 if (p->on_rq && ttwu_remote(p, wake_flags))2573 if (p->on_rq && ttwu_remote(p, wake_flags))
@@ -2639,6 +2641,52 @@ out: · 67 unmodified lines
2639 return success;2641 return success;
2640}2642}
2641 2643
2644/**
2645 * try_invoke_on_locked_down_task - Invoke a function on task in fixed state
2646 * @p: Process for which the function is to be invoked.
2647 * @func: Function to invoke.
2648 * @arg: Argument to function.
2649 *
2650 * If the specified task can be quickly locked into a definite state
2651 * (either sleeping or on a given runqueue), arrange to keep it in that
2652 * state while invoking @func(@arg). This function can use ->on_rq and
2653 * task_curr() to work out what the state is, if required. Given that
2654 * @func can be invoked with a runqueue lock held, it had better be quite
2655 * lightweight.
2656 *
2657 * Returns:
2658 * @false if the task slipped out from under the locks.
2659 * @true if the task was locked onto a runqueue or is sleeping.
2660 * However, @func can override this by returning @false.
2661 */
2662bool try_invoke_on_locked_down_task(struct task_struct *p, bool (*func)(struct task_struct *t, void *arg), void *arg)
2663{
2664 bool ret = false;
2665 struct rq_flags rf;
2666 struct rq *rq;
2667
2668 lockdep_assert_irqs_enabled();
2669 raw_spin_lock_irq(&p->pi_lock);
2670 if (p->on_rq) {
2671 rq = __task_rq_lock(p, &rf);
2672 if (task_rq(p) == rq)
2673 ret = func(p, arg);
2674 rq_unlock(rq, &rf);
2675 } else {
2676 switch (p->state) {
2677 case TASK_RUNNING:
2678 case TASK_WAKING:
2679 break;
2680 default:
2681 smp_rmb(); // See smp_rmb() comment in try_to_wake_up().
2682 if (!p->on_rq)
2683 ret = func(p, arg);
2684 }
2685 }
2686 raw_spin_unlock_irq(&p->pi_lock);
2687 return ret;
2688}
2689
2642/**2690/**
2643 * wake_up_process - Wake up a specific process2691 * wake_up_process - Wake up a specific process
2644 * @p: The process to be woken up.2692 * @p: The process to be woken up.