diff options
-rw-r--r-- | drivers/base/firmware_class.c | 3 | ||||
-rw-r--r-- | include/linux/kmod.h | 4 | ||||
-rw-r--r-- | kernel/kmod.c | 24 |
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); } /** |