diff options
author | Vladimir Davydov <vdavydov@parallels.com> | 2014-04-03 17:48:21 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-03 19:21:04 -0400 |
commit | bcccff93af359533683603255124fc19eb12613d (patch) | |
tree | ccba7107fcb9b08fb038af3b90d0ee213cbe3757 /lib | |
parent | 5509a5d27b971a90b940e148ca9ca53312e4fa7a (diff) |
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 commit f520360d93cd ("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
05f54c13cd0c ("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>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/kobject_uevent.c | 42 |
1 files changed, 36 insertions, 6 deletions
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 5f72767ddd9b..4e3bd71bd949 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c | |||
@@ -124,6 +124,30 @@ static int kobj_usermode_filter(struct kobject *kobj) | |||
124 | return 0; | 124 | return 0; |
125 | } | 125 | } |
126 | 126 | ||
127 | static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem) | ||
128 | { | ||
129 | int len; | ||
130 | |||
131 | len = strlcpy(&env->buf[env->buflen], subsystem, | ||
132 | sizeof(env->buf) - env->buflen); | ||
133 | if (len >= (sizeof(env->buf) - env->buflen)) { | ||
134 | WARN(1, KERN_ERR "init_uevent_argv: buffer size too small\n"); | ||
135 | return -ENOMEM; | ||
136 | } | ||
137 | |||
138 | env->argv[0] = uevent_helper; | ||
139 | env->argv[1] = &env->buf[env->buflen]; | ||
140 | env->argv[2] = NULL; | ||
141 | |||
142 | env->buflen += len + 1; | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static void cleanup_uevent_env(struct subprocess_info *info) | ||
147 | { | ||
148 | kfree(info->data); | ||
149 | } | ||
150 | |||
127 | /** | 151 | /** |
128 | * kobject_uevent_env - send an uevent with environmental data | 152 | * kobject_uevent_env - send an uevent with environmental data |
129 | * | 153 | * |
@@ -301,11 +325,8 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | |||
301 | 325 | ||
302 | /* call uevent_helper, usually only enabled during early boot */ | 326 | /* call uevent_helper, usually only enabled during early boot */ |
303 | if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { | 327 | if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { |
304 | char *argv [3]; | 328 | struct subprocess_info *info; |
305 | 329 | ||
306 | argv [0] = uevent_helper; | ||
307 | argv [1] = (char *)subsystem; | ||
308 | argv [2] = NULL; | ||
309 | retval = add_uevent_var(env, "HOME=/"); | 330 | retval = add_uevent_var(env, "HOME=/"); |
310 | if (retval) | 331 | if (retval) |
311 | goto exit; | 332 | goto exit; |
@@ -313,9 +334,18 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | |||
313 | "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); | 334 | "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); |
314 | if (retval) | 335 | if (retval) |
315 | goto exit; | 336 | goto exit; |
337 | retval = init_uevent_argv(env, subsystem); | ||
338 | if (retval) | ||
339 | goto exit; | ||
316 | 340 | ||
317 | retval = call_usermodehelper(argv[0], argv, | 341 | retval = -ENOMEM; |
318 | env->envp, UMH_WAIT_EXEC); | 342 | info = call_usermodehelper_setup(env->argv[0], env->argv, |
343 | env->envp, GFP_KERNEL, | ||
344 | NULL, cleanup_uevent_env, env); | ||
345 | if (info) { | ||
346 | retval = call_usermodehelper_exec(info, UMH_NO_WAIT); | ||
347 | env = NULL; /* freed by cleanup_uevent_env */ | ||
348 | } | ||
319 | } | 349 | } |
320 | 350 | ||
321 | exit: | 351 | exit: |