diff options
Diffstat (limited to 'block/blk-ioc.c')
-rw-r--r-- | block/blk-ioc.c | 135 |
1 files changed, 88 insertions, 47 deletions
diff --git a/block/blk-ioc.c b/block/blk-ioc.c index 8b782a63c297..fb95dd2f889a 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c | |||
@@ -36,11 +36,23 @@ static void icq_free_icq_rcu(struct rcu_head *head) | |||
36 | kmem_cache_free(icq->__rcu_icq_cache, icq); | 36 | kmem_cache_free(icq->__rcu_icq_cache, icq); |
37 | } | 37 | } |
38 | 38 | ||
39 | /* | 39 | /* Exit an icq. Called with both ioc and q locked. */ |
40 | * Exit and free an icq. Called with both ioc and q locked. | ||
41 | */ | ||
42 | static void ioc_exit_icq(struct io_cq *icq) | 40 | static void ioc_exit_icq(struct io_cq *icq) |
43 | { | 41 | { |
42 | struct elevator_type *et = icq->q->elevator->type; | ||
43 | |||
44 | if (icq->flags & ICQ_EXITED) | ||
45 | return; | ||
46 | |||
47 | if (et->ops.elevator_exit_icq_fn) | ||
48 | et->ops.elevator_exit_icq_fn(icq); | ||
49 | |||
50 | icq->flags |= ICQ_EXITED; | ||
51 | } | ||
52 | |||
53 | /* Release an icq. Called with both ioc and q locked. */ | ||
54 | static void ioc_destroy_icq(struct io_cq *icq) | ||
55 | { | ||
44 | struct io_context *ioc = icq->ioc; | 56 | struct io_context *ioc = icq->ioc; |
45 | struct request_queue *q = icq->q; | 57 | struct request_queue *q = icq->q; |
46 | struct elevator_type *et = q->elevator->type; | 58 | struct elevator_type *et = q->elevator->type; |
@@ -60,8 +72,7 @@ static void ioc_exit_icq(struct io_cq *icq) | |||
60 | if (rcu_dereference_raw(ioc->icq_hint) == icq) | 72 | if (rcu_dereference_raw(ioc->icq_hint) == icq) |
61 | rcu_assign_pointer(ioc->icq_hint, NULL); | 73 | rcu_assign_pointer(ioc->icq_hint, NULL); |
62 | 74 | ||
63 | if (et->ops.elevator_exit_icq_fn) | 75 | ioc_exit_icq(icq); |
64 | et->ops.elevator_exit_icq_fn(icq); | ||
65 | 76 | ||
66 | /* | 77 | /* |
67 | * @icq->q might have gone away by the time RCU callback runs | 78 | * @icq->q might have gone away by the time RCU callback runs |
@@ -79,7 +90,6 @@ static void ioc_release_fn(struct work_struct *work) | |||
79 | { | 90 | { |
80 | struct io_context *ioc = container_of(work, struct io_context, | 91 | struct io_context *ioc = container_of(work, struct io_context, |
81 | release_work); | 92 | release_work); |
82 | struct request_queue *last_q = NULL; | ||
83 | unsigned long flags; | 93 | unsigned long flags; |
84 | 94 | ||
85 | /* | 95 | /* |
@@ -93,44 +103,19 @@ static void ioc_release_fn(struct work_struct *work) | |||
93 | while (!hlist_empty(&ioc->icq_list)) { | 103 | while (!hlist_empty(&ioc->icq_list)) { |
94 | struct io_cq *icq = hlist_entry(ioc->icq_list.first, | 104 | struct io_cq *icq = hlist_entry(ioc->icq_list.first, |
95 | struct io_cq, ioc_node); | 105 | struct io_cq, ioc_node); |
96 | struct request_queue *this_q = icq->q; | 106 | struct request_queue *q = icq->q; |
97 | 107 | ||
98 | if (this_q != last_q) { | 108 | if (spin_trylock(q->queue_lock)) { |
99 | /* | 109 | ioc_destroy_icq(icq); |
100 | * Need to switch to @this_q. Once we release | 110 | spin_unlock(q->queue_lock); |
101 | * @ioc->lock, it can go away along with @cic. | 111 | } else { |
102 | * Hold on to it. | 112 | spin_unlock_irqrestore(&ioc->lock, flags); |
103 | */ | 113 | cpu_relax(); |
104 | __blk_get_queue(this_q); | 114 | spin_lock_irqsave_nested(&ioc->lock, flags, 1); |
105 | |||
106 | /* | ||
107 | * blk_put_queue() might sleep thanks to kobject | ||
108 | * idiocy. Always release both locks, put and | ||
109 | * restart. | ||
110 | */ | ||
111 | if (last_q) { | ||
112 | spin_unlock(last_q->queue_lock); | ||
113 | spin_unlock_irqrestore(&ioc->lock, flags); | ||
114 | blk_put_queue(last_q); | ||
115 | } else { | ||
116 | spin_unlock_irqrestore(&ioc->lock, flags); | ||
117 | } | ||
118 | |||
119 | last_q = this_q; | ||
120 | spin_lock_irqsave(this_q->queue_lock, flags); | ||
121 | spin_lock_nested(&ioc->lock, 1); | ||
122 | continue; | ||
123 | } | 115 | } |
124 | ioc_exit_icq(icq); | ||
125 | } | 116 | } |
126 | 117 | ||
127 | if (last_q) { | 118 | spin_unlock_irqrestore(&ioc->lock, flags); |
128 | spin_unlock(last_q->queue_lock); | ||
129 | spin_unlock_irqrestore(&ioc->lock, flags); | ||
130 | blk_put_queue(last_q); | ||
131 | } else { | ||
132 | spin_unlock_irqrestore(&ioc->lock, flags); | ||
133 | } | ||
134 | 119 | ||
135 | kmem_cache_free(iocontext_cachep, ioc); | 120 | kmem_cache_free(iocontext_cachep, ioc); |
136 | } | 121 | } |
@@ -145,6 +130,7 @@ static void ioc_release_fn(struct work_struct *work) | |||
145 | void put_io_context(struct io_context *ioc) | 130 | void put_io_context(struct io_context *ioc) |
146 | { | 131 | { |
147 | unsigned long flags; | 132 | unsigned long flags; |
133 | bool free_ioc = false; | ||
148 | 134 | ||
149 | if (ioc == NULL) | 135 | if (ioc == NULL) |
150 | return; | 136 | return; |
@@ -159,8 +145,13 @@ void put_io_context(struct io_context *ioc) | |||
159 | spin_lock_irqsave(&ioc->lock, flags); | 145 | spin_lock_irqsave(&ioc->lock, flags); |
160 | if (!hlist_empty(&ioc->icq_list)) | 146 | if (!hlist_empty(&ioc->icq_list)) |
161 | schedule_work(&ioc->release_work); | 147 | schedule_work(&ioc->release_work); |
148 | else | ||
149 | free_ioc = true; | ||
162 | spin_unlock_irqrestore(&ioc->lock, flags); | 150 | spin_unlock_irqrestore(&ioc->lock, flags); |
163 | } | 151 | } |
152 | |||
153 | if (free_ioc) | ||
154 | kmem_cache_free(iocontext_cachep, ioc); | ||
164 | } | 155 | } |
165 | EXPORT_SYMBOL(put_io_context); | 156 | EXPORT_SYMBOL(put_io_context); |
166 | 157 | ||
@@ -168,13 +159,41 @@ EXPORT_SYMBOL(put_io_context); | |||
168 | void exit_io_context(struct task_struct *task) | 159 | void exit_io_context(struct task_struct *task) |
169 | { | 160 | { |
170 | struct io_context *ioc; | 161 | struct io_context *ioc; |
162 | struct io_cq *icq; | ||
163 | struct hlist_node *n; | ||
164 | unsigned long flags; | ||
171 | 165 | ||
172 | task_lock(task); | 166 | task_lock(task); |
173 | ioc = task->io_context; | 167 | ioc = task->io_context; |
174 | task->io_context = NULL; | 168 | task->io_context = NULL; |
175 | task_unlock(task); | 169 | task_unlock(task); |
176 | 170 | ||
177 | atomic_dec(&ioc->nr_tasks); | 171 | if (!atomic_dec_and_test(&ioc->nr_tasks)) { |
172 | put_io_context(ioc); | ||
173 | return; | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * Need ioc lock to walk icq_list and q lock to exit icq. Perform | ||
178 | * reverse double locking. Read comment in ioc_release_fn() for | ||
179 | * explanation on the nested locking annotation. | ||
180 | */ | ||
181 | retry: | ||
182 | spin_lock_irqsave_nested(&ioc->lock, flags, 1); | ||
183 | hlist_for_each_entry(icq, n, &ioc->icq_list, ioc_node) { | ||
184 | if (icq->flags & ICQ_EXITED) | ||
185 | continue; | ||
186 | if (spin_trylock(icq->q->queue_lock)) { | ||
187 | ioc_exit_icq(icq); | ||
188 | spin_unlock(icq->q->queue_lock); | ||
189 | } else { | ||
190 | spin_unlock_irqrestore(&ioc->lock, flags); | ||
191 | cpu_relax(); | ||
192 | goto retry; | ||
193 | } | ||
194 | } | ||
195 | spin_unlock_irqrestore(&ioc->lock, flags); | ||
196 | |||
178 | put_io_context(ioc); | 197 | put_io_context(ioc); |
179 | } | 198 | } |
180 | 199 | ||
@@ -194,7 +213,7 @@ void ioc_clear_queue(struct request_queue *q) | |||
194 | struct io_context *ioc = icq->ioc; | 213 | struct io_context *ioc = icq->ioc; |
195 | 214 | ||
196 | spin_lock(&ioc->lock); | 215 | spin_lock(&ioc->lock); |
197 | ioc_exit_icq(icq); | 216 | ioc_destroy_icq(icq); |
198 | spin_unlock(&ioc->lock); | 217 | spin_unlock(&ioc->lock); |
199 | } | 218 | } |
200 | } | 219 | } |
@@ -363,13 +382,13 @@ struct io_cq *ioc_create_icq(struct request_queue *q, gfp_t gfp_mask) | |||
363 | return icq; | 382 | return icq; |
364 | } | 383 | } |
365 | 384 | ||
366 | void ioc_set_changed(struct io_context *ioc, int which) | 385 | void ioc_set_icq_flags(struct io_context *ioc, unsigned int flags) |
367 | { | 386 | { |
368 | struct io_cq *icq; | 387 | struct io_cq *icq; |
369 | struct hlist_node *n; | 388 | struct hlist_node *n; |
370 | 389 | ||
371 | hlist_for_each_entry(icq, n, &ioc->icq_list, ioc_node) | 390 | hlist_for_each_entry(icq, n, &ioc->icq_list, ioc_node) |
372 | set_bit(which, &icq->changed); | 391 | icq->flags |= flags; |
373 | } | 392 | } |
374 | 393 | ||
375 | /** | 394 | /** |
@@ -387,7 +406,7 @@ void ioc_ioprio_changed(struct io_context *ioc, int ioprio) | |||
387 | 406 | ||
388 | spin_lock_irqsave(&ioc->lock, flags); | 407 | spin_lock_irqsave(&ioc->lock, flags); |
389 | ioc->ioprio = ioprio; | 408 | ioc->ioprio = ioprio; |
390 | ioc_set_changed(ioc, ICQ_IOPRIO_CHANGED); | 409 | ioc_set_icq_flags(ioc, ICQ_IOPRIO_CHANGED); |
391 | spin_unlock_irqrestore(&ioc->lock, flags); | 410 | spin_unlock_irqrestore(&ioc->lock, flags); |
392 | } | 411 | } |
393 | 412 | ||
@@ -404,11 +423,33 @@ void ioc_cgroup_changed(struct io_context *ioc) | |||
404 | unsigned long flags; | 423 | unsigned long flags; |
405 | 424 | ||
406 | spin_lock_irqsave(&ioc->lock, flags); | 425 | spin_lock_irqsave(&ioc->lock, flags); |
407 | ioc_set_changed(ioc, ICQ_CGROUP_CHANGED); | 426 | ioc_set_icq_flags(ioc, ICQ_CGROUP_CHANGED); |
408 | spin_unlock_irqrestore(&ioc->lock, flags); | 427 | spin_unlock_irqrestore(&ioc->lock, flags); |
409 | } | 428 | } |
410 | EXPORT_SYMBOL(ioc_cgroup_changed); | 429 | EXPORT_SYMBOL(ioc_cgroup_changed); |
411 | 430 | ||
431 | /** | ||
432 | * icq_get_changed - fetch and clear icq changed mask | ||
433 | * @icq: icq of interest | ||
434 | * | ||
435 | * Fetch and clear ICQ_*_CHANGED bits from @icq. Grabs and releases | ||
436 | * @icq->ioc->lock. | ||
437 | */ | ||
438 | unsigned icq_get_changed(struct io_cq *icq) | ||
439 | { | ||
440 | unsigned int changed = 0; | ||
441 | unsigned long flags; | ||
442 | |||
443 | if (unlikely(icq->flags & ICQ_CHANGED_MASK)) { | ||
444 | spin_lock_irqsave(&icq->ioc->lock, flags); | ||
445 | changed = icq->flags & ICQ_CHANGED_MASK; | ||
446 | icq->flags &= ~ICQ_CHANGED_MASK; | ||
447 | spin_unlock_irqrestore(&icq->ioc->lock, flags); | ||
448 | } | ||
449 | return changed; | ||
450 | } | ||
451 | EXPORT_SYMBOL(icq_get_changed); | ||
452 | |||
412 | static int __init blk_ioc_init(void) | 453 | static int __init blk_ioc_init(void) |
413 | { | 454 | { |
414 | iocontext_cachep = kmem_cache_create("blkdev_ioc", | 455 | iocontext_cachep = kmem_cache_create("blkdev_ioc", |