Avoid recursion on bio_endio. bio_endio calls bio->bi_end_io which may in turn call bio_endio again. When this recursion happens, put the new bio to the queue and process it later, from the top-level bio_endio. Signed-off-by: Mikulas Patocka --- fs/bio.c | 39 ++++++++++++++++++++++++++++++++++++--- include/linux/bio.h | 3 +++ 2 files changed, 39 insertions(+), 3 deletions(-) Index: linux-2.6.31-rc3-devel/fs/bio.c =================================================================== --- linux-2.6.31-rc3-devel.orig/fs/bio.c 2009-07-14 16:07:22.000000000 +0200 +++ linux-2.6.31-rc3-devel/fs/bio.c 2009-07-14 16:07:52.000000000 +0200 @@ -1390,15 +1390,48 @@ void bio_check_pages_dirty(struct bio *b * bio unless they own it and thus know that it has an end_io * function. **/ +static DEFINE_PER_CPU(struct bio **, bio_end_queue) = { NULL }; + void bio_endio(struct bio *bio, int error) { + struct bio ***bio_end_queue_ptr; + struct bio *bio_queue; + + unsigned long flags; + + bio->bi_flags &= ~(1 << BIO_SEG_VALID); + bio->bi_phys_segments = error; if (error) clear_bit(BIO_UPTODATE, &bio->bi_flags); else if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) - error = -EIO; + bio->bi_phys_segments = -EIO; + + local_irq_save(flags); + bio_end_queue_ptr = &__get_cpu_var(bio_end_queue); + + if (*bio_end_queue_ptr) { + **bio_end_queue_ptr = bio; + *bio_end_queue_ptr = &bio->bi_next; + bio->bi_next = NULL; + } else { + bio_queue = NULL; + *bio_end_queue_ptr = &bio_queue; + +next_bio: + if (bio->bi_end_io) + bio->bi_end_io(bio, (short)bio->bi_phys_segments); + + if (bio_queue) { + bio = bio_queue; + bio_queue = bio->bi_next; + if (!bio_queue) + *bio_end_queue_ptr = &bio_queue; + goto next_bio; + } + *bio_end_queue_ptr = NULL; + } - if (bio->bi_end_io) - bio->bi_end_io(bio, error); + local_irq_restore(flags); } void bio_pair_release(struct bio_pair *bp) Index: linux-2.6.31-rc3-devel/include/linux/bio.h =================================================================== --- linux-2.6.31-rc3-devel.orig/include/linux/bio.h 2009-07-14 16:06:25.000000000 +0200 +++ linux-2.6.31-rc3-devel/include/linux/bio.h 2009-07-14 16:07:52.000000000 +0200 @@ -74,6 +74,9 @@ struct bio { /* Number of segments in this BIO after * physical address coalescing is performed. + * + * When ending a bio request in bio_endio, this field is temporarily + * (ab)used to keep the error code. */ unsigned int bi_phys_segments;