[PATCH] files: lock-free fd look-up
[linux-2.6.git] / fs / select.c
index 25b1ccac2f2cdcc73cbc42135837bfed752e7f9e..f10a10317d5494e0eb4995d9c016113a093f34ee 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/personality.h> /* for STICKY_TIMEOUTS */
 #include <linux/file.h>
 #include <linux/fs.h>
+#include <linux/rcupdate.h>
 
 #include <asm/uaccess.h>
 
@@ -55,7 +56,8 @@ struct poll_table_page {
  * as all select/poll functions have to call it to add an entry to the
  * poll table.
  */
-void __pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p);
+static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
+                      poll_table *p);
 
 void poll_initwait(struct poll_wqueues *pwq)
 {
@@ -87,7 +89,8 @@ void poll_freewait(struct poll_wqueues *pwq)
 
 EXPORT_SYMBOL(poll_freewait);
 
-void __pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *_p)
+static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
+                      poll_table *_p)
 {
        struct poll_wqueues *p = container_of(_p, struct poll_wqueues, pt);
        struct poll_table_page *table = p->table;
@@ -130,11 +133,13 @@ static int max_select_fd(unsigned long n, fd_set_bits *fds)
        unsigned long *open_fds;
        unsigned long set;
        int max;
+       struct fdtable *fdt;
 
        /* handle last in-complete long-word first */
        set = ~(~0UL << (n & (__NFDBITS-1)));
        n /= __NFDBITS;
-       open_fds = current->files->open_fds->fds_bits+n;
+       fdt = files_fdtable(current->files);
+       open_fds = fdt->open_fds->fds_bits+n;
        max = 0;
        if (set) {
                set &= BITS(fds, n);
@@ -181,9 +186,9 @@ int do_select(int n, fd_set_bits *fds, long *timeout)
        int retval, i;
        long __timeout = *timeout;
 
-       spin_lock(&current->files->file_lock);
+       rcu_read_lock();
        retval = max_select_fd(n, fds);
-       spin_unlock(&current->files->file_lock);
+       rcu_read_unlock();
 
        if (retval < 0)
                return retval;
@@ -297,6 +302,7 @@ sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, s
        char *bits;
        long timeout;
        int ret, size, max_fdset;
+       struct fdtable *fdt;
 
        timeout = MAX_SCHEDULE_TIMEOUT;
        if (tvp) {
@@ -324,7 +330,10 @@ sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, s
                goto out_nofds;
 
        /* max_fdset can increase, so grab it once to avoid race */
-       max_fdset = current->files->max_fdset;
+       rcu_read_lock();
+       fdt = files_fdtable(current->files);
+       max_fdset = fdt->max_fdset;
+       rcu_read_unlock();
        if (n > max_fdset)
                n = max_fdset;
 
@@ -462,9 +471,15 @@ asmlinkage long sys_poll(struct pollfd __user * ufds, unsigned int nfds, long ti
        unsigned int i;
        struct poll_list *head;
        struct poll_list *walk;
+       struct fdtable *fdt;
+       int max_fdset;
 
        /* Do a sanity check on nfds ... */
-       if (nfds > current->files->max_fdset && nfds > OPEN_MAX)
+       rcu_read_lock();
+       fdt = files_fdtable(current->files);
+       max_fdset = fdt->max_fdset;
+       rcu_read_unlock();
+       if (nfds > max_fdset && nfds > OPEN_MAX)
                return -EINVAL;
 
        if (timeout) {