[PATCHv3,1/3] firmware_class: move NO_CACHE from private to driver_data_req_params
diff mbox

Message ID 1497653911-11944-2-git-send-email-yi1.li@linux.intel.com
State New
Headers show

Commit Message

Li, Yi June 16, 2017, 10:58 p.m. UTC
From: Yi Li <yi1.li@linux.intel.com>

This adds DRIVER_DATA_REQ_NO_CACHE flag with .req flag under struct
driver_data_req_params. When this flag is set, the driver_data driver
will not cache the firmware during PM cycle, which is expensive. It
will be used by streaming case and other drivers which implement
their own cache thing. Also added the debugfs interface to selftest.

Signed-off-by: Yi Li <yi1.li@linux.intel.com>
---
 drivers/base/firmware_class-dbg.c | 108 ++++++++++++++++++++++++++++++++++++++
 drivers/base/firmware_class.c     |  26 +++++----
 include/linux/driver_data.h       |   4 ++
 3 files changed, 127 insertions(+), 11 deletions(-)
 create mode 100644 drivers/base/firmware_class-dbg.c

Comments

Greg Kroah-Hartman June 23, 2017, 3:41 p.m. UTC | #1
On Fri, Jun 16, 2017 at 05:58:29PM -0500, yi1.li@linux.intel.com wrote:
> From: Yi Li <yi1.li@linux.intel.com>
> 
> This adds DRIVER_DATA_REQ_NO_CACHE flag with .req flag under struct
> driver_data_req_params. When this flag is set, the driver_data driver
> will not cache the firmware during PM cycle, which is expensive.

Why is it "expensive"?  What do you mean by this?  Caching was added to
help things out, why would you not want it?

> It will be used by streaming case and other drivers which implement
> their own cache thing.

Why would a driver implement their own cache?  And what do you mean by
"streaming case"?

Again, as I said to Luis's patches, you need to show that the added code
is actually needed.  You only convert one driver to this new interface
in this series, is that the only user of it?  If so, that seems
excessive don't you think?

> Also added the debugfs interface to selftest.

In this patch?  Why not have it be in a separate patch like it should
be?

> diff --git a/drivers/base/firmware_class-dbg.c b/drivers/base/firmware_class-dbg.c
> new file mode 100644
> index 0000000..102a4cd
> --- /dev/null
> +++ b/drivers/base/firmware_class-dbg.c
> @@ -0,0 +1,108 @@
> +/*
> + * Copyright (c) 2017 by Yi Li <yi1.li@linux.intel.com>
> + *
> + */
> +/* This is part of firmware_class.c for testing firmware cache */
> +
> +#ifndef CONFIG_TEST_DRIVER_DATA
> +static inline void create_debug_files(struct firmware_cache *cache) { }
> +static inline void remove_debug_files(struct firmware_cache *cache) { }
> +#else
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>

This implies you are including a .c file into another one.  Please never
do that.

> -	/* don't cache firmware handled without uevent */

Um, I thought we didn't have this feature already?  But this implies
that we do have it, so what exactly does this patch do?

thanks,

greg k-h
--
To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Li, Yi June 26, 2017, 3:28 p.m. UTC | #2
hi Greg

On 6/23/2017 10:41 AM, Greg KH wrote:
> On Fri, Jun 16, 2017 at 05:58:29PM -0500, yi1.li@linux.intel.com wrote:
>> From: Yi Li <yi1.li@linux.intel.com>
>>
>> This adds DRIVER_DATA_REQ_NO_CACHE flag with .req flag under struct
>> driver_data_req_params. When this flag is set, the driver_data driver
>> will not cache the firmware during PM cycle, which is expensive.
> 
> Why is it "expensive"?  What do you mean by this?  Caching was added to
> help things out, why would you not want it?
> 

Say we have couple drivers (GPU, wifi, FPGA, etc) loading firmwares 
during init, with firmware cache feature on, each time the system going 
to PM suspend, the firmware framework will cache all the firmware files 
(which have been loaded before) from fs to RAM, just in case during PM 
resume the drivers need to re-program firmware. If say FPGA driver does 
not need to re-program firmware to hardware for suspend/resume, it's 
"expensive/wasted" to load those in suspend phase and do not use them. 
Or I am missing something?

