2017-02-14 20:30 GMT+01:00 Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>: > 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@xxxxxxxxxx> >> --- >> 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@xxxxxxxxxx> >> + * >> + * 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@xxxxxxxxxx> >> + * >> + * 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@xxxxxxxxxx>"); >> +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@xxxxxxxxxx> >> + * >> + * 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