diff options
author | Srikant Patnaik | 2015-01-11 19:41:45 +0530 |
---|---|---|
committer | Srikant Patnaik | 2015-01-11 19:41:45 +0530 |
commit | abc18728f7736823a28540346129bbeafb3b5866 (patch) | |
tree | 036140abf92bd5ebc4bfa605cb686e1f1638ca1d /drivers/gpu/mali/umplock/umplock_driver.c | |
parent | b9ae4882794bcdc8d26671dfdbc406173d76c739 (diff) | |
download | FOSSEE-netbook-kernel-source-abc18728f7736823a28540346129bbeafb3b5866.tar.gz FOSSEE-netbook-kernel-source-abc18728f7736823a28540346129bbeafb3b5866.tar.bz2 FOSSEE-netbook-kernel-source-abc18728f7736823a28540346129bbeafb3b5866.zip |
This commit is dedicacted to MALI, UMP and MALI_DRM. This includes MALI-r3p2 from other source
Diffstat (limited to 'drivers/gpu/mali/umplock/umplock_driver.c')
-rw-r--r-- | drivers/gpu/mali/umplock/umplock_driver.c | 604 |
1 files changed, 604 insertions, 0 deletions
diff --git a/drivers/gpu/mali/umplock/umplock_driver.c b/drivers/gpu/mali/umplock/umplock_driver.c new file mode 100644 index 00000000..825f16fa --- /dev/null +++ b/drivers/gpu/mali/umplock/umplock_driver.c @@ -0,0 +1,604 @@ +/* + * Copyright (C) 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <asm/uaccess.h> +#include "umplock_ioctl.h" +#include <linux/sched.h> + +#define MAX_ITEMS 1024 +#define MAX_PIDS 128 + +typedef struct lock_cmd_priv { + uint32_t msg[128]; /*ioctl args*/ + u32 pid; /*process id*/ +} _lock_cmd_priv; + +typedef struct lock_ref { + int ref_count; + u32 pid; +} _lock_ref; + +typedef struct umplock_item { + u32 secure_id; + u32 id_ref_count; + u32 owner; + _lock_access_usage usage; + _lock_ref references[MAX_PIDS]; + struct semaphore item_lock; +} umplock_item; + +typedef struct umplock_device_private { + struct mutex item_list_lock; + atomic_t sessions; + umplock_item items[MAX_ITEMS]; + u32 pids[MAX_PIDS]; +} umplock_device_private; + +struct umplock_device { + struct cdev cdev; + struct class *umplock_class; +}; + +static struct umplock_device umplock_device; +static umplock_device_private device; +static dev_t umplock_dev; +static char umplock_dev_name[] = "umplock"; + +int umplock_debug_level = 0; +module_param(umplock_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(umplock_debug_level, "set umplock_debug_level to print debug messages"); + +#define PDEBUG(level, fmt, args...) do { if ((level) <= umplock_debug_level) printk(KERN_DEBUG "umplock: " fmt, ##args); } while (0) +#define PERROR(fmt, args...) do { printk(KERN_ERR "umplock: " fmt, ##args); } while (0) + +int umplock_find_item(u32 secure_id) +{ + int i; + for (i = 0; i < MAX_ITEMS; i++) { + if (device.items[i].secure_id == secure_id) { + return i; + } + } + + return -1; +} + +static int umplock_find_item_by_pid(_lock_cmd_priv *lock_cmd, int *item_slot, int *ref_slot) +{ + _lock_item_s *lock_item; + int i, j; + + lock_item = (_lock_item_s *)&lock_cmd->msg; + + i = umplock_find_item(lock_item->secure_id); + + if (i < 0) { + return -1; + } + + for (j = 0; j < MAX_PIDS; j++) { + if (device.items[i].references[j].pid == lock_cmd->pid) { + *item_slot = i; + *ref_slot = j; + return 0; + } + } + return -1 ; +} + +static int umplock_find_client_valid(u32 pid) +{ + int i; + + if (pid == 0) { + return -1; + } + + for (i = 0; i < MAX_PIDS; i++) { + if (device.pids[i] == pid) { + return i; + } + } + + return -1; +} + +static int do_umplock_create_locked(_lock_cmd_priv *lock_cmd) +{ + int i_index, ref_index; + int ret; + _lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg; + + i_index = ref_index = -1; + + ret = umplock_find_client_valid(lock_cmd->pid); + if (ret < 0) { + /*lock request from an invalid client pid, do nothing*/ + return -EINVAL; + } + + ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index); + if (ret >= 0) { + } else if ((i_index = umplock_find_item(lock_item->secure_id)) >= 0) { + for (ref_index = 0; ref_index < MAX_PIDS; ref_index++) { + if (device.items[i_index].references[ref_index].pid == 0) { + break; + } + } + if (ref_index < MAX_PIDS) { + device.items[i_index].references[ref_index].pid = lock_cmd->pid; + device.items[i_index].references[ref_index].ref_count = 0; + } else { + PERROR("whoops, item ran out of available reference slots\n"); + return -EINVAL; + + } + } else { + i_index = umplock_find_item(0); + + if (i_index >= 0) { + device.items[i_index].secure_id = lock_item->secure_id; + device.items[i_index].id_ref_count = 0; + device.items[i_index].usage = lock_item->usage; + device.items[i_index].references[0].pid = lock_cmd->pid; + device.items[i_index].references[0].ref_count = 0; + sema_init(&device.items[i_index].item_lock, 1); + } else { + PERROR("whoops, ran out of available slots\n"); + return -EINVAL; + } + } + + return 0; +} +/** IOCTLs **/ + +static int do_umplock_create(_lock_cmd_priv *lock_cmd) +{ + return 0; +} + +static int do_umplock_process(_lock_cmd_priv *lock_cmd) +{ + int ret, i_index, ref_index; + _lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg; + + mutex_lock(&device.item_list_lock); + + if (0 == lock_item->secure_id) { + PERROR("IOCTL_UMPLOCK_PROCESS called with secure_id is 0, pid: %d\n", lock_cmd->pid); + mutex_unlock(&device.item_list_lock); + return -EINVAL; + } + + ret = do_umplock_create_locked(lock_cmd); + if (ret < 0) { + mutex_unlock(&device.item_list_lock); + return -EINVAL; + } + + ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index); + if (ret < 0) { + /*fail to find a item*/ + PERROR("IOCTL_UMPLOCK_PROCESS called with invalid parameter, pid: %d\n", lock_cmd->pid); + mutex_unlock(&device.item_list_lock); + return -EINVAL; + } + device.items[i_index].references[ref_index].ref_count++; + device.items[i_index].id_ref_count++; + PDEBUG(1, "try to lock, pid: %d, secure_id: 0x%x, ref_count: %d\n", lock_cmd->pid, lock_item->secure_id, device.items[i_index].references[ref_index].ref_count); + + if (lock_cmd->pid == device.items[i_index].owner) { + PDEBUG(1, "already own the lock, pid: %d, secure_id: 0x%x, ref_count: %d\n", lock_cmd->pid, lock_item->secure_id, device.items[i_index].references[ref_index].ref_count); + mutex_unlock(&device.item_list_lock); + return 0; + } + + mutex_unlock(&device.item_list_lock); + if (down_interruptible(&device.items[i_index].item_lock)) { + /*wait up without hold the umplock. restore previous state and return*/ + mutex_lock(&device.item_list_lock); + device.items[i_index].references[ref_index].ref_count--; + device.items[i_index].id_ref_count--; + if (0 == device.items[i_index].references[ref_index].ref_count) { + device.items[i_index].references[ref_index].pid = 0; + if (0 == device.items[i_index].id_ref_count) { + PDEBUG(1, "release item, pid: %d, secure_id: 0x%x\n", lock_cmd->pid, lock_item->secure_id); + device.items[i_index].secure_id = 0; + } + } + + PERROR("failed lock, pid: %d, secure_id: 0x%x, ref_count: %d\n", lock_cmd->pid, lock_item->secure_id, device.items[i_index].references[ref_index].ref_count); + + mutex_unlock(&device.item_list_lock); + return -ERESTARTSYS; + } + + mutex_lock(&device.item_list_lock); + PDEBUG(1, "got lock, pid: %d, secure_id: 0x%x, ref_count: %d\n", lock_cmd->pid, lock_item->secure_id, device.items[i_index].references[ref_index].ref_count); + device.items[i_index].owner = lock_cmd->pid; + mutex_unlock(&device.item_list_lock); + + return 0; +} + +static int do_umplock_release(_lock_cmd_priv *lock_cmd) +{ + int ret, i_index, ref_index; + _lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg; + + mutex_lock(&device.item_list_lock); + + if (0 == lock_item->secure_id) { + PERROR("IOCTL_UMPLOCK_RELEASE called with secure_id is 0, pid: %d\n", lock_cmd->pid); + mutex_unlock(&device.item_list_lock); + return -EINVAL; + } + + ret = umplock_find_client_valid(lock_cmd->pid); + if (ret < 0) { + /*lock request from an invalid client pid, do nothing*/ + mutex_unlock(&device.item_list_lock); + return -EPERM; + } + + i_index = ref_index = -1; + + ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index); + if (ret < 0) { + /*fail to find item*/ + PERROR("IOCTL_UMPLOCK_RELEASE called with invalid parameter pid: %d, secid: 0x%x\n", lock_cmd->pid, lock_item->secure_id); + mutex_unlock(&device.item_list_lock); + return -EINVAL; + } + + /* if the lock is not owned by this process */ + if (lock_cmd->pid != device.items[i_index].owner) { + mutex_unlock(&device.item_list_lock); + return -EPERM; + } + + /* if the ref_count is 0, that means nothing to unlock, just return */ + if (0 == device.items[i_index].references[ref_index].ref_count) { + mutex_unlock(&device.item_list_lock); + return 0; + } + + device.items[i_index].references[ref_index].ref_count--; + device.items[i_index].id_ref_count--; + PDEBUG(1, "unlock, pid: %d, secure_id: 0x%x, ref_count: %d\n", lock_cmd->pid, lock_item->secure_id, device.items[i_index].references[ref_index].ref_count); + + if (0 == device.items[i_index].references[ref_index].ref_count) { + device.items[i_index].references[ref_index].pid = 0; + if (0 == device.items[i_index].id_ref_count) { + PDEBUG(1, "release item, pid: %d, secure_id: 0x%x\n", lock_cmd->pid, lock_item->secure_id); + device.items[i_index].secure_id = 0; + } + device.items[i_index].owner = 0; + up(&device.items[i_index].item_lock); + } + mutex_unlock(&device.item_list_lock); + + return 0; +} + +static int do_umplock_zap(void) +{ + int i; + + PDEBUG(1, "ZAP ALL ENTRIES!\n"); + + mutex_lock(&device.item_list_lock); + + for (i = 0; i < MAX_ITEMS; i++) { + device.items[i].secure_id = 0; + memset(&device.items[i].references, 0, sizeof(_lock_ref) * MAX_PIDS); + sema_init(&device.items[i].item_lock, 1); + } + + for (i = 0; i < MAX_PIDS; i++) { + device.pids[i] = 0; + } + mutex_unlock(&device.item_list_lock); + + return 0; +} + +static int do_umplock_dump(void) +{ + int i, j; + + mutex_lock(&device.item_list_lock); + PERROR("dump all the items begin\n"); + for (i = 0; i < MAX_ITEMS; i++) { + for (j = 0; j < MAX_PIDS; j++) { + if (device.items[i].secure_id != 0 && device.items[i].references[j].pid != 0) { + PERROR("item[%d]->secure_id=0x%x, owner=%d\t reference[%d].ref_count=%d.pid=%d\n", + i, + device.items[i].secure_id, + device.items[i].owner, + j, + device.items[i].references[j].ref_count, + device.items[i].references[j].pid); + } + } + } + PERROR("dump all the items end\n"); + mutex_unlock(&device.item_list_lock); + + return 0; +} + +int do_umplock_client_add(_lock_cmd_priv *lock_cmd) +{ + int i; + mutex_lock(&device.item_list_lock); + for (i = 0; i < MAX_PIDS; i++) { + if (device.pids[i] == lock_cmd->pid) { + mutex_unlock(&device.item_list_lock); + return 0; + } + } + for (i = 0; i < MAX_PIDS; i++) { + if (device.pids[i] == 0) { + device.pids[i] = lock_cmd->pid; + break; + } + } + mutex_unlock(&device.item_list_lock); + if (i == MAX_PIDS) { + PERROR("Oops, Run out of client slots\n "); + return -EINVAL; + } + return 0; +} + +int do_umplock_client_delete(_lock_cmd_priv *lock_cmd) +{ + int p_index = -1, i_index = -1, ref_index = -1; + int ret; + _lock_item_s *lock_item; + lock_item = (_lock_item_s *)&lock_cmd->msg; + + mutex_lock(&device.item_list_lock); + p_index = umplock_find_client_valid(lock_cmd->pid); + /*lock item pid is not valid.*/ + if (p_index < 0) { + mutex_unlock(&device.item_list_lock); + return 0; + } + + /*walk through umplock item list and release reference attached to this client*/ + for (i_index = 0; i_index < MAX_ITEMS; i_index++) { + lock_item->secure_id = device.items[i_index].secure_id; + + /*find the item index and reference slot for the lock_item*/ + ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index); + + if (ret < 0) { + /*client has no reference on this umplock item, skip*/ + continue; + } + while (device.items[i_index].references[ref_index].ref_count) { + /*release references on this client*/ + + PDEBUG(1, "delete client, pid: %d, ref_count: %d\n", lock_cmd->pid, device.items[i_index].references[ref_index].ref_count); + + mutex_unlock(&device.item_list_lock); + do_umplock_release(lock_cmd); + mutex_lock(&device.item_list_lock); + } + } + + /*remove the pid from umplock valid pid list*/ + device.pids[p_index] = 0; + mutex_unlock(&device.item_list_lock); + + return 0; +} + +static long umplock_driver_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + int ret; + uint32_t size = _IOC_SIZE(cmd); + _lock_cmd_priv lock_cmd ; + + if (_IOC_TYPE(cmd) != LOCK_IOCTL_GROUP) { + return -ENOTTY; + } + + if (_IOC_NR(cmd) >= LOCK_IOCTL_MAX_CMDS) { + return -ENOTTY; + } + + switch (cmd) { + case LOCK_IOCTL_CREATE: + if (size != sizeof(_lock_item_s)) { + return -ENOTTY; + } + + if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) { + return -EFAULT; + } + lock_cmd.pid = (u32)current->tgid; + ret = do_umplock_create(&lock_cmd); + if (ret) { + return ret; + } + return 0; + + case LOCK_IOCTL_PROCESS: + if (size != sizeof(_lock_item_s)) { + return -ENOTTY; + } + + if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) { + return -EFAULT; + } + lock_cmd.pid = (u32)current->tgid; + return do_umplock_process(&lock_cmd); + + case LOCK_IOCTL_RELEASE: + if (size != sizeof(_lock_item_s)) { + return -ENOTTY; + } + + if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) { + return -EFAULT; + } + lock_cmd.pid = (u32)current->tgid; + ret = do_umplock_release(&lock_cmd); + if (ret) { + return ret; + } + return 0; + + case LOCK_IOCTL_ZAP: + do_umplock_zap(); + return 0; + + case LOCK_IOCTL_DUMP: + do_umplock_dump(); + return 0; + } + + return -ENOIOCTLCMD; +} + +static int umplock_driver_open(struct inode *inode, struct file *filp) +{ + _lock_cmd_priv lock_cmd; + + atomic_inc(&device.sessions); + PDEBUG(1, "OPEN SESSION (%i references)\n", atomic_read(&device.sessions)); + + lock_cmd.pid = (u32)current->tgid; + do_umplock_client_add(&lock_cmd); + + return 0; +} + +static int umplock_driver_release(struct inode *inode, struct file *filp) +{ + int sessions = 0; + _lock_cmd_priv lock_cmd; + + lock_cmd.pid = (u32)current->tgid; + do_umplock_client_delete(&lock_cmd); + + mutex_lock(&device.item_list_lock); + atomic_dec(&device.sessions); + sessions = atomic_read(&device.sessions); + PDEBUG(1, "CLOSE SESSION (%i references)\n", sessions); + mutex_unlock(&device.item_list_lock); + if (sessions == 0) { + do_umplock_zap(); + } + + return 0; +} + +static struct file_operations umplock_fops = { + .owner = THIS_MODULE, + .open = umplock_driver_open, + .release = umplock_driver_release, + .unlocked_ioctl = umplock_driver_ioctl, +}; + +int umplock_device_initialize(void) +{ + int err; + + err = alloc_chrdev_region(&umplock_dev, 0, 1, umplock_dev_name); + + if (0 == err) { + memset(&umplock_device, 0, sizeof(umplock_device)); + cdev_init(&umplock_device.cdev, &umplock_fops); + umplock_device.cdev.owner = THIS_MODULE; + umplock_device.cdev.ops = &umplock_fops; + + err = cdev_add(&umplock_device.cdev, umplock_dev, 1); + if (0 == err) { + umplock_device.umplock_class = class_create(THIS_MODULE, umplock_dev_name); + if (IS_ERR(umplock_device.umplock_class)) { + err = PTR_ERR(umplock_device.umplock_class); + } else { + struct device *mdev; + mdev = device_create(umplock_device.umplock_class, NULL, umplock_dev, NULL, umplock_dev_name); + if (!IS_ERR(mdev)) { + return 0; /* all ok */ + } + + err = PTR_ERR(mdev); + class_destroy(umplock_device.umplock_class); + } + cdev_del(&umplock_device.cdev); + } + + unregister_chrdev_region(umplock_dev, 1); + } else { + PERROR("alloc chardev region failed\n"); + } + + return err; +} + +void umplock_device_terminate(void) +{ + device_destroy(umplock_device.umplock_class, umplock_dev); + class_destroy(umplock_device.umplock_class); + + cdev_del(&umplock_device.cdev); + unregister_chrdev_region(umplock_dev, 1); +} + +static int __init umplock_initialize_module(void) +{ + PDEBUG(1, "Inserting UMP lock device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__); + + mutex_init(&device.item_list_lock); + if (umplock_device_initialize() != 0) { + PERROR("UMP lock device driver init failed\n"); + return -ENOTTY; + } + memset(&device.items, 0, sizeof(umplock_item) * MAX_ITEMS); + memset(&device.pids, 0, sizeof(u32) * MAX_PIDS); + atomic_set(&device.sessions, 0); + + PDEBUG(1, "UMP lock device driver loaded\n"); + + return 0; +} + +static void __exit umplock_cleanup_module(void) +{ + PDEBUG(1, "unloading UMP lock module\n"); + + memset(&device.items, 0, sizeof(umplock_item) * MAX_ITEMS); + memset(&device.pids, 0, sizeof(u32) * MAX_PIDS); + umplock_device_terminate(); + mutex_destroy(&device.item_list_lock); + + PDEBUG(1, "UMP lock module unloaded\n"); +} + +module_init(umplock_initialize_module); +module_exit(umplock_cleanup_module); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ARM Ltd."); +MODULE_DESCRIPTION("ARM UMP locker"); |