aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/firmware_class.c51
-rw-r--r--include/linux/kmod.h1
-rw-r--r--kernel/kmod.c58
3 files changed, 76 insertions, 34 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 60290671f04a..72c644b191a4 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -81,6 +81,11 @@ enum {
81 81
82static int loading_timeout = 60; /* In seconds */ 82static int loading_timeout = 60; /* In seconds */
83 83
84static inline long firmware_loading_timeout(void)
85{
86 return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT;
87}
88
84/* fw_lock could be moved to 'struct firmware_priv' but since it is just 89/* fw_lock could be moved to 'struct firmware_priv' but since it is just
85 * guarding for corner cases a global lock should be OK */ 90 * guarding for corner cases a global lock should be OK */
86static DEFINE_MUTEX(fw_lock); 91static DEFINE_MUTEX(fw_lock);
@@ -541,31 +546,22 @@ static void _request_firmware_cleanup(const struct firmware **firmware_p)
541 546
542static int _request_firmware(const struct firmware *firmware, 547static int _request_firmware(const struct firmware *firmware,
543 const char *name, struct device *device, 548 const char *name, struct device *device,
544 bool uevent, bool nowait) 549 bool uevent, bool nowait, long timeout)
545{ 550{
546 struct firmware_priv *fw_priv; 551 struct firmware_priv *fw_priv;
547 int retval; 552 int retval = 0;
548
549 retval = usermodehelper_read_trylock();
550 if (WARN_ON(retval)) {
551 dev_err(device, "firmware: %s will not be loaded\n", name);
552 return retval;
553 }
554 553
555 if (uevent) 554 if (uevent)
556 dev_dbg(device, "firmware: requesting %s\n", name); 555 dev_dbg(device, "firmware: requesting %s\n", name);
557 556
558 fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); 557 fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
559 if (IS_ERR(fw_priv)) { 558 if (IS_ERR(fw_priv))
560 retval = PTR_ERR(fw_priv); 559 return PTR_ERR(fw_priv);
561 goto out;
562 }
563 560
564 if (uevent) { 561 if (uevent) {
565 if (loading_timeout > 0) 562 if (timeout != MAX_SCHEDULE_TIMEOUT)
566 mod_timer(&fw_priv->timeout, 563 mod_timer(&fw_priv->timeout,
567 round_jiffies_up(jiffies + 564 round_jiffies_up(jiffies + timeout));
568 loading_timeout * HZ));
569 565
570 kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); 566 kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
571 } 567 }
@@ -582,9 +578,6 @@ static int _request_firmware(const struct firmware *firmware,
582 mutex_unlock(&fw_lock); 578 mutex_unlock(&fw_lock);
583 579
584 fw_destroy_instance(fw_priv); 580 fw_destroy_instance(fw_priv);
585
586out:
587 usermodehelper_read_unlock();
588 return retval; 581 return retval;
589} 582}
590 583
@@ -613,7 +606,14 @@ request_firmware(const struct firmware **firmware_p, const char *name,
613 if (ret <= 0) 606 if (ret <= 0)
614 return ret; 607 return ret;
615 608
616 ret = _request_firmware(*firmware_p, name, device, true, false); 609 ret = usermodehelper_read_trylock();
610 if (WARN_ON(ret)) {
611 dev_err(device, "firmware: %s will not be loaded\n", name);
612 } else {
613 ret = _request_firmware(*firmware_p, name, device, true, false,
614 firmware_loading_timeout());
615 usermodehelper_read_unlock();
616 }
617 if (ret) 617 if (ret)
618 _request_firmware_cleanup(firmware_p); 618 _request_firmware_cleanup(firmware_p);
619 619
@@ -648,6 +648,7 @@ static int request_firmware_work_func(void *arg)
648{ 648{
649 struct firmware_work *fw_work = arg; 649 struct firmware_work *fw_work = arg;
650 const struct firmware *fw; 650 const struct firmware *fw;
651 long timeout;
651 int ret; 652 int ret;
652 653
653 if (!arg) { 654 if (!arg) {
@@ -659,8 +660,16 @@ static int request_firmware_work_func(void *arg)
659 if (ret <= 0) 660 if (ret <= 0)
660 goto out; 661 goto out;
661 662
662 ret = _request_firmware(fw, fw_work->name, fw_work->device, 663 timeout = usermodehelper_read_lock_wait(firmware_loading_timeout());
663 fw_work->uevent, true); 664 if (timeout) {
665 ret = _request_firmware(fw, fw_work->name, fw_work->device,
666 fw_work->uevent, true, timeout);
667 usermodehelper_read_unlock();
668 } else {
669 dev_dbg(fw_work->device, "firmware: %s loading timed out\n",
670 fw_work->name);
671 ret = -EAGAIN;
672 }
664 if (ret) 673 if (ret)
665 _request_firmware_cleanup(&fw); 674 _request_firmware_cleanup(&fw);
666 675
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
index 97d22c3e08b1..b087377ae2c4 100644
--- a/include/linux/kmod.h
+++ b/include/linux/kmod.h
@@ -115,6 +115,7 @@ extern void usermodehelper_init(void);
115extern int usermodehelper_disable(void); 115extern int usermodehelper_disable(void);
116extern void usermodehelper_enable(void); 116extern void usermodehelper_enable(void);
117extern int usermodehelper_read_trylock(void); 117extern int usermodehelper_read_trylock(void);
118extern long usermodehelper_read_lock_wait(long timeout);
118extern void usermodehelper_read_unlock(void); 119extern void usermodehelper_read_unlock(void);
119 120
120#endif /* __LINUX_KMOD_H__ */ 121#endif /* __LINUX_KMOD_H__ */
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 4079ac1d5e79..da7fcca279f9 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -334,6 +334,12 @@ static atomic_t running_helpers = ATOMIC_INIT(0);
334static DECLARE_WAIT_QUEUE_HEAD(running_helpers_waitq); 334static DECLARE_WAIT_QUEUE_HEAD(running_helpers_waitq);
335 335
336/* 336/*
337 * Used by usermodehelper_read_lock_wait() to wait for usermodehelper_disabled
338 * to become 'false'.
339 */
340static DECLARE_WAIT_QUEUE_HEAD(usermodehelper_disabled_waitq);
341
342/*
337 * Time to wait for running_helpers to become zero before the setting of 343 * Time to wait for running_helpers to become zero before the setting of
338 * usermodehelper_disabled in usermodehelper_disable() fails 344 * usermodehelper_disabled in usermodehelper_disable() fails
339 */ 345 */
@@ -352,6 +358,33 @@ int usermodehelper_read_trylock(void)
352} 358}
353EXPORT_SYMBOL_GPL(usermodehelper_read_trylock); 359EXPORT_SYMBOL_GPL(usermodehelper_read_trylock);
354 360
361long usermodehelper_read_lock_wait(long timeout)
362{
363 DEFINE_WAIT(wait);
364
365 if (timeout < 0)
366 return -EINVAL;
367
368 down_read(&umhelper_sem);
369 for (;;) {
370 prepare_to_wait(&usermodehelper_disabled_waitq, &wait,
371 TASK_UNINTERRUPTIBLE);
372 if (!usermodehelper_disabled)
373 break;
374
375 up_read(&umhelper_sem);
376
377 timeout = schedule_timeout(timeout);
378 if (!timeout)
379 break;
380
381 down_read(&umhelper_sem);
382 }
383 finish_wait(&usermodehelper_disabled_waitq, &wait);
384 return timeout;
385}
386EXPORT_SYMBOL_GPL(usermodehelper_read_lock_wait);
387
355void usermodehelper_read_unlock(void) 388void usermodehelper_read_unlock(void)
356{ 389{
357 up_read(&umhelper_sem); 390 up_read(&umhelper_sem);
@@ -359,6 +392,17 @@ void usermodehelper_read_unlock(void)
359EXPORT_SYMBOL_GPL(usermodehelper_read_unlock); 392EXPORT_SYMBOL_GPL(usermodehelper_read_unlock);
360 393
361/** 394/**
395 * usermodehelper_enable - allow new helpers to be started again
396 */
397void usermodehelper_enable(void)
398{
399 down_write(&umhelper_sem);
400 usermodehelper_disabled = 0;
401 wake_up(&usermodehelper_disabled_waitq);
402 up_write(&umhelper_sem);
403}
404
405/**
362 * usermodehelper_disable - prevent new helpers from being started 406 * usermodehelper_disable - prevent new helpers from being started
363 */ 407 */
364int usermodehelper_disable(void) 408int usermodehelper_disable(void)
@@ -381,22 +425,10 @@ int usermodehelper_disable(void)
381 if (retval) 425 if (retval)
382 return 0; 426 return 0;
383 427
384 down_write(&umhelper_sem); 428 usermodehelper_enable();
385 usermodehelper_disabled = 0;
386 up_write(&umhelper_sem);
387 return -EAGAIN; 429 return -EAGAIN;
388} 430}
389 431
390/**
391 * usermodehelper_enable - allow new helpers to be started again
392 */
393void usermodehelper_enable(void)
394{
395 down_write(&umhelper_sem);
396 usermodehelper_disabled = 0;
397 up_write(&umhelper_sem);
398}
399
400static void helper_lock(void) 432static void helper_lock(void)
401{ 433{
402 atomic_inc(&running_helpers); 434 atomic_inc(&running_helpers);