kmod: fix race in usermodehelper code
[cascardo/linux.git] / kernel / kmod.c
index 4e8cae2..689d20f 100644 (file)
@@ -37,6 +37,8 @@
 #include <linux/suspend.h>
 #include <asm/uaccess.h>
 
+#include <trace/events/module.h>
+
 extern int max_threads;
 
 static struct workqueue_struct *khelper_wq;
@@ -112,6 +114,8 @@ int __request_module(bool wait, const char *fmt, ...)
                return -ENOMEM;
        }
 
+       trace_module_request(module_name, wait, _RET_IP_);
+
        ret = call_usermodehelper(modprobe_path, argv, envp,
                        wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
        atomic_dec(&kmod_concurrent);
@@ -139,6 +143,7 @@ struct subprocess_info {
 static int ____call_usermodehelper(void *data)
 {
        struct subprocess_info *sub_info = data;
+       enum umh_wait wait = sub_info->wait;
        int retval;
 
        BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
@@ -180,10 +185,14 @@ static int ____call_usermodehelper(void *data)
         */
        set_user_nice(current, 0);
 
+       if (wait == UMH_WAIT_EXEC)
+               complete(sub_info->complete);
+
        retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp);
 
        /* Exec failed? */
-       sub_info->retval = retval;
+       if (wait != UMH_WAIT_EXEC)
+               sub_info->retval = retval;
        do_exit(0);
 }
 
@@ -262,16 +271,14 @@ static void __call_usermodehelper(struct work_struct *work)
 
        switch (wait) {
        case UMH_NO_WAIT:
+       case UMH_WAIT_EXEC:
                break;
 
        case UMH_WAIT_PROC:
                if (pid > 0)
                        break;
                sub_info->retval = pid;
-               /* FALLTHROUGH */
-
-       case UMH_WAIT_EXEC:
-               complete(sub_info->complete);
+               break;
        }
 }