brintos

brintos / linux-shallow public Read only

0
0
Text · 26.4 KiB · bb2a223 Raw
993 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * KUnit test of ext4 multiblocks allocation.4 */5 6#include <kunit/test.h>7#include <kunit/static_stub.h>8#include <linux/random.h>9 10#include "ext4.h"11 12struct mbt_grp_ctx {13	struct buffer_head bitmap_bh;14	/* desc and gd_bh are just the place holders for now */15	struct ext4_group_desc desc;16	struct buffer_head gd_bh;17};18 19struct mbt_ctx {20	struct mbt_grp_ctx *grp_ctx;21};22 23struct mbt_ext4_super_block {24	struct ext4_super_block es;25	struct ext4_sb_info sbi;26	struct mbt_ctx mbt_ctx;27};28 29#define MBT_SB(_sb) (container_of((_sb)->s_fs_info, struct mbt_ext4_super_block, sbi))30#define MBT_CTX(_sb) (&MBT_SB(_sb)->mbt_ctx)31#define MBT_GRP_CTX(_sb, _group) (&MBT_CTX(_sb)->grp_ctx[_group])32 33static struct inode *mbt_alloc_inode(struct super_block *sb)34{35	struct ext4_inode_info *ei;36 37	ei = kmalloc(sizeof(struct ext4_inode_info), GFP_KERNEL);38	if (!ei)39		return NULL;40 41	INIT_LIST_HEAD(&ei->i_orphan);42	init_rwsem(&ei->xattr_sem);43	init_rwsem(&ei->i_data_sem);44	inode_init_once(&ei->vfs_inode);45	ext4_fc_init_inode(&ei->vfs_inode);46 47	return &ei->vfs_inode;48}49 50static void mbt_free_inode(struct inode *inode)51{52	kfree(EXT4_I(inode));53}54 55static const struct super_operations mbt_sops = {56	.alloc_inode	= mbt_alloc_inode,57	.free_inode	= mbt_free_inode,58};59 60static void mbt_kill_sb(struct super_block *sb)61{62	generic_shutdown_super(sb);63}64 65static struct file_system_type mbt_fs_type = {66	.name			= "mballoc test",67	.kill_sb		= mbt_kill_sb,68};69 70static int mbt_mb_init(struct super_block *sb)71{72	ext4_fsblk_t block;73	int ret;74 75	/* needed by ext4_mb_init->bdev_nonrot(sb->s_bdev) */76	sb->s_bdev = kzalloc(sizeof(*sb->s_bdev), GFP_KERNEL);77	if (sb->s_bdev == NULL)78		return -ENOMEM;79 80	sb->s_bdev->bd_queue = kzalloc(sizeof(struct request_queue), GFP_KERNEL);81	if (sb->s_bdev->bd_queue == NULL) {82		kfree(sb->s_bdev);83		return -ENOMEM;84	}85 86	/*87	 * needed by ext4_mb_init->ext4_mb_init_backend-> sbi->s_buddy_cache =88	 * new_inode(sb);89	 */90	INIT_LIST_HEAD(&sb->s_inodes);91	sb->s_op = &mbt_sops;92 93	ret = ext4_mb_init(sb);94	if (ret != 0)95		goto err_out;96 97	block = ext4_count_free_clusters(sb);98	ret = percpu_counter_init(&EXT4_SB(sb)->s_freeclusters_counter, block,99				  GFP_KERNEL);100	if (ret != 0)101		goto err_mb_release;102 103	ret = percpu_counter_init(&EXT4_SB(sb)->s_dirtyclusters_counter, 0,104				  GFP_KERNEL);105	if (ret != 0)106		goto err_freeclusters;107 108	return 0;109 110err_freeclusters:111	percpu_counter_destroy(&EXT4_SB(sb)->s_freeclusters_counter);112err_mb_release:113	ext4_mb_release(sb);114err_out:115	kfree(sb->s_bdev->bd_queue);116	kfree(sb->s_bdev);117	return ret;118}119 120static void mbt_mb_release(struct super_block *sb)121{122	percpu_counter_destroy(&EXT4_SB(sb)->s_dirtyclusters_counter);123	percpu_counter_destroy(&EXT4_SB(sb)->s_freeclusters_counter);124	ext4_mb_release(sb);125	kfree(sb->s_bdev->bd_queue);126	kfree(sb->s_bdev);127}128 129static int mbt_set(struct super_block *sb, void *data)130{131	return 0;132}133 134static struct super_block *mbt_ext4_alloc_super_block(void)135{136	struct mbt_ext4_super_block *fsb;137	struct super_block *sb;138	struct ext4_sb_info *sbi;139 140	fsb = kzalloc(sizeof(*fsb), GFP_KERNEL);141	if (fsb == NULL)142		return NULL;143 144	sb = sget(&mbt_fs_type, NULL, mbt_set, 0, NULL);145	if (IS_ERR(sb))146		goto out;147 148	sbi = &fsb->sbi;149 150	sbi->s_blockgroup_lock =151		kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);152	if (!sbi->s_blockgroup_lock)153		goto out_deactivate;154 155	bgl_lock_init(sbi->s_blockgroup_lock);156 157	sbi->s_es = &fsb->es;158	sb->s_fs_info = sbi;159 160	up_write(&sb->s_umount);161	return sb;162 163out_deactivate:164	deactivate_locked_super(sb);165out:166	kfree(fsb);167	return NULL;168}169 170static void mbt_ext4_free_super_block(struct super_block *sb)171{172	struct mbt_ext4_super_block *fsb = MBT_SB(sb);173	struct ext4_sb_info *sbi = EXT4_SB(sb);174 175	kfree(sbi->s_blockgroup_lock);176	deactivate_super(sb);177	kfree(fsb);178}179 180struct mbt_ext4_block_layout {181	unsigned char blocksize_bits;182	unsigned int cluster_bits;183	uint32_t blocks_per_group;184	ext4_group_t group_count;185	uint16_t desc_size;186};187 188static void mbt_init_sb_layout(struct super_block *sb,189			       struct mbt_ext4_block_layout *layout)190{191	struct ext4_sb_info *sbi = EXT4_SB(sb);192	struct ext4_super_block *es = sbi->s_es;193 194	sb->s_blocksize = 1UL << layout->blocksize_bits;195	sb->s_blocksize_bits = layout->blocksize_bits;196 197	sbi->s_groups_count = layout->group_count;198	sbi->s_blocks_per_group = layout->blocks_per_group;199	sbi->s_cluster_bits = layout->cluster_bits;200	sbi->s_cluster_ratio = 1U << layout->cluster_bits;201	sbi->s_clusters_per_group = layout->blocks_per_group >>202				    layout->cluster_bits;203	sbi->s_desc_size = layout->desc_size;204	sbi->s_desc_per_block_bits =205		sb->s_blocksize_bits - (fls(layout->desc_size) - 1);206	sbi->s_desc_per_block = 1 << sbi->s_desc_per_block_bits;207 208	es->s_first_data_block = cpu_to_le32(0);209	es->s_blocks_count_lo = cpu_to_le32(layout->blocks_per_group *210					    layout->group_count);211}212 213static int mbt_grp_ctx_init(struct super_block *sb,214			    struct mbt_grp_ctx *grp_ctx)215{216	ext4_grpblk_t max = EXT4_CLUSTERS_PER_GROUP(sb);217 218	grp_ctx->bitmap_bh.b_data = kzalloc(EXT4_BLOCK_SIZE(sb), GFP_KERNEL);219	if (grp_ctx->bitmap_bh.b_data == NULL)220		return -ENOMEM;221	mb_set_bits(grp_ctx->bitmap_bh.b_data, max, sb->s_blocksize * 8 - max);222	ext4_free_group_clusters_set(sb, &grp_ctx->desc, max);223 224	return 0;225}226 227static void mbt_grp_ctx_release(struct mbt_grp_ctx *grp_ctx)228{229	kfree(grp_ctx->bitmap_bh.b_data);230	grp_ctx->bitmap_bh.b_data = NULL;231}232 233static void mbt_ctx_mark_used(struct super_block *sb, ext4_group_t group,234			      unsigned int start, unsigned int len)235{236	struct mbt_grp_ctx *grp_ctx = MBT_GRP_CTX(sb, group);237 238	mb_set_bits(grp_ctx->bitmap_bh.b_data, start, len);239}240 241static void *mbt_ctx_bitmap(struct super_block *sb, ext4_group_t group)242{243	struct mbt_grp_ctx *grp_ctx = MBT_GRP_CTX(sb, group);244 245	return grp_ctx->bitmap_bh.b_data;246}247 248/* called after mbt_init_sb_layout */249static int mbt_ctx_init(struct super_block *sb)250{251	struct mbt_ctx *ctx = MBT_CTX(sb);252	ext4_group_t i, ngroups = ext4_get_groups_count(sb);253 254	ctx->grp_ctx = kcalloc(ngroups, sizeof(struct mbt_grp_ctx),255			       GFP_KERNEL);256	if (ctx->grp_ctx == NULL)257		return -ENOMEM;258 259	for (i = 0; i < ngroups; i++)260		if (mbt_grp_ctx_init(sb, &ctx->grp_ctx[i]))261			goto out;262 263	/*264	 * first data block(first cluster in first group) is used by265	 * metadata, mark it used to avoid to alloc data block at first266	 * block which will fail ext4_sb_block_valid check.267	 */268	mb_set_bits(ctx->grp_ctx[0].bitmap_bh.b_data, 0, 1);269	ext4_free_group_clusters_set(sb, &ctx->grp_ctx[0].desc,270				     EXT4_CLUSTERS_PER_GROUP(sb) - 1);271 272	return 0;273out:274	while (i-- > 0)275		mbt_grp_ctx_release(&ctx->grp_ctx[i]);276	kfree(ctx->grp_ctx);277	return -ENOMEM;278}279 280static void mbt_ctx_release(struct super_block *sb)281{282	struct mbt_ctx *ctx = MBT_CTX(sb);283	ext4_group_t i, ngroups = ext4_get_groups_count(sb);284 285	for (i = 0; i < ngroups; i++)286		mbt_grp_ctx_release(&ctx->grp_ctx[i]);287	kfree(ctx->grp_ctx);288}289 290static struct buffer_head *291ext4_read_block_bitmap_nowait_stub(struct super_block *sb, ext4_group_t block_group,292				   bool ignore_locked)293{294	struct mbt_grp_ctx *grp_ctx = MBT_GRP_CTX(sb, block_group);295 296	/* paired with brelse from caller of ext4_read_block_bitmap_nowait */297	get_bh(&grp_ctx->bitmap_bh);298	return &grp_ctx->bitmap_bh;299}300 301static int ext4_wait_block_bitmap_stub(struct super_block *sb,302				       ext4_group_t block_group,303				       struct buffer_head *bh)304{305	/*306	 * real ext4_wait_block_bitmap will set these flags and307	 * functions like ext4_mb_init_cache will verify the flags.308	 */309	set_buffer_uptodate(bh);310	set_bitmap_uptodate(bh);311	set_buffer_verified(bh);312	return 0;313}314 315static struct ext4_group_desc *316ext4_get_group_desc_stub(struct super_block *sb, ext4_group_t block_group,317			 struct buffer_head **bh)318{319	struct mbt_grp_ctx *grp_ctx = MBT_GRP_CTX(sb, block_group);320 321	if (bh != NULL)322		*bh = &grp_ctx->gd_bh;323 324	return &grp_ctx->desc;325}326 327static int328ext4_mb_mark_context_stub(handle_t *handle, struct super_block *sb, bool state,329			  ext4_group_t group, ext4_grpblk_t blkoff,330			  ext4_grpblk_t len, int flags,331			  ext4_grpblk_t *ret_changed)332{333	struct mbt_grp_ctx *grp_ctx = MBT_GRP_CTX(sb, group);334	struct buffer_head *bitmap_bh = &grp_ctx->bitmap_bh;335 336	if (state)337		mb_set_bits(bitmap_bh->b_data, blkoff, len);338	else339		mb_clear_bits(bitmap_bh->b_data, blkoff, len);340 341	return 0;342}343 344#define TEST_GOAL_GROUP 1345static int mbt_kunit_init(struct kunit *test)346{347	struct mbt_ext4_block_layout *layout =348		(struct mbt_ext4_block_layout *)(test->param_value);349	struct super_block *sb;350	int ret;351 352	sb = mbt_ext4_alloc_super_block();353	if (sb == NULL)354		return -ENOMEM;355 356	mbt_init_sb_layout(sb, layout);357 358	ret = mbt_ctx_init(sb);359	if (ret != 0) {360		mbt_ext4_free_super_block(sb);361		return ret;362	}363 364	test->priv = sb;365	kunit_activate_static_stub(test,366				   ext4_read_block_bitmap_nowait,367				   ext4_read_block_bitmap_nowait_stub);368	kunit_activate_static_stub(test,369				   ext4_wait_block_bitmap,370				   ext4_wait_block_bitmap_stub);371	kunit_activate_static_stub(test,372				   ext4_get_group_desc,373				   ext4_get_group_desc_stub);374	kunit_activate_static_stub(test,375				   ext4_mb_mark_context,376				   ext4_mb_mark_context_stub);377 378	/* stub function will be called in mbt_mb_init->ext4_mb_init */379	if (mbt_mb_init(sb) != 0) {380		mbt_ctx_release(sb);381		mbt_ext4_free_super_block(sb);382		return -ENOMEM;383	}384 385	return 0;386}387 388static void mbt_kunit_exit(struct kunit *test)389{390	struct super_block *sb = (struct super_block *)test->priv;391 392	mbt_mb_release(sb);393	mbt_ctx_release(sb);394	mbt_ext4_free_super_block(sb);395}396 397static void test_new_blocks_simple(struct kunit *test)398{399	struct super_block *sb = (struct super_block *)test->priv;400	struct inode *inode;401	struct ext4_allocation_request ar;402	ext4_group_t i, goal_group = TEST_GOAL_GROUP;403	int err = 0;404	ext4_fsblk_t found;405	struct ext4_sb_info *sbi = EXT4_SB(sb);406 407	inode = kunit_kzalloc(test, sizeof(*inode), GFP_KERNEL);408	if (!inode)409		return;410 411	inode->i_sb = sb;412	ar.inode = inode;413 414	/* get block at goal */415	ar.goal = ext4_group_first_block_no(sb, goal_group);416	found = ext4_mb_new_blocks_simple(&ar, &err);417	KUNIT_ASSERT_EQ_MSG(test, ar.goal, found,418		"failed to alloc block at goal, expected %llu found %llu",419		ar.goal, found);420 421	/* get block after goal in goal group */422	ar.goal = ext4_group_first_block_no(sb, goal_group);423	found = ext4_mb_new_blocks_simple(&ar, &err);424	KUNIT_ASSERT_EQ_MSG(test, ar.goal + EXT4_C2B(sbi, 1), found,425		"failed to alloc block after goal in goal group, expected %llu found %llu",426		ar.goal + 1, found);427 428	/* get block after goal group */429	mbt_ctx_mark_used(sb, goal_group, 0, EXT4_CLUSTERS_PER_GROUP(sb));430	ar.goal = ext4_group_first_block_no(sb, goal_group);431	found = ext4_mb_new_blocks_simple(&ar, &err);432	KUNIT_ASSERT_EQ_MSG(test,433		ext4_group_first_block_no(sb, goal_group + 1), found,434		"failed to alloc block after goal group, expected %llu found %llu",435		ext4_group_first_block_no(sb, goal_group + 1), found);436 437	/* get block before goal group */438	for (i = goal_group; i < ext4_get_groups_count(sb); i++)439		mbt_ctx_mark_used(sb, i, 0, EXT4_CLUSTERS_PER_GROUP(sb));440	ar.goal = ext4_group_first_block_no(sb, goal_group);441	found = ext4_mb_new_blocks_simple(&ar, &err);442	KUNIT_ASSERT_EQ_MSG(test,443		ext4_group_first_block_no(sb, 0) + EXT4_C2B(sbi, 1), found,444		"failed to alloc block before goal group, expected %llu found %llu",445		ext4_group_first_block_no(sb, 0 + EXT4_C2B(sbi, 1)), found);446 447	/* no block available, fail to allocate block */448	for (i = 0; i < ext4_get_groups_count(sb); i++)449		mbt_ctx_mark_used(sb, i, 0, EXT4_CLUSTERS_PER_GROUP(sb));450	ar.goal = ext4_group_first_block_no(sb, goal_group);451	found = ext4_mb_new_blocks_simple(&ar, &err);452	KUNIT_ASSERT_NE_MSG(test, err, 0,453		"unexpectedly get block when no block is available");454}455 456#define TEST_RANGE_COUNT 8457 458struct test_range {459	ext4_grpblk_t start;460	ext4_grpblk_t len;461};462 463static void464mbt_generate_test_ranges(struct super_block *sb, struct test_range *ranges,465			 int count)466{467	ext4_grpblk_t start, len, max;468	int i;469 470	max = EXT4_CLUSTERS_PER_GROUP(sb) / count;471	for (i = 0; i < count; i++) {472		start = get_random_u32() % max;473		len = get_random_u32() % max;474		len = min(len, max - start);475 476		ranges[i].start = start + i * max;477		ranges[i].len = len;478	}479}480 481static void482validate_free_blocks_simple(struct kunit *test, struct super_block *sb,483			    ext4_group_t goal_group, ext4_grpblk_t start,484			    ext4_grpblk_t len)485{486	void *bitmap;487	ext4_grpblk_t bit, max = EXT4_CLUSTERS_PER_GROUP(sb);488	ext4_group_t i;489 490	for (i = 0; i < ext4_get_groups_count(sb); i++) {491		if (i == goal_group)492			continue;493 494		bitmap = mbt_ctx_bitmap(sb, i);495		bit = mb_find_next_zero_bit(bitmap, max, 0);496		KUNIT_ASSERT_EQ_MSG(test, bit, max,497				    "free block on unexpected group %d", i);498	}499 500	bitmap = mbt_ctx_bitmap(sb, goal_group);501	bit = mb_find_next_zero_bit(bitmap, max, 0);502	KUNIT_ASSERT_EQ(test, bit, start);503 504	bit = mb_find_next_bit(bitmap, max, bit + 1);505	KUNIT_ASSERT_EQ(test, bit, start + len);506}507 508static void509test_free_blocks_simple_range(struct kunit *test, ext4_group_t goal_group,510			      ext4_grpblk_t start, ext4_grpblk_t len)511{512	struct super_block *sb = (struct super_block *)test->priv;513	struct ext4_sb_info *sbi = EXT4_SB(sb);514	struct inode *inode;515	ext4_fsblk_t block;516 517	inode = kunit_kzalloc(test, sizeof(*inode), GFP_KERNEL);518	if (!inode)519		return;520	inode->i_sb = sb;521 522	if (len == 0)523		return;524 525	block = ext4_group_first_block_no(sb, goal_group) +526		EXT4_C2B(sbi, start);527	ext4_free_blocks_simple(inode, block, len);528	validate_free_blocks_simple(test, sb, goal_group, start, len);529	mbt_ctx_mark_used(sb, goal_group, 0, EXT4_CLUSTERS_PER_GROUP(sb));530}531 532static void test_free_blocks_simple(struct kunit *test)533{534	struct super_block *sb = (struct super_block *)test->priv;535	ext4_grpblk_t max = EXT4_CLUSTERS_PER_GROUP(sb);536	ext4_group_t i;537	struct test_range ranges[TEST_RANGE_COUNT];538 539	for (i = 0; i < ext4_get_groups_count(sb); i++)540		mbt_ctx_mark_used(sb, i, 0, max);541 542	mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT);543	for (i = 0; i < TEST_RANGE_COUNT; i++)544		test_free_blocks_simple_range(test, TEST_GOAL_GROUP,545			ranges[i].start, ranges[i].len);546}547 548static void549test_mark_diskspace_used_range(struct kunit *test,550			       struct ext4_allocation_context *ac,551			       ext4_grpblk_t start,552			       ext4_grpblk_t len)553{554	struct super_block *sb = (struct super_block *)test->priv;555	int ret;556	void *bitmap;557	ext4_grpblk_t i, max;558 559	/* ext4_mb_mark_diskspace_used will BUG if len is 0 */560	if (len == 0)561		return;562 563	ac->ac_b_ex.fe_group = TEST_GOAL_GROUP;564	ac->ac_b_ex.fe_start = start;565	ac->ac_b_ex.fe_len = len;566 567	bitmap = mbt_ctx_bitmap(sb, TEST_GOAL_GROUP);568	memset(bitmap, 0, sb->s_blocksize);569	ret = ext4_mb_mark_diskspace_used(ac, NULL, 0);570	KUNIT_ASSERT_EQ(test, ret, 0);571 572	max = EXT4_CLUSTERS_PER_GROUP(sb);573	i = mb_find_next_bit(bitmap, max, 0);574	KUNIT_ASSERT_EQ(test, i, start);575	i = mb_find_next_zero_bit(bitmap, max, i + 1);576	KUNIT_ASSERT_EQ(test, i, start + len);577	i = mb_find_next_bit(bitmap, max, i + 1);578	KUNIT_ASSERT_EQ(test, max, i);579}580 581static void test_mark_diskspace_used(struct kunit *test)582{583	struct super_block *sb = (struct super_block *)test->priv;584	struct inode *inode;585	struct ext4_allocation_context ac;586	struct test_range ranges[TEST_RANGE_COUNT];587	int i;588 589	mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT);590 591	inode = kunit_kzalloc(test, sizeof(*inode), GFP_KERNEL);592	if (!inode)593		return;594	inode->i_sb = sb;595 596	ac.ac_status = AC_STATUS_FOUND;597	ac.ac_sb = sb;598	ac.ac_inode = inode;599	for (i = 0; i < TEST_RANGE_COUNT; i++)600		test_mark_diskspace_used_range(test, &ac, ranges[i].start,601					       ranges[i].len);602}603 604static void mbt_generate_buddy(struct super_block *sb, void *buddy,605			       void *bitmap, struct ext4_group_info *grp)606{607	struct ext4_sb_info *sbi = EXT4_SB(sb);608	uint32_t order, off;609	void *bb, *bb_h;610	int max;611 612	memset(buddy, 0xff, sb->s_blocksize);613	memset(grp, 0, offsetof(struct ext4_group_info,614				 bb_counters[MB_NUM_ORDERS(sb)]));615 616	bb = bitmap;617	max = EXT4_CLUSTERS_PER_GROUP(sb);618	bb_h = buddy + sbi->s_mb_offsets[1];619 620	off = mb_find_next_zero_bit(bb, max, 0);621	grp->bb_first_free = off;622	while (off < max) {623		grp->bb_counters[0]++;624		grp->bb_free++;625 626		if (!(off & 1) && !mb_test_bit(off + 1, bb)) {627			grp->bb_free++;628			grp->bb_counters[0]--;629			mb_clear_bit(off >> 1, bb_h);630			grp->bb_counters[1]++;631			grp->bb_largest_free_order = 1;632			off++;633		}634 635		off = mb_find_next_zero_bit(bb, max, off + 1);636	}637 638	for (order = 1; order < MB_NUM_ORDERS(sb) - 1; order++) {639		bb = buddy + sbi->s_mb_offsets[order];640		bb_h = buddy + sbi->s_mb_offsets[order + 1];641		max = max >> 1;642		off = mb_find_next_zero_bit(bb, max, 0);643 644		while (off < max) {645			if (!(off & 1) && !mb_test_bit(off + 1, bb)) {646				mb_set_bits(bb, off, 2);647				grp->bb_counters[order] -= 2;648				mb_clear_bit(off >> 1, bb_h);649				grp->bb_counters[order + 1]++;650				grp->bb_largest_free_order = order + 1;651				off++;652			}653 654			off = mb_find_next_zero_bit(bb, max, off + 1);655		}656	}657 658	max = EXT4_CLUSTERS_PER_GROUP(sb);659	off = mb_find_next_zero_bit(bitmap, max, 0);660	while (off < max) {661		grp->bb_fragments++;662 663		off = mb_find_next_bit(bitmap, max, off + 1);664		if (off + 1 >= max)665			break;666 667		off = mb_find_next_zero_bit(bitmap, max, off + 1);668	}669}670 671static void672mbt_validate_group_info(struct kunit *test, struct ext4_group_info *grp1,673			struct ext4_group_info *grp2)674{675	struct super_block *sb = (struct super_block *)test->priv;676	int i;677 678	KUNIT_ASSERT_EQ(test, grp1->bb_first_free,679			grp2->bb_first_free);680	KUNIT_ASSERT_EQ(test, grp1->bb_fragments,681			grp2->bb_fragments);682	KUNIT_ASSERT_EQ(test, grp1->bb_free, grp2->bb_free);683	KUNIT_ASSERT_EQ(test, grp1->bb_largest_free_order,684			grp2->bb_largest_free_order);685 686	for (i = 1; i < MB_NUM_ORDERS(sb); i++) {687		KUNIT_ASSERT_EQ_MSG(test, grp1->bb_counters[i],688				    grp2->bb_counters[i],689				    "bb_counters[%d] diffs, expected %d, generated %d",690				    i, grp1->bb_counters[i],691				    grp2->bb_counters[i]);692	}693}694 695static void696do_test_generate_buddy(struct kunit *test, struct super_block *sb, void *bitmap,697			   void *mbt_buddy, struct ext4_group_info *mbt_grp,698			   void *ext4_buddy, struct ext4_group_info *ext4_grp)699{700	int i;701 702	mbt_generate_buddy(sb, mbt_buddy, bitmap, mbt_grp);703 704	for (i = 0; i < MB_NUM_ORDERS(sb); i++)705		ext4_grp->bb_counters[i] = 0;706	/* needed by validation in ext4_mb_generate_buddy */707	ext4_grp->bb_free = mbt_grp->bb_free;708	memset(ext4_buddy, 0xff, sb->s_blocksize);709	ext4_mb_generate_buddy(sb, ext4_buddy, bitmap, TEST_GOAL_GROUP,710			       ext4_grp);711 712	KUNIT_ASSERT_EQ(test, memcmp(mbt_buddy, ext4_buddy, sb->s_blocksize),713			0);714	mbt_validate_group_info(test, mbt_grp, ext4_grp);715}716 717static void test_mb_generate_buddy(struct kunit *test)718{719	struct super_block *sb = (struct super_block *)test->priv;720	void *bitmap, *expected_bb, *generate_bb;721	struct ext4_group_info *expected_grp, *generate_grp;722	struct test_range ranges[TEST_RANGE_COUNT];723	int i;724 725	bitmap = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL);726	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bitmap);727	expected_bb = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL);728	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected_bb);729	generate_bb = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL);730	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, generate_bb);731	expected_grp = kunit_kzalloc(test, offsetof(struct ext4_group_info,732				bb_counters[MB_NUM_ORDERS(sb)]), GFP_KERNEL);733	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected_grp);734	generate_grp = ext4_get_group_info(sb, TEST_GOAL_GROUP);735	KUNIT_ASSERT_NOT_NULL(test, generate_grp);736 737	mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT);738	for (i = 0; i < TEST_RANGE_COUNT; i++) {739		mb_set_bits(bitmap, ranges[i].start, ranges[i].len);740		do_test_generate_buddy(test, sb, bitmap, expected_bb,741				       expected_grp, generate_bb, generate_grp);742	}743}744 745static void746test_mb_mark_used_range(struct kunit *test, struct ext4_buddy *e4b,747			ext4_grpblk_t start, ext4_grpblk_t len, void *bitmap,748			void *buddy, struct ext4_group_info *grp)749{750	struct super_block *sb = (struct super_block *)test->priv;751	struct ext4_free_extent ex;752	int i;753 754	/* mb_mark_used only accepts non-zero len */755	if (len == 0)756		return;757 758	ex.fe_start = start;759	ex.fe_len = len;760	ex.fe_group = TEST_GOAL_GROUP;761 762	ext4_lock_group(sb, TEST_GOAL_GROUP);763	mb_mark_used(e4b, &ex);764	ext4_unlock_group(sb, TEST_GOAL_GROUP);765 766	mb_set_bits(bitmap, start, len);767	/* bypass bb_free validatoin in ext4_mb_generate_buddy */768	grp->bb_free -= len;769	memset(buddy, 0xff, sb->s_blocksize);770	for (i = 0; i < MB_NUM_ORDERS(sb); i++)771		grp->bb_counters[i] = 0;772	ext4_mb_generate_buddy(sb, buddy, bitmap, 0, grp);773 774	KUNIT_ASSERT_EQ(test, memcmp(buddy, e4b->bd_buddy, sb->s_blocksize),775			0);776	mbt_validate_group_info(test, grp, e4b->bd_info);777}778 779static void test_mb_mark_used(struct kunit *test)780{781	struct ext4_buddy e4b;782	struct super_block *sb = (struct super_block *)test->priv;783	void *bitmap, *buddy;784	struct ext4_group_info *grp;785	int ret;786	struct test_range ranges[TEST_RANGE_COUNT];787	int i;788 789	/* buddy cache assumes that each page contains at least one block */790	if (sb->s_blocksize > PAGE_SIZE)791		kunit_skip(test, "blocksize exceeds pagesize");792 793	bitmap = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL);794	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bitmap);795	buddy = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL);796	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buddy);797	grp = kunit_kzalloc(test, offsetof(struct ext4_group_info,798				bb_counters[MB_NUM_ORDERS(sb)]), GFP_KERNEL);799 800	ret = ext4_mb_load_buddy(sb, TEST_GOAL_GROUP, &e4b);801	KUNIT_ASSERT_EQ(test, ret, 0);802 803	grp->bb_free = EXT4_CLUSTERS_PER_GROUP(sb);804	mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT);805	for (i = 0; i < TEST_RANGE_COUNT; i++)806		test_mb_mark_used_range(test, &e4b, ranges[i].start,807					ranges[i].len, bitmap, buddy, grp);808 809	ext4_mb_unload_buddy(&e4b);810}811 812static void813test_mb_free_blocks_range(struct kunit *test, struct ext4_buddy *e4b,814			  ext4_grpblk_t start, ext4_grpblk_t len, void *bitmap,815			  void *buddy, struct ext4_group_info *grp)816{817	struct super_block *sb = (struct super_block *)test->priv;818	int i;819 820	/* mb_free_blocks will WARN if len is 0 */821	if (len == 0)822		return;823 824	ext4_lock_group(sb, e4b->bd_group);825	mb_free_blocks(NULL, e4b, start, len);826	ext4_unlock_group(sb, e4b->bd_group);827 828	mb_clear_bits(bitmap, start, len);829	/* bypass bb_free validatoin in ext4_mb_generate_buddy */830	grp->bb_free += len;831	memset(buddy, 0xff, sb->s_blocksize);832	for (i = 0; i < MB_NUM_ORDERS(sb); i++)833		grp->bb_counters[i] = 0;834	ext4_mb_generate_buddy(sb, buddy, bitmap, 0, grp);835 836	KUNIT_ASSERT_EQ(test, memcmp(buddy, e4b->bd_buddy, sb->s_blocksize),837			0);838	mbt_validate_group_info(test, grp, e4b->bd_info);839 840}841 842static void test_mb_free_blocks(struct kunit *test)843{844	struct ext4_buddy e4b;845	struct super_block *sb = (struct super_block *)test->priv;846	void *bitmap, *buddy;847	struct ext4_group_info *grp;848	struct ext4_free_extent ex;849	int ret;850	int i;851	struct test_range ranges[TEST_RANGE_COUNT];852 853	/* buddy cache assumes that each page contains at least one block */854	if (sb->s_blocksize > PAGE_SIZE)855		kunit_skip(test, "blocksize exceeds pagesize");856 857	bitmap = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL);858	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bitmap);859	buddy = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL);860	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buddy);861	grp = kunit_kzalloc(test, offsetof(struct ext4_group_info,862				bb_counters[MB_NUM_ORDERS(sb)]), GFP_KERNEL);863 864	ret = ext4_mb_load_buddy(sb, TEST_GOAL_GROUP, &e4b);865	KUNIT_ASSERT_EQ(test, ret, 0);866 867	ex.fe_start = 0;868	ex.fe_len = EXT4_CLUSTERS_PER_GROUP(sb);869	ex.fe_group = TEST_GOAL_GROUP;870 871	ext4_lock_group(sb, TEST_GOAL_GROUP);872	mb_mark_used(&e4b, &ex);873	ext4_unlock_group(sb, TEST_GOAL_GROUP);874 875	grp->bb_free = 0;876	memset(bitmap, 0xff, sb->s_blocksize);877 878	mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT);879	for (i = 0; i < TEST_RANGE_COUNT; i++)880		test_mb_free_blocks_range(test, &e4b, ranges[i].start,881					  ranges[i].len, bitmap, buddy, grp);882 883	ext4_mb_unload_buddy(&e4b);884}885 886#define COUNT_FOR_ESTIMATE 100000887static void test_mb_mark_used_cost(struct kunit *test)888{889	struct ext4_buddy e4b;890	struct super_block *sb = (struct super_block *)test->priv;891	struct ext4_free_extent ex;892	int ret;893	struct test_range ranges[TEST_RANGE_COUNT];894	int i, j;895	unsigned long start, end, all = 0;896 897	/* buddy cache assumes that each page contains at least one block */898	if (sb->s_blocksize > PAGE_SIZE)899		kunit_skip(test, "blocksize exceeds pagesize");900 901	ret = ext4_mb_load_buddy(sb, TEST_GOAL_GROUP, &e4b);902	KUNIT_ASSERT_EQ(test, ret, 0);903 904	ex.fe_group = TEST_GOAL_GROUP;905	for (j = 0; j < COUNT_FOR_ESTIMATE; j++) {906		mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT);907		start = jiffies;908		for (i = 0; i < TEST_RANGE_COUNT; i++) {909			if (ranges[i].len == 0)910				continue;911 912			ex.fe_start = ranges[i].start;913			ex.fe_len = ranges[i].len;914			ext4_lock_group(sb, TEST_GOAL_GROUP);915			mb_mark_used(&e4b, &ex);916			ext4_unlock_group(sb, TEST_GOAL_GROUP);917		}918		end = jiffies;919		all += (end - start);920 921		for (i = 0; i < TEST_RANGE_COUNT; i++) {922			if (ranges[i].len == 0)923				continue;924 925			ext4_lock_group(sb, TEST_GOAL_GROUP);926			mb_free_blocks(NULL, &e4b, ranges[i].start,927				       ranges[i].len);928			ext4_unlock_group(sb, TEST_GOAL_GROUP);929		}930	}931 932	kunit_info(test, "costed jiffies %lu\n", all);933	ext4_mb_unload_buddy(&e4b);934}935 936static const struct mbt_ext4_block_layout mbt_test_layouts[] = {937	{938		.blocksize_bits = 10,939		.cluster_bits = 3,940		.blocks_per_group = 8192,941		.group_count = 4,942		.desc_size = 64,943	},944	{945		.blocksize_bits = 12,946		.cluster_bits = 3,947		.blocks_per_group = 8192,948		.group_count = 4,949		.desc_size = 64,950	},951	{952		.blocksize_bits = 16,953		.cluster_bits = 3,954		.blocks_per_group = 8192,955		.group_count = 4,956		.desc_size = 64,957	},958};959 960static void mbt_show_layout(const struct mbt_ext4_block_layout *layout,961			    char *desc)962{963	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "block_bits=%d cluster_bits=%d "964		 "blocks_per_group=%d group_count=%d desc_size=%d\n",965		 layout->blocksize_bits, layout->cluster_bits,966		 layout->blocks_per_group, layout->group_count,967		 layout->desc_size);968}969KUNIT_ARRAY_PARAM(mbt_layouts, mbt_test_layouts, mbt_show_layout);970 971static struct kunit_case mbt_test_cases[] = {972	KUNIT_CASE_PARAM(test_new_blocks_simple, mbt_layouts_gen_params),973	KUNIT_CASE_PARAM(test_free_blocks_simple, mbt_layouts_gen_params),974	KUNIT_CASE_PARAM(test_mb_generate_buddy, mbt_layouts_gen_params),975	KUNIT_CASE_PARAM(test_mb_mark_used, mbt_layouts_gen_params),976	KUNIT_CASE_PARAM(test_mb_free_blocks, mbt_layouts_gen_params),977	KUNIT_CASE_PARAM(test_mark_diskspace_used, mbt_layouts_gen_params),978	KUNIT_CASE_PARAM_ATTR(test_mb_mark_used_cost, mbt_layouts_gen_params,979			      { .speed = KUNIT_SPEED_SLOW }),980	{}981};982 983static struct kunit_suite mbt_test_suite = {984	.name = "ext4_mballoc_test",985	.init = mbt_kunit_init,986	.exit = mbt_kunit_exit,987	.test_cases = mbt_test_cases,988};989 990kunit_test_suites(&mbt_test_suite);991 992MODULE_LICENSE("GPL");993