From 4e0c92d015235d1dc65f9668a10cef2cea468ba2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 31 Jan 2013 11:13:54 +0100 Subject: 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 Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 287 ++++++++++++++++++++++-------------------- 1 file changed, 154 insertions(+), 133 deletions(-) (limited to 'drivers/base/firmware_class.c') 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 return true; } -static bool fw_get_filesystem_firmware(struct firmware_buf *buf) +static bool fw_get_filesystem_firmware(struct device *device, + struct firmware_buf *buf) { int i; bool success = false; @@ -343,6 +344,16 @@ static bool fw_get_filesystem_firmware(struct firmware_buf *buf) break; } __putname(path); + + if (success) { + dev_dbg(device, "firmware: direct-loading firmware %s\n", + buf->fw_id); + mutex_lock(&fw_lock); + set_bit(FW_STATUS_DONE, &buf->status); + complete_all(&buf->completion); + mutex_unlock(&fw_lock); + } + return success; } @@ -796,99 +807,112 @@ static int fw_add_devm_name(struct device *dev, const char *name) } #endif -static void _request_firmware_cleanup(const struct firmware **firmware_p) +/* wait until the shared firmware_buf becomes ready (or error) */ +static int sync_cached_firmware_buf(struct firmware_buf *buf) { - release_firmware(*firmware_p); - *firmware_p = NULL; + int ret = 0; + + mutex_lock(&fw_lock); + while (!test_bit(FW_STATUS_DONE, &buf->status)) { + if (test_bit(FW_STATUS_ABORT, &buf->status)) { + ret = -ENOENT; + break; + } + mutex_unlock(&fw_lock); + wait_for_completion(&buf->completion); + mutex_lock(&fw_lock); + } + mutex_unlock(&fw_lock); + return ret; } -static struct firmware_priv * -_request_firmware_prepare(const struct firmware **firmware_p, const char *name, - struct device *device, bool uevent, bool nowait) +/* prepare firmware and firmware_buf structs; + * return 0 if a firmware is already assigned, 1 if need to load one, + * or a negative error code + */ +static int +_request_firmware_prepare(struct firmware **firmware_p, const char *name, + struct device *device) { struct firmware *firmware; - struct firmware_priv *fw_priv = NULL; struct firmware_buf *buf; int ret; - if (!firmware_p) - return ERR_PTR(-EINVAL); - *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); if (!firmware) { dev_err(device, "%s: kmalloc(struct firmware) failed\n", __func__); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } if (fw_get_builtin_firmware(firmware, name)) { dev_dbg(device, "firmware: using built-in firmware %s\n", name); - return NULL; + return 0; /* assigned */ } ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); - if (!ret) - fw_priv = fw_create_instance(firmware, name, device, - uevent, nowait); - if (IS_ERR(fw_priv) || ret < 0) { - kfree(firmware); - *firmware_p = NULL; - return ERR_PTR(-ENOMEM); - } else if (fw_priv) { - fw_priv->buf = buf; + /* + * bind with 'buf' now to avoid warning in failure path + * of requesting firmware. + */ + firmware->priv = buf; - /* - * bind with 'buf' now to avoid warning in failure path - * of requesting firmware. - */ - firmware->priv = buf; - return fw_priv; + if (ret > 0) { + ret = sync_cached_firmware_buf(buf); + if (!ret) { + fw_set_page_data(buf, firmware); + return 0; /* assigned */ + } } - /* share the cached buf, which is inprogessing or completed */ - check_status: + if (ret < 0) + return ret; + return 1; /* need to load */ +} + +static int assign_firmware_buf(struct firmware *fw, struct device *device) +{ + struct firmware_buf *buf = fw->priv; + mutex_lock(&fw_lock); - if (test_bit(FW_STATUS_ABORT, &buf->status)) { - fw_priv = ERR_PTR(-ENOENT); - firmware->priv = buf; - _request_firmware_cleanup(firmware_p); - goto exit; - } else if (test_bit(FW_STATUS_DONE, &buf->status)) { - fw_priv = NULL; - fw_set_page_data(buf, firmware); - goto exit; + if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) { + mutex_unlock(&fw_lock); + return -ENOENT; } - mutex_unlock(&fw_lock); - wait_for_completion(&buf->completion); - goto check_status; -exit: + /* + * add firmware name into devres list so that we can auto cache + * and uncache firmware for device. + * + * device may has been deleted already, but the problem + * should be fixed in devres or driver core. + */ + if (device) + fw_add_devm_name(device, buf->fw_id); + + /* + * After caching firmware image is started, let it piggyback + * on request firmware. + */ + if (buf->fwc->state == FW_LOADER_START_CACHE) { + if (fw_cache_piggyback_on_request(buf->fw_id)) + kref_get(&buf->ref); + } + + /* pass the pages buffer to driver at the last minute */ + fw_set_page_data(buf, fw); mutex_unlock(&fw_lock); - return fw_priv; + return 0; } +/* load a firmware via user helper */ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, long timeout) { int retval = 0; struct device *f_dev = &fw_priv->dev; struct firmware_buf *buf = fw_priv->buf; - struct firmware_cache *fwc = &fw_cache; - int direct_load = 0; - - /* try direct loading from fs first */ - if (fw_get_filesystem_firmware(buf)) { - dev_dbg(f_dev->parent, "firmware: direct-loading" - " firmware %s\n", buf->fw_id); - - mutex_lock(&fw_lock); - set_bit(FW_STATUS_DONE, &buf->status); - mutex_unlock(&fw_lock); - complete_all(&buf->completion); - direct_load = 1; - goto handle_fw; - } /* fall back on userspace loading */ buf->fmt = PAGE_BUF; @@ -929,38 +953,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, cancel_delayed_work_sync(&fw_priv->timeout_work); -handle_fw: - mutex_lock(&fw_lock); - if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) - retval = -ENOENT; - - /* - * add firmware name into devres list so that we can auto cache - * and uncache firmware for device. - * - * f_dev->parent may has been deleted already, but the problem - * should be fixed in devres or driver core. - */ - if (!retval && f_dev->parent) - fw_add_devm_name(f_dev->parent, buf->fw_id); - - /* - * After caching firmware image is started, let it piggyback - * on request firmware. - */ - if (!retval && fwc->state == FW_LOADER_START_CACHE) { - if (fw_cache_piggyback_on_request(buf->fw_id)) - kref_get(&buf->ref); - } - - /* pass the pages buffer to driver at the last minute */ - fw_set_page_data(buf, fw_priv->fw); - fw_priv->buf = NULL; - mutex_unlock(&fw_lock); - - if (direct_load) - goto err_put_dev; device_remove_file(f_dev, &dev_attr_loading); err_del_bin_attr: @@ -972,6 +965,73 @@ err_put_dev: return retval; } +static int fw_load_from_user_helper(struct firmware *firmware, + const char *name, struct device *device, + bool uevent, bool nowait, long timeout) +{ + struct firmware_priv *fw_priv; + + fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); + if (IS_ERR(fw_priv)) + return PTR_ERR(fw_priv); + + fw_priv->buf = firmware->priv; + return _request_firmware_load(fw_priv, uevent, timeout); +} + +/* called from request_firmware() and request_firmware_work_func() */ +static int +_request_firmware(const struct firmware **firmware_p, const char *name, + struct device *device, bool uevent, bool nowait) +{ + struct firmware *fw; + long timeout; + int ret; + + if (!firmware_p) + return -EINVAL; + + ret = _request_firmware_prepare(&fw, name, device); + if (ret <= 0) /* error or already assigned */ + goto out; + + ret = 0; + timeout = firmware_loading_timeout(); + if (nowait) { + timeout = usermodehelper_read_lock_wait(timeout); + if (!timeout) { + dev_dbg(device, "firmware: %s loading timed out\n", + name); + ret = -EBUSY; + goto out; + } + } else { + ret = usermodehelper_read_trylock(); + if (WARN_ON(ret)) { + dev_err(device, "firmware: %s will not be loaded\n", + name); + goto out; + } + } + + if (!fw_get_filesystem_firmware(device, fw->priv)) + ret = fw_load_from_user_helper(fw, name, device, + uevent, nowait, timeout); + if (!ret) + ret = assign_firmware_buf(fw, device); + + usermodehelper_read_unlock(); + + out: + if (ret < 0) { + release_firmware(fw); + fw = NULL; + } + + *firmware_p = fw; + return ret; +} + /** * request_firmware: - send firmware request and wait for it * @firmware_p: pointer to firmware image @@ -996,26 +1056,7 @@ int request_firmware(const struct firmware **firmware_p, const char *name, struct device *device) { - struct firmware_priv *fw_priv; - int ret; - - fw_priv = _request_firmware_prepare(firmware_p, name, device, true, - false); - if (IS_ERR_OR_NULL(fw_priv)) - return PTR_RET(fw_priv); - - ret = usermodehelper_read_trylock(); - if (WARN_ON(ret)) { - dev_err(device, "firmware: %s will not be loaded\n", name); - } else { - ret = _request_firmware_load(fw_priv, true, - firmware_loading_timeout()); - usermodehelper_read_unlock(); - } - if (ret) - _request_firmware_cleanup(firmware_p); - - return ret; + return _request_firmware(firmware_p, name, device, true, false); } /** @@ -1046,33 +1087,13 @@ static void request_firmware_work_func(struct work_struct *work) { struct firmware_work *fw_work; const struct firmware *fw; - struct firmware_priv *fw_priv; - long timeout; - int ret; fw_work = container_of(work, struct firmware_work, work); - fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device, - fw_work->uevent, true); - if (IS_ERR_OR_NULL(fw_priv)) { - ret = PTR_RET(fw_priv); - goto out; - } - timeout = usermodehelper_read_lock_wait(firmware_loading_timeout()); - if (timeout) { - ret = _request_firmware_load(fw_priv, fw_work->uevent, timeout); - usermodehelper_read_unlock(); - } else { - dev_dbg(fw_work->device, "firmware: %s loading timed out\n", - fw_work->name); - ret = -EAGAIN; - } - if (ret) - _request_firmware_cleanup(&fw); - - out: + _request_firmware(&fw, fw_work->name, fw_work->device, + fw_work->uevent, true); fw_work->cont(fw, fw_work->context); - put_device(fw_work->device); + put_device(fw_work->device); /* taken in request_firmware_nowait() */ module_put(fw_work->module); kfree(fw_work); -- cgit v1.2.3 From 7b1269f778782d2f42994a74bf4014d0cbebbf9f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 31 Jan 2013 11:13:55 +0100 Subject: firmware: Make user-mode helper optional This patch adds a new kconfig, CONFIG_FW_LOADER_USER_HELPER, and guards the user-helper codes in firmware_class.c with ifdefs. Yeah, yeah, there are lots of ifdefs in this patch. The further clean-up with code shuffling follows in the next. Acked-by: Ming Lei Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 57 +++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 13 deletions(-) (limited to 'drivers/base/firmware_class.c') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 40ec60affd8f..84b1c8d0ea56 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -88,10 +88,12 @@ enum { FW_STATUS_ABORT, }; +#ifdef CONFIG_FW_LOADER_USER_HELPER enum fw_buf_fmt { VMALLOC_BUF, /* used in direct loading */ PAGE_BUF, /* used in loading via userspace */ }; +#endif /* CONFIG_FW_LOADER_USER_HELPER */ static int loading_timeout = 60; /* In seconds */ @@ -128,12 +130,14 @@ struct firmware_buf { struct completion completion; struct firmware_cache *fwc; unsigned long status; - enum fw_buf_fmt fmt; void *data; size_t size; +#ifdef CONFIG_FW_LOADER_USER_HELPER + enum fw_buf_fmt fmt; struct page **pages; int nr_pages; int page_array_size; +#endif char fw_id[]; }; @@ -142,6 +146,7 @@ struct fw_cache_entry { char name[]; }; +#ifdef CONFIG_FW_LOADER_USER_HELPER struct firmware_priv { struct delayed_work timeout_work; bool nowait; @@ -149,6 +154,7 @@ struct firmware_priv { struct firmware_buf *buf; struct firmware *fw; }; +#endif struct fw_name_devm { unsigned long magic; @@ -182,7 +188,9 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name, strcpy(buf->fw_id, fw_name); buf->fwc = fwc; init_completion(&buf->completion); +#ifdef CONFIG_FW_LOADER_USER_HELPER buf->fmt = VMALLOC_BUF; +#endif pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); @@ -240,7 +248,6 @@ static void __fw_free_buf(struct kref *ref) { struct firmware_buf *buf = to_fwbuf(ref); struct firmware_cache *fwc = buf->fwc; - int i; pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", __func__, buf->fw_id, buf, buf->data, @@ -250,12 +257,15 @@ static void __fw_free_buf(struct kref *ref) spin_unlock(&fwc->lock); +#ifdef CONFIG_FW_LOADER_USER_HELPER if (buf->fmt == PAGE_BUF) { + int i; vunmap(buf->data); for (i = 0; i < buf->nr_pages; i++) __free_page(buf->pages[i]); kfree(buf->pages); } else +#endif vfree(buf->data); kfree(buf); } @@ -357,6 +367,19 @@ static bool fw_get_filesystem_firmware(struct device *device, return success; } +/* firmware holds the ownership of pages */ +static void firmware_free_data(const struct firmware *fw) +{ + /* Loaded directly? */ + if (!fw->priv) { + vfree(fw->data); + return; + } + fw_free_buf(fw->priv); +} + +#ifdef CONFIG_FW_LOADER_USER_HELPER + static struct firmware_priv *to_firmware_priv(struct device *dev) { return container_of(dev, struct firmware_priv, dev); @@ -446,17 +469,6 @@ static ssize_t firmware_loading_show(struct device *dev, return sprintf(buf, "%d\n", loading); } -/* firmware holds the ownership of pages */ -static void firmware_free_data(const struct firmware *fw) -{ - /* Loaded directly? */ - if (!fw->priv) { - vfree(fw->data); - return; - } - fw_free_buf(fw->priv); -} - /* Some architectures don't have PAGE_KERNEL_RO */ #ifndef PAGE_KERNEL_RO #define PAGE_KERNEL_RO PAGE_KERNEL @@ -737,12 +749,15 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, exit: return fw_priv; } +#endif /* CONFIG_FW_LOADER_USER_HELPER */ /* store the pages buffer info firmware from buf */ static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) { fw->priv = buf; +#ifdef CONFIG_FW_LOADER_USER_HELPER fw->pages = buf->pages; +#endif fw->size = buf->size; fw->data = buf->data; @@ -906,6 +921,7 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device) return 0; } +#ifdef CONFIG_FW_LOADER_USER_HELPER /* load a firmware via user helper */ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, long timeout) @@ -978,6 +994,15 @@ static int fw_load_from_user_helper(struct firmware *firmware, fw_priv->buf = firmware->priv; return _request_firmware_load(fw_priv, uevent, timeout); } +#else /* CONFIG_FW_LOADER_USER_HELPER */ +static inline int +fw_load_from_user_helper(struct firmware *firmware, const char *name, + struct device *device, bool uevent, bool nowait, + long timeout) +{ + return -ENOENT; +} +#endif /* CONFIG_FW_LOADER_USER_HELPER */ /* called from request_firmware() and request_firmware_work_func() */ static int @@ -1495,7 +1520,11 @@ static void __init fw_cache_init(void) static int __init firmware_class_init(void) { fw_cache_init(); +#ifdef CONFIG_FW_LOADER_USER_HELPER return class_register(&firmware_class); +#else + return 0; +#endif } static void __exit firmware_class_exit(void) @@ -1504,7 +1533,9 @@ static void __exit firmware_class_exit(void) unregister_syscore_ops(&fw_syscore_ops); unregister_pm_notifier(&fw_cache.pm_notify); #endif +#ifdef CONFIG_FW_LOADER_USER_HELPER class_unregister(&firmware_class); +#endif } fs_initcall(firmware_class_init); -- cgit v1.2.3 From cd7239fab7d32e56909027bfb5a6c2d7d3d862f8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 31 Jan 2013 11:13:56 +0100 Subject: firmware: Reduce ifdef CONFIG_FW_LOADER_USER_HELPER By shuffling the code, reduce a few ifdefs in firmware_class.c. Also, firmware_buf fmt field is changed to is_pages_buf boolean for simplification. Acked-by: Ming Lei Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 313 ++++++++++++++++++++---------------------- 1 file changed, 151 insertions(+), 162 deletions(-) (limited to 'drivers/base/firmware_class.c') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 84b1c8d0ea56..51e62ca1d574 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -88,13 +88,6 @@ enum { FW_STATUS_ABORT, }; -#ifdef CONFIG_FW_LOADER_USER_HELPER -enum fw_buf_fmt { - VMALLOC_BUF, /* used in direct loading */ - PAGE_BUF, /* used in loading via userspace */ -}; -#endif /* CONFIG_FW_LOADER_USER_HELPER */ - static int loading_timeout = 60; /* In seconds */ static inline long firmware_loading_timeout(void) @@ -133,7 +126,7 @@ struct firmware_buf { void *data; size_t size; #ifdef CONFIG_FW_LOADER_USER_HELPER - enum fw_buf_fmt fmt; + bool is_paged_buf; struct page **pages; int nr_pages; int page_array_size; @@ -146,16 +139,6 @@ struct fw_cache_entry { char name[]; }; -#ifdef CONFIG_FW_LOADER_USER_HELPER -struct firmware_priv { - struct delayed_work timeout_work; - bool nowait; - struct device dev; - struct firmware_buf *buf; - struct firmware *fw; -}; -#endif - struct fw_name_devm { unsigned long magic; char name[]; @@ -188,9 +171,6 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name, strcpy(buf->fw_id, fw_name); buf->fwc = fwc; init_completion(&buf->completion); -#ifdef CONFIG_FW_LOADER_USER_HELPER - buf->fmt = VMALLOC_BUF; -#endif pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); @@ -256,9 +236,8 @@ static void __fw_free_buf(struct kref *ref) list_del(&buf->list); spin_unlock(&fwc->lock); - #ifdef CONFIG_FW_LOADER_USER_HELPER - if (buf->fmt == PAGE_BUF) { + if (buf->is_paged_buf) { int i; vunmap(buf->data); for (i = 0; i < buf->nr_pages; i++) @@ -378,7 +357,89 @@ static void firmware_free_data(const struct firmware *fw) fw_free_buf(fw->priv); } +/* store the pages buffer info firmware from buf */ +static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) +{ + fw->priv = buf; +#ifdef CONFIG_FW_LOADER_USER_HELPER + fw->pages = buf->pages; +#endif + fw->size = buf->size; + fw->data = buf->data; + + pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", + __func__, buf->fw_id, buf, buf->data, + (unsigned int)buf->size); +} + +#ifdef CONFIG_PM_SLEEP +static void fw_name_devm_release(struct device *dev, void *res) +{ + struct fw_name_devm *fwn = res; + + if (fwn->magic == (unsigned long)&fw_cache) + pr_debug("%s: fw_name-%s devm-%p released\n", + __func__, fwn->name, res); +} + +static int fw_devm_match(struct device *dev, void *res, + void *match_data) +{ + struct fw_name_devm *fwn = res; + + return (fwn->magic == (unsigned long)&fw_cache) && + !strcmp(fwn->name, match_data); +} + +static struct fw_name_devm *fw_find_devm_name(struct device *dev, + const char *name) +{ + struct fw_name_devm *fwn; + + fwn = devres_find(dev, fw_name_devm_release, + fw_devm_match, (void *)name); + return fwn; +} + +/* add firmware name into devres list */ +static int fw_add_devm_name(struct device *dev, const char *name) +{ + struct fw_name_devm *fwn; + + fwn = fw_find_devm_name(dev, name); + if (fwn) + return 1; + + fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm) + + strlen(name) + 1, GFP_KERNEL); + if (!fwn) + return -ENOMEM; + + fwn->magic = (unsigned long)&fw_cache; + strcpy(fwn->name, name); + devres_add(dev, fwn); + + return 0; +} +#else +static int fw_add_devm_name(struct device *dev, const char *name) +{ + return 0; +} +#endif + + +/* + * user-mode helper code + */ #ifdef CONFIG_FW_LOADER_USER_HELPER +struct firmware_priv { + struct delayed_work timeout_work; + bool nowait; + struct device dev; + struct firmware_buf *buf; + struct firmware *fw; +}; static struct firmware_priv *to_firmware_priv(struct device *dev) { @@ -477,7 +538,7 @@ static ssize_t firmware_loading_show(struct device *dev, /* one pages buffer should be mapped/unmapped only once */ static int fw_map_pages_buf(struct firmware_buf *buf) { - if (buf->fmt != PAGE_BUF) + if (!buf->is_paged_buf) return 0; if (buf->data) @@ -749,78 +810,89 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, exit: return fw_priv; } -#endif /* CONFIG_FW_LOADER_USER_HELPER */ -/* store the pages buffer info firmware from buf */ -static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) +/* load a firmware via user helper */ +static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, + long timeout) { - fw->priv = buf; -#ifdef CONFIG_FW_LOADER_USER_HELPER - fw->pages = buf->pages; -#endif - fw->size = buf->size; - fw->data = buf->data; + int retval = 0; + struct device *f_dev = &fw_priv->dev; + struct firmware_buf *buf = fw_priv->buf; - pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", - __func__, buf->fw_id, buf, buf->data, - (unsigned int)buf->size); -} + /* fall back on userspace loading */ + buf->is_paged_buf = true; -#ifdef CONFIG_PM_SLEEP -static void fw_name_devm_release(struct device *dev, void *res) -{ - struct fw_name_devm *fwn = res; + dev_set_uevent_suppress(f_dev, true); - if (fwn->magic == (unsigned long)&fw_cache) - pr_debug("%s: fw_name-%s devm-%p released\n", - __func__, fwn->name, res); -} + /* Need to pin this module until class device is destroyed */ + __module_get(THIS_MODULE); -static int fw_devm_match(struct device *dev, void *res, - void *match_data) -{ - struct fw_name_devm *fwn = res; + retval = device_add(f_dev); + if (retval) { + dev_err(f_dev, "%s: device_register failed\n", __func__); + goto err_put_dev; + } - return (fwn->magic == (unsigned long)&fw_cache) && - !strcmp(fwn->name, match_data); -} + retval = device_create_bin_file(f_dev, &firmware_attr_data); + if (retval) { + dev_err(f_dev, "%s: sysfs_create_bin_file failed\n", __func__); + goto err_del_dev; + } -static struct fw_name_devm *fw_find_devm_name(struct device *dev, - const char *name) -{ - struct fw_name_devm *fwn; + retval = device_create_file(f_dev, &dev_attr_loading); + if (retval) { + dev_err(f_dev, "%s: device_create_file failed\n", __func__); + goto err_del_bin_attr; + } - fwn = devres_find(dev, fw_name_devm_release, - fw_devm_match, (void *)name); - return fwn; -} + if (uevent) { + dev_set_uevent_suppress(f_dev, false); + dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); + if (timeout != MAX_SCHEDULE_TIMEOUT) + schedule_delayed_work(&fw_priv->timeout_work, timeout); -/* add firmware name into devres list */ -static int fw_add_devm_name(struct device *dev, const char *name) -{ - struct fw_name_devm *fwn; + kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); + } - fwn = fw_find_devm_name(dev, name); - if (fwn) - return 1; + wait_for_completion(&buf->completion); - fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm) + - strlen(name) + 1, GFP_KERNEL); - if (!fwn) - return -ENOMEM; + cancel_delayed_work_sync(&fw_priv->timeout_work); - fwn->magic = (unsigned long)&fw_cache; - strcpy(fwn->name, name); - devres_add(dev, fwn); + fw_priv->buf = NULL; - return 0; + device_remove_file(f_dev, &dev_attr_loading); +err_del_bin_attr: + device_remove_bin_file(f_dev, &firmware_attr_data); +err_del_dev: + device_del(f_dev); +err_put_dev: + put_device(f_dev); + return retval; } -#else -static int fw_add_devm_name(struct device *dev, const char *name) + +static int fw_load_from_user_helper(struct firmware *firmware, + const char *name, struct device *device, + bool uevent, bool nowait, long timeout) { - return 0; + struct firmware_priv *fw_priv; + + fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); + if (IS_ERR(fw_priv)) + return PTR_ERR(fw_priv); + + fw_priv->buf = firmware->priv; + return _request_firmware_load(fw_priv, uevent, timeout); } -#endif +#else /* CONFIG_FW_LOADER_USER_HELPER */ +static inline int +fw_load_from_user_helper(struct firmware *firmware, const char *name, + struct device *device, bool uevent, bool nowait, + long timeout) +{ + return -ENOENT; +} +#endif /* CONFIG_FW_LOADER_USER_HELPER */ + /* wait until the shared firmware_buf becomes ready (or error) */ static int sync_cached_firmware_buf(struct firmware_buf *buf) @@ -921,89 +993,6 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device) return 0; } -#ifdef CONFIG_FW_LOADER_USER_HELPER -/* load a firmware via user helper */ -static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, - long timeout) -{ - int retval = 0; - struct device *f_dev = &fw_priv->dev; - struct firmware_buf *buf = fw_priv->buf; - - /* fall back on userspace loading */ - buf->fmt = PAGE_BUF; - - dev_set_uevent_suppress(f_dev, true); - - /* Need to pin this module until class device is destroyed */ - __module_get(THIS_MODULE); - - retval = device_add(f_dev); - if (retval) { - dev_err(f_dev, "%s: device_register failed\n", __func__); - goto err_put_dev; - } - - retval = device_create_bin_file(f_dev, &firmware_attr_data); - if (retval) { - dev_err(f_dev, "%s: sysfs_create_bin_file failed\n", __func__); - goto err_del_dev; - } - - retval = device_create_file(f_dev, &dev_attr_loading); - if (retval) { - dev_err(f_dev, "%s: device_create_file failed\n", __func__); - goto err_del_bin_attr; - } - - if (uevent) { - dev_set_uevent_suppress(f_dev, false); - dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); - if (timeout != MAX_SCHEDULE_TIMEOUT) - schedule_delayed_work(&fw_priv->timeout_work, timeout); - - kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); - } - - wait_for_completion(&buf->completion); - - cancel_delayed_work_sync(&fw_priv->timeout_work); - - fw_priv->buf = NULL; - - device_remove_file(f_dev, &dev_attr_loading); -err_del_bin_attr: - device_remove_bin_file(f_dev, &firmware_attr_data); -err_del_dev: - device_del(f_dev); -err_put_dev: - put_device(f_dev); - return retval; -} - -static int fw_load_from_user_helper(struct firmware *firmware, - const char *name, struct device *device, - bool uevent, bool nowait, long timeout) -{ - struct firmware_priv *fw_priv; - - fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); - if (IS_ERR(fw_priv)) - return PTR_ERR(fw_priv); - - fw_priv->buf = firmware->priv; - return _request_firmware_load(fw_priv, uevent, timeout); -} -#else /* CONFIG_FW_LOADER_USER_HELPER */ -static inline int -fw_load_from_user_helper(struct firmware *firmware, const char *name, - struct device *device, bool uevent, bool nowait, - long timeout) -{ - return -ENOENT; -} -#endif /* CONFIG_FW_LOADER_USER_HELPER */ - /* called from request_firmware() and request_firmware_work_func() */ static int _request_firmware(const struct firmware **firmware_p, const char *name, -- cgit v1.2.3 From 807be03cae191cb88e2f267adcd49aba785c658b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 31 Jan 2013 11:13:57 +0100 Subject: firmware: Ignore abort check when no user-helper is used FW_STATUS_ABORT can be set only during the user-helper invocation, thus we can ignore the check when CONFIG_HW_LOADER_USER_HELPER is disabled. Acked-by: Ming Lei Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/base/firmware_class.c') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 51e62ca1d574..4a223fedcd73 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -454,6 +454,9 @@ static void fw_load_abort(struct firmware_priv *fw_priv) complete_all(&buf->completion); } +#define is_fw_load_aborted(buf) \ + test_bit(FW_STATUS_ABORT, &(buf)->status) + static ssize_t firmware_timeout_show(struct class *class, struct class_attribute *attr, char *buf) @@ -891,6 +894,10 @@ fw_load_from_user_helper(struct firmware *firmware, const char *name, { return -ENOENT; } + +/* No abort during direct loading */ +#define is_fw_load_aborted(buf) false + #endif /* CONFIG_FW_LOADER_USER_HELPER */ @@ -901,7 +908,7 @@ static int sync_cached_firmware_buf(struct firmware_buf *buf) mutex_lock(&fw_lock); while (!test_bit(FW_STATUS_DONE, &buf->status)) { - if (test_bit(FW_STATUS_ABORT, &buf->status)) { + if (is_fw_load_aborted(buf)) { ret = -ENOENT; break; } @@ -963,7 +970,7 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device) struct firmware_buf *buf = fw->priv; mutex_lock(&fw_lock); - if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) { + if (!buf->size || is_fw_load_aborted(buf)) { mutex_unlock(&fw_lock); return -ENOENT; } -- cgit v1.2.3 From 3dadecce20603aa380023c65e6f55f108fd5e952 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Jan 2013 02:18:08 -0500 Subject: switch vfs_getattr() to struct path Signed-off-by: Al Viro --- drivers/base/firmware_class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base/firmware_class.c') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b392b353be39..a2be09dd7771 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -290,7 +290,7 @@ MODULE_PARM_DESC(path, "customized firmware image search path with a higher prio static noinline_for_stack long fw_file_size(struct file *file) { struct kstat st; - if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st)) + if (vfs_getattr(&file->f_path, &st)) return -1; if (!S_ISREG(st.mode)) return -1; -- cgit v1.2.3