diff options
Diffstat (limited to 'kernel/kmod.c')
| -rw-r--r-- | kernel/kmod.c | 76 | 
1 files changed, 37 insertions, 39 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c index 8637e041a247..80f7a6d00519 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -196,12 +196,34 @@ int __request_module(bool wait, const char *fmt, ...)  EXPORT_SYMBOL(__request_module);  #endif /* CONFIG_MODULES */ +static void call_usermodehelper_freeinfo(struct subprocess_info *info) +{ +	if (info->cleanup) +		(*info->cleanup)(info); +	kfree(info); +} + +static void umh_complete(struct subprocess_info *sub_info) +{ +	struct completion *comp = xchg(&sub_info->complete, NULL); +	/* +	 * See call_usermodehelper_exec(). If xchg() returns NULL +	 * we own sub_info, the UMH_KILLABLE caller has gone away +	 * or the caller used UMH_NO_WAIT. +	 */ +	if (comp) +		complete(comp); +	else +		call_usermodehelper_freeinfo(sub_info); +} +  /*   * This is the task which runs the usermode application   */  static int ____call_usermodehelper(void *data)  {  	struct subprocess_info *sub_info = data; +	int wait = sub_info->wait & ~UMH_KILLABLE;  	struct cred *new;  	int retval; @@ -221,7 +243,7 @@ static int ____call_usermodehelper(void *data)  	retval = -ENOMEM;  	new = prepare_kernel_cred(current);  	if (!new) -		goto fail; +		goto out;  	spin_lock(&umh_sysctl_lock);  	new->cap_bset = cap_intersect(usermodehelper_bset, new->cap_bset); @@ -233,7 +255,7 @@ static int ____call_usermodehelper(void *data)  		retval = sub_info->init(sub_info, new);  		if (retval) {  			abort_creds(new); -			goto fail; +			goto out;  		}  	} @@ -242,12 +264,13 @@ static int ____call_usermodehelper(void *data)  	retval = do_execve(getname_kernel(sub_info->path),  			   (const char __user *const __user *)sub_info->argv,  			   (const char __user *const __user *)sub_info->envp); +out: +	sub_info->retval = retval; +	/* wait_for_helper() will call umh_complete if UHM_WAIT_PROC. */ +	if (wait != UMH_WAIT_PROC) +		umh_complete(sub_info);  	if (!retval)  		return 0; - -	/* Exec failed? */ -fail: -	sub_info->retval = retval;  	do_exit(0);  } @@ -258,26 +281,6 @@ static int call_helper(void *data)  	return ____call_usermodehelper(data);  } -static void call_usermodehelper_freeinfo(struct subprocess_info *info) -{ -	if (info->cleanup) -		(*info->cleanup)(info); -	kfree(info); -} - -static void umh_complete(struct subprocess_info *sub_info) -{ -	struct completion *comp = xchg(&sub_info->complete, NULL); -	/* -	 * See call_usermodehelper_exec(). If xchg() returns NULL -	 * we own sub_info, the UMH_KILLABLE caller has gone away. -	 */ -	if (comp) -		complete(comp); -	else -		call_usermodehelper_freeinfo(sub_info); -} -  /* Keventd can't block, but this (a child) can. */  static int wait_for_helper(void *data)  { @@ -336,18 +339,8 @@ static void __call_usermodehelper(struct work_struct *work)  		kmod_thread_locker = NULL;  	} -	switch (wait) { -	case UMH_NO_WAIT: -		call_usermodehelper_freeinfo(sub_info); -		break; - -	case UMH_WAIT_PROC: -		if (pid > 0) -			break; -		/* FALLTHROUGH */ -	case UMH_WAIT_EXEC: -		if (pid < 0) -			sub_info->retval = pid; +	if (pid < 0) { +		sub_info->retval = pid;  		umh_complete(sub_info);  	}  } @@ -588,7 +581,12 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)  		goto out;  	} -	sub_info->complete = &done; +	/* +	 * Set the completion pointer only if there is a waiter. +	 * This makes it possible to use umh_complete to free +	 * the data structure in case of UMH_NO_WAIT. +	 */ +	sub_info->complete = (wait == UMH_NO_WAIT) ? NULL : &done;  	sub_info->wait = wait;  	queue_work(khelper_wq, &sub_info->work);  | 
