summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/mips64/kernel/linux32.c102
-rw-r--r--arch/mips64/kernel/scall_o32.S2
2 files changed, 103 insertions, 1 deletions
diff --git a/arch/mips64/kernel/linux32.c b/arch/mips64/kernel/linux32.c
index 0ec965872..408aa340c 100644
--- a/arch/mips64/kernel/linux32.c
+++ b/arch/mips64/kernel/linux32.c
@@ -16,6 +16,7 @@
#include <linux/resource.h>
#include <linux/highmem.h>
#include <linux/time.h>
+#include <linux/poll.h>
#include <asm/uaccess.h>
#include <asm/mman.h>
@@ -938,3 +939,104 @@ extern asmlinkage int sys32_llseek(unsigned int fd, unsigned int offset_high,
{
return sys_llseek(fd, offset_high, offset_low, result, origin);
}
+
+/*
+ * We can actually return ERESTARTSYS instead of EINTR, but I'd
+ * like to be certain this leads to no problems. So I return
+ * EINTR just for safety.
+ *
+ * Update: ERESTARTSYS breaks at least the xview clock binary, so
+ * I'm trying ERESTARTNOHAND which restart only when you want to.
+ */
+#define MAX_SELECT_SECONDS \
+ ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
+#define ROUND_UP(x,y) (((x)+(y)-1)/(y))
+
+asmlinkage int
+sys32_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval32 *tvp32)
+{
+ fd_set_bits fds;
+ char *bits;
+ long timeout;
+ int ret, size;
+
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ if (tvp32) {
+ time_t sec, usec;
+
+ get_user(sec, &tvp32->tv_sec);
+ get_user(usec, &tvp32->tv_usec);
+
+ ret = -EINVAL;
+ if (sec < 0 || usec < 0)
+ goto out_nofds;
+
+ if ((unsigned long) sec < MAX_SELECT_SECONDS) {
+ timeout = ROUND_UP(usec, 1000000/HZ);
+ timeout += sec * (unsigned long) HZ;
+ }
+ }
+
+ ret = -EINVAL;
+ if (n < 0)
+ goto out_nofds;
+
+ if (n > current->files->max_fdset)
+ n = current->files->max_fdset;
+
+ /*
+ * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
+ * since we used fdset we need to allocate memory in units of
+ * long-words.
+ */
+ ret = -ENOMEM;
+ size = FDS_BYTES(n);
+ bits = kmalloc(6 * size, GFP_KERNEL);
+ if (!bits)
+ goto out_nofds;
+ fds.in = (unsigned long *) bits;
+ fds.out = (unsigned long *) (bits + size);
+ fds.ex = (unsigned long *) (bits + 2*size);
+ fds.res_in = (unsigned long *) (bits + 3*size);
+ fds.res_out = (unsigned long *) (bits + 4*size);
+ fds.res_ex = (unsigned long *) (bits + 5*size);
+
+ if ((ret = get_fd_set(n, inp, fds.in)) ||
+ (ret = get_fd_set(n, outp, fds.out)) ||
+ (ret = get_fd_set(n, exp, fds.ex)))
+ goto out;
+ zero_fd_set(n, fds.res_in);
+ zero_fd_set(n, fds.res_out);
+ zero_fd_set(n, fds.res_ex);
+
+ ret = do_select(n, &fds, &timeout);
+
+ if (tvp32 && !(current->personality & STICKY_TIMEOUTS)) {
+ time_t sec = 0, usec = 0;
+ if (timeout) {
+ sec = timeout / HZ;
+ usec = timeout % HZ;
+ usec *= (1000000/HZ);
+ }
+ put_user(sec, (int *)&tvp32->tv_sec);
+ put_user(usec, (int *)&tvp32->tv_usec);
+ }
+
+ if (ret < 0)
+ goto out;
+ if (!ret) {
+ ret = -ERESTARTNOHAND;
+ if (signal_pending(current))
+ goto out;
+ ret = 0;
+ }
+
+ set_fd_set(n, inp, fds.res_in);
+ set_fd_set(n, outp, fds.res_out);
+ set_fd_set(n, exp, fds.res_ex);
+
+out:
+ kfree(bits);
+out_nofds:
+ return ret;
+}
diff --git a/arch/mips64/kernel/scall_o32.S b/arch/mips64/kernel/scall_o32.S
index 847474ff3..90b388083 100644
--- a/arch/mips64/kernel/scall_o32.S
+++ b/arch/mips64/kernel/scall_o32.S
@@ -344,7 +344,7 @@ illegal_syscall:
sys sys_setfsgid 1
sys sys32_llseek 5 /* 4140 */
sys sys32_getdents 3
- sys sys_select 5
+ sys sys32_select 5
sys sys_flock 2
sys sys_msync 3
sys sys_readv 3 /* 4145 */