diff options
Diffstat (limited to 'fs/btrfs/async-thread.c')
-rw-r--r-- | fs/btrfs/async-thread.c | 67 |
1 files changed, 64 insertions, 3 deletions
diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c index d82efd722a48..e1e49715459e 100644 --- a/fs/btrfs/async-thread.c +++ b/fs/btrfs/async-thread.c | |||
@@ -23,6 +23,10 @@ | |||
23 | # include <linux/freezer.h> | 23 | # include <linux/freezer.h> |
24 | #include "async-thread.h" | 24 | #include "async-thread.h" |
25 | 25 | ||
26 | #define WORK_QUEUED_BIT 0 | ||
27 | #define WORK_DONE_BIT 1 | ||
28 | #define WORK_ORDER_DONE_BIT 2 | ||
29 | |||
26 | /* | 30 | /* |
27 | * container for the kthread task pointer and the list of pending work | 31 | * container for the kthread task pointer and the list of pending work |
28 | * One of these is allocated per thread. | 32 | * One of these is allocated per thread. |
@@ -88,6 +92,47 @@ static void check_busy_worker(struct btrfs_worker_thread *worker) | |||
88 | } | 92 | } |
89 | } | 93 | } |
90 | 94 | ||
95 | static noinline int run_ordered_completions(struct btrfs_workers *workers, | ||
96 | struct btrfs_work *work) | ||
97 | { | ||
98 | unsigned long flags; | ||
99 | |||
100 | if (!workers->ordered) | ||
101 | return 0; | ||
102 | |||
103 | set_bit(WORK_DONE_BIT, &work->flags); | ||
104 | |||
105 | spin_lock_irqsave(&workers->lock, flags); | ||
106 | |||
107 | while(!list_empty(&workers->order_list)) { | ||
108 | work = list_entry(workers->order_list.next, | ||
109 | struct btrfs_work, order_list); | ||
110 | |||
111 | if (!test_bit(WORK_DONE_BIT, &work->flags)) | ||
112 | break; | ||
113 | |||
114 | /* we are going to call the ordered done function, but | ||
115 | * we leave the work item on the list as a barrier so | ||
116 | * that later work items that are done don't have their | ||
117 | * functions called before this one returns | ||
118 | */ | ||
119 | if (test_and_set_bit(WORK_ORDER_DONE_BIT, &work->flags)) | ||
120 | break; | ||
121 | |||
122 | spin_unlock_irqrestore(&workers->lock, flags); | ||
123 | |||
124 | work->ordered_func(work); | ||
125 | |||
126 | /* now take the lock again and call the freeing code */ | ||
127 | spin_lock_irqsave(&workers->lock, flags); | ||
128 | list_del(&work->order_list); | ||
129 | work->ordered_free(work); | ||
130 | } | ||
131 | |||
132 | spin_unlock_irqrestore(&workers->lock, flags); | ||
133 | return 0; | ||
134 | } | ||
135 | |||
91 | /* | 136 | /* |
92 | * main loop for servicing work items | 137 | * main loop for servicing work items |
93 | */ | 138 | */ |
@@ -102,7 +147,7 @@ static int worker_loop(void *arg) | |||
102 | cur = worker->pending.next; | 147 | cur = worker->pending.next; |
103 | work = list_entry(cur, struct btrfs_work, list); | 148 | work = list_entry(cur, struct btrfs_work, list); |
104 | list_del(&work->list); | 149 | list_del(&work->list); |
105 | clear_bit(0, &work->flags); | 150 | clear_bit(WORK_QUEUED_BIT, &work->flags); |
106 | 151 | ||
107 | work->worker = worker; | 152 | work->worker = worker; |
108 | spin_unlock_irq(&worker->lock); | 153 | spin_unlock_irq(&worker->lock); |
@@ -110,8 +155,15 @@ static int worker_loop(void *arg) | |||
110 | work->func(work); | 155 | work->func(work); |
111 | 156 | ||
112 | atomic_dec(&worker->num_pending); | 157 | atomic_dec(&worker->num_pending); |
158 | /* | ||
159 | * unless this is an ordered work queue, | ||
160 | * 'work' was probably freed by func above. | ||
161 | */ | ||
162 | run_ordered_completions(worker->workers, work); | ||
163 | |||
113 | spin_lock_irq(&worker->lock); | 164 | spin_lock_irq(&worker->lock); |
114 | check_idle_worker(worker); | 165 | check_idle_worker(worker); |
166 | |||
115 | } | 167 | } |
116 | worker->working = 0; | 168 | worker->working = 0; |
117 | if (freezing(current)) { | 169 | if (freezing(current)) { |
@@ -154,10 +206,12 @@ void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max) | |||
154 | workers->num_workers = 0; | 206 | workers->num_workers = 0; |
155 | INIT_LIST_HEAD(&workers->worker_list); | 207 | INIT_LIST_HEAD(&workers->worker_list); |
156 | INIT_LIST_HEAD(&workers->idle_list); | 208 | INIT_LIST_HEAD(&workers->idle_list); |
209 | INIT_LIST_HEAD(&workers->order_list); | ||
157 | spin_lock_init(&workers->lock); | 210 | spin_lock_init(&workers->lock); |
158 | workers->max_workers = max; | 211 | workers->max_workers = max; |
159 | workers->idle_thresh = 32; | 212 | workers->idle_thresh = 32; |
160 | workers->name = name; | 213 | workers->name = name; |
214 | workers->ordered = 0; | ||
161 | } | 215 | } |
162 | 216 | ||
163 | /* | 217 | /* |
@@ -296,7 +350,7 @@ int btrfs_requeue_work(struct btrfs_work *work) | |||
296 | struct btrfs_worker_thread *worker = work->worker; | 350 | struct btrfs_worker_thread *worker = work->worker; |
297 | unsigned long flags; | 351 | unsigned long flags; |
298 | 352 | ||
299 | if (test_and_set_bit(0, &work->flags)) | 353 | if (test_and_set_bit(WORK_QUEUED_BIT, &work->flags)) |
300 | goto out; | 354 | goto out; |
301 | 355 | ||
302 | spin_lock_irqsave(&worker->lock, flags); | 356 | spin_lock_irqsave(&worker->lock, flags); |
@@ -330,10 +384,17 @@ int btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work) | |||
330 | int wake = 0; | 384 | int wake = 0; |
331 | 385 | ||
332 | /* don't requeue something already on a list */ | 386 | /* don't requeue something already on a list */ |
333 | if (test_and_set_bit(0, &work->flags)) | 387 | if (test_and_set_bit(WORK_QUEUED_BIT, &work->flags)) |
334 | goto out; | 388 | goto out; |
335 | 389 | ||
336 | worker = find_worker(workers); | 390 | worker = find_worker(workers); |
391 | if (workers->ordered) { | ||
392 | spin_lock_irqsave(&workers->lock, flags); | ||
393 | list_add_tail(&work->order_list, &workers->order_list); | ||
394 | spin_unlock_irqrestore(&workers->lock, flags); | ||
395 | } else { | ||
396 | INIT_LIST_HEAD(&work->order_list); | ||
397 | } | ||
337 | 398 | ||
338 | spin_lock_irqsave(&worker->lock, flags); | 399 | spin_lock_irqsave(&worker->lock, flags); |
339 | atomic_inc(&worker->num_pending); | 400 | atomic_inc(&worker->num_pending); |