[2/2] fpga: add FPGA manager debugfs
diff mbox series

Message ID 20180815220958.3606-2-atull@kernel.org
State New
Headers show
Series
  • [1/2] fpga: doc: documentation for FPGA debugfs
Related show

Commit Message

Alan Tull Aug. 15, 2018, 10:09 p.m. UTC
From: Alan Tull <atull@opensource.altera.com>

Implement DebugFS for the FPGA Manager Framework for
debugging and developmental use.

Enabled by CONFIG_FPGA_MGR_DEBUG_FS

Each FPGA gets its own directory such as
 <debugfs>/fpga_manager/fpga0 and three files:

 * [RW] flags          = flags as defined in fpga-mgr.h
 * [RW] firmware_name  = write/read back name of FPGA image
                         firmware file to program
 * [WO] image          = write-only file for directly writing
                         fpga image w/o firmware layer
 * [RW] config_complete_timeout_us = maximum for the FPGA to
                         go to the operating state after
			 programming

The debugfs is implemented in a separate fpga_mgr_debugfs.c
file, but the FPGA manager core is still built as one
module.  Note the name change from fpga-mgr.ko to fpga_mgr.ko.

Signed-off-by: Alan Tull <atull@kernel.org>
Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
---
 drivers/fpga/Kconfig            |   7 ++
 drivers/fpga/Makefile           |   4 +-
 drivers/fpga/fpga-mgr-debugfs.c | 221 ++++++++++++++++++++++++++++++++++++++++
 drivers/fpga/fpga-mgr-debugfs.h |  22 ++++
 drivers/fpga/fpga-mgr.c         |   8 ++
 include/linux/fpga/fpga-mgr.h   |   3 +
 6 files changed, 264 insertions(+), 1 deletion(-)
 create mode 100644 drivers/fpga/fpga-mgr-debugfs.c
 create mode 100644 drivers/fpga/fpga-mgr-debugfs.h

Comments

Randy Dunlap Aug. 15, 2018, 10:34 p.m. UTC | #1
On 08/15/2018 03:09 PM, Alan Tull wrote:
> From: Alan Tull <atull@opensource.altera.com>
> 
> Implement DebugFS for the FPGA Manager Framework for
> debugging and developmental use.
> 
> Enabled by CONFIG_FPGA_MGR_DEBUG_FS

Hi Alan,

for your v2 changes:

> Each FPGA gets its own directory such as
>  <debugfs>/fpga_manager/fpga0 and three files:

s/three/four/

> 
>  * [RW] flags          = flags as defined in fpga-mgr.h
>  * [RW] firmware_name  = write/read back name of FPGA image
>                          firmware file to program
>  * [WO] image          = write-only file for directly writing
>                          fpga image w/o firmware layer
>  * [RW] config_complete_timeout_us = maximum for the FPGA to
>                          go to the operating state after
> 			 programming
> 
> The debugfs is implemented in a separate fpga_mgr_debugfs.c

      debugfs interface is

> file, but the FPGA manager core is still built as one
> module.  Note the name change from fpga-mgr.ko to fpga_mgr.ko.
> 
> Signed-off-by: Alan Tull <atull@kernel.org>
> Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
> ---
>  drivers/fpga/Kconfig            |   7 ++
>  drivers/fpga/Makefile           |   4 +-
>  drivers/fpga/fpga-mgr-debugfs.c | 221 ++++++++++++++++++++++++++++++++++++++++
>  drivers/fpga/fpga-mgr-debugfs.h |  22 ++++
>  drivers/fpga/fpga-mgr.c         |   8 ++
>  include/linux/fpga/fpga-mgr.h   |   3 +
>  6 files changed, 264 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/fpga/fpga-mgr-debugfs.c
>  create mode 100644 drivers/fpga/fpga-mgr-debugfs.h
> 
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index 1ebcef4..600924d 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -9,6 +9,13 @@ menuconfig FPGA
>  	  kernel.  The FPGA framework adds a FPGA manager class and FPGA
>  	  manager drivers.
>  
> +config FPGA_MGR_DEBUG_FS
> +       bool "FPGA Manager DebugFS"
> +       depends on FPGA && DEBUG_FS
> +       help
> +         Say Y here if you want to expose a DebugFS interface for the
> +	 FPGA Manager Framework.

Use tab indentation for bool, depends, and help lines.

Use tab +2 spaces for help text.

> +
>  if FPGA
>  
>  config FPGA_MGR_SOCFPGA

thanks,
Alan Tull Aug. 16, 2018, 2:25 p.m. UTC | #2
On Wed, Aug 15, 2018 at 5:34 PM, Randy Dunlap <rdunlap@infradead.org> wrote:

Hi Randy,

> On 08/15/2018 03:09 PM, Alan Tull wrote:
>> From: Alan Tull <atull@opensource.altera.com>
>>
>> Implement DebugFS for the FPGA Manager Framework for
>> debugging and developmental use.
>>
>> Enabled by CONFIG_FPGA_MGR_DEBUG_FS
>
> Hi Alan,
>
> for your v2 changes:
>
>> Each FPGA gets its own directory such as
>>  <debugfs>/fpga_manager/fpga0 and three files:
>
> s/three/four/

Yes

>
>>
>>  * [RW] flags          = flags as defined in fpga-mgr.h
>>  * [RW] firmware_name  = write/read back name of FPGA image
>>                          firmware file to program
>>  * [WO] image          = write-only file for directly writing
>>                          fpga image w/o firmware layer
>>  * [RW] config_complete_timeout_us = maximum for the FPGA to
>>                          go to the operating state after
>>                        programming
>>
>> The debugfs is implemented in a separate fpga_mgr_debugfs.c
>
>       debugfs interface is

Yes

>
>> file, but the FPGA manager core is still built as one
>> module.  Note the name change from fpga-mgr.ko to fpga_mgr.ko.
>>
>> Signed-off-by: Alan Tull <atull@kernel.org>
>> Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
>> ---
>>  drivers/fpga/Kconfig            |   7 ++
>>  drivers/fpga/Makefile           |   4 +-
>>  drivers/fpga/fpga-mgr-debugfs.c | 221 ++++++++++++++++++++++++++++++++++++++++
>>  drivers/fpga/fpga-mgr-debugfs.h |  22 ++++
>>  drivers/fpga/fpga-mgr.c         |   8 ++
>>  include/linux/fpga/fpga-mgr.h   |   3 +
>>  6 files changed, 264 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/fpga/fpga-mgr-debugfs.c
>>  create mode 100644 drivers/fpga/fpga-mgr-debugfs.h
>>
>> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
>> index 1ebcef4..600924d 100644
>> --- a/drivers/fpga/Kconfig
>> +++ b/drivers/fpga/Kconfig
>> @@ -9,6 +9,13 @@ menuconfig FPGA
>>         kernel.  The FPGA framework adds a FPGA manager class and FPGA
>>         manager drivers.
>>
>> +config FPGA_MGR_DEBUG_FS
>> +       bool "FPGA Manager DebugFS"
>> +       depends on FPGA && DEBUG_FS
>> +       help
>> +         Say Y here if you want to expose a DebugFS interface for the
>> +      FPGA Manager Framework.
>
> Use tab indentation for bool, depends, and help lines.
>
> Use tab +2 spaces for help text.

Yes, thanks for catching that.  I'll fix these in v2.

Thanks for the review!

Alan

>
>> +
>>  if FPGA
>>
>>  config FPGA_MGR_SOCFPGA
>
> thanks,
> --
> ~Randy
Moritz Fischer Aug. 16, 2018, 6:59 p.m. UTC | #3
Hi Alan,

comments inline. While I see how this is useful, I have the
suspicion that from the moment this gets merged vendor kernels
will just default to use this ...

On Wed, Aug 15, 2018 at 05:09:58PM -0500, Alan Tull wrote:
> From: Alan Tull <atull@opensource.altera.com>
> 
> Implement DebugFS for the FPGA Manager Framework for
> debugging and developmental use.
> 
> Enabled by CONFIG_FPGA_MGR_DEBUG_FS
> 
> Each FPGA gets its own directory such as
>  <debugfs>/fpga_manager/fpga0 and three files:
> 
>  * [RW] flags          = flags as defined in fpga-mgr.h
>  * [RW] firmware_name  = write/read back name of FPGA image
>                          firmware file to program
>  * [WO] image          = write-only file for directly writing
>                          fpga image w/o firmware layer
>  * [RW] config_complete_timeout_us = maximum for the FPGA to
>                          go to the operating state after
> 			 programming
> 
> The debugfs is implemented in a separate fpga_mgr_debugfs.c
> file, but the FPGA manager core is still built as one
> module.  Note the name change from fpga-mgr.ko to fpga_mgr.ko.
> 
> Signed-off-by: Alan Tull <atull@kernel.org>
> Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
> ---
>  drivers/fpga/Kconfig            |   7 ++
>  drivers/fpga/Makefile           |   4 +-
>  drivers/fpga/fpga-mgr-debugfs.c | 221 ++++++++++++++++++++++++++++++++++++++++
>  drivers/fpga/fpga-mgr-debugfs.h |  22 ++++
>  drivers/fpga/fpga-mgr.c         |   8 ++
>  include/linux/fpga/fpga-mgr.h   |   3 +
>  6 files changed, 264 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/fpga/fpga-mgr-debugfs.c
>  create mode 100644 drivers/fpga/fpga-mgr-debugfs.h
> 
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index 1ebcef4..600924d 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -9,6 +9,13 @@ menuconfig FPGA
>  	  kernel.  The FPGA framework adds a FPGA manager class and FPGA
>  	  manager drivers.
>  
> +config FPGA_MGR_DEBUG_FS
> +       bool "FPGA Manager DebugFS"
> +       depends on FPGA && DEBUG_FS
> +       help
> +         Say Y here if you want to expose a DebugFS interface for the
> +	 FPGA Manager Framework.
> +
>  if FPGA
>  
>  config FPGA_MGR_SOCFPGA
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> index 7a2d73b..62910cc 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -4,7 +4,9 @@
>  #
>  
>  # Core FPGA Manager Framework
> -obj-$(CONFIG_FPGA)			+= fpga-mgr.o
> +obj-$(CONFIG_FPGA)			+= fpga_mgr.o
> +fpga_mgr-y				:= fpga-mgr.o
> +fpga_mgr-$(CONFIG_FPGA_MGR_DEBUG_FS)	+= fpga-mgr-debugfs.o
>  
>  # FPGA Manager Drivers
>  obj-$(CONFIG_FPGA_MGR_ALTERA_CVP)	+= altera-cvp.o
> diff --git a/drivers/fpga/fpga-mgr-debugfs.c b/drivers/fpga/fpga-mgr-debugfs.c
> new file mode 100644
> index 0000000..f2fdf58
> --- /dev/null
> +++ b/drivers/fpga/fpga-mgr-debugfs.c
> @@ -0,0 +1,221 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * FPGA Manager DebugFS
> + *
> + *  Copyright (C) 2016-2018 Intel Corporation.  All rights reserved.
> + */
> +#include <linux/debugfs.h>
> +#include <linux/fpga/fpga-mgr.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +
> +static struct dentry *fpga_mgr_debugfs_root;
> +
> +struct fpga_mgr_debugfs {
> +	struct dentry *debugfs_dir;
> +	struct fpga_image_info *info;
> +};
> +
> +static ssize_t fpga_mgr_firmware_write_file(struct file *file,
> +					    const char __user *user_buf,
> +					    size_t count, loff_t *ppos)
> +{
> +	struct fpga_manager *mgr = file->private_data;
> +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> +	struct device *dev = &mgr->dev;
> +	char *buf;
> +	int ret;
> +
> +	ret = fpga_mgr_lock(mgr);
> +	if (ret) {
> +		dev_err(dev, "FPGA manager is busy\n");
> +		return -EBUSY;
> +	}
> +
> +	buf = devm_kzalloc(dev, count, GFP_KERNEL);
> +	if (!buf) {
> +		fpga_mgr_unlock(mgr);
> +		return -ENOMEM;
> +	}
> +
> +	if (copy_from_user(buf, user_buf, count)) {
> +		fpga_mgr_unlock(mgr);
> +		devm_kfree(dev, buf);
> +		return -EFAULT;
> +	}
> +
> +	buf[count] = 0;
> +	if (buf[count - 1] == '\n')
> +		buf[count - 1] = 0;
> +
> +	/* Release previous firmware name (if any). Save current one. */
> +	if (debugfs->info->firmware_name)
> +		devm_kfree(dev, debugfs->info->firmware_name);
> +	debugfs->info->firmware_name = buf;
> +
> +	ret = fpga_mgr_load(mgr, debugfs->info);
> +	if (ret)
> +		dev_err(dev, "fpga_mgr_load returned with value %d\n", ret);
> +
> +	fpga_mgr_unlock(mgr);
> +
> +	return count;
> +}
> +
> +static ssize_t fpga_mgr_firmware_read_file(struct file *file,
> +					   char __user *user_buf,
> +					   size_t count, loff_t *ppos)
> +{
> +	struct fpga_manager *mgr = file->private_data;
> +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> +	char *buf;
> +	int ret;
> +
> +	if (!debugfs->info->firmware_name)
> +		return 0;
> +
> +	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	ret = snprintf(buf, PAGE_SIZE, "%s\n", debugfs->info->firmware_name);
snip:
---->8->8->8-----
> +	if (ret < 0) {
> +		kfree(buf);
> +		return ret;
> +	}
---->8->8->8-----
just replace with:
	if (ret < 0)
		goto out;
> +
> +	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);

out:
> +	kfree(buf);
> +	return ret;
> +}
> +
> +static const struct file_operations fpga_mgr_firmware_fops = {
> +	.open = simple_open,
> +	.read = fpga_mgr_firmware_read_file,
> +	.write = fpga_mgr_firmware_write_file,
> +	.llseek = default_llseek,
> +};
> +
> +static ssize_t fpga_mgr_image_write_file(struct file *file,
> +					 const char __user *user_buf,
> +					 size_t count, loff_t *ppos)
> +{
> +	struct fpga_manager *mgr = file->private_data;
> +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> +	struct device *dev = &mgr->dev;
> +	char *buf;
> +	int ret;
> +
> +	dev_info(dev, "writing %zu bytes to %s\n", count, mgr->name);
> +
> +	ret = fpga_mgr_lock(mgr);
> +	if (ret) {
> +		dev_err(dev, "FPGA manager is busy\n");
> +		return -EBUSY;
> +	}
> +
> +	buf = kzalloc(count, GFP_KERNEL);
> +	if (!buf) {
> +		fpga_mgr_unlock(mgr);
> +		return -ENOMEM;
> +	}
> +
> +	if (copy_from_user(buf, user_buf, count)) {
> +		fpga_mgr_unlock(mgr);
> +		kfree(buf);
> +		return -EFAULT;
> +	}
> +
> +	/* If firmware interface was previously used, forget it. */
> +	if (debugfs->info->firmware_name)
> +		devm_kfree(dev, debugfs->info->firmware_name);
> +	debugfs->info->firmware_name = NULL;
> +
> +	debugfs->info->buf = buf;
> +	debugfs->info->count = count;
> +
> +	ret = fpga_mgr_load(mgr, debugfs->info);
> +	if (ret)
> +		dev_err(dev, "fpga_mgr_load returned with value %d\n", ret);
> +
> +	fpga_mgr_unlock(mgr);
> +
> +	debugfs->info->buf = NULL;
> +	debugfs->info->count = 0;

Is that ordering between unlock() and setting those correct?
> +
> +	kfree(buf);
> +
> +	return count;
> +}
> +
> +static const struct file_operations fpga_mgr_image_fops = {
> +	.open = simple_open,
> +	.write = fpga_mgr_image_write_file,
> +	.llseek = default_llseek,
> +};
> +
> +void fpga_mgr_debugfs_add(struct fpga_manager *mgr)
> +{
> +	struct device *dev = &mgr->dev;
> +	struct fpga_mgr_debugfs *debugfs;
> +	struct fpga_image_info *info;
> +
> +	if (!fpga_mgr_debugfs_root)
> +		return;
> +
> +	debugfs = kzalloc(sizeof(*debugfs), GFP_KERNEL);
> +	if (!debugfs)
> +		return;
> +
> +	info = fpga_image_info_alloc(dev);
> +	if (!info) {
> +		kfree(debugfs);
> +		return;
> +	}
> +	debugfs->info = info;
> +
> +	debugfs->debugfs_dir = debugfs_create_dir(dev_name(dev),
> +						  fpga_mgr_debugfs_root);
> +
> +	debugfs_create_file("firmware_name", 0600, debugfs->debugfs_dir, mgr,
> +			    &fpga_mgr_firmware_fops);
> +
> +	debugfs_create_file("image", 0200, debugfs->debugfs_dir, mgr,
> +			    &fpga_mgr_image_fops);
> +
> +	debugfs_create_u32("flags", 0600, debugfs->debugfs_dir, &info->flags);
> +
> +	debugfs_create_u32("config_complete_timeout_us", 0600,
> +			   debugfs->debugfs_dir,
> +			   &info->config_complete_timeout_us);
> +
> +	mgr->debugfs = debugfs;
> +}
> +
> +void fpga_mgr_debugfs_remove(struct fpga_manager *mgr)
> +{
> +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> +
> +	if (!fpga_mgr_debugfs_root)
> +		return;
> +
> +	debugfs_remove_recursive(debugfs->debugfs_dir);
> +
> +	/* this function also frees debugfs->info->firmware_name */
> +	fpga_image_info_free(debugfs->info);
> +
> +	kfree(debugfs);
> +}
> +
> +void fpga_mgr_debugfs_init(void)
> +{
> +	fpga_mgr_debugfs_root = debugfs_create_dir("fpga_manager", NULL);
> +	if (!fpga_mgr_debugfs_root)
> +		pr_warn("fpga_mgr: Failed to create debugfs root\n");
> +}
> +
> +void fpga_mgr_debugfs_uninit(void)
> +{
> +	debugfs_remove_recursive(fpga_mgr_debugfs_root);
> +}
> diff --git a/drivers/fpga/fpga-mgr-debugfs.h b/drivers/fpga/fpga-mgr-debugfs.h
> new file mode 100644
> index 0000000..17cd3eb
> --- /dev/null
> +++ b/drivers/fpga/fpga-mgr-debugfs.h
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#ifndef _LINUX_FPGA_MGR_DEBUGFS_H
> +#define _LINUX_FPGA_MGR_DEBUGFS_H
> +
> +#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
> +
> +void fpga_mgr_debugfs_add(struct fpga_manager *mgr);
> +void fpga_mgr_debugfs_remove(struct fpga_manager *mgr);
> +void fpga_mgr_debugfs_init(void);
> +void fpga_mgr_debugfs_uninit(void);
> +
> +#else
> +
> +void fpga_mgr_debugfs_add(struct fpga_manager *mgr) {}
> +void fpga_mgr_debugfs_remove(struct fpga_manager *mgr) {}
> +void fpga_mgr_debugfs_init(void) {}
> +void fpga_mgr_debugfs_uninit(void) {}
> +
> +#endif /* CONFIG_FPGA_MGR_DEBUG_FS */
> +
> +#endif /*_LINUX_FPGA_MGR_DEBUGFS_H */
> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
> index c8684bc..66eb6f6 100644
> --- a/drivers/fpga/fpga-mgr.c
> +++ b/drivers/fpga/fpga-mgr.c
> @@ -17,6 +17,7 @@
>  #include <linux/slab.h>
>  #include <linux/scatterlist.h>
>  #include <linux/highmem.h>
> +#include "fpga-mgr-debugfs.h"
>  
>  static DEFINE_IDA(fpga_mgr_ida);
>  static struct class *fpga_mgr_class;
> @@ -698,6 +699,8 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>  	if (ret)
>  		goto error_device;
>  
> +	fpga_mgr_debugfs_add(mgr);
> +
>  	dev_info(&mgr->dev, "%s registered\n", mgr->name);
>  
>  	return 0;
> @@ -722,6 +725,8 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>  {
>  	dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
>  
> +	fpga_mgr_debugfs_remove(mgr);
> +
>  	/*
>  	 * If the low level driver provides a method for putting fpga into
>  	 * a desired state upon unregister, do it.
> @@ -748,11 +753,14 @@ static int __init fpga_mgr_class_init(void)
>  	fpga_mgr_class->dev_groups = fpga_mgr_groups;
>  	fpga_mgr_class->dev_release = fpga_mgr_dev_release;
>  
> +	fpga_mgr_debugfs_init();
> +
>  	return 0;
>  }
>  
>  static void __exit fpga_mgr_class_exit(void)
>  {
> +	fpga_mgr_debugfs_uninit();
>  	class_destroy(fpga_mgr_class);
>  	ida_destroy(&fpga_mgr_ida);
>  }
> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
> index 777c502..e8f2159 100644
> --- a/include/linux/fpga/fpga-mgr.h
> +++ b/include/linux/fpga/fpga-mgr.h
> @@ -170,6 +170,9 @@ struct fpga_manager {
>  	struct fpga_compat_id *compat_id;
>  	const struct fpga_manager_ops *mops;
>  	void *priv;
> +#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
> +	void *debugfs;
> +#endif
>  };
>  
>  #define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)
> -- 
> 2.7.4
> 

Thanks,

Moritz
Alan Tull Aug. 16, 2018, 8:04 p.m. UTC | #4
On Thu, Aug 16, 2018 at 1:59 PM, Moritz Fischer <mdf@kernel.org> wrote:
> Hi Alan,

Hi Moritz,

> comments inline. While I see how this is useful, I have the
> suspicion that from the moment this gets merged vendor kernels
> will just default to use this ...

Yeah, I have that suspicion as well.  That's probably why I sat on
this and didn't upstream it for 2 years.  But on the other hand, I
keep hearing of lots of cases of people implementing this
independently anyway.  At least if it is debugfs, it makes it clear
that it's not intended for production use.

>
> On Wed, Aug 15, 2018 at 05:09:58PM -0500, Alan Tull wrote:
>> From: Alan Tull <atull@opensource.altera.com>
>>
>> Implement DebugFS for the FPGA Manager Framework for
>> debugging and developmental use.
>>
>> Enabled by CONFIG_FPGA_MGR_DEBUG_FS
>>
>> Each FPGA gets its own directory such as
>>  <debugfs>/fpga_manager/fpga0 and three files:
>>
>>  * [RW] flags          = flags as defined in fpga-mgr.h
>>  * [RW] firmware_name  = write/read back name of FPGA image
>>                          firmware file to program
>>  * [WO] image          = write-only file for directly writing
>>                          fpga image w/o firmware layer
>>  * [RW] config_complete_timeout_us = maximum for the FPGA to
>>                          go to the operating state after
>>                        programming
>>
>> The debugfs is implemented in a separate fpga_mgr_debugfs.c
>> file, but the FPGA manager core is still built as one
>> module.  Note the name change from fpga-mgr.ko to fpga_mgr.ko.
>>
>> Signed-off-by: Alan Tull <atull@kernel.org>
>> Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
>> ---
>>  drivers/fpga/Kconfig            |   7 ++
>>  drivers/fpga/Makefile           |   4 +-
>>  drivers/fpga/fpga-mgr-debugfs.c | 221 ++++++++++++++++++++++++++++++++++++++++
>>  drivers/fpga/fpga-mgr-debugfs.h |  22 ++++
>>  drivers/fpga/fpga-mgr.c         |   8 ++
>>  include/linux/fpga/fpga-mgr.h   |   3 +
>>  6 files changed, 264 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/fpga/fpga-mgr-debugfs.c
>>  create mode 100644 drivers/fpga/fpga-mgr-debugfs.h
>>
>> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
>> index 1ebcef4..600924d 100644
>> --- a/drivers/fpga/Kconfig
>> +++ b/drivers/fpga/Kconfig
>> @@ -9,6 +9,13 @@ menuconfig FPGA
>>         kernel.  The FPGA framework adds a FPGA manager class and FPGA
>>         manager drivers.
>>
>> +config FPGA_MGR_DEBUG_FS
>> +       bool "FPGA Manager DebugFS"
>> +       depends on FPGA && DEBUG_FS
>> +       help
>> +         Say Y here if you want to expose a DebugFS interface for the
>> +      FPGA Manager Framework.
>> +
>>  if FPGA
>>
>>  config FPGA_MGR_SOCFPGA
>> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
>> index 7a2d73b..62910cc 100644
>> --- a/drivers/fpga/Makefile
>> +++ b/drivers/fpga/Makefile
>> @@ -4,7 +4,9 @@
>>  #
>>
>>  # Core FPGA Manager Framework
>> -obj-$(CONFIG_FPGA)                   += fpga-mgr.o
>> +obj-$(CONFIG_FPGA)                   += fpga_mgr.o
>> +fpga_mgr-y                           := fpga-mgr.o
>> +fpga_mgr-$(CONFIG_FPGA_MGR_DEBUG_FS) += fpga-mgr-debugfs.o
>>
>>  # FPGA Manager Drivers
>>  obj-$(CONFIG_FPGA_MGR_ALTERA_CVP)    += altera-cvp.o
>> diff --git a/drivers/fpga/fpga-mgr-debugfs.c b/drivers/fpga/fpga-mgr-debugfs.c
>> new file mode 100644
>> index 0000000..f2fdf58
>> --- /dev/null
>> +++ b/drivers/fpga/fpga-mgr-debugfs.c
>> @@ -0,0 +1,221 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * FPGA Manager DebugFS
>> + *
>> + *  Copyright (C) 2016-2018 Intel Corporation.  All rights reserved.
>> + */
>> +#include <linux/debugfs.h>
>> +#include <linux/fpga/fpga-mgr.h>
>> +#include <linux/slab.h>
>> +#include <linux/uaccess.h>
>> +
>> +static struct dentry *fpga_mgr_debugfs_root;
>> +
>> +struct fpga_mgr_debugfs {
>> +     struct dentry *debugfs_dir;
>> +     struct fpga_image_info *info;
>> +};
>> +
>> +static ssize_t fpga_mgr_firmware_write_file(struct file *file,
>> +                                         const char __user *user_buf,
>> +                                         size_t count, loff_t *ppos)
>> +{
>> +     struct fpga_manager *mgr = file->private_data;
>> +     struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
>> +     struct device *dev = &mgr->dev;
>> +     char *buf;
>> +     int ret;
>> +
>> +     ret = fpga_mgr_lock(mgr);
>> +     if (ret) {
>> +             dev_err(dev, "FPGA manager is busy\n");
>> +             return -EBUSY;
>> +     }
>> +
>> +     buf = devm_kzalloc(dev, count, GFP_KERNEL);
>> +     if (!buf) {
>> +             fpga_mgr_unlock(mgr);
>> +             return -ENOMEM;
>> +     }
>> +
>> +     if (copy_from_user(buf, user_buf, count)) {
>> +             fpga_mgr_unlock(mgr);
>> +             devm_kfree(dev, buf);
>> +             return -EFAULT;
>> +     }
>> +
>> +     buf[count] = 0;
>> +     if (buf[count - 1] == '\n')
>> +             buf[count - 1] = 0;
>> +
>> +     /* Release previous firmware name (if any). Save current one. */
>> +     if (debugfs->info->firmware_name)
>> +             devm_kfree(dev, debugfs->info->firmware_name);
>> +     debugfs->info->firmware_name = buf;
>> +
>> +     ret = fpga_mgr_load(mgr, debugfs->info);
>> +     if (ret)
>> +             dev_err(dev, "fpga_mgr_load returned with value %d\n", ret);
>> +
>> +     fpga_mgr_unlock(mgr);
>> +
>> +     return count;
>> +}
>> +
>> +static ssize_t fpga_mgr_firmware_read_file(struct file *file,
>> +                                        char __user *user_buf,
>> +                                        size_t count, loff_t *ppos)
>> +{
>> +     struct fpga_manager *mgr = file->private_data;
>> +     struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
>> +     char *buf;
>> +     int ret;
>> +
>> +     if (!debugfs->info->firmware_name)
>> +             return 0;
>> +
>> +     buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
>> +     if (!buf)
>> +             return -ENOMEM;
>> +
>> +     ret = snprintf(buf, PAGE_SIZE, "%s\n", debugfs->info->firmware_name);
> snip:
> ---->8->8->8-----
>> +     if (ret < 0) {
>> +             kfree(buf);
>> +             return ret;
>> +     }
> ---->8->8->8-----
> just replace with:
>         if (ret < 0)
>                 goto out;
>> +
>> +     ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
>
> out:
>> +     kfree(buf);
>> +     return ret;
>> +}

Yes, that's better. I'll include that in v2.

>> +
>> +static const struct file_operations fpga_mgr_firmware_fops = {
>> +     .open = simple_open,
>> +     .read = fpga_mgr_firmware_read_file,
>> +     .write = fpga_mgr_firmware_write_file,
>> +     .llseek = default_llseek,
>> +};
>> +
>> +static ssize_t fpga_mgr_image_write_file(struct file *file,
>> +                                      const char __user *user_buf,
>> +                                      size_t count, loff_t *ppos)
>> +{
>> +     struct fpga_manager *mgr = file->private_data;
>> +     struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
>> +     struct device *dev = &mgr->dev;
>> +     char *buf;
>> +     int ret;
>> +
>> +     dev_info(dev, "writing %zu bytes to %s\n", count, mgr->name);
>> +
>> +     ret = fpga_mgr_lock(mgr);
>> +     if (ret) {
>> +             dev_err(dev, "FPGA manager is busy\n");
>> +             return -EBUSY;
>> +     }
>> +
>> +     buf = kzalloc(count, GFP_KERNEL);
>> +     if (!buf) {
>> +             fpga_mgr_unlock(mgr);
>> +             return -ENOMEM;
>> +     }
>> +
>> +     if (copy_from_user(buf, user_buf, count)) {
>> +             fpga_mgr_unlock(mgr);
>> +             kfree(buf);
>> +             return -EFAULT;
>> +     }
>> +
>> +     /* If firmware interface was previously used, forget it. */
>> +     if (debugfs->info->firmware_name)
>> +             devm_kfree(dev, debugfs->info->firmware_name);
>> +     debugfs->info->firmware_name = NULL;
>> +
>> +     debugfs->info->buf = buf;
>> +     debugfs->info->count = count;
>> +
>> +     ret = fpga_mgr_load(mgr, debugfs->info);
>> +     if (ret)
>> +             dev_err(dev, "fpga_mgr_load returned with value %d\n", ret);
>> +
>> +     fpga_mgr_unlock(mgr);
>> +
>> +     debugfs->info->buf = NULL;
>> +     debugfs->info->count = 0;
>
> Is that ordering between unlock() and setting those correct?

This order should be alright, but I'll switch it anyway to be more
obviously correct.

>> +
>> +     kfree(buf);
>> +
>> +     return count;
>> +}
>> +
>> +static const struct file_operations fpga_mgr_image_fops = {
>> +     .open = simple_open,
>> +     .write = fpga_mgr_image_write_file,
>> +     .llseek = default_llseek,
>> +};
>> +
>> +void fpga_mgr_debugfs_add(struct fpga_manager *mgr)
>> +{
>> +     struct device *dev = &mgr->dev;
>> +     struct fpga_mgr_debugfs *debugfs;
>> +     struct fpga_image_info *info;
>> +
>> +     if (!fpga_mgr_debugfs_root)
>> +             return;
>> +
>> +     debugfs = kzalloc(sizeof(*debugfs), GFP_KERNEL);
>> +     if (!debugfs)
>> +             return;
>> +
>> +     info = fpga_image_info_alloc(dev);
>> +     if (!info) {
>> +             kfree(debugfs);
>> +             return;
>> +     }
>> +     debugfs->info = info;
>> +
>> +     debugfs->debugfs_dir = debugfs_create_dir(dev_name(dev),
>> +                                               fpga_mgr_debugfs_root);
>> +
>> +     debugfs_create_file("firmware_name", 0600, debugfs->debugfs_dir, mgr,
>> +                         &fpga_mgr_firmware_fops);
>> +
>> +     debugfs_create_file("image", 0200, debugfs->debugfs_dir, mgr,
>> +                         &fpga_mgr_image_fops);
>> +
>> +     debugfs_create_u32("flags", 0600, debugfs->debugfs_dir, &info->flags);
>> +
>> +     debugfs_create_u32("config_complete_timeout_us", 0600,
>> +                        debugfs->debugfs_dir,
>> +                        &info->config_complete_timeout_us);
>> +
>> +     mgr->debugfs = debugfs;
>> +}
>> +
>> +void fpga_mgr_debugfs_remove(struct fpga_manager *mgr)
>> +{
>> +     struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
>> +
>> +     if (!fpga_mgr_debugfs_root)
>> +             return;
>> +
>> +     debugfs_remove_recursive(debugfs->debugfs_dir);
>> +
>> +     /* this function also frees debugfs->info->firmware_name */
>> +     fpga_image_info_free(debugfs->info);
>> +
>> +     kfree(debugfs);
>> +}
>> +
>> +void fpga_mgr_debugfs_init(void)
>> +{
>> +     fpga_mgr_debugfs_root = debugfs_create_dir("fpga_manager", NULL);
>> +     if (!fpga_mgr_debugfs_root)
>> +             pr_warn("fpga_mgr: Failed to create debugfs root\n");
>> +}
>> +
>> +void fpga_mgr_debugfs_uninit(void)
>> +{
>> +     debugfs_remove_recursive(fpga_mgr_debugfs_root);
>> +}
>> diff --git a/drivers/fpga/fpga-mgr-debugfs.h b/drivers/fpga/fpga-mgr-debugfs.h
>> new file mode 100644
>> index 0000000..17cd3eb
>> --- /dev/null
>> +++ b/drivers/fpga/fpga-mgr-debugfs.h
>> @@ -0,0 +1,22 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +#ifndef _LINUX_FPGA_MGR_DEBUGFS_H
>> +#define _LINUX_FPGA_MGR_DEBUGFS_H
>> +
>> +#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
>> +
>> +void fpga_mgr_debugfs_add(struct fpga_manager *mgr);
>> +void fpga_mgr_debugfs_remove(struct fpga_manager *mgr);
>> +void fpga_mgr_debugfs_init(void);
>> +void fpga_mgr_debugfs_uninit(void);
>> +
>> +#else
>> +
>> +void fpga_mgr_debugfs_add(struct fpga_manager *mgr) {}
>> +void fpga_mgr_debugfs_remove(struct fpga_manager *mgr) {}
>> +void fpga_mgr_debugfs_init(void) {}
>> +void fpga_mgr_debugfs_uninit(void) {}
>> +
>> +#endif /* CONFIG_FPGA_MGR_DEBUG_FS */
>> +
>> +#endif /*_LINUX_FPGA_MGR_DEBUGFS_H */
>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>> index c8684bc..66eb6f6 100644
>> --- a/drivers/fpga/fpga-mgr.c
>> +++ b/drivers/fpga/fpga-mgr.c
>> @@ -17,6 +17,7 @@
>>  #include <linux/slab.h>
>>  #include <linux/scatterlist.h>
>>  #include <linux/highmem.h>
>> +#include "fpga-mgr-debugfs.h"
>>
>>  static DEFINE_IDA(fpga_mgr_ida);
>>  static struct class *fpga_mgr_class;
>> @@ -698,6 +699,8 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>       if (ret)
>>               goto error_device;
>>
>> +     fpga_mgr_debugfs_add(mgr);
>> +
>>       dev_info(&mgr->dev, "%s registered\n", mgr->name);
>>
>>       return 0;
>> @@ -722,6 +725,8 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>  {
>>       dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
>>
>> +     fpga_mgr_debugfs_remove(mgr);
>> +
>>       /*
>>        * If the low level driver provides a method for putting fpga into
>>        * a desired state upon unregister, do it.
>> @@ -748,11 +753,14 @@ static int __init fpga_mgr_class_init(void)
>>       fpga_mgr_class->dev_groups = fpga_mgr_groups;
>>       fpga_mgr_class->dev_release = fpga_mgr_dev_release;
>>
>> +     fpga_mgr_debugfs_init();
>> +
>>       return 0;
>>  }
>>
>>  static void __exit fpga_mgr_class_exit(void)
>>  {
>> +     fpga_mgr_debugfs_uninit();
>>       class_destroy(fpga_mgr_class);
>>       ida_destroy(&fpga_mgr_ida);
>>  }
>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>> index 777c502..e8f2159 100644
>> --- a/include/linux/fpga/fpga-mgr.h
>> +++ b/include/linux/fpga/fpga-mgr.h
>> @@ -170,6 +170,9 @@ struct fpga_manager {
>>       struct fpga_compat_id *compat_id;
>>       const struct fpga_manager_ops *mops;
>>       void *priv;
>> +#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
>> +     void *debugfs;
>> +#endif
>>  };
>>
>>  #define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)
>> --
>> 2.7.4
>>
>
> Thanks,
>
> Moritz

Thanks for the review!

Alan
Federico Vaga Aug. 16, 2018, 9:21 p.m. UTC | #5
Hi,

On Thursday, August 16, 2018 10:04:51 PM CEST Alan Tull wrote:
> On Thu, Aug 16, 2018 at 1:59 PM, Moritz Fischer <mdf@kernel.org> wrote:
> > Hi Alan,
> 
> Hi Moritz,
> 
> > comments inline. While I see how this is useful, I have the
> > suspicion that from the moment this gets merged vendor kernels
> > will just default to use this ...
> 
> Yeah, I have that suspicion as well.  That's probably why I sat on
> this and didn't upstream it for 2 years.  But on the other hand, I
> keep hearing of lots of cases of people implementing this
> independently anyway.  At least if it is debugfs, it makes it clear
> that it's not intended for production use.

I'm one of those guys who implemented this independently.

@Mortiz
I do not see how this can be a bad thing (from what you wrote I guess you 
prefer another interface). Which interface to use depends on the use case.
If you have this suspicion it's, I guess, because such interface it is 
extremely easy to use.

@Alan
DebugFS can be a first step, but I would go for a normal device in /dev at 
some point. I do not see why this should not be used in production

Below, again, my code that does the same thing. I already posted this in 
another thread [1] but probably this is the best place. As I said in the 
other thread, this is what I did quickly last year to make things working 
for us (where this kind of interface is a must). Unfortunately, I do not 
have time to review my own code and improve it.


[1] https://lkml.org/lkml/2018/8/16/107


-----
From d6a6df9515c5e92cc74b8948c3c10ac9cbeec6d2 Mon Sep 17 00:00:00 2001
From: Federico Vaga <federico.vaga@vaga.pv.it>
Date: Mon, 20 Nov 2017 14:40:26 +0100
Subject: [PATCH] fpga: program from user-space

Signed-off-by: Federico Vaga <federico.vaga@vaga.pv.it>
---
 Documentation/fpga/fpga-mgr.txt |   3 +
 drivers/fpga/fpga-mgr.c         | 261 ++++++++++++++++++++++++++++++++++++
++
+-
 include/linux/fpga/fpga-mgr.h   |  19 +++
 3 files changed, 281 insertions(+), 2 deletions(-)

diff --git a/Documentation/fpga/fpga-mgr.txt b/Documentation/fpga/fpga-
mgr.txt
index 78f197f..397dae9 100644
--- a/Documentation/fpga/fpga-mgr.txt
+++ b/Documentation/fpga/fpga-mgr.txt
@@ -197,3 +197,6 @@ to put the FPGA into operating mode.
 The ops include a .state function which will read the hardware FPGA 
manager 
and
 return a code of type enum fpga_mgr_states.  It doesn't result in a change 
in
 hardware state.
+
+Configuring the FPGA from user-space
+====================================
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index 6441f91..964b7e4 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -27,10 +27,56 @@
 #include <linux/slab.h>
 #include <linux/scatterlist.h>
 #include <linux/highmem.h>
+#include <linux/spinlock.h>
 #include <linux/version.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
 
 static DEFINE_IDA(fpga_mgr_ida);
 static struct class *fpga_mgr_class;
+static dev_t fpga_mgr_devt;
+
+/**
+ * struct fpga_image_chunck - FPGA configuration chunck
+ * @data: chunk data
+ * @size: chunk data size
+ * @list: for the linked list of chunks
+ */
+struct fpga_image_chunk {
+       void *data;
+       size_t size;
+       struct list_head list;
+};
+#define CHUNK_MAX_SIZE (PAGE_SIZE)
+
+/**
+ * struct fpga_user_load - structure to handle configuration from user-
space
+ * @mgr: pointer to the FPGA manager
+ * @chunk_list: HEAD point of a linked list of FPGA chunks
+ * @n_chunks: number of chunks in the list
+ * @lock: it protects: chunk_list, n_chunks
+ */
+struct fpga_user_load {
+       struct fpga_manager *mgr;
+       struct list_head chunk_list;
+       unsigned int n_chunks;
+       struct spinlock lock;
+};
+
+
+/**
+ * It sets by default a huge timeout for configuration coming from user-
space
+ * just to play safe.
+ *
+ * FIXME what about sysfs parameters to adjust it? The flag bit in 
particular
+ */
+struct fpga_image_info default_user_info = {
+       .flags = 0,
+       .enable_timeout_us = 10000000, /* 10s */
+       .disable_timeout_us = 10000000, /* 10s */
+       .config_complete_timeout_us = 10000000, /* 10s */
+};
+
 
 /*
  * Call the low level driver's write_init function.  This will do the
@@ -310,6 +356,158 @@ int fpga_mgr_firmware_load(struct fpga_manager *mgr,
 }
 EXPORT_SYMBOL_GPL(fpga_mgr_firmware_load);
 
+
+static int fpga_mgr_open(struct inode *inode, struct file *file)
+{
+       struct fpga_manager *mgr = container_of(inode->i_cdev,
+                                               struct fpga_manager,
+                                               cdev);
+       struct fpga_user_load *usr;
+       int ret;
+
+       /* Allow the user-space programming only if user unlocked the FPGA 
*/
+       if (!test_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags)) {
+               dev_info(&mgr->dev, "The FPGA programming is locked\n");
+               return -EPERM;
+       }
+
+       ret = mutex_trylock(&mgr->usr_mutex);
+       if (ret == 0)
+               return -EBUSY;
+
+       usr = kzalloc(sizeof(struct fpga_user_load), GFP_KERNEL);
+       if (!usr) {
+               ret = -ENOMEM;
+               goto err_alloc;
+       }
+
+       usr->mgr = mgr;
+       spin_lock_init(&usr->lock);
+       INIT_LIST_HEAD(&usr->chunk_list);
+       file->private_data = usr;
+
+       return 0;
+
+err_alloc:
+       spin_lock(&mgr->lock);
+       clear_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags);
+       spin_unlock(&mgr->lock);
+       mutex_unlock(&mgr->usr_mutex);
+       return ret;
+}
+
+
+static int fpga_mgr_flush(struct file *file, fl_owner_t id)
+{
+       struct fpga_user_load *usr = file->private_data;
+       struct fpga_image_chunk *chunk, *tmp;
+       struct sg_table sgt;
+       struct scatterlist *sg;
+       int err = 0;
+
+       if (!usr->n_chunks)
+               return 0;
+
+       err = sg_alloc_table(&sgt, usr->n_chunks, GFP_KERNEL);
+       if (err)
+               goto out;
+
+       sg = sgt.sgl;
+       list_for_each_entry(chunk, &usr->chunk_list, list) {
+               sg_set_buf(sg, chunk->data, chunk->size);
+               sg = sg_next(sg);
+               if (!sg)
+                       break;
+       }
+
+       err = fpga_mgr_buf_load_sg(usr->mgr,
+                                  &default_user_info,
+                                  &sgt);
+       sg_free_table(&sgt);
+
+out:
+       /* Remove all chunks */
+       spin_lock(&usr->lock);
+       list_for_each_entry_safe(chunk, tmp, &usr->chunk_list, list) {
+               list_del(&chunk->list);
+               kfree(chunk->data);
+               kfree(chunk);
+               usr->n_chunks--;
+       }
+       spin_unlock(&usr->lock);
+
+       return err;
+}
+
+
+static int fpga_mgr_close(struct inode *inode, struct file *file)
+{
+       struct fpga_user_load *usr = file->private_data;
+       struct fpga_manager *mgr = usr->mgr;
+
+       kfree(usr);
+
+       spin_lock(&mgr->lock);
+       clear_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags);
+       spin_unlock(&mgr->lock);
+
+       mutex_unlock(&mgr->usr_mutex);
+
+       return 0;
+}
+
+
+static ssize_t fpga_mgr_write(struct file *file, const char __user *buf,
+                             size_t count, loff_t *offp)
+{
+       struct fpga_user_load *usr = file->private_data;
+       struct fpga_image_chunk *chunk;
+       int err;
+
+       chunk = kmalloc(sizeof(struct fpga_image_chunk), GFP_KERNEL);
+       if (!chunk)
+               return -ENOMEM;
+
+       chunk->size = count > CHUNK_MAX_SIZE ? CHUNK_MAX_SIZE : count;
+       chunk->data = kmalloc(chunk->size, GFP_KERNEL);
+       if (!chunk->data) {
+               err = -ENOMEM;
+               goto err_buf_alloc;
+       }
+
+       err = copy_from_user(chunk->data, buf, chunk->size);
+       if(err)
+               goto err_buf_copy;
+
+       spin_lock(&usr->lock);
+       list_add_tail(&chunk->list, &usr->chunk_list);
+       usr->n_chunks++;
+       spin_unlock(&usr->lock);
+
+       *offp += count;
+
+       return chunk->size;
+
+err_buf_copy:
+       kfree(chunk->data);
+err_buf_alloc:
+       kfree(chunk);
+       return err;
+}
+
+
+/**
+ * Char device operation
+ */
+static const struct file_operations fpga_mgr_fops = {
+       .owner = THIS_MODULE,
+       .open = fpga_mgr_open,
+       .flush = fpga_mgr_flush,
+       .release = fpga_mgr_close,
+       .write  = fpga_mgr_write,
+};
+
+
 static const char * const state_str[] = {
        [FPGA_MGR_STATE_UNKNOWN] =              "unknown",
        [FPGA_MGR_STATE_POWER_OFF] =            "power off",
@@ -352,13 +550,43 @@ static ssize_t state_show(struct device *dev,
        return sprintf(buf, "%s\n", state_str[mgr->state]);
 }
 
+static ssize_t config_lock_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct fpga_manager *mgr = to_fpga_manager(dev);
+
+       if (test_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags))
+               return sprintf(buf, "unlock\n");
+       return sprintf(buf, "lock\n");
+}
+
+static ssize_t config_lock_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       struct fpga_manager *mgr = to_fpga_manager(dev);
+
+       spin_lock(&mgr->lock);
+       if (strncmp(buf, "lock" , min(4, (int)count)) == 0)
+               clear_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags);
+       else if (strncmp(buf, "unlock" , min(6, (int)count)) == 0)
+               set_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags);
+       else
+               count = -EINVAL;
+       spin_unlock(&mgr->lock);
+
+       return count;
+}
+
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
 static DEVICE_ATTR_RO(name);
 static DEVICE_ATTR_RO(state);
