kobject: don't block for each kobject_uevent
Currently kobject_uevent has somewhat unpredictable semantics. The point is, since it may call a usermode helper and wait for it to execute (UMH_WAIT_EXEC), it is impossible to say for sure what lock dependencies it will introduce for the caller - strictly speaking it depends on what fs the binary is located on and the set of locks fork may take. There are quite a few kobject_uevent's users that do not take this into account and call it with various mutexes taken, e.g. rtnl_mutex, net_mutex, which might potentially lead to a deadlock. Since there is actually no reason to wait for the usermode helper to execute there, let's make kobject_uevent start the helper asynchronously with the aid of the UMH_NO_WAIT flag. Personally, I'm interested in this, because I really want kobject_uevent to be called under the slab_mutex in the slub implementation as it used to be some time ago, because it greatly simplifies synchronization and automatically fixes a kmemcg-related race. However, there was a deadlock detected on an attempt to call kobject_uevent under the slab_mutex (see https://lkml.org/lkml/2012/1/14/45), which was reported to be fixed by releasing the slab_mutex for kobject_uevent. Unfortunately, there was no information about who exactly blocked on the slab_mutex causing the usermode helper to stall, neither have I managed to find this out or reproduce the issue. BTW, this is not the first attempt to make kobject_uevent use UMH_NO_WAIT. Previous one was made by commitf520360d93
("kobject: don't block for each kobject_uevent"), but it was wrong (it passed arguments allocated on stack to async thread) so it was reverted in05f54c13cd
("Revert "kobject: don't block for each kobject_uevent"."). It targeted on speeding up the boot process though. Signed-off-by: Vladimir Davydov <vdavydov@parallels.com> Cc: Greg KH <greg@kroah.com> Cc: Christoph Lameter <cl@linux.com> Cc: Pekka Enberg <penberg@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
5509a5d27b
commit
bcccff93af
|
@ -119,6 +119,7 @@ struct kobj_type {
|
|||
};
|
||||
|
||||
struct kobj_uevent_env {
|
||||
char *argv[3];
|
||||
char *envp[UEVENT_NUM_ENVP];
|
||||
int envp_idx;
|
||||
char buf[UEVENT_BUFFER_SIZE];
|
||||
|
|
|
@ -124,6 +124,30 @@ static int kobj_usermode_filter(struct kobject *kobj)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = strlcpy(&env->buf[env->buflen], subsystem,
|
||||
sizeof(env->buf) - env->buflen);
|
||||
if (len >= (sizeof(env->buf) - env->buflen)) {
|
||||
WARN(1, KERN_ERR "init_uevent_argv: buffer size too small\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
env->argv[0] = uevent_helper;
|
||||
env->argv[1] = &env->buf[env->buflen];
|
||||
env->argv[2] = NULL;
|
||||
|
||||
env->buflen += len + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cleanup_uevent_env(struct subprocess_info *info)
|
||||
{
|
||||
kfree(info->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* kobject_uevent_env - send an uevent with environmental data
|
||||
*
|
||||
|
@ -301,11 +325,8 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
|
|||
|
||||
/* call uevent_helper, usually only enabled during early boot */
|
||||
if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
|
||||
char *argv [3];
|
||||
struct subprocess_info *info;
|
||||
|
||||
argv [0] = uevent_helper;
|
||||
argv [1] = (char *)subsystem;
|
||||
argv [2] = NULL;
|
||||
retval = add_uevent_var(env, "HOME=/");
|
||||
if (retval)
|
||||
goto exit;
|
||||
|
@ -313,9 +334,18 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
|
|||
"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
|
||||
if (retval)
|
||||
goto exit;
|
||||
retval = init_uevent_argv(env, subsystem);
|
||||
if (retval)
|
||||
goto exit;
|
||||
|
||||
retval = call_usermodehelper(argv[0], argv,
|
||||
env->envp, UMH_WAIT_EXEC);
|
||||
retval = -ENOMEM;
|
||||
info = call_usermodehelper_setup(env->argv[0], env->argv,
|
||||
env->envp, GFP_KERNEL,
|
||||
NULL, cleanup_uevent_env, env);
|
||||
if (info) {
|
||||
retval = call_usermodehelper_exec(info, UMH_NO_WAIT);
|
||||
env = NULL; /* freed by cleanup_uevent_env */
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
|
|
Loading…
Reference in New Issue