Btrfs: Avoid using __GFP_HIGHMEM with slab allocator
[safe/jmp/linux-2.6] / fs / btrfs / async-thread.c
index d82efd7..c84ca1f 100644 (file)
  * Boston, MA 021110-1307, USA.
  */
 
-#include <linux/version.h>
 #include <linux/kthread.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
-# include <linux/freezer.h>
+#include <linux/freezer.h>
+#include <linux/ftrace.h>
 #include "async-thread.h"
 
+#define WORK_QUEUED_BIT 0
+#define WORK_DONE_BIT 1
+#define WORK_ORDER_DONE_BIT 2
+
 /*
  * container for the kthread task pointer and the list of pending work
  * One of these is allocated per thread.
@@ -88,6 +92,47 @@ static void check_busy_worker(struct btrfs_worker_thread *worker)
        }
 }
 
+static noinline int run_ordered_completions(struct btrfs_workers *workers,
+                                           struct btrfs_work *work)
+{
+       unsigned long flags;
+
+       if (!workers->ordered)
+               return 0;
+
+       set_bit(WORK_DONE_BIT, &work->flags);
+
+       spin_lock_irqsave(&workers->lock, flags);
+
+       while (!list_empty(&workers->order_list)) {
+               work = list_entry(workers->order_list.next,
+                                 struct btrfs_work, order_list);
+
+               if (!test_bit(WORK_DONE_BIT, &work->flags))
+                       break;
+
+               /* we are going to call the ordered done function, but
+                * we leave the work item on the list as a barrier so
+                * that later work items that are done don't have their
+                * functions called before this one returns
+                */
+               if (test_and_set_bit(WORK_ORDER_DONE_BIT, &work->flags))
+                       break;
+
+               spin_unlock_irqrestore(&workers->lock, flags);
+
+               work->ordered_func(work);
+
+               /* now take the lock again and call the freeing code */
+               spin_lock_irqsave(&workers->lock, flags);
+               list_del(&work->order_list);
+               work->ordered_free(work);
+       }
+
+       spin_unlock_irqrestore(&workers->lock, flags);
+       return 0;
+}
+
 /*
  * main loop for servicing work items
  */
@@ -98,11 +143,12 @@ static int worker_loop(void *arg)
        struct btrfs_work *work;
        do {
                spin_lock_irq(&worker->lock);
-               while(!list_empty(&worker->pending)) {
+again_locked:
+               while (!list_empty(&worker->pending)) {
                        cur = worker->pending.next;
                        work = list_entry(cur, struct btrfs_work, list);
                        list_del(&work->list);
-                       clear_bit(0, &work->flags);
+                       clear_bit(WORK_QUEUED_BIT, &work->flags);
 
                        work->worker = worker;
                        spin_unlock_irq(&worker->lock);
@@ -110,16 +156,60 @@ static int worker_loop(void *arg)
                        work->func(work);
 
                        atomic_dec(&worker->num_pending);
+                       /*
+                        * unless this is an ordered work queue,
+                        * 'work' was probably freed by func above.
+                        */
+                       run_ordered_completions(worker->workers, work);
+
                        spin_lock_irq(&worker->lock);
                        check_idle_worker(worker);
+
                }
-               worker->working = 0;
                if (freezing(current)) {
+                       worker->working = 0;
+                       spin_unlock_irq(&worker->lock);
                        refrigerator();
                } else {
-                       set_current_state(TASK_INTERRUPTIBLE);
                        spin_unlock_irq(&worker->lock);
-                       schedule();
+                       if (!kthread_should_stop()) {
+                               cpu_relax();
+                               /*
+                                * we've dropped the lock, did someone else
+                                * jump_in?
+                                */
+                               smp_mb();
+                               if (!list_empty(&worker->pending))
+                                       continue;
+
+                               /*
+                                * this short schedule allows more work to
+                                * come in without the queue functions
+                                * needing to go through wake_up_process()
+                                *
+                                * worker->working is still 1, so nobody
+                                * is going to try and wake us up
+                                */
+                               schedule_timeout(1);
+                               smp_mb();
+                               if (!list_empty(&worker->pending))
+                                       continue;
+
+                               /* still no more work?, sleep for real */
+                               spin_lock_irq(&worker->lock);
+                               set_current_state(TASK_INTERRUPTIBLE);
+                               if (!list_empty(&worker->pending))
+                                       goto again_locked;
+
+                               /*
+                                * this makes sure we get a wakeup when someone
+                                * adds something new to the queue
+                                */
+                               worker->working = 0;
+                               spin_unlock_irq(&worker->lock);
+
+                               schedule();
+                       }
                        __set_current_state(TASK_RUNNING);
                }
        } while (!kthread_should_stop());
@@ -135,7 +225,7 @@ int btrfs_stop_workers(struct btrfs_workers *workers)
        struct btrfs_worker_thread *worker;
 
        list_splice_init(&workers->idle_list, &workers->worker_list);
-       while(!list_empty(&workers->worker_list)) {
+       while (!list_empty(&workers->worker_list)) {
                cur = workers->worker_list.next;
                worker = list_entry(cur, struct btrfs_worker_thread,
                                    worker_list);
@@ -154,10 +244,12 @@ void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max)
        workers->num_workers = 0;
        INIT_LIST_HEAD(&workers->worker_list);
        INIT_LIST_HEAD(&workers->idle_list);
+       INIT_LIST_HEAD(&workers->order_list);
        spin_lock_init(&workers->lock);
        workers->max_workers = max;
        workers->idle_thresh = 32;
        workers->name = name;
+       workers->ordered = 0;
 }
 
 /*
@@ -295,13 +387,14 @@ int btrfs_requeue_work(struct btrfs_work *work)
 {
        struct btrfs_worker_thread *worker = work->worker;
        unsigned long flags;
+       int wake = 0;
 
-       if (test_and_set_bit(0, &work->flags))
+       if (test_and_set_bit(WORK_QUEUED_BIT, &work->flags))
                goto out;
 
        spin_lock_irqsave(&worker->lock, flags);
-       atomic_inc(&worker->num_pending);
        list_add_tail(&work->list, &worker->pending);
+       atomic_inc(&worker->num_pending);
 
        /* by definition we're busy, take ourselves off the idle
         * list
@@ -313,10 +406,16 @@ int btrfs_requeue_work(struct btrfs_work *work)
                               &worker->workers->worker_list);
                spin_unlock_irqrestore(&worker->workers->lock, flags);
        }
+       if (!worker->working) {
+               wake = 1;
+               worker->working = 1;
+       }
 
        spin_unlock_irqrestore(&worker->lock, flags);
-
+       if (wake)
+               wake_up_process(worker->task);
 out:
+
        return 0;
 }
 
@@ -330,15 +429,23 @@ int btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work)
        int wake = 0;
 
        /* don't requeue something already on a list */
-       if (test_and_set_bit(0, &work->flags))
+       if (test_and_set_bit(WORK_QUEUED_BIT, &work->flags))
                goto out;
 
        worker = find_worker(workers);
+       if (workers->ordered) {
+               spin_lock_irqsave(&workers->lock, flags);
+               list_add_tail(&work->order_list, &workers->order_list);
+               spin_unlock_irqrestore(&workers->lock, flags);
+       } else {
+               INIT_LIST_HEAD(&work->order_list);
+       }
 
        spin_lock_irqsave(&worker->lock, flags);
+
+       list_add_tail(&work->list, &worker->pending);
        atomic_inc(&worker->num_pending);
        check_busy_worker(worker);
-       list_add_tail(&work->list, &worker->pending);
 
        /*
         * avoid calling into wake_up_process if this thread has already