diff options
Diffstat (limited to 'drivers/base')
| -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 | } |
