firmware_loader: Abort all upcoming firmware load request once reboot triggered
authorMukesh Ojha <quic_mojha@quicinc.com>
Thu, 26 Oct 2023 14:27:39 +0000 (19:57 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 27 Oct 2023 11:30:38 +0000 (13:30 +0200)
There could be following scenario where there is a ongoing reboot
is going from processA which tries to call all the reboot notifier
callback and one of them is firmware reboot call which tries to
abort all the ongoing firmware userspace request under fw_lock but
there could be another processB which tries to do request firmware,
which came just after abort done from ProcessA and ask for userspace
to load the firmware and this can stop the ongoing reboot ProcessA
to stall for next 60s(default timeout) which may not be expected
behaviour everyone like to see, instead we should abort any firmware
load request which came once firmware knows about the reboot through
notification.

      ProcessA                             ProcessB

kernel_restart_prepare
  blocking_notifier_call_chain
   fw_shutdown_notify
     kill_pending_fw_fallback_reqs
      __fw_load_abort
       fw_state_aborted                request_firmware
         __fw_state_set                 firmware_fallback_sysfs
...                                       fw_load_from_user_helper
..                                         ...
.                                          ..
                                            usermodehelper_read_trylock
                                             fw_load_sysfs_fallback
                                              fw_sysfs_wait_timeout
usermodehelper_disable
 __usermodehelper_disable
  down_write()

Signed-off-by: Mukesh Ojha <quic_mojha@quicinc.com>
Acked-by: Luis Chamberlain <mcgrof@kernel.org>
Link: https://lore.kernel.org/r/1698330459-31776-2-git-send-email-quic_mojha@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/base/firmware_loader/fallback.c
drivers/base/firmware_loader/firmware.h
drivers/base/firmware_loader/main.c

index b3ce07160281e7309d8471b91645b6385ae3ad3b..3ef0b312ae71905e924ae929ef3d64f0705e9b23 100644 (file)
@@ -57,6 +57,10 @@ void kill_pending_fw_fallback_reqs(bool kill_all)
                if (kill_all || !fw_priv->need_uevent)
                         __fw_load_abort(fw_priv);
        }
+
+       if (kill_all)
+               fw_load_abort_all = true;
+
        mutex_unlock(&fw_lock);
 }
 
@@ -86,7 +90,7 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout)
        }
 
        mutex_lock(&fw_lock);
-       if (fw_state_is_aborted(fw_priv)) {
+       if (fw_load_abort_all || fw_state_is_aborted(fw_priv)) {
                mutex_unlock(&fw_lock);
                retval = -EINTR;
                goto out;
index bf549d6500d7bc8e177ae90aae8d149983b22b4e..e891742ba2647fe380a0dae8dd3f6aefdb77ba93 100644 (file)
@@ -86,6 +86,7 @@ struct fw_priv {
 
 extern struct mutex fw_lock;
 extern struct firmware_cache fw_cache;
+extern bool fw_load_abort_all;
 
 static inline bool __fw_state_check(struct fw_priv *fw_priv,
                                    enum fw_status status)
index 522ccee781b41211356587322b5b0f920205c896..ea28102d421ebf2d3d611d77547816e48368ab5c 100644 (file)
@@ -93,6 +93,7 @@ static inline struct fw_priv *to_fw_priv(struct kref *ref)
 DEFINE_MUTEX(fw_lock);
 
 struct firmware_cache fw_cache;
+bool fw_load_abort_all;
 
 void fw_state_init(struct fw_priv *fw_priv)
 {