diff options
author | Takashi Iwai <tiwai@suse.de> | 2013-01-31 05:13:54 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-02-03 20:57:40 -0500 |
commit | 4e0c92d015235d1dc65f9668a10cef2cea468ba2 (patch) | |
tree | 33ebc946f3ea15b14c40865001f5ba4b244f268c /drivers/base/firmware_class.c | |
parent | 4fa3e78be7e985ca814ce2aa0c09cbee404efcf7 (diff) |
firmware: Refactoring for splitting user-mode helper code
Since 3.7 kernel, the firmware loader can read the firmware files
directly, and the traditional user-mode helper is invoked only as a
fallback. This seems working pretty well, and the next step would be
to reduce the redundant user-mode helper stuff in future.
This patch is a preparation for that: refactor the code for splitting
user-mode helper stuff more easily. No functional change.
Acked-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base/firmware_class.c')
-rw-r--r-- | drivers/base/firmware_class.c | 287 |
1 files changed, 154 insertions, 133 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b392b353be39..40ec60affd8f 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c | |||
@@ -319,7 +319,8 @@ static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf | |||
319 | return true; | 319 | return true; |
320 | } | 320 | } |
321 | 321 | ||
322 | static bool fw_get_filesystem_firmware(struct firmware_buf *buf) | 322 | static bool fw_get_filesystem_firmware(struct device *device, |
323 | struct firmware_buf *buf) | ||
323 | { | 324 | { |
324 | int i; | 325 | int i; |
325 | bool success = false; | 326 | bool success = false; |
@@ -343,6 +344,16 @@ static bool fw_get_filesystem_firmware(struct firmware_buf *buf) | |||
343 | break; | 344 | break; |
344 | } | 345 | } |
345 | __putname(path); | 346 | __putname(path); |
347 | |||
348 | if (success) { | ||
349 | dev_dbg(device, "firmware: direct-loading firmware %s\n", | ||
350 | buf->fw_id); | ||
351 | mutex_lock(&fw_lock); | ||
352 | set_bit(FW_STATUS_DONE, &buf->status); | ||
353 | complete_all(&buf->completion); | ||
354 | mutex_unlock(&fw_lock); | ||
355 | } | ||
356 | |||
346 | return success; | 357 | return success; |
347 | } | 358 | } |
348 | 359 | ||
@@ -796,99 +807,112 @@ static int fw_add_devm_name(struct device *dev, const char *name) | |||
796 | } | 807 | } |
797 | #endif | 808 | #endif |
798 | 809 | ||
799 | static void _request_firmware_cleanup(const struct firmware **firmware_p) | 810 | /* wait until the shared firmware_buf becomes ready (or error) */ |
811 | static int sync_cached_firmware_buf(struct firmware_buf *buf) | ||
800 | { | 812 | { |
801 | release_firmware(*firmware_p); | 813 | int ret = 0; |
802 | *firmware_p = NULL; | 814 | |
815 | mutex_lock(&fw_lock); | ||
816 | while (!test_bit(FW_STATUS_DONE, &buf->status)) { | ||
817 | if (test_bit(FW_STATUS_ABORT, &buf->status)) { | ||
818 | ret = -ENOENT; | ||
819 | break; | ||
820 | } | ||
821 | mutex_unlock(&fw_lock); | ||
822 | wait_for_completion(&buf->completion); | ||
823 | mutex_lock(&fw_lock); | ||
824 | } | ||
825 | mutex_unlock(&fw_lock); | ||
826 | return ret; | ||
803 | } | 827 | } |
804 | 828 | ||
805 | static struct firmware_priv * | 829 | /* prepare firmware and firmware_buf structs; |
806 | _request_firmware_prepare(const struct firmware **firmware_p, const char *name, | 830 | * return 0 if a firmware is already assigned, 1 if need to load one, |
807 | struct device *device, bool uevent, bool nowait) | 831 | * or a negative error code |
832 | */ | ||
833 | static int | ||
834 | _request_firmware_prepare(struct firmware **firmware_p, const char *name, | ||
835 | struct device *device) | ||
808 | { | 836 | { |
809 | struct firmware *firmware; | 837 | struct firmware *firmware; |
810 | struct firmware_priv *fw_priv = NULL; | ||
811 | struct firmware_buf *buf; | 838 | struct firmware_buf *buf; |
812 | int ret; | 839 | int ret; |
813 | 840 | ||
814 | if (!firmware_p) | ||
815 | return ERR_PTR(-EINVAL); | ||
816 | |||
817 | *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); | 841 | *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); |
818 | if (!firmware) { | 842 | if (!firmware) { |
819 | dev_err(device, "%s: kmalloc(struct firmware) failed\n", | 843 | dev_err(device, "%s: kmalloc(struct firmware) failed\n", |
820 | __func__); | 844 | __func__); |
821 | return ERR_PTR(-ENOMEM); | 845 | return -ENOMEM; |
822 | } | 846 | } |
823 | 847 | ||
824 | if (fw_get_builtin_firmware(firmware, name)) { | 848 | if (fw_get_builtin_firmware(firmware, name)) { |
825 | dev_dbg(device, "firmware: using built-in firmware %s\n", name); | 849 | dev_dbg(device, "firmware: using built-in firmware %s\n", name); |
826 | return NULL; | 850 | return 0; /* assigned */ |
827 | } | 851 | } |
828 | 852 | ||
829 | ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); | 853 | ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); |
830 | if (!ret) | ||
831 | fw_priv = fw_create_instance(firmware, name, device, | ||
832 | uevent, nowait); | ||
833 | 854 | ||
834 | if (IS_ERR(fw_priv) || ret < 0) { | 855 | /* |
835 | kfree(firmware); | 856 | * bind with 'buf' now to avoid warning in failure path |
836 | *firmware_p = NULL; | 857 | * of requesting firmware. |
837 | return ERR_PTR(-ENOMEM); | 858 | */ |
838 | } else if (fw_priv) { | 859 | firmware->priv = buf; |
839 | fw_priv->buf = buf; | ||
840 | 860 | ||
841 | /* | 861 | if (ret > 0) { |
842 | * bind with 'buf' now to avoid warning in failure path | 862 | ret = sync_cached_firmware_buf(buf); |
843 | * of requesting firmware. | 863 | if (!ret) { |
844 | */ | 864 | fw_set_page_data(buf, firmware); |
845 | firmware->priv = buf; | 865 | return 0; /* assigned */ |
846 | return fw_priv; | 866 | } |
847 | } | 867 | } |
848 | 868 | ||
849 | /* share the cached buf, which is inprogessing or completed */ | 869 | if (ret < 0) |
850 | check_status: | 870 | return ret; |
871 | return 1; /* need to load */ | ||
872 | } | ||
873 | |||
874 | static int assign_firmware_buf(struct firmware *fw, struct device *device) | ||
875 | { | ||
876 | struct firmware_buf *buf = fw->priv; | ||
877 | |||
851 | mutex_lock(&fw_lock); | 878 | mutex_lock(&fw_lock); |
852 | if (test_bit(FW_STATUS_ABORT, &buf->status)) { | 879 | if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) { |
853 | fw_priv = ERR_PTR(-ENOENT); | 880 | mutex_unlock(&fw_lock); |
854 | firmware->priv = buf; | 881 | return -ENOENT; |
855 | _request_firmware_cleanup(firmware_p); | ||
856 | goto exit; | ||
857 | } else if (test_bit(FW_STATUS_DONE, &buf->status)) { | ||
858 | fw_priv = NULL; | ||
859 | fw_set_page_data(buf, firmware); | ||
860 | goto exit; | ||
861 | } | 882 | } |
862 | mutex_unlock(&fw_lock); | ||
863 | wait_for_completion(&buf->completion); | ||
864 | goto check_status; | ||
865 | 883 | ||
866 | exit: | 884 | /* |
885 | * add firmware name into devres list so that we can auto cache | ||
886 | * and uncache firmware for device. | ||
887 | * | ||
888 | * device may has been deleted already, but the problem | ||
889 | * should be fixed in devres or driver core. | ||
890 | */ | ||
891 | if (device) | ||
892 | fw_add_devm_name(device, buf->fw_id); | ||
893 | |||
894 | /* | ||
895 | * After caching firmware image is started, let it piggyback | ||
896 | * on request firmware. | ||
897 | */ | ||
898 | if (buf->fwc->state == FW_LOADER_START_CACHE) { | ||
899 | if (fw_cache_piggyback_on_request(buf->fw_id)) | ||
900 | kref_get(&buf->ref); | ||
901 | } | ||
902 | |||
903 | /* pass the pages buffer to driver at the last minute */ | ||
904 | fw_set_page_data(buf, fw); | ||
867 | mutex_unlock(&fw_lock); | 905 | mutex_unlock(&fw_lock); |
868 | return fw_priv; | 906 | return 0; |
869 | } | 907 | } |
870 | 908 | ||
909 | /* load a firmware via user helper */ | ||
871 | static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | 910 | static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, |
872 | long timeout) | 911 | long timeout) |
873 | { | 912 | { |
874 | int retval = 0; | 913 | int retval = 0; |
875 | struct device *f_dev = &fw_priv->dev; | 914 | struct device *f_dev = &fw_priv->dev; |
876 | struct firmware_buf *buf = fw_priv->buf; | 915 | struct firmware_buf *buf = fw_priv->buf; |
877 | struct firmware_cache *fwc = &fw_cache; | ||
878 | int direct_load = 0; | ||
879 | |||
880 | /* try direct loading from fs first */ | ||
881 | if (fw_get_filesystem_firmware(buf)) { | ||
882 | dev_dbg(f_dev->parent, "firmware: direct-loading" | ||
883 | " firmware %s\n", buf->fw_id); | ||
884 | |||
885 | mutex_lock(&fw_lock); | ||
886 | set_bit(FW_STATUS_DONE, &buf->status); | ||
887 | mutex_unlock(&fw_lock); | ||
888 | complete_all(&buf->completion); | ||
889 | direct_load = 1; | ||
890 | goto handle_fw; | ||
891 | } | ||
892 | 916 | ||
893 | /* fall back on userspace loading */ | 917 | /* fall back on userspace loading */ |
894 | buf->fmt = PAGE_BUF; | 918 | buf->fmt = PAGE_BUF; |
@@ -929,38 +953,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | |||
929 | 953 | ||
930 | cancel_delayed_work_sync(&fw_priv->timeout_work); | 954 | cancel_delayed_work_sync(&fw_priv->timeout_work); |
931 | 955 | ||
932 | handle_fw: | ||
933 | mutex_lock(&fw_lock); | ||
934 | if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) | ||
935 | retval = -ENOENT; | ||
936 | |||
937 | /* | ||
938 | * add firmware name into devres list so that we can auto cache | ||
939 | * and uncache firmware for device. | ||
940 | * | ||
941 | * f_dev->parent may has been deleted already, but the problem | ||
942 | * should be fixed in devres or driver core. | ||
943 | */ | ||
944 | if (!retval && f_dev->parent) | ||
945 | fw_add_devm_name(f_dev->parent, buf->fw_id); | ||
946 | |||
947 | /* | ||
948 | * After caching firmware image is started, let it piggyback | ||
949 | * on request firmware. | ||
950 | */ | ||
951 | if (!retval && fwc->state == FW_LOADER_START_CACHE) { | ||
952 | if (fw_cache_piggyback_on_request(buf->fw_id)) | ||
953 | kref_get(&buf->ref); | ||
954 | } | ||
955 | |||
956 | /* pass the pages buffer to driver at the last minute */ | ||
957 | fw_set_page_data(buf, fw_priv->fw); | ||
958 | |||
959 | fw_priv->buf = NULL; | 956 | fw_priv->buf = NULL; |
960 | mutex_unlock(&fw_lock); | ||
961 | |||
962 | if (direct_load) | ||
963 | goto err_put_dev; | ||
964 | 957 | ||
965 | device_remove_file(f_dev, &dev_attr_loading); | 958 | device_remove_file(f_dev, &dev_attr_loading); |
966 | err_del_bin_attr: | 959 | err_del_bin_attr: |
@@ -972,6 +965,73 @@ err_put_dev: | |||
972 | return retval; | 965 | return retval; |
973 | } | 966 | } |
974 | 967 | ||
968 | static int fw_load_from_user_helper(struct firmware *firmware, | ||
969 | const char *name, struct device *device, | ||
970 | bool uevent, bool nowait, long timeout) | ||
971 | { | ||
972 | struct firmware_priv *fw_priv; | ||
973 | |||
974 | fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); | ||
975 | if (IS_ERR(fw_priv)) | ||
976 | return PTR_ERR(fw_priv); | ||
977 | |||
978 | fw_priv->buf = firmware->priv; | ||
979 | return _request_firmware_load(fw_priv, uevent, timeout); | ||
980 | } | ||
981 | |||
982 | /* called from request_firmware() and request_firmware_work_func() */ | ||
983 | static int | ||
984 | _request_firmware(const struct firmware **firmware_p, const char *name, | ||
985 | struct device *device, bool uevent, bool nowait) | ||
986 | { | ||
987 | struct firmware *fw; | ||
988 | long timeout; | ||
989 | int ret; | ||
990 | |||
991 | if (!firmware_p) | ||
992 | return -EINVAL; | ||
993 | |||
994 | ret = _request_firmware_prepare(&fw, name, device); | ||
995 | if (ret <= 0) /* error or already assigned */ | ||
996 | goto out; | ||
997 | |||
998 | ret = 0; | ||
999 | timeout = firmware_loading_timeout(); | ||
1000 | if (nowait) { | ||
1001 | timeout = usermodehelper_read_lock_wait(timeout); | ||
1002 | if (!timeout) { | ||
1003 | dev_dbg(device, "firmware: %s loading timed out\n", | ||
1004 | name); | ||
1005 | ret = -EBUSY; | ||
1006 | goto out; | ||
1007 | } | ||
1008 | } else { | ||
1009 | ret = usermodehelper_read_trylock(); | ||
1010 | if (WARN_ON(ret)) { | ||
1011 | dev_err(device, "firmware: %s will not be loaded\n", | ||
1012 | name); | ||
1013 | goto out; | ||
1014 | } | ||
1015 | } | ||
1016 | |||
1017 | if (!fw_get_filesystem_firmware(device, fw->priv)) | ||
1018 | ret = fw_load_from_user_helper(fw, name, device, | ||
1019 | uevent, nowait, timeout); | ||
1020 | if (!ret) | ||
1021 | ret = assign_firmware_buf(fw, device); | ||
1022 | |||
1023 | usermodehelper_read_unlock(); | ||
1024 | |||
1025 | out: | ||
1026 | if (ret < 0) { | ||
1027 | release_firmware(fw); | ||
1028 | fw = NULL; | ||
1029 | } | ||
1030 | |||
1031 | *firmware_p = fw; | ||
1032 | return ret; | ||
1033 | } | ||
1034 | |||
975 | /** | 1035 | /** |
976 | * request_firmware: - send firmware request and wait for it | 1036 | * request_firmware: - send firmware request and wait for it |
977 | * @firmware_p: pointer to firmware image | 1037 | * @firmware_p: pointer to firmware image |
@@ -996,26 +1056,7 @@ int | |||
996 | request_firmware(const struct firmware **firmware_p, const char *name, | 1056 | request_firmware(const struct firmware **firmware_p, const char *name, |
997 | struct device *device) | 1057 | struct device *device) |
998 | { | 1058 | { |
999 | struct firmware_priv *fw_priv; | 1059 | return _request_firmware(firmware_p, name, device, true, false); |
1000 | int ret; | ||
1001 | |||
1002 | fw_priv = _request_firmware_prepare(firmware_p, name, device, true, | ||
1003 | false); | ||
1004 | if (IS_ERR_OR_NULL(fw_priv)) | ||
1005 | return PTR_RET(fw_priv); | ||
1006 | |||
1007 | ret = usermodehelper_read_trylock(); | ||
1008 | if (WARN_ON(ret)) { | ||
1009 | dev_err(device, "firmware: %s will not be loaded\n", name); | ||
1010 | } else { | ||
1011 | ret = _request_firmware_load(fw_priv, true, | ||
1012 | firmware_loading_timeout()); | ||
1013 | usermodehelper_read_unlock(); | ||
1014 | } | ||
1015 | if (ret) | ||
1016 | _request_firmware_cleanup(firmware_p); | ||
1017 | |||
1018 | return ret; | ||
1019 | } | 1060 | } |
1020 | 1061 | ||
1021 | /** | 1062 | /** |
@@ -1046,33 +1087,13 @@ static void request_firmware_work_func(struct work_struct *work) | |||
1046 | { | 1087 | { |
1047 | struct firmware_work *fw_work; | 1088 | struct firmware_work *fw_work; |
1048 | const struct firmware *fw; | 1089 | const struct firmware *fw; |
1049 | struct firmware_priv *fw_priv; | ||
1050 | long timeout; | ||
1051 | int ret; | ||
1052 | 1090 | ||
1053 | fw_work = container_of(work, struct firmware_work, work); | 1091 | fw_work = container_of(work, struct firmware_work, work); |
1054 | fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device, | ||
1055 | fw_work->uevent, true); | ||
1056 | if (IS_ERR_OR_NULL(fw_priv)) { | ||
1057 | ret = PTR_RET(fw_priv); | ||
1058 | goto out; | ||
1059 | } | ||
1060 | 1092 | ||
1061 | timeout = usermodehelper_read_lock_wait(firmware_loading_timeout()); | 1093 | _request_firmware(&fw, fw_work->name, fw_work->device, |
1062 | if (timeout) { | 1094 | fw_work->uevent, true); |
1063 | ret = _request_firmware_load(fw_priv, fw_work->uevent, timeout); | ||
1064 | usermodehelper_read_unlock(); | ||
1065 | } else { | ||
1066 | dev_dbg(fw_work->device, "firmware: %s loading timed out\n", | ||
1067 | fw_work->name); | ||
1068 | ret = -EAGAIN; | ||
1069 | } | ||
1070 | if (ret) | ||
1071 | _request_firmware_cleanup(&fw); | ||
1072 | |||
1073 | out: | ||
1074 | fw_work->cont(fw, fw_work->context); | 1095 | fw_work->cont(fw, fw_work->context); |
1075 | put_device(fw_work->device); | 1096 | put_device(fw_work->device); /* taken in request_firmware_nowait() */ |
1076 | 1097 | ||
1077 | module_put(fw_work->module); | 1098 | module_put(fw_work->module); |
1078 | kfree(fw_work); | 1099 | kfree(fw_work); |