diff options
-rw-r--r-- | drivers/base/firmware_class.c | 51 | ||||
-rw-r--r-- | include/linux/kmod.h | 1 | ||||
-rw-r--r-- | kernel/kmod.c | 58 |
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 | ||
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 | ||
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); | |||
115 | extern int usermodehelper_disable(void); | 115 | extern int usermodehelper_disable(void); |
116 | extern void usermodehelper_enable(void); | 116 | extern void usermodehelper_enable(void); |
117 | extern int usermodehelper_read_trylock(void); | 117 | extern int usermodehelper_read_trylock(void); |
118 | extern long usermodehelper_read_lock_wait(long timeout); | ||
118 | extern void usermodehelper_read_unlock(void); | 119 | extern 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); | |||
334 | static DECLARE_WAIT_QUEUE_HEAD(running_helpers_waitq); | 334 | static 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 | */ | ||
340 | static 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 | } |
353 | EXPORT_SYMBOL_GPL(usermodehelper_read_trylock); | 359 | EXPORT_SYMBOL_GPL(usermodehelper_read_trylock); |
354 | 360 | ||
361 | long 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 | } | ||
386 | EXPORT_SYMBOL_GPL(usermodehelper_read_lock_wait); | ||
387 | |||
355 | void usermodehelper_read_unlock(void) | 388 | void 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) | |||
359 | EXPORT_SYMBOL_GPL(usermodehelper_read_unlock); | 392 | EXPORT_SYMBOL_GPL(usermodehelper_read_unlock); |
360 | 393 | ||
361 | /** | 394 | /** |
395 | * usermodehelper_enable - allow new helpers to be started again | ||
396 | */ | ||
397 | void 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 | */ |
364 | int usermodehelper_disable(void) | 408 | int 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 | */ | ||
393 | void usermodehelper_enable(void) | ||
394 | { | ||
395 | down_write(&umhelper_sem); | ||
396 | usermodehelper_disabled = 0; | ||
397 | up_write(&umhelper_sem); | ||
398 | } | ||
399 | |||
400 | static void helper_lock(void) | 432 | static void helper_lock(void) |
401 | { | 433 | { |
402 | atomic_inc(&running_helpers); | 434 | atomic_inc(&running_helpers); |