diff --git a/core/linux-sun4i/0001-Backport-firmware-loader.patch b/core/linux-sun4i/0001-Backport-firmware-loader.patch new file mode 100644 index 000000000..f340ebda7 --- /dev/null +++ b/core/linux-sun4i/0001-Backport-firmware-loader.patch @@ -0,0 +1,2620 @@ +From c1715f7de4f2b7c42f432d4a7eb02f3f954d34ca Mon Sep 17 00:00:00 2001 +From: Kevin Mihelich <kevin@archlinuxarm.org> +Date: Sat, 6 Dec 2014 10:44:46 -0700 +Subject: [PATCH] Backport firmware loader + +commit 390ac45ceb37d70308194820e25b1cb885b67260 +Author: Ming Lei <ming.lei@canonical.com> +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 <fengguang.wu@intel.com> + Signed-off-by: Ming Lei <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 1dc17a9704a49c008a8d4cd8bdf03eccc54bcf84 +Author: Kevin Mihelich <kevin@archlinuxarm.org> +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 <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 9373da78351d15876f7508734147c25fa018f512 +Author: Mark Brown <broonie@opensource.wolfsonmicro.com> +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 <broonie@opensource.wolfsonmicro.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 6183261d20682701b6ddd189060da4f8325e775c +Author: Mark Brown <broonie@opensource.wolfsonmicro.com> +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 <broonie@opensource.wolfsonmicro.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit bf4725b4926af17c246faf3b6dda250890b3e1ae +Author: Takashi Iwai <tiwai@suse.de> +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 <ming.lei@canonical.com> + Signed-off-by: Takashi Iwai <tiwai@suse.de> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 5d5c8b23919037f575fb6370d87b875c86793834 +Author: Takashi Iwai <tiwai@suse.de> +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 <ming.lei@canonical.com> + Signed-off-by: Takashi Iwai <tiwai@suse.de> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 861d2fc3539af167b5c4c9e81b5bf67300465cef +Author: Takashi Iwai <tiwai@suse.de> +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 <ming.lei@canonical.com> + Signed-off-by: Takashi Iwai <tiwai@suse.de> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit eb2e7c184b7e7bf2ae1968bd32c4354d28c75e69 +Author: Takashi Iwai <tiwai@suse.de> +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 <ming.lei@canonical.com> + Signed-off-by: Takashi Iwai <tiwai@suse.de> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 542061e36122107363fcf7c9aeffa957dee8bffc +Author: Luciano Coelho <coelho@ti.com> +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 <stable@vger.kernel.org> [3.7] + Signed-off-by: Luciano Coelho <coelho@ti.com> + Acked-by: Ming Lei <ming.lei@canonical.com> + Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> + +commit 906aa734d888dfeb855d50ebdaa1abafb4e16880 +Author: Ming Lei <tom.leiming@gmail.com> +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 <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 2a2f0be2bb74403c0079a823271e6ff7c2434735 +Author: Ming Lei <ming.lei@canonical.com> +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 <torvalds@linux-foundation.org> + Signed-off-by: Ming Lei <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + + Conflicts: + Documentation/firmware_class/README + +commit d6c6925a1c5091163df48d68ba38d2ec2003302e +Author: Cesar Eduardo Barros <cesarb@cesarb.net> +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 <cesarb@cesarb.net> + Acked-by: Ming Lei <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit f0d798c22f43f008b0b86ff58f2c68b522f1be90 +Author: Chuansheng Liu <chuansheng.liu@intel.com> +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 <ming.lei@canonical.com> + Signed-off-by: liu chuansheng <chuansheng.liu@intel.com> + Cc: stable <stable@vger.kernel.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 58af61921c9d1f6a8ffa097043bf78a2ff0bc44e +Author: Chuansheng Liu <chuansheng.liu@intel.com> +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 <chuansheng.liu@intel.com> + Acked-by: Ming Lei <ming.lei@canonical.com> + Cc: stable <stable@vger.kernel.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit c6a3b74d6c42ca8aead23788768cdd358f672ed3 +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 9a27bd527d58a887a65bad24fd864122f860ef79 +Author: Ming Lei <ming.lei@canonical.com> +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 <torvalds@linux-foundation.org> + Signed-off-by: Ming Lei <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 451768e107b4bd2beb054f3ec44b666910a8f6ce +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 31159a04875c46fe2f8634479f5c2c07b3ebd6aa +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 4c7559d930c34c76a33b51f413c13f27367e95ff +Author: Linus Torvalds <torvalds@linux-foundation.org> +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 <fengguang.wu@intel.com> + Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> + +commit 2047dd6d95ac6066e518c26cb5133f4787aefb52 +Author: Linus Torvalds <torvalds@linux-foundation.org> +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 <ikalvachev@gmail.com> + Acked-by: Greg KH <gregkh@linuxfoundation.org> + Acked-by: Al Viro <viro@zeniv.linux.org.uk> + Cc: Mauro Carvalho Chehab <mchehab@redhat.com> + Cc: Kay Sievers <kay@redhat.com> + Cc: Ming Lei <ming.lei@canonical.com> + Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> + +commit 7dc9f6ac529be96085a096c440823fe49f95f1cd +Author: Ming Lei <ming.lei@canonical.com> +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 <akpm@linux-foundation.org> + Signed-off-by: Ming Lei <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit d4da2957c44390959fea1003188f449b3f38aba3 +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 580eb13b0fb703c49e729d42c313dc901145c278 +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 529f2b00940c82abdf17bdc902e2b02d1904a789 +Author: Ming Lei <ming.lei@canonical.com> +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 <fengguang.wu@intel.com> + Signed-off-by: Ming Lei <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit e51f2ca3d0d22b9cd08b6262d6e8423888a339e6 +Author: Ming Lei <ming.lei@canonical.com> +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 <fengguang.wu@intel.com> + Signed-off-by: Ming Lei <ming.lei@canonical.com> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 402bbcd0a468c1388fe9fd98a6bd0d4d57f1f5e4 +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 6b5ef252737d7e46cee1471de1f8f778816ed432 +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 3a78247eb488608da2dbf2c0b5f312abc7fb36bf +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit fec2fa2ad35e9b5ac3b556b57f2ce0d93d88dad3 +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 82502e82fdb519661d280f9a2ff7888a7e11719d +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 885f2a4d3e79590a5ac56c600ef21c7f33edf1ff +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit f8c40c19896e07c5e24596d1c73e0004b83bf564 +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 33f7f4606f2add663f0f055d9e22224b2ed927e6 +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 23f444643c50ab3dc66e2cd699ef03d90dbdccc8 +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit c341f46309619686830be4b608c9db06b68bafc1 +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit fca2060e5dac12511c0860878e89e0e5ad8e7a3e +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit 4eaaa8782ff182e0de3ac050aeb0305879dc89eb +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit dc3ca115481cc50a7d4aa866f607b470280b1e5e +Author: Ming Lei <ming.lei@canonical.com> +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 <ming.lei@canonical.com> + Cc: Linus Torvalds <torvalds@linux-foundation.org> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +commit e728c819e3666bb37e08a49255b24e6933bb72e5 +Author: Lars-Peter Clausen <lars@metafoo.de> +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 <lars@metafoo.de> + Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + 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 <linux/firmware.h> + #include <linux/slab.h> + #include <linux/sched.h> ++#include <linux/file.h> ++#include <linux/list.h> ++#include <linux/async.h> ++#include <linux/pm.h> ++#include <linux/suspend.h> ++#include <linux/syscore_ops.h> + +-#define to_dev(obj) container_of(obj, struct device, kobj) ++#include <generated/utsrelease.h> ++ ++#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 + diff --git a/core/linux-sun4i/PKGBUILD b/core/linux-sun4i/PKGBUILD index 8e676474a..1cfe5cf26 100644 --- a/core/linux-sun4i/PKGBUILD +++ b/core/linux-sun4i/PKGBUILD @@ -4,12 +4,12 @@ buildarch=4 pkgbase=linux-sun4i -_commit=e37d760b363888f3a65cd6455c99a75cac70a7b8 +_commit=9a1cd034181af628d4145202289e1993c1687db6 _srcname=linux-sunxi-${_commit} _kernelname=${pkgname#linux} _desc="AllWinner A10" -pkgver=3.4.90 -pkgrel=4 +pkgver=3.4.103 +pkgrel=1 arch=('armv7h') url="http://www.kernel.org/" license=('GPL2') @@ -17,10 +17,12 @@ makedepends=('xmlto' 'docbook-xsl' 'uboot-mkimage' 'bc' 'kmod' 'inetutils' 'git' options=('!strip') source=("https://github.com/linux-sunxi/linux-sunxi/archive/${_commit}.tar.gz" '0001-Backport-BFP-XOR-operation.patch' + '0001-Backport-firmware-loader.patch' 'config') -md5sums=('dbf73f4cb6b1399e73449f4e91bf5694' +md5sums=('6cdd44f9131a854cdbf5ddad86187c57' 'ca5b466850782493b99e824d6efbea4d' - '079150dcdc1cb81f02d0b31730c333e6') + '520bde63dd6803f3332e9b26cb34cff9' + '4592842f7b5702452815d5601f43ab7a') prepare() { cd "${srcdir}/${_srcname}" @@ -28,6 +30,7 @@ prepare() { cat "${srcdir}/config" > ./.config git apply ../0001-Backport-BFP-XOR-operation.patch + git apply ../0001-Backport-firmware-loader.patch # add pkgrel to extraversion sed -ri "s|^(EXTRAVERSION =)(.*)|\1 \2-${pkgrel}|" Makefile diff --git a/core/linux-sun4i/config b/core/linux-sun4i/config index 7bcd0b9c2..cdbd6cf86 100644 --- a/core/linux-sun4i/config +++ b/core/linux-sun4i/config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 3.4.90-4 Kernel Configuration +# Linux/arm 3.4.90-5 Kernel Configuration # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -996,6 +996,7 @@ CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=y CONFIG_FIRMWARE_IN_KERNEL=y CONFIG_EXTRA_FIRMWARE="" +# CONFIG_FW_LOADER_USER_HELPER is not set # CONFIG_DEBUG_DRIVER is not set # CONFIG_DEBUG_DEVRES is not set # CONFIG_SYS_HYPERVISOR is not set