diff options
author | Paolo Valente <paolo.valente@linaro.org> | 2017-10-09 07:11:23 -0400 |
---|---|---|
committer | Jens Axboe <axboe@kernel.dk> | 2017-10-09 11:54:58 -0400 |
commit | 99fead8d38e5302b1be9403d4de815ce9174a3df (patch) | |
tree | 9aaad76e82a450377f5175b3d0730ce093d6238b /block/bfq-iosched.c | |
parent | b5dc5d4d1f4ff9032eb6c21a3c571a1317dc9289 (diff) |
block, bfq: fix unbalanced decrements of burst size
The commit "block, bfq: decrease burst size when queues in burst
exit" introduced the decrement of burst_size on the removal of a
bfq_queue from the burst list. Unfortunately, this decrement can
happen to be performed even when burst size is already equal to 0,
because of unbalanced decrements. A description follows of the cause
of these unbalanced decrements, namely a wrong assumption, and of the
way how this wrong assumption leads to unbalanced decrements.
The wrong assumption is that a bfq_queue can exit only if the process
associated with the bfq_queue has exited. This is false, because a
bfq_queue, say Q, may exit also as a consequence of a merge with
another bfq_queue. In this case, Q exits because the I/O of its
associated process has been redirected to another bfq_queue.
The decrement unbalance occurs because Q may then be re-created after
a split, and added back to the current burst list, *without*
incrementing burst_size. burst_size is not incremented because Q is
not a new bfq_queue added to the burst list, but a bfq_queue only
temporarily removed from the list, and, before the commit "bfq-sq,
bfq-mq: decrease burst size when queues in burst exit", burst_size was
not decremented when Q was removed.
This commit addresses this issue by just checking whether the exiting
bfq_queue is a merged bfq_queue, and, in that case, not decrementing
burst_size. Unfortunately, this still leaves room for unbalanced
decrements, in the following rarer case: on a split, the bfq_queue
happens to be inserted into a different burst list than that it was
removed from when merged. If this happens, the number of elements in
the new burst list becomes higher than burst_size (by one). When the
bfq_queue then exits, it is of course not in a merged state any
longer, thus burst_size is decremented, which results in an unbalanced
decrement. To handle this sporadic, unlucky case in a simple way,
this commit also checks that burst_size is larger than 0 before
decrementing it.
Finally, this commit removes an useless, extra check: the check that
the bfq_queue is sync, performed before checking whether the bfq_queue
is in the burst list. This extra check is redundant, because only sync
bfq_queues can be inserted into the burst list.
Fixes: 7cb04004fa37 ("block, bfq: decrease burst size when queues in burst exit")
Reported-by: Philip Müller <philm@manjaro.org>
Signed-off-by: Paolo Valente <paolo.valente@linaro.org>
Signed-off-by: Angelo Ruocco <angeloruocco90@gmail.com>
Tested-by: Philip Müller <philm@manjaro.org>
Tested-by: Oleksandr Natalenko <oleksandr@natalenko.name>
Tested-by: Lee Tibbert <lee.tibbert@gmail.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'block/bfq-iosched.c')
-rw-r--r-- | block/bfq-iosched.c | 59 |
1 files changed, 57 insertions, 2 deletions
diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 261f98695910..889a8549d97f 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c | |||
@@ -3726,9 +3726,36 @@ void bfq_put_queue(struct bfq_queue *bfqq) | |||
3726 | if (bfqq->ref) | 3726 | if (bfqq->ref) |
3727 | return; | 3727 | return; |
3728 | 3728 | ||
3729 | if (bfq_bfqq_sync(bfqq) && !hlist_unhashed(&bfqq->burst_list_node)) { | 3729 | if (!hlist_unhashed(&bfqq->burst_list_node)) { |
3730 | hlist_del_init(&bfqq->burst_list_node); | 3730 | hlist_del_init(&bfqq->burst_list_node); |
3731 | bfqq->bfqd->burst_size--; | 3731 | /* |
3732 | * Decrement also burst size after the removal, if the | ||
3733 | * process associated with bfqq is exiting, and thus | ||
3734 | * does not contribute to the burst any longer. This | ||
3735 | * decrement helps filter out false positives of large | ||
3736 | * bursts, when some short-lived process (often due to | ||
3737 | * the execution of commands by some service) happens | ||
3738 | * to start and exit while a complex application is | ||
3739 | * starting, and thus spawning several processes that | ||
3740 | * do I/O (and that *must not* be treated as a large | ||
3741 | * burst, see comments on bfq_handle_burst). | ||
3742 | * | ||
3743 | * In particular, the decrement is performed only if: | ||
3744 | * 1) bfqq is not a merged queue, because, if it is, | ||
3745 | * then this free of bfqq is not triggered by the exit | ||
3746 | * of the process bfqq is associated with, but exactly | ||
3747 | * by the fact that bfqq has just been merged. | ||
3748 | * 2) burst_size is greater than 0, to handle | ||
3749 | * unbalanced decrements. Unbalanced decrements may | ||
3750 | * happen in te following case: bfqq is inserted into | ||
3751 | * the current burst list--without incrementing | ||
3752 | * bust_size--because of a split, but the current | ||
3753 | * burst list is not the burst list bfqq belonged to | ||
3754 | * (see comments on the case of a split in | ||
3755 | * bfq_set_request). | ||
3756 | */ | ||
3757 | if (bfqq->bic && bfqq->bfqd->burst_size > 0) | ||
3758 | bfqq->bfqd->burst_size--; | ||
3732 | } | 3759 | } |
3733 | 3760 | ||
3734 | kmem_cache_free(bfq_pool, bfqq); | 3761 | kmem_cache_free(bfq_pool, bfqq); |
@@ -4460,6 +4487,34 @@ static struct bfq_queue *bfq_get_bfqq_handle_split(struct bfq_data *bfqd, | |||
4460 | else { | 4487 | else { |
4461 | bfq_clear_bfqq_in_large_burst(bfqq); | 4488 | bfq_clear_bfqq_in_large_burst(bfqq); |
4462 | if (bic->was_in_burst_list) | 4489 | if (bic->was_in_burst_list) |
4490 | /* | ||
4491 | * If bfqq was in the current | ||
4492 | * burst list before being | ||
4493 | * merged, then we have to add | ||
4494 | * it back. And we do not need | ||
4495 | * to increase burst_size, as | ||
4496 | * we did not decrement | ||
4497 | * burst_size when we removed | ||
4498 | * bfqq from the burst list as | ||
4499 | * a consequence of a merge | ||
4500 | * (see comments in | ||
4501 | * bfq_put_queue). In this | ||
4502 | * respect, it would be rather | ||
4503 | * costly to know whether the | ||
4504 | * current burst list is still | ||
4505 | * the same burst list from | ||
4506 | * which bfqq was removed on | ||
4507 | * the merge. To avoid this | ||
4508 | * cost, if bfqq was in a | ||
4509 | * burst list, then we add | ||
4510 | * bfqq to the current burst | ||
4511 | * list without any further | ||
4512 | * check. This can cause | ||
4513 | * inappropriate insertions, | ||
4514 | * but rarely enough to not | ||
4515 | * harm the detection of large | ||
4516 | * bursts significantly. | ||
4517 | */ | ||
4463 | hlist_add_head(&bfqq->burst_list_node, | 4518 | hlist_add_head(&bfqq->burst_list_node, |
4464 | &bfqd->burst_list); | 4519 | &bfqd->burst_list); |
4465 | } | 4520 | } |