Commit 078d1d8e authored by Gregory Price's avatar Gregory Price Committed by Yu Kuai
Browse files

md/raid0: use kvzalloc/kvfree for strip_zone and devlist allocations



syzbot reported a WARNING at mm/page_alloc.c:__alloc_frozen_pages_noprof()
triggered by create_strip_zones() in the RAID0 driver.

When raid_disks is large, the allocation size exceeds MAX_PAGE_ORDER (4MB
on x86), causing WARN_ON_ONCE_GFP(order > MAX_PAGE_ORDER).

Convert the strip_zone and devlist allocations from kzalloc/kzalloc_objs to
kvzalloc/kvzalloc_objs, which first attempts a contiguous allocation with
__GFP_NOWARN and then falls back to vmalloc for large sizes. Convert the
corresponding kfree calls to kvfree.

Both arrays are pure metadata lookup tables (arrays of pointers and zone
descriptors) accessed only via indexing, so they do not require physically
contiguous memory.

Reported-by: default avatar <syzbot+924649752adf0d3ac9dd@syzkaller.appspotmail.com>
Closes: https://lore.kernel.org/all/69adaba8.a00a0220.b130.0005.GAE@google.com/


Signed-off-by: default avatarGregory Price <gourry@gourry.net>
Reviewed-by: default avatarYu Kuai <yukuai@fnnas.com>
Reviewed-by: default avatarLi Nan <linan122@huawei.com>
Link: https://lore.kernel.org/linux-raid/20260308234202.3118119-1-gourry@gourry.net/


Signed-off-by: default avatarYu Kuai <yukuai@fnnas.com>
parent 2aa72276
Loading
Loading
Loading
Loading
+9 −9
Original line number Diff line number Diff line
@@ -143,10 +143,10 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
	}

	err = -ENOMEM;
	conf->strip_zone = kzalloc_objs(struct strip_zone, conf->nr_strip_zones);
	conf->strip_zone = kvzalloc_objs(struct strip_zone, conf->nr_strip_zones);
	if (!conf->strip_zone)
		goto abort;
	conf->devlist = kzalloc(array3_size(sizeof(struct md_rdev *),
	conf->devlist = kvzalloc(array3_size(sizeof(struct md_rdev *),
					     conf->nr_strip_zones,
					     mddev->raid_disks),
				 GFP_KERNEL);
@@ -291,8 +291,8 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)

	return 0;
abort:
	kfree(conf->strip_zone);
	kfree(conf->devlist);
	kvfree(conf->strip_zone);
	kvfree(conf->devlist);
	kfree(conf);
	*private_conf = ERR_PTR(err);
	return err;
@@ -373,8 +373,8 @@ static void raid0_free(struct mddev *mddev, void *priv)
{
	struct r0conf *conf = priv;

	kfree(conf->strip_zone);
	kfree(conf->devlist);
	kvfree(conf->strip_zone);
	kvfree(conf->devlist);
	kfree(conf);
}