Merge branch 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6
[pandora-kernel.git] / fs / file.c
index b3c6b82..51aef67 100644 (file)
--- a/fs/file.c
+++ b/fs/file.c
@@ -21,7 +21,6 @@
 struct fdtable_defer {
        spinlock_t lock;
        struct work_struct wq;
-       struct timer_list timer;
        struct fdtable *next;
 };
 
@@ -75,24 +74,10 @@ static void __free_fdtable(struct fdtable *fdt)
        kfree(fdt);
 }
 
-static void fdtable_timer(unsigned long data)
-{
-       struct fdtable_defer *fddef = (struct fdtable_defer *)data;
-
-       spin_lock(&fddef->lock);
-       /*
-        * If someone already emptied the queue return.
-        */
-       if (!fddef->next)
-               goto out;
-       if (!schedule_work(&fddef->wq))
-               mod_timer(&fddef->timer, 5);
-out:
-       spin_unlock(&fddef->lock);
-}
-
-static void free_fdtable_work(struct fdtable_defer *f)
+static void free_fdtable_work(struct work_struct *work)
 {
+       struct fdtable_defer *f =
+               container_of(work, struct fdtable_defer, wq);
        struct fdtable *fdt;
 
        spin_lock_bh(&f->lock);
@@ -142,13 +127,8 @@ static void free_fdtable_rcu(struct rcu_head *rcu)
                spin_lock(&fddef->lock);
                fdt->next = fddef->next;
                fddef->next = fdt;
-               /*
-                * vmallocs are handled from the workqueue context.
-                * If the per-cpu workqueue is running, then we
-                * defer work scheduling through a timer.
-                */
-               if (!schedule_work(&fddef->wq))
-                       mod_timer(&fddef->timer, 5);
+               /* vmallocs are handled from the workqueue context */
+               schedule_work(&fddef->wq);
                spin_unlock(&fddef->lock);
                put_cpu_var(fdtable_defer_list);
        }
@@ -281,90 +261,77 @@ static struct fdtable *alloc_fdtable(int nr)
 out2:
        nfds = fdt->max_fdset;
 out:
-       if (new_openset)
-               free_fdset(new_openset, nfds);
-       if (new_execset)
-               free_fdset(new_execset, nfds);
+       free_fdset(new_openset, nfds);
+       free_fdset(new_execset, nfds);
        kfree(fdt);
        return NULL;
 }
 
 /*
- * Expands the file descriptor table - it will allocate a new fdtable and
- * both fd array and fdset. It is expected to be called with the
- * files_lock held.
+ * Expand the file descriptor table.
+ * This function will allocate a new fdtable and both fd array and fdset, of
+ * the given size.
+ * Return <0 error code on error; 1 on successful completion.
+ * The files->file_lock should be held on entry, and will be held on exit.
  */
 static int expand_fdtable(struct files_struct *files, int nr)
        __releases(files->file_lock)
        __acquires(files->file_lock)
 {
-       int error = 0;
-       struct fdtable *fdt;
-       struct fdtable *nfdt = NULL;
+       struct fdtable *new_fdt, *cur_fdt;
 
        spin_unlock(&files->file_lock);
-       nfdt = alloc_fdtable(nr);
-       if (!nfdt) {
-               error = -ENOMEM;
-               spin_lock(&files->file_lock);
-               goto out;
-       }
-
+       new_fdt = alloc_fdtable(nr);
        spin_lock(&files->file_lock);
-       fdt = files_fdtable(files);
+       if (!new_fdt)
+               return -ENOMEM;
        /*
-        * Check again since another task may have expanded the
-        * fd table while we dropped the lock
+        * Check again since another task may have expanded the fd table while
+        * we dropped the lock
         */
-       if (nr >= fdt->max_fds || nr >= fdt->max_fdset) {
-               copy_fdtable(nfdt, fdt);
+       cur_fdt = files_fdtable(files);
+       if (nr >= cur_fdt->max_fds || nr >= cur_fdt->max_fdset) {
+               /* Continue as planned */
+               copy_fdtable(new_fdt, cur_fdt);
+               rcu_assign_pointer(files->fdt, new_fdt);
+               free_fdtable(cur_fdt);
        } else {
-               /* Somebody expanded while we dropped file_lock */
-               spin_unlock(&files->file_lock);
-               __free_fdtable(nfdt);
-               spin_lock(&files->file_lock);
-               goto out;
+               /* Somebody else expanded, so undo our attempt */
+               __free_fdtable(new_fdt);
        }
-       rcu_assign_pointer(files->fdt, nfdt);
-       free_fdtable(fdt);
-out:
-       return error;
+       return 1;
 }
 
 /*
  * Expand files.
- * Return <0 on error; 0 nothing done; 1 files expanded, we may have blocked.
- * Should be called with the files->file_lock spinlock held for write.
+ * This function will expand the file structures, if the requested size exceeds
+ * the current capacity and there is room for expansion.
+ * Return <0 error code on error; 0 when nothing done; 1 when files were
+ * expanded and execution may have blocked.
+ * The files->file_lock should be held on entry, and will be held on exit.
  */
 int expand_files(struct files_struct *files, int nr)
 {
-       int err, expand = 0;
        struct fdtable *fdt;
 
        fdt = files_fdtable(files);
-       if (nr >= fdt->max_fdset || nr >= fdt->max_fds) {
-               if (fdt->max_fdset >= NR_OPEN ||
-                       fdt->max_fds >= NR_OPEN || nr >= NR_OPEN) {
-                       err = -EMFILE;
-                       goto out;
-               }
-               expand = 1;
-               if ((err = expand_fdtable(files, nr)))
-                       goto out;
-       }
-       err = expand;
-out:
-       return err;
+       /* Do we need to expand? */
+       if (nr < fdt->max_fdset && nr < fdt->max_fds)
+               return 0;
+       /* Can we expand? */
+       if (fdt->max_fdset >= NR_OPEN || fdt->max_fds >= NR_OPEN ||
+           nr >= NR_OPEN)
+               return -EMFILE;
+
+       /* All good, so we try */
+       return expand_fdtable(files, nr);
 }
 
 static void __devinit fdtable_defer_list_init(int cpu)
 {
        struct fdtable_defer *fddef = &per_cpu(fdtable_defer_list, cpu);
        spin_lock_init(&fddef->lock);
-       INIT_WORK(&fddef->wq, (void (*)(void *))free_fdtable_work, fddef);
-       init_timer(&fddef->timer);
-       fddef->timer.data = (unsigned long)fddef;
-       fddef->timer.function = fdtable_timer;
+       INIT_WORK(&fddef->wq, free_fdtable_work);
        fddef->next = NULL;
 }