make request_module() killable
Subsystems (8)
RFC PATCH 1/1 make request_module() killable
In reply to a message not in the archiveQuoted 2 lines · Oleg Nesterov
OK. I am convinced we need this ;) Thanks Tetsuo.
So. Please see the attachments. COMPLETELY UNTESTED, only for review/discussion.
For those who were not CC'ed. As Tetsuo pointed out, request_module() is very much unfriendly wrt OOM. It needs "a lot" of time/memory to finish while the caller is blocked in TASK_UNINTERRUPTIBLE.
0001-usermodehelper-kill-umh_wait-renumber-UMH_-consta.patch 0002-usermodehelper-introduce-umh_complete-sub_info.patch 0003-usermodehelper-implement-UMH_KILLABLE.patch
this adds UMH_KILLABLE,
0004-kmod-introduce-call_modprobe-helper.patch 0005-kmod-make-__request_module-killable.patch
this changes __request_module() to use UMH_KILLABLE.
Perhaps we should also mark the modprobe process as OOM_ADJUST_MAX.
What do you all think?
Oleg.
include/linux/kmod.h | 18 ++++----- kernel/kmod.c | 81 +++++++++++++++++++++++++++++++++++-------- security/keys/request_key.c | 2 +- 3 files changed, 75 insertions(+), 26 deletions(-)
Quoted 1 line
From
03940f43d03efc0354c2e591f93cfaf12cc38c71Mon Sep 17 00:00:00 2001
From: Oleg Nesterov <[email protected]>
Date: Tue, 7 Feb 2012 16:25:34 +0100
Subject: [PATCH 1/5] usermodehelper: kill umh_wait, renumber UMH_* constants
No functional changes, preparation.
umh_wait doesn't can't be used as a bitmask, and we do not want to add another argument call_usermodehelper_* helpers.
Kill this enum and redefine the UMH_* constants.
--- include/linux/kmod.h | 17 +++++++---------- kernel/kmod.c | 5 ++--- security/keys/request_key.c | 2 +- 3 files changed, 10 insertions(+), 14 deletions(-)
include/linux/kmod.h +7 -10
@@ -48,11 +48,9 @@ static inline int request_module_nowait(const char *name, ...) { return -ENOSYS;struct cred ;struct file ; -enum umh_wait { -UMH_NO_WAIT =-1 ,/* don't wait at all */ -UMH_WAIT_EXEC =0 ,/* wait for the exec, but not the process */ -UMH_WAIT_PROC =1 ,/* wait for the process to complete */ -}; +#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 */ struct subprocess_info {struct work_struct work;
@@ -60,7 +58,7 @@ struct subprocess_info {char *path;char **argv;char **envp; -enum umh_wait wait; +int wait;int retval;int (*init)(struct subprocess_info *info,struct cred *new);void (*cleanup)(struct subprocess_info *info);
@@ -78,15 +76,14 @@ void call_usermodehelper_setfns(struct subprocess_info *info, void *data);/* Actually execute the sub-process */ -int call_usermodehelper_exec (struct subprocess_info *info,enum umh_wait wait); +int call_usermodehelper_exec (struct subprocess_info *info,int wait);/* Free the subprocess_info. This is only needed if you're not going to call call_usermodehelper_exec */ void call_usermodehelper_freeinfo (struct subprocess_info *info);static inline int -call_usermodehelper_fns (char *path,char **argv,char **envp, -enum umh_wait wait, +call_usermodehelper_fns (char *path,char **argv,char **envp,int wait,int (*init)(struct subprocess_info *info,struct cred *new),void (*cleanup)(struct subprocess_info *),void *data) {
@@ -104,7 +101,7 @@ call_usermodehelper_fns(char *path, char **argv, char **envp, }static inline int -call_usermodehelper (char *path,char **argv,char **envp,enum umh_wait wait) +call_usermodehelper (char *path,char **argv,char **envp,int wait) {return call_usermodehelper_fns (path, argv, envp, wait,NULL ,NULL ,NULL );
kernel/kmod.c +2 -3
@@ -244,7 +244,7 @@ static void __call_usermodehelper(struct work_struct *work) {struct subprocess_info *sub_info = container_of(work, struct subprocess_info, work); -enum umh_wait wait = sub_info->wait; +int wait = sub_info->wait;pid_t pid;/* CLONE_VFORK: wait until the usermode helper has execve'd
@@ -435,8 +435,7 @@ EXPORT_SYMBOL(call_usermodehelper_setfns);* asynchronously if wait is not set, and runs as a child of keventd. * (ie. it runs with full root capabilities). */ -int call_usermodehelper_exec (struct subprocess_info *sub_info, -enum umh_wait wait) +int call_usermodehelper_exec (struct subprocess_info *sub_info,int wait) {DECLARE_COMPLETION_ONSTACK (done);int retval =0 ;
security/keys/request_key.c +1 -1
@@ -91,7 +91,7 @@ static void umh_keys_cleanup(struct subprocess_info *info)* Call a usermode helper with a specific session keyring. */ static int call_usermodehelper_keys (char *path,char **argv,char **envp, -struct key *session_keyring,enum umh_wait wait) +struct key *session_keyring,int wait) {gfp_t gfp_mask = (wait ==UMH_NO_WAIT ) ?GFP_ATOMIC :GFP_KERNEL ; struct subprocess_info *info =
Signature · 1 line
1.5.5.1
Quoted 1 line
From
0ef1c578505d765e1a3fd07c1676027c5b205cfbMon Sep 17 00:00:00 2001
From: Oleg Nesterov <[email protected]>
Date: Tue, 7 Feb 2012 17:09:07 +0100
Subject: [PATCH 2/5] usermodehelper: introduce umh_complete(sub_info)
Preparation. Add the new trivial helper, umh_complete(). Currently it simply does complete(sub_info->complete).
--- kernel/kmod.c | 9 +++++++-- 1 files changed, 7 insertions(+), 2 deletions(-)
kernel/kmod.c +7 -2
@@ -199,6 +199,11 @@ void call_usermodehelper_freeinfo(struct subprocess_info *info) }EXPORT_SYMBOL (call_usermodehelper_freeinfo); +static void umh_complete (struct subprocess_info *sub_info) +{ +complete (sub_info->complete); +} +/* Keventd can't block, but this (a child) can. */ static int wait_for_helper (void *data) {
@@ -235,7 +240,7 @@ static int wait_for_helper(void *data) sub_info->retval = ret; } -complete (sub_info->complete); +umh_complete (sub_info);return 0 ; }
@@ -269,7 +274,7 @@ static void __call_usermodehelper(struct work_struct *work)case UMH_WAIT_EXEC :if (pid <0 ) sub_info->retval = pid; -complete (sub_info->complete); +umh_complete (sub_info); } }
Signature · 1 line
1.5.5.1
Quoted 1 line
From
4ab2af61fc601b550b4e59dc6fa2bd01375920f0Mon Sep 17 00:00:00 2001
From: Oleg Nesterov <[email protected]>
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(-)
include/linux/kmod.h +1 -0
@@ -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;
kernel/kmod.c +20 -3
@@ -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:
Signature · 1 line
1.5.5.1
Quoted 1 line
From
4e2347777f969bc4e8ba960d31fcff34b38ad318Mon Sep 17 00:00:00 2001
From: Oleg Nesterov <[email protected]>
Date: Tue, 7 Feb 2012 18:35:33 +0100
Subject: [PATCH 4/5] kmod: introduce call_modprobe() helper
No functional changes. Move the call_usermodehelper code from __request_module() into the new simple helper, call_modprobe().
--- kernel/kmod.c | 24 ++++++++++++++++-------- 1 files changed, 16 insertions(+), 8 deletions(-)
kernel/kmod.c +16 -8
@@ -60,6 +60,21 @@ static DECLARE_RWSEM(umhelper_sem);*/ char modprobe_path[KMOD_PATH_LEN ] ="/sbin/modprobe" ; +static int call_modprobe (char *module_name,int wait) +{ +static char *envp[] = { +"HOME=/" , +"TERM=linux" , +"PATH=/sbin:/usr/sbin:/bin:/usr/bin" , +NULL + }; + +char *argv[] = { modprobe_path,"-q" ,"--" , module_name,NULL }; + +return call_usermodehelper_fns (modprobe_path, argv, envp, + wait,NULL ,NULL ,NULL ); +} +/** * __request_module - try to load a kernel module * @wait: wait (or not) for the operation to complete
@@ -81,11 +96,6 @@ int __request_module(bool wait, const char *fmt, ...)char module_name[MODULE_NAME_LEN ];unsigned int max_modprobes;int ret; -char *argv[] = { modprobe_path,"-q" ,"--" , module_name,NULL }; -static char *envp[] = {"HOME=/" , -"TERM=linux" , -"PATH=/sbin:/usr/sbin:/bin:/usr/bin" , -NULL };static atomic_t kmod_concurrent =ATOMIC_INIT (0 );#define MAX_KMOD_CONCURRENT 50/* Completely arbitrary value - KAO */ static int kmod_loop_msg;
@@ -128,9 +138,7 @@ int __request_module(bool wait, const char *fmt, ...)trace_module_request (module_name, wait, _RET_IP_); - ret =call_usermodehelper_fns (modprobe_path, argv, envp, - wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC , -NULL ,NULL ,NULL ); + ret =call_modprobe (module_name, wait ?UMH_WAIT_PROC :UMH_WAIT_EXEC );atomic_dec (&kmod_concurrent);return ret;
Signature · 1 line
1.5.5.1
Quoted 1 line
From
cae641ffc528461798fe3698d0d327bb7048f274Mon Sep 17 00:00:00 2001
From: Oleg Nesterov <[email protected]>
Date: Tue, 7 Feb 2012 19:24:49 +0100
Subject: [PATCH 5/5] kmod: make __request_module() killable
Make __request_module() killable. The only necessary change is that call_modprobe() should kmalloc argv and module_name, they can't live in the stack if we use UMH_KILLABLE.
This memory is freed via call_usermodehelper_freeinfo()->cleanup.
--- kernel/kmod.c | 26 ++++++++++++++++++++++++-- 1 files changed, 24 insertions(+), 2 deletions(-)
kernel/kmod.c +24 -2
@@ -60,6 +60,12 @@ static DECLARE_RWSEM(umhelper_sem);*/ char modprobe_path[KMOD_PATH_LEN ] ="/sbin/modprobe" ; +static void free_modprobe_argv (struct subprocess_info *info) +{ +kfree (info->argv[3 ]);/* check call_modprobe() */ +kfree (info->argv); +} +static int call_modprobe (char *module_name,int wait) {static char *envp[] = {
@@ -69,10 +75,26 @@ static int call_modprobe(char *module_name, int wait) NULL }; -char *argv[] = { modprobe_path,"-q" ,"--" , module_name,NULL }; +char **argv =kmalloc (sizeof (char *[5 ]),GFP_KERNEL ); +if (!argv) + goto out; + + module_name =kstrdup (module_name,GFP_KERNEL ); +if (!module_name) + goto free_argv; + + argv[0 ] = modprobe_path; + argv[1 ] ="-q" ; + argv[2 ] ="--" ; + argv[3 ] = module_name;/* check free_modprobe_argv() */ + argv[4 ] =NULL ;return call_usermodehelper_fns (modprobe_path, argv, envp, - wait,NULL ,NULL ,NULL ); + wait |UMH_KILLABLE ,NULL , free_modprobe_argv,NULL ); +free_argv: +kfree (argv); +out: +return -ENOMEM ; }/**
Signature · 1 line
1.5.5.1
cover usermodehelper: use UMH_WAIT_PROC consistently
In reply to a message not in the archiveQuoted 7 lines · Tetsuo Handa
Quoted 4 lines · Oleg Nesterov
Subject: [PATCH 1/5] usermodehelper: kill
umh_wait, renumber UMH_* constantsNo functional changes, preparation.
Quoted 7 lines
Quoted 4 lines
umh_waitdoesn't can't be used as a bitmask, and we do not want to add another argument call_usermodehelper_* helpers.Kill this enum and redefine the UMH_* constants.
There are several users that call
call_usermodehelper()without using this enum.security/tomoyo/load_policy.cis one of such users
... and there are more.
Thanks a lot Tetsuo.
Quoted 2 lines
and there will be some
in out-of-tree code.
Well, they should be fixed imho.
Quoted 3 lines
Since I think distro kernels want
to backport this killablerequest_module()patchset, this patch should not change the constants in a way that makes impossible to detect kABI changes.
Good point, but I think the backporter should take care to not break the KABI. I do not think we should defer the possible cleanups for the sake of backporting.
It is possible to skip the first patch and add UMH_KILLABLE, just call_usermodehelper_exec() needs the additional wait != UMH_NO_WAIT check before testind/clearing UMH_KILLABLE. UMH_NO_WAIT | KILLABLE is not possible.
Quoted 7 lines
#define
UMH_WAIT_EXEC0 /* wait for the exec, but not the process */
#defineUMH_WAIT_PROC1 /* wait for the process to complete */
#defineUMH_KILLABLE2 /* wait for EXEC/PROC killable */
#defineUMH_NO_WAIT4 /* don't wait at all */might be acceptable because we can remap -1 ->
UMH_NO_WAIToncall_usermodehelper_exec()side.
Yes... but I think it is better to fix the users.
Perhaps we can do something like
#define UMH_NO_WAIT 0x8000 #define UMH_WAIT_EXEC 0x8001 #define UMH_WAIT_ 0x8002
and then call_usermodehelper_exec() does
/* catch the old buggy users */ BUG_ON(!(wait & 0x8000) || (wait < 0))
In any case. Whatever we do, I think we should fix the current ab-users.
Andrew, could you take this patch? Or should I split this trivial change by file/maintainer?
Oleg.
PATCH 1/1 usermodehelper: use UMH_WAIT_PROC consistently
A few call_usermodehelper() callers use the hardcoded constant instead of the proper UMH_WAIT_PROC, fix them.
--- drivers/block/drbd/drbd_nl.c | 2 +- drivers/staging/rtl8187se/r8180_core.c | 2 +- drivers/staging/rtl8192e/rtl8192e/rtl_dm.c | 4 ++-- drivers/video/uvesafb.c | 2 +- security/tomoyo/load_policy.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-)
drivers/block/drbd/drbd_nl.c +1 -1
@@ -179,7 +179,7 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd)dev_info (DEV ,"helper command: %s %s %s\n" , usermode_helper, cmd, mb);drbd_bcast_ev_helper (mdev, cmd); - ret =call_usermodehelper (usermode_helper, argv, envp,1 ); + ret =call_usermodehelper (usermode_helper, argv, envp,UMH_WAIT_PROC );if (ret) dev_warn(DEV, "helper command: %s %s %s exit code %u (0x%x)\n", usermode_helper, cmd, mb,
drivers/staging/rtl8187se/r8180_core.c +1 -1
@@ -4144,7 +4144,7 @@ void GPIOChangeRFWorkItemCallBack(struct work_struct *work) argv[0 ] = RadioPowerPath; argv[2 ] =NULL ; -call_usermodehelper (RadioPowerPath, argv, envp,1 ); +call_usermodehelper (RadioPowerPath, argv, envp,UMH_WAIT_PROC ); } }
drivers/staging/rtl8192e/rtl8192e/rtl_dm.c +2 -2
@@ -208,7 +208,7 @@ static void dm_check_ac_dc_power(struct net_device *dev)if (priv->rtllib->state !=RTLLIB_LINKED )return ; -call_usermodehelper (ac_dc_check_script_path, argv, envp,1 ); +call_usermodehelper (ac_dc_check_script_path, argv, envp,UMH_WAIT_PROC );return ; };
@@ -2296,7 +2296,7 @@ void dm_CheckRfCtrlGPIO(void *data) argv[0 ] = RadioPowerPath; argv[2 ] =NULL ; -call_usermodehelper (RadioPowerPath, argv, envp,1 ); +call_usermodehelper (RadioPowerPath, argv, envp,UMH_WAIT_PROC ); } }
drivers/video/uvesafb.c +1 -1
@@ -121,7 +121,7 @@ static int uvesafb_helper_start(void) NULL, }; -return call_usermodehelper (v86d_path, argv, envp,1 ); +return call_usermodehelper (v86d_path, argv, envp,UMH_WAIT_PROC ); }/*
security/tomoyo/load_policy.c +1 -1
@@ -102,7 +102,7 @@ void tomoyo_load_policy(const char *filename) envp[0 ] ="HOME=/" ; envp[1 ] ="PATH=/sbin:/bin:/usr/sbin:/usr/bin" ; envp[2 ] =NULL ; -call_usermodehelper (argv[0 ], argv, envp,1 ); +call_usermodehelper (argv[0 ], argv, envp,UMH_WAIT_PROC );tomoyo_check_profile (); }
Signature · 1 line
1.5.5.1