Commit da341114 authored by Linus Torvalds's avatar Linus Torvalds Committed by Greg Kroah-Hartman

Revert "firmware: add sanity check on shutdown/suspend"

commit f007cad1 upstream.

This reverts commit 81f95076.

It causes random failures of firmware loading at resume time (well,
random for me, it seems to be more reliable for others) because the
firmware disabling is not actually synchronous with any particular
resume event, and at least the btusb driver that uses a workqueue to
load the firmware at resume seems to occasionally hit the "firmware
loading is disabled" logic because the firmware loader hasn't gotten the
resume event yet.

Some kind of sanity check for not trying to load firmware when it's not
possible might be a good thing, but this commit was not it.

Greg seems to have silently suffered the same issue, and pointed to the
likely culprit, and Gabriel C verified the revert fixed it for him too.
Reported-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Pointed-at-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tested-by: default avatarGabriel C <nix.or.die@gmail.com>
Cc: Luis R. Rodriguez <mcgrof@kernel.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9d7960e0
...@@ -44,17 +44,6 @@ request_firmware_nowait ...@@ -44,17 +44,6 @@ request_firmware_nowait
.. kernel-doc:: drivers/base/firmware_class.c .. kernel-doc:: drivers/base/firmware_class.c
:functions: request_firmware_nowait :functions: request_firmware_nowait
Considerations for suspend and resume
=====================================
During suspend and resume only the built-in firmware and the firmware cache
elements of the firmware API can be used. This is managed by fw_pm_notify().
fw_pm_notify
------------
.. kernel-doc:: drivers/base/firmware_class.c
:functions: fw_pm_notify
request firmware API expected driver use request firmware API expected driver use
======================================== ========================================
......
...@@ -256,38 +256,6 @@ static int fw_cache_piggyback_on_request(const char *name); ...@@ -256,38 +256,6 @@ static int fw_cache_piggyback_on_request(const char *name);
* guarding for corner cases a global lock should be OK */ * guarding for corner cases a global lock should be OK */
static DEFINE_MUTEX(fw_lock); static DEFINE_MUTEX(fw_lock);
static bool __enable_firmware = false;
static void enable_firmware(void)
{
mutex_lock(&fw_lock);
__enable_firmware = true;
mutex_unlock(&fw_lock);
}
static void disable_firmware(void)
{
mutex_lock(&fw_lock);
__enable_firmware = false;
mutex_unlock(&fw_lock);
}
/*
* When disabled only the built-in firmware and the firmware cache will be
* used to look for firmware.
*/
static bool firmware_enabled(void)
{
bool enabled = false;
mutex_lock(&fw_lock);
if (__enable_firmware)
enabled = true;
mutex_unlock(&fw_lock);
return enabled;
}
static struct firmware_cache fw_cache; static struct firmware_cache fw_cache;
static struct firmware_buf *__allocate_fw_buf(const char *fw_name, static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
...@@ -1239,12 +1207,6 @@ _request_firmware(const struct firmware **firmware_p, const char *name, ...@@ -1239,12 +1207,6 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
if (ret <= 0) /* error or already assigned */ if (ret <= 0) /* error or already assigned */
goto out; goto out;
if (!firmware_enabled()) {
WARN(1, "firmware request while host is not available\n");
ret = -EHOSTDOWN;
goto out;
}
ret = fw_get_filesystem_firmware(device, fw->priv); ret = fw_get_filesystem_firmware(device, fw->priv);
if (ret) { if (ret) {
if (!(opt_flags & FW_OPT_NO_WARN)) if (!(opt_flags & FW_OPT_NO_WARN))
...@@ -1755,62 +1717,6 @@ static void device_uncache_fw_images_delay(unsigned long delay) ...@@ -1755,62 +1717,6 @@ static void device_uncache_fw_images_delay(unsigned long delay)
msecs_to_jiffies(delay)); msecs_to_jiffies(delay));
} }
/**
* fw_pm_notify - notifier for suspend/resume
* @notify_block: unused
* @mode: mode we are switching to
* @unused: unused
*
* Used to modify the firmware_class state as we move in between states.
* The firmware_class implements a firmware cache to enable device driver
* to fetch firmware upon resume before the root filesystem is ready. We
* disable API calls which do not use the built-in firmware or the firmware
* cache when we know these calls will not work.
*
* The inner logic behind all this is a bit complex so it is worth summarizing
* the kernel's own suspend/resume process with context and focus on how this
* can impact the firmware API.
*
* First a review on how we go to suspend::
*
* pm_suspend() --> enter_state() -->
* sys_sync()
* suspend_prepare() -->
* __pm_notifier_call_chain(PM_SUSPEND_PREPARE, ...);
* suspend_freeze_processes() -->
* freeze_processes() -->
* __usermodehelper_set_disable_depth(UMH_DISABLED);
* freeze all tasks ...
* freeze_kernel_threads()
* suspend_devices_and_enter() -->
* dpm_suspend_start() -->
* dpm_prepare()
* dpm_suspend()
* suspend_enter() -->
* platform_suspend_prepare()
* dpm_suspend_late()
* freeze_enter()
* syscore_suspend()
*
* When we resume we bail out of a loop from suspend_devices_and_enter() and
* unwind back out to the caller enter_state() where we were before as follows::
*
* enter_state() -->
* suspend_devices_and_enter() --> (bail from loop)
* dpm_resume_end() -->
* dpm_resume()
* dpm_complete()
* suspend_finish() -->
* suspend_thaw_processes() -->
* thaw_processes() -->
* __usermodehelper_set_disable_depth(UMH_FREEZING);
* thaw_workqueues();
* thaw all processes ...
* usermodehelper_enable();
* pm_notifier_call_chain(PM_POST_SUSPEND);
*
* fw_pm_notify() works through pm_notifier_call_chain().
*/
static int fw_pm_notify(struct notifier_block *notify_block, static int fw_pm_notify(struct notifier_block *notify_block,
unsigned long mode, void *unused) unsigned long mode, void *unused)
{ {
...@@ -1824,7 +1730,6 @@ static int fw_pm_notify(struct notifier_block *notify_block, ...@@ -1824,7 +1730,6 @@ static int fw_pm_notify(struct notifier_block *notify_block,
*/ */
kill_pending_fw_fallback_reqs(true); kill_pending_fw_fallback_reqs(true);
device_cache_fw_images(); device_cache_fw_images();
disable_firmware();
break; break;
case PM_POST_SUSPEND: case PM_POST_SUSPEND:
...@@ -1837,7 +1742,6 @@ static int fw_pm_notify(struct notifier_block *notify_block, ...@@ -1837,7 +1742,6 @@ static int fw_pm_notify(struct notifier_block *notify_block,
mutex_lock(&fw_lock); mutex_lock(&fw_lock);
fw_cache.state = FW_LOADER_NO_CACHE; fw_cache.state = FW_LOADER_NO_CACHE;
mutex_unlock(&fw_lock); mutex_unlock(&fw_lock);
enable_firmware();
device_uncache_fw_images_delay(10 * MSEC_PER_SEC); device_uncache_fw_images_delay(10 * MSEC_PER_SEC);
break; break;
...@@ -1886,7 +1790,6 @@ static void __init fw_cache_init(void) ...@@ -1886,7 +1790,6 @@ static void __init fw_cache_init(void)
static int fw_shutdown_notify(struct notifier_block *unused1, static int fw_shutdown_notify(struct notifier_block *unused1,
unsigned long unused2, void *unused3) unsigned long unused2, void *unused3)
{ {
disable_firmware();
/* /*
* Kill all pending fallback requests to avoid both stalling shutdown, * Kill all pending fallback requests to avoid both stalling shutdown,
* and avoid a deadlock with the usermode_lock. * and avoid a deadlock with the usermode_lock.
...@@ -1902,7 +1805,6 @@ static struct notifier_block fw_shutdown_nb = { ...@@ -1902,7 +1805,6 @@ static struct notifier_block fw_shutdown_nb = {
static int __init firmware_class_init(void) static int __init firmware_class_init(void)
{ {
enable_firmware();
fw_cache_init(); fw_cache_init();
register_reboot_notifier(&fw_shutdown_nb); register_reboot_notifier(&fw_shutdown_nb);
#ifdef CONFIG_FW_LOADER_USER_HELPER #ifdef CONFIG_FW_LOADER_USER_HELPER
...@@ -1914,7 +1816,6 @@ static int __init firmware_class_init(void) ...@@ -1914,7 +1816,6 @@ static int __init firmware_class_init(void)
static void __exit firmware_class_exit(void) static void __exit firmware_class_exit(void)
{ {
disable_firmware();
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
unregister_syscore_ops(&fw_syscore_ops); unregister_syscore_ops(&fw_syscore_ops);
unregister_pm_notifier(&fw_cache.pm_notify); unregister_pm_notifier(&fw_cache.pm_notify);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment