[PATCH RFC 2/3] fpga: support loading from a pre-allocated buffer
Martin Hundebøll
mhu at silicom.dk
Tue Apr 6 07:42:51 BST 2021
Hi Nava,
On minor comment below.
On 02/04/2021 11.09, Nava kishore Manne wrote:
> Some systems are memory constrained but they need to load very
> large Configuration files. The FPGA subsystem allows drivers to
> request this Configuration image be loaded from the filesystem,
> but this requires that the entire configuration data be loaded
> into kernel memory first before it's provided to the driver.
> This can lead to a situation where we map the configuration
> data twice, once to load the configuration data into kernel
> memory and once to copy the configuration data into the final
> resting place which is nothing but a dma-able continuous buffer.
>
> This creates needless memory pressure and delays due to multiple
> copies. Let's add a dmabuf handling support to the fpga manager
> framework that allows drivers to load the Configuration data
> directly from a pre-allocated buffer. This skips the intermediate
> step of allocating a buffer in kernel memory to hold the
> Configuration data.
>
> Signed-off-by: Nava kishore Manne <nava.manne at xilinx.com>
> ---
> drivers/fpga/fpga-mgr.c | 126 +++++++++++++++++++++++++++++++++-
> drivers/fpga/of-fpga-region.c | 3 +
> include/linux/fpga/fpga-mgr.h | 6 +-
> 3 files changed, 132 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
> index b85bc47c91a9..13faed61af62 100644
> --- a/drivers/fpga/fpga-mgr.c
> +++ b/drivers/fpga/fpga-mgr.c
> @@ -8,6 +8,8 @@
> * With code from the mailing list:
> * Copyright (C) 2013 Xilinx, Inc.
> */
> +#include <linux/dma-buf.h>
> +#include <linux/dma-map-ops.h>
> #include <linux/firmware.h>
> #include <linux/fpga/fpga-mgr.h>
> #include <linux/idr.h>
> @@ -306,6 +308,51 @@ static int fpga_mgr_buf_load(struct fpga_manager *mgr,
> return rc;
> }
>
> +/**
> + * fpga_mgr_buf_load - load fpga from image in dma buffer
s/fpga_mgr_buf_load/fpga_dmabuf_load
// Martin
> + * @mgr: fpga manager
> + * @info: fpga image info
> + *
> + * Step the low level fpga manager through the device-specific steps of getting
> + * an FPGA ready to be configured, writing the image to it, then doing whatever
> + * post-configuration steps necessary. This code assumes the caller got the
> + * mgr pointer from of_fpga_mgr_get() and checked that it is not an error code.
> + *
> + * Return: 0 on success, negative error code otherwise.
> + */
> +static int fpga_dmabuf_load(struct fpga_manager *mgr,
> + struct fpga_image_info *info)
> +{
> + struct dma_buf_attachment *attach;
> + struct sg_table *sgt;
> + int ret;
> +
> + /* create attachment for dmabuf with the user device */
> + attach = dma_buf_attach(mgr->dmabuf, &mgr->dev);
> + if (IS_ERR(attach)) {
> + pr_err("failed to attach dmabuf\n");
> + ret = PTR_ERR(attach);
> + goto fail_put;
> + }
> +
> + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
> + if (IS_ERR(sgt)) {
> + ret = PTR_ERR(sgt);
> + goto fail_detach;
> + }
> +
> + info->sgt = sgt;
> + ret = fpga_mgr_buf_load_sg(mgr, info, info->sgt);
> + dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
> +
> +fail_detach:
> + dma_buf_detach(mgr->dmabuf, attach);
> +fail_put:
> + dma_buf_put(mgr->dmabuf);
> +
> + return ret;
> +}
> +
> /**
> * fpga_mgr_firmware_load - request firmware and load to fpga
> * @mgr: fpga manager
> @@ -358,6 +405,8 @@ static int fpga_mgr_firmware_load(struct fpga_manager *mgr,
> */
> int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info)
> {
> + if (info->flags & FPGA_MGR_CONFIG_DMA_BUF)
> + return fpga_dmabuf_load(mgr, info);
> if (info->sgt)
> return fpga_mgr_buf_load_sg(mgr, info, info->sgt);
> if (info->buf && info->count)
> @@ -549,6 +598,62 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
> }
> EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>
> +static int fpga_dmabuf_fd_get(struct file *file, char __user *argp)
> +{
> + struct fpga_manager *mgr = (struct fpga_manager *)(file->private_data);
> + int buffd;
> +
> + if (copy_from_user(&buffd, argp, sizeof(buffd)))
> + return -EFAULT;
> +
> + mgr->dmabuf = dma_buf_get(buffd);
> + if (IS_ERR_OR_NULL(mgr->dmabuf))
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int fpga_device_open(struct inode *inode, struct file *file)
> +{
> + struct miscdevice *miscdev = file->private_data;
> + struct fpga_manager *mgr = container_of(miscdev,
> + struct fpga_manager, miscdev);
> +
> + file->private_data = mgr;
> +
> + return 0;
> +}
> +
> +static int fpga_device_release(struct inode *inode, struct file *file)
> +{
> + return 0;
> +}
> +
> +static long fpga_device_ioctl(struct file *file, unsigned int cmd,
> + unsigned long arg)
> +{
> + char __user *argp = (char __user *)arg;
> + int err;
> +
> + switch (cmd) {
> + case FPGA_IOCTL_LOAD_DMA_BUFF:
> + err = fpga_dmabuf_fd_get(file, argp);
> + break;
> + default:
> + err = -ENOTTY;
> + }
> +
> + return err;
> +}
> +
> +static const struct file_operations fpga_fops = {
> + .owner = THIS_MODULE,
> + .open = fpga_device_open,
> + .release = fpga_device_release,
> + .unlocked_ioctl = fpga_device_ioctl,
> + .compat_ioctl = fpga_device_ioctl,
> +};
> +
> /**
> * fpga_mgr_create - create and initialize a FPGA manager struct
> * @dev: fpga manager device from pdev
> @@ -569,8 +674,7 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> int id, ret;
>
> if (!mops || !mops->write_complete || !mops->state ||
> - !mops->write_init || (!mops->write && !mops->write_sg) ||
> - (mops->write && mops->write_sg)) {
> + !mops->write_init || (!mops->write && !mops->write_sg)) {
> dev_err(dev, "Attempt to register without fpga_manager_ops\n");
> return NULL;
> }
> @@ -601,10 +705,28 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> mgr->dev.of_node = dev->of_node;
> mgr->dev.id = id;
>
> + /* Make device dma capable by inheriting from parent's */
> + set_dma_ops(&mgr->dev, get_dma_ops(dev));
> + ret = dma_coerce_mask_and_coherent(&mgr->dev, dma_get_mask(dev));
> + if (ret) {
> + dev_warn(dev,
> + "Failed to set DMA mask %llx.Trying to continue.%x\n",
> + dma_get_mask(dev), ret);
> + }
> +
> ret = dev_set_name(&mgr->dev, "fpga%d", id);
> if (ret)
> goto error_device;
>
> + mgr->miscdev.minor = MISC_DYNAMIC_MINOR;
> + mgr->miscdev.name = kobject_name(&mgr->dev.kobj);
> + mgr->miscdev.fops = &fpga_fops;
> + ret = misc_register(&mgr->miscdev);
> + if (ret) {
> + pr_err("fpga: failed to register misc device.\n");
> + goto error_device;
> + }
> +
> return mgr;
>
> error_device:
> diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
> index 35fc2f3d4bd8..698e3e42ccba 100644
> --- a/drivers/fpga/of-fpga-region.c
> +++ b/drivers/fpga/of-fpga-region.c
> @@ -229,6 +229,9 @@ static struct fpga_image_info *of_fpga_region_parse_ov(
> if (of_property_read_bool(overlay, "encrypted-fpga-config"))
> info->flags |= FPGA_MGR_ENCRYPTED_BITSTREAM;
>
> + if (of_property_read_bool(overlay, "fpga-config-from-dmabuf"))
> + info->flags |= FPGA_MGR_CONFIG_DMA_BUF;
> +
> if (!of_property_read_string(overlay, "firmware-name",
> &firmware_name)) {
> info->firmware_name = devm_kstrdup(dev, firmware_name,
> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
> index 2bc3030a69e5..6208c22f7bed 100644
> --- a/include/linux/fpga/fpga-mgr.h
> +++ b/include/linux/fpga/fpga-mgr.h
> @@ -9,6 +9,7 @@
> #define _LINUX_FPGA_MGR_H
>
> #include <linux/mutex.h>
> +#include <linux/miscdevice.h>
> #include <linux/platform_device.h>
>
> struct fpga_manager;
> @@ -73,7 +74,7 @@ enum fpga_mgr_states {
> #define FPGA_MGR_ENCRYPTED_BITSTREAM BIT(2)
> #define FPGA_MGR_BITSTREAM_LSB_FIRST BIT(3)
> #define FPGA_MGR_COMPRESSED_BITSTREAM BIT(4)
> -
> +#define FPGA_MGR_CONFIG_DMA_BUF BIT(5)
> /**
> * struct fpga_image_info - information specific to a FPGA image
> * @flags: boolean flags as defined above
> @@ -167,6 +168,8 @@ struct fpga_compat_id {
> struct fpga_manager {
> const char *name;
> struct device dev;
> + struct miscdevice miscdev;
> + struct dma_buf *dmabuf;
> struct mutex ref_mutex;
> enum fpga_mgr_states state;
> struct fpga_compat_id *compat_id;
> @@ -204,4 +207,5 @@ struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> const struct fpga_manager_ops *mops,
> void *priv);
>
> +#define FPGA_IOCTL_LOAD_DMA_BUFF _IOWR('R', 1, __u32)
> #endif /*_LINUX_FPGA_MGR_H */
>
More information about the linux-arm-kernel
mailing list