diff options
author | Varun Wadekar <vwadekar@nvidia.com> | 2010-06-01 11:58:56 +0530 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-06-07 12:48:41 -0700 |
commit | 21cfe0729a6ad3a9858371b388d3263889fd5a28 (patch) | |
tree | ddecb5cfb8aa57b4011f7695cedaf1a694a41c88 | |
parent | 82241cc9a50a44eea7b0ae033ac8e9de024a40c0 (diff) |
tegra nvos: break single mega node into multiple nodes
Convert /dev/nvos to a general purpose node, /dev/nvos and a super
user node, /dev/knvos. /dev/nvos exposes semaphore related
functionality whereas /dev/knvos will be used for interrupt
and memory management. Track the semaphores in a rbtree
so that we do not stomp on anybody else's space and corrupt
the memory.
Change-Id: I98bacb7312fd3eb86122dde80381b74ae865ce57
Reviewed-on: http://git-master/r/2192
Tested-by: Varun Wadekar <vwadekar@nvidia.com>
Reviewed-by: Gary King <gking@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/nvos/nvos.c | 166 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvos_user.c | 50 |
2 files changed, 185 insertions, 31 deletions
diff --git a/arch/arm/mach-tegra/nvos/nvos.c b/arch/arm/mach-tegra/nvos/nvos.c index 64fd9c41c5f9..9c45e1a255ba 100644 --- a/arch/arm/mach-tegra/nvos/nvos.c +++ b/arch/arm/mach-tegra/nvos/nvos.c @@ -61,6 +61,8 @@ #include <asm/cacheflush.h> #include <mach/irqs.h> #include <linux/freezer.h> +#include <linux/rbtree.h> +#include <linux/rwsem.h> #define ATAG_NVIDIA_TEGRA 0x41000801 struct tag_nvidia_tegra { @@ -141,6 +143,9 @@ struct tag_nvidia_tegra { static spinlock_t gs_NvOsSpinLock; +/* this semaphore lives forever and is used to protect the rb tree*/ +static DECLARE_RWSEM(s_sem); + typedef struct NvOsIrqHandlerRec { union @@ -790,18 +795,95 @@ void NvOsSpinMutexDestroy(NvOsSpinMutexHandle mutex) kfree(mutex); } +struct sem_node +{ + struct rb_node node; + NvOsSemaphoreHandle h; +}; + typedef struct NvOsSemaphoreRec { struct semaphore sem; atomic_t refcount; + NvU32 id; /* identifier to track the handle in rb tree */ + struct rb_root root; } NvOsSemaphore; +static struct sem_node *sem_handle_search(NvOsSemaphoreHandle h) +{ + struct rb_node **node = &(h->root.rb_node); + + down_read(&s_sem); + while (*node) { + struct sem_node *data = rb_entry(*node, struct sem_node, node); + + if (h < data->h) + node = &((*node)->rb_left); + else if (h > data->h) + node = &((*node)->rb_right); + else + up_read(&s_sem); + return data; + } + up_read(&s_sem); + return NULL; +} + +static int sem_handle_insert(NvOsSemaphoreHandle h) +{ + struct rb_node **new = &(h->root.rb_node); + struct rb_node *parent = NULL; + struct sem_node *data; + + down_write(&s_sem); + /* Figure out where to put the new node */ + while (*new) { + data = rb_entry(*new, struct sem_node, node); + parent = *new; + + if (h < data->h) { + new = &((*new)->rb_left); + } else if (h > data->h) { + new = &((*new)->rb_right); + } else + /* It already is in the tree */ + up_write(&s_sem); + return -EALREADY; + } + + /* Add the new node and rebalance the tree. */ + data = kzalloc(sizeof(struct sem_node), GFP_KERNEL); + if (!data) { + pr_err("Could not allocate sema handle\n"); + up_write(&s_sem); + return -ENOMEM; + } + + data->h = h; + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, &(h->root)); + up_write(&s_sem); + return 0; +} + +static int sem_handle_remove(struct sem_node *data, NvOsSemaphoreHandle h) +{ + down_write(&s_sem); + rb_erase(&data->node, &(h->root)); + up_write(&s_sem); + kfree(data); + return 0; +} + NvError NvOsSemaphoreCreate( NvOsSemaphoreHandle *semaphore, NvU32 value) { NvOsSemaphore *s; + if (!semaphore) + return NvError_BadParameter; + s = kzalloc( sizeof(NvOsSemaphore), GFP_KERNEL ); if( !s ) return NvError_InsufficientMemory; @@ -809,6 +891,8 @@ NvError NvOsSemaphoreCreate( sema_init( &s->sem, value ); atomic_set( &s->refcount, 1 ); + /* start tracking the handle */ + sem_handle_insert(s); *semaphore = s; return NvSuccess; @@ -821,7 +905,16 @@ NvError NvOsSemaphoreClone( NV_ASSERT( orig ); NV_ASSERT( semaphore ); + if (!orig || !semaphore) + return NvError_BadParameter; + atomic_inc( &orig->refcount ); + + /* track the handle if we are not already tracking it */ + if (sem_handle_search(orig) == NULL) { + sem_handle_insert(orig); + } + *semaphore = orig; return NvSuccess; @@ -834,7 +927,15 @@ NvError NvOsSemaphoreUnmarshal( NV_ASSERT( hClientSema ); NV_ASSERT( phDriverSema ); + if (!hClientSema || !phDriverSema) + return NvError_BadParameter; + atomic_inc( &hClientSema->refcount ); + + /* track the handle if we are not already tracking it */ + if (sem_handle_search(hClientSema) == NULL) { + sem_handle_insert(hClientSema); + } *phDriverSema = hClientSema; return NvSuccess; @@ -845,29 +946,34 @@ int NvOsSemaphoreWaitInterruptible(NvOsSemaphoreHandle semaphore) { NV_ASSERT(semaphore); + if (sem_handle_search(semaphore) == NULL) + return -EINVAL; + return down_interruptible(&semaphore->sem); } void NvOsSemaphoreWait(NvOsSemaphoreHandle semaphore) { int ret; - + NV_ASSERT(semaphore); - do - { - /* FIXME: We should split the implementation into two parts - - * one for semaphore that were created by users ioctl'ing into - * the nvos device (which need down_interruptible), and others that - * are created and used by the kernel drivers, which do not */ - ret = down_interruptible(&semaphore->sem); - /* The kernel doesn't reschedule tasks - * that have pending signals. If a signal - * is pending, forcibly reschedule the task. - */ - if (ret && !try_to_freeze()) - schedule(); - } while (ret); + if (sem_handle_search(semaphore) != NULL) { + do + { + /* FIXME: We should split the implementation into two parts - + * one for semaphore that were created by users ioctl'ing into + * the nvos device (which need down_interruptible), and others that + * are created and used by the kernel drivers, which do not */ + ret = down_interruptible(&semaphore->sem); + /* The kernel doesn't reschedule tasks + * that have pending signals. If a signal + * is pending, forcibly reschedule the task. + */ + if (ret && !try_to_freeze()) + schedule(); + } while (ret); + } } NvError NvOsSemaphoreWaitTimeout( @@ -881,6 +987,9 @@ NvError NvOsSemaphoreWaitTimeout( if (!semaphore) return NvError_Timeout; + if (sem_handle_search(semaphore) == NULL) + return NvError_Timeout; + if (msec==NV_WAIT_INFINITE) { NvOsSemaphoreWait(semaphore); @@ -912,16 +1021,31 @@ void NvOsSemaphoreSignal(NvOsSemaphoreHandle semaphore) { NV_ASSERT( semaphore ); - up( &semaphore->sem ); + if (semaphore) { + if (!in_interrupt()) { + if (sem_handle_search(semaphore) != NULL) + up( &semaphore->sem ); + } + else + up( &semaphore->sem ); + } } void NvOsSemaphoreDestroy(NvOsSemaphoreHandle semaphore) { - if (!semaphore) - return; - - if( atomic_dec_return( &semaphore->refcount ) == 0 ) - kfree( semaphore ); + struct sem_node *data = NULL; + + if (semaphore) { + /* check if the node exists */ + data = sem_handle_search(semaphore); + if (data) { + if( atomic_dec_return( &semaphore->refcount ) == 0 ) { + if ( sem_handle_remove( data, semaphore ) == 0 ) { + kfree( semaphore ); + } + } + } + } } NvError NvOsThreadMode(int coop) diff --git a/arch/arm/mach-tegra/nvos_user.c b/arch/arm/mach-tegra/nvos_user.c index 19f151815a55..75fb0cd9b97a 100644 --- a/arch/arm/mach-tegra/nvos_user.c +++ b/arch/arm/mach-tegra/nvos_user.c @@ -41,8 +41,6 @@ static long nvos_ioctl(struct file *file, unsigned int cmd, unsigned long arg); int nvos_mmap(struct file *file, struct vm_area_struct *vma); int NvOsSemaphoreWaitInterruptible(NvOsSemaphoreHandle semaphore); -#define DEVICE_NAME "nvos" - static const struct file_operations nvos_fops = { .owner = THIS_MODULE, @@ -54,11 +52,27 @@ static const struct file_operations nvos_fops = static struct miscdevice nvosDevice = { - .name = DEVICE_NAME, + .name = "nvos", .fops = &nvos_fops, .minor = MISC_DYNAMIC_MINOR, }; +static const struct file_operations knvos_fops = +{ + .owner = THIS_MODULE, + .open = nvos_open, + .release = nvos_close, + .unlocked_ioctl = nvos_ioctl, + .mmap = nvos_mmap +}; + +static struct miscdevice knvosDevice = +{ + .name = "knvos", + .fops = &knvos_fops, + .minor = MISC_DYNAMIC_MINOR, +}; + typedef struct NvOsIrqListNodeRec { struct list_head list; @@ -74,20 +88,26 @@ typedef struct NvOsInstanceRec spinlock_t Lock; struct list_head IrqHandles; int pid; + bool su; } NvOsInstance; static int __init nvos_init( void ) { - int retVal = 0; + int ret; - retVal = misc_register(&nvosDevice); + ret = misc_register(&nvosDevice); + if (ret != 0) { + pr_err("%s error 0x%x registering %s\n", __func__, ret, nvosDevice.name); + return ret; + } - if (retVal < 0) - { - printk("nvos init failure\n" ); + ret = misc_register(&knvosDevice); + if (ret != 0) { + pr_err("%s error 0x%x registering %s\n", __func__, ret, knvosDevice.name); + return ret; } - return retVal; + return 0; } static void __exit nvos_deinit( void ) @@ -111,6 +131,7 @@ int nvos_open(struct inode *inode, struct file *filp) Instance->tsk = current; Instance->pid = current->group_leader->pid; Instance->MemRange = NULL; + Instance->su = (filp->f_op == &knvos_fops); spin_lock_init(&Instance->Lock); INIT_LIST_HEAD(&Instance->IrqHandles); filp->private_data = (void*)Instance; @@ -394,7 +415,7 @@ static long nvos_ioctl(struct file *filp, ); NvOsSemaphoreSignal(kernelSem); - break; + break; case NV_IOCTL_SEMAPHORE_WAIT: DO_CLEANUP( NvOsCopyIn( &kernelSem, (void *)arg, sizeof(kernelSem) ) @@ -427,6 +448,9 @@ static long nvos_ioctl(struct file *filp, break; } case NV_IOCTL_INTERRUPT_REGISTER: + if (filp->f_op != &knvos_fops) + return -EACCES; + lock_kernel(); e = interrupt_register(Instance, arg); unlock_kernel(); @@ -436,6 +460,9 @@ static long nvos_ioctl(struct file *filp, case NV_IOCTL_INTERRUPT_DONE: case NV_IOCTL_INTERRUPT_ENABLE: case NV_IOCTL_INTERRUPT_MASK: + if (filp->f_op != &knvos_fops) + return -EACCES; + lock_kernel(); e = interrupt_op(Instance, cmd, arg); unlock_kernel(); @@ -445,6 +472,9 @@ static long nvos_ioctl(struct file *filp, { NvOsMemRangeParams *p; + if (filp->f_op != &knvos_fops) + return -EACCES; + p = NvOsAlloc( sizeof(NvOsMemRangeParams) ); if( !p ) { |