firmware: add driver data API
Subsystems (7)
Luis R. Rodriguez
maintainerPATCH v9 3/5 test: add new driver_data load tester
In reply to Luis R. RodriguezThis adds a load tester driver test_driver_data a for the new extensible driver_data loader API, part of firmware_class. This test driver enables you to build your tests in userspace by exposing knobs of the exported API to userspace and enables a trigger action to mimic a one time use of the kernel API. This gives us the flexibility to build test case from userspace with less kernel changes.
--- MAINTAINERS | 1 + lib/Kconfig.debug | 10 + lib/Makefile | 1 + lib/test_driver_data.c | 1279 +++++++++++++++++++++++ tools/testing/selftests/firmware/Makefile | 2 +- tools/testing/selftests/firmware/config | 1 + tools/testing/selftests/firmware/driver_data.sh | 1003 ++++++++++++++++++ 7 files changed, 2296 insertions(+), 1 deletion(-) create mode 100644 lib/test_driver_data.c create mode 100755 tools/testing/selftests/firmware/driver_data.sh
MAINTAINERS +1 -0
@@ -5245,6 +5245,7 @@ L: [email protected] S: Maintained F: Documentation/firmware_class/ F: drivers/base/firmware*.c +F: lib/test_driver_data.c F: include/linux/firmware.h F: include/linux/driver_data.h
lib/Kconfig.debug +10 -0
@@ -1791,6 +1791,16 @@ config TEST_SYSCTL proc sysctl interfaces available to drivers safely without affecting production knobs which might alter system functionality. +config TEST_DRIVER_DATA + tristate "Test driver data loading via driver_data APIs" + default n + depends on FW_LOADER + help + This builds the "test_driver_data" module that creates a userspace + interface for testing driver data loading using the driver_data API. + This can be used to control the triggering of driver data loading + without needing an actual real device. + If unsure, say N. config TEST_UDELAY
lib/Makefile +1 -0
@@ -48,6 +48,7 @@ obj-$(CONFIG_TEST_BPF) += test_bpf.o obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o obj-$(CONFIG_TEST_HASH) += test_hash.o test_siphash.o +obj-$(CONFIG_TEST_DRIVER_DATA) += test_driver_data.o obj-$(CONFIG_TEST_KASAN) += test_kasan.o obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o
lib/test_driver_data.c new file +1279 -0
1,279 lines not on this page · raw message
tools/testing/selftests/firmware/Makefile +1 -1
@@ -3,7 +3,7 @@ # No binaries, but make sure arg-less "make" doesn't trigger "run_tests" all: -TEST_PROGS := fw_filesystem.sh fw_fallback.sh +TEST_PROGS := fw_filesystem.sh fw_fallback.sh driver_data.sh include ../lib.mk
tools/testing/selftests/firmware/config +1 -0
@@ -1 +1,2 @@ CONFIG_TEST_FIRMWARE=y +CONFIG_TEST_DRIVER_DATA=y
tools/testing/selftests/firmware/driver_data.sh new file +1003 -0
1,003 lines not on this page · raw message
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v9 4/5 firmware: document the extensible driver data API
In reply to Luis R. RodriguezThis documents the driver data API.
--- Documentation/driver-api/firmware/driver_data.rst | 167 +++++++++++++++++++++ Documentation/driver-api/firmware/index.rst | 1 + Documentation/driver-api/firmware/introduction.rst | 16 ++ .../driver-api/firmware/request_firmware.rst | 2 + 4 files changed, 186 insertions(+) create mode 100644 Documentation/driver-api/firmware/driver_data.rst
Documentation/driver-api/firmware/driver_data.rst new file +167 -0
@@ -0,0 +1,167 @@ +.. _driver_data: + +=============== +driver_data API +=============== + +The driver data APIs provides a flexible API for general driver data file +lookups. Its flexibility aims at mitigating collateral evolutions on the kernel +as new functionality is introduced. + +Driver data modes of operation +============================== + +There are two types of modes of operation for driver data requests: + + * synchronous - driver_data_request_sync() + * asynchronous - driver_data_request_async() + +Synchronous requests expect requests to be done immediately, asynchronous +requests enable requests to be scheduled for a later time. + +Driver data request parameters +============================== + +Variations of types of driver data requests are specified by a driver data +request parameter data structure. The flexibility of the API is provided by +expanding the request parameters as new functionality is needed, without +loosely modifying or adding new exported APIs. + +driver_data_sync_cbs +-------------------- +.. kernel-doc:: include/linux/driver_data.h + :functions: driver_data_sync_cbs + +driver_data_async_cbs +--------------------- +.. kernel-doc:: include/linux/driver_data.h + :functions: driver_data_async_cbs + +driver_data_cbs +--------------- +.. kernel-doc:: include/linux/driver_data.h + :functions: driver_data_cbs + +driver_data_reqs +---------------- +.. kernel-doc:: include/linux/driver_data.h + :functions: driver_data_reqs + +driver_data_req_params +---------------------- +.. kernel-doc:: include/linux/driver_data.h + :functions: driver_data_req_params + +Synchronous driver data requests +================================ + +Synchronous driver data requests will wait until the driver data is found or +until an error is returned. + +driver_data_request_sync +------------------------ +.. kernel-doc:: drivers/base/firmware_class.c + :functions: driver_data_request_sync + +Asynchronous driver data requests +================================= + +Asynchronous driver data requests allow driver code to not have to wait +until the driver data or an error is returned. Function callbacks are +required so that when the firmware or an error is found the driver is +informed through the callbacks. Asynchronous driver data requests cannot +be called from atomic contexts. + +driver_data_request_async +------------------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: driver_data_request_async + +Reference counting and releasing the driver data file +===================================================== + +The device and module are bumped with reference counts during the driver data +requests. This prevents removal of the device and module making the driver data +request call until the driver data request callbacks have completed, either +synchronously or asynchronously. When synchronous requests are made the +firmware_class is refcounted. When asynchronous requests are made the caller's +module is refcounted. Asynchronous requests do not refcount the firmware_class +module. + +The driver data request API enables callers to provide a callback for both +synchronous and asynchronous requests and since consumption can be expected +in these callbacks it frees it for you by default after callback handlers +are issued. If you wish to keep the driver data around after your callbacks +you must specify this through the driver data request parameter data structure. + +Driver data private internal functionality +========================================== + +This section documents functionality not exposed to users, but important in +understanding how the driver data internals work. + +driver_data_mode +---------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: driver_data_mode + +driver_data_priv_reqs +--------------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: driver_data_priv_reqs + +driver_data_priv_params +----------------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: driver_data_priv_params + +driver_data_params +------------------ +.. kernel-doc:: drivers/base/firmware_class.c + :functions: driver_data_params + +Testing the driver_data API +=========================== + +The driver data API has a selftest driver: lib/test_driver_data.c. The +test_driver_data enables you to build your tests in userspace by exposing knobs +of the exported API in userspace and enabling userspace to configure and +trigger a kernel call. This lets us build most possible test cases of +the kernel APIs from userspace. + +The test_driver_data also enables multiple test triggers to be created +enabling testing to be done in parallel, one test interface per test case. + +To test an async call one could do:: + + echo anything > /lib/firmware/test-driver_data.bin + echo -n 1 > /sys/devices/virtual/misc/test_driver_data0/config_async + echo -n 1 > /sys/devices/virtual/misc/test_driver_data0/trigger_config + +A series of tests have been written to test the driver data API thoroughly. +A respective test case is expected to bet written as new features get added. +For details of existing tests run:: + + tools/testing/selftests/firmware/driver_data.sh -l + +To see all available options:: + + tools/testing/selftests/firmware/driver_data.sh --help + +To run a test 0010 case 40 times:: + + tools/testing/selftests/firmware/driver_data.sh -c 0010 40 + +Note that driver_data.sh uses its own temporary custom path for creating and +looking for driver data files, it does this to not overwrite any production +files you might have which may share the same names used by the test shell +script driver_data.sh. If you are not using the driver_data.sh script your +default path will be used. + +Tracking development enhancements and ideas +=========================================== + +To help track ongoing development for firmware_class and related items to +firmware_class refer to the kernel newbies wiki page [0]. + +[0] http://kernelnewbies.org/KernelProjects/firmware-class-enhancements
Documentation/driver-api/firmware/index.rst +1 -0
@@ -8,6 +8,7 @@ Linux Firmware API core request_firmware other_interfaces + driver_data .. only:: subproject and html
Documentation/driver-api/firmware/introduction.rst +16 -0
@@ -25,3 +25,19 @@ are already using asynchronous initialization mechanisms which will not stall or delay boot. Even if loading firmware does not take a lot of time processing firmware might, and this can still delay boot or initialization, as such mechanisms such as asynchronous probe can help supplement drivers. + +Two APIs +======== + +Two APIs are provided for firmware: + +* Old firmware API - :ref:`request_firmware` +* Flexible driver data API - :ref:`driver_data` + +We have historically extended the firmware API by adding new routines or at +times extending existing routines with more or less arguments. This doesn't +scale well, when new arguments are added to existing routines it means we need +to traverse the kernel with a slew of collateral evolutions to adjust old +driver users. The driver data API is an extensible API enabling extensions to +be added by avoiding unnecessary collateral evolutions as features get added. +New features and development should be added through the driver_data API.
Documentation/driver-api/firmware/request_firmware.rst +2 -0
@@ -1,3 +1,5 @@ +.. _request_firmware: + ==================== request_firmware API ====================
Signature · 1 line
2.11.0
Luis R. Rodriguez
maintainerPATCH v9 5/5 iwlwifi: convert to use driver data API
In reply to Luis R. RodriguezThe driver data API provides support for looking for firmware from a specific set of API ranges, so just use that. Since we free the firmware on the callback immediately after consuming it, this also takes avantage of that feature.
--- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 91 ++++++++++------------------ 1 file changed, 31 insertions(+), 60 deletions(-)
drivers/net/wireless/intel/iwlwifi/iwl-drv.c +31 -60
@@ -66,7 +66,7 @@*****************************************************************************/ #include <linux/completion.h> #include <linux/dma-mapping.h> -#include <linux/firmware.h> +#include <linux/driver_data.h> #include <linux/module.h> #include <linux/vmalloc.h>
@@ -206,58 +206,34 @@ static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc,return 0 ; } -static void iwl_req_fw_callback (const struct firmware *ucode_raw, -void *context); +static int iwl_req_fw_callback (const struct firmware *ucode_raw,void *context, +int unused_error); -static int iwl_request_firmware (struct iwl_drv *drv,bool first) +static const char *iwl_get_fw_name_pre (struct iwl_drv *drv) { -const struct iwl_cfg *cfg = drv->trans->cfg; -char tag[8 ];const char *fw_pre_name;if (drv->trans->cfg->device_family ==IWL_DEVICE_FAMILY_8000 &&CSR_HW_REV_STEP (drv->trans->hw_rev) ==SILICON_B_STEP ) - fw_pre_name = cfg->fw_name_pre_next_step; + fw_pre_name = drv->trans->cfg->fw_name_pre_next_step;else - fw_pre_name = cfg->fw_name_pre; - -if (first) { - drv->fw_index = cfg->ucode_api_max; -sprintf (tag,"%d" , drv->fw_index); - }else { - drv->fw_index--; -sprintf (tag,"%d" , drv->fw_index); - } - -if (drv->fw_index < cfg->ucode_api_min) { -IWL_ERR (drv,"no suitable firmware found!\n" ); - -if (cfg->ucode_api_min == cfg->ucode_api_max) { -IWL_ERR (drv,"%s%d is required\n" , fw_pre_name, - cfg->ucode_api_max); - }else { -IWL_ERR (drv,"minimum version required: %s%d\n" , - fw_pre_name, - cfg->ucode_api_min); -IWL_ERR (drv,"maximum version supported: %s%d\n" , - fw_pre_name, - cfg->ucode_api_max); - } - -IWL_ERR (drv, -"check git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git\n" ); -return -ENOENT ; - } + fw_pre_name = drv->trans->cfg->fw_name_pre; -snprintf (drv->firmware_name,sizeof (drv->firmware_name),"%s%s.ucode" , - fw_pre_name, tag); - -IWL_DEBUG_INFO (drv,"attempting to load firmware '%s'\n" , - drv->firmware_name); +return fw_pre_name; +} -return request_firmware_nowait (THIS_MODULE ,1 , drv->firmware_name, - drv->trans->dev, -GFP_KERNEL , drv, iwl_req_fw_callback); +static int iwl_request_firmware (struct iwl_drv *drv) +{ +const char *name_pre =iwl_get_fw_name_pre (drv); +const struct iwl_cfg *cfg = drv->trans->cfg; +const struct driver_data_req_params req_params = { +DRIVER_DATA_API_CB (iwl_req_fw_callback, drv), +DRIVER_DATA_API (cfg->ucode_api_min, cfg->ucode_api_max,".ucode" ), + }; + +return driver_data_request_async (name_pre, + &req_params, + drv->trans->dev); }struct fw_img_parsing {
@@ -1259,7 +1235,8 @@ static void _iwl_op_mode_stop(struct iwl_drv *drv)* If loaded successfully, copies the firmware into buffers * for the card to fetch (via DMA). */ -static void iwl_req_fw_callback (const struct firmware *ucode_raw,void *context) +static int iwl_req_fw_callback (const struct firmware *ucode_raw,void *context, +int unused_error) {struct iwl_drv *drv = context;struct iwl_fw *fw = &drv->fw;
@@ -1282,10 +1259,12 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) pieces =kzalloc (sizeof (*pieces),GFP_KERNEL );if (!pieces) - goto out_free_fw; +return -ENOMEM ; -if (!ucode_raw) - goto try_again; +if (!ucode_raw) { + err = -ENOENT ; + goto free; + }IWL_DEBUG_INFO (drv,"Loaded firmware file '%s' (%zd bytes).\n" , drv->firmware_name, ucode_raw->size);
@@ -1448,9 +1427,6 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) fw->ucode_capa.standard_phy_calibration_size =IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE ; -/* We have our copies now, allow OS release its copies */ -release_firmware (ucode_raw); -mutex_lock (&iwlwifi_opmode_table_mtx);switch (fw->type) {case IWL_FW_DVM :
@@ -1505,15 +1481,11 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) goto free; try_again: -/* try next, if any */ -release_firmware (ucode_raw); -if (iwl_request_firmware (drv, false)) - goto out_unbind; + err = -EAGAIN ; goto free; out_free_fw:iwl_dealloc_ucode (drv); -release_firmware (ucode_raw); out_unbind:complete (&drv->request_firmware_complete);device_release_driver (drv->trans->dev);
@@ -1524,6 +1496,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)kfree (pieces->dbg_mem_tlv);kfree (pieces); } +return err; }struct iwl_drv *iwl_drv_start (struct iwl_trans *trans)
@@ -1564,11 +1537,9 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans) }#endif - ret =iwl_request_firmware (drv, true); -if (ret) { -IWL_ERR (trans,"Couldn't request the fw\n" ); + ret =iwl_request_firmware (drv); +if (ret) goto err_fw; - }return drv;
Signature · 1 line
2.11.0