call_usermodehelper() has to synchronously wait until it has handed its local variables off to keventd. This can lead to deadlocks when the caller holds semphores which keventd may also want. To fix this we introduce call_usermodehelper_async(), which does not block on a keventd response. --- 25-akpm/include/linux/kmod.h | 5 ++- 25-akpm/kernel/kmod.c | 69 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 10 deletions(-) diff -puN include/linux/kmod.h~call_usermodehelper_async include/linux/kmod.h --- 25/include/linux/kmod.h~call_usermodehelper_async 2004-04-10 13:08:12.554227824 -0700 +++ 25-akpm/include/linux/kmod.h 2004-04-10 13:08:12.558227216 -0700 @@ -32,7 +32,10 @@ static inline int request_module(const c #endif #define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x))) -extern int call_usermodehelper(char *path, char *argv[], char *envp[], int wait); + +int call_usermodehelper(char *path, char **argv, char **envp, int wait); +int call_usermodehelper_async(char *path, char **argv, + char **envp, int gfp_flags); #ifdef CONFIG_HOTPLUG extern char hotplug_path []; diff -puN kernel/kmod.c~call_usermodehelper_async kernel/kmod.c --- 25/kernel/kmod.c~call_usermodehelper_async 2004-04-10 13:08:12.555227672 -0700 +++ 25-akpm/kernel/kmod.c 2004-04-10 13:08:12.559227064 -0700 @@ -109,6 +109,7 @@ int request_module(const char *fmt, ...) atomic_dec(&kmod_concurrent); return ret; } +EXPORT_SYMBOL(request_module); #endif /* CONFIG_KMOD */ #ifdef CONFIG_HOTPLUG @@ -140,7 +141,9 @@ struct subprocess_info { char **argv; char **envp; int wait; + int async; int retval; + struct work_struct async_work; }; /* @@ -197,6 +200,16 @@ static int wait_for_helper(void *data) return 0; } +static void destroy_subinfo(struct subprocess_info *sub_info) +{ + if (!sub_info) + return; + kfree_strvec(sub_info->argv); + kfree_strvec(sub_info->envp); + kfree(sub_info->path); + kfree(sub_info); +} + /* * This is run by keventd. */ @@ -215,11 +228,15 @@ static void __call_usermodehelper(void * pid = kernel_thread(____call_usermodehelper, sub_info, CLONE_VFORK | SIGCHLD); - if (pid < 0) { - sub_info->retval = pid; - complete(sub_info->complete); - } else if (!sub_info->wait) - complete(sub_info->complete); + if (sub_info->async) { + destroy_subinfo(sub_info); + } else { + if (pid < 0) { + sub_info->retval = pid; + complete(sub_info->complete); + } else if (!sub_info->wait) + complete(sub_info->complete); + } } /** @@ -265,10 +282,44 @@ int call_usermodehelper(char *path, char out: return sub_info.retval; } - EXPORT_SYMBOL(call_usermodehelper); -#ifdef CONFIG_KMOD -EXPORT_SYMBOL(request_module); -#endif +/** + * call_usermodehelper_async - start a usermode application + * + * Like call_usermodehelper(), except it is fully asynchronous. Should only + * be used in extremis, such as when the caller unavoidably holds locks which + * keventd might block on. + */ +int call_usermodehelper_async(char *path, char **argv, + char **envp, int gfp_flags) +{ + struct subprocess_info *sub_info; + + if (system_state != SYSTEM_RUNNING) + return -EBUSY; + if (path[0] == '\0') + goto out; + sub_info = kzmalloc(sizeof(*sub_info), gfp_flags); + if (!sub_info) + goto enomem; + sub_info->async = 1; + sub_info->path = kstrdup(path, gfp_flags); + if (!sub_info->path) + goto enomem; + sub_info->argv = kstrdup_vec(argv, gfp_flags); + if (!sub_info->argv) + goto enomem; + sub_info->envp = kstrdup_vec(envp, gfp_flags); + if (!sub_info->envp) + goto enomem; + INIT_WORK(&sub_info->async_work, __call_usermodehelper, sub_info); + schedule_work(&sub_info->async_work); +out: + return 0; +enomem: + destroy_subinfo(sub_info); + return -ENOMEM; +} +EXPORT_SYMBOL(call_usermodehelper_async); _