summaryrefslogtreecommitdiffstats
path: root/lib/sbitmap.c
diff options
context:
space:
mode:
authorOmar Sandoval <osandov@fb.com>2017-01-18 14:55:22 -0500
committerJens Axboe <axboe@fb.com>2017-01-18 15:41:55 -0500
commit6c0ca7ae292adea09b8bdd33a524bb9326c3e989 (patch)
treebbe47f1b91edbae94b582649b7d11a4b2b2fb877 /lib/sbitmap.c
parentf66227de5924ed0fde1823f5cbc4d8b8f45faaa2 (diff)
sbitmap: fix wakeup hang after sbq resize
When we resize a struct sbitmap_queue, we update the wakeup batch size, but we don't update the wait count in the struct sbq_wait_states. If we resized down from a size which could use a bigger batch size, these counts could be too large and cause us to miss necessary wakeups. To fix this, update the wait counts when we resize (ensuring some careful memory ordering so that it's safe w.r.t. concurrent clears). This also fixes a theoretical issue where two threads could end up bumping the wait count up by the batch size, which could also potentially lead to hangs. Reported-by: Martin Raiber <martin@urbackup.org> Fixes: e3a2b3f931f5 ("blk-mq: allow changing of queue depth through sysfs") Fixes: 2971c35f3588 ("blk-mq: bitmap tag: fix race on blk_mq_bitmap_tags::wake_cnt") Signed-off-by: Omar Sandoval <osandov@fb.com> Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'lib/sbitmap.c')
-rw-r--r--lib/sbitmap.c35
1 files changed, 30 insertions, 5 deletions
diff --git a/lib/sbitmap.c b/lib/sbitmap.c
index df4e472df8a3..8f5c3b268c77 100644
--- a/lib/sbitmap.c
+++ b/lib/sbitmap.c
@@ -239,7 +239,19 @@ EXPORT_SYMBOL_GPL(sbitmap_queue_init_node);
239 239
240void sbitmap_queue_resize(struct sbitmap_queue *sbq, unsigned int depth) 240void sbitmap_queue_resize(struct sbitmap_queue *sbq, unsigned int depth)
241{ 241{
242 sbq->wake_batch = sbq_calc_wake_batch(depth); 242 unsigned int wake_batch = sbq_calc_wake_batch(depth);
243 int i;
244
245 if (sbq->wake_batch != wake_batch) {
246 WRITE_ONCE(sbq->wake_batch, wake_batch);
247 /*
248 * Pairs with the memory barrier in sbq_wake_up() to ensure that
249 * the batch size is updated before the wait counts.
250 */
251 smp_mb__before_atomic();
252 for (i = 0; i < SBQ_WAIT_QUEUES; i++)
253 atomic_set(&sbq->ws[i].wait_cnt, 1);
254 }
243 sbitmap_resize(&sbq->sb, depth); 255 sbitmap_resize(&sbq->sb, depth);
244} 256}
245EXPORT_SYMBOL_GPL(sbitmap_queue_resize); 257EXPORT_SYMBOL_GPL(sbitmap_queue_resize);
@@ -297,6 +309,7 @@ static struct sbq_wait_state *sbq_wake_ptr(struct sbitmap_queue *sbq)
297static void sbq_wake_up(struct sbitmap_queue *sbq) 309static void sbq_wake_up(struct sbitmap_queue *sbq)
298{ 310{
299 struct sbq_wait_state *ws; 311 struct sbq_wait_state *ws;
312 unsigned int wake_batch;
300 int wait_cnt; 313 int wait_cnt;
301 314
302 /* 315 /*
@@ -313,10 +326,22 @@ static void sbq_wake_up(struct sbitmap_queue *sbq)
313 return; 326 return;
314 327
315 wait_cnt = atomic_dec_return(&ws->wait_cnt); 328 wait_cnt = atomic_dec_return(&ws->wait_cnt);
316 if (unlikely(wait_cnt < 0)) 329 if (wait_cnt <= 0) {
317 wait_cnt = atomic_inc_return(&ws->wait_cnt); 330 wake_batch = READ_ONCE(sbq->wake_batch);
318 if (wait_cnt == 0) { 331 /*
319 atomic_add(sbq->wake_batch, &ws->wait_cnt); 332 * Pairs with the memory barrier in sbitmap_queue_resize() to
333 * ensure that we see the batch size update before the wait
334 * count is reset.
335 */
336 smp_mb__before_atomic();
337 /*
338 * If there are concurrent callers to sbq_wake_up(), the last
339 * one to decrement the wait count below zero will bump it back
340 * up. If there is a concurrent resize, the count reset will
341 * either cause the cmpxchg to fail or overwrite after the
342 * cmpxchg.
343 */
344 atomic_cmpxchg(&ws->wait_cnt, wait_cnt, wait_cnt + wake_batch);
320 sbq_index_atomic_inc(&sbq->wake_index); 345 sbq_index_atomic_inc(&sbq->wake_index);
321 wake_up(&ws->wait); 346 wake_up(&ws->wait);
322 } 347 }