diff options
Diffstat (limited to 'arch/mips/kernel/rtlx.c')
-rw-r--r-- | arch/mips/kernel/rtlx.c | 253 |
1 files changed, 114 insertions, 139 deletions
diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index d92c48e0d7a6..bfc8ca168f83 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c @@ -53,7 +53,8 @@ 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; + struct mutex mutex; } channel_wqs[RTLX_CHANNELS]; static struct irqaction irq; @@ -146,110 +147,91 @@ static void stopping(int vpe) int rtlx_open(int index, int can_sleep) { - int ret; + struct rtlx_info **p; struct rtlx_channel *chan; - volatile struct rtlx_info **p; + 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; } } + smp_rmb(); 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); + DEFINE_WAIT(wait); + + for (;;) { + prepare_to_wait(&channel_wqs[index].lx_queue, &wait, TASK_INTERRUPTIBLE); + smp_rmb(); + if (*p != NULL) + break; + if (!signal_pending(current)) { + schedule(); + continue; + } + ret = -ERESTARTSYS; + goto out_fail; } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&channel_wqs[index].lx_queue, &wait); - - /* back running */ - } - else { + finish_wait(&channel_wqs[index].lx_queue, &wait); + } 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 +252,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; } @@ -320,56 +289,53 @@ unsigned int rtlx_write_poll(int index) return write_spacefree(chan->rt_read, chan->rt_write, chan->buffer_size); } -static inline void copy_to(void *dst, void *src, size_t count, int user) +ssize_t rtlx_read(int index, void __user *buff, size_t count) { - if (user) - copy_to_user(dst, src, count); - else - memcpy(dst, src, count); -} - -static inline void copy_from(void *dst, void *src, size_t count, int user) -{ - if (user) - copy_from_user(dst, src, count); - else - memcpy(dst, src, count); -} - -ssize_t rtlx_read(int index, void *buff, size_t count, int user) -{ - size_t fl = 0L; + size_t lx_write, fl = 0L; struct rtlx_channel *lx; + unsigned long failed; if (rtlx == NULL) return -ENOSYS; lx = &rtlx->channel[index]; + mutex_lock(&channel_wqs[index].mutex); + smp_rmb(); + lx_write = lx->lx_write; + /* find out how much in total */ count = min(count, - (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read) + (size_t)(lx_write + lx->buffer_size - lx->lx_read) % lx->buffer_size); /* then how much from the read pointer onwards */ - fl = min( count, (size_t)lx->buffer_size - lx->lx_read); + fl = min(count, (size_t)lx->buffer_size - lx->lx_read); - copy_to(buff, &lx->lx_buffer[lx->lx_read], fl, user); + failed = copy_to_user(buff, lx->lx_buffer + lx->lx_read, fl); + if (failed) + goto out; /* and if there is anything left at the beginning of the buffer */ - if ( count - fl ) - copy_to (buff + fl, lx->lx_buffer, count - fl, user); + if (count - fl) + failed = copy_to_user(buff + fl, lx->lx_buffer, count - fl); + +out: + count -= failed; - /* update the index */ - lx->lx_read += count; - lx->lx_read %= lx->buffer_size; + smp_wmb(); + lx->lx_read = (lx->lx_read + count) % lx->buffer_size; + smp_wmb(); + mutex_unlock(&channel_wqs[index].mutex); return count; } -ssize_t rtlx_write(int index, void *buffer, size_t count, int user) +ssize_t rtlx_write(int index, const void __user *buffer, size_t count) { struct rtlx_channel *rt; + unsigned long failed; + size_t rt_read; size_t fl; if (rtlx == NULL) @@ -377,24 +343,35 @@ ssize_t rtlx_write(int index, void *buffer, size_t count, int user) rt = &rtlx->channel[index]; + mutex_lock(&channel_wqs[index].mutex); + smp_rmb(); + rt_read = rt->rt_read; + /* total number of bytes to copy */ count = min(count, - (size_t)write_spacefree(rt->rt_read, rt->rt_write, - rt->buffer_size)); + (size_t)write_spacefree(rt_read, rt->rt_write, rt->buffer_size)); /* first bit from write pointer to the end of the buffer, or count */ fl = min(count, (size_t) rt->buffer_size - rt->rt_write); - copy_from (&rt->rt_buffer[rt->rt_write], buffer, fl, user); + failed = copy_from_user(rt->rt_buffer + rt->rt_write, buffer, fl); + if (failed) + goto out; /* if there's any left copy to the beginning of the buffer */ - if( count - fl ) - copy_from (rt->rt_buffer, buffer + fl, count - fl, user); + if (count - fl) { + failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl); + } - rt->rt_write += count; - rt->rt_write %= rt->buffer_size; +out: + count -= failed; - return(count); + smp_wmb(); + rt->rt_write = (rt->rt_write + count) % rt->buffer_size; + smp_wmb(); + mutex_unlock(&channel_wqs[index].mutex); + + return count; } @@ -446,7 +423,7 @@ static ssize_t file_read(struct file *file, char __user * buffer, size_t count, return 0; // -EAGAIN makes cat whinge } - return rtlx_read(minor, buffer, count, 1); + return rtlx_read(minor, buffer, count); } static ssize_t file_write(struct file *file, const char __user * buffer, @@ -454,28 +431,25 @@ 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); + return rtlx_write(minor, buffer, count); } static const struct file_operations rtlx_fops = { @@ -513,7 +487,8 @@ 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); + mutex_init(&channel_wqs[i].mutex); dev = device_create(mt_class, NULL, MKDEV(major, i), "%s%d", module_name, i); |