
From: Jens Axboe <axboe@suse.de>

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 <akpm@osdl.org>
---

 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);
 }
 
_
