diff options
-rw-r--r-- | kernel/kmod.c | 78 |
1 files changed, 68 insertions, 10 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c index 928f3678142a..beedbdc64608 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c | |||
@@ -41,14 +41,6 @@ extern int max_threads; | |||
41 | 41 | ||
42 | static struct workqueue_struct *khelper_wq; | 42 | static struct workqueue_struct *khelper_wq; |
43 | 43 | ||
44 | /* | ||
45 | * If set, both call_usermodehelper_keys() and call_usermodehelper_pipe() exit | ||
46 | * immediately returning -EBUSY. Used for preventing user land processes from | ||
47 | * being created after the user land has been frozen during a system-wide | ||
48 | * hibernation or suspend operation. | ||
49 | */ | ||
50 | static int usermodehelper_disabled; | ||
51 | |||
52 | #ifdef CONFIG_KMOD | 44 | #ifdef CONFIG_KMOD |
53 | 45 | ||
54 | /* | 46 | /* |
@@ -275,15 +267,55 @@ static void __call_usermodehelper(struct work_struct *work) | |||
275 | } | 267 | } |
276 | } | 268 | } |
277 | 269 | ||
270 | #ifdef CONFIG_PM | ||
271 | /* | ||
272 | * If set, call_usermodehelper_exec() will exit immediately returning -EBUSY | ||
273 | * (used for preventing user land processes from being created after the user | ||
274 | * land has been frozen during a system-wide hibernation or suspend operation). | ||
275 | */ | ||
276 | static int usermodehelper_disabled; | ||
277 | |||
278 | /* Number of helpers running */ | ||
279 | static atomic_t running_helpers = ATOMIC_INIT(0); | ||
280 | |||
281 | /* | ||
282 | * Wait queue head used by usermodehelper_pm_callback() to wait for all running | ||
283 | * helpers to finish. | ||
284 | */ | ||
285 | static DECLARE_WAIT_QUEUE_HEAD(running_helpers_waitq); | ||
286 | |||
287 | /* | ||
288 | * Time to wait for running_helpers to become zero before the setting of | ||
289 | * usermodehelper_disabled in usermodehelper_pm_callback() fails | ||
290 | */ | ||
291 | #define RUNNING_HELPERS_TIMEOUT (5 * HZ) | ||
292 | |||
278 | static int usermodehelper_pm_callback(struct notifier_block *nfb, | 293 | static int usermodehelper_pm_callback(struct notifier_block *nfb, |
279 | unsigned long action, | 294 | unsigned long action, |
280 | void *ignored) | 295 | void *ignored) |
281 | { | 296 | { |
297 | long retval; | ||
298 | |||
282 | switch (action) { | 299 | switch (action) { |
283 | case PM_HIBERNATION_PREPARE: | 300 | case PM_HIBERNATION_PREPARE: |
284 | case PM_SUSPEND_PREPARE: | 301 | case PM_SUSPEND_PREPARE: |
285 | usermodehelper_disabled = 1; | 302 | usermodehelper_disabled = 1; |
286 | return NOTIFY_OK; | 303 | smp_mb(); |
304 | /* | ||
305 | * From now on call_usermodehelper_exec() won't start any new | ||
306 | * helpers, so it is sufficient if running_helpers turns out to | ||
307 | * be zero at one point (it may be increased later, but that | ||
308 | * doesn't matter). | ||
309 | */ | ||
310 | retval = wait_event_timeout(running_helpers_waitq, | ||
311 | atomic_read(&running_helpers) == 0, | ||
312 | RUNNING_HELPERS_TIMEOUT); | ||
313 | if (retval) { | ||
314 | return NOTIFY_OK; | ||
315 | } else { | ||
316 | usermodehelper_disabled = 0; | ||
317 | return NOTIFY_BAD; | ||
318 | } | ||
287 | case PM_POST_HIBERNATION: | 319 | case PM_POST_HIBERNATION: |
288 | case PM_POST_SUSPEND: | 320 | case PM_POST_SUSPEND: |
289 | usermodehelper_disabled = 0; | 321 | usermodehelper_disabled = 0; |
@@ -293,6 +325,30 @@ static int usermodehelper_pm_callback(struct notifier_block *nfb, | |||
293 | return NOTIFY_DONE; | 325 | return NOTIFY_DONE; |
294 | } | 326 | } |
295 | 327 | ||
328 | static void helper_lock(void) | ||
329 | { | ||
330 | atomic_inc(&running_helpers); | ||
331 | smp_mb__after_atomic_inc(); | ||
332 | } | ||
333 | |||
334 | static void helper_unlock(void) | ||
335 | { | ||
336 | if (atomic_dec_and_test(&running_helpers)) | ||
337 | wake_up(&running_helpers_waitq); | ||
338 | } | ||
339 | |||
340 | static void register_pm_notifier_callback(void) | ||
341 | { | ||
342 | pm_notifier(usermodehelper_pm_callback, 0); | ||
343 | } | ||
344 | #else /* CONFIG_PM */ | ||
345 | #define usermodehelper_disabled 0 | ||
346 | |||
347 | static inline void helper_lock(void) {} | ||
348 | static inline void helper_unlock(void) {} | ||
349 | static inline void register_pm_notifier_callback(void) {} | ||
350 | #endif /* CONFIG_PM */ | ||
351 | |||
296 | /** | 352 | /** |
297 | * call_usermodehelper_setup - prepare to call a usermode helper | 353 | * call_usermodehelper_setup - prepare to call a usermode helper |
298 | * @path - path to usermode executable | 354 | * @path - path to usermode executable |
@@ -397,6 +453,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, | |||
397 | DECLARE_COMPLETION_ONSTACK(done); | 453 | DECLARE_COMPLETION_ONSTACK(done); |
398 | int retval; | 454 | int retval; |
399 | 455 | ||
456 | helper_lock(); | ||
400 | if (sub_info->path[0] == '\0') { | 457 | if (sub_info->path[0] == '\0') { |
401 | retval = 0; | 458 | retval = 0; |
402 | goto out; | 459 | goto out; |
@@ -418,6 +475,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, | |||
418 | 475 | ||
419 | out: | 476 | out: |
420 | call_usermodehelper_freeinfo(sub_info); | 477 | call_usermodehelper_freeinfo(sub_info); |
478 | helper_unlock(); | ||
421 | return retval; | 479 | return retval; |
422 | } | 480 | } |
423 | EXPORT_SYMBOL(call_usermodehelper_exec); | 481 | EXPORT_SYMBOL(call_usermodehelper_exec); |
@@ -459,5 +517,5 @@ void __init usermodehelper_init(void) | |||
459 | { | 517 | { |
460 | khelper_wq = create_singlethread_workqueue("khelper"); | 518 | khelper_wq = create_singlethread_workqueue("khelper"); |
461 | BUG_ON(!khelper_wq); | 519 | BUG_ON(!khelper_wq); |
462 | pm_notifier(usermodehelper_pm_callback, 0); | 520 | register_pm_notifier_callback(); |
463 | } | 521 | } |