Skip to content

Commit

Permalink
pm: rework disabling of user mode helpers during suspend/hibernation
Browse files Browse the repository at this point in the history
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.

[[email protected]: remove unneeded ifdefs]
Signed-off-by: Rafael J. Wysocki <[email protected]>
Cc: Alan Stern <[email protected]>
Acked-by: Pavel Machek <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
rjwysocki authored and torvalds committed Oct 16, 2008
1 parent 574f34c commit 1bfcf13
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 38 deletions.
3 changes: 3 additions & 0 deletions include/linux/kmod.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,7 @@ struct file;
extern int call_usermodehelper_pipe(char *path, char *argv[], char *envp[],
struct file **filp);

extern int usermodehelper_disable(void);
extern void usermodehelper_enable(void);

#endif /* __LINUX_KMOD_H__ */
65 changes: 28 additions & 37 deletions kernel/kmod.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ static void __call_usermodehelper(struct work_struct *work)
}
}

#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
/*
* If set, call_usermodehelper_exec() will exit immediately returning -EBUSY
* (used for preventing user land processes from being created after the user
Expand All @@ -288,39 +288,37 @@ static DECLARE_WAIT_QUEUE_HEAD(running_helpers_waitq);
*/
#define RUNNING_HELPERS_TIMEOUT (5 * HZ)

static int usermodehelper_pm_callback(struct notifier_block *nfb,
unsigned long action,
void *ignored)
/**
* usermodehelper_disable - prevent new helpers from being started
*/
int usermodehelper_disable(void)
{
long retval;

switch (action) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
usermodehelper_disabled = 1;
smp_mb();
/*
* From now on call_usermodehelper_exec() won't start any new
* helpers, so it is sufficient if running_helpers turns out to
* be zero at one point (it may be increased later, but that
* doesn't matter).
*/
retval = wait_event_timeout(running_helpers_waitq,
usermodehelper_disabled = 1;
smp_mb();
/*
* From now on call_usermodehelper_exec() won't start any new
* helpers, so it is sufficient if running_helpers turns out to
* be zero at one point (it may be increased later, but that
* doesn't matter).
*/
retval = wait_event_timeout(running_helpers_waitq,
atomic_read(&running_helpers) == 0,
RUNNING_HELPERS_TIMEOUT);
if (retval) {
return NOTIFY_OK;
} else {
usermodehelper_disabled = 0;
return NOTIFY_BAD;
}
case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
usermodehelper_disabled = 0;
return NOTIFY_OK;
}
if (retval)
return 0;

return NOTIFY_DONE;
usermodehelper_disabled = 0;
return -EAGAIN;
}

/**
* usermodehelper_enable - allow new helpers to be started again
*/
void usermodehelper_enable(void)
{
usermodehelper_disabled = 0;
}

static void helper_lock(void)
Expand All @@ -334,18 +332,12 @@ static void helper_unlock(void)
if (atomic_dec_and_test(&running_helpers))
wake_up(&running_helpers_waitq);
}

static void register_pm_notifier_callback(void)
{
pm_notifier(usermodehelper_pm_callback, 0);
}
#else /* CONFIG_PM */
#else /* CONFIG_PM_SLEEP */
#define usermodehelper_disabled 0

static inline void helper_lock(void) {}
static inline void helper_unlock(void) {}
static inline void register_pm_notifier_callback(void) {}
#endif /* CONFIG_PM */
#endif /* CONFIG_PM_SLEEP */

/**
* call_usermodehelper_setup - prepare to call a usermode helper
Expand Down Expand Up @@ -515,5 +507,4 @@ void __init usermodehelper_init(void)
{
khelper_wq = create_singlethread_workqueue("khelper");
BUG_ON(!khelper_wq);
register_pm_notifier_callback();
}
11 changes: 11 additions & 0 deletions kernel/power/disk.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/reboot.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/kmod.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/mount.h>
Expand Down Expand Up @@ -520,6 +521,10 @@ int hibernate(void)
if (error)
goto Exit;

error = usermodehelper_disable();
if (error)
goto Exit;

/* Allocate memory management structures */
error = create_basic_memory_bitmaps();
if (error)
Expand Down Expand Up @@ -558,6 +563,7 @@ int hibernate(void)
thaw_processes();
Finish:
free_basic_memory_bitmaps();
usermodehelper_enable();
Exit:
pm_notifier_call_chain(PM_POST_HIBERNATION);
pm_restore_console();
Expand Down Expand Up @@ -634,6 +640,10 @@ static int software_resume(void)
if (error)
goto Finish;

error = usermodehelper_disable();
if (error)
goto Finish;

error = create_basic_memory_bitmaps();
if (error)
goto Finish;
Expand All @@ -656,6 +666,7 @@ static int software_resume(void)
thaw_processes();
Done:
free_basic_memory_bitmaps();
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_RESTORE);
pm_restore_console();
Expand Down
7 changes: 7 additions & 0 deletions kernel/power/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/cpu.h>
Expand Down Expand Up @@ -237,6 +238,10 @@ static int suspend_prepare(void)
if (error)
goto Finish;

error = usermodehelper_disable();
if (error)
goto Finish;

if (suspend_freeze_processes()) {
error = -EAGAIN;
goto Thaw;
Expand All @@ -256,6 +261,7 @@ static int suspend_prepare(void)

Thaw:
suspend_thaw_processes();
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
Expand Down Expand Up @@ -376,6 +382,7 @@ int suspend_devices_and_enter(suspend_state_t state)
static void suspend_finish(void)
{
suspend_thaw_processes();
usermodehelper_enable();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
}
Expand Down
10 changes: 9 additions & 1 deletion kernel/power/user.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,20 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
case SNAPSHOT_FREEZE:
if (data->frozen)
break;

printk("Syncing filesystems ... ");
sys_sync();
printk("done.\n");

error = freeze_processes();
error = usermodehelper_disable();
if (error)
break;

error = freeze_processes();
if (error) {
thaw_processes();
usermodehelper_enable();
}
if (!error)
data->frozen = 1;
break;
Expand All @@ -227,6 +234,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
if (!data->frozen || data->ready)
break;
thaw_processes();
usermodehelper_enable();
data->frozen = 0;
break;

Expand Down

0 comments on commit 1bfcf13

Please sign in to comment.