>> It will be used by streaming case and other drivers which implement
>> their own cache thing.
> 
> Why would a driver implement their own cache?  And what do you mean by
> "streaming case"?
When the firmware file size is too big to fit into the RAM, we try to 
streaming it (load a page and program the page to the hardware and 
repeat till the end of file). For this case, the firmware was not loaded 
to RAM as a whole piece, there is no point/way to cache it.

> 
> Again, as I said to Luis's patches, you need to show that the added code
> is actually needed.  You only convert one driver to this new interface
> in this series, is that the only user of it?  If so, that seems
> excessive don't you think?
>
I started adding the new streaming use case with current firmware code 
back on March, saw Luis is working on the new driver data, which suppose 
to make expending new features cleaner, that's the reason I switched to 
driver data vehicle. New feature/requirement normally come one at a 
time, with this logic it's always excessive to add new interface. :) But 
if you all decide to keep the current firmware framework, I can switch 
back.

>> Also added the debugfs interface to selftest.
> 
> In this patch?  Why not have it be in a separate patch like it should
> be?
Sure, I can do that.

> 
>> diff --git a/drivers/base/firmware_class-dbg.c b/drivers/base/firmware_class-dbg.c
>> new file mode 100644
>> index 0000000..102a4cd
>> --- /dev/null
>> +++ b/drivers/base/firmware_class-dbg.c
>> @@ -0,0 +1,108 @@
>> +/*
>> + * Copyright (c) 2017 by Yi Li <yi1.li@linux.intel.com>
>> + *
>> + */
>> +/* This is part of firmware_class.c for testing firmware cache */
>> +
>> +#ifndef CONFIG_TEST_DRIVER_DATA
>> +static inline void create_debug_files(struct firmware_cache *cache) { }
>> +static inline void remove_debug_files(struct firmware_cache *cache) { }
>> +#else
>> +#include <linux/debugfs.h>
>> +#include <linux/seq_file.h>
> 
> This implies you are including a .c file into another one.  Please never
> do that.
> 
>> -	/* don't cache firmware handled without uevent */
> 
> Um, I thought we didn't have this feature already?  But this implies
> that we do have it, so what exactly does this patch do?
> 
> thanks,
> 
> greg k-h
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alan Tull July 13, 2017, 9:08 p.m. UTC | #3
On Mon, Jun 26, 2017 at 10:28 AM, Li, Yi <yi1.li@linux.intel.com> wrote:

Hi Greg,

> hi Greg
>
> On 6/23/2017 10:41 AM, Greg KH wrote:
>>
>> On Fri, Jun 16, 2017 at 05:58:29PM -0500, yi1.li@linux.intel.com wrote:
>>>
>>> From: Yi Li <yi1.li@linux.intel.com>
>>>
>>> This adds DRIVER_DATA_REQ_NO_CACHE flag with .req flag under struct
>>> driver_data_req_params. When this flag is set, the driver_data driver
>>> will not cache the firmware during PM cycle, which is expensive.
>>
>>
>> Why is it "expensive"?  What do you mean by this?  Caching was added to
>> help things out, why would you not want it?
>>
>
> Say we have couple drivers (GPU, wifi, FPGA, etc) loading firmwares during
> init, with firmware cache feature on, each time the system going to PM
> suspend, the firmware framework will cache all the firmware files (which
> have been loaded before) from fs to RAM, just in case during PM resume the
> drivers need to re-program firmware. If say FPGA driver does not need to
> re-program firmware to hardware for suspend/resume, it's "expensive/wasted"
> to load those in suspend phase and do not use them. Or I am missing
> something?
>
>>> It will be used by streaming case and other drivers which implement
>>> their own cache thing.
>>
>>
>> Why would a driver implement their own cache?  And what do you mean by
>> "streaming case"?
>
> When the firmware file size is too big to fit into the RAM, we try to
> streaming it (load a page and program the page to the hardware and repeat
> till the end of file). For this case, the firmware was not loaded to RAM as
> a whole piece, there is no point/way to cache it.

Streaming for FPGA programming would mean a FPGA driver could load the
firmware a page at a time and write each page to the FPGA. In most
cases there is no need to keep the FPGA image in memory after
programming is done or even keep the whole image in memory while
programming.

As we're starting to look at larger devices with 100Meg FPGA images,
we want to avoid contiguous buffers. An alternative would be firmware
files loaded to scatter gather tables since Jason Gunthorpe added sg
support to fpga-mgr.

Alan Tull
--
To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch
diff mbox

