Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/dtor/input
[pandora-kernel.git] / arch / mips / kernel / rtlx.c
index d92c48e..e14ae09 100644 (file)
@@ -53,7 +53,7 @@ static char module_name[] = "rtlx";
 static struct chan_waitqueues {
        wait_queue_head_t rt_queue;
        wait_queue_head_t lx_queue;
-       int in_open;
+       atomic_t in_open;
 } channel_wqs[RTLX_CHANNELS];
 
 static struct irqaction irq;
@@ -146,110 +146,80 @@ static void stopping(int vpe)
 
 int rtlx_open(int index, int can_sleep)
 {
-       int ret;
-       struct rtlx_channel *chan;
        volatile struct rtlx_info **p;
+       struct rtlx_channel *chan;
+       enum rtlx_state state;
+       int ret = 0;
 
        if (index >= RTLX_CHANNELS) {
                printk(KERN_DEBUG "rtlx_open index out of range\n");
                return -ENOSYS;
        }
 
-       if (channel_wqs[index].in_open) {
-               printk(KERN_DEBUG "rtlx_open channel %d already opened\n", index);
-               return -EBUSY;
+       if (atomic_inc_return(&channel_wqs[index].in_open) > 1) {
+               printk(KERN_DEBUG "rtlx_open channel %d already opened\n",
+                      index);
+               ret = -EBUSY;
+               goto out_fail;
        }
 
-       channel_wqs[index].in_open++;
-
        if (rtlx == NULL) {
                if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) {
                        if (can_sleep) {
-                               DECLARE_WAITQUEUE(wait, current);
-
-                               /* go to sleep */
-                               add_wait_queue(&channel_wqs[index].lx_queue, &wait);
-
-                               set_current_state(TASK_INTERRUPTIBLE);
-                               while ((p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) {
-                                       schedule();
-                                       set_current_state(TASK_INTERRUPTIBLE);
-                               }
-
-                               set_current_state(TASK_RUNNING);
-                               remove_wait_queue(&channel_wqs[index].lx_queue, &wait);
-
-                               /* back running */
+                               __wait_event_interruptible(channel_wqs[index].lx_queue,
+                                                          (p = vpe_get_shared(RTLX_TARG_VPE)),
+                                                          ret);
+                               if (ret)
+                                       goto out_fail;
                        } else {
-                               printk( KERN_DEBUG "No SP program loaded, and device "
+                               printk(KERN_DEBUG "No SP program loaded, and device "
                                        "opened with O_NONBLOCK\n");
-                               channel_wqs[index].in_open = 0;
-                               return -ENOSYS;
+                               ret = -ENOSYS;
+                               goto out_fail;
                        }
                }
 
                if (*p == NULL) {
                        if (can_sleep) {
-                               DECLARE_WAITQUEUE(wait, current);
-
-                               /* go to sleep */
-                               add_wait_queue(&channel_wqs[index].lx_queue, &wait);
-
-                               set_current_state(TASK_INTERRUPTIBLE);
-                               while (*p == NULL) {
-                                       schedule();
-
-                                       /* reset task state to interruptable otherwise
-                                          we'll whizz round here like a very fast loopy
-                                          thing. schedule() appears to return with state
-                                          set to TASK_RUNNING.
-
-                                          If the loaded SP program, for whatever reason,
-                                          doesn't set up the shared structure *p will never
-                                          become true. So whoever connected to either /dev/rt?
-                                          or if it was kspd, will then take up rather a lot of
-                                          processor cycles.
-                                       */
-
-                                       set_current_state(TASK_INTERRUPTIBLE);
-                               }
-
-                               set_current_state(TASK_RUNNING);
-                               remove_wait_queue(&channel_wqs[index].lx_queue, &wait);
-
-                               /* back running */
-                       }
-                       else {
+                               __wait_event_interruptible(channel_wqs[index].lx_queue,
+                                                          *p != NULL,
+                                                          ret);
+                               if (ret)
+                                       goto out_fail;
+                       } else {
                                printk(" *vpe_get_shared is NULL. "
                                       "Has an SP program been loaded?\n");
-                               channel_wqs[index].in_open = 0;
-                               return -ENOSYS;
+                               ret = -ENOSYS;
+                               goto out_fail;
                        }
                }
 
                if ((unsigned int)*p < KSEG0) {
                        printk(KERN_WARNING "vpe_get_shared returned an invalid pointer "
                               "maybe an error code %d\n", (int)*p);
-                       channel_wqs[index].in_open = 0;
-                       return -ENOSYS;
+                       ret = -ENOSYS;
+                       goto out_fail;
                }
 
-               if ((ret = rtlx_init(*p)) < 0) {
-                       channel_wqs[index].in_open = 0;
-                       return ret;
-               }
+               if ((ret = rtlx_init(*p)) < 0)
+                       goto out_ret;
        }
 
        chan = &rtlx->channel[index];
 
-       if (chan->lx_state == RTLX_STATE_OPENED) {
-               channel_wqs[index].in_open = 0;
-               return -EBUSY;
-       }
+       state = xchg(&chan->lx_state, RTLX_STATE_OPENED);
+       if (state == RTLX_STATE_OPENED) {
+               ret = -EBUSY;
+               goto out_fail;
+       }
 
-       chan->lx_state = RTLX_STATE_OPENED;
-       channel_wqs[index].in_open = 0;
-       return 0;
+out_fail:
+       smp_mb();
+       atomic_dec(&channel_wqs[index].in_open);
+       smp_mb();
+
+out_ret:
+       return ret;
 }
 
 int rtlx_release(int index)
@@ -270,30 +240,17 @@ unsigned int rtlx_read_poll(int index, int can_sleep)
        /* data available to read? */
        if (chan->lx_read == chan->lx_write) {
                if (can_sleep) {
-                       DECLARE_WAITQUEUE(wait, current);
-
-                       /* go to sleep */
-                       add_wait_queue(&channel_wqs[index].lx_queue, &wait);
-
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       while (chan->lx_read == chan->lx_write) {
-                               schedule();
+                       int ret = 0;
 
-                               set_current_state(TASK_INTERRUPTIBLE);
+                       __wait_event_interruptible(channel_wqs[index].lx_queue,
+                                                  chan->lx_read != chan->lx_write || sp_stopping,
+                                                  ret);
+                       if (ret)
+                               return ret;
 
-                               if (sp_stopping) {
-                                       set_current_state(TASK_RUNNING);
-                                       remove_wait_queue(&channel_wqs[index].lx_queue, &wait);
-                                       return 0;
-                               }
-                       }
-
-                       set_current_state(TASK_RUNNING);
-                       remove_wait_queue(&channel_wqs[index].lx_queue, &wait);
-
-                       /* back running */
-               }
-               else
+                       if (sp_stopping)
+                               return 0;
+               } else
                        return 0;
        }
 
@@ -454,25 +411,22 @@ static ssize_t file_write(struct file *file, const char __user * buffer,
 {
        int minor;
        struct rtlx_channel *rt;
-       DECLARE_WAITQUEUE(wait, current);
 
        minor = iminor(file->f_path.dentry->d_inode);
        rt = &rtlx->channel[minor];
 
        /* any space left... */
        if (!rtlx_write_poll(minor)) {
+               int ret = 0;
 
                if (file->f_flags & O_NONBLOCK)
                        return -EAGAIN;
 
-               add_wait_queue(&channel_wqs[minor].rt_queue, &wait);
-               set_current_state(TASK_INTERRUPTIBLE);
-
-               while (!rtlx_write_poll(minor))
-                       schedule();
-
-               set_current_state(TASK_RUNNING);
-               remove_wait_queue(&channel_wqs[minor].rt_queue, &wait);
+               __wait_event_interruptible(channel_wqs[minor].rt_queue,
+                                          rtlx_write_poll(minor),
+                                          ret);
+               if (ret)
+                       return ret;
        }
 
        return rtlx_write(minor, (void *)buffer, count, 1);
@@ -513,7 +467,7 @@ static int rtlx_module_init(void)
        for (i = 0; i < RTLX_CHANNELS; i++) {
                init_waitqueue_head(&channel_wqs[i].rt_queue);
                init_waitqueue_head(&channel_wqs[i].lx_queue);
-               channel_wqs[i].in_open = 0;
+               atomic_set(&channel_wqs[i].in_open, 0);
 
                dev = device_create(mt_class, NULL, MKDEV(major, i),
                                    "%s%d", module_name, i);