]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/base/firmware_class.c
Merge tag 'driver-core-4.13-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / drivers / base / firmware_class.c
index b9f907eedbf770ee32359468d2b8d07e57bde667..bfbe1e15412889dfb40e699af7c3623a06d83253 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/syscore_ops.h>
 #include <linux/reboot.h>
 #include <linux/security.h>
-#include <linux/swait.h>
 
 #include <generated/utsrelease.h>
 
@@ -112,13 +111,13 @@ static inline long firmware_loading_timeout(void)
  * state of the firmware loading.
  */
 struct fw_state {
-       struct swait_queue_head wq;
+       struct completion completion;
        enum fw_status status;
 };
 
 static void fw_state_init(struct fw_state *fw_st)
 {
-       init_swait_queue_head(&fw_st->wq);
+       init_completion(&fw_st->completion);
        fw_st->status = FW_STATUS_UNKNOWN;
 }
 
@@ -131,9 +130,7 @@ static int __fw_state_wait_common(struct fw_state *fw_st, long timeout)
 {
        long ret;
 
-       ret = swait_event_interruptible_timeout(fw_st->wq,
-                               __fw_state_is_done(READ_ONCE(fw_st->status)),
-                               timeout);
+       ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout);
        if (ret != 0 && fw_st->status == FW_STATUS_ABORTED)
                return -ENOENT;
        if (!ret)
@@ -148,35 +145,34 @@ static void __fw_state_set(struct fw_state *fw_st,
        WRITE_ONCE(fw_st->status, status);
 
        if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED)
-               swake_up(&fw_st->wq);
+               complete_all(&fw_st->completion);
 }
 
 #define fw_state_start(fw_st)                                  \
        __fw_state_set(fw_st, FW_STATUS_LOADING)
 #define fw_state_done(fw_st)                                   \
        __fw_state_set(fw_st, FW_STATUS_DONE)
+#define fw_state_aborted(fw_st)                                        \
+       __fw_state_set(fw_st, FW_STATUS_ABORTED)
 #define fw_state_wait(fw_st)                                   \
        __fw_state_wait_common(fw_st, MAX_SCHEDULE_TIMEOUT)
 
-#ifndef CONFIG_FW_LOADER_USER_HELPER
-
-#define fw_state_is_aborted(fw_st)     false
-
-#else /* CONFIG_FW_LOADER_USER_HELPER */
-
 static int __fw_state_check(struct fw_state *fw_st, enum fw_status status)
 {
        return fw_st->status == status;
 }
 
+#define fw_state_is_aborted(fw_st)                             \
+       __fw_state_check(fw_st, FW_STATUS_ABORTED)
+
+#ifdef CONFIG_FW_LOADER_USER_HELPER
+
 #define fw_state_aborted(fw_st)                                        \
        __fw_state_set(fw_st, FW_STATUS_ABORTED)
 #define fw_state_is_done(fw_st)                                        \
        __fw_state_check(fw_st, FW_STATUS_DONE)
 #define fw_state_is_loading(fw_st)                             \
        __fw_state_check(fw_st, FW_STATUS_LOADING)
-#define fw_state_is_aborted(fw_st)                             \
-       __fw_state_check(fw_st, FW_STATUS_ABORTED)
 #define fw_state_wait_timeout(fw_st, timeout)                  \
        __fw_state_wait_common(fw_st, timeout)
 
@@ -1200,6 +1196,28 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
        return 1; /* need to load */
 }
 
+/*
+ * Batched requests need only one wake, we need to do this step last due to the
+ * fallback mechanism. The buf is protected with kref_get(), and it won't be
+ * released until the last user calls release_firmware().
+ *
+ * Failed batched requests are possible as well, in such cases we just share
+ * the struct firmware_buf and won't release it until all requests are woken
+ * and have gone through this same path.
+ */
+static void fw_abort_batch_reqs(struct firmware *fw)
+{
+       struct firmware_buf *buf;
+
+       /* Loaded directly? */
+       if (!fw || !fw->priv)
+               return;
+
+       buf = fw->priv;
+       if (!fw_state_is_aborted(&buf->fw_st))
+               fw_state_aborted(&buf->fw_st);
+}
+
 /* called from request_firmware() and request_firmware_work_func() */
 static int
 _request_firmware(const struct firmware **firmware_p, const char *name,
@@ -1243,6 +1261,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
 
  out:
        if (ret < 0) {
+               fw_abort_batch_reqs(fw);
                release_firmware(fw);
                fw = NULL;
        }