summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVarun Wadekar <vwadekar@nvidia.com>2010-06-01 11:58:56 +0530
committerGary King <gking@nvidia.com>2010-06-07 12:48:41 -0700
commit21cfe0729a6ad3a9858371b388d3263889fd5a28 (patch)
treeddecb5cfb8aa57b4011f7695cedaf1a694a41c88
parent82241cc9a50a44eea7b0ae033ac8e9de024a40c0 (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.c166
-rw-r--r--arch/arm/mach-tegra/nvos_user.c50
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 )
{