aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShaohua Li <shli@fb.com>2016-08-23 00:14:01 -0400
committerShaohua Li <shli@fb.com>2016-08-24 13:21:52 -0400
commit5f9d1fde7d54a5d5fd8cccbee9c9c31474fcdcf2 (patch)
tree928efbe6ba6efef4e84eed37103038015770614a
parent27028626b4b9022dcac23688e09ea43b36e1183c (diff)
raid5: fix memory leak of bio integrity data
Yi reported a memory leak of raid5 with DIF/DIX enabled disks. raid5 doesn't alloc/free bio, instead it reuses bios. There are two issues in current code: 1. the code calls bio_init (from init_stripe->raid5_build_block->bio_init) then bio_reset (ops_run_io). The bio is reused, so likely there is integrity data attached. bio_init will clear a pointer to integrity data and makes bio_reset can't release the data 2. bio_reset is called before dispatching bio. After bio is finished, it's possible we don't free bio's integrity data (eg, we don't call bio_reset again) Both issues will cause memory leak. The patch moves bio_init to stripe creation and bio_reset to bio end io. This will fix the two issues. Reported-by: Yi Zhang <yizhan@redhat.com> Signed-off-by: Shaohua Li <shli@fb.com>
-rw-r--r--drivers/md/raid5.c22
1 files changed, 15 insertions, 7 deletions
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 2119e094dfb3..d1a279b1916b 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -1005,7 +1005,6 @@ again:
1005 1005
1006 set_bit(STRIPE_IO_STARTED, &sh->state); 1006 set_bit(STRIPE_IO_STARTED, &sh->state);
1007 1007
1008 bio_reset(bi);
1009 bi->bi_bdev = rdev->bdev; 1008 bi->bi_bdev = rdev->bdev;
1010 bio_set_op_attrs(bi, op, op_flags); 1009 bio_set_op_attrs(bi, op, op_flags);
1011 bi->bi_end_io = op_is_write(op) 1010 bi->bi_end_io = op_is_write(op)
@@ -1057,7 +1056,6 @@ again:
1057 1056
1058 set_bit(STRIPE_IO_STARTED, &sh->state); 1057 set_bit(STRIPE_IO_STARTED, &sh->state);
1059 1058
1060 bio_reset(rbi);
1061 rbi->bi_bdev = rrdev->bdev; 1059 rbi->bi_bdev = rrdev->bdev;
1062 bio_set_op_attrs(rbi, op, op_flags); 1060 bio_set_op_attrs(rbi, op, op_flags);
1063 BUG_ON(!op_is_write(op)); 1061 BUG_ON(!op_is_write(op));
@@ -1990,9 +1988,11 @@ static void raid_run_ops(struct stripe_head *sh, unsigned long ops_request)
1990 put_cpu(); 1988 put_cpu();
1991} 1989}
1992 1990
1993static struct stripe_head *alloc_stripe(struct kmem_cache *sc, gfp_t gfp) 1991static struct stripe_head *alloc_stripe(struct kmem_cache *sc, gfp_t gfp,
1992 int disks)
1994{ 1993{
1995 struct stripe_head *sh; 1994 struct stripe_head *sh;
1995 int i;
1996 1996
1997 sh = kmem_cache_zalloc(sc, gfp); 1997 sh = kmem_cache_zalloc(sc, gfp);
1998 if (sh) { 1998 if (sh) {
@@ -2001,6 +2001,12 @@ static struct stripe_head *alloc_stripe(struct kmem_cache *sc, gfp_t gfp)
2001 INIT_LIST_HEAD(&sh->batch_list); 2001 INIT_LIST_HEAD(&sh->batch_list);
2002 INIT_LIST_HEAD(&sh->lru); 2002 INIT_LIST_HEAD(&sh->lru);
2003 atomic_set(&sh->count, 1); 2003 atomic_set(&sh->count, 1);
2004 for (i = 0; i < disks; i++) {
2005 struct r5dev *dev = &sh->dev[i];
2006
2007 bio_init(&dev->req);
2008 bio_init(&dev->rreq);
2009 }
2004 } 2010 }
2005 return sh; 2011 return sh;
2006} 2012}
@@ -2008,7 +2014,7 @@ static int grow_one_stripe(struct r5conf *conf, gfp_t gfp)
2008{ 2014{
2009 struct stripe_head *sh; 2015 struct stripe_head *sh;
2010 2016
2011 sh = alloc_stripe(conf->slab_cache, gfp); 2017 sh = alloc_stripe(conf->slab_cache, gfp, conf->pool_size);
2012 if (!sh) 2018 if (!sh)
2013 return 0; 2019 return 0;
2014 2020
@@ -2179,7 +2185,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
2179 mutex_lock(&conf->cache_size_mutex); 2185 mutex_lock(&conf->cache_size_mutex);
2180 2186
2181 for (i = conf->max_nr_stripes; i; i--) { 2187 for (i = conf->max_nr_stripes; i; i--) {
2182 nsh = alloc_stripe(sc, GFP_KERNEL); 2188 nsh = alloc_stripe(sc, GFP_KERNEL, newsize);
2183 if (!nsh) 2189 if (!nsh)
2184 break; 2190 break;
2185 2191
@@ -2311,6 +2317,7 @@ static void raid5_end_read_request(struct bio * bi)
2311 (unsigned long long)sh->sector, i, atomic_read(&sh->count), 2317 (unsigned long long)sh->sector, i, atomic_read(&sh->count),
2312 bi->bi_error); 2318 bi->bi_error);
2313 if (i == disks) { 2319 if (i == disks) {
2320 bio_reset(bi);
2314 BUG(); 2321 BUG();
2315 return; 2322 return;
2316 } 2323 }
@@ -2414,6 +2421,7 @@ static void raid5_end_read_request(struct bio * bi)
2414 clear_bit(R5_LOCKED, &sh->dev[i].flags); 2421 clear_bit(R5_LOCKED, &sh->dev[i].flags);
2415 set_bit(STRIPE_HANDLE, &sh->state); 2422 set_bit(STRIPE_HANDLE, &sh->state);
2416 raid5_release_stripe(sh); 2423 raid5_release_stripe(sh);
2424 bio_reset(bi);
2417} 2425}
2418 2426
2419static void raid5_end_write_request(struct bio *bi) 2427static void raid5_end_write_request(struct bio *bi)
@@ -2448,6 +2456,7 @@ static void raid5_end_write_request(struct bio *bi)
2448 (unsigned long long)sh->sector, i, atomic_read(&sh->count), 2456 (unsigned long long)sh->sector, i, atomic_read(&sh->count),
2449 bi->bi_error); 2457 bi->bi_error);
2450 if (i == disks) { 2458 if (i == disks) {
2459 bio_reset(bi);
2451 BUG(); 2460 BUG();
2452 return; 2461 return;
2453 } 2462 }
@@ -2491,18 +2500,17 @@ static void raid5_end_write_request(struct bio *bi)
2491 2500
2492 if (sh->batch_head && sh != sh->batch_head) 2501 if (sh->batch_head && sh != sh->batch_head)
2493 raid5_release_stripe(sh->batch_head); 2502 raid5_release_stripe(sh->batch_head);
2503 bio_reset(bi);
2494} 2504}
2495 2505
2496static void raid5_build_block(struct stripe_head *sh, int i, int previous) 2506static void raid5_build_block(struct stripe_head *sh, int i, int previous)
2497{ 2507{
2498 struct r5dev *dev = &sh->dev[i]; 2508 struct r5dev *dev = &sh->dev[i];
2499 2509
2500 bio_init(&dev->req);
2501 dev->req.bi_io_vec = &dev->vec; 2510 dev->req.bi_io_vec = &dev->vec;
2502 dev->req.bi_max_vecs = 1; 2511 dev->req.bi_max_vecs = 1;
2503 dev->req.bi_private = sh; 2512 dev->req.bi_private = sh;
2504 2513
2505 bio_init(&dev->rreq);
2506 dev->rreq.bi_io_vec = &dev->rvec; 2514 dev->rreq.bi_io_vec = &dev->rvec;
2507 dev->rreq.bi_max_vecs = 1; 2515 dev->rreq.bi_max_vecs = 1;
2508 dev->rreq.bi_private = sh; 2516 dev->rreq.bi_private = sh;