+static DEVICE_ATTR_RW(config_lock);
 
 static struct attribute *fpga_mgr_attrs[] = {
        &dev_attr_name.attr,
        &dev_attr_state.attr,
+       &dev_attr_lock.attr,
        NULL,
 };
 ATTRIBUTE_GROUPS(fpga_mgr);
@@ -367,6 +595,9 @@ ATTRIBUTE_GROUPS(fpga_mgr);
 static struct device_attribute fpga_mgr_attrs[] = {
        __ATTR(name, S_IRUGO, name_show, NULL),
        __ATTR(state, S_IRUGO, state_show, NULL),
+       __ATTR(config_lock, S_IRUGO | S_IWUSR | S_IRGRP | S_IWGRP,
+              config_lock_show, config_lock_store),
+       __ATTR_NULL,
 };
 #endif
 
@@ -507,6 +738,8 @@ int fpga_mgr_register(struct device *dev, const char 
*name,
        }
 
        mutex_init(&mgr->ref_mutex);
+       mutex_init(&mgr->usr_mutex);
+       spin_lock_init(&mgr->lock);
 
        mgr->name = name;
        mgr->mops = mops;
@@ -524,12 +757,19 @@ int fpga_mgr_register(struct device *dev, const char 
*name,
        mgr->dev.parent = dev;
        mgr->dev.of_node = dev->of_node;
        mgr->dev.id = id;
+       mgr->dev.devt = MKDEV(MAJOR(fpga_mgr_devt), id);
        dev_set_drvdata(dev, mgr);
 
        ret = dev_set_name(&mgr->dev, "fpga%d", id);
        if (ret)
                goto error_device;
 
+       cdev_init(&mgr->cdev, &fpga_mgr_fops);
+       mgr->cdev.owner = THIS_MODULE;
+       ret = cdev_add(&mgr->cdev, mgr->dev.devt, 1);
+       if (ret)
+               goto err_cdev;
+
        ret = device_add(&mgr->dev);
        if (ret)
                goto error_device;
@@ -539,6 +779,8 @@ int fpga_mgr_register(struct device *dev, const char 
*name,
        return 0;
 
 error_device:
+       cdev_del(&mgr->cdev);
+err_cdev:
        ida_simple_remove(&fpga_mgr_ida, id);
 error_kfree:
        kfree(mgr);
@@ -572,17 +814,27 @@ static void fpga_mgr_dev_release(struct device *dev)
 {
        struct fpga_manager *mgr = to_fpga_manager(dev);
 
+       cdev_del(&mgr->cdev);
        ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
        kfree(mgr);
 }
 
 static int __init fpga_mgr_class_init(void)
 {
+       int err;
+
        pr_info("FPGA manager framework\n");
 
+       err = alloc_chrdev_region(&fpga_mgr_devt, 0, FPGA_MGR_MAX_DEV,
+                                 "fpga_mgr");
+       if (err)
+               return err;
+
        fpga_mgr_class = class_create(THIS_MODULE, "fpga_manager");
-       if (IS_ERR(fpga_mgr_class))
-               return PTR_ERR(fpga_mgr_class);
+       if (IS_ERR(fpga_mgr_class)) {
+               err = PTR_ERR(fpga_mgr_class);
+               goto err_class;
+       }
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
        fpga_mgr_class->dev_groups = fpga_mgr_groups;
 #else
@@ -591,12 +843,17 @@ static int __init fpga_mgr_class_init(void)
        fpga_mgr_class->dev_release = fpga_mgr_dev_release;
 
        return 0;
+
+err_class:
+       unregister_chrdev_region(fpga_mgr_devt, FPGA_MGR_MAX_DEV);
+       return err;
 }
 
 static void __exit fpga_mgr_class_exit(void)
 {
        class_destroy(fpga_mgr_class);
        ida_destroy(&fpga_mgr_ida);
+       unregister_chrdev_region(fpga_mgr_devt, FPGA_MGR_MAX_DEV);
 }
 
 MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index bfa14bc..ae38e48 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -15,8 +15,10 @@
  * You should have received a copy of the GNU General Public License along 
with
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
+#include <linux/cdev.h>
 #include <linux/mutex.h>
 #include <linux/platform_device.h>
+#include <linux/spinlock.h>
 
 #ifndef _LINUX_FPGA_MGR_H
 #define _LINUX_FPGA_MGR_H
@@ -24,6 +26,8 @@
 struct fpga_manager;
 struct sg_table;
 
+#define FPGA_MGR_MAX_DEV (256)
+
 /**
  * enum fpga_mgr_states - fpga framework states
  * @FPGA_MGR_STATE_UNKNOWN: can't determine state
@@ -118,22 +122,37 @@ struct fpga_manager_ops {
        void (*fpga_remove)(struct fpga_manager *mgr);
 };
 
+/*
+ * List of status FLAGS for the FPGA manager
+ */
+#define FPGA_MGR_FLAG_BITS (32)
+#define FPGA_MGR_FLAG_UNLOCK BIT(0)
+
 /**
  * struct fpga_manager - fpga manager structure
  * @name: name of low level fpga manager
+ * @cdev: char device interface
  * @dev: fpga manager device
  * @ref_mutex: only allows one reference to fpga manager
  * @state: state of fpga manager
  * @mops: pointer to struct of fpga manager ops
  * @priv: low level driver private date
+ * @flags: manager status bits
+ * @lock: it protects: flags
+ * @usr_mutex: only allows one user to program the FPGA
  */
 struct fpga_manager {
        const char *name;
+       struct cdev cdev;
        struct device dev;
        struct mutex ref_mutex;
        enum fpga_mgr_states state;
        const struct fpga_manager_ops *mops;
        void *priv;
+
+       DECLARE_BITMAP(flags, FPGA_MGR_FLAG_BITS);
+       struct spinlock lock;
+       struct mutex usr_mutex;
 };
 
 #define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)
Moritz Fischer Aug. 16, 2018, 10 p.m. UTC | #6
Hi Federico,

On Thu, Aug 16, 2018 at 11:21:32PM +0200, Federico Vaga wrote:
> Hi,
> 
> On Thursday, August 16, 2018 10:04:51 PM CEST Alan Tull wrote:
> > On Thu, Aug 16, 2018 at 1:59 PM, Moritz Fischer <mdf@kernel.org> wrote:
> > > Hi Alan,
> > 
> > Hi Moritz,
> > 
> > > comments inline. While I see how this is useful, I have the
> > > suspicion that from the moment this gets merged vendor kernels
> > > will just default to use this ...
> > 
> > Yeah, I have that suspicion as well.  That's probably why I sat on
> > this and didn't upstream it for 2 years.  But on the other hand, I
> > keep hearing of lots of cases of people implementing this
> > independently anyway.  At least if it is debugfs, it makes it clear
> > that it's not intended for production use.
> 
> I'm one of those guys who implemented this independently.

We all have in one way or another ;) Most people on ARM run an out of tree
patch using devicetree overlays these days I hope rather than /dev/mem
and UIO ... or other vender solutions...
> 
> @Mortiz
> I do not see how this can be a bad thing (from what you wrote I guess you 
> prefer another interface). Which interface to use depends on the use case.
> If you have this suspicion it's, I guess, because such interface it is 
> extremely easy to use.

What happens to a kernel driver doing MMIO with devices while you reload
the entire FPGA from userland?
> 
> @Alan
> DebugFS can be a first step, but I would go for a normal device in /dev at 
> some point. I do not see why this should not be used in production

I'm not against having a userland interface to reprogram the FPGA, the 
Intel DFL code is a good example of a sensible one, doing so in a safe
manner.

Ideally we'll get around to have a more generic interface, as we get
time to work on it.

- Moritz
Federico Vaga Aug. 17, 2018, 7 a.m. UTC | #7
Hi Mortiz,

I'm not 100% into the problem to understand all cases. I'm putting on the 
table the point of view, mainly, of an user. If you say there are problems 
here or there I believe you. At the beginning, you did not say that this 
interface may introduce problems (and I'm interested in those problems since I 
implemented one and we are using it), but that you fear that it becomes the 
default (usually, being a default is a good thing).

Since you and Alan are working on this for a long time, you can read each 
other mind, but I need a more verbose email to understand ^_^'

Of course the interface must be safe, I totally agree. In order to make me 
understand what are the issues, can you list some of them? And expand your 
comment about  MMIO.

(I just did a dummy backport, and implemented that interface. So, I repeat 
myself: I do not have enough experience with this framework to understand all 
consequences, but I'm interested to know what are the risks behind this 
interface)

On Friday, August 17, 2018 12:00:34 AM CEST Moritz Fischer wrote:
> Hi Federico,
> 
> On Thu, Aug 16, 2018 at 11:21:32PM +0200, Federico Vaga wrote:
> > Hi,
> > 
> > On Thursday, August 16, 2018 10:04:51 PM CEST Alan Tull wrote:
> > > On Thu, Aug 16, 2018 at 1:59 PM, Moritz Fischer <mdf@kernel.org> wrote:
> > > > Hi Alan,
> > > 
> > > Hi Moritz,
> > > 
> > > > comments inline. While I see how this is useful, I have the
> > > > suspicion that from the moment this gets merged vendor kernels
> > > > will just default to use this ...
> > > 
> > > Yeah, I have that suspicion as well.  That's probably why I sat on
> > > this and didn't upstream it for 2 years.  But on the other hand, I
> > > keep hearing of lots of cases of people implementing this
> > > independently anyway.  At least if it is debugfs, it makes it clear
> > > that it's not intended for production use.
> > 
> > I'm one of those guys who implemented this independently.
> 
> We all have in one way or another ;) Most people on ARM run an out of tree
> patch using devicetree overlays these days I hope rather than /dev/mem
> and UIO ... or other vender solutions...
> 
> > @Mortiz
> > I do not see how this can be a bad thing (from what you wrote I guess you
> > prefer another interface). Which interface to use depends on the use case.
> > If you have this suspicion it's, I guess, because such interface it is
> > extremely easy to use.
> 
> What happens to a kernel driver doing MMIO with devices while you reload
> the entire FPGA from userland?
> 
> > @Alan
> > DebugFS can be a first step, but I would go for a normal device in /dev at
> > some point. I do not see why this should not be used in production
> 
> I'm not against having a userland interface to reprogram the FPGA, the
> Intel DFL code is a good example of a sensible one, doing so in a safe
> manner.
>
> Ideally we'll get around to have a more generic interface, as we get
> time to work on it.
> 
> - Moritz
Federico Vaga Aug. 17, 2018, 2:54 p.m. UTC | #8
On Friday, August 17, 2018 3:19:24 PM CEST Alan Tull wrote:
> On Fri, Aug 17, 2018, 2:00 AM Federico Vaga <federico.vaga@cern.ch> wrote:
> > Hi Mortiz,
> > 
> > I'm not 100% into the problem to understand all cases. I'm putting on the
> > table the point of view, mainly, of an user. If you say there are problems
> > here or there I believe you. At the beginning, you did not say that this
> > interface may introduce problems (and I'm interested in those problems
> > since I
> > implemented one and we are using it), but that you fear that it becomes
> > the
> > default (usually, being a default is a good thing).
> > 
> > Since you and Alan are working on this for a long time, you can read each
> > other mind, but I need a more verbose email to understand ^_^'
> > 
> > Of course the interface must be safe, I totally agree. In order to make me
> > understand what are the issues, can you list some of them?
> 
> Before we repeat what the doc l posted says, could you look at it and
> comment on what I'm not saying there?
> 
> https://lkml.org/lkml/2018/8/15/525

