From 53d47b62620d0e822c848656eec68ea8dd2274d8 Mon Sep 17 00:00:00 2001 From: Show Liu Date: Tue, 20 May 2014 15:55:03 +0800 Subject: add dma_buf lock driver --- drivers/base/dma_buf_lock/sconscript | 27 ++ drivers/base/dma_buf_lock/src/Kbuild | 18 + drivers/base/dma_buf_lock/src/Makefile | 32 ++ drivers/base/dma_buf_lock/src/dma_buf_lock.c | 481 +++++++++++++++++++++++++++ drivers/base/dma_buf_lock/src/dma_buf_lock.h | 42 +++ drivers/base/dma_buf_lock/src/sconscript | 36 ++ 6 files changed, 636 insertions(+) create mode 100755 drivers/base/dma_buf_lock/sconscript create mode 100755 drivers/base/dma_buf_lock/src/Kbuild create mode 100755 drivers/base/dma_buf_lock/src/Makefile create mode 100755 drivers/base/dma_buf_lock/src/dma_buf_lock.c create mode 100755 drivers/base/dma_buf_lock/src/dma_buf_lock.h create mode 100755 drivers/base/dma_buf_lock/src/sconscript diff --git a/drivers/base/dma_buf_lock/sconscript b/drivers/base/dma_buf_lock/sconscript new file mode 100755 index 00000000000..05908b2d589 --- /dev/null +++ b/drivers/base/dma_buf_lock/sconscript @@ -0,0 +1,27 @@ +# +# (C) COPYRIGHT 2012, 2014 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + +import os +import re +Import('env') + +linux_config_file = os.path.normpath(os.environ['KDIR']) + '/.config' +search_term = '^[\ ]*CONFIG_DMA_SHARED_BUFFER_USES_KDS[\ ]*=[\ ]*y' +for line in open(linux_config_file, 'r'): + if re.search(search_term, line): + SConscript( 'src/sconscript' ) + if Glob('tests/sconscript'): + SConscript( 'tests/sconscript' ) + break diff --git a/drivers/base/dma_buf_lock/src/Kbuild b/drivers/base/dma_buf_lock/src/Kbuild new file mode 100755 index 00000000000..68dad07ad60 --- /dev/null +++ b/drivers/base/dma_buf_lock/src/Kbuild @@ -0,0 +1,18 @@ +# +# (C) COPYRIGHT 2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + +ifneq ($(CONFIG_DMA_SHARED_BUFFER),) +obj-m := dma_buf_lock.o +endif diff --git a/drivers/base/dma_buf_lock/src/Makefile b/drivers/base/dma_buf_lock/src/Makefile new file mode 100755 index 00000000000..cf76132e6dd --- /dev/null +++ b/drivers/base/dma_buf_lock/src/Makefile @@ -0,0 +1,32 @@ +# +# (C) COPYRIGHT 2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + +# linux build system bootstrap for out-of-tree module + +# default to building for the host +ARCH ?= $(shell uname -m) + +ifeq ($(KDIR),) +$(error Must specify KDIR to point to the kernel to target)) +endif + +all: dma_buf_lock + +dma_buf_lock: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../../include" + +clean: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean + diff --git a/drivers/base/dma_buf_lock/src/dma_buf_lock.c b/drivers/base/dma_buf_lock/src/dma_buf_lock.c new file mode 100755 index 00000000000..9a0b35b05db --- /dev/null +++ b/drivers/base/dma_buf_lock/src/dma_buf_lock.c @@ -0,0 +1,481 @@ +/* + * + * (C) COPYRIGHT ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained + * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dma_buf_lock.h" + +/* Maximum number of buffers that a single handle can address */ +#define DMA_BUF_LOCK_BUF_MAX 32 + +#define DMA_BUF_LOCK_DEBUG 1 + +static dev_t dma_buf_lock_dev; +static struct cdev dma_buf_lock_cdev; +static struct class *dma_buf_lock_class; +static char dma_buf_lock_dev_name[] = "dma_buf_lock"; + +#ifdef HAVE_UNLOCKED_IOCTL +static long dma_buf_lock_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#else +static int dma_buf_lock_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#endif + +static struct file_operations dma_buf_lock_fops = +{ + .owner = THIS_MODULE, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = dma_buf_lock_ioctl, +#else + .ioctl = dma_buf_lock_ioctl, +#endif + .compat_ioctl = dma_buf_lock_ioctl, +}; + +typedef struct dma_buf_lock_resource +{ + int *list_of_dma_buf_fds; /* List of buffers copied from userspace */ + atomic_t locked; /* Status of lock */ + struct dma_buf **dma_bufs; + struct kds_resource **kds_resources; /* List of KDS resources associated with buffers */ + struct kds_resource_set *resource_set; + unsigned long exclusive; /* Exclusive access bitmap */ + wait_queue_head_t wait; + struct kds_callback cb; + struct kref refcount; + struct list_head link; + int count; +} dma_buf_lock_resource; + +static LIST_HEAD(dma_buf_lock_resource_list); +static DEFINE_MUTEX(dma_buf_lock_mutex); + +static inline int is_dma_buf_lock_file(struct file *); +static void dma_buf_lock_dounlock(struct kref *ref); + +static int dma_buf_lock_handle_release(struct inode *inode, struct file *file) +{ + dma_buf_lock_resource *resource; + + if (!is_dma_buf_lock_file(file)) + return -EINVAL; + + resource = file->private_data; +#if DMA_BUF_LOCK_DEBUG + printk("dma_buf_lock_handle_release\n"); +#endif + mutex_lock(&dma_buf_lock_mutex); + kref_put(&resource->refcount, dma_buf_lock_dounlock); + mutex_unlock(&dma_buf_lock_mutex); + + return 0; +} + +static void dma_buf_lock_kds_callback(void *param1, void *param2) +{ + dma_buf_lock_resource *resource = param1; +#if DMA_BUF_LOCK_DEBUG + printk("dma_buf_lock_kds_callback\n"); +#endif + atomic_set(&resource->locked, 1); + + wake_up(&resource->wait); +} + +static unsigned int dma_buf_lock_handle_poll(struct file *file, + struct poll_table_struct *wait) +{ + dma_buf_lock_resource *resource; + unsigned int ret = 0; + + if (!is_dma_buf_lock_file(file)) + return POLLERR; + + resource = file->private_data; +#if DMA_BUF_LOCK_DEBUG + printk("dma_buf_lock_handle_poll\n"); +#endif + if (1 == atomic_read(&resource->locked)) + { + /* Resources have been locked */ + ret = POLLIN | POLLRDNORM; + if (resource->exclusive) + { + ret |= POLLOUT | POLLWRNORM; + } + } + else + { + if (!poll_does_not_wait(wait)) + { + poll_wait(file, &resource->wait, wait); + } + } +#if DMA_BUF_LOCK_DEBUG + printk("dma_buf_lock_handle_poll : return %i\n", ret); +#endif + return ret; +} + +static const struct file_operations dma_buf_lock_handle_fops = { + .release = dma_buf_lock_handle_release, + .poll = dma_buf_lock_handle_poll, +}; + +/* + * is_dma_buf_lock_file - Check if struct file* is associated with dma_buf_lock + */ +static inline int is_dma_buf_lock_file(struct file *file) +{ + return file->f_op == &dma_buf_lock_handle_fops; +} + + + +/* + * Start requested lock. + * + * Allocates required memory, copies dma_buf_fd list from userspace, + * acquires related KDS resources, and starts the lock. + */ +static int dma_buf_lock_dolock(dma_buf_lock_k_request *request) +{ + dma_buf_lock_resource *resource; + int size; + int fd; + int i; + int ret; + + if (NULL == request->list_of_dma_buf_fds) + { + return -EINVAL; + } + if (request->count <= 0) + { + return -EINVAL; + } + if (request->count > DMA_BUF_LOCK_BUF_MAX) + { + return -EINVAL; + } + if (request->exclusive != DMA_BUF_LOCK_NONEXCLUSIVE && + request->exclusive != DMA_BUF_LOCK_EXCLUSIVE) + { + return -EINVAL; + } + + resource = kzalloc(sizeof(dma_buf_lock_resource), GFP_KERNEL); + if (NULL == resource) + { + return -ENOMEM; + } + + atomic_set(&resource->locked, 0); + kref_init(&resource->refcount); + INIT_LIST_HEAD(&resource->link); + resource->count = request->count; + + /* Allocate space to store dma_buf_fds received from user space */ + size = request->count * sizeof(int); + resource->list_of_dma_buf_fds = kmalloc(size, GFP_KERNEL); + + if (NULL == resource->list_of_dma_buf_fds) + { + kfree(resource); + return -ENOMEM; + } + + /* Allocate space to store dma_buf pointers associated with dma_buf_fds */ + size = sizeof(struct dma_buf *) * request->count; + resource->dma_bufs = kmalloc(size, GFP_KERNEL); + + if (NULL == resource->dma_bufs) + { + kfree(resource->list_of_dma_buf_fds); + kfree(resource); + return -ENOMEM; + } + /* Allocate space to store kds_resources associated with dma_buf_fds */ + size = sizeof(struct kds_resource *) * request->count; + resource->kds_resources = kmalloc(size, GFP_KERNEL); + + if (NULL == resource->kds_resources) + { + kfree(resource->dma_bufs); + kfree(resource->list_of_dma_buf_fds); + kfree(resource); + return -ENOMEM; + } + + /* Copy requested list of dma_buf_fds from user space */ + size = request->count * sizeof(int); + if (0 != copy_from_user(resource->list_of_dma_buf_fds, (void __user *)request->list_of_dma_buf_fds, size)) + { + kfree(resource->list_of_dma_buf_fds); + kfree(resource->dma_bufs); + kfree(resource->kds_resources); + kfree(resource); + return -ENOMEM; + } +#if DMA_BUF_LOCK_DEBUG + for (i = 0; i < request->count; i++) + { + printk("dma_buf %i = %X\n", i, resource->list_of_dma_buf_fds[i]); + } +#endif + + /* Add resource to global list */ + mutex_lock(&dma_buf_lock_mutex); + + list_add(&resource->link, &dma_buf_lock_resource_list); + + mutex_unlock(&dma_buf_lock_mutex); + + for (i = 0; i < request->count; i++) + { + /* Convert fd into dma_buf structure */ + resource->dma_bufs[i] = dma_buf_get(resource->list_of_dma_buf_fds[i]); + + if (IS_ERR_VALUE(PTR_ERR(resource->dma_bufs[i]))) + { + mutex_lock(&dma_buf_lock_mutex); + kref_put(&resource->refcount, dma_buf_lock_dounlock); + mutex_unlock(&dma_buf_lock_mutex); + return -EINVAL; + } + + /*Get kds_resource associated with dma_buf */ + resource->kds_resources[i] = get_dma_buf_kds_resource(resource->dma_bufs[i]); + + if (NULL == resource->kds_resources[i]) + { + mutex_lock(&dma_buf_lock_mutex); + kref_put(&resource->refcount, dma_buf_lock_dounlock); + mutex_unlock(&dma_buf_lock_mutex); + return -EINVAL; + } +#if DMA_BUF_LOCK_DEBUG + printk("dma_buf_lock_dolock : dma_buf_fd %i dma_buf %X kds_resource %X\n", resource->list_of_dma_buf_fds[i], + (unsigned int)resource->dma_bufs[i], (unsigned int)resource->kds_resources[i]); +#endif + } + + kds_callback_init(&resource->cb, 1, dma_buf_lock_kds_callback); + init_waitqueue_head(&resource->wait); + + kref_get(&resource->refcount); + + /* Create file descriptor associated with lock request */ + fd = anon_inode_getfd("dma_buf_lock", &dma_buf_lock_handle_fops, + (void *)resource, 0); + if (fd < 0) + { + mutex_lock(&dma_buf_lock_mutex); + kref_put(&resource->refcount, dma_buf_lock_dounlock); + kref_put(&resource->refcount, dma_buf_lock_dounlock); + mutex_unlock(&dma_buf_lock_mutex); + return fd; + } + + resource->exclusive = request->exclusive; + + /* Start locking process */ + ret = kds_async_waitall(&resource->resource_set, + &resource->cb, resource, NULL, + request->count, &resource->exclusive, + resource->kds_resources); + + if (IS_ERR_VALUE(ret)) + { + put_unused_fd(fd); + + mutex_lock(&dma_buf_lock_mutex); + kref_put(&resource->refcount, dma_buf_lock_dounlock); + mutex_unlock(&dma_buf_lock_mutex); + + return ret; + } + +#if DMA_BUF_LOCK_DEBUG + printk("dma_buf_lock_dolock : complete\n"); +#endif + mutex_lock(&dma_buf_lock_mutex); + kref_put(&resource->refcount, dma_buf_lock_dounlock); + mutex_unlock(&dma_buf_lock_mutex); + + return fd; +} + +static void dma_buf_lock_dounlock(struct kref *ref) +{ + int i; + dma_buf_lock_resource *resource = container_of(ref, dma_buf_lock_resource, refcount); + + atomic_set(&resource->locked, 0); + + kds_callback_term(&resource->cb); + + kds_resource_set_release(&resource->resource_set); + + list_del(&resource->link); + + for (i = 0; i < resource->count; i++) + { + dma_buf_put(resource->dma_bufs[i]); + } + + kfree(resource->kds_resources); + kfree(resource->dma_bufs); + kfree(resource->list_of_dma_buf_fds); + kfree(resource); +} + +static int __init dma_buf_lock_init(void) +{ + int err; +#if DMA_BUF_LOCK_DEBUG + printk("dma_buf_lock_init\n"); +#endif + err = alloc_chrdev_region(&dma_buf_lock_dev, 0, 1, dma_buf_lock_dev_name); + + if (0 == err) + { + cdev_init(&dma_buf_lock_cdev, &dma_buf_lock_fops); + + err = cdev_add(&dma_buf_lock_cdev, dma_buf_lock_dev, 1); + + if (0 == err) + { + dma_buf_lock_class = class_create(THIS_MODULE, dma_buf_lock_dev_name); + if (IS_ERR(dma_buf_lock_class)) + { + err = PTR_ERR(dma_buf_lock_class); + } + else + { + struct device *mdev; + mdev = device_create(dma_buf_lock_class, NULL, dma_buf_lock_dev, NULL, dma_buf_lock_dev_name); + if (!IS_ERR(mdev)) + { + return 0; + } + + err = PTR_ERR(mdev); + class_destroy(dma_buf_lock_class); + } + cdev_del(&dma_buf_lock_cdev); + } + + unregister_chrdev_region(dma_buf_lock_dev, 1); + } +#if DMA_BUF_LOCK_DEBUG + printk("dma_buf_lock_init failed\n"); +#endif + return err; +} + +static void __exit dma_buf_lock_exit(void) +{ +#if DMA_BUF_LOCK_DEBUG + printk("dma_buf_lock_exit\n"); +#endif + + /* Unlock all outstanding references */ + while (1) + { + mutex_lock(&dma_buf_lock_mutex); + if (list_empty(&dma_buf_lock_resource_list)) + { + mutex_unlock(&dma_buf_lock_mutex); + break; + } + else + { + dma_buf_lock_resource *resource = list_entry(dma_buf_lock_resource_list.next, + dma_buf_lock_resource, link); + kref_put(&resource->refcount, dma_buf_lock_dounlock); + mutex_unlock(&dma_buf_lock_mutex); + } + } + + device_destroy(dma_buf_lock_class, dma_buf_lock_dev); + + class_destroy(dma_buf_lock_class); + + cdev_del(&dma_buf_lock_cdev); + + unregister_chrdev_region(dma_buf_lock_dev, 1); +} + +#ifdef HAVE_UNLOCKED_IOCTL +static long dma_buf_lock_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int dma_buf_lock_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + dma_buf_lock_k_request request; + int size = _IOC_SIZE(cmd); + + if (_IOC_TYPE(cmd) != DMA_BUF_LOCK_IOC_MAGIC) + { + return -ENOTTY; + + } + if ((_IOC_NR(cmd) < DMA_BUF_LOCK_IOC_MINNR) || (_IOC_NR(cmd) > DMA_BUF_LOCK_IOC_MAXNR)) + { + return -ENOTTY; + } + + switch (cmd) + { + case DMA_BUF_LOCK_FUNC_LOCK_ASYNC: + if (size != sizeof(dma_buf_lock_k_request)) + { + return -ENOTTY; + } + if (copy_from_user(&request, (void __user *)arg, size)) + { + return -EFAULT; + } +#if DMA_BUF_LOCK_DEBUG + printk("DMA_BUF_LOCK_FUNC_LOCK_ASYNC - %i\n", request.count); +#endif + return dma_buf_lock_dolock(&request); + } + + return -ENOTTY; +} + +module_init(dma_buf_lock_init); +module_exit(dma_buf_lock_exit); + +MODULE_LICENSE("GPL"); + diff --git a/drivers/base/dma_buf_lock/src/dma_buf_lock.h b/drivers/base/dma_buf_lock/src/dma_buf_lock.h new file mode 100755 index 00000000000..5d248e8ccae --- /dev/null +++ b/drivers/base/dma_buf_lock/src/dma_buf_lock.h @@ -0,0 +1,42 @@ +/* + * + * (C) COPYRIGHT ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained + * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + + + +#ifndef _DMA_BUF_LOCK_H +#define _DMA_BUF_LOCK_H + +typedef enum dma_buf_lock_exclusive +{ + DMA_BUF_LOCK_NONEXCLUSIVE = 0, + DMA_BUF_LOCK_EXCLUSIVE = -1 +} dma_buf_lock_exclusive; + +typedef struct dma_buf_lock_k_request +{ + int count; + int *list_of_dma_buf_fds; + int timeout; + dma_buf_lock_exclusive exclusive; +} dma_buf_lock_k_request; + +#define DMA_BUF_LOCK_IOC_MAGIC '~' + +#define DMA_BUF_LOCK_FUNC_LOCK_ASYNC _IOW(DMA_BUF_LOCK_IOC_MAGIC, 11, dma_buf_lock_k_request) + +#define DMA_BUF_LOCK_IOC_MINNR 11 +#define DMA_BUF_LOCK_IOC_MAXNR 11 + +#endif /* _DMA_BUF_LOCK_H */ diff --git a/drivers/base/dma_buf_lock/src/sconscript b/drivers/base/dma_buf_lock/src/sconscript new file mode 100755 index 00000000000..251c9a6f53d --- /dev/null +++ b/drivers/base/dma_buf_lock/src/sconscript @@ -0,0 +1,36 @@ +# +# (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + +import os +import re +Import('env') + +if env['v'] != '1': + env['MAKECOMSTR'] = '[MAKE] ${SOURCE.dir}' + +src = [Glob('#kernel/drivers/base/dma_buf_lock/src/*.c'), Glob('#kernel/drivers/base/dma_buf_lock/src/*.h'), Glob('#kernel/drivers/base/dma_buf_lock/src/K*')] + +if env.GetOption('clean') : + # Clean module + env.Execute(Action("make clean", '[CLEAN] dma_buf_lock')) + cmd = env.Command('$STATIC_LIB_PATH/dma_buf_lock.ko', src, []) + env.ProgTarget('dma_buf_lock', cmd) + +else: + # Build module + makeAction=Action("cd ${SOURCE.dir} && make dma_buf_lock && cp dma_buf_lock.ko $STATIC_LIB_PATH/", '$MAKECOMSTR') + cmd = env.Command('$STATIC_LIB_PATH/dma_buf_lock.ko', src, [makeAction]) + env.ProgTarget('dma_buf_lock', cmd) + -- cgit v1.2.3