summaryrefslogtreecommitdiff
path: root/fs/bio.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/bio.c')
-rw-r--r--fs/bio.c26
1 files changed, 26 insertions, 0 deletions
diff --git a/fs/bio.c b/fs/bio.c
index e7bf6ca64dcf..d86764f7e9ce 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -257,6 +257,7 @@ void bio_init(struct bio *bio)
bio->bi_flags = 1 << BIO_UPTODATE;
bio->bi_comp_cpu = -1;
atomic_set(&bio->bi_cnt, 1);
+ atomic_set(&bio->bi_remaining, 1);
}
EXPORT_SYMBOL(bio_init);
@@ -1422,16 +1423,41 @@ EXPORT_SYMBOL(bio_flush_dcache_pages);
**/
void bio_endio(struct bio *bio, int error)
{
+ int old, new;
if (error)
clear_bit(BIO_UPTODATE, &bio->bi_flags);
else if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
error = -EIO;
+ if (error) {
+ do {
+ old = new = atomic_read(&bio->bi_remaining);
+ if (!(new >> 16))
+ new += -error << 16;
+
+ } while (atomic_cmpxchg(&bio->bi_remaining, old, --new) != old);
+ } else {
+ new = atomic_sub_return(1, &bio->bi_remaining);
+ error = -(new >> 16);
+ }
+
+ if (new & ~(~0 << 16))
+ return;
+ atomic_set(&bio->bi_remaining, 0);
+
if (bio->bi_end_io)
bio->bi_end_io(bio, error);
}
EXPORT_SYMBOL(bio_endio);
+void bio_split_endio(struct bio *bio, int error)
+{
+ struct bio *p = bio->bi_private;
+ bio_put(bio);
+ bio_endio(p, error);
+}
+EXPORT_SYMBOL(bio_split_endio);
+
void bio_pair_release(struct bio_pair *bp)
{
if (atomic_dec_and_test(&bp->cnt)) {