summaryrefslogtreecommitdiffstats
path: root/kernel/power
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-03-28 23:30:28 +0200
committerRafael J. Wysocki <rjw@sisk.pl>2012-03-28 23:30:28 +0200
commit247bc03742545fec2f79939a3b9f738392a0f7b4 (patch)
treeea07ff0b82da841ba89968d360d7c2560ae740b4 /kernel/power
parent1e73203cd1157a03facc41ffb54050f5b28e55bd (diff)
downloadlinux-247bc03742545fec2f79939a3b9f738392a0f7b4.tar.gz
linux-247bc03742545fec2f79939a3b9f738392a0f7b4.tar.bz2
linux-247bc03742545fec2f79939a3b9f738392a0f7b4.zip
PM / Sleep: Mitigate race between the freezer and request_firmware()
There is a race condition between the freezer and request_firmware() such that if request_firmware() is run on one CPU and freeze_processes() is run on another CPU and usermodehelper_disable() called by it succeeds to grab umhelper_sem for writing before usermodehelper_read_trylock() called from request_firmware() acquires it for reading, the request_firmware() will fail and trigger a WARN_ON() complaining that it was called at a wrong time. However, in fact, it wasn't called at a wrong time and freeze_processes() simply happened to be executed simultaneously. To avoid this race, at least in some cases, modify usermodehelper_read_trylock() so that it doesn't fail if the freezing of tasks has just started and hasn't been completed yet. Instead, during the freezing of tasks, it will try to freeze the task that has called it so that it can wait until user space is thawed without triggering the scary warning. For this purpose, change usermodehelper_disabled so that it can take three different values, UMH_ENABLED (0), UMH_FREEZING and UMH_DISABLED. The first one means that usermode helpers are enabled, the last one means "hard disable" (i.e. the system is not ready for usermode helpers to be used) and the second one is reserved for the freezer. Namely, when freeze_processes() is started, it sets usermodehelper_disabled to UMH_FREEZING which tells usermodehelper_read_trylock() that it shouldn't fail just yet and should call try_to_freeze() if woken up and cannot return immediately. This way all freezable tasks that happen to call request_firmware() right before freeze_processes() is started and lose the race for umhelper_sem with it will be frozen and will sleep until thaw_processes() unsets usermodehelper_disabled. [For the non-freezable callers of request_firmware() the race for umhelper_sem against freeze_processes() is unfortunately unavoidable.] Reported-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: stable@vger.kernel.org
Diffstat (limited to 'kernel/power')
-rw-r--r--kernel/power/process.c3
1 files changed, 2 insertions, 1 deletions
diff --git a/kernel/power/process.c b/kernel/power/process.c
index 56eaac7e88ab..19db29f67558 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -123,7 +123,7 @@ int freeze_processes(void)
{
int error;
- error = usermodehelper_disable();
+ error = __usermodehelper_disable(UMH_FREEZING);
if (error)
return error;
@@ -135,6 +135,7 @@ int freeze_processes(void)
error = try_to_freeze_tasks(true);
if (!error) {
printk("done.");
+ __usermodehelper_set_disable_depth(UMH_DISABLED);
oom_killer_disable();
}
printk("\n");