diff options
| -rw-r--r-- | drivers/base/firmware_class.c | 62 |
1 files changed, 39 insertions, 23 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 803cfc1597a9..1cbefcfd15f7 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c | |||
| @@ -93,6 +93,8 @@ struct firmware_priv { | |||
| 93 | struct completion completion; | 93 | struct completion completion; |
| 94 | struct firmware *fw; | 94 | struct firmware *fw; |
| 95 | unsigned long status; | 95 | unsigned long status; |
| 96 | void *data; | ||
| 97 | size_t size; | ||
| 96 | struct page **pages; | 98 | struct page **pages; |
| 97 | int nr_pages; | 99 | int nr_pages; |
| 98 | int page_array_size; | 100 | int page_array_size; |
| @@ -156,9 +158,11 @@ static void fw_dev_release(struct device *dev) | |||
| 156 | struct firmware_priv *fw_priv = to_firmware_priv(dev); | 158 | struct firmware_priv *fw_priv = to_firmware_priv(dev); |
| 157 | int i; | 159 | int i; |
| 158 | 160 | ||
| 161 | /* free untransfered pages buffer */ | ||
| 159 | for (i = 0; i < fw_priv->nr_pages; i++) | 162 | for (i = 0; i < fw_priv->nr_pages; i++) |
| 160 | __free_page(fw_priv->pages[i]); | 163 | __free_page(fw_priv->pages[i]); |
| 161 | kfree(fw_priv->pages); | 164 | kfree(fw_priv->pages); |
| 165 | |||
| 162 | kfree(fw_priv); | 166 | kfree(fw_priv); |
| 163 | 167 | ||
| 164 | module_put(THIS_MODULE); | 168 | module_put(THIS_MODULE); |
| @@ -194,6 +198,7 @@ static ssize_t firmware_loading_show(struct device *dev, | |||
| 194 | return sprintf(buf, "%d\n", loading); | 198 | return sprintf(buf, "%d\n", loading); |
| 195 | } | 199 | } |
| 196 | 200 | ||
| 201 | /* firmware holds the ownership of pages */ | ||
| 197 | static void firmware_free_data(const struct firmware *fw) | 202 | static void firmware_free_data(const struct firmware *fw) |
| 198 | { | 203 | { |
| 199 | int i; | 204 | int i; |
| @@ -237,9 +242,7 @@ static ssize_t firmware_loading_store(struct device *dev, | |||
| 237 | 242 | ||
| 238 | switch (loading) { | 243 | switch (loading) { |
| 239 | case 1: | 244 | case 1: |
| 240 | firmware_free_data(fw_priv->fw); | 245 | /* discarding any previous partial load */ |
| 241 | memset(fw_priv->fw, 0, sizeof(struct firmware)); | ||
| 242 | /* If the pages are not owned by 'struct firmware' */ | ||
| 243 | for (i = 0; i < fw_priv->nr_pages; i++) | 246 | for (i = 0; i < fw_priv->nr_pages; i++) |
| 244 | __free_page(fw_priv->pages[i]); | 247 | __free_page(fw_priv->pages[i]); |
| 245 | kfree(fw_priv->pages); | 248 | kfree(fw_priv->pages); |
| @@ -250,20 +253,6 @@ static ssize_t firmware_loading_store(struct device *dev, | |||
| 250 | break; | 253 | break; |
| 251 | case 0: | 254 | case 0: |
| 252 | if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { | 255 | if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { |
| 253 | vunmap(fw_priv->fw->data); | ||
| 254 | fw_priv->fw->data = vmap(fw_priv->pages, | ||
| 255 | fw_priv->nr_pages, | ||
| 256 | 0, PAGE_KERNEL_RO); | ||
| 257 | if (!fw_priv->fw->data) { | ||
| 258 | dev_err(dev, "%s: vmap() failed\n", __func__); | ||
| 259 | goto err; | ||
| 260 | } | ||
| 261 | /* Pages are now owned by 'struct firmware' */ | ||
| 262 | fw_priv->fw->pages = fw_priv->pages; | ||
| 263 | fw_priv->pages = NULL; | ||
| 264 | |||
| 265 | fw_priv->page_array_size = 0; | ||
| 266 | fw_priv->nr_pages = 0; | ||
| 267 | complete(&fw_priv->completion); | 256 | complete(&fw_priv->completion); |
| 268 | clear_bit(FW_STATUS_LOADING, &fw_priv->status); | 257 | clear_bit(FW_STATUS_LOADING, &fw_priv->status); |
| 269 | break; | 258 | break; |
| @@ -273,7 +262,6 @@ static ssize_t firmware_loading_store(struct device *dev, | |||
| 273 | dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); | 262 | dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); |
| 274 | /* fallthrough */ | 263 | /* fallthrough */ |
| 275 | case -1: | 264 | case -1: |
| 276 | err: | ||
| 277 | fw_load_abort(fw_priv); | 265 | fw_load_abort(fw_priv); |
| 278 | break; | 266 | break; |
| 279 | } | 267 | } |
| @@ -299,12 +287,12 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj, | |||
| 299 | ret_count = -ENODEV; | 287 | ret_count = -ENODEV; |
| 300 | goto out; | 288 | goto out; |
| 301 | } | 289 | } |
| 302 | if (offset > fw->size) { | 290 | if (offset > fw_priv->size) { |
| 303 | ret_count = 0; | 291 | ret_count = 0; |
| 304 | goto out; | 292 | goto out; |
| 305 | } | 293 | } |
| 306 | if (count > fw->size - offset) | 294 | if (count > fw_priv->size - offset) |
| 307 | count = fw->size - offset; | 295 | count = fw_priv->size - offset; |
| 308 | 296 | ||
| 309 | ret_count = count; | 297 | ret_count = count; |
| 310 | 298 | ||
| @@ -396,6 +384,7 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj, | |||
| 396 | retval = -ENODEV; | 384 | retval = -ENODEV; |
| 397 | goto out; | 385 | goto out; |
| 398 | } | 386 | } |
| 387 | |||
| 399 | retval = fw_realloc_buffer(fw_priv, offset + count); | 388 | retval = fw_realloc_buffer(fw_priv, offset + count); |
| 400 | if (retval) | 389 | if (retval) |
| 401 | goto out; | 390 | goto out; |
| @@ -418,7 +407,7 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj, | |||
| 418 | count -= page_cnt; | 407 | count -= page_cnt; |
| 419 | } | 408 | } |
| 420 | 409 | ||
| 421 | fw->size = max_t(size_t, offset, fw->size); | 410 | fw_priv->size = max_t(size_t, offset, fw_priv->size); |
| 422 | out: | 411 | out: |
| 423 | mutex_unlock(&fw_lock); | 412 | mutex_unlock(&fw_lock); |
| 424 | return retval; | 413 | return retval; |
| @@ -504,6 +493,29 @@ static void _request_firmware_cleanup(const struct firmware **firmware_p) | |||
| 504 | *firmware_p = NULL; | 493 | *firmware_p = NULL; |
| 505 | } | 494 | } |
| 506 | 495 | ||
| 496 | /* transfer the ownership of pages to firmware */ | ||
| 497 | static int fw_set_page_data(struct firmware_priv *fw_priv) | ||
| 498 | { | ||
| 499 | struct firmware *fw = fw_priv->fw; | ||
| 500 | |||
| 501 | fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, | ||
| 502 | 0, PAGE_KERNEL_RO); | ||
| 503 | if (!fw_priv->data) | ||
| 504 | return -ENOMEM; | ||
| 505 | |||
| 506 | fw->data = fw_priv->data; | ||
| 507 | fw->pages = fw_priv->pages; | ||
| 508 | fw->size = fw_priv->size; | ||
| 509 | |||
| 510 | WARN_ON(PFN_UP(fw->size) != fw_priv->nr_pages); | ||
| 511 | |||
| 512 | fw_priv->nr_pages = 0; | ||
| 513 | fw_priv->pages = NULL; | ||
| 514 | fw_priv->data = NULL; | ||
| 515 | |||
| 516 | return 0; | ||
| 517 | } | ||
| 518 | |||
| 507 | static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | 519 | static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, |
| 508 | long timeout) | 520 | long timeout) |
| 509 | { | 521 | { |
| @@ -549,8 +561,12 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | |||
| 549 | del_timer_sync(&fw_priv->timeout); | 561 | del_timer_sync(&fw_priv->timeout); |
| 550 | 562 | ||
| 551 | mutex_lock(&fw_lock); | 563 | mutex_lock(&fw_lock); |
| 552 | if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) | 564 | if (!fw_priv->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) |
| 553 | retval = -ENOENT; | 565 | retval = -ENOENT; |
| 566 | |||
| 567 | /* transfer pages ownership at the last minute */ | ||
| 568 | if (!retval) | ||
| 569 | retval = fw_set_page_data(fw_priv); | ||
| 554 | fw_priv->fw = NULL; | 570 | fw_priv->fw = NULL; |
| 555 | mutex_unlock(&fw_lock); | 571 | mutex_unlock(&fw_lock); |
| 556 | 572 | ||
