diff options
Diffstat (limited to 'drivers/base/firmware_class.c')
-rw-r--r-- | drivers/base/firmware_class.c | 51 |
1 files changed, 30 insertions, 21 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 | ||
82 | static int loading_timeout = 60; /* In seconds */ | 82 | static int loading_timeout = 60; /* In seconds */ |
83 | 83 | ||
84 | static 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 */ |
86 | static DEFINE_MUTEX(fw_lock); | 91 | static DEFINE_MUTEX(fw_lock); |
@@ -541,31 +546,22 @@ static void _request_firmware_cleanup(const struct firmware **firmware_p) | |||
541 | 546 | ||
542 | static int _request_firmware(const struct firmware *firmware, | 547 | static 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 | |||
586 | out: | ||
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 | ||