I read it, but do you mean that the problems you foresee are only the ones 
listed in there? If this is so, comments:

loading devices
It is true that it is a problem, and probably it is clear to everyone who will 
try to use such interface: "and now how do I load my devices?". But this is 
only a possible case, the FPGA may run without device driver and in this case 
it is perfectly fine for production.

If the answer to the above question is: "ok, let me see where my devices are 
in the memory ..." well if the machine crashes, it's their problem. This 
problem exists even without the FPGA manager.

bridge
My understand is that it is optional. Developers are allowed to not implement 
the bridge's operations. Which probably means that it does not exist or it is 
not needed.
When an user uses this interface from userspace it shouldn't be hard to detect 
if the operation is risky or not (bridge enabled/disabled). And if it is 
risky, the operation fails with EPERM, EBUSY.

I have to say that I'm not familiar with the bridge design, perhaps I'm 
missing something.


Conclusions
Yes, those are problems but I think they do not justify the label "not for 
production": in some cases I think is fine.
Moritz Fischer Aug. 17, 2018, 3:22 p.m. UTC | #9
Hi Alan, Federico,

On Fri, Aug 17, 2018 at 6:19 AM, Alan Tull <atull@kernel.org> wrote:
> On Fri, Aug 17, 2018, 2:00 AM Federico Vaga <federico.vaga@cern.ch> wrote:
>>
>> Hi Mortiz,
>>
>> I'm not 100% into the problem to understand all cases. I'm putting on the
>> table the point of view, mainly, of an user. If you say there are problems
>> here or there I believe you. At the beginning, you did not say that this
>> interface may introduce problems (and I'm interested in those problems
>> since I
>> implemented one and we are using it), but that you fear that it becomes
>> the
>> default (usually, being a default is a good thing).
>>
>> Since you and Alan are working on this for a long time, you can read each
>> other mind, but I need a more verbose email to understand ^_^'
>>
>> Of course the interface must be safe, I totally agree. In order to make me
>> understand what are the issues, can you list some of them?

