From 9e34682026572f07328208f7d2b2c611d2001844 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 15 Mar 2007 17:08:28 +0000 Subject: [MIPS] RTLX: Don't use volatile; it's fragile. Signed-off-by: Ralf Baechle --- arch/mips/kernel/rtlx.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'arch/mips/kernel/rtlx.c') diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index e14ae09eda2b..16d3fdecf3ea 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c @@ -146,7 +146,7 @@ static void stopping(int vpe) int rtlx_open(int index, int can_sleep) { - volatile struct rtlx_info **p; + struct rtlx_info **p; struct rtlx_channel *chan; enum rtlx_state state; int ret = 0; @@ -179,13 +179,24 @@ int rtlx_open(int index, int can_sleep) } } + smp_rmb(); if (*p == NULL) { if (can_sleep) { - __wait_event_interruptible(channel_wqs[index].lx_queue, - *p != NULL, - ret); - if (ret) + 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; + } + finish_wait(&channel_wqs[index].lx_queue, &wait); } else { printk(" *vpe_get_shared is NULL. " "Has an SP program been loaded?\n"); -- cgit From 61dcc6f4d9ed5db71f4f0be9026bdd09f1a7dc06 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 15 Mar 2007 17:10:16 +0000 Subject: [MIPS] RTLX: Harden against compiler reordering and optimization. RTLX communication is based on lock-free shared memory buffers. It happened to be working by luck so far but relies on the optimizer doing certain optimizations but no reordering. Fixed by inserting proper barriers in rtlx_read and rtlx_write, and careful pointer dereferencing. Signed-off-by: Ralf Baechle --- arch/mips/kernel/rtlx.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) (limited to 'arch/mips/kernel/rtlx.c') diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index 16d3fdecf3ea..0441c7c1e44c 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c @@ -306,7 +306,7 @@ static inline void copy_from(void *dst, void *src, size_t count, int user) 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; if (rtlx == NULL) @@ -314,23 +314,26 @@ ssize_t rtlx_read(int index, void *buff, size_t count, int user) lx = &rtlx->channel[index]; + 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); + copy_to(buff, lx->lx_buffer + lx->lx_read, fl, user); /* 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) + copy_to(buff + fl, lx->lx_buffer, count - fl, user); - /* 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(); return count; } @@ -338,6 +341,7 @@ ssize_t rtlx_read(int index, void *buff, size_t count, int user) ssize_t rtlx_write(int index, void *buffer, size_t count, int user) { struct rtlx_channel *rt; + size_t rt_read; size_t fl; if (rtlx == NULL) @@ -345,24 +349,27 @@ ssize_t rtlx_write(int index, void *buffer, size_t count, int user) rt = &rtlx->channel[index]; + 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); + copy_from(rt->rt_buffer + rt->rt_write, buffer, fl, user); /* 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) + copy_from(rt->rt_buffer, buffer + fl, count - fl, user); - rt->rt_write += count; - rt->rt_write %= rt->buffer_size; + smp_wmb(); + rt->rt_write = (rt->rt_write + count) % rt->buffer_size; + smp_wmb(); - return(count); + return count; } -- cgit From bc4809e939b91c9642f1ddaea732e2d432ee6af6 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 15 Mar 2007 17:13:47 +0000 Subject: [MIPS] RTLX: Protect rtlx_{read,write} with mutex. Signed-off-by: Ralf Baechle --- arch/mips/kernel/rtlx.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'arch/mips/kernel/rtlx.c') diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index 0441c7c1e44c..745649e15adc 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c @@ -54,6 +54,7 @@ static struct chan_waitqueues { wait_queue_head_t rt_queue; wait_queue_head_t lx_queue; atomic_t in_open; + struct mutex mutex; } channel_wqs[RTLX_CHANNELS]; static struct irqaction irq; @@ -314,6 +315,7 @@ ssize_t rtlx_read(int index, void *buff, size_t count, int user) lx = &rtlx->channel[index]; + mutex_lock(&channel_wqs[index].mutex); smp_rmb(); lx_write = lx->lx_write; @@ -334,6 +336,7 @@ ssize_t rtlx_read(int index, void *buff, size_t count, int user) smp_wmb(); lx->lx_read = (lx->lx_read + count) % lx->buffer_size; smp_wmb(); + mutex_unlock(&channel_wqs[index].mutex); return count; } @@ -349,6 +352,7 @@ 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; @@ -368,6 +372,7 @@ ssize_t rtlx_write(int index, void *buffer, size_t count, int user) smp_wmb(); rt->rt_write = (rt->rt_write + count) % rt->buffer_size; smp_wmb(); + mutex_unlock(&channel_wqs[index].mutex); return count; } @@ -486,6 +491,7 @@ static int rtlx_module_init(void) init_waitqueue_head(&channel_wqs[i].rt_queue); init_waitqueue_head(&channel_wqs[i].lx_queue); 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); -- cgit From 46230aa6ea1671690e3e5efa2a961fc0745fe9b5 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Fri, 16 Mar 2007 12:16:27 +0000 Subject: [MIPS] RTLX: Handle copy_*_user return values. Signed-off-by: Ralf Baechle --- arch/mips/kernel/kspd.c | 18 ++++++++++++++---- arch/mips/kernel/rtlx.c | 46 +++++++++++++++++++++------------------------- include/asm-mips/rtlx.h | 4 ++-- 3 files changed, 37 insertions(+), 31 deletions(-) (limited to 'arch/mips/kernel/rtlx.c') diff --git a/arch/mips/kernel/kspd.c b/arch/mips/kernel/kspd.c index 241ee7a2906e..29eadd404fa5 100644 --- a/arch/mips/kernel/kspd.c +++ b/arch/mips/kernel/kspd.c @@ -191,6 +191,8 @@ void sp_work_handle_request(void) struct mtsp_syscall_generic generic; struct mtsp_syscall_ret ret; struct kspd_notifications *n; + unsigned long written; + mm_segment_t old_fs; struct timeval tv; struct timezone tz; int cmd; @@ -201,7 +203,11 @@ void sp_work_handle_request(void) ret.retval = -1; - if (!rtlx_read(RTLX_CHANNEL_SYSIO, &sc, sizeof(struct mtsp_syscall), 0)) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + + if (!rtlx_read(RTLX_CHANNEL_SYSIO, &sc, sizeof(struct mtsp_syscall))) { + set_fs(old_fs); printk(KERN_ERR "Expected request but nothing to read\n"); return; } @@ -209,7 +215,8 @@ void sp_work_handle_request(void) size = sc.size; if (size) { - if (!rtlx_read(RTLX_CHANNEL_SYSIO, &generic, size, 0)) { + if (!rtlx_read(RTLX_CHANNEL_SYSIO, &generic, size)) { + set_fs(old_fs); printk(KERN_ERR "Expected request but nothing to read\n"); return; } @@ -282,8 +289,11 @@ void sp_work_handle_request(void) if (vpe_getuid(SP_VPE)) sp_setfsuidgid( 0, 0); - if ((rtlx_write(RTLX_CHANNEL_SYSIO, &ret, sizeof(struct mtsp_syscall_ret), 0)) - < sizeof(struct mtsp_syscall_ret)) + old_fs = get_fs(); + set_fs(KERNEL_DS); + written = rtlx_write(RTLX_CHANNEL_SYSIO, &ret, sizeof(ret)); + set_fs(old_fs); + if (written < sizeof(ret)) printk("KSPD: sp_work_handle_request failed to send to SP\n"); } diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index 745649e15adc..e6e3047151a6 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c @@ -289,26 +289,11 @@ 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) -{ - 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) +ssize_t rtlx_read(int index, void __user *buff, size_t count, int user) { size_t lx_write, fl = 0L; struct rtlx_channel *lx; + unsigned long failed; if (rtlx == NULL) return -ENOSYS; @@ -327,11 +312,16 @@ ssize_t rtlx_read(int index, void *buff, size_t count, int user) /* then how much from the read pointer onwards */ 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); + failed = copy_to_user(buff + fl, lx->lx_buffer, count - fl); + +out: + count -= failed; smp_wmb(); lx->lx_read = (lx->lx_read + count) % lx->buffer_size; @@ -341,7 +331,7 @@ ssize_t rtlx_read(int index, void *buff, size_t count, int user) 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, int user) { struct rtlx_channel *rt; size_t rt_read; @@ -363,11 +353,17 @@ ssize_t rtlx_write(int index, void *buffer, size_t count, int user) /* 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); + } + +out: + count -= cailed; smp_wmb(); rt->rt_write = (rt->rt_write + count) % rt->buffer_size; @@ -426,7 +422,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, @@ -452,7 +448,7 @@ static ssize_t file_write(struct file *file, const char __user * buffer, return ret; } - return rtlx_write(minor, (void *)buffer, count, 1); + return rtlx_write(minor, buffer, count); } static const struct file_operations rtlx_fops = { diff --git a/include/asm-mips/rtlx.h b/include/asm-mips/rtlx.h index 59162f74a798..65778c890a62 100644 --- a/include/asm-mips/rtlx.h +++ b/include/asm-mips/rtlx.h @@ -23,8 +23,8 @@ extern int rtlx_open(int index, int can_sleep); extern int rtlx_release(int index); -extern ssize_t rtlx_read(int index, void *buff, size_t count, int user); -extern ssize_t rtlx_write(int index, void *buffer, size_t count, int user); +extern ssize_t rtlx_read(int index, void __user *buff, size_t count); +extern ssize_t rtlx_write(int index, const void __user *buffer, size_t count); extern unsigned int rtlx_read_poll(int index, int can_sleep); extern unsigned int rtlx_write_poll(int index); -- cgit