driver core fixes for 4.13-rc5
Here are 3 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. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWY+z8w8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yngsgCeJQzYKjLyfY1QXhhjLY2Xy/ufFYIAoM2WafSL 7OnYFrt2s2VWPp3jEY9a =XEz8 -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
c9dc281d91
@ -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)
|
|||||||
* 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)
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
fw_abort_batch_reqs(fw);
|
||||||
release_firmware(fw);
|
release_firmware(fw);
|
||||||
fw = NULL;
|
fw = NULL;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user