# This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2004/08/24 11:42:34-07:00 garloff@suse.de # [PATCH] fix bio_uncopy_user() mem leak # # When using bounce buffers for SG_IO commands with unaligned buffers in # blk_rq_map_user(), we should free the pages from blk_rq_unmap_user() which # calls bio_uncopy_user() for the non-BIO_USER_MAPPED case. That function # failed to free the pages for write requests. # # So we leaked pages and you machine would go OOM. Rebooting helped ;-) # # This bug was triggered by writing audio CDs (but not on data CDs), as the # audio frames are not aligned well (2352 bytes), so the user pages don't just # get mapped. # # Bug was reported by Mathias Homan and debugged by Chris Mason + me. (Jens # is away.) # # From: Chris Mason # # Fix the leak for real # # Signed-off-by: Kurt Garloff # # Signed-off-by: Andrew Morton # Signed-off-by: Linus Torvalds # # fs/bio.c # 2004/08/24 02:09:01-07:00 garloff@suse.de +11 -12 # fix bio_uncopy_user() mem leak # diff -Nru a/fs/bio.c b/fs/bio.c --- a/fs/bio.c 2004-08-24 15:53:22 -07:00 +++ b/fs/bio.c 2004-08-24 15:53:22 -07:00 @@ -388,20 +388,17 @@ struct bio_vec *bvec; int i, ret = 0; - if (bio_data_dir(bio) == READ) { - char *uaddr = bio->bi_private; + char *uaddr = bio->bi_private; - __bio_for_each_segment(bvec, bio, i, 0) { - char *addr = page_address(bvec->bv_page); - - if (!ret && copy_to_user(uaddr, addr, bvec->bv_len)) - ret = -EFAULT; + __bio_for_each_segment(bvec, bio, i, 0) { + char *addr = page_address(bvec->bv_page); + if (bio_data_dir(bio) == READ && !ret && + copy_to_user(uaddr, addr, bvec->bv_len)) + ret = -EFAULT; - __free_page(bvec->bv_page); - uaddr += bvec->bv_len; - } + __free_page(bvec->bv_page); + uaddr += bvec->bv_len; } - bio_put(bio); return ret; } @@ -457,6 +454,7 @@ */ if (!ret) { if (!write_to_vm) { + unsigned long p = uaddr; bio->bi_rw |= (1 << BIO_RW); /* * for a write, copy in data to kernel pages @@ -465,8 +463,9 @@ bio_for_each_segment(bvec, bio, i) { char *addr = page_address(bvec->bv_page); - if (copy_from_user(addr, (char *) uaddr, bvec->bv_len)) + if (copy_from_user(addr, (char *) p, bvec->bv_len)) goto cleanup; + p += bvec->bv_len; } }