Message ID | 1486997106-23277-2-git-send-email-benjamin.gaignard@linaro.org |
---|---|
State | New |
Headers | show |
Series | [RFC,simple,allocator,v2,1/2] Create Simple Allocator module | expand |
Hi Benjamin, Thank you for the patch. I've CC'ed the linux-api mailing list. On Monday 13 Feb 2017 15:45:05 Benjamin Gaignard wrote: > This is the core of simple allocator module. > It aim to offert one common ioctl to allocate specific memory. > > version 2: > - rebased on 4.10-rc7 > > Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> > --- > Documentation/simple-allocator.txt | 81 +++++++++++ > drivers/Kconfig | 2 + > drivers/Makefile | 1 + > drivers/simpleallocator/Kconfig | 10 ++ > drivers/simpleallocator/Makefile | 1 + > drivers/simpleallocator/simple-allocator-priv.h | 33 +++++ > drivers/simpleallocator/simple-allocator.c | 180 +++++++++++++++++++++ > include/uapi/linux/simple-allocator.h | 35 +++++ > 8 files changed, 343 insertions(+) > create mode 100644 Documentation/simple-allocator.txt > create mode 100644 drivers/simpleallocator/Kconfig > create mode 100644 drivers/simpleallocator/Makefile > create mode 100644 drivers/simpleallocator/simple-allocator-priv.h > create mode 100644 drivers/simpleallocator/simple-allocator.c > create mode 100644 include/uapi/linux/simple-allocator.h > > diff --git a/Documentation/simple-allocator.txt > b/Documentation/simple-allocator.txt new file mode 100644 > index 0000000..89ba883 > --- /dev/null > +++ b/Documentation/simple-allocator.txt > @@ -0,0 +1,81 @@ > +Simple Allocator Framework There's nothing simple in buffer allocation, otherwise we would have solved the problem a long time ago. Let's not use a misleading name. > +Simple Allocator offer a single ioctl SA_IOC_ALLOC to allocate buffers > +on dedicated memory regions and export them as a dmabuf file descriptor. > +Using dmabuf file descriptor allow to share this memory between processes > +and/or import it into other frameworks like v4l2 or drm/kms (prime). > +When userland wants to free the memory only a call to close() in needed > +so it could done even without knowing that buffer has been allocated by > +simple allocator ioctl. > + > +Each memory regions will be seen as a filein /dev/. > +For example CMA regions will exposed has /dev/cmaX. Why do you need multiple devices ? Furthermore, given how central this API will become, I believe it needs very strict review, and would be a candidate for a syscall. Without diving into details yet, there are a few particular points I'm concerned about. - What is the scope of this API ? What problems do you want to solve, plan to solve in the future, and consider as out of scope ? - How should we handle permissions and resource limits ? Is file-based permission on a device node a good model ? How do we prevent or at least mitigate memory-related DoS ? - How should such a central allocator API interact with containers and virtualization in general ? - What model do we want to expose to applications to select a memory type ? You still haven't convinced me that we should expose memory pools explicitly to application (à la ION), and I believe a usage/constraint model would be better. > +Implementing a simple allocator > +------------------------------- > + > +Simple Allocator provide helpers functions to register/unregister an > +allocator: > +- simple_allocator_register(struct sa_device *sadev) > + Register simple_allocator_device using sa_device structure where name, > + owner and allocate fields must be set. > + > +- simple_allocator_unregister(struct sa_device *sadev) > + Unregister a simple allocator device. > + > +Using Simple Allocator /dev interface example > +--------------------------------------------- > + > +This example of code allocate a buffer on the first CMA region (/dev/cma0) > +before mmap and close it. > + > +#include <errno.h> > +#include <fcntl.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <sys/ioctl.h> > +#include <sys/mman.h> > +#include <sys/stat.h> > +#include <sys/types.h> > + > +#include "simple-allocator.h" > + > +#define LENGTH 1024*16 > + > +void main (void) > +{ > + struct simple_allocate_data data; > + int fd = open("/dev/cma0", O_RDWR, 0); > + int ret; > + void *mem; > + > + if (fd < 0) { > + printf("Can't open /dev/cma0\n"); > + return; > + } > + > + memset(&data, 0, sizeof(data)); > + > + data.length = LENGTH; > + data.flags = O_RDWR | O_CLOEXEC; > + > + ret = ioctl(fd, SA_IOC_ALLOC, &data); > + if (ret) { > + printf("Buffer allocation failed\n"); > + goto end; > + } > + > + mem = mmap(0, LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, data.fd, 0); > + if (mem == MAP_FAILED) { > + printf("mmap failed\n"); > + } > + > + memset(mem, 0xFF, LENGTH); > + munmap(mem, LENGTH); > + > + printf("test simple allocator CMA OK\n"); > +end: > + close(fd); > +} > diff --git a/drivers/Kconfig b/drivers/Kconfig > index e1e2066..a6d8828 100644 > --- a/drivers/Kconfig > +++ b/drivers/Kconfig > @@ -202,4 +202,6 @@ source "drivers/hwtracing/intel_th/Kconfig" > > source "drivers/fpga/Kconfig" > > +source "drivers/simpleallocator/Kconfig" > + > endmenu > diff --git a/drivers/Makefile b/drivers/Makefile > index 060026a..5081eb8 100644 > --- a/drivers/Makefile > +++ b/drivers/Makefile > @@ -173,3 +173,4 @@ obj-$(CONFIG_STM) += hwtracing/stm/ > obj-$(CONFIG_ANDROID) += android/ > obj-$(CONFIG_NVMEM) += nvmem/ > obj-$(CONFIG_FPGA) += fpga/ > +obj-$(CONFIG_SIMPLE_ALLOCATOR) += simpleallocator/ > diff --git a/drivers/simpleallocator/Kconfig > b/drivers/simpleallocator/Kconfig new file mode 100644 > index 0000000..c6fc2e3 > --- /dev/null > +++ b/drivers/simpleallocator/Kconfig > @@ -0,0 +1,10 @@ > +menu "Simple Allocator" > + > +config SIMPLE_ALLOCATOR > + tristate "Simple Alllocator Framework" > + select DMA_SHARED_BUFFER > + ---help--- > + The Simple Allocator Framework adds an API to allocate and share > + memory in userland. > + > +endmenu > diff --git a/drivers/simpleallocator/Makefile > b/drivers/simpleallocator/Makefile new file mode 100644 > index 0000000..e27c6ad > --- /dev/null > +++ b/drivers/simpleallocator/Makefile > @@ -0,0 +1 @@ > +obj-$(CONFIG_SIMPLE_ALLOCATOR) += simple-allocator.o > diff --git a/drivers/simpleallocator/simple-allocator-priv.h > b/drivers/simpleallocator/simple-allocator-priv.h new file mode 100644 > index 0000000..33f5a33 > --- /dev/null > +++ b/drivers/simpleallocator/simple-allocator-priv.h > @@ -0,0 +1,33 @@ > +/* > + * Copyright (C) Linaro 2016 > + * > + * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> > + * > + * License terms: GNU General Public License (GPL) > + */ > + > +#ifndef _SIMPLE_ALLOCATOR_PRIV_H_ > +#define _SIMPLE_ALLOCATOR_PRIV_H_ > + > +#include <linux/cdev.h> > +#include <linux/device.h> > +#include <linux/dma-buf.h> > + > +/** > + * struct sa_device - simple allocator device > + * @owner: module owner, must be set to THIS_MODULE > + * @name: name of the allocator > + * @allocate: callabck for memory allocation > + */ > +struct sa_device { > + struct device dev; > + struct cdev chrdev; > + struct module *owner; > + const char *name; > + struct dma_buf *(*allocate)(struct sa_device *, u64 length, u32 flags); > +}; > + > +int simple_allocator_register(struct sa_device *sadev); > +void simple_allocator_unregister(struct sa_device *sadev); > + > +#endif > diff --git a/drivers/simpleallocator/simple-allocator.c > b/drivers/simpleallocator/simple-allocator.c new file mode 100644 > index 0000000..d89ccbf > --- /dev/null > +++ b/drivers/simpleallocator/simple-allocator.c > @@ -0,0 +1,180 @@ > +/* > + * Copyright (C) Linaro 2016 > + * > + * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> > + * > + * License terms: GNU General Public License (GPL) > + */ > + > +#include <linux/module.h> > +#include <linux/simple-allocator.h> > +#include <linux/uaccess.h> > + > +#include "simple-allocator-priv.h" > + > +#define SA_MAJOR 222 > +#define SA_NUM_DEVICES 256 > +#define SA_NAME "simple_allocator" > + > +static int sa_minor; > + > +static struct class sa_class = { > + .name = SA_NAME, > +}; > + > +static long sa_ioctl(struct file *filp, unsigned int cmd, unsigned long > arg) +{ > + struct sa_device *sadev = filp->private_data; > + int ret = -ENODEV; > + > + switch (cmd) { > + case SA_IOC_ALLOC: > + { > + struct simple_allocate_data data; > + struct dma_buf *dmabuf; > + > + if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd))) > + return -EFAULT; > + > + if (data.version != 0) > + return -EINVAL; > + > + dmabuf = sadev->allocate(sadev, data.length, data.flags); > + if (!dmabuf) > + return -EINVAL; > + > + data.fd = dma_buf_fd(dmabuf, data.flags); > + if (data.fd < 0) { > + dma_buf_put(dmabuf); > + return -EINVAL; > + } > + > + data.length = dmabuf->size; > + > + if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) { > + dma_buf_put(dmabuf); > + return -EFAULT; > + } > + > + return 0; > + } > + } > + return ret; > +} > + > +static int sa_open(struct inode *inode, struct file *filp) > +{ > + struct sa_device *sadev = container_of(inode->i_cdev, > + struct sa_device, chrdev); > + > + if (!sadev) > + return -ENODEV; > + > + get_device(&sadev->dev); > + filp->private_data = sadev; > + return 0; > +} > + > +static int sa_release(struct inode *inode, struct file *filp) > +{ > + struct sa_device *sadev = container_of(inode->i_cdev, > + struct sa_device, chrdev); > + > + if (!sadev) > + return -ENODEV; > + > + put_device(&sadev->dev); > + return 0; > +} > + > +static const struct file_operations sa_fops = { > + .owner = THIS_MODULE, > + .open = sa_open, > + .release = sa_release, > + .unlocked_ioctl = sa_ioctl, > +}; > + > +/** > + * simple_allocator_register - register a simple allocator > + * @sadev: simple allocator structure to be registered > + * > + * Return 0 if allocator has been regsitered, either a negative value. > + */ > +int simple_allocator_register(struct sa_device *sadev) > +{ > + int ret; > + > + if (!sadev->name || !sadev->allocate) > + return -EINVAL; > + > + cdev_init(&sadev->chrdev, &sa_fops); > + sadev->chrdev.owner = sadev->owner; > + > + ret = cdev_add(&sadev->chrdev, MKDEV(SA_MAJOR, sa_minor), 1); > + if (ret < 0) > + return ret; > + > + sadev->dev.class = &sa_class; > + sadev->dev.devt = MKDEV(SA_MAJOR, sa_minor); > + dev_set_name(&sadev->dev, "%s%d", sadev->name, sa_minor); > + ret = device_register(&sadev->dev); > + if (ret < 0) > + goto cleanup; > + > + sa_minor++; > + return 0; > + > +cleanup: > + cdev_del(&sadev->chrdev); > + return ret; > +} > +EXPORT_SYMBOL_GPL(simple_allocator_register); > + > +/** > + * simple_allocator_unregister - unregister a simple allocator > + * @sadev: simple allocator device to be unregistered > + */ > +void simple_allocator_unregister(struct sa_device *sadev) > +{ > + if (!sadev) > + return; > + > + cdev_del(&sadev->chrdev); > + device_del(&sadev->dev); > + put_device(&sadev->dev); > +} > +EXPORT_SYMBOL_GPL(simple_allocator_unregister); > + > +static int __init sa_init(void) > +{ > + dev_t dev = MKDEV(SA_MAJOR, 0); > + int ret; > + > + ret = register_chrdev_region(dev, SA_NUM_DEVICES, SA_NAME); > + if (ret < 0) > + return ret; > + > + ret = class_register(&sa_class); > + if (ret < 0) { > + unregister_chrdev_region(dev, SA_NUM_DEVICES); > + return -EIO; > + } > + > + return 0; > +} > + > +static void __exit sa_exit(void) > +{ > + dev_t dev = MKDEV(SA_MAJOR, 0); > + > + class_unregister(&sa_class); > + unregister_chrdev_region(dev, SA_NUM_DEVICES); > +} > + > +subsys_initcall(sa_init); > +module_exit(sa_exit); > + > +MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@linaro.org>"); > +MODULE_DESCRIPTION("Simple allocator"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS_CHARDEV_MAJOR(SA_MAJOR); > diff --git a/include/uapi/linux/simple-allocator.h > b/include/uapi/linux/simple-allocator.h new file mode 100644 > index 0000000..5520a85 > --- /dev/null > +++ b/include/uapi/linux/simple-allocator.h > @@ -0,0 +1,35 @@ > +/* > + * Copyright (C) Linaro 2016 > + * > + * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> > + * > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#ifndef _SIMPLE_ALLOCATOR_H_ > +#define _SIMPLE_ALLOCATOR_H_ > + > +#include <linux/ioctl.h> > +#include <linux/types.h> > + > +/** > + * struct simple_allocate_data - allocation parameters > + * @version: structure version (must be set to 0) > + * @length: size of the requested buffer > + * @flags: mode flags for the file like O_RDWR or O_CLOEXEC > + * @fd: returned file descriptor > + */ > +struct simple_allocate_data { > + __u64 version; > + __u64 length; > + __u32 flags; > + __u32 reserved1; > + __s32 fd; > + __u32 reserved2; > +}; > + > +#define SA_IOC_MAGIC 'S' > + > +#define SA_IOC_ALLOC _IOWR(SA_IOC_MAGIC, 0, struct simple_allocate_data) > + > +#endif -- Regards, Laurent Pinchart
2017-02-14 20:30 GMT+01:00 Laurent Pinchart <laurent.pinchart@ideasonboard.com>: > Hi Benjamin, > > Thank you for the patch. I've CC'ed the linux-api mailing list. > > On Monday 13 Feb 2017 15:45:05 Benjamin Gaignard wrote: >> This is the core of simple allocator module. >> It aim to offert one common ioctl to allocate specific memory. >> >> version 2: >> - rebased on 4.10-rc7 >> >> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> >> --- >> Documentation/simple-allocator.txt | 81 +++++++++++ >> drivers/Kconfig | 2 + >> drivers/Makefile | 1 + >> drivers/simpleallocator/Kconfig | 10 ++ >> drivers/simpleallocator/Makefile | 1 + >> drivers/simpleallocator/simple-allocator-priv.h | 33 +++++ >> drivers/simpleallocator/simple-allocator.c | 180 +++++++++++++++++++++ >> include/uapi/linux/simple-allocator.h | 35 +++++ >> 8 files changed, 343 insertions(+) >> create mode 100644 Documentation/simple-allocator.txt >> create mode 100644 drivers/simpleallocator/Kconfig >> create mode 100644 drivers/simpleallocator/Makefile >> create mode 100644 drivers/simpleallocator/simple-allocator-priv.h >> create mode 100644 drivers/simpleallocator/simple-allocator.c >> create mode 100644 include/uapi/linux/simple-allocator.h >> >> diff --git a/Documentation/simple-allocator.txt >> b/Documentation/simple-allocator.txt new file mode 100644 >> index 0000000..89ba883 >> --- /dev/null >> +++ b/Documentation/simple-allocator.txt >> @@ -0,0 +1,81 @@ >> +Simple Allocator Framework > > There's nothing simple in buffer allocation, otherwise we would have solved > the problem a long time ago. Let's not use a misleading name. Simple was not about the problem, it was about the using only one ioctl. Anyway a better name is needed: "Limited allocation API", "Basic allocator framework" ? suggestions are welcomes > >> +Simple Allocator offer a single ioctl SA_IOC_ALLOC to allocate buffers >> +on dedicated memory regions and export them as a dmabuf file descriptor. >> +Using dmabuf file descriptor allow to share this memory between processes >> +and/or import it into other frameworks like v4l2 or drm/kms (prime). >> +When userland wants to free the memory only a call to close() in needed >> +so it could done even without knowing that buffer has been allocated by >> +simple allocator ioctl. >> + >> +Each memory regions will be seen as a filein /dev/. >> +For example CMA regions will exposed has /dev/cmaX. > > Why do you need multiple devices ? Furthermore, given how central this API > will become, I believe it needs very strict review, and would be a candidate > for a syscall. I believe that having a device per memory regions is simple than have having, like ION, heaps which request to add masks (or heap ids) to select them. My goal here is not to have a a central device /dev/simple_allocator where allcoations are dispatched over heaps but only a common ioctl to make userland life easier. Maybe I should regroup all the device under the same directory: /dev/simple/ ? > > Without diving into details yet, there are a few particular points I'm > concerned about. > > - What is the scope of this API ? What problems do you want to solve, plan to > solve in the future, and consider as out of scope ? My problem is to be able to allocate a buffer in contiguous memory (CMA) when using a software decoder and Weston. This will save the copy between decoder and display because display only accepted contiguous memory. Wayland dmabuf protocol can't allocate buffers so I need an other way to get access to CMA. I also other use cases where I need to allocate memory in special memory region for secure purpose. For those both cases I need an API to allocate memory from userland so I decided to propose a common ioctl which may be used for even more memory regions. > > - How should we handle permissions and resource limits ? Is file-based > permission on a device node a good model ? How do we prevent or at least > mitigate memory-related DoS ? Since each memory region will be represented by a device we could set per device permissions for example with SELinux policy. > > - How should such a central allocator API interact with containers and > virtualization in general ? I'm not expert enough in container and virtualization but I know they could have access to some files/directories to store data so I guess it will possible for them to call (for example) /dev/cma0 to allocate memory > - What model do we want to expose to applications to select a memory type ? > You still haven't convinced me that we should expose memory pools explicitly > to application (à la ION), and I believe a usage/constraint model would be > better. Memory type selection should be solved by something more smarter than my code. My goal here is only to provided a common way to perform allocations. If "Unix Device Memory Allocator" project manage to define a way to expose memory constraint I plan to add one ioctl to let it know the capabilities of each memory region. I hope that what I'm proposing could become a backend for "Unix Device Memory Allocator". > >> +Implementing a simple allocator >> +------------------------------- >> + >> +Simple Allocator provide helpers functions to register/unregister an >> +allocator: >> +- simple_allocator_register(struct sa_device *sadev) >> + Register simple_allocator_device using sa_device structure where name, >> + owner and allocate fields must be set. >> + >> +- simple_allocator_unregister(struct sa_device *sadev) >> + Unregister a simple allocator device. >> + >> +Using Simple Allocator /dev interface example >> +--------------------------------------------- >> + >> +This example of code allocate a buffer on the first CMA region (/dev/cma0) >> +before mmap and close it. >> + >> +#include <errno.h> >> +#include <fcntl.h> >> +#include <stdio.h> >> +#include <stdlib.h> >> +#include <string.h> >> +#include <unistd.h> >> +#include <sys/ioctl.h> >> +#include <sys/mman.h> >> +#include <sys/stat.h> >> +#include <sys/types.h> >> + >> +#include "simple-allocator.h" >> + >> +#define LENGTH 1024*16 >> + >> +void main (void) >> +{ >> + struct simple_allocate_data data; >> + int fd = open("/dev/cma0", O_RDWR, 0); >> + int ret; >> + void *mem; >> + >> + if (fd < 0) { >> + printf("Can't open /dev/cma0\n"); >> + return; >> + } >> + >> + memset(&data, 0, sizeof(data)); >> + >> + data.length = LENGTH; >> + data.flags = O_RDWR | O_CLOEXEC; >> + >> + ret = ioctl(fd, SA_IOC_ALLOC, &data); >> + if (ret) { >> + printf("Buffer allocation failed\n"); >> + goto end; >> + } >> + >> + mem = mmap(0, LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, data.fd, 0); >> + if (mem == MAP_FAILED) { >> + printf("mmap failed\n"); >> + } >> + >> + memset(mem, 0xFF, LENGTH); >> + munmap(mem, LENGTH); >> + >> + printf("test simple allocator CMA OK\n"); >> +end: >> + close(fd); >> +} >> diff --git a/drivers/Kconfig b/drivers/Kconfig >> index e1e2066..a6d8828 100644 >> --- a/drivers/Kconfig >> +++ b/drivers/Kconfig >> @@ -202,4 +202,6 @@ source "drivers/hwtracing/intel_th/Kconfig" >> >> source "drivers/fpga/Kconfig" >> >> +source "drivers/simpleallocator/Kconfig" >> + >> endmenu >> diff --git a/drivers/Makefile b/drivers/Makefile >> index 060026a..5081eb8 100644 >> --- a/drivers/Makefile >> +++ b/drivers/Makefile >> @@ -173,3 +173,4 @@ obj-$(CONFIG_STM) += hwtracing/stm/ >> obj-$(CONFIG_ANDROID) += android/ >> obj-$(CONFIG_NVMEM) += nvmem/ >> obj-$(CONFIG_FPGA) += fpga/ >> +obj-$(CONFIG_SIMPLE_ALLOCATOR) += simpleallocator/ >> diff --git a/drivers/simpleallocator/Kconfig >> b/drivers/simpleallocator/Kconfig new file mode 100644 >> index 0000000..c6fc2e3 >> --- /dev/null >> +++ b/drivers/simpleallocator/Kconfig >> @@ -0,0 +1,10 @@ >> +menu "Simple Allocator" >> + >> +config SIMPLE_ALLOCATOR >> + tristate "Simple Alllocator Framework" >> + select DMA_SHARED_BUFFER >> + ---help--- >> + The Simple Allocator Framework adds an API to allocate and share >> + memory in userland. >> + >> +endmenu >> diff --git a/drivers/simpleallocator/Makefile >> b/drivers/simpleallocator/Makefile new file mode 100644 >> index 0000000..e27c6ad >> --- /dev/null >> +++ b/drivers/simpleallocator/Makefile >> @@ -0,0 +1 @@ >> +obj-$(CONFIG_SIMPLE_ALLOCATOR) += simple-allocator.o >> diff --git a/drivers/simpleallocator/simple-allocator-priv.h >> b/drivers/simpleallocator/simple-allocator-priv.h new file mode 100644 >> index 0000000..33f5a33 >> --- /dev/null >> +++ b/drivers/simpleallocator/simple-allocator-priv.h >> @@ -0,0 +1,33 @@ >> +/* >> + * Copyright (C) Linaro 2016 >> + * >> + * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> >> + * >> + * License terms: GNU General Public License (GPL) >> + */ >> + >> +#ifndef _SIMPLE_ALLOCATOR_PRIV_H_ >> +#define _SIMPLE_ALLOCATOR_PRIV_H_ >> + >> +#include <linux/cdev.h> >> +#include <linux/device.h> >> +#include <linux/dma-buf.h> >> + >> +/** >> + * struct sa_device - simple allocator device >> + * @owner: module owner, must be set to THIS_MODULE >> + * @name: name of the allocator >> + * @allocate: callabck for memory allocation >> + */ >> +struct sa_device { >> + struct device dev; >> + struct cdev chrdev; >> + struct module *owner; >> + const char *name; >> + struct dma_buf *(*allocate)(struct sa_device *, u64 length, u32 > flags); >> +}; >> + >> +int simple_allocator_register(struct sa_device *sadev); >> +void simple_allocator_unregister(struct sa_device *sadev); >> + >> +#endif >> diff --git a/drivers/simpleallocator/simple-allocator.c >> b/drivers/simpleallocator/simple-allocator.c new file mode 100644 >> index 0000000..d89ccbf >> --- /dev/null >> +++ b/drivers/simpleallocator/simple-allocator.c >> @@ -0,0 +1,180 @@ >> +/* >> + * Copyright (C) Linaro 2016 >> + * >> + * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> >> + * >> + * License terms: GNU General Public License (GPL) >> + */ >> + >> +#include <linux/module.h> >> +#include <linux/simple-allocator.h> >> +#include <linux/uaccess.h> >> + >> +#include "simple-allocator-priv.h" >> + >> +#define SA_MAJOR 222 >> +#define SA_NUM_DEVICES 256 >> +#define SA_NAME "simple_allocator" >> + >> +static int sa_minor; >> + >> +static struct class sa_class = { >> + .name = SA_NAME, >> +}; >> + >> +static long sa_ioctl(struct file *filp, unsigned int cmd, unsigned long >> arg) +{ >> + struct sa_device *sadev = filp->private_data; >> + int ret = -ENODEV; >> + >> + switch (cmd) { >> + case SA_IOC_ALLOC: >> + { >> + struct simple_allocate_data data; >> + struct dma_buf *dmabuf; >> + >> + if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd))) >> + return -EFAULT; >> + >> + if (data.version != 0) >> + return -EINVAL; >> + >> + dmabuf = sadev->allocate(sadev, data.length, data.flags); >> + if (!dmabuf) >> + return -EINVAL; >> + >> + data.fd = dma_buf_fd(dmabuf, data.flags); >> + if (data.fd < 0) { >> + dma_buf_put(dmabuf); >> + return -EINVAL; >> + } >> + >> + data.length = dmabuf->size; >> + >> + if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) { >> + dma_buf_put(dmabuf); >> + return -EFAULT; >> + } >> + >> + return 0; >> + } >> + } >> + return ret; >> +} >> + >> +static int sa_open(struct inode *inode, struct file *filp) >> +{ >> + struct sa_device *sadev = container_of(inode->i_cdev, >> + struct sa_device, chrdev); >> + >> + if (!sadev) >> + return -ENODEV; >> + >> + get_device(&sadev->dev); >> + filp->private_data = sadev; >> + return 0; >> +} >> + >> +static int sa_release(struct inode *inode, struct file *filp) >> +{ >> + struct sa_device *sadev = container_of(inode->i_cdev, >> + struct sa_device, chrdev); >> + >> + if (!sadev) >> + return -ENODEV; >> + >> + put_device(&sadev->dev); >> + return 0; >> +} >> + >> +static const struct file_operations sa_fops = { >> + .owner = THIS_MODULE, >> + .open = sa_open, >> + .release = sa_release, >> + .unlocked_ioctl = sa_ioctl, >> +}; >> + >> +/** >> + * simple_allocator_register - register a simple allocator >> + * @sadev: simple allocator structure to be registered >> + * >> + * Return 0 if allocator has been regsitered, either a negative value. >> + */ >> +int simple_allocator_register(struct sa_device *sadev) >> +{ >> + int ret; >> + >> + if (!sadev->name || !sadev->allocate) >> + return -EINVAL; >> + >> + cdev_init(&sadev->chrdev, &sa_fops); >> + sadev->chrdev.owner = sadev->owner; >> + >> + ret = cdev_add(&sadev->chrdev, MKDEV(SA_MAJOR, sa_minor), 1); >> + if (ret < 0) >> + return ret; >> + >> + sadev->dev.class = &sa_class; >> + sadev->dev.devt = MKDEV(SA_MAJOR, sa_minor); >> + dev_set_name(&sadev->dev, "%s%d", sadev->name, sa_minor); >> + ret = device_register(&sadev->dev); >> + if (ret < 0) >> + goto cleanup; >> + >> + sa_minor++; >> + return 0; >> + >> +cleanup: >> + cdev_del(&sadev->chrdev); >> + return ret; >> +} >> +EXPORT_SYMBOL_GPL(simple_allocator_register); >> + >> +/** >> + * simple_allocator_unregister - unregister a simple allocator >> + * @sadev: simple allocator device to be unregistered >> + */ >> +void simple_allocator_unregister(struct sa_device *sadev) >> +{ >> + if (!sadev) >> + return; >> + >> + cdev_del(&sadev->chrdev); >> + device_del(&sadev->dev); >> + put_device(&sadev->dev); >> +} >> +EXPORT_SYMBOL_GPL(simple_allocator_unregister); >> + >> +static int __init sa_init(void) >> +{ >> + dev_t dev = MKDEV(SA_MAJOR, 0); >> + int ret; >> + >> + ret = register_chrdev_region(dev, SA_NUM_DEVICES, SA_NAME); >> + if (ret < 0) >> + return ret; >> + >> + ret = class_register(&sa_class); >> + if (ret < 0) { >> + unregister_chrdev_region(dev, SA_NUM_DEVICES); >> + return -EIO; >> + } >> + >> + return 0; >> +} >> + >> +static void __exit sa_exit(void) >> +{ >> + dev_t dev = MKDEV(SA_MAJOR, 0); >> + >> + class_unregister(&sa_class); >> + unregister_chrdev_region(dev, SA_NUM_DEVICES); >> +} >> + >> +subsys_initcall(sa_init); >> +module_exit(sa_exit); >> + >> +MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@linaro.org>"); >> +MODULE_DESCRIPTION("Simple allocator"); >> +MODULE_LICENSE("GPL"); >> +MODULE_ALIAS_CHARDEV_MAJOR(SA_MAJOR); >> diff --git a/include/uapi/linux/simple-allocator.h >> b/include/uapi/linux/simple-allocator.h new file mode 100644 >> index 0000000..5520a85 >> --- /dev/null >> +++ b/include/uapi/linux/simple-allocator.h >> @@ -0,0 +1,35 @@ >> +/* >> + * Copyright (C) Linaro 2016 >> + * >> + * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> >> + * >> + * License terms: GNU General Public License (GPL), version 2 >> + */ >> + >> +#ifndef _SIMPLE_ALLOCATOR_H_ >> +#define _SIMPLE_ALLOCATOR_H_ >> + >> +#include <linux/ioctl.h> >> +#include <linux/types.h> >> + >> +/** >> + * struct simple_allocate_data - allocation parameters >> + * @version: structure version (must be set to 0) >> + * @length: size of the requested buffer >> + * @flags: mode flags for the file like O_RDWR or O_CLOEXEC >> + * @fd: returned file descriptor >> + */ >> +struct simple_allocate_data { >> + __u64 version; >> + __u64 length; >> + __u32 flags; >> + __u32 reserved1; >> + __s32 fd; >> + __u32 reserved2; >> +}; >> + >> +#define SA_IOC_MAGIC 'S' >> + >> +#define SA_IOC_ALLOC _IOWR(SA_IOC_MAGIC, 0, struct simple_allocate_data) >> + >> +#endif > > -- > Regards, > > Laurent Pinchart > -- Benjamin Gaignard Graphic Study Group Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog
diff --git a/Documentation/simple-allocator.txt b/Documentation/simple-allocator.txt new file mode 100644 index 0000000..89ba883 --- /dev/null +++ b/Documentation/simple-allocator.txt @@ -0,0 +1,81 @@ +Simple Allocator Framework + +Simple Allocator offer a single ioctl SA_IOC_ALLOC to allocate buffers +on dedicated memory regions and export them as a dmabuf file descriptor. +Using dmabuf file descriptor allow to share this memory between processes +and/or import it into other frameworks like v4l2 or drm/kms (prime). +When userland wants to free the memory only a call to close() in needed +so it could done even without knowing that buffer has been allocated by +simple allocator ioctl. + +Each memory regions will be seen as a filein /dev/. +For example CMA regions will exposed has /dev/cmaX. + +Implementing a simple allocator +------------------------------- + +Simple Allocator provide helpers functions to register/unregister an +allocator: +- simple_allocator_register(struct sa_device *sadev) + Register simple_allocator_device using sa_device structure where name, + owner and allocate fields must be set. + +- simple_allocator_unregister(struct sa_device *sadev) + Unregister a simple allocator device. + +Using Simple Allocator /dev interface example +--------------------------------------------- + +This example of code allocate a buffer on the first CMA region (/dev/cma0) +before mmap and close it. + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "simple-allocator.h" + +#define LENGTH 1024*16 + +void main (void) +{ + struct simple_allocate_data data; + int fd = open("/dev/cma0", O_RDWR, 0); + int ret; + void *mem; + + if (fd < 0) { + printf("Can't open /dev/cma0\n"); + return; + } + + memset(&data, 0, sizeof(data)); + + data.length = LENGTH; + data.flags = O_RDWR | O_CLOEXEC; + + ret = ioctl(fd, SA_IOC_ALLOC, &data); + if (ret) { + printf("Buffer allocation failed\n"); + goto end; + } + + mem = mmap(0, LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, data.fd, 0); + if (mem == MAP_FAILED) { + printf("mmap failed\n"); + } + + memset(mem, 0xFF, LENGTH); + munmap(mem, LENGTH); + + printf("test simple allocator CMA OK\n"); +end: + close(fd); +} diff --git a/drivers/Kconfig b/drivers/Kconfig index e1e2066..a6d8828 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -202,4 +202,6 @@ source "drivers/hwtracing/intel_th/Kconfig" source "drivers/fpga/Kconfig" +source "drivers/simpleallocator/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 060026a..5081eb8 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -173,3 +173,4 @@ obj-$(CONFIG_STM) += hwtracing/stm/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_NVMEM) += nvmem/ obj-$(CONFIG_FPGA) += fpga/ +obj-$(CONFIG_SIMPLE_ALLOCATOR) += simpleallocator/ diff --git a/drivers/simpleallocator/Kconfig b/drivers/simpleallocator/Kconfig new file mode 100644 index 0000000..c6fc2e3 --- /dev/null +++ b/drivers/simpleallocator/Kconfig @@ -0,0 +1,10 @@ +menu "Simple Allocator" + +config SIMPLE_ALLOCATOR + tristate "Simple Alllocator Framework" + select DMA_SHARED_BUFFER + ---help--- + The Simple Allocator Framework adds an API to allocate and share + memory in userland. + +endmenu diff --git a/drivers/simpleallocator/Makefile b/drivers/simpleallocator/Makefile new file mode 100644 index 0000000..e27c6ad --- /dev/null +++ b/drivers/simpleallocator/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SIMPLE_ALLOCATOR) += simple-allocator.o diff --git a/drivers/simpleallocator/simple-allocator-priv.h b/drivers/simpleallocator/simple-allocator-priv.h new file mode 100644 index 0000000..33f5a33 --- /dev/null +++ b/drivers/simpleallocator/simple-allocator-priv.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) Linaro 2016 + * + * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> + * + * License terms: GNU General Public License (GPL) + */ + +#ifndef _SIMPLE_ALLOCATOR_PRIV_H_ +#define _SIMPLE_ALLOCATOR_PRIV_H_ + +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/dma-buf.h> + +/** + * struct sa_device - simple allocator device + * @owner: module owner, must be set to THIS_MODULE + * @name: name of the allocator + * @allocate: callabck for memory allocation + */ +struct sa_device { + struct device dev; + struct cdev chrdev; + struct module *owner; + const char *name; + struct dma_buf *(*allocate)(struct sa_device *, u64 length, u32 flags); +}; + +int simple_allocator_register(struct sa_device *sadev); +void simple_allocator_unregister(struct sa_device *sadev); + +#endif diff --git a/drivers/simpleallocator/simple-allocator.c b/drivers/simpleallocator/simple-allocator.c new file mode 100644 index 0000000..d89ccbf --- /dev/null +++ b/drivers/simpleallocator/simple-allocator.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) Linaro 2016 + * + * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> + * + * License terms: GNU General Public License (GPL) + */ + +#include <linux/module.h> +#include <linux/simple-allocator.h> +#include <linux/uaccess.h> + +#include "simple-allocator-priv.h" + +#define SA_MAJOR 222 +#define SA_NUM_DEVICES 256 +#define SA_NAME "simple_allocator" + +static int sa_minor; + +static struct class sa_class = { + .name = SA_NAME, +}; + +static long sa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct sa_device *sadev = filp->private_data; + int ret = -ENODEV; + + switch (cmd) { + case SA_IOC_ALLOC: + { + struct simple_allocate_data data; + struct dma_buf *dmabuf; + + if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd))) + return -EFAULT; + + if (data.version != 0) + return -EINVAL; + + dmabuf = sadev->allocate(sadev, data.length, data.flags); + if (!dmabuf) + return -EINVAL; + + data.fd = dma_buf_fd(dmabuf, data.flags); + if (data.fd < 0) { + dma_buf_put(dmabuf); + return -EINVAL; + } + + data.length = dmabuf->size; + + if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) { + dma_buf_put(dmabuf); + return -EFAULT; + } + + return 0; + } + } + return ret; +} + +static int sa_open(struct inode *inode, struct file *filp) +{ + struct sa_device *sadev = container_of(inode->i_cdev, + struct sa_device, chrdev); + + if (!sadev) + return -ENODEV; + + get_device(&sadev->dev); + filp->private_data = sadev; + return 0; +} + +static int sa_release(struct inode *inode, struct file *filp) +{ + struct sa_device *sadev = container_of(inode->i_cdev, + struct sa_device, chrdev); + + if (!sadev) + return -ENODEV; + + put_device(&sadev->dev); + return 0; +} + +static const struct file_operations sa_fops = { + .owner = THIS_MODULE, + .open = sa_open, + .release = sa_release, + .unlocked_ioctl = sa_ioctl, +}; + +/** + * simple_allocator_register - register a simple allocator + * @sadev: simple allocator structure to be registered + * + * Return 0 if allocator has been regsitered, either a negative value. + */ +int simple_allocator_register(struct sa_device *sadev) +{ + int ret; + + if (!sadev->name || !sadev->allocate) + return -EINVAL; + + cdev_init(&sadev->chrdev, &sa_fops); + sadev->chrdev.owner = sadev->owner; + + ret = cdev_add(&sadev->chrdev, MKDEV(SA_MAJOR, sa_minor), 1); + if (ret < 0) + return ret; + + sadev->dev.class = &sa_class; + sadev->dev.devt = MKDEV(SA_MAJOR, sa_minor); + dev_set_name(&sadev->dev, "%s%d", sadev->name, sa_minor); + ret = device_register(&sadev->dev); + if (ret < 0) + goto cleanup; + + sa_minor++; + return 0; + +cleanup: + cdev_del(&sadev->chrdev); + return ret; +} +EXPORT_SYMBOL_GPL(simple_allocator_register); + +/** + * simple_allocator_unregister - unregister a simple allocator + * @sadev: simple allocator device to be unregistered + */ +void simple_allocator_unregister(struct sa_device *sadev) +{ + if (!sadev) + return; + + cdev_del(&sadev->chrdev); + device_del(&sadev->dev); + put_device(&sadev->dev); +} +EXPORT_SYMBOL_GPL(simple_allocator_unregister); + +static int __init sa_init(void) +{ + dev_t dev = MKDEV(SA_MAJOR, 0); + int ret; + + ret = register_chrdev_region(dev, SA_NUM_DEVICES, SA_NAME); + if (ret < 0) + return ret; + + ret = class_register(&sa_class); + if (ret < 0) { + unregister_chrdev_region(dev, SA_NUM_DEVICES); + return -EIO; + } + + return 0; +} + +static void __exit sa_exit(void) +{ + dev_t dev = MKDEV(SA_MAJOR, 0); + + class_unregister(&sa_class); + unregister_chrdev_region(dev, SA_NUM_DEVICES); +} + +subsys_initcall(sa_init); +module_exit(sa_exit); + +MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@linaro.org>"); +MODULE_DESCRIPTION("Simple allocator"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(SA_MAJOR); diff --git a/include/uapi/linux/simple-allocator.h b/include/uapi/linux/simple-allocator.h new file mode 100644 index 0000000..5520a85 --- /dev/null +++ b/include/uapi/linux/simple-allocator.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) Linaro 2016 + * + * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> + * + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _SIMPLE_ALLOCATOR_H_ +#define _SIMPLE_ALLOCATOR_H_ + +#include <linux/ioctl.h> +#include <linux/types.h> + +/** + * struct simple_allocate_data - allocation parameters + * @version: structure version (must be set to 0) + * @length: size of the requested buffer + * @flags: mode flags for the file like O_RDWR or O_CLOEXEC + * @fd: returned file descriptor + */ +struct simple_allocate_data { + __u64 version; + __u64 length; + __u32 flags; + __u32 reserved1; + __s32 fd; + __u32 reserved2; +}; + +#define SA_IOC_MAGIC 'S' + +#define SA_IOC_ALLOC _IOWR(SA_IOC_MAGIC, 0, struct simple_allocate_data) + +#endif
This is the core of simple allocator module. It aim to offert one common ioctl to allocate specific memory. version 2: - rebased on 4.10-rc7 Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> --- Documentation/simple-allocator.txt | 81 +++++++++++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/simpleallocator/Kconfig | 10 ++ drivers/simpleallocator/Makefile | 1 + drivers/simpleallocator/simple-allocator-priv.h | 33 +++++ drivers/simpleallocator/simple-allocator.c | 180 ++++++++++++++++++++++++ include/uapi/linux/simple-allocator.h | 35 +++++ 8 files changed, 343 insertions(+) create mode 100644 Documentation/simple-allocator.txt create mode 100644 drivers/simpleallocator/Kconfig create mode 100644 drivers/simpleallocator/Makefile create mode 100644 drivers/simpleallocator/simple-allocator-priv.h create mode 100644 drivers/simpleallocator/simple-allocator.c create mode 100644 include/uapi/linux/simple-allocator.h -- 1.9.1