Skip to content

Commit

Permalink
kobject: don't block for each kobject_uevent
Browse files Browse the repository at this point in the history
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 commit f520360 ("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 in
05f54c1 ("Revert "kobject: don't block for each kobject_uevent".").
It targeted on speeding up the boot process though.

Signed-off-by: Vladimir Davydov <[email protected]>
Cc: Greg KH <[email protected]>
Cc: Christoph Lameter <[email protected]>
Cc: Pekka Enberg <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Vladimir Davydov authored and torvalds committed Apr 3, 2014
1 parent 5509a5d commit bcccff9
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 6 deletions.
1 change: 1 addition & 0 deletions include/linux/kobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
42 changes: 36 additions & 6 deletions lib/kobject_uevent.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -301,21 +325,27 @@ 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;
retval = add_uevent_var(env,
"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:
Expand Down

0 comments on commit bcccff9

Please sign in to comment.