From: Cao Guanghui <[email protected]>
free_conf() iterates conf->pool_size entries to free extra_page
allocations, but pool_size may not reflect the actual size of the
conf->disks array. Two scenarios cause a mismatch:
1. setup_conf() early abort: pool_size is 0 (not yet set by
grow_stripes) but conf->disks has max_disks entries with
extra_page allocated. The loop iterates 0 times, leaking all
pages.
2. resize_stripes() Step 4 failure: conf->disks was replaced with
a newsize-entry array in Step 3, but pool_size is only updated
on success. The loop iterates pool_size (old, smaller value)
times, leaking (newsize - pool_size) pages.
Add a dedicated disks_cnt field to track the actual number of
entries in conf->disks. Set it immediately after each allocation
or replacement (in setup_conf and resize_stripes Step 3, where the
array is safely stalled with no concurrent access), and use it in
free_conf() instead of pool_size.
This leaves pool_size untouched, preserving the check_reshape()
retry behavior that depends on pool_size only being updated on
full success.
Fixes: d7bd398e97f2 ("md/r5cache: handle alloc_page failure")
Cc: [email protected]
Signed-off-by: Cao Guanghui <[email protected]>
---
drivers/md/raid5.c | 4 +++-
drivers/md/raid5.h | 1 +
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2642,6 +2642,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
} else {
kfree(conf->disks);
conf->disks = ndisks;
+ conf->disks_cnt = newsize;
}
} else
err = -ENOMEM;
@@ -7552,7 +7553,7 @@ static void free_conf(struct r5conf *conf)
free_thread_groups(conf);
shrink_stripes(conf);
raid5_free_percpu(conf);
- for (i = 0; i < conf->pool_size; i++)
+ for (i = 0; i < conf->disks_cnt; i++)
if (conf->disks[i].extra_page)
put_page(conf->disks[i].extra_page);
kfree(conf->disks);
@@ -7733,7 +7734,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
if (!conf->disks)
goto abort;
-
+ conf->disks_cnt = max_disks;
for (i = 0; i < max_disks; i++) {
conf->disks[i].extra_page = alloc_page(GFP_KERNEL);
if (!conf->disks[i].extra_page)
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -667,6 +667,7 @@ struct r5conf {
unsigned long cache_state;
struct shrinker *shrinker;
int pool_size; /* number of disks in stripeheads in pool */
+ int disks_cnt; /* number of entries in disks[] array */
spinlock_t device_lock;
struct disk_info *disks;
struct bio_set bio_split;
--
2.34.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.