Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pm: rework disabling of user mode helpers during suspend/hibernation

We currently use a PM notifier to disable user mode helpers before suspend
and hibernation and to re-enable them during resume. However, this is not
an ideal solution, because if any drivers want to upload firmware into
memory before suspend, they have to use a PM notifier for this purpose and
there is no guarantee that the ordering of PM notifiers will be as
expected (ie. the notifier that disables user mode helpers has to be run
after the driver's notifier used for uploading the firmware).

For this reason, it seems better to move the disabling and enabling of
user mode helpers to separate functions that will be called by the PM core
as necessary.

[akpm@linux-foundation.org: remove unneeded ifdefs]
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Pavel Machek <pavel@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Rafael J. Wysocki and committed by
Linus Torvalds
1bfcf130 574f34ce

+58 -38
+3
include/linux/kmod.h
··· 99 99 extern int call_usermodehelper_pipe(char *path, char *argv[], char *envp[], 100 100 struct file **filp); 101 101 102 + extern int usermodehelper_disable(void); 103 + extern void usermodehelper_enable(void); 104 + 102 105 #endif /* __LINUX_KMOD_H__ */
+28 -37
kernel/kmod.c
··· 265 265 } 266 266 } 267 267 268 - #ifdef CONFIG_PM 268 + #ifdef CONFIG_PM_SLEEP 269 269 /* 270 270 * If set, call_usermodehelper_exec() will exit immediately returning -EBUSY 271 271 * (used for preventing user land processes from being created after the user ··· 288 288 */ 289 289 #define RUNNING_HELPERS_TIMEOUT (5 * HZ) 290 290 291 - static int usermodehelper_pm_callback(struct notifier_block *nfb, 292 - unsigned long action, 293 - void *ignored) 291 + /** 292 + * usermodehelper_disable - prevent new helpers from being started 293 + */ 294 + int usermodehelper_disable(void) 294 295 { 295 296 long retval; 296 297 297 - switch (action) { 298 - case PM_HIBERNATION_PREPARE: 299 - case PM_SUSPEND_PREPARE: 300 - usermodehelper_disabled = 1; 301 - smp_mb(); 302 - /* 303 - * From now on call_usermodehelper_exec() won't start any new 304 - * helpers, so it is sufficient if running_helpers turns out to 305 - * be zero at one point (it may be increased later, but that 306 - * doesn't matter). 307 - */ 308 - retval = wait_event_timeout(running_helpers_waitq, 298 + usermodehelper_disabled = 1; 299 + smp_mb(); 300 + /* 301 + * From now on call_usermodehelper_exec() won't start any new 302 + * helpers, so it is sufficient if running_helpers turns out to 303 + * be zero at one point (it may be increased later, but that 304 + * doesn't matter). 305 + */ 306 + retval = wait_event_timeout(running_helpers_waitq, 309 307 atomic_read(&running_helpers) == 0, 310 308 RUNNING_HELPERS_TIMEOUT); 311 - if (retval) { 312 - return NOTIFY_OK; 313 - } else { 314 - usermodehelper_disabled = 0; 315 - return NOTIFY_BAD; 316 - } 317 - case PM_POST_HIBERNATION: 318 - case PM_POST_SUSPEND: 319 - usermodehelper_disabled = 0; 320 - return NOTIFY_OK; 321 - } 309 + if (retval) 310 + return 0; 322 311 323 - return NOTIFY_DONE; 312 + usermodehelper_disabled = 0; 313 + return -EAGAIN; 314 + } 315 + 316 + /** 317 + * usermodehelper_enable - allow new helpers to be started again 318 + */ 319 + void usermodehelper_enable(void) 320 + { 321 + usermodehelper_disabled = 0; 324 322 } 325 323 326 324 static void helper_lock(void) ··· 332 334 if (atomic_dec_and_test(&running_helpers)) 333 335 wake_up(&running_helpers_waitq); 334 336 } 335 - 336 - static void register_pm_notifier_callback(void) 337 - { 338 - pm_notifier(usermodehelper_pm_callback, 0); 339 - } 340 - #else /* CONFIG_PM */ 337 + #else /* CONFIG_PM_SLEEP */ 341 338 #define usermodehelper_disabled 0 342 339 343 340 static inline void helper_lock(void) {} 344 341 static inline void helper_unlock(void) {} 345 - static inline void register_pm_notifier_callback(void) {} 346 - #endif /* CONFIG_PM */ 342 + #endif /* CONFIG_PM_SLEEP */ 347 343 348 344 /** 349 345 * call_usermodehelper_setup - prepare to call a usermode helper ··· 507 515 { 508 516 khelper_wq = create_singlethread_workqueue("khelper"); 509 517 BUG_ON(!khelper_wq); 510 - register_pm_notifier_callback(); 511 518 }
+11
kernel/power/disk.c
··· 14 14 #include <linux/reboot.h> 15 15 #include <linux/string.h> 16 16 #include <linux/device.h> 17 + #include <linux/kmod.h> 17 18 #include <linux/delay.h> 18 19 #include <linux/fs.h> 19 20 #include <linux/mount.h> ··· 521 520 if (error) 522 521 goto Exit; 523 522 523 + error = usermodehelper_disable(); 524 + if (error) 525 + goto Exit; 526 + 524 527 /* Allocate memory management structures */ 525 528 error = create_basic_memory_bitmaps(); 526 529 if (error) ··· 563 558 thaw_processes(); 564 559 Finish: 565 560 free_basic_memory_bitmaps(); 561 + usermodehelper_enable(); 566 562 Exit: 567 563 pm_notifier_call_chain(PM_POST_HIBERNATION); 568 564 pm_restore_console(); ··· 640 634 if (error) 641 635 goto Finish; 642 636 637 + error = usermodehelper_disable(); 638 + if (error) 639 + goto Finish; 640 + 643 641 error = create_basic_memory_bitmaps(); 644 642 if (error) 645 643 goto Finish; ··· 666 656 thaw_processes(); 667 657 Done: 668 658 free_basic_memory_bitmaps(); 659 + usermodehelper_enable(); 669 660 Finish: 670 661 pm_notifier_call_chain(PM_POST_RESTORE); 671 662 pm_restore_console();
+7
kernel/power/main.c
··· 14 14 #include <linux/string.h> 15 15 #include <linux/delay.h> 16 16 #include <linux/errno.h> 17 + #include <linux/kmod.h> 17 18 #include <linux/init.h> 18 19 #include <linux/console.h> 19 20 #include <linux/cpu.h> ··· 238 237 if (error) 239 238 goto Finish; 240 239 240 + error = usermodehelper_disable(); 241 + if (error) 242 + goto Finish; 243 + 241 244 if (suspend_freeze_processes()) { 242 245 error = -EAGAIN; 243 246 goto Thaw; ··· 261 256 262 257 Thaw: 263 258 suspend_thaw_processes(); 259 + usermodehelper_enable(); 264 260 Finish: 265 261 pm_notifier_call_chain(PM_POST_SUSPEND); 266 262 pm_restore_console(); ··· 382 376 static void suspend_finish(void) 383 377 { 384 378 suspend_thaw_processes(); 379 + usermodehelper_enable(); 385 380 pm_notifier_call_chain(PM_POST_SUSPEND); 386 381 pm_restore_console(); 387 382 }
+9 -1
kernel/power/user.c
··· 212 212 case SNAPSHOT_FREEZE: 213 213 if (data->frozen) 214 214 break; 215 + 215 216 printk("Syncing filesystems ... "); 216 217 sys_sync(); 217 218 printk("done.\n"); 218 219 219 - error = freeze_processes(); 220 + error = usermodehelper_disable(); 220 221 if (error) 222 + break; 223 + 224 + error = freeze_processes(); 225 + if (error) { 221 226 thaw_processes(); 227 + usermodehelper_enable(); 228 + } 222 229 if (!error) 223 230 data->frozen = 1; 224 231 break; ··· 234 227 if (!data->frozen || data->ready) 235 228 break; 236 229 thaw_processes(); 230 + usermodehelper_enable(); 237 231 data->frozen = 0; 238 232 break; 239 233