Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

md/raid1: r1buf_pool_alloc: free allocate pages when subsequent allocation fails.

When performing a user-request check/repair (MD_RECOVERY_REQUEST is set)
on a raid1, we allocate multiple bios each with their own set of pages.

If the page allocations for one bio fails, we currently do *not* free
the pages allocated for the previous bios, nor do we free the bio itself.

This patch frees all the already-allocate pages, and makes sure that
all the bios are freed as well.

This bug can cause a memory leak which can ultimately OOM a machine.
It was introduced in 3.10-rc1.

Fixes: a07876064a0b73ab5ef1ebcf14b1cf0231c07858
Cc: Kent Overstreet <koverstreet@google.com>
Cc: stable@vger.kernel.org (3.10+)
Reported-by: Russell King - ARM Linux <linux@arm.linux.org.uk>
Signed-off-by: NeilBrown <neilb@suse.de>

NeilBrown da1aab3d 035328c2

+13 -4
+13 -4
drivers/md/raid1.c
··· 97 97 struct pool_info *pi = data; 98 98 struct r1bio *r1_bio; 99 99 struct bio *bio; 100 + int need_pages; 100 101 int i, j; 101 102 102 103 r1_bio = r1bio_pool_alloc(gfp_flags, pi); ··· 120 119 * RESYNC_PAGES for each bio. 121 120 */ 122 121 if (test_bit(MD_RECOVERY_REQUESTED, &pi->mddev->recovery)) 123 - j = pi->raid_disks; 122 + need_pages = pi->raid_disks; 124 123 else 125 - j = 1; 126 - while(j--) { 124 + need_pages = 1; 125 + for (j = 0; j < need_pages; j++) { 127 126 bio = r1_bio->bios[j]; 128 127 bio->bi_vcnt = RESYNC_PAGES; 129 128 130 129 if (bio_alloc_pages(bio, gfp_flags)) 131 - goto out_free_bio; 130 + goto out_free_pages; 132 131 } 133 132 /* If not user-requests, copy the page pointers to all bios */ 134 133 if (!test_bit(MD_RECOVERY_REQUESTED, &pi->mddev->recovery)) { ··· 141 140 r1_bio->master_bio = NULL; 142 141 143 142 return r1_bio; 143 + 144 + out_free_pages: 145 + while (--j >= 0) { 146 + struct bio_vec *bv; 147 + 148 + bio_for_each_segment_all(bv, r1_bio->bios[j], i) 149 + __free_page(bv->bv_page); 150 + } 144 151 145 152 out_free_bio: 146 153 while (++j < pi->raid_disks)