diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-08-13 15:44:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-08-13 15:44:18 -0400 |
commit | c9dc281d91aed57251e94c7cca81a5c8777e3e4a (patch) | |
tree | 4c154e7070cf59198635df19b989f74b391ce333 | |
parent | ce7ba95cf078337c9921b301a7e4b9e82561db38 (diff) | |
parent | 260d9f2fc5655a2552701cdd0b909c4466732f19 (diff) |
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
-rw-r--r-- | drivers/base/firmware_class.c | 49 |
1 files changed, 34 insertions, 15 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b9f907eedbf7..bfbe1e154128 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c | |||
@@ -30,7 +30,6 @@ | |||
30 | #include <linux/syscore_ops.h> | 30 | #include <linux/syscore_ops.h> |
31 | #include <linux/reboot.h> | 31 | #include <linux/reboot.h> |
32 | #include <linux/security.h> | 32 | #include <linux/security.h> |
33 | #include <linux/swait.h> | ||
34 | 33 | ||
35 | #include <generated/utsrelease.h> | 34 | #include <generated/utsrelease.h> |
36 | 35 | ||
@@ -112,13 +111,13 @@ static inline long firmware_loading_timeout(void) | |||
112 | * state of the firmware loading. | 111 | * state of the firmware loading. |
113 | */ | 112 | */ |
114 | struct fw_state { | 113 | struct fw_state { |
115 | struct swait_queue_head wq; | 114 | struct completion completion; |
116 | enum fw_status status; | 115 | enum fw_status status; |
117 | }; | 116 | }; |
118 | 117 | ||
119 | static void fw_state_init(struct fw_state *fw_st) | 118 | static void fw_state_init(struct fw_state *fw_st) |
120 | { | 119 | { |
121 | init_swait_queue_head(&fw_st->wq); | 120 | init_completion(&fw_st->completion); |
122 | fw_st->status = FW_STATUS_UNKNOWN; | 121 | fw_st->status = FW_STATUS_UNKNOWN; |
123 | } | 122 | } |
124 | 123 | ||
@@ -131,9 +130,7 @@ static int __fw_state_wait_common(struct fw_state *fw_st, long timeout) | |||
131 | { | 130 | { |
132 | long ret; | 131 | long ret; |
133 | 132 | ||
134 | ret = swait_event_interruptible_timeout(fw_st->wq, | 133 | ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout); |
135 | __fw_state_is_done(READ_ONCE(fw_st->status)), | ||
136 | timeout); | ||
137 | if (ret != 0 && fw_st->status == FW_STATUS_ABORTED) | 134 | if (ret != 0 && fw_st->status == FW_STATUS_ABORTED) |
138 | return -ENOENT; | 135 | return -ENOENT; |
139 | if (!ret) | 136 | if (!ret) |
@@ -148,35 +145,34 @@ static void __fw_state_set(struct fw_state *fw_st, | |||
148 | WRITE_ONCE(fw_st->status, status); | 145 | WRITE_ONCE(fw_st->status, status); |
149 | 146 | ||
150 | if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) | 147 | if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) |
151 | swake_up(&fw_st->wq); | 148 | complete_all(&fw_st->completion); |
152 | } | 149 | } |
153 | 150 | ||
154 | #define fw_state_start(fw_st) \ | 151 | #define fw_state_start(fw_st) \ |
155 | __fw_state_set(fw_st, FW_STATUS_LOADING) | 152 | __fw_state_set(fw_st, FW_STATUS_LOADING) |
156 | #define fw_state_done(fw_st) \ | 153 | #define fw_state_done(fw_st) \ |
157 | __fw_state_set(fw_st, FW_STATUS_DONE) | 154 | __fw_state_set(fw_st, FW_STATUS_DONE) |
155 | #define fw_state_aborted(fw_st) \ | ||
156 | __fw_state_set(fw_st, FW_STATUS_ABORTED) | ||
158 | #define fw_state_wait(fw_st) \ | 157 | #define fw_state_wait(fw_st) \ |
159 | __fw_state_wait_common(fw_st, MAX_SCHEDULE_TIMEOUT) | 158 | __fw_state_wait_common(fw_st, MAX_SCHEDULE_TIMEOUT) |
160 | 159 | ||
161 | #ifndef CONFIG_FW_LOADER_USER_HELPER | ||
162 | |||
163 | #define fw_state_is_aborted(fw_st) false | ||
164 | |||
165 | #else /* CONFIG_FW_LOADER_USER_HELPER */ | ||
166 | |||
167 | static int __fw_state_check(struct fw_state *fw_st, enum fw_status status) | 160 | static int __fw_state_check(struct fw_state *fw_st, enum fw_status status) |
168 | { | 161 | { |
169 | return fw_st->status == status; | 162 | return fw_st->status == status; |
170 | } | 163 | } |
171 | 164 | ||
165 | #define fw_state_is_aborted(fw_st) \ | ||
166 | __fw_state_check(fw_st, FW_STATUS_ABORTED) | ||
167 | |||
168 | #ifdef CONFIG_FW_LOADER_USER_HELPER | ||
169 | |||
172 | #define fw_state_aborted(fw_st) \ | 170 | #define fw_state_aborted(fw_st) \ |
173 | __fw_state_set(fw_st, FW_STATUS_ABORTED) | 171 | __fw_state_set(fw_st, FW_STATUS_ABORTED) |
174 | #define fw_state_is_done(fw_st) \ | 172 | #define fw_state_is_done(fw_st) \ |
175 | __fw_state_check(fw_st, FW_STATUS_DONE) | 173 | __fw_state_check(fw_st, FW_STATUS_DONE) |
176 | #define fw_state_is_loading(fw_st) \ | 174 | #define fw_state_is_loading(fw_st) \ |
177 | __fw_state_check(fw_st, FW_STATUS_LOADING) | 175 | __fw_state_check(fw_st, FW_STATUS_LOADING) |
178 | #define fw_state_is_aborted(fw_st) \ | ||
179 | __fw_state_check(fw_st, FW_STATUS_ABORTED) | ||
180 | #define fw_state_wait_timeout(fw_st, timeout) \ | 176 | #define fw_state_wait_timeout(fw_st, timeout) \ |
181 | __fw_state_wait_common(fw_st, timeout) | 177 | __fw_state_wait_common(fw_st, timeout) |
182 | 178 | ||
@@ -1200,6 +1196,28 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name, | |||
1200 | return 1; /* need to load */ | 1196 | return 1; /* need to load */ |
1201 | } | 1197 | } |
1202 | 1198 | ||
1199 | /* | ||
1200 | * Batched requests need only one wake, we need to do this step last due to the | ||
1201 | * fallback mechanism. The buf is protected with kref_get(), and it won't be | ||
1202 | * released until the last user calls release_firmware(). | ||
1203 | * | ||
1204 | * Failed batched requests are possible as well, in such cases we just share | ||
1205 | * the struct firmware_buf and won't release it until all requests are woken | ||
1206 | * and have gone through this same path. | ||
1207 | */ | ||
1208 | static void fw_abort_batch_reqs(struct firmware *fw) | ||
1209 | { | ||
1210 | struct firmware_buf *buf; | ||
1211 | |||
1212 | /* Loaded directly? */ | ||
1213 | if (!fw || !fw->priv) | ||
1214 | return; | ||
1215 | |||
1216 | buf = fw->priv; | ||
1217 | if (!fw_state_is_aborted(&buf->fw_st)) | ||
1218 | fw_state_aborted(&buf->fw_st); | ||
1219 | } | ||
1220 | |||
1203 | /* called from request_firmware() and request_firmware_work_func() */ | 1221 | /* called from request_firmware() and request_firmware_work_func() */ |
1204 | static int | 1222 | static int |
1205 | _request_firmware(const struct firmware **firmware_p, const char *name, | 1223 | _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 | 1261 | ||
1244 | out: | 1262 | out: |
1245 | if (ret < 0) { | 1263 | if (ret < 0) { |
1264 | fw_abort_batch_reqs(fw); | ||
1246 | release_firmware(fw); | 1265 | release_firmware(fw); |
1247 | fw = NULL; | 1266 | fw = NULL; |
1248 | } | 1267 | } |