diff options
| author | Mark Brown <[email protected]> | 2015-10-12 18:09:27 +0100 | 
|---|---|---|
| committer | Mark Brown <[email protected]> | 2015-10-12 18:09:27 +0100 | 
| commit | 79828b4fa835f73cdaf4bffa48696abdcbea9d02 (patch) | |
| tree | 5e0fa7156acb75ba603022bc807df8f2fedb97a8 /kernel/kmod.c | |
| parent | 721b51fcf91898299d96f4b72cb9434cda29dce6 (diff) | |
| parent | 8c1a9d6323abf0fb1e5dad96cf3f1c783505ea5a (diff) | |
Merge remote-tracking branch 'asoc/fix/rt5645' into asoc-fix-rt5645
Diffstat (limited to 'kernel/kmod.c')
| -rw-r--r-- | kernel/kmod.c | 100 | 
1 files changed, 54 insertions, 46 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c index 2777f40a9c7b..da98d0593de2 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -45,8 +45,6 @@  extern int max_threads; -static struct workqueue_struct *khelper_wq; -  #define CAP_BSET	(void *)1  #define CAP_PI		(void *)2 @@ -114,10 +112,11 @@ out:   * @...: arguments as specified in the format string   *   * Load a module using the user mode module loader. The function returns - * zero on success or a negative errno code on failure. Note that a - * successful module load does not mean the module did not then unload - * and exit on an error of its own. Callers must check that the service - * they requested is now available not blindly invoke it. + * zero on success or a negative errno code or positive exit code from + * "modprobe" on failure. Note that a successful module load does not mean + * the module did not then unload and exit on an error of its own. Callers + * must check that the service they requested is now available not blindly + * invoke it.   *   * If module auto-loading support is disabled then this function   * becomes a no-operation. @@ -213,7 +212,7 @@ static void umh_complete(struct subprocess_info *sub_info)  /*   * This is the task which runs the usermode application   */ -static int ____call_usermodehelper(void *data) +static int call_usermodehelper_exec_async(void *data)  {  	struct subprocess_info *sub_info = data;  	struct cred *new; @@ -223,12 +222,9 @@ static int ____call_usermodehelper(void *data)  	flush_signal_handlers(current, 1);  	spin_unlock_irq(¤t->sighand->siglock); -	/* We can run anywhere, unlike our parent keventd(). */ -	set_cpus_allowed_ptr(current, cpu_all_mask); -  	/* -	 * Our parent is keventd, which runs with elevated scheduling priority. -	 * Avoid propagating that into the userspace child. +	 * Our parent (unbound workqueue) runs with elevated scheduling +	 * priority. Avoid propagating that into the userspace child.  	 */  	set_user_nice(current, 0); @@ -258,7 +254,10 @@ static int ____call_usermodehelper(void *data)  			   (const char __user *const __user *)sub_info->envp);  out:  	sub_info->retval = retval; -	/* wait_for_helper() will call umh_complete if UHM_WAIT_PROC. */ +	/* +	 * call_usermodehelper_exec_sync() will call umh_complete +	 * if UHM_WAIT_PROC. +	 */  	if (!(sub_info->wait & UMH_WAIT_PROC))  		umh_complete(sub_info);  	if (!retval) @@ -266,15 +265,14 @@ out:  	do_exit(0);  } -/* Keventd can't block, but this (a child) can. */ -static int wait_for_helper(void *data) +/* Handles UMH_WAIT_PROC.  */ +static void call_usermodehelper_exec_sync(struct subprocess_info *sub_info)  { -	struct subprocess_info *sub_info = data;  	pid_t pid;  	/* If SIGCLD is ignored sys_wait4 won't populate the status. */  	kernel_sigaction(SIGCHLD, SIG_DFL); -	pid = kernel_thread(____call_usermodehelper, sub_info, SIGCHLD); +	pid = kernel_thread(call_usermodehelper_exec_async, sub_info, SIGCHLD);  	if (pid < 0) {  		sub_info->retval = pid;  	} else { @@ -282,44 +280,60 @@ static int wait_for_helper(void *data)  		/*  		 * Normally it is bogus to call wait4() from in-kernel because  		 * wait4() wants to write the exit code to a userspace address. -		 * But wait_for_helper() always runs as keventd, and put_user() -		 * to a kernel address works OK for kernel threads, due to their -		 * having an mm_segment_t which spans the entire address space. +		 * But call_usermodehelper_exec_sync() always runs as kernel +		 * thread (workqueue) and put_user() to a kernel address works +		 * OK for kernel threads, due to their having an mm_segment_t +		 * which spans the entire address space.  		 *  		 * Thus the __user pointer cast is valid here.  		 */  		sys_wait4(pid, (int __user *)&ret, 0, NULL);  		/* -		 * If ret is 0, either ____call_usermodehelper failed and the -		 * real error code is already in sub_info->retval or +		 * If ret is 0, either call_usermodehelper_exec_async failed and +		 * the real error code is already in sub_info->retval or  		 * sub_info->retval is 0 anyway, so don't mess with it then.  		 */  		if (ret)  			sub_info->retval = ret;  	} +	/* Restore default kernel sig handler */ +	kernel_sigaction(SIGCHLD, SIG_IGN); +  	umh_complete(sub_info); -	do_exit(0);  } -/* This is run by khelper thread  */ -static void __call_usermodehelper(struct work_struct *work) +/* + * We need to create the usermodehelper kernel thread from a task that is affine + * to an optimized set of CPUs (or nohz housekeeping ones) such that they + * inherit a widest affinity irrespective of call_usermodehelper() callers with + * possibly reduced affinity (eg: per-cpu workqueues). We don't want + * usermodehelper targets to contend a busy CPU. + * + * Unbound workqueues provide such wide affinity and allow to block on + * UMH_WAIT_PROC requests without blocking pending request (up to some limit). + * + * Besides, workqueues provide the privilege level that caller might not have + * to perform the usermodehelper request. + * + */ +static void call_usermodehelper_exec_work(struct work_struct *work)  {  	struct subprocess_info *sub_info =  		container_of(work, struct subprocess_info, work); -	pid_t pid; -	if (sub_info->wait & UMH_WAIT_PROC) -		pid = kernel_thread(wait_for_helper, sub_info, -				    CLONE_FS | CLONE_FILES | SIGCHLD); -	else -		pid = kernel_thread(____call_usermodehelper, sub_info, -				    SIGCHLD); +	if (sub_info->wait & UMH_WAIT_PROC) { +		call_usermodehelper_exec_sync(sub_info); +	} else { +		pid_t pid; -	if (pid < 0) { -		sub_info->retval = pid; -		umh_complete(sub_info); +		pid = kernel_thread(call_usermodehelper_exec_async, sub_info, +				    SIGCHLD); +		if (pid < 0) { +			sub_info->retval = pid; +			umh_complete(sub_info); +		}  	}  } @@ -509,7 +523,7 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,  	if (!sub_info)  		goto out; -	INIT_WORK(&sub_info->work, __call_usermodehelper); +	INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);  	sub_info->path = path;  	sub_info->argv = argv;  	sub_info->envp = envp; @@ -531,8 +545,8 @@ EXPORT_SYMBOL(call_usermodehelper_setup);   *        from interrupt context.   *   * Runs a user-space application.  The application is started - * asynchronously if wait is not set, and runs as a child of keventd. - * (ie. it runs with full root capabilities). + * asynchronously if wait is not set, and runs as a child of system workqueues. + * (ie. it runs with full root capabilities and optimized affinity).   */  int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)  { @@ -544,7 +558,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)  		return -EINVAL;  	}  	helper_lock(); -	if (!khelper_wq || usermodehelper_disabled) { +	if (usermodehelper_disabled) {  		retval = -EBUSY;  		goto out;  	} @@ -556,7 +570,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)  	sub_info->complete = (wait == UMH_NO_WAIT) ? NULL : &done;  	sub_info->wait = wait; -	queue_work(khelper_wq, &sub_info->work); +	queue_work(system_unbound_wq, &sub_info->work);  	if (wait == UMH_NO_WAIT)	/* task has freed sub_info */  		goto unlock; @@ -686,9 +700,3 @@ struct ctl_table usermodehelper_table[] = {  	},  	{ }  }; - -void __init usermodehelper_init(void) -{ -	khelper_wq = create_singlethread_workqueue("khelper"); -	BUG_ON(!khelper_wq); -}  |