From: Jens Axboe Hopefully fixes the free-of-a-freed-page BUG caused during CDRW writing. This also fixes a problem in the bouncing for io errors (it needs to free the pages and clear the BIO_UPTODATE flag, not set it. it's already set. passing -EIO to bio_endio() takes care of that). Signed-off-by: Andrew Morton --- 25-akpm/drivers/block/ll_rw_blk.c | 6 ++++++ 25-akpm/drivers/block/scsi_ioctl.c | 7 +++++++ 25-akpm/drivers/cdrom/cdrom.c | 3 +++ 25-akpm/fs/bio.c | 1 - 25-akpm/mm/highmem.c | 9 +++------ 5 files changed, 19 insertions(+), 7 deletions(-) diff -puN drivers/block/ll_rw_blk.c~bio-page-refcounting-fix drivers/block/ll_rw_blk.c --- 25/drivers/block/ll_rw_blk.c~bio-page-refcounting-fix 2004-07-27 00:08:43.923380224 -0700 +++ 25-akpm/drivers/block/ll_rw_blk.c 2004-07-27 00:08:43.937378096 -0700 @@ -1813,6 +1813,12 @@ EXPORT_SYMBOL(blk_insert_request); * * A matching blk_rq_unmap_user() must be issued at the end of io, while * still in process context. + * + * Note: The mapped bio may need to be bounced through blk_queue_bounce() + * before being submitted to the device, as pages mapped may be out of + * reach. It's the callers responsibility to make sure this happens. The + * original bio must be passed back in to blk_rq_unmap_user() for proper + * unmapping. */ struct request *blk_rq_map_user(request_queue_t *q, int rw, void __user *ubuf, unsigned int len) diff -puN drivers/block/scsi_ioctl.c~bio-page-refcounting-fix drivers/block/scsi_ioctl.c --- 25/drivers/block/scsi_ioctl.c~bio-page-refcounting-fix 2004-07-27 00:08:43.925379920 -0700 +++ 25-akpm/drivers/block/scsi_ioctl.c 2004-07-27 00:08:43.937378096 -0700 @@ -170,6 +170,13 @@ static int sg_io(request_queue_t *q, str rq->flags |= REQ_BLOCK_PC; bio = rq->bio; + /* + * bounce this after holding a reference to the original bio, it's + * needed for proper unmapping + */ + if (rq->bio) + blk_queue_bounce(q, &rq->bio); + rq->timeout = (hdr->timeout * HZ) / 1000; if (!rq->timeout) rq->timeout = q->sg_timeout; diff -puN drivers/cdrom/cdrom.c~bio-page-refcounting-fix drivers/cdrom/cdrom.c --- 25/drivers/cdrom/cdrom.c~bio-page-refcounting-fix 2004-07-27 00:08:43.927379616 -0700 +++ 25-akpm/drivers/cdrom/cdrom.c 2004-07-27 00:08:43.940377640 -0700 @@ -2002,6 +2002,9 @@ static int cdrom_read_cdda_bpc(struct cd rq->timeout = 60 * HZ; bio = rq->bio; + if (rq->bio) + blk_queue_bounce(q, &rq->bio); + if (blk_execute_rq(q, cdi->disk, rq)) { struct request_sense *s = rq->sense; ret = -EIO; diff -puN fs/bio.c~bio-page-refcounting-fix fs/bio.c --- 25/fs/bio.c~bio-page-refcounting-fix 2004-07-27 00:08:43.929379312 -0700 +++ 25-akpm/fs/bio.c 2004-07-27 00:08:43.940377640 -0700 @@ -446,7 +446,6 @@ static struct bio *__bio_map_user(reques if (!write_to_vm) bio->bi_rw |= (1 << BIO_RW); - blk_queue_bounce(q, &bio); return bio; out: kfree(pages); diff -puN mm/highmem.c~bio-page-refcounting-fix mm/highmem.c --- 25/mm/highmem.c~bio-page-refcounting-fix 2004-07-27 00:08:43.931379008 -0700 +++ 25-akpm/mm/highmem.c 2004-07-27 00:08:43.941377488 -0700 @@ -308,12 +308,10 @@ static void bounce_end_io(struct bio *bi { struct bio *bio_orig = bio->bi_private; struct bio_vec *bvec, *org_vec; - int i; + int i, err = 0; if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) - goto out_eio; - - set_bit(BIO_UPTODATE, &bio_orig->bi_flags); + err = -EIO; /* * free up bounce indirect pages used @@ -326,8 +324,7 @@ static void bounce_end_io(struct bio *bi mempool_free(bvec->bv_page, pool); } -out_eio: - bio_endio(bio_orig, bio_orig->bi_size, 0); + bio_endio(bio_orig, bio_orig->bi_size, err); bio_put(bio); } _