You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
block: remove management of bi_remaining when restoring original bi_end_io
Commitc4cf5261("bio: skip atomic inc/dec of ->bi_remaining for non-chains") regressed all existing callers that followed this pattern: 1) saving a bio's original bi_end_io 2) wiring up an intermediate bi_end_io 3) restoring the original bi_end_io from intermediate bi_end_io 4) calling bio_endio() to execute the restored original bi_end_io The regression was due to BIO_CHAIN only ever getting set if bio_inc_remaining() is called. For the above pattern it isn't set until step 3 above (step 2 would've needed to establish BIO_CHAIN). As such the first bio_endio(), in step 2 above, never decremented __bi_remaining before calling the intermediate bi_end_io -- leaving __bi_remaining with the value 1 instead of 0. When bio_inc_remaining() occurred during step 3 it brought it to a value of 2. When the second bio_endio() was called, in step 4 above, it should've called the original bi_end_io but it didn't because there was an extra reference that wasn't dropped (due to atomic operations being optimized away since BIO_CHAIN wasn't set upfront). Fix this issue by removing the __bi_remaining management complexity for all callers that use the above pattern -- bio_chain() is the only interface that _needs_ to be concerned with __bi_remaining. For the above pattern callers just expect the bi_end_io they set to get called! Remove bio_endio_nodec() and also remove all bio_inc_remaining() calls that aren't associated with the bio_chain() interface. Also, the bio_inc_remaining() interface has been moved local to bio.c. Fixes:c4cf5261("bio: skip atomic inc/dec of ->bi_remaining for non-chains") Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Jan Kara <jack@suse.cz> Signed-off-by: Mike Snitzer <snitzer@redhat.com> Signed-off-by: Jens Axboe <axboe@fb.com>
This commit is contained in:
@@ -361,7 +361,7 @@ static void bio_integrity_verify_fn(struct work_struct *work)
|
||||
|
||||
/* Restore original bio completion handler */
|
||||
bio->bi_end_io = bip->bip_end_io;
|
||||
bio_endio_nodec(bio, error);
|
||||
bio_endio(bio, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,7 +388,7 @@ void bio_integrity_endio(struct bio *bio, int error)
|
||||
*/
|
||||
if (error) {
|
||||
bio->bi_end_io = bip->bip_end_io;
|
||||
bio_endio_nodec(bio, error);
|
||||
bio_endio(bio, error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
+14
-21
@@ -303,6 +303,17 @@ static void bio_chain_endio(struct bio *bio, int error)
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment chain count for the bio. Make sure the CHAIN flag update
|
||||
* is visible before the raised count.
|
||||
*/
|
||||
static inline void bio_inc_remaining(struct bio *bio)
|
||||
{
|
||||
bio->bi_flags |= (1 << BIO_CHAIN);
|
||||
smp_mb__before_atomic();
|
||||
atomic_inc(&bio->__bi_remaining);
|
||||
}
|
||||
|
||||
/**
|
||||
* bio_chain - chain bio completions
|
||||
* @bio: the target bio
|
||||
@@ -1756,8 +1767,10 @@ static inline bool bio_remaining_done(struct bio *bio)
|
||||
|
||||
BUG_ON(atomic_read(&bio->__bi_remaining) <= 0);
|
||||
|
||||
if (atomic_dec_and_test(&bio->__bi_remaining))
|
||||
if (atomic_dec_and_test(&bio->__bi_remaining)) {
|
||||
clear_bit(BIO_CHAIN, &bio->bi_flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1808,26 +1821,6 @@ void bio_endio(struct bio *bio, int error)
|
||||
}
|
||||
EXPORT_SYMBOL(bio_endio);
|
||||
|
||||
/**
|
||||
* bio_endio_nodec - end I/O on a bio, without decrementing bi_remaining
|
||||
* @bio: bio
|
||||
* @error: error, if any
|
||||
*
|
||||
* For code that has saved and restored bi_end_io; thing hard before using this
|
||||
* function, probably you should've cloned the entire bio.
|
||||
**/
|
||||
void bio_endio_nodec(struct bio *bio, int error)
|
||||
{
|
||||
/*
|
||||
* If it's not flagged as a chain, we are not going to dec the count
|
||||
*/
|
||||
if (bio_flagged(bio, BIO_CHAIN))
|
||||
bio_inc_remaining(bio);
|
||||
|
||||
bio_endio(bio, error);
|
||||
}
|
||||
EXPORT_SYMBOL(bio_endio_nodec);
|
||||
|
||||
/**
|
||||
* bio_split - split a bio
|
||||
* @bio: bio to split
|
||||
|
||||
Reference in New Issue
Block a user