v2: a better approach, suggested by Petr (thank you) ---8<--- When a KLP fails to apply, klp_reverse_transition will clear the TIF_PATCH_PENDING flag on all tasks, except for newly created tasks which are not on the task list yet. Meanwhile, fork will copy over the TIF_PATCH_PENDING flag from the parent to the child early on, in dup_task_struct -> setup_thread_stack. Much later, klp_copy_process will set child->patch_state to match that of the parent. However, the parent's patch_state may have been changed by KLP loading or unloading since it was initially copied over into the child. This results in the KLP code occasionally hitting this warning in klp_complete_transition: for_each_process_thread(g, task) { WARN_ON_ONCE(test_tsk_thread_flag(task, TIF_PATCH_PENDING)); task->patch_state = KLP_UNDEFINED; } This patch will set, or clear, the TIF_PATCH_PENDING flag in the child process depending on whether or not it is needed at the time klp_copy_process is called, at a point in copy_process where the tasklist_lock is held exclusively, preventing races with the KLP code. This should prevent this warning from triggering again in the future. I have not yet figured out whether this would also help with races in the other direction, where the child process fails to have TIF_PATCH_PENDING set and somehow misses a transition, or whether the retries in klp_try_complete_transition would catch that task and help it transition later. Signed-off-by: Rik van Riel <riel@xxxxxxxxxxx> Reported-by: Breno Leitao <leitao@xxxxxxxxxx> --- kernel/livepatch/transition.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c index 5d03a2ad1066..30187b1d8275 100644 --- a/kernel/livepatch/transition.c +++ b/kernel/livepatch/transition.c @@ -610,9 +610,23 @@ void klp_reverse_transition(void) /* Called from copy_process() during fork */ void klp_copy_process(struct task_struct *child) { - child->patch_state = current->patch_state; - /* TIF_PATCH_PENDING gets copied in setup_thread_stack() */ + /* + * The parent process may have gone through a KLP transition since + * the thread flag was copied in setup_thread_stack earlier. Bring + * the task flag up to date with the parent here. + * + * The operation is serialized against all klp_*_transition() + * operations by the tasklist_lock. The only exception is + * klp_update_patch_state(current), but we cannot race with + * that because we are current. + */ + if (test_tsk_thread_flag(current, TIF_PATCH_PENDING)) + set_tsk_thread_flag(child, TIF_PATCH_PENDING); + else + clear_tsk_thread_flag(child, TIF_PATCH_PENDING); + + child->patch_state = current->patch_state; } /* -- 2.35.1