From c1715f7de4f2b7c42f432d4a7eb02f3f954d34ca Mon Sep 17 00:00:00 2001 From: Kevin Mihelich Date: Sat, 6 Dec 2014 10:44:46 -0700 Subject: [PATCH] Backport firmware loader commit 390ac45ceb37d70308194820e25b1cb885b67260 Author: Ming Lei Date: Fri Aug 17 22:06:59 2012 +0800 PM / Sleep: introduce dpm_for_each_dev dpm_list and its pm lock provide a good way to iterate all devices in system. Except this way, there is no other easy way to iterate devices in system. firmware loader need to cache firmware images for devices before system sleep, so introduce the function to meet its demand. Reported-by: Fengguang Wu Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman commit 1dc17a9704a49c008a8d4cd8bdf03eccc54bcf84 Author: Kevin Mihelich Date: Wed Nov 26 13:03:08 2014 -0700 Revert "firmware loader: sync firmware cache by async_synchronize_full_domain" This reverts commit d28d3882bd1fdb88ae4e02f11b091a92b0e5068b. commit 6cf1b1b3016fb279d6a866151e75c94986be032b Author: Ming Lei Date: Sat Aug 4 12:01:26 2012 +0800 driver core: devres: introduce devres_for_each_res This patch introduces one devres API of devres_for_each_res so that the device's driver can iterate each resource it has interest in. The firmware loader will use the API to get each firmware name from the device instance. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit 9373da78351d15876f7508734147c25fa018f512 Author: Mark Brown Date: Thu May 3 18:15:13 2012 +0100 devres: Add devres_release() APIs using devres frequently want to implement a "remove and free the resource" operation so it seems sensible that they should be able to just have devres do the freeing for them since that's a big part of what devres is all about. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman commit 6183261d20682701b6ddd189060da4f8325e775c Author: Mark Brown Date: Thu May 3 18:15:12 2012 +0100 devres: Clarify documentation for devres_destroy() It's not massively obvious (at least to me) that removing and freeing a resource does not involve calling the release function for the resource but rather only removes the management of it. Make the documentation more explicit. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman commit bf4725b4926af17c246faf3b6dda250890b3e1ae Author: Takashi Iwai Date: Thu Jan 31 11:13:57 2013 +0100 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 commit 5d5c8b23919037f575fb6370d87b875c86793834 Author: Takashi Iwai Date: Thu Jan 31 11:13:56 2013 +0100 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 commit 861d2fc3539af167b5c4c9e81b5bf67300465cef Author: Takashi Iwai Date: Thu Jan 31 11:13:55 2013 +0100 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 commit eb2e7c184b7e7bf2ae1968bd32c4354d28c75e69 Author: Takashi Iwai Date: Thu Jan 31 11:13:54 2013 +0100 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 commit 542061e36122107363fcf7c9aeffa957dee8bffc Author: Luciano Coelho Date: Tue Jan 15 10:43:43 2013 +0200 firmware: make sure the fw file size is not 0 If the requested firmware file size is 0 bytes in the filesytem, we will try to vmalloc(0), which causes a warning: vmalloc: allocation failure: 0 bytes kworker/1:1: page allocation failure: order:0, mode:0xd2 __vmalloc_node_range+0x164/0x208 __vmalloc_node+0x4c/0x58 vmalloc+0x38/0x44 _request_firmware_load+0x220/0x6b0 request_firmware+0x64/0xc8 wl18xx_setup+0xb4/0x570 [wl18xx] wlcore_nvs_cb+0x64/0x9f8 [wlcore] request_firmware_work_func+0x94/0x100 process_one_work+0x1d0/0x750 worker_thread+0x184/0x4ac kthread+0xb4/0xc0 To fix this, check whether the file size is less than or equal to zero in fw_read_file_contents(). Cc: stable [3.7] Signed-off-by: Luciano Coelho Acked-by: Ming Lei Signed-off-by: Linus Torvalds commit 906aa734d888dfeb855d50ebdaa1abafb4e16880 Author: Ming Lei Date: Sat Nov 3 17:48:16 2012 +0800 firmware loader: document firmware cache mechanism This patch documents the firmware cache mechanism so that users of request_firmware() know that it can be called safely inside device's suspend and resume callback, and the device's firmware needn't be cached any more by individual driver itself to deal with firmware loss during system resume. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman commit 2a2f0be2bb74403c0079a823271e6ff7c2434735 Author: Ming Lei Date: Sat Nov 3 17:47:58 2012 +0800 firmware loader: introduce module parameter to customize(v4) fw search path This patch introduces one module parameter of 'path' in firmware_class to support customizing firmware image search path, so that people can use its own firmware path if the default built-in paths can't meet their demand[1], and the typical usage is passing the below from kernel command parameter when 'firmware_class' is built in kernel: firmware_class.path=$CUSTOMIZED_PATH [1], https://lkml.org/lkml/2012/10/11/337 Cc: Linus Torvalds Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman Conflicts: Documentation/firmware_class/README commit d6c6925a1c5091163df48d68ba38d2ec2003302e Author: Cesar Eduardo Barros Date: Sat Oct 27 20:37:10 2012 -0200 firmware: use noinline_for_stack The comment above fw_file_size() suggests it is noinline for stack size reasons. Use noinline_for_stack to make this more clear. Signed-off-by: Cesar Eduardo Barros Acked-by: Ming Lei Signed-off-by: Greg Kroah-Hartman commit f0d798c22f43f008b0b86ff58f2c68b522f1be90 Author: Chuansheng Liu Date: Sat Nov 10 01:27:22 2012 +0800 firmware loader: Fix the concurrent request_firmware() race for kref_get/put There is one race that both request_firmware() with the same firmware name. The race scenerio is as below: CPU1 CPU2 request_firmware() --> _request_firmware_load() return err another request_firmware() is coming --> _request_firmware_cleanup is called --> _request_firmware_prepare --> release_firmware ---> fw_lookup_and_allocate_buf --> spin_lock(&fwc->lock) ... __fw_lookup_buf() return true fw_free_buf() will be called --> ... kref_put --> decrease the refcount to 0 kref_get(&tmp->ref) ==> it will trigger warning due to refcount == 0 __fw_free_buf() --> ... spin_unlock(&fwc->lock) spin_lock(&fwc->lock) list_del(&buf->list) spin_unlock(&fwc->lock) kfree(buf) After that, the freed buf will be used. The key race is decreasing refcount to 0 and list_del is not protected together by fwc->lock, and it is possible another thread try to get it between refcount==0 and list_del. Fix it here to protect it together. Acked-by: Ming Lei Signed-off-by: liu chuansheng Cc: stable Signed-off-by: Greg Kroah-Hartman commit 58af61921c9d1f6a8ffa097043bf78a2ff0bc44e Author: Chuansheng Liu Date: Thu Nov 8 19:14:40 2012 +0800 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 Acked-by: Ming Lei Cc: stable Signed-off-by: Greg Kroah-Hartman commit c6a3b74d6c42ca8aead23788768cdd358f672ed3 Author: Ming Lei Date: Tue Oct 9 12:01:04 2012 +0800 firmware loader: sync firmware cache by async_synchronize_full_domain async.c has provided synchronization mechanism on async_schedule_*, so use async_synchronize_full_domain to sync caching firmware instead of reinventing the wheel. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman commit 9a27bd527d58a887a65bad24fd864122f860ef79 Author: Ming Lei Date: Tue Oct 9 12:01:03 2012 +0800 firmware loader: let direct loading back on 'firmware_buf' Firstly 'firmware_buf' is introduced to make all loading requests to share one firmware kernel buffer, so firmware_buf should be used in direct loading for saving memory and speedup firmware loading. Secondly, the commit below abb139e75c2cdbb955e840d6331cb5863e409d0e(firmware:teach the kernel to load firmware files directly from the filesystem) introduces direct loading for fixing udev regression, but it bypasses the firmware cache meachnism, so this patch enables caching firmware for direct loading case since it is still needed to solve drivers' dependency during system resume. Cc: Linus Torvalds Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman commit 451768e107b4bd2beb054f3ec44b666910a8f6ce Author: Ming Lei Date: Tue Oct 9 12:01:02 2012 +0800 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 Signed-off-by: Greg Kroah-Hartman commit 31159a04875c46fe2f8634479f5c2c07b3ebd6aa Author: Ming Lei Date: Tue Oct 9 12:01:01 2012 +0800 firmware loader: cancel uncache work before caching firmware Under 'Opportunistic sleep' situation, system sleep might be triggered very frequently, so the uncahce work may not be completed before caching firmware during next suspend. This patch cancels the uncache work before caching firmware to fix the problem above. Also this patch optimizes the cacheing firmware mechanism a bit by only storing one firmware cache entry for one firmware image. So if the firmware is still cached during suspend, it doesn't need to be loaded from user space any more. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman commit 4c7559d930c34c76a33b51f413c13f27367e95ff Author: Linus Torvalds Date: Thu Oct 4 09:19:02 2012 -0700 firmware: use 'kernel_read()' to read firmware into kernel buffer Fengguang correctly points out that the firmware reading should not use vfs_read(), since the buffer is in kernel space. The vfs_read() just happened to work for kernel threads, but sparse warns about the incorrect address spaces, and it's definitely incorrect and could fail for other users of the firmware loading. Reported-by: Fengguang Wu Signed-off-by: Linus Torvalds commit 2047dd6d95ac6066e518c26cb5133f4787aefb52 Author: Linus Torvalds Date: Wed Oct 3 15:58:32 2012 -0700 firmware: teach the kernel to load firmware files directly from the filesystem This is a first step in allowing people to by-pass udev for loading device firmware. Current versions of udev will deadlock (causing us to block for the 30 second timeout) under some circumstances if the firmware is loaded as part of the module initialization path, and this is causing problems for media drivers in particular. The current patch hardcodes the firmware path that udev uses by default, and will fall back to the legacy udev mode if the firmware cannot be found there. We'd like to add support for both configuring the paths and the fallback behaviour, but in the meantime this hopefully fixes the immediate problem, while also giving us a way forward. [ v2: Some VFS layer interface cleanups suggested by Al Viro ] [ v3: use the default udev paths suggested by Kay Sievers ] Suggested-by: Ivan Kalvachev Acked-by: Greg KH Acked-by: Al Viro Cc: Mauro Carvalho Chehab Cc: Kay Sievers Cc: Ming Lei Signed-off-by: Linus Torvalds commit 7dc9f6ac529be96085a096c440823fe49f95f1cd Author: Ming Lei Date: Sat Sep 8 17:32:30 2012 +0800 firmware loader: fix compile warning when CONFIG_PM=n This patch replaces the previous macro of CONFIG_PM with CONFIG_PM_SLEEP becasue firmware cache is only used in system sleep situations. Also this patch fixes the below compile warning when CONFIG_PM=n: drivers/base/firmware_class.c:1147: warning: 'device_cache_fw_images' defined but not used drivers/base/firmware_class.c:1212: warning: 'device_uncache_fw_images_delay' defined but not used Reported-by: Andrew Morton Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman commit d4da2957c44390959fea1003188f449b3f38aba3 Author: Ming Lei Date: Mon Aug 20 19:04:16 2012 +0800 firmware loader: let caching firmware piggyback on loading firmware After starting caching firmware, there is still some time left before devices are suspended, during the period, request_firmware or its nowait version may still be triggered by the below situations to load firmware images which can't be cached during suspend/resume cycle. - new devices added - driver bind - or device open kind of things This patch utilizes the piggyback trick to cache firmware for this kind of situation: just increase the firmware buf's reference count and add the fw name entry into cache entry list after starting caching firmware and before syscore_suspend() is called. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman commit 580eb13b0fb703c49e729d42c313dc901145c278 Author: Ming Lei Date: Mon Aug 20 19:04:15 2012 +0800 firmware loader: fix firmware -ENOENT situations If the requested firmware image doesn't exist, firmware->priv should be set for the later concurrent requests, otherwise warning and oops will be triggered inside firmware_free_data(). Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman commit 529f2b00940c82abdf17bdc902e2b02d1904a789 Author: Ming Lei Date: Fri Aug 17 22:07:00 2012 +0800 firmware loader: fix build failure if FW_LOADER is m device_cache_fw_images need to iterate devices in system, so this patch applies the introduced dpm_for_each_dev to avoid link failure if CONFIG_FW_LOADER is m. Reported-by: Fengguang Wu Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman commit e51f2ca3d0d22b9cd08b6262d6e8423888a339e6 Author: Ming Lei Date: Fri Aug 17 22:06:58 2012 +0800 firmware loader: fix compile failure if !PM 'return 0' should be added to fw_pm_notify if !PM because return value of the funcion is defined as 'int'. Reported-by: Fengguang Wu Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman commit 402bbcd0a468c1388fe9fd98a6bd0d4d57f1f5e4 Author: Ming Lei Date: Sat Aug 4 12:01:29 2012 +0800 firmware loader: cache devices firmware during suspend/resume cycle This patch implements caching devices' firmware automatically during system syspend/resume cycle, so any device drivers can call request_firmware or request_firmware_nowait inside resume path to get the cached firmware if they have loaded firmwares successfully at least once before entering suspend. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit 6b5ef252737d7e46cee1471de1f8f778816ed432 Author: Ming Lei Date: Sat Aug 4 12:01:28 2012 +0800 firmware loader: use small timeout for cache device firmware Because device_cache_fw_images only cache the firmware which has been loaded sucessfully at leat once, using a small loading timeout should be reasonable. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit 3a78247eb488608da2dbf2c0b5f312abc7fb36bf Author: Ming Lei Date: Sat Aug 4 12:01:27 2012 +0800 firmware: introduce device_cache/uncache_fw_images This patch introduces the three helpers below: void device_cache_fw_images(void) void device_uncache_fw_images(void) void device_uncache_fw_images_delay(unsigned long) so we can use device_cache_fw_images() to cache firmware for all devices which need firmware to work, and the device driver can get the firmware easily from kernel memory when system isn't ready for completing requests of loading firmware. After system is ready for completing firmware loading, driver core will call device_uncache_fw_images() or its delay version to free the cached firmware. The above helpers will be used to cache device firmware during system suspend/resume cycle in the following patches. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit fec2fa2ad35e9b5ac3b556b57f2ce0d93d88dad3 Author: Ming Lei Date: Sat Aug 4 12:01:25 2012 +0800 firmware loader: store firmware name into devres list This patch will store firmware name into devres list of the device which is requesting firmware loading, so that we can implement auto cache and uncache firmware for devices in need. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit 82502e82fdb519661d280f9a2ff7888a7e11719d Author: Ming Lei Date: Sat Aug 4 12:01:24 2012 +0800 firmware loader: fix comments on request_firmware_nowait request_firmware_nowait is allowed to be called in atomic context now if @gfp is GFP_ATOMIC, so fix the obsolete comments and states which situations are suitable for using it. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit 885f2a4d3e79590a5ac56c600ef21c7f33edf1ff Author: Ming Lei Date: Sat Aug 4 12:01:23 2012 +0800 firmware loader: fix device lifetime Callers of request_firmware* must hold the reference count of @device, otherwise it is easy to trigger oops since the firmware loader device is the child of @device. This patch adds comments about the usage. In fact, most of drivers call request_firmware* in its probe() or open(), so the constraint should be reasonable and can be satisfied. Also this patch holds the reference count of @device before schedule_work() in request_firmware_nowait() to avoid that the @device is released after request_firmware_nowait returns and before the worker function is scheduled. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit f8c40c19896e07c5e24596d1c73e0004b83bf564 Author: Ming Lei Date: Sat Aug 4 12:01:22 2012 +0800 firmware loader: introduce cache_firmware and uncache_firmware This patches introduce two kernel APIs of cache_firmware and uncache_firmware, both of which take the firmware file name as the only parameter. So any drivers can call cache_firmware to cache the specified firmware file into kernel memory, and can use the cached firmware in situations which can't request firmware from user space. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit 33f7f4606f2add663f0f055d9e22224b2ed927e6 Author: Ming Lei Date: Sat Aug 4 12:01:21 2012 +0800 firmware loader: always let firmware_buf own the pages buffer This patch always let firmware_buf own the pages buffer allocated inside firmware_data_write, and add all instances of firmware_buf into the firmware cache global list. Also introduce one private field in 'struct firmware', so release_firmware will see the instance of firmware_buf associated with the current firmware instance, then just 'free' the instance of firmware_buf. The firmware_buf instance represents one pages buffer for one firmware image, so lots of firmware loading requests can share the same firmware_buf instance if they request the same firmware image file. This patch will make implementation of the following cache_firmware/ uncache_firmware very easy and simple. In fact, the patch improves request_formware/release_firmware: - only request userspace to write firmware image once if several devices share one same firmware image and its drivers call request_firmware concurrently. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit 23f444643c50ab3dc66e2cd699ef03d90dbdccc8 Author: Ming Lei Date: Sat Aug 4 12:01:20 2012 +0800 firmware loader: introduce firmware_buf This patch introduces struct firmware_buf to describe the buffer which holds the firmware data, which will make the following cache_firmware/uncache_firmware implemented easily. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit c341f46309619686830be4b608c9db06b68bafc1 Author: Ming Lei Date: Sat Aug 4 12:01:19 2012 +0800 firmware loader: fix creation failure of fw loader device If one device driver calls request_firmware_nowait() to request several different firmwares' loading, device_add() will return failure since all firmware loader device use same name of the device who is requesting firmware. This patch always use the name of firmware image as the firmware loader device name to fix the problem since the following patches for caching firmware will make sure only one loading for same firmware is alllowd at the same time. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit fca2060e5dac12511c0860878e89e0e5ad8e7a3e Author: Ming Lei Date: Sat Aug 4 12:01:18 2012 +0800 firmware loader: remove unnecessary wmb() The wmb() inside fw_load_abort is not necessary, since complete() and wait_on_completion() has implied one pair of memory barrier. Also wmb() isn't a correct usage, so just remove it. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit 4eaaa8782ff182e0de3ac050aeb0305879dc89eb Author: Ming Lei Date: Sat Aug 4 12:01:17 2012 +0800 firmware loader: fix races during loading firmware This patch fixes two races in loading firmware: 1, FW_STATUS_DONE should be set before waking up the task waitting on _request_firmware_load, otherwise FW_STATUS_ABORT may be thought as DONE mistakenly. 2, Inside _request_firmware_load(), there is a small window between wait_for_completion() and mutex_lock(&fw_lock), and 'echo 1 > loading' still may happen during the period, so this patch checks FW_STATUS_DONE to prevent pages' buffer completed from being freed in firmware_loading_store. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit dc3ca115481cc50a7d4aa866f607b470280b1e5e Author: Ming Lei Date: Sat Aug 4 12:01:16 2012 +0800 firmware loader: simplify pages ownership transfer This patch doesn't transfer ownership of pages' buffer to the instance of firmware until the firmware loading is completed, which will simplify firmware_loading_store a lot, so help to introduce the following cache_firmware and uncache_firmware mechanism during system suspend-resume cycle. In fact, this patch fixes one bug: if writing data into firmware loader device is bypassed between writting 1 and 0 to 'loading', OOPS will be triggered without the patch. Also handle the vmap failure case, and add some comments to make code more readable. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman commit e728c819e3666bb37e08a49255b24e6933bb72e5 Author: Lars-Peter Clausen Date: Tue Jul 3 18:49:36 2012 +0200 driver-core: Use kobj_to_dev instead of re-implementing it Signed-off-by: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman --- Documentation/firmware_class/README | 7 + drivers/base/Kconfig | 11 + drivers/base/core.c | 17 +- drivers/base/devres.c | 77 +++ drivers/base/firmware_class.c | 1216 +++++++++++++++++++++++++++++------ drivers/base/power/main.c | 22 + include/linux/device.h | 6 + include/linux/firmware.h | 15 + include/linux/pm.h | 5 + 9 files changed, 1177 insertions(+), 199 deletions(-) diff --git a/Documentation/firmware_class/README b/Documentation/firmware_class/README index 7eceaff..5fde1ef 100644 --- a/Documentation/firmware_class/README +++ b/Documentation/firmware_class/README @@ -106,3 +106,10 @@ on the setup, so I think that the choice on what firmware to make persistent should be left to userspace. + about firmware cache: + -------------------- + After firmware cache mechanism is introduced during system sleep, + request_firmware can be called safely inside device's suspend and + resume callback, and callers need't cache the firmware by + themselves any more for dealing with firmware loss during system + resume. diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 7c96a3a..b7af956 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -145,6 +145,17 @@ config EXTRA_FIRMWARE_DIR this option you can point it elsewhere, such as /lib/firmware/ or some other directory containing the firmware files. +config FW_LOADER_USER_HELPER + bool "Fallback user-helper invocation for firmware loading" + depends on FW_LOADER + default y + help + This option enables / disables the invocation of user-helper + (e.g. udev) for loading firmware files as a fallback after the + direct file loading in kernel fails. The user-mode helper is + no longer required unless you have a special firmware file that + resides in a non-standard path. + config DEBUG_DRIVER bool "Driver Core verbose debug messages" depends on DEBUG_KERNEL diff --git a/drivers/base/core.c b/drivers/base/core.c index f428321..c360a22 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -84,14 +84,13 @@ const char *dev_driver_string(const struct device *dev) } EXPORT_SYMBOL(dev_driver_string); -#define to_dev(obj) container_of(obj, struct device, kobj) #define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct device_attribute *dev_attr = to_dev_attr(attr); - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); ssize_t ret = -EIO; if (dev_attr->show) @@ -107,7 +106,7 @@ static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct device_attribute *dev_attr = to_dev_attr(attr); - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); ssize_t ret = -EIO; if (dev_attr->store) @@ -181,7 +180,7 @@ EXPORT_SYMBOL_GPL(device_show_int); */ static void device_release(struct kobject *kobj) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); struct device_private *p = dev->p; if (dev->release) @@ -199,7 +198,7 @@ static void device_release(struct kobject *kobj) static const void *device_namespace(struct kobject *kobj) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); const void *ns = NULL; if (dev->class && dev->class->ns_type) @@ -220,7 +219,7 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj) struct kobj_type *ktype = get_ktype(kobj); if (ktype == &device_ktype) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); if (dev->bus) return 1; if (dev->class) @@ -231,7 +230,7 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj) static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); if (dev->bus) return dev->bus->name; @@ -243,7 +242,7 @@ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj) static int dev_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); int retval = 0; /* add device node properties if present */ @@ -1131,7 +1130,7 @@ int device_register(struct device *dev) */ struct device *get_device(struct device *dev) { - return dev ? to_dev(kobject_get(&dev->kobj)) : NULL; + return dev ? kobj_to_dev(kobject_get(&dev->kobj)) : NULL; } /** diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 524bf96..8731979 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -144,6 +144,48 @@ EXPORT_SYMBOL_GPL(devres_alloc); #endif /** + * devres_for_each_res - Resource iterator + * @dev: Device to iterate resource from + * @release: Look for resources associated with this release function + * @match: Match function (optional) + * @match_data: Data for the match function + * @fn: Function to be called for each matched resource. + * @data: Data for @fn, the 3rd parameter of @fn + * + * Call @fn for each devres of @dev which is associated with @release + * and for which @match returns 1. + * + * RETURNS: + * void + */ +void devres_for_each_res(struct device *dev, dr_release_t release, + dr_match_t match, void *match_data, + void (*fn)(struct device *, void *, void *), + void *data) +{ + struct devres_node *node; + struct devres_node *tmp; + unsigned long flags; + + if (!fn) + return; + + spin_lock_irqsave(&dev->devres_lock, flags); + list_for_each_entry_safe_reverse(node, tmp, + &dev->devres_head, entry) { + struct devres *dr = container_of(node, struct devres, node); + + if (node->release != release) + continue; + if (match && !match(dev, dr->data, match_data)) + continue; + fn(dev, dr->data, data); + } + spin_unlock_irqrestore(&dev->devres_lock, flags); +} +EXPORT_SYMBOL_GPL(devres_for_each_res); + +/** * devres_free - Free device resource data * @res: Pointer to devres data to free * @@ -309,6 +351,10 @@ EXPORT_SYMBOL_GPL(devres_remove); * which @match returns 1. If @match is NULL, it's considered to * match all. If found, the resource is removed atomically and freed. * + * Note that the release function for the resource will not be called, + * only the devres-allocated data will be freed. The caller becomes + * responsible for freeing any other data. + * * RETURNS: * 0 if devres is found and freed, -ENOENT if not found. */ @@ -326,6 +372,37 @@ int devres_destroy(struct device *dev, dr_release_t release, } EXPORT_SYMBOL_GPL(devres_destroy); + +/** + * devres_release - Find a device resource and destroy it, calling release + * @dev: Device to find resource from + * @release: Look for resources associated with this release function + * @match: Match function (optional) + * @match_data: Data for the match function + * + * Find the latest devres of @dev associated with @release and for + * which @match returns 1. If @match is NULL, it's considered to + * match all. If found, the resource is removed atomically, the + * release function called and the resource freed. + * + * RETURNS: + * 0 if devres is found and freed, -ENOENT if not found. + */ +int devres_release(struct device *dev, dr_release_t release, + dr_match_t match, void *match_data) +{ + void *res; + + res = devres_remove(dev, release, match, match_data); + if (unlikely(!res)) + return -ENOENT; + + (*release)(dev, res); + devres_free(res); + return 0; +} +EXPORT_SYMBOL_GPL(devres_release); + static int remove_nodes(struct device *dev, struct list_head *first, struct list_head *end, struct list_head *todo) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 5401814..15b36b7 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -21,8 +21,16 @@ #include #include #include +#include +#include +#include +#include +#include +#include -#define to_dev(obj) container_of(obj, struct device, kobj) +#include + +#include "base.h" MODULE_AUTHOR("Manuel Estrada Sainz"); MODULE_DESCRIPTION("Multi purpose firmware loading support"); @@ -87,23 +95,354 @@ static inline long firmware_loading_timeout(void) return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT; } -/* fw_lock could be moved to 'struct firmware_priv' but since it is just - * guarding for corner cases a global lock should be OK */ -static DEFINE_MUTEX(fw_lock); +struct firmware_cache { + /* firmware_buf instance will be added into the below list */ + spinlock_t lock; + struct list_head head; + int state; + +#ifdef CONFIG_PM_SLEEP + /* + * Names of firmware images which have been cached successfully + * will be added into the below list so that device uncache + * helper can trace which firmware images have been cached + * before. + */ + spinlock_t name_lock; + struct list_head fw_names; + + wait_queue_head_t wait_queue; + int cnt; + struct delayed_work work; + + struct notifier_block pm_notify; +#endif +}; -struct firmware_priv { +struct firmware_buf { + struct kref ref; + struct list_head list; struct completion completion; - struct firmware *fw; + struct firmware_cache *fwc; unsigned long status; + void *data; + size_t size; +#ifdef CONFIG_FW_LOADER_USER_HELPER + bool is_paged_buf; struct page **pages; int nr_pages; int page_array_size; - struct timer_list timeout; - struct device dev; - bool nowait; +#endif char fw_id[]; }; +struct fw_cache_entry { + struct list_head list; + char name[]; +}; + +struct fw_name_devm { + unsigned long magic; + char name[]; +}; + +#define to_fwbuf(d) container_of(d, struct firmware_buf, ref) + +#define FW_LOADER_NO_CACHE 0 +#define FW_LOADER_START_CACHE 1 + +static int fw_cache_piggyback_on_request(const char *name); + +/* fw_lock could be moved to 'struct firmware_priv' but since it is just + * guarding for corner cases a global lock should be OK */ +static DEFINE_MUTEX(fw_lock); + +static struct firmware_cache fw_cache; + +static struct firmware_buf *__allocate_fw_buf(const char *fw_name, + struct firmware_cache *fwc) +{ + struct firmware_buf *buf; + + buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1 , GFP_ATOMIC); + + if (!buf) + return buf; + + kref_init(&buf->ref); + strcpy(buf->fw_id, fw_name); + buf->fwc = fwc; + init_completion(&buf->completion); + + pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); + + return buf; +} + +static struct firmware_buf *__fw_lookup_buf(const char *fw_name) +{ + struct firmware_buf *tmp; + struct firmware_cache *fwc = &fw_cache; + + list_for_each_entry(tmp, &fwc->head, list) + if (!strcmp(tmp->fw_id, fw_name)) + return tmp; + return NULL; +} + +static int fw_lookup_and_allocate_buf(const char *fw_name, + struct firmware_cache *fwc, + struct firmware_buf **buf) +{ + struct firmware_buf *tmp; + + spin_lock(&fwc->lock); + tmp = __fw_lookup_buf(fw_name); + if (tmp) { + kref_get(&tmp->ref); + spin_unlock(&fwc->lock); + *buf = tmp; + return 1; + } + tmp = __allocate_fw_buf(fw_name, fwc); + if (tmp) + list_add(&tmp->list, &fwc->head); + spin_unlock(&fwc->lock); + + *buf = tmp; + + return tmp ? 0 : -ENOMEM; +} + +static struct firmware_buf *fw_lookup_buf(const char *fw_name) +{ + struct firmware_buf *tmp; + struct firmware_cache *fwc = &fw_cache; + + spin_lock(&fwc->lock); + tmp = __fw_lookup_buf(fw_name); + spin_unlock(&fwc->lock); + + return tmp; +} + +static void __fw_free_buf(struct kref *ref) +{ + struct firmware_buf *buf = to_fwbuf(ref); + struct firmware_cache *fwc = buf->fwc; + + pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", + __func__, buf->fw_id, buf, buf->data, + (unsigned int)buf->size); + + list_del(&buf->list); + spin_unlock(&fwc->lock); + +#ifdef CONFIG_FW_LOADER_USER_HELPER + if (buf->is_paged_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); +} + +static void fw_free_buf(struct firmware_buf *buf) +{ + struct firmware_cache *fwc = buf->fwc; + spin_lock(&fwc->lock); + if (!kref_put(&buf->ref, __fw_free_buf)) + spin_unlock(&fwc->lock); +} + +/* direct firmware loading support */ +static char fw_path_para[256]; +static const char * const fw_path[] = { + fw_path_para, + "/lib/firmware/updates/" UTS_RELEASE, + "/lib/firmware/updates", + "/lib/firmware/" UTS_RELEASE, + "/lib/firmware" +}; + +/* + * Typical usage is that passing 'firmware_class.path=$CUSTOMIZED_PATH' + * from kernel command line because firmware_class is generally built in + * kernel instead of module. + */ +module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644); +MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path"); + +/* Don't inline this: 'struct kstat' is biggish */ +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)) + return -1; + if (!S_ISREG(st.mode)) + return -1; + if (st.size != (long)st.size) + return -1; + return st.size; +} + +static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf) +{ + long size; + char *buf; + + size = fw_file_size(file); + if (size <= 0) + return false; + buf = vmalloc(size); + if (!buf) + return false; + if (kernel_read(file, 0, buf, size) != size) { + vfree(buf); + return false; + } + fw_buf->data = buf; + fw_buf->size = size; + return true; +} + +static bool fw_get_filesystem_firmware(struct device *device, + struct firmware_buf *buf) +{ + int i; + bool success = false; + char *path = __getname(); + + for (i = 0; i < ARRAY_SIZE(fw_path); i++) { + struct file *file; + + /* skip the unset customized path */ + if (!fw_path[i][0]) + continue; + + snprintf(path, PATH_MAX, "%s/%s", fw_path[i], buf->fw_id); + + file = filp_open(path, O_RDONLY, 0); + if (IS_ERR(file)) + continue; + success = fw_read_file_contents(file, buf); + fput(file); + if (success) + 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; +} + +/* 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); +} + +/* 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) { return container_of(dev, struct firmware_priv, dev); @@ -111,11 +450,15 @@ static struct firmware_priv *to_firmware_priv(struct device *dev) static void fw_load_abort(struct firmware_priv *fw_priv) { - set_bit(FW_STATUS_ABORT, &fw_priv->status); - wmb(); - complete(&fw_priv->completion); + struct firmware_buf *buf = fw_priv->buf; + + set_bit(FW_STATUS_ABORT, &buf->status); + 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) @@ -156,11 +499,7 @@ static struct class_attribute firmware_class_attrs[] = { static void fw_dev_release(struct device *dev) { struct firmware_priv *fw_priv = to_firmware_priv(dev); - int i; - for (i = 0; i < fw_priv->nr_pages; i++) - __free_page(fw_priv->pages[i]); - kfree(fw_priv->pages); kfree(fw_priv); module_put(THIS_MODULE); @@ -170,7 +509,7 @@ static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env) { struct firmware_priv *fw_priv = to_firmware_priv(dev); - if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id)) + if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->buf->fw_id)) return -ENOMEM; if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout)) return -ENOMEM; @@ -191,26 +530,30 @@ static ssize_t firmware_loading_show(struct device *dev, struct device_attribute *attr, char *buf) { struct firmware_priv *fw_priv = to_firmware_priv(dev); - int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status); + int loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status); return sprintf(buf, "%d\n", loading); } -static void firmware_free_data(const struct firmware *fw) -{ - int i; - vunmap(fw->data); - if (fw->pages) { - for (i = 0; i < PFN_UP(fw->size); i++) - __free_page(fw->pages[i]); - kfree(fw->pages); - } -} - /* Some architectures don't have PAGE_KERNEL_RO */ #ifndef PAGE_KERNEL_RO #define PAGE_KERNEL_RO PAGE_KERNEL #endif + +/* one pages buffer should be mapped/unmapped only once */ +static int fw_map_pages_buf(struct firmware_buf *buf) +{ + if (!buf->is_paged_buf) + return 0; + + if (buf->data) + vunmap(buf->data); + buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); + if (!buf->data) + return -ENOMEM; + return 0; +} + /** * firmware_loading_store - set value in the 'loading' control file * @dev: device pointer @@ -229,45 +572,41 @@ static ssize_t firmware_loading_store(struct device *dev, const char *buf, size_t count) { struct firmware_priv *fw_priv = to_firmware_priv(dev); + struct firmware_buf *fw_buf = fw_priv->buf; int loading = simple_strtol(buf, NULL, 10); int i; mutex_lock(&fw_lock); - if (!fw_priv->fw) + if (!fw_buf) goto out; switch (loading) { case 1: - firmware_free_data(fw_priv->fw); - memset(fw_priv->fw, 0, sizeof(struct firmware)); - /* If the pages are not owned by 'struct firmware' */ - for (i = 0; i < fw_priv->nr_pages; i++) - __free_page(fw_priv->pages[i]); - kfree(fw_priv->pages); - fw_priv->pages = NULL; - fw_priv->page_array_size = 0; - fw_priv->nr_pages = 0; - set_bit(FW_STATUS_LOADING, &fw_priv->status); + /* discarding any previous partial load */ + if (!test_bit(FW_STATUS_DONE, &fw_buf->status)) { + for (i = 0; i < fw_buf->nr_pages; i++) + __free_page(fw_buf->pages[i]); + kfree(fw_buf->pages); + fw_buf->pages = NULL; + fw_buf->page_array_size = 0; + fw_buf->nr_pages = 0; + set_bit(FW_STATUS_LOADING, &fw_buf->status); + } break; case 0: - if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { - vunmap(fw_priv->fw->data); - fw_priv->fw->data = vmap(fw_priv->pages, - fw_priv->nr_pages, - 0, PAGE_KERNEL_RO); - if (!fw_priv->fw->data) { - dev_err(dev, "%s: vmap() failed\n", __func__); - goto err; - } - /* Pages are now owned by 'struct firmware' */ - fw_priv->fw->pages = fw_priv->pages; - fw_priv->pages = NULL; - - fw_priv->page_array_size = 0; - fw_priv->nr_pages = 0; - complete(&fw_priv->completion); - clear_bit(FW_STATUS_LOADING, &fw_priv->status); + if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) { + set_bit(FW_STATUS_DONE, &fw_buf->status); + clear_bit(FW_STATUS_LOADING, &fw_buf->status); + + /* + * Several loading requests may be pending on + * one same firmware buf, so let all requests + * see the mapped 'buf->data' once the loading + * is completed. + * */ + fw_map_pages_buf(fw_buf); + complete_all(&fw_buf->completion); break; } /* fallthrough */ @@ -275,7 +614,6 @@ static ssize_t firmware_loading_store(struct device *dev, dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); /* fallthrough */ case -1: - err: fw_load_abort(fw_priv); break; } @@ -290,23 +628,23 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); struct firmware_priv *fw_priv = to_firmware_priv(dev); - struct firmware *fw; + struct firmware_buf *buf; ssize_t ret_count; mutex_lock(&fw_lock); - fw = fw_priv->fw; - if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) { + buf = fw_priv->buf; + if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) { ret_count = -ENODEV; goto out; } - if (offset > fw->size) { + if (offset > buf->size) { ret_count = 0; goto out; } - if (count > fw->size - offset) - count = fw->size - offset; + if (count > buf->size - offset) + count = buf->size - offset; ret_count = count; @@ -316,11 +654,11 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj, int page_ofs = offset & (PAGE_SIZE-1); int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count); - page_data = kmap(fw_priv->pages[page_nr]); + page_data = kmap(buf->pages[page_nr]); memcpy(buffer, page_data + page_ofs, page_cnt); - kunmap(fw_priv->pages[page_nr]); + kunmap(buf->pages[page_nr]); buffer += page_cnt; offset += page_cnt; count -= page_cnt; @@ -332,12 +670,13 @@ out: static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) { + struct firmware_buf *buf = fw_priv->buf; int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT; /* If the array of pages is too small, grow it... */ - if (fw_priv->page_array_size < pages_needed) { + if (buf->page_array_size < pages_needed) { int new_array_size = max(pages_needed, - fw_priv->page_array_size * 2); + buf->page_array_size * 2); struct page **new_pages; new_pages = kmalloc(new_array_size * sizeof(void *), @@ -346,24 +685,24 @@ static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) fw_load_abort(fw_priv); return -ENOMEM; } - memcpy(new_pages, fw_priv->pages, - fw_priv->page_array_size * sizeof(void *)); - memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) * - (new_array_size - fw_priv->page_array_size)); - kfree(fw_priv->pages); - fw_priv->pages = new_pages; - fw_priv->page_array_size = new_array_size; + memcpy(new_pages, buf->pages, + buf->page_array_size * sizeof(void *)); + memset(&new_pages[buf->page_array_size], 0, sizeof(void *) * + (new_array_size - buf->page_array_size)); + kfree(buf->pages); + buf->pages = new_pages; + buf->page_array_size = new_array_size; } - while (fw_priv->nr_pages < pages_needed) { - fw_priv->pages[fw_priv->nr_pages] = + while (buf->nr_pages < pages_needed) { + buf->pages[buf->nr_pages] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM); - if (!fw_priv->pages[fw_priv->nr_pages]) { + if (!buf->pages[buf->nr_pages]) { fw_load_abort(fw_priv); return -ENOMEM; } - fw_priv->nr_pages++; + buf->nr_pages++; } return 0; } @@ -384,20 +723,21 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); struct firmware_priv *fw_priv = to_firmware_priv(dev); - struct firmware *fw; + struct firmware_buf *buf; ssize_t retval; if (!capable(CAP_SYS_RAWIO)) return -EPERM; mutex_lock(&fw_lock); - fw = fw_priv->fw; - if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) { + buf = fw_priv->buf; + if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) { retval = -ENODEV; goto out; } + retval = fw_realloc_buffer(fw_priv, offset + count); if (retval) goto out; @@ -410,17 +750,17 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj, int page_ofs = offset & (PAGE_SIZE - 1); int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count); - page_data = kmap(fw_priv->pages[page_nr]); + page_data = kmap(buf->pages[page_nr]); memcpy(page_data + page_ofs, buffer, page_cnt); - kunmap(fw_priv->pages[page_nr]); + kunmap(buf->pages[page_nr]); buffer += page_cnt; offset += page_cnt; count -= page_cnt; } - fw->size = max_t(size_t, offset, fw->size); + buf->size = max_t(size_t, offset, buf->size); out: mutex_unlock(&fw_lock); return retval; @@ -433,11 +773,18 @@ static struct bin_attribute firmware_attr_data = { .write = firmware_data_write, }; -static void firmware_class_timeout(u_long data) +static void firmware_class_timeout_work(struct work_struct *work) { - struct firmware_priv *fw_priv = (struct firmware_priv *) data; + struct firmware_priv *fw_priv = container_of(work, + struct firmware_priv, timeout_work.work); + mutex_lock(&fw_lock); + if (test_bit(FW_STATUS_DONE, &(fw_priv->buf->status))) { + mutex_unlock(&fw_lock); + return; + } fw_load_abort(fw_priv); + mutex_unlock(&fw_lock); } static struct firmware_priv * @@ -447,70 +794,38 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, struct firmware_priv *fw_priv; struct device *f_dev; - fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL); + fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL); if (!fw_priv) { dev_err(device, "%s: kmalloc failed\n", __func__); - return ERR_PTR(-ENOMEM); + fw_priv = ERR_PTR(-ENOMEM); + goto exit; } - fw_priv->fw = firmware; fw_priv->nowait = nowait; - strcpy(fw_priv->fw_id, fw_name); - init_completion(&fw_priv->completion); - setup_timer(&fw_priv->timeout, - firmware_class_timeout, (u_long) fw_priv); + fw_priv->fw = firmware; + INIT_DELAYED_WORK(&fw_priv->timeout_work, + firmware_class_timeout_work); f_dev = &fw_priv->dev; device_initialize(f_dev); - dev_set_name(f_dev, "%s", dev_name(device)); + dev_set_name(f_dev, "%s", fw_name); f_dev->parent = device; f_dev->class = &firmware_class; - +exit: return fw_priv; } -static struct firmware_priv * -_request_firmware_prepare(const struct firmware **firmware_p, const char *name, - struct device *device, bool uevent, bool nowait) -{ - struct firmware *firmware; - struct firmware_priv *fw_priv; - - 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); - } - - if (fw_get_builtin_firmware(firmware, name)) { - dev_dbg(device, "firmware: using built-in firmware %s\n", name); - return NULL; - } - - fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); - if (IS_ERR(fw_priv)) { - release_firmware(firmware); - *firmware_p = NULL; - } - return fw_priv; -} - -static void _request_firmware_cleanup(const struct firmware **firmware_p) -{ - release_firmware(*firmware_p); - *firmware_p = NULL; -} - +/* 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->is_paged_buf = true; dev_set_uevent_suppress(f_dev, true); @@ -537,24 +852,18 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, if (uevent) { dev_set_uevent_suppress(f_dev, false); - dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id); + dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); if (timeout != MAX_SCHEDULE_TIMEOUT) - mod_timer(&fw_priv->timeout, - round_jiffies_up(jiffies + timeout)); + schedule_delayed_work(&fw_priv->timeout_work, timeout); kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); } - wait_for_completion(&fw_priv->completion); + wait_for_completion(&buf->completion); - set_bit(FW_STATUS_DONE, &fw_priv->status); - del_timer_sync(&fw_priv->timeout); + cancel_delayed_work_sync(&fw_priv->timeout_work); - mutex_lock(&fw_lock); - if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) - retval = -ENOENT; - fw_priv->fw = NULL; - mutex_unlock(&fw_lock); + fw_priv->buf = NULL; device_remove_file(f_dev, &dev_attr_loading); err_del_bin_attr: @@ -566,6 +875,186 @@ 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); +} +#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; +} + +/* No abort during direct loading */ +#define is_fw_load_aborted(buf) false + +#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) +{ + int ret = 0; + + mutex_lock(&fw_lock); + while (!test_bit(FW_STATUS_DONE, &buf->status)) { + if (is_fw_load_aborted(buf)) { + ret = -ENOENT; + break; + } + mutex_unlock(&fw_lock); + wait_for_completion(&buf->completion); + mutex_lock(&fw_lock); + } + mutex_unlock(&fw_lock); + return ret; +} + +/* 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_buf *buf; + int ret; + + *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); + if (!firmware) { + dev_err(device, "%s: kmalloc(struct firmware) failed\n", + __func__); + return -ENOMEM; + } + + if (fw_get_builtin_firmware(firmware, name)) { + dev_dbg(device, "firmware: using built-in firmware %s\n", name); + return 0; /* assigned */ + } + + ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); + + /* + * bind with 'buf' now to avoid warning in failure path + * of requesting firmware. + */ + firmware->priv = buf; + + if (ret > 0) { + ret = sync_cached_firmware_buf(buf); + if (!ret) { + fw_set_page_data(buf, firmware); + return 0; /* assigned */ + } + } + + 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 (!buf->size || is_fw_load_aborted(buf)) { + mutex_unlock(&fw_lock); + return -ENOENT; + } + + /* + * 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 0; +} + +/* 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 @@ -580,31 +1069,17 @@ err_put_dev: * @name will be used as $FIRMWARE in the uevent environment and * should be distinctive enough not to be confused with any other * firmware image for this or any other device. + * + * Caller must hold the reference count of @device. + * + * The function can be called safely inside device's suspend and + * resume callback. **/ 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); } /** @@ -635,32 +1110,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); /* taken in request_firmware_nowait() */ module_put(fw_work->module); kfree(fw_work); @@ -679,9 +1135,15 @@ static void request_firmware_work_func(struct work_struct *work) * @cont: function will be called asynchronously when the firmware * request is over. * - * Asynchronous variant of request_firmware() for user contexts where - * it is not possible to sleep for long time. It can't be called - * in atomic contexts. + * Caller must hold the reference count of @device. + * + * Asynchronous variant of request_firmware() for user contexts: + * - sleep for as small periods as possible since it may + * increase kernel boot time of built-in device drivers + * requesting firmware in their ->probe() methods, if + * @gfp is GFP_KERNEL. + * + * - can't sleep at all if @gfp is GFP_ATOMIC. **/ int request_firmware_nowait( @@ -707,19 +1169,391 @@ request_firmware_nowait( return -EFAULT; } + get_device(fw_work->device); INIT_WORK(&fw_work->work, request_firmware_work_func); schedule_work(&fw_work->work); return 0; } +/** + * cache_firmware - cache one firmware image in kernel memory space + * @fw_name: the firmware image name + * + * Cache firmware in kernel memory so that drivers can use it when + * system isn't ready for them to request firmware image from userspace. + * Once it returns successfully, driver can use request_firmware or its + * nowait version to get the cached firmware without any interacting + * with userspace + * + * Return 0 if the firmware image has been cached successfully + * Return !0 otherwise + * + */ +int cache_firmware(const char *fw_name) +{ + int ret; + const struct firmware *fw; + + pr_debug("%s: %s\n", __func__, fw_name); + + ret = request_firmware(&fw, fw_name, NULL); + if (!ret) + kfree(fw); + + pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret); + + return ret; +} + +/** + * uncache_firmware - remove one cached firmware image + * @fw_name: the firmware image name + * + * Uncache one firmware image which has been cached successfully + * before. + * + * Return 0 if the firmware cache has been removed successfully + * Return !0 otherwise + * + */ +int uncache_firmware(const char *fw_name) +{ + struct firmware_buf *buf; + struct firmware fw; + + pr_debug("%s: %s\n", __func__, fw_name); + + if (fw_get_builtin_firmware(&fw, fw_name)) + return 0; + + buf = fw_lookup_buf(fw_name); + if (buf) { + fw_free_buf(buf); + return 0; + } + + return -EINVAL; +} + +#ifdef CONFIG_PM_SLEEP +static struct fw_cache_entry *alloc_fw_cache_entry(const char *name) +{ + struct fw_cache_entry *fce; + + fce = kzalloc(sizeof(*fce) + strlen(name) + 1, GFP_ATOMIC); + if (!fce) + goto exit; + + strcpy(fce->name, name); +exit: + return fce; +} + +static int __fw_entry_found(const char *name) +{ + struct firmware_cache *fwc = &fw_cache; + struct fw_cache_entry *fce; + + list_for_each_entry(fce, &fwc->fw_names, list) { + if (!strcmp(fce->name, name)) + return 1; + } + return 0; +} + +static int fw_cache_piggyback_on_request(const char *name) +{ + struct firmware_cache *fwc = &fw_cache; + struct fw_cache_entry *fce; + int ret = 0; + + spin_lock(&fwc->name_lock); + if (__fw_entry_found(name)) + goto found; + + fce = alloc_fw_cache_entry(name); + if (fce) { + ret = 1; + list_add(&fce->list, &fwc->fw_names); + pr_debug("%s: fw: %s\n", __func__, name); + } +found: + spin_unlock(&fwc->name_lock); + return ret; +} + +static void free_fw_cache_entry(struct fw_cache_entry *fce) +{ + kfree(fce); +} + +static void __async_dev_cache_fw_image(void *fw_entry, + async_cookie_t cookie) +{ + struct fw_cache_entry *fce = fw_entry; + struct firmware_cache *fwc = &fw_cache; + int ret; + + ret = cache_firmware(fce->name); + if (ret) { + spin_lock(&fwc->name_lock); + list_del(&fce->list); + spin_unlock(&fwc->name_lock); + + free_fw_cache_entry(fce); + } + + spin_lock(&fwc->name_lock); + fwc->cnt--; + spin_unlock(&fwc->name_lock); + + wake_up(&fwc->wait_queue); +} + +/* called with dev->devres_lock held */ +static void dev_create_fw_entry(struct device *dev, void *res, + void *data) +{ + struct fw_name_devm *fwn = res; + const char *fw_name = fwn->name; + struct list_head *head = data; + struct fw_cache_entry *fce; + + fce = alloc_fw_cache_entry(fw_name); + if (fce) + list_add(&fce->list, head); +} + +static int devm_name_match(struct device *dev, void *res, + void *match_data) +{ + struct fw_name_devm *fwn = res; + return (fwn->magic == (unsigned long)match_data); +} + +static void dev_cache_fw_image(struct device *dev, void *data) +{ + LIST_HEAD(todo); + struct fw_cache_entry *fce; + struct fw_cache_entry *fce_next; + struct firmware_cache *fwc = &fw_cache; + + devres_for_each_res(dev, fw_name_devm_release, + devm_name_match, &fw_cache, + dev_create_fw_entry, &todo); + + list_for_each_entry_safe(fce, fce_next, &todo, list) { + list_del(&fce->list); + + spin_lock(&fwc->name_lock); + /* only one cache entry for one firmware */ + if (!__fw_entry_found(fce->name)) { + fwc->cnt++; + list_add(&fce->list, &fwc->fw_names); + } else { + free_fw_cache_entry(fce); + fce = NULL; + } + spin_unlock(&fwc->name_lock); + + if (fce) + async_schedule(__async_dev_cache_fw_image, + (void *)fce); + } +} + +static void __device_uncache_fw_images(void) +{ + struct firmware_cache *fwc = &fw_cache; + struct fw_cache_entry *fce; + + spin_lock(&fwc->name_lock); + while (!list_empty(&fwc->fw_names)) { + fce = list_entry(fwc->fw_names.next, + struct fw_cache_entry, list); + list_del(&fce->list); + spin_unlock(&fwc->name_lock); + + uncache_firmware(fce->name); + free_fw_cache_entry(fce); + + spin_lock(&fwc->name_lock); + } + spin_unlock(&fwc->name_lock); +} + +/** + * device_cache_fw_images - cache devices' firmware + * + * If one device called request_firmware or its nowait version + * successfully before, the firmware names are recored into the + * device's devres link list, so device_cache_fw_images can call + * cache_firmware() to cache these firmwares for the device, + * then the device driver can load its firmwares easily at + * time when system is not ready to complete loading firmware. + */ +static void device_cache_fw_images(void) +{ + struct firmware_cache *fwc = &fw_cache; + int old_timeout; + DEFINE_WAIT(wait); + + pr_debug("%s\n", __func__); + + /* cancel uncache work */ + cancel_delayed_work_sync(&fwc->work); + + /* + * use small loading timeout for caching devices' firmware + * because all these firmware images have been loaded + * successfully at lease once, also system is ready for + * completing firmware loading now. The maximum size of + * firmware in current distributions is about 2M bytes, + * so 10 secs should be enough. + */ + old_timeout = loading_timeout; + loading_timeout = 10; + + mutex_lock(&fw_lock); + fwc->state = FW_LOADER_START_CACHE; + dpm_for_each_dev(NULL, dev_cache_fw_image); + mutex_unlock(&fw_lock); + + /* wait for completion of caching firmware for all devices */ + spin_lock(&fwc->name_lock); + for (;;) { + prepare_to_wait(&fwc->wait_queue, &wait, + TASK_UNINTERRUPTIBLE); + if (!fwc->cnt) + break; + + spin_unlock(&fwc->name_lock); + + schedule(); + + spin_lock(&fwc->name_lock); + } + spin_unlock(&fwc->name_lock); + finish_wait(&fwc->wait_queue, &wait); + + loading_timeout = old_timeout; +} + +/** + * device_uncache_fw_images - uncache devices' firmware + * + * uncache all firmwares which have been cached successfully + * by device_uncache_fw_images earlier + */ +static void device_uncache_fw_images(void) +{ + pr_debug("%s\n", __func__); + __device_uncache_fw_images(); +} + +static void device_uncache_fw_images_work(struct work_struct *work) +{ + device_uncache_fw_images(); +} + +/** + * device_uncache_fw_images_delay - uncache devices firmwares + * @delay: number of milliseconds to delay uncache device firmwares + * + * uncache all devices's firmwares which has been cached successfully + * by device_cache_fw_images after @delay milliseconds. + */ +static void device_uncache_fw_images_delay(unsigned long delay) +{ + schedule_delayed_work(&fw_cache.work, + msecs_to_jiffies(delay)); +} + +static int fw_pm_notify(struct notifier_block *notify_block, + unsigned long mode, void *unused) +{ + switch (mode) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + device_cache_fw_images(); + break; + + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + /* + * In case that system sleep failed and syscore_suspend is + * not called. + */ + mutex_lock(&fw_lock); + fw_cache.state = FW_LOADER_NO_CACHE; + mutex_unlock(&fw_lock); + + device_uncache_fw_images_delay(10 * MSEC_PER_SEC); + break; + } + + return 0; +} + +/* stop caching firmware once syscore_suspend is reached */ +static int fw_suspend(void) +{ + fw_cache.state = FW_LOADER_NO_CACHE; + return 0; +} + +static struct syscore_ops fw_syscore_ops = { + .suspend = fw_suspend, +}; +#else +static int fw_cache_piggyback_on_request(const char *name) +{ + return 0; +} +#endif + +static void __init fw_cache_init(void) +{ + spin_lock_init(&fw_cache.lock); + INIT_LIST_HEAD(&fw_cache.head); + fw_cache.state = FW_LOADER_NO_CACHE; + +#ifdef CONFIG_PM_SLEEP + spin_lock_init(&fw_cache.name_lock); + INIT_LIST_HEAD(&fw_cache.fw_names); + fw_cache.cnt = 0; + + init_waitqueue_head(&fw_cache.wait_queue); + INIT_DELAYED_WORK(&fw_cache.work, + device_uncache_fw_images_work); + + fw_cache.pm_notify.notifier_call = fw_pm_notify; + register_pm_notifier(&fw_cache.pm_notify); + + register_syscore_ops(&fw_syscore_ops); +#endif +} + 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) { +#ifdef CONFIG_PM_SLEEP + 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); @@ -728,3 +1562,5 @@ module_exit(firmware_class_exit); EXPORT_SYMBOL(release_firmware); EXPORT_SYMBOL(request_firmware); EXPORT_SYMBOL(request_firmware_nowait); +EXPORT_SYMBOL_GPL(cache_firmware); +EXPORT_SYMBOL_GPL(uncache_firmware); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 77e3b97..8bde48f 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1387,3 +1387,25 @@ int device_pm_wait_for_dev(struct device *subordinate, struct device *dev) return async_error; } EXPORT_SYMBOL_GPL(device_pm_wait_for_dev); + +/** + * dpm_for_each_dev - device iterator. + * @data: data for the callback. + * @fn: function to be called for each device. + * + * Iterate over devices in dpm_list, and call @fn for each device, + * passing it @data. + */ +void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)) +{ + struct device *dev; + + if (!fn) + return; + + device_pm_lock(); + list_for_each_entry(dev, &dpm_list, power.entry) + fn(dev, data); + device_pm_unlock(); +} +EXPORT_SYMBOL_GPL(dpm_for_each_dev); diff --git a/include/linux/device.h b/include/linux/device.h index 5ad17cc..ad8904d 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -531,6 +531,10 @@ extern void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp, #else extern void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp); #endif +extern void devres_for_each_res(struct device *dev, dr_release_t release, + dr_match_t match, void *match_data, + void (*fn)(struct device *, void *, void *), + void *data); extern void devres_free(void *res); extern void devres_add(struct device *dev, void *res); extern void *devres_find(struct device *dev, dr_release_t release, @@ -541,6 +545,8 @@ extern void *devres_remove(struct device *dev, dr_release_t release, dr_match_t match, void *match_data); extern int devres_destroy(struct device *dev, dr_release_t release, dr_match_t match, void *match_data); +extern int devres_release(struct device *dev, dr_release_t release, + dr_match_t match, void *match_data); /* devres group */ extern void * __must_check devres_open_group(struct device *dev, void *id, diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 1e7c011..e4279fe 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -12,6 +12,9 @@ struct firmware { size_t size; const u8 *data; struct page **pages; + + /* firmware loader private fields */ + void *priv; }; struct module; @@ -44,6 +47,8 @@ int request_firmware_nowait( void (*cont)(const struct firmware *fw, void *context)); void release_firmware(const struct firmware *fw); +int cache_firmware(const char *name); +int uncache_firmware(const char *name); #else static inline int request_firmware(const struct firmware **fw, const char *name, @@ -62,6 +67,16 @@ static inline int request_firmware_nowait( static inline void release_firmware(const struct firmware *fw) { } + +static inline int cache_firmware(const char *name) +{ + return -ENOENT; +} + +static inline int uncache_firmware(const char *name) +{ + return -EINVAL; +} #endif #endif diff --git a/include/linux/pm.h b/include/linux/pm.h index f067e60..88f034a 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -638,6 +638,7 @@ extern void __suspend_report_result(const char *function, void *fn, int ret); } while (0) extern int device_pm_wait_for_dev(struct device *sub, struct device *dev); +extern void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)); extern int pm_generic_prepare(struct device *dev); extern int pm_generic_suspend_late(struct device *dev); @@ -677,6 +678,10 @@ static inline int device_pm_wait_for_dev(struct device *a, struct device *b) return 0; } +static inline void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)) +{ +} + #define pm_generic_prepare NULL #define pm_generic_suspend NULL #define pm_generic_resume NULL -- 2.1.3