xref: /linux/drivers/char/misc_minor_kunit.c (revision f4e47affdb2edb99834dfd03f5ea8a899bbb98f0)
18ac646d6SZijun Hu // SPDX-License-Identifier: GPL-2.0
28ac646d6SZijun Hu #include <kunit/test.h>
38ac646d6SZijun Hu #include <kunit/test-bug.h>
48ac646d6SZijun Hu #include <linux/module.h>
58ac646d6SZijun Hu #include <linux/miscdevice.h>
68ac646d6SZijun Hu #include <linux/fs.h>
78ac646d6SZijun Hu #include <linux/file.h>
88ac646d6SZijun Hu #include <linux/init_syscalls.h>
98ac646d6SZijun Hu 
108ac646d6SZijun Hu /* static minor (LCD_MINOR) */
118ac646d6SZijun Hu static struct miscdevice dev_static_minor = {
128ac646d6SZijun Hu 	.minor  = LCD_MINOR,
138ac646d6SZijun Hu 	.name   = "dev_static_minor",
148ac646d6SZijun Hu };
158ac646d6SZijun Hu 
168ac646d6SZijun Hu /* misc dynamic minor */
178ac646d6SZijun Hu static struct miscdevice dev_misc_dynamic_minor = {
188ac646d6SZijun Hu 	.minor  = MISC_DYNAMIC_MINOR,
198ac646d6SZijun Hu 	.name   = "dev_misc_dynamic_minor",
208ac646d6SZijun Hu };
218ac646d6SZijun Hu 
228ac646d6SZijun Hu static void kunit_static_minor(struct kunit *test)
238ac646d6SZijun Hu {
248ac646d6SZijun Hu 	int ret;
258ac646d6SZijun Hu 
268ac646d6SZijun Hu 	ret = misc_register(&dev_static_minor);
278ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, 0, ret);
288ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, LCD_MINOR, dev_static_minor.minor);
298ac646d6SZijun Hu 	misc_deregister(&dev_static_minor);
308ac646d6SZijun Hu }
318ac646d6SZijun Hu 
328ac646d6SZijun Hu static void kunit_misc_dynamic_minor(struct kunit *test)
338ac646d6SZijun Hu {
348ac646d6SZijun Hu 	int ret;
358ac646d6SZijun Hu 
368ac646d6SZijun Hu 	ret = misc_register(&dev_misc_dynamic_minor);
378ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, 0, ret);
388ac646d6SZijun Hu 	misc_deregister(&dev_misc_dynamic_minor);
398ac646d6SZijun Hu }
408ac646d6SZijun Hu 
418ac646d6SZijun Hu struct miscdev_test_case {
428ac646d6SZijun Hu 	const char *str;
438ac646d6SZijun Hu 	int minor;
448ac646d6SZijun Hu };
458ac646d6SZijun Hu 
468ac646d6SZijun Hu static struct miscdev_test_case miscdev_test_ranges[] = {
478ac646d6SZijun Hu 	{
488ac646d6SZijun Hu 		.str = "lower static range, top",
498ac646d6SZijun Hu 		.minor = 15,
508ac646d6SZijun Hu 	},
518ac646d6SZijun Hu 	{
528ac646d6SZijun Hu 		.str = "upper static range, bottom",
538ac646d6SZijun Hu 		.minor = 130,
548ac646d6SZijun Hu 	},
558ac646d6SZijun Hu 	{
568ac646d6SZijun Hu 		.str = "lower static range, bottom",
578ac646d6SZijun Hu 		.minor = 0,
588ac646d6SZijun Hu 	},
598ac646d6SZijun Hu 	{
608ac646d6SZijun Hu 		.str = "upper static range, top",
618ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR - 1,
628ac646d6SZijun Hu 	},
638ac646d6SZijun Hu };
648ac646d6SZijun Hu 
658ac646d6SZijun Hu KUNIT_ARRAY_PARAM_DESC(miscdev, miscdev_test_ranges, str);
668ac646d6SZijun Hu 
678ac646d6SZijun Hu static int miscdev_find_minors(struct kunit_suite *suite)
688ac646d6SZijun Hu {
698ac646d6SZijun Hu 	int ret;
708ac646d6SZijun Hu 	struct miscdevice miscstat = {
718ac646d6SZijun Hu 		.name = "miscstat",
728ac646d6SZijun Hu 	};
738ac646d6SZijun Hu 	int i;
748ac646d6SZijun Hu 
758ac646d6SZijun Hu 	for (i = 15; i >= 0; i--) {
768ac646d6SZijun Hu 		miscstat.minor = i;
778ac646d6SZijun Hu 		ret = misc_register(&miscstat);
788ac646d6SZijun Hu 		if (ret == 0)
798ac646d6SZijun Hu 			break;
808ac646d6SZijun Hu 	}
818ac646d6SZijun Hu 
828ac646d6SZijun Hu 	if (ret == 0) {
838ac646d6SZijun Hu 		kunit_info(suite, "found misc device minor %d available\n",
848ac646d6SZijun Hu 				miscstat.minor);
858ac646d6SZijun Hu 		miscdev_test_ranges[0].minor = miscstat.minor;
868ac646d6SZijun Hu 		misc_deregister(&miscstat);
878ac646d6SZijun Hu 	} else {
888ac646d6SZijun Hu 		return ret;
898ac646d6SZijun Hu 	}
908ac646d6SZijun Hu 
918ac646d6SZijun Hu 	for (i = 128; i < MISC_DYNAMIC_MINOR; i++) {
928ac646d6SZijun Hu 		miscstat.minor = i;
938ac646d6SZijun Hu 		ret = misc_register(&miscstat);
948ac646d6SZijun Hu 		if (ret == 0)
958ac646d6SZijun Hu 			break;
968ac646d6SZijun Hu 	}
978ac646d6SZijun Hu 
988ac646d6SZijun Hu 	if (ret == 0) {
998ac646d6SZijun Hu 		kunit_info(suite, "found misc device minor %d available\n",
1008ac646d6SZijun Hu 				miscstat.minor);
1018ac646d6SZijun Hu 		miscdev_test_ranges[1].minor = miscstat.minor;
1028ac646d6SZijun Hu 		misc_deregister(&miscstat);
1038ac646d6SZijun Hu 	} else {
1048ac646d6SZijun Hu 		return ret;
1058ac646d6SZijun Hu 	}
1068ac646d6SZijun Hu 
1078ac646d6SZijun Hu 	for (i = 0; i < miscdev_test_ranges[0].minor; i++) {
1088ac646d6SZijun Hu 		miscstat.minor = i;
1098ac646d6SZijun Hu 		ret = misc_register(&miscstat);
1108ac646d6SZijun Hu 		if (ret == 0)
1118ac646d6SZijun Hu 			break;
1128ac646d6SZijun Hu 	}
1138ac646d6SZijun Hu 
1148ac646d6SZijun Hu 	if (ret == 0) {
1158ac646d6SZijun Hu 		kunit_info(suite, "found misc device minor %d available\n",
1168ac646d6SZijun Hu 			miscstat.minor);
1178ac646d6SZijun Hu 		miscdev_test_ranges[2].minor = miscstat.minor;
1188ac646d6SZijun Hu 		misc_deregister(&miscstat);
1198ac646d6SZijun Hu 	} else {
1208ac646d6SZijun Hu 		return ret;
1218ac646d6SZijun Hu 	}
1228ac646d6SZijun Hu 
1238ac646d6SZijun Hu 	for (i = MISC_DYNAMIC_MINOR - 1; i > miscdev_test_ranges[1].minor; i--) {
1248ac646d6SZijun Hu 		miscstat.minor = i;
1258ac646d6SZijun Hu 		ret = misc_register(&miscstat);
1268ac646d6SZijun Hu 		if (ret == 0)
1278ac646d6SZijun Hu 			break;
1288ac646d6SZijun Hu 	}
1298ac646d6SZijun Hu 
1308ac646d6SZijun Hu 	if (ret == 0) {
1318ac646d6SZijun Hu 		kunit_info(suite, "found misc device minor %d available\n",
1328ac646d6SZijun Hu 			miscstat.minor);
1338ac646d6SZijun Hu 		miscdev_test_ranges[3].minor = miscstat.minor;
1348ac646d6SZijun Hu 		misc_deregister(&miscstat);
1358ac646d6SZijun Hu 	}
1368ac646d6SZijun Hu 
1378ac646d6SZijun Hu 	return ret;
1388ac646d6SZijun Hu }
1398ac646d6SZijun Hu 
1408ac646d6SZijun Hu static bool is_valid_dynamic_minor(int minor)
1418ac646d6SZijun Hu {
1428ac646d6SZijun Hu 	if (minor < 0)
1438ac646d6SZijun Hu 		return false;
144*f4e47affSZijun Hu 	return minor > MISC_DYNAMIC_MINOR;
1458ac646d6SZijun Hu }
1468ac646d6SZijun Hu 
1478ac646d6SZijun Hu static int miscdev_test_open(struct inode *inode, struct file *file)
1488ac646d6SZijun Hu {
1498ac646d6SZijun Hu 	return 0;
1508ac646d6SZijun Hu }
1518ac646d6SZijun Hu 
1528ac646d6SZijun Hu static const struct file_operations miscdev_test_fops = {
1538ac646d6SZijun Hu 	.open	= miscdev_test_open,
1548ac646d6SZijun Hu };
1558ac646d6SZijun Hu 
1568ac646d6SZijun Hu static void __init miscdev_test_can_open(struct kunit *test, struct miscdevice *misc)
1578ac646d6SZijun Hu {
1588ac646d6SZijun Hu 	int ret;
1598ac646d6SZijun Hu 	struct file *filp;
1608ac646d6SZijun Hu 	char *devname;
1618ac646d6SZijun Hu 
1628ac646d6SZijun Hu 	devname = kasprintf(GFP_KERNEL, "/dev/%s", misc->name);
1638ac646d6SZijun Hu 	ret = init_mknod(devname, S_IFCHR | 0600,
1648ac646d6SZijun Hu 			 new_encode_dev(MKDEV(MISC_MAJOR, misc->minor)));
1658ac646d6SZijun Hu 	if (ret != 0)
1668ac646d6SZijun Hu 		KUNIT_FAIL(test, "failed to create node\n");
1678ac646d6SZijun Hu 
1688ac646d6SZijun Hu 	filp = filp_open(devname, O_RDONLY, 0);
1698ac646d6SZijun Hu 	if (IS_ERR_OR_NULL(filp))
1708ac646d6SZijun Hu 		KUNIT_FAIL(test, "failed to open misc device: %ld\n", PTR_ERR(filp));
1718ac646d6SZijun Hu 	else
1728ac646d6SZijun Hu 		fput(filp);
1738ac646d6SZijun Hu 
1748ac646d6SZijun Hu 	init_unlink(devname);
1758ac646d6SZijun Hu 	kfree(devname);
1768ac646d6SZijun Hu }
1778ac646d6SZijun Hu 
1788ac646d6SZijun Hu static void __init miscdev_test_static_basic(struct kunit *test)
1798ac646d6SZijun Hu {
1808ac646d6SZijun Hu 	struct miscdevice misc_test = {
1818ac646d6SZijun Hu 		.name = "misc_test",
1828ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
1838ac646d6SZijun Hu 	};
1848ac646d6SZijun Hu 	int ret;
1858ac646d6SZijun Hu 	const struct miscdev_test_case *params = test->param_value;
1868ac646d6SZijun Hu 
1878ac646d6SZijun Hu 	misc_test.minor = params->minor;
1888ac646d6SZijun Hu 
1898ac646d6SZijun Hu 	ret = misc_register(&misc_test);
1908ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
1918ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
1928ac646d6SZijun Hu 
1938ac646d6SZijun Hu 	if (ret == 0) {
1948ac646d6SZijun Hu 		miscdev_test_can_open(test, &misc_test);
1958ac646d6SZijun Hu 		misc_deregister(&misc_test);
1968ac646d6SZijun Hu 	}
1978ac646d6SZijun Hu }
1988ac646d6SZijun Hu 
1998ac646d6SZijun Hu static void __init miscdev_test_dynamic_basic(struct kunit *test)
2008ac646d6SZijun Hu {
2018ac646d6SZijun Hu 	struct miscdevice misc_test = {
2028ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
2038ac646d6SZijun Hu 		.name = "misc_test",
2048ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
2058ac646d6SZijun Hu 	};
2068ac646d6SZijun Hu 	int ret;
2078ac646d6SZijun Hu 
2088ac646d6SZijun Hu 	ret = misc_register(&misc_test);
2098ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
2108ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc_test.minor));
2118ac646d6SZijun Hu 
2128ac646d6SZijun Hu 	if (ret == 0) {
2138ac646d6SZijun Hu 		miscdev_test_can_open(test, &misc_test);
2148ac646d6SZijun Hu 		misc_deregister(&misc_test);
2158ac646d6SZijun Hu 	}
2168ac646d6SZijun Hu }
2178ac646d6SZijun Hu 
2188ac646d6SZijun Hu static void miscdev_test_twice(struct kunit *test)
2198ac646d6SZijun Hu {
2208ac646d6SZijun Hu 	struct miscdevice misc_test = {
2218ac646d6SZijun Hu 		.name = "misc_test",
2228ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
2238ac646d6SZijun Hu 	};
2248ac646d6SZijun Hu 	int ret;
2258ac646d6SZijun Hu 	const struct miscdev_test_case *params = test->param_value;
2268ac646d6SZijun Hu 
2278ac646d6SZijun Hu 	misc_test.minor = params->minor;
2288ac646d6SZijun Hu 
2298ac646d6SZijun Hu 	ret = misc_register(&misc_test);
2308ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
2318ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
2328ac646d6SZijun Hu 	if (ret == 0)
2338ac646d6SZijun Hu 		misc_deregister(&misc_test);
2348ac646d6SZijun Hu 
2358ac646d6SZijun Hu 	ret = misc_register(&misc_test);
2368ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
2378ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
2388ac646d6SZijun Hu 	if (ret == 0)
2398ac646d6SZijun Hu 		misc_deregister(&misc_test);
2408ac646d6SZijun Hu }
2418ac646d6SZijun Hu 
2428ac646d6SZijun Hu static void miscdev_test_duplicate_minor(struct kunit *test)
2438ac646d6SZijun Hu {
2448ac646d6SZijun Hu 	struct miscdevice misc1 = {
2458ac646d6SZijun Hu 		.name = "misc1",
2468ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
2478ac646d6SZijun Hu 	};
2488ac646d6SZijun Hu 	struct miscdevice misc2 = {
2498ac646d6SZijun Hu 		.name = "misc2",
2508ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
2518ac646d6SZijun Hu 	};
2528ac646d6SZijun Hu 	int ret;
2538ac646d6SZijun Hu 	const struct miscdev_test_case *params = test->param_value;
2548ac646d6SZijun Hu 
2558ac646d6SZijun Hu 	misc1.minor = params->minor;
2568ac646d6SZijun Hu 	misc2.minor = params->minor;
2578ac646d6SZijun Hu 
2588ac646d6SZijun Hu 	ret = misc_register(&misc1);
2598ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
2608ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, misc1.minor, params->minor);
2618ac646d6SZijun Hu 
2628ac646d6SZijun Hu 	ret = misc_register(&misc2);
2638ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EBUSY);
2648ac646d6SZijun Hu 	if (ret == 0)
2658ac646d6SZijun Hu 		misc_deregister(&misc2);
2668ac646d6SZijun Hu 
2678ac646d6SZijun Hu 	misc_deregister(&misc1);
2688ac646d6SZijun Hu }
2698ac646d6SZijun Hu 
2708ac646d6SZijun Hu static void miscdev_test_duplicate_name(struct kunit *test)
2718ac646d6SZijun Hu {
2728ac646d6SZijun Hu 	struct miscdevice misc1 = {
2738ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
2748ac646d6SZijun Hu 		.name = "misc1",
2758ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
2768ac646d6SZijun Hu 	};
2778ac646d6SZijun Hu 	struct miscdevice misc2 = {
2788ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
2798ac646d6SZijun Hu 		.name = "misc1",
2808ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
2818ac646d6SZijun Hu 	};
2828ac646d6SZijun Hu 	int ret;
2838ac646d6SZijun Hu 
2848ac646d6SZijun Hu 	ret = misc_register(&misc1);
2858ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
2868ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor));
2878ac646d6SZijun Hu 
2888ac646d6SZijun Hu 	ret = misc_register(&misc2);
2898ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EEXIST);
2908ac646d6SZijun Hu 	if (ret == 0)
2918ac646d6SZijun Hu 		misc_deregister(&misc2);
2928ac646d6SZijun Hu 
2938ac646d6SZijun Hu 	misc_deregister(&misc1);
2948ac646d6SZijun Hu }
2958ac646d6SZijun Hu 
2968ac646d6SZijun Hu /*
2978ac646d6SZijun Hu  * Test that after a duplicate name failure, the reserved minor number is
2988ac646d6SZijun Hu  * freed to be allocated next.
2998ac646d6SZijun Hu  */
3008ac646d6SZijun Hu static void miscdev_test_duplicate_name_leak(struct kunit *test)
3018ac646d6SZijun Hu {
3028ac646d6SZijun Hu 	struct miscdevice misc1 = {
3038ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
3048ac646d6SZijun Hu 		.name = "misc1",
3058ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
3068ac646d6SZijun Hu 	};
3078ac646d6SZijun Hu 	struct miscdevice misc2 = {
3088ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
3098ac646d6SZijun Hu 		.name = "misc1",
3108ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
3118ac646d6SZijun Hu 	};
3128ac646d6SZijun Hu 	struct miscdevice misc3 = {
3138ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
3148ac646d6SZijun Hu 		.name = "misc3",
3158ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
3168ac646d6SZijun Hu 	};
3178ac646d6SZijun Hu 	int ret;
3188ac646d6SZijun Hu 	int dyn_minor;
3198ac646d6SZijun Hu 
3208ac646d6SZijun Hu 	ret = misc_register(&misc1);
3218ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
3228ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor));
3238ac646d6SZijun Hu 
3248ac646d6SZijun Hu 	/*
3258ac646d6SZijun Hu 	 * Find out what is the next minor number available.
3268ac646d6SZijun Hu 	 */
3278ac646d6SZijun Hu 	ret = misc_register(&misc3);
3288ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
3298ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor));
3308ac646d6SZijun Hu 	dyn_minor = misc3.minor;
3318ac646d6SZijun Hu 	misc_deregister(&misc3);
3328ac646d6SZijun Hu 	misc3.minor = MISC_DYNAMIC_MINOR;
3338ac646d6SZijun Hu 
3348ac646d6SZijun Hu 	ret = misc_register(&misc2);
3358ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EEXIST);
3368ac646d6SZijun Hu 	if (ret == 0)
3378ac646d6SZijun Hu 		misc_deregister(&misc2);
3388ac646d6SZijun Hu 
3398ac646d6SZijun Hu 	/*
3408ac646d6SZijun Hu 	 * Now check that we can still get the same minor we found before.
3418ac646d6SZijun Hu 	 */
3428ac646d6SZijun Hu 	ret = misc_register(&misc3);
3438ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
3448ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor));
3458ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, misc3.minor, dyn_minor);
3468ac646d6SZijun Hu 	misc_deregister(&misc3);
3478ac646d6SZijun Hu 
3488ac646d6SZijun Hu 	misc_deregister(&misc1);
3498ac646d6SZijun Hu }
3508ac646d6SZijun Hu 
3518ac646d6SZijun Hu /*
3528ac646d6SZijun Hu  * Try to register a static minor with a duplicate name. That might not
3538ac646d6SZijun Hu  * deallocate the minor, preventing it from being used again.
3548ac646d6SZijun Hu  */
3558ac646d6SZijun Hu static void miscdev_test_duplicate_error(struct kunit *test)
3568ac646d6SZijun Hu {
3578ac646d6SZijun Hu 	struct miscdevice miscdyn = {
3588ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
3598ac646d6SZijun Hu 		.name = "name1",
3608ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
3618ac646d6SZijun Hu 	};
3628ac646d6SZijun Hu 	struct miscdevice miscstat = {
3638ac646d6SZijun Hu 		.name = "name1",
3648ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
3658ac646d6SZijun Hu 	};
3668ac646d6SZijun Hu 	struct miscdevice miscnew = {
3678ac646d6SZijun Hu 		.name = "name2",
3688ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
3698ac646d6SZijun Hu 	};
3708ac646d6SZijun Hu 	int ret;
3718ac646d6SZijun Hu 	const struct miscdev_test_case *params = test->param_value;
3728ac646d6SZijun Hu 
3738ac646d6SZijun Hu 	miscstat.minor = params->minor;
3748ac646d6SZijun Hu 	miscnew.minor = params->minor;
3758ac646d6SZijun Hu 
3768ac646d6SZijun Hu 	ret = misc_register(&miscdyn);
3778ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
3788ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
3798ac646d6SZijun Hu 
3808ac646d6SZijun Hu 	ret = misc_register(&miscstat);
3818ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EEXIST);
3828ac646d6SZijun Hu 	if (ret == 0)
3838ac646d6SZijun Hu 		misc_deregister(&miscstat);
3848ac646d6SZijun Hu 
3858ac646d6SZijun Hu 	ret = misc_register(&miscnew);
3868ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
3878ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, miscnew.minor, params->minor);
3888ac646d6SZijun Hu 	if (ret == 0)
3898ac646d6SZijun Hu 		misc_deregister(&miscnew);
3908ac646d6SZijun Hu 
3918ac646d6SZijun Hu 	misc_deregister(&miscdyn);
3928ac646d6SZijun Hu }
3938ac646d6SZijun Hu 
3948ac646d6SZijun Hu static void __init miscdev_test_dynamic_only_range(struct kunit *test)
3958ac646d6SZijun Hu {
3968ac646d6SZijun Hu 	int ret;
3978ac646d6SZijun Hu 	struct miscdevice *miscdev;
3988ac646d6SZijun Hu 	const int dynamic_minors = 256;
3998ac646d6SZijun Hu 	int i;
4008ac646d6SZijun Hu 
4018ac646d6SZijun Hu 	miscdev = kunit_kmalloc_array(test, dynamic_minors,
4028ac646d6SZijun Hu 					sizeof(struct miscdevice),
4038ac646d6SZijun Hu 					GFP_KERNEL | __GFP_ZERO);
4048ac646d6SZijun Hu 
4058ac646d6SZijun Hu 	for (i = 0; i < dynamic_minors; i++) {
4068ac646d6SZijun Hu 		miscdev[i].minor = MISC_DYNAMIC_MINOR;
4078ac646d6SZijun Hu 		miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
4088ac646d6SZijun Hu 		miscdev[i].fops = &miscdev_test_fops;
4098ac646d6SZijun Hu 		ret = misc_register(&miscdev[i]);
4108ac646d6SZijun Hu 		if (ret != 0)
4118ac646d6SZijun Hu 			break;
4128ac646d6SZijun Hu 		/*
4138ac646d6SZijun Hu 		 * This is the bug we are looking for!
4148ac646d6SZijun Hu 		 * We asked for a dynamic minor and got a minor in the static range space.
4158ac646d6SZijun Hu 		 */
4168ac646d6SZijun Hu 		if (miscdev[i].minor >= 0 && miscdev[i].minor <= 15) {
4178ac646d6SZijun Hu 			KUNIT_FAIL(test, "misc_register allocated minor %d\n", miscdev[i].minor);
4188ac646d6SZijun Hu 			i++;
4198ac646d6SZijun Hu 			break;
4208ac646d6SZijun Hu 		}
4218ac646d6SZijun Hu 		KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
4228ac646d6SZijun Hu 	}
4238ac646d6SZijun Hu 
4248ac646d6SZijun Hu 	for (i--; i >= 0; i--) {
4258ac646d6SZijun Hu 		miscdev_test_can_open(test, &miscdev[i]);
4268ac646d6SZijun Hu 		misc_deregister(&miscdev[i]);
4278ac646d6SZijun Hu 		kfree_const(miscdev[i].name);
4288ac646d6SZijun Hu 	}
4298ac646d6SZijun Hu 
4308ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
4318ac646d6SZijun Hu }
4328ac646d6SZijun Hu 
4338ac646d6SZijun Hu static void __init miscdev_test_collision(struct kunit *test)
4348ac646d6SZijun Hu {
4358ac646d6SZijun Hu 	int ret;
4368ac646d6SZijun Hu 	struct miscdevice *miscdev;
4378ac646d6SZijun Hu 	struct miscdevice miscstat = {
4388ac646d6SZijun Hu 		.name = "miscstat",
4398ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
4408ac646d6SZijun Hu 	};
4418ac646d6SZijun Hu 	const int dynamic_minors = 256;
4428ac646d6SZijun Hu 	int i;
4438ac646d6SZijun Hu 
4448ac646d6SZijun Hu 	miscdev = kunit_kmalloc_array(test, dynamic_minors,
4458ac646d6SZijun Hu 					sizeof(struct miscdevice),
4468ac646d6SZijun Hu 					GFP_KERNEL | __GFP_ZERO);
4478ac646d6SZijun Hu 
4488ac646d6SZijun Hu 	miscstat.minor = miscdev_test_ranges[0].minor;
4498ac646d6SZijun Hu 	ret = misc_register(&miscstat);
4508ac646d6SZijun Hu 	KUNIT_ASSERT_EQ(test, ret, 0);
4518ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor);
4528ac646d6SZijun Hu 
4538ac646d6SZijun Hu 	for (i = 0; i < dynamic_minors; i++) {
4548ac646d6SZijun Hu 		miscdev[i].minor = MISC_DYNAMIC_MINOR;
4558ac646d6SZijun Hu 		miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
4568ac646d6SZijun Hu 		miscdev[i].fops = &miscdev_test_fops;
4578ac646d6SZijun Hu 		ret = misc_register(&miscdev[i]);
4588ac646d6SZijun Hu 		if (ret != 0)
4598ac646d6SZijun Hu 			break;
4608ac646d6SZijun Hu 		KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
4618ac646d6SZijun Hu 	}
4628ac646d6SZijun Hu 
4638ac646d6SZijun Hu 	for (i--; i >= 0; i--) {
4648ac646d6SZijun Hu 		miscdev_test_can_open(test, &miscdev[i]);
4658ac646d6SZijun Hu 		misc_deregister(&miscdev[i]);
4668ac646d6SZijun Hu 		kfree_const(miscdev[i].name);
4678ac646d6SZijun Hu 	}
4688ac646d6SZijun Hu 
4698ac646d6SZijun Hu 	misc_deregister(&miscstat);
4708ac646d6SZijun Hu 
4718ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
4728ac646d6SZijun Hu }
4738ac646d6SZijun Hu 
4748ac646d6SZijun Hu static void __init miscdev_test_collision_reverse(struct kunit *test)
4758ac646d6SZijun Hu {
4768ac646d6SZijun Hu 	int ret;
4778ac646d6SZijun Hu 	struct miscdevice *miscdev;
4788ac646d6SZijun Hu 	struct miscdevice miscstat = {
4798ac646d6SZijun Hu 		.name = "miscstat",
4808ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
4818ac646d6SZijun Hu 	};
4828ac646d6SZijun Hu 	const int dynamic_minors = 256;
4838ac646d6SZijun Hu 	int i;
4848ac646d6SZijun Hu 
4858ac646d6SZijun Hu 	miscdev = kunit_kmalloc_array(test, dynamic_minors,
4868ac646d6SZijun Hu 					sizeof(struct miscdevice),
4878ac646d6SZijun Hu 					GFP_KERNEL | __GFP_ZERO);
4888ac646d6SZijun Hu 
4898ac646d6SZijun Hu 	for (i = 0; i < dynamic_minors; i++) {
4908ac646d6SZijun Hu 		miscdev[i].minor = MISC_DYNAMIC_MINOR;
4918ac646d6SZijun Hu 		miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
4928ac646d6SZijun Hu 		miscdev[i].fops = &miscdev_test_fops;
4938ac646d6SZijun Hu 		ret = misc_register(&miscdev[i]);
4948ac646d6SZijun Hu 		if (ret != 0)
4958ac646d6SZijun Hu 			break;
4968ac646d6SZijun Hu 		KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
4978ac646d6SZijun Hu 	}
4988ac646d6SZijun Hu 
4998ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
5008ac646d6SZijun Hu 
5018ac646d6SZijun Hu 	miscstat.minor = miscdev_test_ranges[0].minor;
5028ac646d6SZijun Hu 	ret = misc_register(&miscstat);
5038ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
5048ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor);
5058ac646d6SZijun Hu 	if (ret == 0)
5068ac646d6SZijun Hu 		misc_deregister(&miscstat);
5078ac646d6SZijun Hu 
5088ac646d6SZijun Hu 	for (i--; i >= 0; i--) {
5098ac646d6SZijun Hu 		miscdev_test_can_open(test, &miscdev[i]);
5108ac646d6SZijun Hu 		misc_deregister(&miscdev[i]);
5118ac646d6SZijun Hu 		kfree_const(miscdev[i].name);
5128ac646d6SZijun Hu 	}
5138ac646d6SZijun Hu }
5148ac646d6SZijun Hu 
5158ac646d6SZijun Hu static void __init miscdev_test_conflict(struct kunit *test)
5168ac646d6SZijun Hu {
5178ac646d6SZijun Hu 	int ret;
5188ac646d6SZijun Hu 	struct miscdevice miscdyn = {
5198ac646d6SZijun Hu 		.name = "miscdyn",
5208ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
5218ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
5228ac646d6SZijun Hu 	};
5238ac646d6SZijun Hu 	struct miscdevice miscstat = {
5248ac646d6SZijun Hu 		.name = "miscstat",
5258ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
5268ac646d6SZijun Hu 	};
5278ac646d6SZijun Hu 
5288ac646d6SZijun Hu 	ret = misc_register(&miscdyn);
5298ac646d6SZijun Hu 	KUNIT_ASSERT_EQ(test, ret, 0);
5308ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
5318ac646d6SZijun Hu 
5328ac646d6SZijun Hu 	/*
5338ac646d6SZijun Hu 	 * Try to register a static minor with the same minor as the
5348ac646d6SZijun Hu 	 * dynamic one.
5358ac646d6SZijun Hu 	 */
5368ac646d6SZijun Hu 	miscstat.minor = miscdyn.minor;
5378ac646d6SZijun Hu 	ret = misc_register(&miscstat);
538*f4e47affSZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EINVAL);
5398ac646d6SZijun Hu 	if (ret == 0)
5408ac646d6SZijun Hu 		misc_deregister(&miscstat);
5418ac646d6SZijun Hu 
5428ac646d6SZijun Hu 	miscdev_test_can_open(test, &miscdyn);
5438ac646d6SZijun Hu 
5448ac646d6SZijun Hu 	misc_deregister(&miscdyn);
5458ac646d6SZijun Hu }
5468ac646d6SZijun Hu 
5478ac646d6SZijun Hu static void __init miscdev_test_conflict_reverse(struct kunit *test)
5488ac646d6SZijun Hu {
5498ac646d6SZijun Hu 	int ret;
5508ac646d6SZijun Hu 	struct miscdevice miscdyn = {
5518ac646d6SZijun Hu 		.name = "miscdyn",
5528ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
5538ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
5548ac646d6SZijun Hu 	};
5558ac646d6SZijun Hu 	struct miscdevice miscstat = {
5568ac646d6SZijun Hu 		.name = "miscstat",
5578ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
5588ac646d6SZijun Hu 	};
5598ac646d6SZijun Hu 
5608ac646d6SZijun Hu 	/*
5618ac646d6SZijun Hu 	 * Find the first available dynamic minor to use it as a static
5628ac646d6SZijun Hu 	 * minor later on.
5638ac646d6SZijun Hu 	 */
5648ac646d6SZijun Hu 	ret = misc_register(&miscdyn);
5658ac646d6SZijun Hu 	KUNIT_ASSERT_EQ(test, ret, 0);
5668ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
5678ac646d6SZijun Hu 	miscstat.minor = miscdyn.minor;
5688ac646d6SZijun Hu 	misc_deregister(&miscdyn);
5698ac646d6SZijun Hu 
5708ac646d6SZijun Hu 	ret = misc_register(&miscstat);
571*f4e47affSZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EINVAL);
572*f4e47affSZijun Hu 	if (ret == 0)
573*f4e47affSZijun Hu 		misc_deregister(&miscstat);
5748ac646d6SZijun Hu 
5758ac646d6SZijun Hu 	/*
5768ac646d6SZijun Hu 	 * Try to register a dynamic minor after registering a static minor
5778ac646d6SZijun Hu 	 * within the dynamic range. It should work but get a different
5788ac646d6SZijun Hu 	 * minor.
5798ac646d6SZijun Hu 	 */
5808ac646d6SZijun Hu 	miscdyn.minor = MISC_DYNAMIC_MINOR;
5818ac646d6SZijun Hu 	ret = misc_register(&miscdyn);
5828ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
583*f4e47affSZijun Hu 	KUNIT_EXPECT_EQ(test, miscdyn.minor, miscstat.minor);
5848ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
5858ac646d6SZijun Hu 	if (ret == 0)
5868ac646d6SZijun Hu 		misc_deregister(&miscdyn);
587*f4e47affSZijun Hu }
5888ac646d6SZijun Hu 
589*f4e47affSZijun Hu /* Take minor(> MISC_DYNAMIC_MINOR) as invalid when register miscdevice */
590*f4e47affSZijun Hu static void miscdev_test_invalid_input(struct kunit *test)
591*f4e47affSZijun Hu {
592*f4e47affSZijun Hu 	struct miscdevice misc_test = {
593*f4e47affSZijun Hu 		.minor = MISC_DYNAMIC_MINOR + 1,
594*f4e47affSZijun Hu 		.name = "misc_test",
595*f4e47affSZijun Hu 		.fops = &miscdev_test_fops,
596*f4e47affSZijun Hu 	};
597*f4e47affSZijun Hu 	int ret;
5988ac646d6SZijun Hu 
599*f4e47affSZijun Hu 	ret = misc_register(&misc_test);
600*f4e47affSZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EINVAL);
601*f4e47affSZijun Hu 	if (ret == 0)
602*f4e47affSZijun Hu 		misc_deregister(&misc_test);
6038ac646d6SZijun Hu }
6048ac646d6SZijun Hu 
6058ac646d6SZijun Hu static struct kunit_case test_cases[] = {
6068ac646d6SZijun Hu 	KUNIT_CASE(kunit_static_minor),
6078ac646d6SZijun Hu 	KUNIT_CASE(kunit_misc_dynamic_minor),
608*f4e47affSZijun Hu 	KUNIT_CASE(miscdev_test_invalid_input),
6098ac646d6SZijun Hu 	KUNIT_CASE_PARAM(miscdev_test_twice, miscdev_gen_params),
6108ac646d6SZijun Hu 	KUNIT_CASE_PARAM(miscdev_test_duplicate_minor, miscdev_gen_params),
6118ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_duplicate_name),
6128ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_duplicate_name_leak),
6138ac646d6SZijun Hu 	KUNIT_CASE_PARAM(miscdev_test_duplicate_error, miscdev_gen_params),
6148ac646d6SZijun Hu 	{}
6158ac646d6SZijun Hu };
6168ac646d6SZijun Hu 
6178ac646d6SZijun Hu static struct kunit_suite test_suite = {
6188ac646d6SZijun Hu 	.name = "miscdev",
6198ac646d6SZijun Hu 	.suite_init = miscdev_find_minors,
6208ac646d6SZijun Hu 	.test_cases = test_cases,
6218ac646d6SZijun Hu };
6228ac646d6SZijun Hu kunit_test_suite(test_suite);
6238ac646d6SZijun Hu 
6248ac646d6SZijun Hu static struct kunit_case __refdata test_init_cases[] = {
6258ac646d6SZijun Hu 	KUNIT_CASE_PARAM(miscdev_test_static_basic, miscdev_gen_params),
6268ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_dynamic_basic),
6278ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_dynamic_only_range),
6288ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_collision),
6298ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_collision_reverse),
6308ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_conflict),
6318ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_conflict_reverse),
6328ac646d6SZijun Hu 	{}
6338ac646d6SZijun Hu };
6348ac646d6SZijun Hu 
6358ac646d6SZijun Hu static struct kunit_suite test_init_suite = {
6368ac646d6SZijun Hu 	.name = "miscdev_init",
6378ac646d6SZijun Hu 	.suite_init = miscdev_find_minors,
6388ac646d6SZijun Hu 	.test_cases = test_init_cases,
6398ac646d6SZijun Hu };
6408ac646d6SZijun Hu kunit_test_init_section_suite(test_init_suite);
6418ac646d6SZijun Hu 
6428ac646d6SZijun Hu MODULE_LICENSE("GPL");
6438ac646d6SZijun Hu MODULE_AUTHOR("Vimal Agrawal");
6448ac646d6SZijun Hu MODULE_AUTHOR("Thadeu Lima de Souza Cascardo <cascardo@igalia.com>");
6458ac646d6SZijun Hu MODULE_DESCRIPTION("Test module for misc character devices");
646