]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/file.c
sched: add debug checks to idle functions
[linux-2.6-omap-h63xx.git] / fs / file.c
index 857fa49e984ca025b36a653aced4af23d56f20b5..4c6f0ea12c419c52154558c271586b7887dabf13 100644 (file)
--- a/fs/file.c
+++ b/fs/file.c
@@ -12,6 +12,7 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/file.h>
+#include <linux/fdtable.h>
 #include <linux/bitops.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
@@ -24,6 +25,8 @@ struct fdtable_defer {
        struct fdtable *next;
 };
 
+int sysctl_nr_open __read_mostly = 1024*1024;
+
 /*
  * We use this list to defer free fdtables that have vmalloced
  * sets/arrays. By keeping a per-cpu list, we avoid having to embed
@@ -147,8 +150,16 @@ static struct fdtable * alloc_fdtable(unsigned int nr)
        nr /= (1024 / sizeof(struct file *));
        nr = roundup_pow_of_two(nr + 1);
        nr *= (1024 / sizeof(struct file *));
-       if (nr > NR_OPEN)
-               nr = NR_OPEN;
+       /*
+        * Note that this can drive nr *below* what we had passed if sysctl_nr_open
+        * had been set lower between the check in expand_files() and here.  Deal
+        * with that in caller, it's cheaper that way.
+        *
+        * We make sure that nr remains a multiple of BITS_PER_LONG - otherwise
+        * bitmaps handling below becomes unpleasant, to put it mildly...
+        */
+       if (unlikely(nr > sysctl_nr_open))
+               nr = ((sysctl_nr_open - 1) | (BITS_PER_LONG - 1)) + 1;
 
        fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL);
        if (!fdt)
@@ -196,6 +207,16 @@ static int expand_fdtable(struct files_struct *files, int nr)
        spin_lock(&files->file_lock);
        if (!new_fdt)
                return -ENOMEM;
+       /*
+        * extremely unlikely race - sysctl_nr_open decreased between the check in
+        * caller and alloc_fdtable().  Cheaper to catch it here...
+        */
+       if (unlikely(new_fdt->max_fds <= nr)) {
+               free_fdarr(new_fdt);
+               free_fdset(new_fdt);
+               kfree(new_fdt);
+               return -EMFILE;
+       }
        /*
         * Check again since another task may have expanded the fd table while
         * we dropped the lock
@@ -206,7 +227,7 @@ static int expand_fdtable(struct files_struct *files, int nr)
                copy_fdtable(new_fdt, cur_fdt);
                rcu_assign_pointer(files->fdt, new_fdt);
                if (cur_fdt->max_fds > NR_OPEN_DEFAULT)
-                       call_rcu(&cur_fdt->rcu, free_fdtable_rcu);
+                       free_fdtable(cur_fdt);
        } else {
                /* Somebody else expanded, so undo our attempt */
                free_fdarr(new_fdt);
@@ -233,7 +254,7 @@ int expand_files(struct files_struct *files, int nr)
        if (nr < fdt->max_fds)
                return 0;
        /* Can we expand? */
-       if (nr >= NR_OPEN)
+       if (nr >= sysctl_nr_open)
                return -EMFILE;
 
        /* All good, so we try */