aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorMing Lei <ming.lei@canonical.com>2012-10-09 00:01:02 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-10-22 11:37:17 -0400
commit253c9240ee09fd2a05d315ea44ac037a893d8981 (patch)
tree51694cfbb6d5f4c74e32eb19eb0ee6f5f48e0bb4 /drivers/base
parent373304fe10fc46e68815c7116709ad292695dfd1 (diff)
firmware loader: fix one reqeust_firmware race
Several loading requests may be pending on one same firmware buf, and this patch moves fw_map_pages_buf() before complete_all(&fw_buf->completion) and let all requests see the mapped 'buf->data' once the loading is completed. Signed-off-by: Ming Lei <ming.lei@canonical.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/firmware_class.c32
1 files changed, 20 insertions, 12 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index d06a8d0534bf..f2882511a9c1 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -423,6 +423,18 @@ static void firmware_free_data(const struct firmware *fw)
423#ifndef PAGE_KERNEL_RO 423#ifndef PAGE_KERNEL_RO
424#define PAGE_KERNEL_RO PAGE_KERNEL 424#define PAGE_KERNEL_RO PAGE_KERNEL
425#endif 425#endif
426
427/* one pages buffer should be mapped/unmapped only once */
428static int fw_map_pages_buf(struct firmware_buf *buf)
429{
430 if (buf->data)
431 vunmap(buf->data);
432 buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO);
433 if (!buf->data)
434 return -ENOMEM;
435 return 0;
436}
437
426/** 438/**
427 * firmware_loading_store - set value in the 'loading' control file 439 * firmware_loading_store - set value in the 'loading' control file
428 * @dev: device pointer 440 * @dev: device pointer
@@ -467,6 +479,14 @@ static ssize_t firmware_loading_store(struct device *dev,
467 if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) { 479 if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
468 set_bit(FW_STATUS_DONE, &fw_buf->status); 480 set_bit(FW_STATUS_DONE, &fw_buf->status);
469 clear_bit(FW_STATUS_LOADING, &fw_buf->status); 481 clear_bit(FW_STATUS_LOADING, &fw_buf->status);
482
483 /*
484 * Several loading requests may be pending on
485 * one same firmware buf, so let all requests
486 * see the mapped 'buf->data' once the loading
487 * is completed.
488 * */
489 fw_map_pages_buf(fw_buf);
470 complete_all(&fw_buf->completion); 490 complete_all(&fw_buf->completion);
471 break; 491 break;
472 } 492 }
@@ -670,15 +690,6 @@ exit:
670 return fw_priv; 690 return fw_priv;
671} 691}
672 692
673/* one pages buffer is mapped/unmapped only once */
674static int fw_map_pages_buf(struct firmware_buf *buf)
675{
676 buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO);
677 if (!buf->data)
678 return -ENOMEM;
679 return 0;
680}
681
682/* store the pages buffer info firmware from buf */ 693/* store the pages buffer info firmware from buf */
683static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) 694static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw)
684{ 695{
@@ -884,9 +895,6 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
884 if (!retval && f_dev->parent) 895 if (!retval && f_dev->parent)
885 fw_add_devm_name(f_dev->parent, buf->fw_id); 896 fw_add_devm_name(f_dev->parent, buf->fw_id);
886 897
887 if (!retval)
888 retval = fw_map_pages_buf(buf);
889
890 /* 898 /*
891 * After caching firmware image is started, let it piggyback 899 * After caching firmware image is started, let it piggyback
892 * on request firmware. 900 * on request firmware.