diff options
author | Chuansheng Liu <chuansheng.liu@intel.com> | 2012-11-08 06:14:40 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-11-14 18:04:23 -0500 |
commit | ce2fcbd99cef580623116bb33531dbc3e6f690b0 (patch) | |
tree | baeea3fda4375556df4c54c1a6402f536c2a93f9 | |
parent | ecdca043ebe8e3172e0400d0966831e2e60870a0 (diff) |
firmware loader: Fix the race FW_STATUS_DONE is followed by class_timeout
There is a race as below when calling request_firmware():
CPU1 CPU2
write 0 > loading
mutex_lock(&fw_lock)
...
set_bit FW_STATUS_DONE class_timeout is coming
set_bit FW_STATUS_ABORT
complete_all &completion
...
mutex_unlock(&fw_lock)
In this time, the bit FW_STATUS_DONE and FW_STATUS_ABORT are set,
and request_firmware() will return failure due to condition in
_request_firmware_load():
if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status))
retval = -ENOENT;
But from the above scenerio, it should be a successful requesting.
So we need judge if the bit FW_STATUS_DONE is already set before
calling fw_load_abort() in timeout function.
As Ming's proposal, we need change the timer into sched_work to
benefit from using &fw_lock mutex also.
Signed-off-by: liu chuansheng <chuansheng.liu@intel.com>
Acked-by: Ming Lei <ming.lei@canonical.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/base/firmware_class.c | 24 |
1 files changed, 16 insertions, 8 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 8945f4e489ed..b44ed35ac38d 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c | |||
@@ -143,7 +143,7 @@ struct fw_cache_entry { | |||
143 | }; | 143 | }; |
144 | 144 | ||
145 | struct firmware_priv { | 145 | struct firmware_priv { |
146 | struct timer_list timeout; | 146 | struct delayed_work timeout_work; |
147 | bool nowait; | 147 | bool nowait; |
148 | struct device dev; | 148 | struct device dev; |
149 | struct firmware_buf *buf; | 149 | struct firmware_buf *buf; |
@@ -667,11 +667,18 @@ static struct bin_attribute firmware_attr_data = { | |||
667 | .write = firmware_data_write, | 667 | .write = firmware_data_write, |
668 | }; | 668 | }; |
669 | 669 | ||
670 | static void firmware_class_timeout(u_long data) | 670 | static void firmware_class_timeout_work(struct work_struct *work) |
671 | { | 671 | { |
672 | struct firmware_priv *fw_priv = (struct firmware_priv *) data; | 672 | struct firmware_priv *fw_priv = container_of(work, |
673 | struct firmware_priv, timeout_work.work); | ||
673 | 674 | ||
675 | mutex_lock(&fw_lock); | ||
676 | if (test_bit(FW_STATUS_DONE, &(fw_priv->buf->status))) { | ||
677 | mutex_unlock(&fw_lock); | ||
678 | return; | ||
679 | } | ||
674 | fw_load_abort(fw_priv); | 680 | fw_load_abort(fw_priv); |
681 | mutex_unlock(&fw_lock); | ||
675 | } | 682 | } |
676 | 683 | ||
677 | static struct firmware_priv * | 684 | static struct firmware_priv * |
@@ -690,8 +697,8 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, | |||
690 | 697 | ||
691 | fw_priv->nowait = nowait; | 698 | fw_priv->nowait = nowait; |
692 | fw_priv->fw = firmware; | 699 | fw_priv->fw = firmware; |
693 | setup_timer(&fw_priv->timeout, | 700 | INIT_DELAYED_WORK(&fw_priv->timeout_work, |
694 | firmware_class_timeout, (u_long) fw_priv); | 701 | firmware_class_timeout_work); |
695 | 702 | ||
696 | f_dev = &fw_priv->dev; | 703 | f_dev = &fw_priv->dev; |
697 | 704 | ||
@@ -858,7 +865,9 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | |||
858 | dev_dbg(f_dev->parent, "firmware: direct-loading" | 865 | dev_dbg(f_dev->parent, "firmware: direct-loading" |
859 | " firmware %s\n", buf->fw_id); | 866 | " firmware %s\n", buf->fw_id); |
860 | 867 | ||
868 | mutex_lock(&fw_lock); | ||
861 | set_bit(FW_STATUS_DONE, &buf->status); | 869 | set_bit(FW_STATUS_DONE, &buf->status); |
870 | mutex_unlock(&fw_lock); | ||
862 | complete_all(&buf->completion); | 871 | complete_all(&buf->completion); |
863 | direct_load = 1; | 872 | direct_load = 1; |
864 | goto handle_fw; | 873 | goto handle_fw; |
@@ -894,15 +903,14 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | |||
894 | dev_set_uevent_suppress(f_dev, false); | 903 | dev_set_uevent_suppress(f_dev, false); |
895 | dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); | 904 | dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); |
896 | if (timeout != MAX_SCHEDULE_TIMEOUT) | 905 | if (timeout != MAX_SCHEDULE_TIMEOUT) |
897 | mod_timer(&fw_priv->timeout, | 906 | schedule_delayed_work(&fw_priv->timeout_work, timeout); |
898 | round_jiffies_up(jiffies + timeout)); | ||
899 | 907 | ||
900 | kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); | 908 | kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); |
901 | } | 909 | } |
902 | 910 | ||
903 | wait_for_completion(&buf->completion); | 911 | wait_for_completion(&buf->completion); |
904 | 912 | ||
905 | del_timer_sync(&fw_priv->timeout); | 913 | cancel_delayed_work_sync(&fw_priv->timeout_work); |
906 | 914 | ||
907 | handle_fw: | 915 | handle_fw: |
908 | mutex_lock(&fw_lock); | 916 | mutex_lock(&fw_lock); |