diff --git a/drivers/base/firmware_class-dbg.c b/drivers/base/firmware_class-dbg.c
new file mode 100644
index 0000000..102a4cd
--- /dev/null
+++ b/drivers/base/firmware_class-dbg.c
@@ -0,0 +1,108 @@ 
+/*
+ * Copyright (c) 2017 by Yi Li <yi1.li@linux.intel.com>
+ *
+ */
+/* This is part of firmware_class.c for testing firmware cache */
+
+#ifndef CONFIG_TEST_DRIVER_DATA
+static inline void create_debug_files(struct firmware_cache *cache) { }
+static inline void remove_debug_files(struct firmware_cache *cache) { }
+#else
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static int debug_cache_show(struct seq_file *s, void *v)
+{
+	struct firmware_cache *cache = s->private;
+	unsigned long flags;
+	struct fw_cache_entry *cache_entry;
+
+	spin_lock_irqsave(&cache->lock, flags);
+
+	list_for_each_entry(cache_entry, &cache->fw_names, list)
+		seq_printf(s, "cached %s\n", cache_entry->name);
+
+	spin_unlock_irqrestore(&cache->lock, flags);
+
+	return 0;
+}
+
+static int debug_cache_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, debug_cache_show, inode->i_private);
+}
+
+#define MAX_LEN	16
+/**
+ * test_cache - set value in the 'cache' control file
+ *
+ *	The relevant values are:
+ *
+ *	 1: Test the suspend and start the cache
+ *	 0: Test the resume and clear the cache.
+ **/
+static ssize_t test_cache(struct file *fp, const char __user *user_buffer,
+			  size_t size, loff_t *ppos)
+{
+	char buf[MAX_LEN];
+	size_t len;
+	long cmd;
+
+	len = min(size, (size_t)(MAX_LEN - 1));
+	if (copy_from_user(buf, user_buffer, len))
+		return -EFAULT;
+	buf[len] = 0;
+	if (kstrtol(buf, 10, &cmd))
+		return -EFAULT;
+
+#ifdef CONFIG_PM_SLEEP
+	switch (cmd) {
+	/* Simulate PM suspend prepare and start to cache */
+	case 1:
+		kill_pending_fw_fallback_reqs(true);
+		device_cache_fw_images();
+		disable_firmware();
+		break;
+	/* Simulate PM resume and un-cache */
+	case 0:
+		mutex_lock(&fw_lock);
+		fw_cache.state = FW_LOADER_NO_CACHE;
+		mutex_unlock(&fw_lock);
+		enable_firmware();
+		device_uncache_fw_images_delay(10);
+		break;
+	default:
+		pr_err("unexpected cmd\n");
+	}
+#endif
+	return size;
+}
+
+static const struct file_operations debug_cache_fops = {
+	.open = debug_cache_open,
+	.read = seq_read,
+	.write = test_cache,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static void create_debug_files(struct firmware_cache *cache)
+{
+	cache->debug = debugfs_create_dir("firmware", NULL);
+	if (!cache->debug)
+		return;
+	if (!debugfs_create_file("cache", 0644, cache->debug,
+				 cache, &debug_cache_fops))
+		goto failed_create;
+	return;
+
+failed_create:
+	debugfs_remove_recursive(cache->debug);
+}
+
+static void remove_debug_files(struct firmware_cache *cache)
+{
+	debugfs_remove_recursive(cache->debug);
+	cache->debug = NULL;
+}
+#endif
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 7af430a..a70a2a7 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -72,14 +72,10 @@  enum driver_data_mode {
  * 	issue a uevent to userspace. Userspace in turn is expected to be
  * 	monitoring for uevents for the firmware_class and will use the
  * 	exposted sysfs interface to upload the driver data for the caller.
- * @DRIVER_DATA_PRIV_REQ_NO_CACHE: indicates that the driver data request
- * 	should not set up and use the internal caching mechanism to assist
- * 	drivers from fetching driver data at resume time after suspend.
  */
 enum driver_data_priv_reqs {
 	DRIVER_DATA_PRIV_REQ_FALLBACK			= 1 << 0,
 	DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT		= 1 << 1,
-	DRIVER_DATA_PRIV_REQ_NO_CACHE			= 1 << 2,
 };
 
 /**
@@ -151,10 +147,12 @@  struct driver_data_params {
 	}
 
 #define __DATA_REQ_FIRMWARE_BUF(buf, size)				\
+	.req_params = {							\
+		.reqs = DRIVER_DATA_REQ_NO_CACHE,			\
+	},								\
 	.priv_params = {						\
 		.priv_reqs = DRIVER_DATA_PRIV_REQ_FALLBACK |		\
-			     DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT |	\
-			     DRIVER_DATA_PRIV_REQ_NO_CACHE,		\
+			     DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT,	\
 		.alloc_buf = buf,					\
 		.alloc_buf_size = size,					\
 	}
@@ -186,7 +184,7 @@  struct driver_data_params {
 #define driver_data_param_uevent(params)	\
 	(!!((params)->priv_reqs & DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT))
 #define driver_data_param_nocache(params)	\
-	(!!((params)->priv_reqs & DRIVER_DATA_PRIV_REQ_NO_CACHE))
+	(!!((params)->reqs & DRIVER_DATA_REQ_NO_CACHE))
 
 #define driver_data_param_optional(params)	\
 	(!!((params)->reqs & DRIVER_DATA_REQ_OPTIONAL))
@@ -424,6 +422,8 @@  struct firmware_cache {
 	struct delayed_work work;
 
 	struct notifier_block   pm_notify;
+
+	struct dentry *debug;
 #endif
 };
 
@@ -790,17 +790,17 @@  static int assign_firmware_buf(struct firmware *fw, struct device *device,
 	 * device may has been deleted already, but the problem
 	 * should be fixed in devres or driver core.
 	 */
-	/* don't cache firmware handled without uevent */
+	/* don't cache without uevent for legacy API */
 	if (device &&
-	    driver_data_param_uevent(&data_params->priv_params) &&
-	    !driver_data_param_nocache(&data_params->priv_params))
+	    (driver_data_param_uevent(&data_params->priv_params) ||
+	    !driver_data_param_nocache(&data_params->req_params)))
 		fw_add_devm_name(device, buf->fw_id);
 
 	/*
 	 * After caching firmware image is started, let it piggyback
 	 * on request firmware.
 	 */
-	if (!driver_data_param_nocache(&data_params->priv_params) &&
+	if (!driver_data_param_nocache(&data_params->req_params) &&
 	    buf->fwc->state == FW_LOADER_START_CACHE) {
 		if (fw_cache_piggyback_on_request(buf->fw_id))
 			kref_get(&buf->ref);
@@ -2437,6 +2437,8 @@  static int fw_cache_piggyback_on_request(const char *name)
 }
 #endif
 
+#include "firmware_class-dbg.c"
+
 static void __init fw_cache_init(void)
 {
 	spin_lock_init(&fw_cache.lock);
@@ -2454,6 +2456,7 @@  static void __init fw_cache_init(void)
 	register_pm_notifier(&fw_cache.pm_notify);
 
 	register_syscore_ops(&fw_syscore_ops);
+	create_debug_files(&fw_cache);
 #endif
 }
 
@@ -2492,6 +2495,7 @@  static void __exit firmware_class_exit(void)
 #ifdef CONFIG_PM_SLEEP
 	unregister_syscore_ops(&fw_syscore_ops);
 	unregister_pm_notifier(&fw_cache.pm_notify);
+	remove_debug_files(&fw_cache);
 #endif
 	unregister_reboot_notifier(&fw_shutdown_nb);
 #ifdef CONFIG_FW_LOADER_USER_HELPER
diff --git a/include/linux/driver_data.h b/include/linux/driver_data.h
index 2cb999e..4d5f9d1 100644
--- a/include/linux/driver_data.h
+++ b/include/linux/driver_data.h
@@ -124,11 +124,15 @@  union driver_data_cbs {
  *	file to be present given the API range, it is only required for one
  *	file in the API range to be present.  If the %DRIVER_DATA_REQ_OPTIONAL
  *	flag is also enabled then all files are treated as optional.
+ * @DRIVER_DATA_REQ_NO_CACHE: indicates that the driver data request
+ *	should not set up and use the internal caching mechanism to assist
+ *	drivers from fetching driver data at resume time after suspend.
  */
 enum driver_data_reqs {
 	DRIVER_DATA_REQ_OPTIONAL			= 1 << 0,
 	DRIVER_DATA_REQ_KEEP				= 1 << 1,
 	DRIVER_DATA_REQ_USE_API_VERSIONING		= 1 << 2,
+	DRIVER_DATA_REQ_NO_CACHE			= 1 << 3,
 };
 
 /**