block: Add warning for bi_next not NULL in bio_endio()
Recently found a bug where a driver left bi_next not NULL and then called bio_endio(), and then the submitter of the bio used bio_copy_data() which was treating src and dst as lists of bios. Fixed that bug by splitting out bio_list_copy_data(), but in case other things are depending on bi_next in weird ways, add a warning to help avoid more bugs like that in the future. Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
6e6e811d74
commit
0ba99ca483
@ -1775,6 +1775,9 @@ void bio_endio(struct bio *bio)
|
|||||||
if (!bio_integrity_endio(bio))
|
if (!bio_integrity_endio(bio))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (WARN_ONCE(bio->bi_next, "driver left bi_next not NULL"))
|
||||||
|
bio->bi_next = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Need to have a real endio function for chained bios, otherwise
|
* Need to have a real endio function for chained bios, otherwise
|
||||||
* various corner cases will break (like stacking block devices that
|
* various corner cases will break (like stacking block devices that
|
||||||
|
@ -279,6 +279,10 @@ static void req_bio_endio(struct request *rq, struct bio *bio,
|
|||||||
bio_advance(bio, nbytes);
|
bio_advance(bio, nbytes);
|
||||||
|
|
||||||
/* don't actually finish bio if it's part of flush sequence */
|
/* don't actually finish bio if it's part of flush sequence */
|
||||||
|
/*
|
||||||
|
* XXX this code looks suspicious - it's not consistent with advancing
|
||||||
|
* req->bio in caller
|
||||||
|
*/
|
||||||
if (bio->bi_iter.bi_size == 0 && !(rq->rq_flags & RQF_FLUSH_SEQ))
|
if (bio->bi_iter.bi_size == 0 && !(rq->rq_flags & RQF_FLUSH_SEQ))
|
||||||
bio_endio(bio);
|
bio_endio(bio);
|
||||||
}
|
}
|
||||||
@ -3083,8 +3087,10 @@ bool blk_update_request(struct request *req, blk_status_t error,
|
|||||||
struct bio *bio = req->bio;
|
struct bio *bio = req->bio;
|
||||||
unsigned bio_bytes = min(bio->bi_iter.bi_size, nr_bytes);
|
unsigned bio_bytes = min(bio->bi_iter.bi_size, nr_bytes);
|
||||||
|
|
||||||
if (bio_bytes == bio->bi_iter.bi_size)
|
if (bio_bytes == bio->bi_iter.bi_size) {
|
||||||
req->bio = bio->bi_next;
|
req->bio = bio->bi_next;
|
||||||
|
bio->bi_next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Completion has already been traced */
|
/* Completion has already been traced */
|
||||||
bio_clear_flag(bio, BIO_TRACE_COMPLETION);
|
bio_clear_flag(bio, BIO_TRACE_COMPLETION);
|
||||||
|
Loading…
Reference in New Issue
Block a user