Commit 738a2738 authored by NeilBrown's avatar NeilBrown

md/raid5: fix allocation of 'scribble' array.

As the new 'scribble' array is sized based on chunk size,
we need to make sure the size matches the largest of 'old'
and 'new' chunk sizes when the array is undergoing reshape.

We also potentially need to resize it even when not resizing
the stripe cache, as chunk size can change without changing
number of devices.

So move the 'resize' code into a separate function, and
consider old and new sizes when allocating.
Signed-off-by: default avatarNeilBrown <neilb@suse.de>
Fixes: 46d5b785 ("raid5: use flex_array for scribble data")
parent 6e9eac2d
...@@ -2068,6 +2068,35 @@ static struct flex_array *scribble_alloc(int num, int cnt, gfp_t flags) ...@@ -2068,6 +2068,35 @@ static struct flex_array *scribble_alloc(int num, int cnt, gfp_t flags)
return ret; return ret;
} }
static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors)
{
unsigned long cpu;
int err = 0;
mddev_suspend(conf->mddev);
get_online_cpus();
for_each_present_cpu(cpu) {
struct raid5_percpu *percpu;
struct flex_array *scribble;
percpu = per_cpu_ptr(conf->percpu, cpu);
scribble = scribble_alloc(new_disks,
new_sectors / STRIPE_SECTORS,
GFP_NOIO);
if (scribble) {
flex_array_free(percpu->scribble);
percpu->scribble = scribble;
} else {
err = -ENOMEM;
break;
}
}
put_online_cpus();
mddev_resume(conf->mddev);
return err;
}
static int resize_stripes(struct r5conf *conf, int newsize) static int resize_stripes(struct r5conf *conf, int newsize)
{ {
/* Make all the stripes able to hold 'newsize' devices. /* Make all the stripes able to hold 'newsize' devices.
...@@ -2096,7 +2125,6 @@ static int resize_stripes(struct r5conf *conf, int newsize) ...@@ -2096,7 +2125,6 @@ static int resize_stripes(struct r5conf *conf, int newsize)
struct stripe_head *osh, *nsh; struct stripe_head *osh, *nsh;
LIST_HEAD(newstripes); LIST_HEAD(newstripes);
struct disk_info *ndisks; struct disk_info *ndisks;
unsigned long cpu;
int err; int err;
struct kmem_cache *sc; struct kmem_cache *sc;
int i; int i;
...@@ -2178,25 +2206,6 @@ static int resize_stripes(struct r5conf *conf, int newsize) ...@@ -2178,25 +2206,6 @@ static int resize_stripes(struct r5conf *conf, int newsize)
} else } else
err = -ENOMEM; err = -ENOMEM;
get_online_cpus();
for_each_present_cpu(cpu) {
struct raid5_percpu *percpu;
struct flex_array *scribble;
percpu = per_cpu_ptr(conf->percpu, cpu);
scribble = scribble_alloc(newsize, conf->chunk_sectors /
STRIPE_SECTORS, GFP_NOIO);
if (scribble) {
flex_array_free(percpu->scribble);
percpu->scribble = scribble;
} else {
err = -ENOMEM;
break;
}
}
put_online_cpus();
/* Step 4, return new stripes to service */ /* Step 4, return new stripes to service */
while(!list_empty(&newstripes)) { while(!list_empty(&newstripes)) {
nsh = list_entry(newstripes.next, struct stripe_head, lru); nsh = list_entry(newstripes.next, struct stripe_head, lru);
...@@ -6228,8 +6237,11 @@ static int alloc_scratch_buffer(struct r5conf *conf, struct raid5_percpu *percpu ...@@ -6228,8 +6237,11 @@ static int alloc_scratch_buffer(struct r5conf *conf, struct raid5_percpu *percpu
percpu->spare_page = alloc_page(GFP_KERNEL); percpu->spare_page = alloc_page(GFP_KERNEL);
if (!percpu->scribble) if (!percpu->scribble)
percpu->scribble = scribble_alloc(max(conf->raid_disks, percpu->scribble = scribble_alloc(max(conf->raid_disks,
conf->previous_raid_disks), conf->chunk_sectors / conf->previous_raid_disks),
STRIPE_SECTORS, GFP_KERNEL); max(conf->chunk_sectors,
conf->prev_chunk_sectors)
/ STRIPE_SECTORS,
GFP_KERNEL);
if (!percpu->scribble || (conf->level == 6 && !percpu->spare_page)) { if (!percpu->scribble || (conf->level == 6 && !percpu->spare_page)) {
free_scratch_buffer(conf, percpu); free_scratch_buffer(conf, percpu);
...@@ -7205,6 +7217,15 @@ static int check_reshape(struct mddev *mddev) ...@@ -7205,6 +7217,15 @@ static int check_reshape(struct mddev *mddev)
if (!check_stripe_cache(mddev)) if (!check_stripe_cache(mddev))
return -ENOSPC; return -ENOSPC;
if (mddev->new_chunk_sectors > mddev->chunk_sectors ||
mddev->delta_disks > 0)
if (resize_chunks(conf,
conf->previous_raid_disks
+ max(0, mddev->delta_disks),
max(mddev->new_chunk_sectors,
mddev->chunk_sectors)
) < 0)
return -ENOMEM;
return resize_stripes(conf, (conf->previous_raid_disks return resize_stripes(conf, (conf->previous_raid_disks
+ mddev->delta_disks)); + mddev->delta_disks));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment