Commit c9dc281d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'driver-core-4.13-rc5' of...

Merge tag 'driver-core-4.13-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core

Pull driver core fixes from Greg KH:
 "Here are three firmware core fixes for 4.13-rc5.

  All three of these fix reported issues and have been floating around
  for a few weeks. They have been in linux-next with no reported
  problems"

* tag 'driver-core-4.13-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core:
  firmware: avoid invalid fallback aborts by using killable wait
  firmware: fix batched requests - send wake up on failure on direct lookups
  firmware: fix batched requests - wake all waiters
parents ce7ba95c 260d9f2f
...@@ -30,7 +30,6 @@ ...@@ -30,7 +30,6 @@
#include <linux/syscore_ops.h> #include <linux/syscore_ops.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/swait.h>
#include <generated/utsrelease.h> #include <generated/utsrelease.h>
...@@ -112,13 +111,13 @@ static inline long firmware_loading_timeout(void) ...@@ -112,13 +111,13 @@ static inline long firmware_loading_timeout(void)
* state of the firmware loading. * state of the firmware loading.
*/ */
struct fw_state { struct fw_state {
struct swait_queue_head wq; struct completion completion;
enum fw_status status; enum fw_status status;
}; };
static void fw_state_init(struct fw_state *fw_st) 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; fw_st->status = FW_STATUS_UNKNOWN;
} }
...@@ -131,9 +130,7 @@ static int __fw_state_wait_common(struct fw_state *fw_st, long timeout) ...@@ -131,9 +130,7 @@ static int __fw_state_wait_common(struct fw_state *fw_st, long timeout)
{ {
long ret; long ret;
ret = swait_event_interruptible_timeout(fw_st->wq, ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout);
__fw_state_is_done(READ_ONCE(fw_st->status)),
timeout);
if (ret != 0 && fw_st->status == FW_STATUS_ABORTED) if (ret != 0 && fw_st->status == FW_STATUS_ABORTED)
return -ENOENT; return -ENOENT;
if (!ret) if (!ret)
...@@ -148,35 +145,34 @@ static void __fw_state_set(struct fw_state *fw_st, ...@@ -148,35 +145,34 @@ static void __fw_state_set(struct fw_state *fw_st,
WRITE_ONCE(fw_st->status, status); WRITE_ONCE(fw_st->status, status);
if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) 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) \ #define fw_state_start(fw_st) \
__fw_state_set(fw_st, FW_STATUS_LOADING) __fw_state_set(fw_st, FW_STATUS_LOADING)
#define fw_state_done(fw_st) \ #define fw_state_done(fw_st) \
__fw_state_set(fw_st, FW_STATUS_DONE) __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) \ #define fw_state_wait(fw_st) \
__fw_state_wait_common(fw_st, MAX_SCHEDULE_TIMEOUT) __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) static int __fw_state_check(struct fw_state *fw_st, enum fw_status status)
{ {
return fw_st->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) \ #define fw_state_aborted(fw_st) \
__fw_state_set(fw_st, FW_STATUS_ABORTED) __fw_state_set(fw_st, FW_STATUS_ABORTED)
#define fw_state_is_done(fw_st) \ #define fw_state_is_done(fw_st) \
__fw_state_check(fw_st, FW_STATUS_DONE) __fw_state_check(fw_st, FW_STATUS_DONE)
#define fw_state_is_loading(fw_st) \ #define fw_state_is_loading(fw_st) \
__fw_state_check(fw_st, FW_STATUS_LOADING) __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) \ #define fw_state_wait_timeout(fw_st, timeout) \
__fw_state_wait_common(fw_st, timeout) __fw_state_wait_common(fw_st, timeout)
...@@ -1200,6 +1196,28 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name, ...@@ -1200,6 +1196,28 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
return 1; /* need to load */ 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() */ /* called from request_firmware() and request_firmware_work_func() */
static int static int
_request_firmware(const struct firmware **firmware_p, const char *name, _request_firmware(const struct firmware **firmware_p, const char *name,
...@@ -1243,6 +1261,7 @@ _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: out:
if (ret < 0) { if (ret < 0) {
fw_abort_batch_reqs(fw);
release_firmware(fw); release_firmware(fw);
fw = NULL; fw = NULL;
} }
......
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