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
---
drivers/fpga/fpga-mgr.c | 113 ++
include/linux/fpga/fpga-mgr.h | 10 +++
2 files changed, 123 insertions(+)
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index 06651389c592..23d2b4d45827 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
+#include
#include
#include
#include
@@ -519,6 +521,39 @@ static int fpga_mgr_buf_load(struct fpga_manager *mgr,
return rc;
}
+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, >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
@@ -573,6 +608,8 @@ int fpga_mgr_load(struct fpga_manager *mgr, struct
fpga_image_info *info)
{
info->header_size = mgr->mops->initial_header_size;
+ if (mgr->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)
@@ -732,6 +769,64 @@ void fpga_mgr_put(struct fpga_manager *mgr)
}
EXPORT_SYMBOL_GPL(fpga_mgr_put);
+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(, argp, sizeof(buffd)))
+ return -EFAULT;
+
+ mgr->dmabuf = dma_buf_get(buffd);
+ if (IS_ERR_OR_NULL(mgr->dmabuf))
+ return -EINVAL;
+
+ mgr->flags = FPGA_MGR_CONFIG_DMA_BUF;
+
+ 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_lock - Lock FPGA manager for exclusive use
* @mgr: fpga manager
@@ -815,10 +910,28 @@ fpga_mgr_register_full(struct device *parent, const
struct fpga_manager_info *in
mgr->dev.of_node = parent->of_node;
mgr->dev.id = id;
+ /* Make device dma capable by inheriting from parent's */
+ set_dma_ops(>dev, get_dma_ops(parent));
+ ret =