Say you have kernel drivers (a network driver in the FPGA, or an I2C controller)
for example bound to hardware on a MMIO bus in the the FPGA.
You reprogram the FPGA using the debugfs interface, and the drivers don't get
unloaded correctly, the driver will try to access the registers and depending
on your system / bus either give you bad values or lock up.
Now userland locked up your system. Bad.

I'm not saying it isn't possible to do this if you're careful, of
course you could
first unload the drivers using rrmod and it would work just fine.

I just feel an interface like this might make it easier to create the
wrong design.
I've seen plenty of Application notes from vendors where they literally did
"cat foo.bin > /dev/fpga" followed by mmap(/dev/mem...).

>
>
> Before we repeat what the doc l posted says, could you look at it and
> comment on what I'm not saying there?
>
> https://lkml.org/lkml/2018/8/15/525

Alan, maybe I didn't express myself well. I'm fine with the debugfs interface
as a debug interface, just not for general usage ;-) I think your document is
clear on that.

Thanks,

Moritz
Federico Vaga Aug. 17, 2018, 5:44 p.m. UTC | #10
Hi,

On Friday, August 17, 2018 5:22:56 PM CEST Moritz Fischer wrote:
> Hi Alan, Federico,
> 
> On Fri, Aug 17, 2018 at 6:19 AM, Alan Tull <atull@kernel.org> wrote:
> > On Fri, Aug 17, 2018, 2:00 AM Federico Vaga <federico.vaga@cern.ch> 
wrote:
> >> Hi Mortiz,
> >> 
> >> I'm not 100% into the problem to understand all cases. I'm putting on
> >> the table the point of view, mainly, of an user. If you say there are
> >> problems here or there I believe you. At the beginning, you did not
> >> say that this interface may introduce problems (and I'm interested in
> >> those problems since I
> >> implemented one and we are using it), but that you fear that it
> >> becomes
> >> the
> >> default (usually, being a default is a good thing).
> >> 
> >> Since you and Alan are working on this for a long time, you can read
> >> each other mind, but I need a more verbose email to understand ^_^'
> >> 
> >> Of course the interface must be safe, I totally agree. In order to
> >> make me understand what are the issues, can you list some of them?
> 
> Say you have kernel drivers (a network driver in the FPGA, or an I2C
> controller) for example bound to hardware on a MMIO bus in the the FPGA.
> You reprogram the FPGA using the debugfs interface, and the drivers don't
> get unloaded correctly, the driver will try to access the registers and
> depending on your system / bus either give you bad values or lock up.
> Now userland locked up your system. Bad.

I think I got confused by your reference to the MMIO, but now it sound like 
it was just a very specific example of a more general problem. Because this 
is true for any device driver for FPGA soft-IP/IP-core, it is not strictly 
an MMIO problem. Am I missing something?

I get the problem, I have to fight with **this** problem daily because I'm 
loading images with:

cat hello.bin > /dev/fpga0

And then, somehow I have to load the device drivers (memory, IRQ, ...). But 
I will not say publicly what I do (it is a "don't try this at home" thing).

> I'm not saying it isn't possible to do this if you're careful, of
> course you could
> first unload the drivers using rrmod and it would work just fine.

Or having some reference counter on the last loaded FPGA image may work. 
This way it will be possible to detect if there are users of the current 
FPGA and inhibit any unwanted FPGA load (like the module counter forbid 
rmmod when the device is in use). If a device driver is using some FPGA 
component the reference counter increase. How to do it? Need more studies, 
but probably this is a safe way that perhaps worth to look at.
 
> I just feel an interface like this might make it easier to create the
> wrong design.
> I've seen plenty of Application notes from vendors where they literally
> did "cat foo.bin > /dev/fpga" followed by mmap(/dev/mem...).

Actually, I'm doing worst than this (to compensate the lack of 
infrastructure). You tried, but you are not scaring me :P
 
> > Before we repeat what the doc l posted says, could you look at it and
> > comment on what I'm not saying there?
> > 
> > https://lkml.org/lkml/2018/8/15/525
> 
> Alan, maybe I didn't express myself well. I'm fine with the debugfs
> interface as a debug interface, just not for general usage ;-) I think
> your document is clear on that.
> 
> Thanks,
> 
> Moritz
Appana Durga Kedareswara Rao March 19, 2019, 10:28 a.m. UTC | #11
FYI...

Regards,
Kedar.

> -----Original Message-----
> From: Alan Tull <atull@kernel.org>
> Sent: Thursday, August 16, 2018 3:40 AM
> To: Moritz Fischer <mdf@kernel.org>; Jonathan Corbet <corbet@lwn.net>;
> Randy Dunlap <rdunlap@infradead.org>; Dinh Nguyen
> <dinguyen@kernel.org>
> Cc: Appana Durga Kedareswara Rao <appanad@xilinx.com>; linux-
> kernel@vger.kernel.org; linux-fpga@vger.kernel.org; linux-
> doc@vger.kernel.org; Alan Tull <atull@kernel.org>; Alan Tull
> <atull@opensource.altera.com>; Matthew Gerlach
> <matthew.gerlach@linux.intel.com>
> Subject: [PATCH 2/2] fpga: add FPGA manager debugfs
> 
> From: Alan Tull <atull@opensource.altera.com>
> 
> Implement DebugFS for the FPGA Manager Framework for debugging and
> developmental use.
> 
> Enabled by CONFIG_FPGA_MGR_DEBUG_FS
> 
> Each FPGA gets its own directory such as
>  <debugfs>/fpga_manager/fpga0 and three files:
> 
>  * [RW] flags          = flags as defined in fpga-mgr.h
>  * [RW] firmware_name  = write/read back name of FPGA image
>                          firmware file to program
>  * [WO] image          = write-only file for directly writing
>                          fpga image w/o firmware layer
>  * [RW] config_complete_timeout_us = maximum for the FPGA to
>                          go to the operating state after
> 			 programming
> 
> The debugfs is implemented in a separate fpga_mgr_debugfs.c file, but the
> FPGA manager core is still built as one module.  Note the name change from
> fpga-mgr.ko to fpga_mgr.ko.
> 
> Signed-off-by: Alan Tull <atull@kernel.org>
> Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
> ---
>  drivers/fpga/Kconfig            |   7 ++
>  drivers/fpga/Makefile           |   4 +-
>  drivers/fpga/fpga-mgr-debugfs.c | 221
> ++++++++++++++++++++++++++++++++++++++++
>  drivers/fpga/fpga-mgr-debugfs.h |  22 ++++
>  drivers/fpga/fpga-mgr.c         |   8 ++
>  include/linux/fpga/fpga-mgr.h   |   3 +
>  6 files changed, 264 insertions(+), 1 deletion(-)  create mode 100644
> drivers/fpga/fpga-mgr-debugfs.c  create mode 100644 drivers/fpga/fpga-mgr-
> debugfs.h
> 
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 1ebcef4..600924d
> 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -9,6 +9,13 @@ menuconfig FPGA
>  	  kernel.  The FPGA framework adds a FPGA manager class and FPGA
>  	  manager drivers.
> 
> +config FPGA_MGR_DEBUG_FS
> +       bool "FPGA Manager DebugFS"
> +       depends on FPGA && DEBUG_FS
> +       help
> +         Say Y here if you want to expose a DebugFS interface for the
> +	 FPGA Manager Framework.
> +
>  if FPGA
> 
>  config FPGA_MGR_SOCFPGA
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index
> 7a2d73b..62910cc 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -4,7 +4,9 @@
>  #
> 
>  # Core FPGA Manager Framework
> -obj-$(CONFIG_FPGA)			+= fpga-mgr.o
> +obj-$(CONFIG_FPGA)			+= fpga_mgr.o
> +fpga_mgr-y				:= fpga-mgr.o
> +fpga_mgr-$(CONFIG_FPGA_MGR_DEBUG_FS)	+= fpga-mgr-debugfs.o
> 
>  # FPGA Manager Drivers
>  obj-$(CONFIG_FPGA_MGR_ALTERA_CVP)	+= altera-cvp.o
> diff --git a/drivers/fpga/fpga-mgr-debugfs.c b/drivers/fpga/fpga-mgr-debugfs.c
> new file mode 100644 index 0000000..f2fdf58
> --- /dev/null
> +++ b/drivers/fpga/fpga-mgr-debugfs.c
> @@ -0,0 +1,221 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * FPGA Manager DebugFS
> + *
> + *  Copyright (C) 2016-2018 Intel Corporation.  All rights reserved.
> + */
> +#include <linux/debugfs.h>
> +#include <linux/fpga/fpga-mgr.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +
> +static struct dentry *fpga_mgr_debugfs_root;
> +
> +struct fpga_mgr_debugfs {
> +	struct dentry *debugfs_dir;
> +	struct fpga_image_info *info;
> +};
> +
> +static ssize_t fpga_mgr_firmware_write_file(struct file *file,
> +					    const char __user *user_buf,
> +					    size_t count, loff_t *ppos)
> +{
> +	struct fpga_manager *mgr = file->private_data;
> +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> +	struct device *dev = &mgr->dev;
> +	char *buf;
> +	int ret;
> +
> +	ret = fpga_mgr_lock(mgr);
> +	if (ret) {
> +		dev_err(dev, "FPGA manager is busy\n");
> +		return -EBUSY;
> +	}
> +
> +	buf = devm_kzalloc(dev, count, GFP_KERNEL);
> +	if (!buf) {
> +		fpga_mgr_unlock(mgr);
> +		return -ENOMEM;
> +	}
> +
> +	if (copy_from_user(buf, user_buf, count)) {
> +		fpga_mgr_unlock(mgr);
> +		devm_kfree(dev, buf);
> +		return -EFAULT;
> +	}
> +
> +	buf[count] = 0;
> +	if (buf[count - 1] == '\n')
> +		buf[count - 1] = 0;
> +
> +	/* Release previous firmware name (if any). Save current one. */
> +	if (debugfs->info->firmware_name)
> +		devm_kfree(dev, debugfs->info->firmware_name);
> +	debugfs->info->firmware_name = buf;
> +
> +	ret = fpga_mgr_load(mgr, debugfs->info);
> +	if (ret)
> +		dev_err(dev, "fpga_mgr_load returned with value %d\n", ret);
> +
> +	fpga_mgr_unlock(mgr);
> +
> +	return count;
> +}
> +
> +static ssize_t fpga_mgr_firmware_read_file(struct file *file,
> +					   char __user *user_buf,
> +					   size_t count, loff_t *ppos)
> +{
> +	struct fpga_manager *mgr = file->private_data;
> +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> +	char *buf;
> +	int ret;
> +
> +	if (!debugfs->info->firmware_name)
> +		return 0;
> +
> +	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	ret = snprintf(buf, PAGE_SIZE, "%s\n", debugfs->info-
> >firmware_name);
> +	if (ret < 0) {
> +		kfree(buf);
> +		return ret;
> +	}
> +
> +	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
> +	kfree(buf);
> +
> +	return ret;
> +}
> +
> +static const struct file_operations fpga_mgr_firmware_fops = {
> +	.open = simple_open,
> +	.read = fpga_mgr_firmware_read_file,
> +	.write = fpga_mgr_firmware_write_file,
> +	.llseek = default_llseek,
> +};
> +
> +static ssize_t fpga_mgr_image_write_file(struct file *file,
> +					 const char __user *user_buf,
> +					 size_t count, loff_t *ppos)
> +{
> +	struct fpga_manager *mgr = file->private_data;
> +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> +	struct device *dev = &mgr->dev;
> +	char *buf;
> +	int ret;
> +
> +	dev_info(dev, "writing %zu bytes to %s\n", count, mgr->name);
> +
> +	ret = fpga_mgr_lock(mgr);
> +	if (ret) {
> +		dev_err(dev, "FPGA manager is busy\n");
> +		return -EBUSY;
> +	}
> +
> +	buf = kzalloc(count, GFP_KERNEL);
> +	if (!buf) {
> +		fpga_mgr_unlock(mgr);
> +		return -ENOMEM;
> +	}
> +
> +	if (copy_from_user(buf, user_buf, count)) {
> +		fpga_mgr_unlock(mgr);
> +		kfree(buf);
> +		return -EFAULT;
> +	}
> +
> +	/* If firmware interface was previously used, forget it. */
> +	if (debugfs->info->firmware_name)
> +		devm_kfree(dev, debugfs->info->firmware_name);
> +	debugfs->info->firmware_name = NULL;
> +
> +	debugfs->info->buf = buf;
> +	debugfs->info->count = count;
> +
> +	ret = fpga_mgr_load(mgr, debugfs->info);
> +	if (ret)
> +		dev_err(dev, "fpga_mgr_load returned with value %d\n", ret);
> +
> +	fpga_mgr_unlock(mgr);
> +
> +	debugfs->info->buf = NULL;
> +	debugfs->info->count = 0;
> +
> +	kfree(buf);
> +
> +	return count;
> +}
> +
> +static const struct file_operations fpga_mgr_image_fops = {
> +	.open = simple_open,
> +	.write = fpga_mgr_image_write_file,
> +	.llseek = default_llseek,
> +};
> +
> +void fpga_mgr_debugfs_add(struct fpga_manager *mgr) {
> +	struct device *dev = &mgr->dev;
> +	struct fpga_mgr_debugfs *debugfs;
> +	struct fpga_image_info *info;
> +
> +	if (!fpga_mgr_debugfs_root)
> +		return;
> +
> +	debugfs = kzalloc(sizeof(*debugfs), GFP_KERNEL);
> +	if (!debugfs)
> +		return;
> +
> +	info = fpga_image_info_alloc(dev);
> +	if (!info) {
> +		kfree(debugfs);
> +		return;
> +	}
> +	debugfs->info = info;
> +
> +	debugfs->debugfs_dir = debugfs_create_dir(dev_name(dev),
> +						  fpga_mgr_debugfs_root);
> +
> +	debugfs_create_file("firmware_name", 0600, debugfs->debugfs_dir,
> mgr,
> +			    &fpga_mgr_firmware_fops);
> +
> +	debugfs_create_file("image", 0200, debugfs->debugfs_dir, mgr,
> +			    &fpga_mgr_image_fops);
> +
> +	debugfs_create_u32("flags", 0600, debugfs->debugfs_dir, &info-
> >flags);
> +
> +	debugfs_create_u32("config_complete_timeout_us", 0600,
> +			   debugfs->debugfs_dir,
> +			   &info->config_complete_timeout_us);
> +
> +	mgr->debugfs = debugfs;
> +}
> +
> +void fpga_mgr_debugfs_remove(struct fpga_manager *mgr) {
> +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> +
> +	if (!fpga_mgr_debugfs_root)
> +		return;
> +
> +	debugfs_remove_recursive(debugfs->debugfs_dir);
> +
> +	/* this function also frees debugfs->info->firmware_name */
> +	fpga_image_info_free(debugfs->info);
> +
> +	kfree(debugfs);
> +}
> +
> +void fpga_mgr_debugfs_init(void)
> +{
> +	fpga_mgr_debugfs_root = debugfs_create_dir("fpga_manager", NULL);
> +	if (!fpga_mgr_debugfs_root)
> +		pr_warn("fpga_mgr: Failed to create debugfs root\n"); }
> +
> +void fpga_mgr_debugfs_uninit(void)
> +{
> +	debugfs_remove_recursive(fpga_mgr_debugfs_root);
> +}
> diff --git a/drivers/fpga/fpga-mgr-debugfs.h b/drivers/fpga/fpga-mgr-debugfs.h
> new file mode 100644 index 0000000..17cd3eb
> --- /dev/null
> +++ b/drivers/fpga/fpga-mgr-debugfs.h
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#ifndef _LINUX_FPGA_MGR_DEBUGFS_H
> +#define _LINUX_FPGA_MGR_DEBUGFS_H
> +
> +#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
> +
> +void fpga_mgr_debugfs_add(struct fpga_manager *mgr); void
> +fpga_mgr_debugfs_remove(struct fpga_manager *mgr); void
> +fpga_mgr_debugfs_init(void); void fpga_mgr_debugfs_uninit(void);
> +
> +#else
> +
> +void fpga_mgr_debugfs_add(struct fpga_manager *mgr) {} void
> +fpga_mgr_debugfs_remove(struct fpga_manager *mgr) {} void
> +fpga_mgr_debugfs_init(void) {} void fpga_mgr_debugfs_uninit(void) {}
> +
> +#endif /* CONFIG_FPGA_MGR_DEBUG_FS */
> +
> +#endif /*_LINUX_FPGA_MGR_DEBUGFS_H */
> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c index
> c8684bc..66eb6f6 100644
> --- a/drivers/fpga/fpga-mgr.c
> +++ b/drivers/fpga/fpga-mgr.c
> @@ -17,6 +17,7 @@
>  #include <linux/slab.h>
>  #include <linux/scatterlist.h>
>  #include <linux/highmem.h>
> +#include "fpga-mgr-debugfs.h"
> 
>  static DEFINE_IDA(fpga_mgr_ida);
>  static struct class *fpga_mgr_class;
> @@ -698,6 +699,8 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>  	if (ret)
>  		goto error_device;
> 
> +	fpga_mgr_debugfs_add(mgr);
> +
>  	dev_info(&mgr->dev, "%s registered\n", mgr->name);
> 
>  	return 0;
> @@ -722,6 +725,8 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)  {
>  	dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
> 
> +	fpga_mgr_debugfs_remove(mgr);
> +
>  	/*
>  	 * If the low level driver provides a method for putting fpga into
>  	 * a desired state upon unregister, do it.
> @@ -748,11 +753,14 @@ static int __init fpga_mgr_class_init(void)
>  	fpga_mgr_class->dev_groups = fpga_mgr_groups;
>  	fpga_mgr_class->dev_release = fpga_mgr_dev_release;
> 
> +	fpga_mgr_debugfs_init();
> +
>  	return 0;
>  }
> 
>  static void __exit fpga_mgr_class_exit(void)  {
> +	fpga_mgr_debugfs_uninit();
>  	class_destroy(fpga_mgr_class);
>  	ida_destroy(&fpga_mgr_ida);
>  }
> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
> index 777c502..e8f2159 100644
> --- a/include/linux/fpga/fpga-mgr.h
> +++ b/include/linux/fpga/fpga-mgr.h
> @@ -170,6 +170,9 @@ struct fpga_manager {
>  	struct fpga_compat_id *compat_id;
>  	const struct fpga_manager_ops *mops;
>  	void *priv;
> +#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
> +	void *debugfs;
> +#endif
>  };
> 
>  #define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)
> --
> 2.7.4
Appana Durga Kedareswara Rao March 19, 2019, 10:32 a.m. UTC | #12
Please ignore this mail....

Regards,
Kedar.

> -----Original Message-----
> From: Appana Durga Kedareswara Rao
> Sent: Tuesday, March 19, 2019 3:58 PM
> To: 'Alan Tull' <atull@kernel.org>; Moritz Fischer <mdf@kernel.org>; Jonathan
> Corbet <corbet@lwn.net>; Randy Dunlap <rdunlap@infradead.org>; Dinh
> Nguyen <dinguyen@kernel.org>
> Cc: linux-kernel@vger.kernel.org; linux-fpga@vger.kernel.org; linux-
> doc@vger.kernel.org; Alan Tull <atull@opensource.altera.com>; Matthew
> Gerlach <matthew.gerlach@linux.intel.com>
> Subject: RE: [PATCH 2/2] fpga: add FPGA manager debugfs
> 
> FYI...
> 
> Regards,
> Kedar.
> 
> > -----Original Message-----
> > From: Alan Tull <atull@kernel.org>
> > Sent: Thursday, August 16, 2018 3:40 AM
> > To: Moritz Fischer <mdf@kernel.org>; Jonathan Corbet <corbet@lwn.net>;
> > Randy Dunlap <rdunlap@infradead.org>; Dinh Nguyen
> > <dinguyen@kernel.org>
> > Cc: Appana Durga Kedareswara Rao <appanad@xilinx.com>; linux-
> > kernel@vger.kernel.org; linux-fpga@vger.kernel.org; linux-
> > doc@vger.kernel.org; Alan Tull <atull@kernel.org>; Alan Tull
> > <atull@opensource.altera.com>; Matthew Gerlach
> > <matthew.gerlach@linux.intel.com>
> > Subject: [PATCH 2/2] fpga: add FPGA manager debugfs
> >
> > From: Alan Tull <atull@opensource.altera.com>
> >
> > Implement DebugFS for the FPGA Manager Framework for debugging and
> > developmental use.
> >
> > Enabled by CONFIG_FPGA_MGR_DEBUG_FS
> >
> > Each FPGA gets its own directory such as
> >  <debugfs>/fpga_manager/fpga0 and three files:
> >
> >  * [RW] flags          = flags as defined in fpga-mgr.h
> >  * [RW] firmware_name  = write/read back name of FPGA image
> >                          firmware file to program
> >  * [WO] image          = write-only file for directly writing
> >                          fpga image w/o firmware layer
> >  * [RW] config_complete_timeout_us = maximum for the FPGA to
> >                          go to the operating state after
> > 			 programming
> >
> > The debugfs is implemented in a separate fpga_mgr_debugfs.c file, but
> > the FPGA manager core is still built as one module.  Note the name
> > change from fpga-mgr.ko to fpga_mgr.ko.
> >
> > Signed-off-by: Alan Tull <atull@kernel.org>
> > Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
> > ---
> >  drivers/fpga/Kconfig            |   7 ++
> >  drivers/fpga/Makefile           |   4 +-
> >  drivers/fpga/fpga-mgr-debugfs.c | 221
> > ++++++++++++++++++++++++++++++++++++++++
> >  drivers/fpga/fpga-mgr-debugfs.h |  22 ++++
> >  drivers/fpga/fpga-mgr.c         |   8 ++
> >  include/linux/fpga/fpga-mgr.h   |   3 +
> >  6 files changed, 264 insertions(+), 1 deletion(-)  create mode 100644
> > drivers/fpga/fpga-mgr-debugfs.c  create mode 100644
> > drivers/fpga/fpga-mgr- debugfs.h
> >
> > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index
> > 1ebcef4..600924d
> > 100644
> > --- a/drivers/fpga/Kconfig
> > +++ b/drivers/fpga/Kconfig
> > @@ -9,6 +9,13 @@ menuconfig FPGA
> >  	  kernel.  The FPGA framework adds a FPGA manager class and FPGA
> >  	  manager drivers.
> >
> > +config FPGA_MGR_DEBUG_FS
> > +       bool "FPGA Manager DebugFS"
> > +       depends on FPGA && DEBUG_FS
> > +       help
> > +         Say Y here if you want to expose a DebugFS interface for the
> > +	 FPGA Manager Framework.
> > +
> >  if FPGA
> >
> >  config FPGA_MGR_SOCFPGA
> > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index
> > 7a2d73b..62910cc 100644
> > --- a/drivers/fpga/Makefile
> > +++ b/drivers/fpga/Makefile
> > @@ -4,7 +4,9 @@
> >  #
> >
> >  # Core FPGA Manager Framework
> > -obj-$(CONFIG_FPGA)			+= fpga-mgr.o
> > +obj-$(CONFIG_FPGA)			+= fpga_mgr.o
> > +fpga_mgr-y				:= fpga-mgr.o
> > +fpga_mgr-$(CONFIG_FPGA_MGR_DEBUG_FS)	+= fpga-mgr-debugfs.o
> >
> >  # FPGA Manager Drivers
> >  obj-$(CONFIG_FPGA_MGR_ALTERA_CVP)	+= altera-cvp.o
> > diff --git a/drivers/fpga/fpga-mgr-debugfs.c
> > b/drivers/fpga/fpga-mgr-debugfs.c new file mode 100644 index
> > 0000000..f2fdf58
> > --- /dev/null
> > +++ b/drivers/fpga/fpga-mgr-debugfs.c
> > @@ -0,0 +1,221 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * FPGA Manager DebugFS
> > + *
> > + *  Copyright (C) 2016-2018 Intel Corporation.  All rights reserved.
> > + */
> > +#include <linux/debugfs.h>
> > +#include <linux/fpga/fpga-mgr.h>
> > +#include <linux/slab.h>
> > +#include <linux/uaccess.h>
> > +
> > +static struct dentry *fpga_mgr_debugfs_root;
> > +
> > +struct fpga_mgr_debugfs {
> > +	struct dentry *debugfs_dir;
> > +	struct fpga_image_info *info;
> > +};
> > +
> > +static ssize_t fpga_mgr_firmware_write_file(struct file *file,
> > +					    const char __user *user_buf,
> > +					    size_t count, loff_t *ppos)
> > +{
> > +	struct fpga_manager *mgr = file->private_data;
> > +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> > +	struct device *dev = &mgr->dev;
> > +	char *buf;
> > +	int ret;
> > +
> > +	ret = fpga_mgr_lock(mgr);
> > +	if (ret) {
> > +		dev_err(dev, "FPGA manager is busy\n");
> > +		return -EBUSY;
> > +	}
> > +
> > +	buf = devm_kzalloc(dev, count, GFP_KERNEL);
> > +	if (!buf) {
> > +		fpga_mgr_unlock(mgr);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	if (copy_from_user(buf, user_buf, count)) {
> > +		fpga_mgr_unlock(mgr);
> > +		devm_kfree(dev, buf);
> > +		return -EFAULT;
> > +	}
> > +
> > +	buf[count] = 0;
> > +	if (buf[count - 1] == '\n')
> > +		buf[count - 1] = 0;
> > +
> > +	/* Release previous firmware name (if any). Save current one. */
> > +	if (debugfs->info->firmware_name)
> > +		devm_kfree(dev, debugfs->info->firmware_name);
> > +	debugfs->info->firmware_name = buf;
> > +
> > +	ret = fpga_mgr_load(mgr, debugfs->info);
> > +	if (ret)
> > +		dev_err(dev, "fpga_mgr_load returned with value %d\n", ret);
> > +
> > +	fpga_mgr_unlock(mgr);
> > +
> > +	return count;
> > +}
> > +
> > +static ssize_t fpga_mgr_firmware_read_file(struct file *file,
> > +					   char __user *user_buf,
> > +					   size_t count, loff_t *ppos)
> > +{
> > +	struct fpga_manager *mgr = file->private_data;
> > +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> > +	char *buf;
> > +	int ret;
> > +
> > +	if (!debugfs->info->firmware_name)
> > +		return 0;
> > +
> > +	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
> > +	if (!buf)
> > +		return -ENOMEM;
> > +
> > +	ret = snprintf(buf, PAGE_SIZE, "%s\n", debugfs->info-
> > >firmware_name);
> > +	if (ret < 0) {
> > +		kfree(buf);
> > +		return ret;
> > +	}
> > +
> > +	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
> > +	kfree(buf);
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct file_operations fpga_mgr_firmware_fops = {
> > +	.open = simple_open,
> > +	.read = fpga_mgr_firmware_read_file,
> > +	.write = fpga_mgr_firmware_write_file,
> > +	.llseek = default_llseek,
> > +};
> > +
> > +static ssize_t fpga_mgr_image_write_file(struct file *file,
> > +					 const char __user *user_buf,
> > +					 size_t count, loff_t *ppos)
> > +{
> > +	struct fpga_manager *mgr = file->private_data;
> > +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> > +	struct device *dev = &mgr->dev;
> > +	char *buf;
> > +	int ret;
> > +
> > +	dev_info(dev, "writing %zu bytes to %s\n", count, mgr->name);
> > +
> > +	ret = fpga_mgr_lock(mgr);
> > +	if (ret) {
> > +		dev_err(dev, "FPGA manager is busy\n");
> > +		return -EBUSY;
> > +	}
> > +
> > +	buf = kzalloc(count, GFP_KERNEL);
> > +	if (!buf) {
> > +		fpga_mgr_unlock(mgr);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	if (copy_from_user(buf, user_buf, count)) {
> > +		fpga_mgr_unlock(mgr);
> > +		kfree(buf);
> > +		return -EFAULT;
> > +	}
> > +
> > +	/* If firmware interface was previously used, forget it. */
> > +	if (debugfs->info->firmware_name)
> > +		devm_kfree(dev, debugfs->info->firmware_name);
> > +	debugfs->info->firmware_name = NULL;
> > +
> > +	debugfs->info->buf = buf;
> > +	debugfs->info->count = count;
> > +
> > +	ret = fpga_mgr_load(mgr, debugfs->info);
> > +	if (ret)
> > +		dev_err(dev, "fpga_mgr_load returned with value %d\n", ret);
> > +
> > +	fpga_mgr_unlock(mgr);
> > +
> > +	debugfs->info->buf = NULL;
> > +	debugfs->info->count = 0;
> > +
> > +	kfree(buf);
> > +
> > +	return count;
> > +}
> > +
> > +static const struct file_operations fpga_mgr_image_fops = {
> > +	.open = simple_open,
> > +	.write = fpga_mgr_image_write_file,
> > +	.llseek = default_llseek,
> > +};
> > +
> > +void fpga_mgr_debugfs_add(struct fpga_manager *mgr) {
> > +	struct device *dev = &mgr->dev;
> > +	struct fpga_mgr_debugfs *debugfs;
> > +	struct fpga_image_info *info;
> > +
> > +	if (!fpga_mgr_debugfs_root)
> > +		return;
> > +
> > +	debugfs = kzalloc(sizeof(*debugfs), GFP_KERNEL);
> > +	if (!debugfs)
> > +		return;
> > +
> > +	info = fpga_image_info_alloc(dev);
> > +	if (!info) {
> > +		kfree(debugfs);
> > +		return;
> > +	}
> > +	debugfs->info = info;
> > +
> > +	debugfs->debugfs_dir = debugfs_create_dir(dev_name(dev),
> > +						  fpga_mgr_debugfs_root);
> > +
> > +	debugfs_create_file("firmware_name", 0600, debugfs->debugfs_dir,
> > mgr,
> > +			    &fpga_mgr_firmware_fops);
> > +
> > +	debugfs_create_file("image", 0200, debugfs->debugfs_dir, mgr,
> > +			    &fpga_mgr_image_fops);
> > +
> > +	debugfs_create_u32("flags", 0600, debugfs->debugfs_dir, &info-
> > >flags);
> > +
> > +	debugfs_create_u32("config_complete_timeout_us", 0600,
> > +			   debugfs->debugfs_dir,
> > +			   &info->config_complete_timeout_us);
> > +
> > +	mgr->debugfs = debugfs;
> > +}
> > +
> > +void fpga_mgr_debugfs_remove(struct fpga_manager *mgr) {
> > +	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
> > +
> > +	if (!fpga_mgr_debugfs_root)
> > +		return;
> > +
> > +	debugfs_remove_recursive(debugfs->debugfs_dir);
> > +
> > +	/* this function also frees debugfs->info->firmware_name */
> > +	fpga_image_info_free(debugfs->info);
> > +
> > +	kfree(debugfs);
> > +}
> > +
> > +void fpga_mgr_debugfs_init(void)
> > +{
> > +	fpga_mgr_debugfs_root = debugfs_create_dir("fpga_manager", NULL);
> > +	if (!fpga_mgr_debugfs_root)
> > +		pr_warn("fpga_mgr: Failed to create debugfs root\n"); }
> > +
> > +void fpga_mgr_debugfs_uninit(void)
> > +{
> > +	debugfs_remove_recursive(fpga_mgr_debugfs_root);
> > +}
> > diff --git a/drivers/fpga/fpga-mgr-debugfs.h
> > b/drivers/fpga/fpga-mgr-debugfs.h new file mode 100644 index
> > 0000000..17cd3eb
> > --- /dev/null
> > +++ b/drivers/fpga/fpga-mgr-debugfs.h
> > @@ -0,0 +1,22 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#ifndef _LINUX_FPGA_MGR_DEBUGFS_H
> > +#define _LINUX_FPGA_MGR_DEBUGFS_H
> > +
> > +#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
> > +
> > +void fpga_mgr_debugfs_add(struct fpga_manager *mgr); void
> > +fpga_mgr_debugfs_remove(struct fpga_manager *mgr); void
> > +fpga_mgr_debugfs_init(void); void fpga_mgr_debugfs_uninit(void);
> > +
> > +#else
> > +
> > +void fpga_mgr_debugfs_add(struct fpga_manager *mgr) {} void
> > +fpga_mgr_debugfs_remove(struct fpga_manager *mgr) {} void
> > +fpga_mgr_debugfs_init(void) {} void fpga_mgr_debugfs_uninit(void) {}
> > +
> > +#endif /* CONFIG_FPGA_MGR_DEBUG_FS */
> > +
> > +#endif /*_LINUX_FPGA_MGR_DEBUGFS_H */
> > diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c index
> > c8684bc..66eb6f6 100644
> > --- a/drivers/fpga/fpga-mgr.c
> > +++ b/drivers/fpga/fpga-mgr.c
> > @@ -17,6 +17,7 @@
> >  #include <linux/slab.h>
> >  #include <linux/scatterlist.h>
> >  #include <linux/highmem.h>
> > +#include "fpga-mgr-debugfs.h"
> >
> >  static DEFINE_IDA(fpga_mgr_ida);
> >  static struct class *fpga_mgr_class;
> > @@ -698,6 +699,8 @@ int fpga_mgr_register(struct fpga_manager *mgr)
> >  	if (ret)
> >  		goto error_device;
> >
> > +	fpga_mgr_debugfs_add(mgr);
> > +
> >  	dev_info(&mgr->dev, "%s registered\n", mgr->name);
> >
> >  	return 0;
> > @@ -722,6 +725,8 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
> {
> >  	dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
> >
> > +	fpga_mgr_debugfs_remove(mgr);
> > +
> >  	/*
> >  	 * If the low level driver provides a method for putting fpga into
> >  	 * a desired state upon unregister, do it.
> > @@ -748,11 +753,14 @@ static int __init fpga_mgr_class_init(void)
> >  	fpga_mgr_class->dev_groups = fpga_mgr_groups;
> >  	fpga_mgr_class->dev_release = fpga_mgr_dev_release;
> >
> > +	fpga_mgr_debugfs_init();
> > +
> >  	return 0;
> >  }
> >
> >  static void __exit fpga_mgr_class_exit(void)  {
> > +	fpga_mgr_debugfs_uninit();
> >  	class_destroy(fpga_mgr_class);
> >  	ida_destroy(&fpga_mgr_ida);
> >  }
> > diff --git a/include/linux/fpga/fpga-mgr.h
> > b/include/linux/fpga/fpga-mgr.h index 777c502..e8f2159 100644
> > --- a/include/linux/fpga/fpga-mgr.h
> > +++ b/include/linux/fpga/fpga-mgr.h
> > @@ -170,6 +170,9 @@ struct fpga_manager {
> >  	struct fpga_compat_id *compat_id;
> >  	const struct fpga_manager_ops *mops;
> >  	void *priv;
> > +#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
> > +	void *debugfs;
> > +#endif
> >  };
> >
> >  #define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)
> > --
> > 2.7.4

Patch
diff mbox series

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 1ebcef4..600924d 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -9,6 +9,13 @@  menuconfig FPGA
 	  kernel.  The FPGA framework adds a FPGA manager class and FPGA
 	  manager drivers.
 
+config FPGA_MGR_DEBUG_FS
+       bool "FPGA Manager DebugFS"
+       depends on FPGA && DEBUG_FS
+       help
+         Say Y here if you want to expose a DebugFS interface for the
+	 FPGA Manager Framework.
+
 if FPGA
 
 config FPGA_MGR_SOCFPGA
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 7a2d73b..62910cc 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -4,7 +4,9 @@ 
 #
 
 # Core FPGA Manager Framework
-obj-$(CONFIG_FPGA)			+= fpga-mgr.o
+obj-$(CONFIG_FPGA)			+= fpga_mgr.o
+fpga_mgr-y				:= fpga-mgr.o
+fpga_mgr-$(CONFIG_FPGA_MGR_DEBUG_FS)	+= fpga-mgr-debugfs.o
 
 # FPGA Manager Drivers
 obj-$(CONFIG_FPGA_MGR_ALTERA_CVP)	+= altera-cvp.o
diff --git a/drivers/fpga/fpga-mgr-debugfs.c b/drivers/fpga/fpga-mgr-debugfs.c
new file mode 100644
index 0000000..f2fdf58
--- /dev/null
+++ b/drivers/fpga/fpga-mgr-debugfs.c
@@ -0,0 +1,221 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FPGA Manager DebugFS
+ *
+ *  Copyright (C) 2016-2018 Intel Corporation.  All rights reserved.
+ */
+#include <linux/debugfs.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+static struct dentry *fpga_mgr_debugfs_root;
+
+struct fpga_mgr_debugfs {
+	struct dentry *debugfs_dir;
+	struct fpga_image_info *info;
+};
+
+static ssize_t fpga_mgr_firmware_write_file(struct file *file,
+					    const char __user *user_buf,
+					    size_t count, loff_t *ppos)
+{
+	struct fpga_manager *mgr = file->private_data;
+	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
+	struct device *dev = &mgr->dev;
+	char *buf;
+	int ret;
+
+	ret = fpga_mgr_lock(mgr);
+	if (ret) {
+		dev_err(dev, "FPGA manager is busy\n");
+		return -EBUSY;
+	}
+
+	buf = devm_kzalloc(dev, count, GFP_KERNEL);
+	if (!buf) {
+		fpga_mgr_unlock(mgr);
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(buf, user_buf, count)) {
+		fpga_mgr_unlock(mgr);
+		devm_kfree(dev, buf);
+		return -EFAULT;
+	}
+
+	buf[count] = 0;
+	if (buf[count - 1] == '\n')
+		buf[count - 1] = 0;
+
+	/* Release previous firmware name (if any). Save current one. */
+	if (debugfs->info->firmware_name)
+		devm_kfree(dev, debugfs->info->firmware_name);
+	debugfs->info->firmware_name = buf;
+
+	ret = fpga_mgr_load(mgr, debugfs->info);
+	if (ret)
+		dev_err(dev, "fpga_mgr_load returned with value %d\n", ret);
+
+	fpga_mgr_unlock(mgr);
+
+	return count;
+}
+
+static ssize_t fpga_mgr_firmware_read_file(struct file *file,
+					   char __user *user_buf,
+					   size_t count, loff_t *ppos)
+{
+	struct fpga_manager *mgr = file->private_data;
+	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
+	char *buf;
+	int ret;
+
+	if (!debugfs->info->firmware_name)
+		return 0;
+
+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s\n", debugfs->info->firmware_name);
+	if (ret < 0) {
+		kfree(buf);
+		return ret;
+	}
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+	kfree(buf);
+
+	return ret;
+}
+
+static const struct file_operations fpga_mgr_firmware_fops = {
+	.open = simple_open,
+	.read = fpga_mgr_firmware_read_file,
+	.write = fpga_mgr_firmware_write_file,
+	.llseek = default_llseek,
+};
+
+static ssize_t fpga_mgr_image_write_file(struct file *file,
+					 const char __user *user_buf,
+					 size_t count, loff_t *ppos)
+{
+	struct fpga_manager *mgr = file->private_data;
+	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
+	struct device *dev = &mgr->dev;
+	char *buf;
+	int ret;
+
+	dev_info(dev, "writing %zu bytes to %s\n", count, mgr->name);
+
+	ret = fpga_mgr_lock(mgr);
+	if (ret) {
+		dev_err(dev, "FPGA manager is busy\n");
+		return -EBUSY;
+	}
+
+	buf = kzalloc(count, GFP_KERNEL);
+	if (!buf) {
+		fpga_mgr_unlock(mgr);
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(buf, user_buf, count)) {
+		fpga_mgr_unlock(mgr);
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	/* If firmware interface was previously used, forget it. */
+	if (debugfs->info->firmware_name)
+		devm_kfree(dev, debugfs->info->firmware_name);
+	debugfs->info->firmware_name = NULL;
+
+	debugfs->info->buf = buf;
+	debugfs->info->count = count;
+
+	ret = fpga_mgr_load(mgr, debugfs->info);
+	if (ret)
+		dev_err(dev, "fpga_mgr_load returned with value %d\n", ret);
+
+	fpga_mgr_unlock(mgr);
+
+	debugfs->info->buf = NULL;
+	debugfs->info->count = 0;
+
+	kfree(buf);
+
+	return count;
+}
+
+static const struct file_operations fpga_mgr_image_fops = {
+	.open = simple_open,
+	.write = fpga_mgr_image_write_file,
+	.llseek = default_llseek,
+};
+
+void fpga_mgr_debugfs_add(struct fpga_manager *mgr)
+{
+	struct device *dev = &mgr->dev;
+	struct fpga_mgr_debugfs *debugfs;
+	struct fpga_image_info *info;
+
+	if (!fpga_mgr_debugfs_root)
+		return;
+
+	debugfs = kzalloc(sizeof(*debugfs), GFP_KERNEL);
+	if (!debugfs)
+		return;
+
+	info = fpga_image_info_alloc(dev);
+	if (!info) {
+		kfree(debugfs);
+		return;
+	}
+	debugfs->info = info;
+
+	debugfs->debugfs_dir = debugfs_create_dir(dev_name(dev),
+						  fpga_mgr_debugfs_root);
+
+	debugfs_create_file("firmware_name", 0600, debugfs->debugfs_dir, mgr,
+			    &fpga_mgr_firmware_fops);
+
+	debugfs_create_file("image", 0200, debugfs->debugfs_dir, mgr,
+			    &fpga_mgr_image_fops);
+
+	debugfs_create_u32("flags", 0600, debugfs->debugfs_dir, &info->flags);
+
+	debugfs_create_u32("config_complete_timeout_us", 0600,
+			   debugfs->debugfs_dir,
+			   &info->config_complete_timeout_us);
+
+	mgr->debugfs = debugfs;
+}
+
+void fpga_mgr_debugfs_remove(struct fpga_manager *mgr)
+{
+	struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
+
+	if (!fpga_mgr_debugfs_root)
+		return;
+
+	debugfs_remove_recursive(debugfs->debugfs_dir);
+
+	/* this function also frees debugfs->info->firmware_name */
+	fpga_image_info_free(debugfs->info);
+
+	kfree(debugfs);
+}
+
+void fpga_mgr_debugfs_init(void)
+{
+	fpga_mgr_debugfs_root = debugfs_create_dir("fpga_manager", NULL);
+	if (!fpga_mgr_debugfs_root)
+		pr_warn("fpga_mgr: Failed to create debugfs root\n");
+}
+
+void fpga_mgr_debugfs_uninit(void)
+{
+	debugfs_remove_recursive(fpga_mgr_debugfs_root);
+}
diff --git a/drivers/fpga/fpga-mgr-debugfs.h b/drivers/fpga/fpga-mgr-debugfs.h
new file mode 100644
index 0000000..17cd3eb
--- /dev/null
+++ b/drivers/fpga/fpga-mgr-debugfs.h
@@ -0,0 +1,22 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef _LINUX_FPGA_MGR_DEBUGFS_H
+#define _LINUX_FPGA_MGR_DEBUGFS_H
+
+#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
+
+void fpga_mgr_debugfs_add(struct fpga_manager *mgr);
+void fpga_mgr_debugfs_remove(struct fpga_manager *mgr);
+void fpga_mgr_debugfs_init(void);
+void fpga_mgr_debugfs_uninit(void);
+
+#else
+
+void fpga_mgr_debugfs_add(struct fpga_manager *mgr) {}
+void fpga_mgr_debugfs_remove(struct fpga_manager *mgr) {}
+void fpga_mgr_debugfs_init(void) {}
+void fpga_mgr_debugfs_uninit(void) {}
+
+#endif /* CONFIG_FPGA_MGR_DEBUG_FS */
+
+#endif /*_LINUX_FPGA_MGR_DEBUGFS_H */
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index c8684bc..66eb6f6 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -17,6 +17,7 @@ 
 #include <linux/slab.h>
 #include <linux/scatterlist.h>
 #include <linux/highmem.h>
+#include "fpga-mgr-debugfs.h"
 
 static DEFINE_IDA(fpga_mgr_ida);
 static struct class *fpga_mgr_class;
@@ -698,6 +699,8 @@  int fpga_mgr_register(struct fpga_manager *mgr)
 	if (ret)
 		goto error_device;
 
+	fpga_mgr_debugfs_add(mgr);
+
 	dev_info(&mgr->dev, "%s registered\n", mgr->name);
 
 	return 0;
@@ -722,6 +725,8 @@  void fpga_mgr_unregister(struct fpga_manager *mgr)
 {
 	dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
 
+	fpga_mgr_debugfs_remove(mgr);
+
 	/*
 	 * If the low level driver provides a method for putting fpga into
 	 * a desired state upon unregister, do it.
@@ -748,11 +753,14 @@  static int __init fpga_mgr_class_init(void)
 	fpga_mgr_class->dev_groups = fpga_mgr_groups;
 	fpga_mgr_class->dev_release = fpga_mgr_dev_release;
 
+	fpga_mgr_debugfs_init();
+
 	return 0;
 }
 
 static void __exit fpga_mgr_class_exit(void)
 {
+	fpga_mgr_debugfs_uninit();
 	class_destroy(fpga_mgr_class);
 	ida_destroy(&fpga_mgr_ida);
 }
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index 777c502..e8f2159 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -170,6 +170,9 @@  struct fpga_manager {
 	struct fpga_compat_id *compat_id;
 	const struct fpga_manager_ops *mops;
 	void *priv;
+#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
+	void *debugfs;
+#endif
 };
 
 #define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)