summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/base/firmware_class.c3
-rw-r--r--include/linux/kmod.h4
-rw-r--r--kernel/kmod.c24
3 files changed, 30 insertions, 1 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 080e44e2e6a9..de2e1732f55a 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -508,6 +508,8 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
return 0;
}
+ read_lock_usermodehelper();
+
if (WARN_ON(usermodehelper_is_disabled())) {
dev_err(device, "firmware: %s will not be loaded\n", name);
retval = -EBUSY;
@@ -551,6 +553,7 @@ error_kfree_fw:
kfree(firmware);
*firmware_p = NULL;
out:
+ read_unlock_usermodehelper();
return retval;
}
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
index 5a048bdcc1d2..0546fe7ef1af 100644
--- a/include/linux/kmod.h
+++ b/include/linux/kmod.h
@@ -108,8 +108,12 @@ extern int call_usermodehelper_pipe(char *path, char *argv[], char *envp[],
extern int usermodehelper_disable(void);
extern void usermodehelper_enable(void);
extern bool usermodehelper_is_disabled(void);
+extern void read_lock_usermodehelper(void);
+extern void read_unlock_usermodehelper(void);
#else
static inline bool usermodehelper_is_disabled(void) { return false; }
+static inline void read_lock_usermodehelper(void) {}
+static inline void read_unlock_usermodehelper(void) {}
#endif
#endif /* __LINUX_KMOD_H__ */
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 163a9193f5c3..a061472a7c7e 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -35,6 +35,7 @@
#include <linux/resource.h>
#include <linux/notifier.h>
#include <linux/suspend.h>
+#include <linux/rwsem.h>
#include <asm/uaccess.h>
#include <trace/events/module.h>
@@ -43,6 +44,8 @@ extern int max_threads;
static struct workqueue_struct *khelper_wq;
+static DECLARE_RWSEM(umhelper_sem);
+
#ifdef CONFIG_MODULES
/*
@@ -286,6 +289,7 @@ static void __call_usermodehelper(struct work_struct *work)
* If set, call_usermodehelper_exec() will exit immediately returning -EBUSY
* (used for preventing user land processes from being created after the user
* land has been frozen during a system-wide hibernation or suspend operation).
+ * Should always be manipulated under umhelper_sem acquired for write.
*/
static int usermodehelper_disabled;
@@ -304,6 +308,18 @@ static DECLARE_WAIT_QUEUE_HEAD(running_helpers_waitq);
*/
#define RUNNING_HELPERS_TIMEOUT (5 * HZ)
+void read_lock_usermodehelper(void)
+{
+ down_read(&umhelper_sem);
+}
+EXPORT_SYMBOL_GPL(read_lock_usermodehelper);
+
+void read_unlock_usermodehelper(void)
+{
+ up_read(&umhelper_sem);
+}
+EXPORT_SYMBOL_GPL(read_unlock_usermodehelper);
+
/**
* usermodehelper_disable - prevent new helpers from being started
*/
@@ -311,8 +327,10 @@ int usermodehelper_disable(void)
{
long retval;
+ down_write(&umhelper_sem);
usermodehelper_disabled = 1;
- smp_mb();
+ up_write(&umhelper_sem);
+
/*
* From now on call_usermodehelper_exec() won't start any new
* helpers, so it is sufficient if running_helpers turns out to
@@ -325,7 +343,9 @@ int usermodehelper_disable(void)
if (retval)
return 0;
+ down_write(&umhelper_sem);
usermodehelper_disabled = 0;
+ up_write(&umhelper_sem);
return -EAGAIN;
}
@@ -334,7 +354,9 @@ int usermodehelper_disable(void)
*/
void usermodehelper_enable(void)
{
+ down_write(&umhelper_sem);
usermodehelper_disabled = 0;
+ up_write(&umhelper_sem);
}
/**