task_work: allow TWA_SIGNAL without a rescheduling IPI
Some use cases don't always need an IPI when sending a TWA_SIGNAL notification. Add TWA_SIGNAL_NO_IPI, which is just like TWA_SIGNAL, except it doesn't send an IPI to the target task. It merely sets TIF_NOTIFY_SIGNAL and wakes up the task. This can be useful in avoiding a forceful transition to the kernel if the task is running in userspace. Depending on the task_work in question, it may be quite fine waiting for the next reschedule or kernel enter anyway, or the use case may even have other mechanisms for hinting to the task that a transition may be useful. This can drive more cooperative scheduling of task_work. Reviewed-by: Pavel Begunkov <asml.silence@gmail.com> Link: https://lore.kernel.org/r/821f42b6-7d91-8074-8212-d34998097de4@kernel.dk Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
69cc1b6fa5
commit
e788be95a5
|
@ -355,14 +355,23 @@ static inline void clear_notify_signal(void)
|
||||||
smp_mb__after_atomic();
|
smp_mb__after_atomic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns 'true' if kick_process() is needed to force a transition from
|
||||||
|
* user -> kernel to guarantee expedient run of TWA_SIGNAL based task_work.
|
||||||
|
*/
|
||||||
|
static inline bool __set_notify_signal(struct task_struct *task)
|
||||||
|
{
|
||||||
|
return !test_and_set_tsk_thread_flag(task, TIF_NOTIFY_SIGNAL) &&
|
||||||
|
!wake_up_state(task, TASK_INTERRUPTIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called to break out of interruptible wait loops, and enter the
|
* Called to break out of interruptible wait loops, and enter the
|
||||||
* exit_to_user_mode_loop().
|
* exit_to_user_mode_loop().
|
||||||
*/
|
*/
|
||||||
static inline void set_notify_signal(struct task_struct *task)
|
static inline void set_notify_signal(struct task_struct *task)
|
||||||
{
|
{
|
||||||
if (!test_and_set_tsk_thread_flag(task, TIF_NOTIFY_SIGNAL) &&
|
if (__set_notify_signal(task))
|
||||||
!wake_up_state(task, TASK_INTERRUPTIBLE))
|
|
||||||
kick_process(task);
|
kick_process(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ enum task_work_notify_mode {
|
||||||
TWA_NONE,
|
TWA_NONE,
|
||||||
TWA_RESUME,
|
TWA_RESUME,
|
||||||
TWA_SIGNAL,
|
TWA_SIGNAL,
|
||||||
|
TWA_SIGNAL_NO_IPI,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline bool task_work_pending(struct task_struct *task)
|
static inline bool task_work_pending(struct task_struct *task)
|
||||||
|
|
|
@ -12,12 +12,22 @@ static struct callback_head work_exited; /* all we need is ->next == NULL */
|
||||||
* @notify: how to notify the targeted task
|
* @notify: how to notify the targeted task
|
||||||
*
|
*
|
||||||
* Queue @work for task_work_run() below and notify the @task if @notify
|
* Queue @work for task_work_run() below and notify the @task if @notify
|
||||||
* is @TWA_RESUME or @TWA_SIGNAL. @TWA_SIGNAL works like signals, in that the
|
* is @TWA_RESUME, @TWA_SIGNAL, or @TWA_SIGNAL_NO_IPI.
|
||||||
* it will interrupt the targeted task and run the task_work. @TWA_RESUME
|
*
|
||||||
* work is run only when the task exits the kernel and returns to user mode,
|
* @TWA_SIGNAL works like signals, in that the it will interrupt the targeted
|
||||||
* or before entering guest mode. Fails if the @task is exiting/exited and thus
|
* task and run the task_work, regardless of whether the task is currently
|
||||||
* it can't process this @work. Otherwise @work->func() will be called when the
|
* running in the kernel or userspace.
|
||||||
* @task goes through one of the aforementioned transitions, or exits.
|
* @TWA_SIGNAL_NO_IPI works like @TWA_SIGNAL, except it doesn't send a
|
||||||
|
* reschedule IPI to force the targeted task to reschedule and run task_work.
|
||||||
|
* This can be advantageous if there's no strict requirement that the
|
||||||
|
* task_work be run as soon as possible, just whenever the task enters the
|
||||||
|
* kernel anyway.
|
||||||
|
* @TWA_RESUME work is run only when the task exits the kernel and returns to
|
||||||
|
* user mode, or before entering guest mode.
|
||||||
|
*
|
||||||
|
* Fails if the @task is exiting/exited and thus it can't process this @work.
|
||||||
|
* Otherwise @work->func() will be called when the @task goes through one of
|
||||||
|
* the aforementioned transitions, or exits.
|
||||||
*
|
*
|
||||||
* If the targeted task is exiting, then an error is returned and the work item
|
* If the targeted task is exiting, then an error is returned and the work item
|
||||||
* is not queued. It's up to the caller to arrange for an alternative mechanism
|
* is not queued. It's up to the caller to arrange for an alternative mechanism
|
||||||
|
@ -53,6 +63,9 @@ int task_work_add(struct task_struct *task, struct callback_head *work,
|
||||||
case TWA_SIGNAL:
|
case TWA_SIGNAL:
|
||||||
set_notify_signal(task);
|
set_notify_signal(task);
|
||||||
break;
|
break;
|
||||||
|
case TWA_SIGNAL_NO_IPI:
|
||||||
|
__set_notify_signal(task);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue