summaryrefslogtreecommitdiffstats
path: root/fs/select.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-02-15 02:15:32 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-02-15 02:15:32 +0000
commit86464aed71025541805e7b1515541aee89879e33 (patch)
treee01a457a4912a8553bc65524aa3125d51f29f810 /fs/select.c
parent88f99939ecc6a95a79614574cb7d95ffccfc3466 (diff)
Merge with Linux 2.2.1.
Diffstat (limited to 'fs/select.c')
-rw-r--r--fs/select.c202
1 files changed, 127 insertions, 75 deletions
diff --git a/fs/select.c b/fs/select.c
index 38742c84e..a89425503 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -41,28 +41,66 @@
static void free_wait(poll_table * p)
{
- struct poll_table_entry * entry = p->entry + p->nr;
+ struct poll_table_entry * entry;
+ poll_table *old;
+
+ while (p) {
+ entry = p->entry + p->nr;
+ while (p->nr > 0) {
+ p->nr--;
+ entry--;
+ remove_wait_queue(entry->wait_address,&entry->wait);
+ fput(entry->filp);
+ }
+ old = p;
+ p = p->next;
+ free_page((unsigned long) old);
+ }
+}
- while (p->nr > 0) {
- p->nr--;
- entry--;
- remove_wait_queue(entry->wait_address,&entry->wait);
- fput(entry->filp);
+void __pollwait(struct file * filp, struct wait_queue ** wait_address, poll_table *p)
+{
+ for (;;) {
+ if (p->nr < __MAX_POLL_TABLE_ENTRIES) {
+ struct poll_table_entry * entry;
+ok_table:
+ entry = p->entry + p->nr;
+ entry->filp = filp;
+ filp->f_count++;
+ entry->wait_address = wait_address;
+ entry->wait.task = current;
+ entry->wait.next = NULL;
+ add_wait_queue(wait_address,&entry->wait);
+ p->nr++;
+ return;
+ }
+ if (p->next == NULL) {
+ poll_table *tmp = (poll_table *) __get_free_page(GFP_KERNEL);
+ if (!tmp)
+ return;
+ tmp->nr = 0;
+ tmp->entry = (struct poll_table_entry *)(tmp + 1);
+ tmp->next = NULL;
+ p->next = tmp;
+ p = tmp;
+ goto ok_table;
+ }
+ p = p->next;
}
}
-#define __IN(in) (in)
-#define __OUT(in) (in + sizeof(kernel_fd_set)/sizeof(unsigned long))
-#define __EX(in) (in + 2*sizeof(kernel_fd_set)/sizeof(unsigned long))
-#define __RES_IN(in) (in + 3*sizeof(kernel_fd_set)/sizeof(unsigned long))
-#define __RES_OUT(in) (in + 4*sizeof(kernel_fd_set)/sizeof(unsigned long))
-#define __RES_EX(in) (in + 5*sizeof(kernel_fd_set)/sizeof(unsigned long))
+#define __IN(fds, n) (fds->in + n)
+#define __OUT(fds, n) (fds->out + n)
+#define __EX(fds, n) (fds->ex + n)
+#define __RES_IN(fds, n) (fds->res_in + n)
+#define __RES_OUT(fds, n) (fds->res_out + n)
+#define __RES_EX(fds, n) (fds->res_ex + n)
-#define BITS(in) (*__IN(in)|*__OUT(in)|*__EX(in))
+#define BITS(fds, n) (*__IN(fds, n)|*__OUT(fds, n)|*__EX(fds, n))
-static int max_select_fd(unsigned long n, fd_set_buffer *fds)
+static int max_select_fd(unsigned long n, fd_set_bits *fds)
{
- unsigned long *open_fds, *in;
+ unsigned long *open_fds;
unsigned long set;
int max;
@@ -70,10 +108,9 @@ static int max_select_fd(unsigned long n, fd_set_buffer *fds)
set = ~(~0UL << (n & (__NFDBITS-1)));
n /= __NFDBITS;
open_fds = current->files->open_fds.fds_bits+n;
- in = fds->in+n;
max = 0;
if (set) {
- set &= BITS(in);
+ set &= BITS(fds, n);
if (set) {
if (!(set & ~*open_fds))
goto get_max;
@@ -81,10 +118,9 @@ static int max_select_fd(unsigned long n, fd_set_buffer *fds)
}
}
while (n) {
- in--;
open_fds--;
n--;
- set = BITS(in);
+ set = BITS(fds, n);
if (!set)
continue;
if (set & ~*open_fds)
@@ -111,21 +147,22 @@ get_max:
#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
#define POLLEX_SET (POLLPRI)
-int do_select(int n, fd_set_buffer *fds, long *timeout)
+int do_select(int n, fd_set_bits *fds, long *timeout)
{
- poll_table wait_table, *wait;
- int retval, i;
+ poll_table *wait_table, *wait;
+ int retval, i, off;
long __timeout = *timeout;
- wait = NULL;
+ wait = wait_table = NULL;
if (__timeout) {
- struct poll_table_entry *entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
- if (!entry)
+ wait_table = (poll_table *) __get_free_page(GFP_KERNEL);
+ if (!wait_table)
return -ENOMEM;
- wait_table.nr = 0;
- wait_table.entry = entry;
- wait = &wait_table;
+ wait_table->nr = 0;
+ wait_table->entry = (struct poll_table_entry *)(wait_table + 1);
+ wait_table->next = NULL;
+ wait = wait_table;
}
lock_kernel();
@@ -139,11 +176,11 @@ int do_select(int n, fd_set_buffer *fds, long *timeout)
current->state = TASK_INTERRUPTIBLE;
for (i = 0 ; i < n; i++) {
unsigned long bit = BIT(i);
- unsigned long *in = MEM(i,fds->in);
unsigned long mask;
struct file *file;
- if (!(bit & BITS(in)))
+ off = i / __NFDBITS;
+ if (!(bit & BITS(fds, off)))
continue;
/*
* The poll_wait routine will increment f_count if
@@ -157,18 +194,18 @@ int do_select(int n, fd_set_buffer *fds, long *timeout)
if (file->f_op && file->f_op->poll)
mask = file->f_op->poll(file, wait);
}
- if ((mask & POLLIN_SET) && ISSET(bit, __IN(in))) {
- SET(bit, __RES_IN(in));
+ if ((mask & POLLIN_SET) && ISSET(bit, __IN(fds,off))) {
+ SET(bit, __RES_IN(fds,off));
retval++;
wait = NULL;
}
- if ((mask & POLLOUT_SET) && ISSET(bit, __OUT(in))) {
- SET(bit, __RES_OUT(in));
+ if ((mask & POLLOUT_SET) && ISSET(bit, __OUT(fds,off))) {
+ SET(bit, __RES_OUT(fds,off));
retval++;
wait = NULL;
}
- if ((mask & POLLEX_SET) && ISSET(bit, __EX(in))) {
- SET(bit, __RES_EX(in));
+ if ((mask & POLLEX_SET) && ISSET(bit, __EX(fds,off))) {
+ SET(bit, __RES_EX(fds,off));
retval++;
wait = NULL;
}
@@ -181,10 +218,8 @@ int do_select(int n, fd_set_buffer *fds, long *timeout)
current->state = TASK_RUNNING;
out:
- if (*timeout) {
- free_wait(&wait_table);
- free_page((unsigned long) wait_table.entry);
- }
+ if (*timeout)
+ free_wait(wait_table);
/*
* Up-to-date the caller timeout.
@@ -208,9 +243,10 @@ out:
asmlinkage int
sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
{
- fd_set_buffer *fds;
+ fd_set_bits fds;
+ char *bits;
long timeout;
- int ret;
+ int ret, size;
timeout = MAX_SCHEDULE_TIMEOUT;
if (tvp) {
@@ -221,30 +257,45 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
|| (ret = __get_user(usec, &tvp->tv_usec)))
goto out_nofds;
+ 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 || n > KFDS_NR)
+ goto out_nofds;
+ /*
+ * 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;
- fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL);
- if (!fds)
+ size = FDS_BYTES(n);
+ bits = kmalloc(6 * size, GFP_KERNEL);
+ if (!bits)
goto out_nofds;
- ret = -EINVAL;
- if (n < 0)
- goto out;
- if (n > KFDS_NR)
- n = KFDS_NR;
- 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)))
+ 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);
+ 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);
+ ret = do_select(n, &fds, &timeout);
if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
time_t sec = 0, usec = 0;
@@ -266,12 +317,12 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
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);
+ 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:
- free_page((unsigned long) fds);
+ kfree(bits);
out_nofds:
return ret;
}
@@ -323,7 +374,7 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout)
{
int i, fdcount, err, size;
struct pollfd * fds, *fds1;
- poll_table wait_table, *wait = NULL;
+ poll_table *wait_table = NULL, *wait = NULL;
lock_kernel();
/* Do a sanity check on nfds ... */
@@ -331,20 +382,23 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout)
if (nfds > NR_OPEN)
goto out;
- if (timeout < 0)
- timeout = MAX_SCHEDULE_TIMEOUT;
- else if (timeout)
- timeout = (timeout*HZ+999)/1000+1;
+ if (timeout) {
+ /* Carefula about overflow in the intermediate values */
+ if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ)
+ timeout = (unsigned long)(timeout*HZ+999)/1000+1;
+ else /* Negative or overflow */
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ }
err = -ENOMEM;
if (timeout) {
- struct poll_table_entry *entry;
- entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
- if (!entry)
+ wait_table = (poll_table *) __get_free_page(GFP_KERNEL);
+ if (!wait_table)
goto out;
- wait_table.nr = 0;
- wait_table.entry = entry;
- wait = &wait_table;
+ wait_table->nr = 0;
+ wait_table->entry = (struct poll_table_entry *)(wait_table + 1);
+ wait_table->next = NULL;
+ wait = wait_table;
}
size = nfds * sizeof(struct pollfd);
@@ -371,10 +425,8 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout)
out_fds:
kfree(fds);
out:
- if (wait) {
- free_wait(&wait_table);
- free_page((unsigned long) wait->entry);
- }
+ if (wait)
+ free_wait(wait_table);
unlock_kernel();
return err;
}