Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 738a2738 authored by NeilBrown's avatar NeilBrown
Browse files

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
Loading
Loading
Loading
Loading
+43 −22
Original line number Original line Diff line number Diff line
@@ -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)
	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)
	} 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
		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)
	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));
}
}