>From 4ab2af61fc601b550b4e59dc6fa2bd01375920f0 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 7 Feb 2012 17:40:32 +0100 Subject: [PATCH 3/5] usermodehelper: implement UMH_KILLABLE Implement UMH_KILLABLE, should be used along with UMH_WAIT_EXEC/PROC. The caller must ensure that subprocess_info->path/etc can not go away until call_usermodehelper_freeinfo(). call_usermodehelper_exec(UMH_KILLABLE) does wait_for_completion_killable. If it fails, it uses xchg(&sub_info->complete, NULL) to serialize with umh_complete() which does the same xhcg() to access sub_info->complete. If call_usermodehelper_exec wins, it can safely return. umh_complete() should get NULL and call call_usermodehelper_freeinfo(). Otherwise we know that umh_complete() was already called, in this case call_usermodehelper_exec() falls back to wait_for_completion() which should succeed "very soon". --- include/linux/kmod.h | 1 + kernel/kmod.c | 23 ++++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 26187f2..9efeae6 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -51,6 +51,7 @@ struct file; #define UMH_NO_WAIT 0 /* don't wait at all */ #define UMH_WAIT_EXEC 1 /* wait for the exec, but not the process */ #define UMH_WAIT_PROC 2 /* wait for the process to complete */ +#define UMH_KILLABLE 4 /* wait for EXEC/PROC killable */ struct subprocess_info { struct work_struct work; diff --git a/kernel/kmod.c b/kernel/kmod.c index 2443213..7452b4e 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -201,7 +201,12 @@ EXPORT_SYMBOL(call_usermodehelper_freeinfo); static void umh_complete(struct subprocess_info *sub_info) { - complete(sub_info->complete); + struct completion *done = xchg(&sub_info->complete, NULL); + + if (done) + complete(done); + else /* see call_usermodehelper_exec(), we own sub_info */ + call_usermodehelper_freeinfo(sub_info); } /* Keventd can't block, but this (a child) can. */ @@ -455,14 +460,26 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) } sub_info->complete = &done; - sub_info->wait = wait; + sub_info->wait = (wait & ~UMH_KILLABLE); queue_work(khelper_wq, &sub_info->work); if (wait == UMH_NO_WAIT) /* task has freed sub_info */ goto unlock; + + if (wait & UMH_KILLABLE) { + retval = wait_for_completion_killable(&done); + if (!retval) + goto wait_done; + + /* see umh_complete()->call_usermodehelper_freeinfo() */ + if (xchg(&sub_info->complete, NULL)) + goto unlock; + /* wait for umh_complete()->complete() in progress... */ + } + wait_for_completion(&done); +wait_done: retval = sub_info->retval; - out: call_usermodehelper_freeinfo(sub_info); unlock: -- 1.5.5.1