Skip to contentSkip to conversation

firmware: add driver data API

PATCH Superseded Luis R. Rodriguez 11 participants 98 messages Mar 30, 2017 – Jun 27, 2017 Page 2 of 2

Subsystems (7)
lore
Share & tools
lore
author maintainer
#95 lore raw

PATCH v9 3/5 test: add new driver_data load tester

In reply to Luis R. Rodriguez

This 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
index 5f7a9aa49281..1c8235f57487 100644
@@ -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
index 650250aec2d0..14c72e98b6c9 100644
@@ -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
index 1935a97171db..95da06a20dcd 100644
@@ -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
index 1894d625af2d..c9bf6c44435f 100644
@@ -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
index c8137f70e291..0f1a299f9270 100644
@@ -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
author maintainer
#96 lore raw

PATCH v9 4/5 firmware: document the extensible driver data API

In reply to Luis R. Rodriguez

This 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
new file mode 100644
index 000000000000..be7c7ff99151
@@ -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
index 29da39ec4b8a..70a3dea0c5de 100644
@@ -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
index 211cb44eb972..c1173bac0dbb 100644
@@ -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
index 1c2c4967cd43..b31938244b7f 100644
@@ -1,3 +1,5 @@
+.. _request_firmware:
+
 ====================
 request_firmware API
 ====================
Signature · 1 line
2.11.0
author maintainer
#97 lore raw

PATCH v9 5/5 iwlwifi: convert to use driver data API

In reply to Luis R. Rodriguez

The 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
index 5cfacb0bca84..028854d31f55 100644
@@ -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