aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/kmod.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2007-07-19 04:47:37 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-19 13:04:42 -0400
commitccd4b65aef4be2278543fde5b999e55a4d694fd8 (patch)
tree2753440a79dab444eb3fa01fcaed1c996e1cfe28 /kernel/kmod.c
parent8cdd4936c17bd8085cb0dfacc4a37ccf8d0ada7b (diff)
PM: prevent frozen user mode helpers from failing the freezing of tasks
At present, if a user mode helper is running while usermodehelper_pm_callback() is executed, the helper may be frozen and the completion in call_usermodehelper_exec() won't be completed until user space processes are thawed. As a result, the freezing of kernel threads may fail, which is not desirable. Prevent this from happening by introducing a counter of running user mode helpers and allowing usermodehelper_pm_callback() to succeed for action = PM_HIBERNATION_PREPARE or action = PM_SUSPEND_PREPARE only if there are no helpers running. [Namely, usermodehelper_pm_callback() waits for at most RUNNING_HELPERS_TIMEOUT for the number of running helpers to become zero and fails if that doesn't happen.] Special thanks to Uli Luckas <u.luckas@road.de>, Pavel Machek <pavel@ucw.cz> and Oleg Nesterov <oleg@tv-sign.ru> for reviewing the previous versions of this patch and for very useful comments. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Uli Luckas <u.luckas@road.de> Acked-by: Nigel Cunningham <nigel@nigel.suspend2.net> Acked-by: Pavel Machek <pavel@ucw.cz> Cc: Oleg Nesterov <oleg@tv-sign.ru> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/kmod.c')
-rw-r--r--kernel/kmod.c78
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
42static struct workqueue_struct *khelper_wq; 42static 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 */
50static 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 */
276static int usermodehelper_disabled;
277
278/* Number of helpers running */
279static 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 */
285static 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
278static int usermodehelper_pm_callback(struct notifier_block *nfb, 293static 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
328static void helper_lock(void)
329{
330 atomic_inc(&running_helpers);
331 smp_mb__after_atomic_inc();
332}
333
334static void helper_unlock(void)
335{
336 if (atomic_dec_and_test(&running_helpers))
337 wake_up(&running_helpers_waitq);
338}
339
340static 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
347static inline void helper_lock(void) {}
348static inline void helper_unlock(void) {}
349static 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}
423EXPORT_SYMBOL(call_usermodehelper_exec); 481EXPORT_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}