All calls to bfq_setup_merge() are followed by bfq_merge_bfqqs() so there should be no chance for chaining several queue merges. And if chained queue merges were possible, then bfq_put_cooperator() would drop cooperator references without clearing corresponding bfqq->new_bfqq pointers causing possible use-after-free issues. Fix these problems by making bfq_put_cooperator() drop only the immediate bfqq->new_bfqq reference.
CC: stable@vger.kernel.org Fixes: 36eca8948323 ("block, bfq: add Early Queue Merge (EQM)") Signed-off-by: Jan Kara jack@suse.cz --- block/bfq-iosched.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-)
diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 0da47f2ca781..654191c6face 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -5184,22 +5184,16 @@ static void bfq_put_stable_ref(struct bfq_queue *bfqq) bfq_put_queue(bfqq); }
+ +/* + * If this queue was scheduled to merge with another queue, be + * sure to drop the reference taken on that queue. + */ static void bfq_put_cooperator(struct bfq_queue *bfqq) { - struct bfq_queue *__bfqq, *next; - - /* - * If this queue was scheduled to merge with another queue, be - * sure to drop the reference taken on that queue (and others in - * the merge chain). See bfq_setup_merge and bfq_merge_bfqqs. - */ - __bfqq = bfqq->new_bfqq; - while (__bfqq) { - if (__bfqq == bfqq) - break; - next = __bfqq->new_bfqq; - bfq_put_queue(__bfqq); - __bfqq = next; + if (bfqq->new_bfqq) { + bfq_put_queue(bfqq->new_bfqq); + bfqq->new_bfqq = NULL; } }
linux-stable-mirror@lists.linaro.org