xref: /linux/drivers/char/misc_minor_kunit.c (revision 8ac646d6dfbf8e42bdb31b71e03a7984557ab1b1)
1*8ac646d6SZijun Hu // SPDX-License-Identifier: GPL-2.0
2*8ac646d6SZijun Hu #include <kunit/test.h>
3*8ac646d6SZijun Hu #include <kunit/test-bug.h>
4*8ac646d6SZijun Hu #include <linux/module.h>
5*8ac646d6SZijun Hu #include <linux/miscdevice.h>
6*8ac646d6SZijun Hu #include <linux/fs.h>
7*8ac646d6SZijun Hu #include <linux/file.h>
8*8ac646d6SZijun Hu #include <linux/init_syscalls.h>
9*8ac646d6SZijun Hu 
10*8ac646d6SZijun Hu /* dynamic minor (2) */
11*8ac646d6SZijun Hu static struct miscdevice dev_dynamic_minor = {
12*8ac646d6SZijun Hu 	.minor  = 2,
13*8ac646d6SZijun Hu 	.name   = "dev_dynamic_minor",
14*8ac646d6SZijun Hu };
15*8ac646d6SZijun Hu 
16*8ac646d6SZijun Hu /* static minor (LCD_MINOR) */
17*8ac646d6SZijun Hu static struct miscdevice dev_static_minor = {
18*8ac646d6SZijun Hu 	.minor  = LCD_MINOR,
19*8ac646d6SZijun Hu 	.name   = "dev_static_minor",
20*8ac646d6SZijun Hu };
21*8ac646d6SZijun Hu 
22*8ac646d6SZijun Hu /* misc dynamic minor */
23*8ac646d6SZijun Hu static struct miscdevice dev_misc_dynamic_minor = {
24*8ac646d6SZijun Hu 	.minor  = MISC_DYNAMIC_MINOR,
25*8ac646d6SZijun Hu 	.name   = "dev_misc_dynamic_minor",
26*8ac646d6SZijun Hu };
27*8ac646d6SZijun Hu 
28*8ac646d6SZijun Hu static void kunit_dynamic_minor(struct kunit *test)
29*8ac646d6SZijun Hu {
30*8ac646d6SZijun Hu 	int ret;
31*8ac646d6SZijun Hu 
32*8ac646d6SZijun Hu 	ret = misc_register(&dev_dynamic_minor);
33*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, 0, ret);
34*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, 2, dev_dynamic_minor.minor);
35*8ac646d6SZijun Hu 	misc_deregister(&dev_dynamic_minor);
36*8ac646d6SZijun Hu }
37*8ac646d6SZijun Hu 
38*8ac646d6SZijun Hu static void kunit_static_minor(struct kunit *test)
39*8ac646d6SZijun Hu {
40*8ac646d6SZijun Hu 	int ret;
41*8ac646d6SZijun Hu 
42*8ac646d6SZijun Hu 	ret = misc_register(&dev_static_minor);
43*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, 0, ret);
44*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, LCD_MINOR, dev_static_minor.minor);
45*8ac646d6SZijun Hu 	misc_deregister(&dev_static_minor);
46*8ac646d6SZijun Hu }
47*8ac646d6SZijun Hu 
48*8ac646d6SZijun Hu static void kunit_misc_dynamic_minor(struct kunit *test)
49*8ac646d6SZijun Hu {
50*8ac646d6SZijun Hu 	int ret;
51*8ac646d6SZijun Hu 
52*8ac646d6SZijun Hu 	ret = misc_register(&dev_misc_dynamic_minor);
53*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, 0, ret);
54*8ac646d6SZijun Hu 	misc_deregister(&dev_misc_dynamic_minor);
55*8ac646d6SZijun Hu }
56*8ac646d6SZijun Hu 
57*8ac646d6SZijun Hu struct miscdev_test_case {
58*8ac646d6SZijun Hu 	const char *str;
59*8ac646d6SZijun Hu 	int minor;
60*8ac646d6SZijun Hu };
61*8ac646d6SZijun Hu 
62*8ac646d6SZijun Hu static struct miscdev_test_case miscdev_test_ranges[] = {
63*8ac646d6SZijun Hu 	{
64*8ac646d6SZijun Hu 		.str = "lower static range, top",
65*8ac646d6SZijun Hu 		.minor = 15,
66*8ac646d6SZijun Hu 	},
67*8ac646d6SZijun Hu 	{
68*8ac646d6SZijun Hu 		.str = "upper static range, bottom",
69*8ac646d6SZijun Hu 		.minor = 130,
70*8ac646d6SZijun Hu 	},
71*8ac646d6SZijun Hu 	{
72*8ac646d6SZijun Hu 		.str = "lower static range, bottom",
73*8ac646d6SZijun Hu 		.minor = 0,
74*8ac646d6SZijun Hu 	},
75*8ac646d6SZijun Hu 	{
76*8ac646d6SZijun Hu 		.str = "upper static range, top",
77*8ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR - 1,
78*8ac646d6SZijun Hu 	},
79*8ac646d6SZijun Hu };
80*8ac646d6SZijun Hu 
81*8ac646d6SZijun Hu KUNIT_ARRAY_PARAM_DESC(miscdev, miscdev_test_ranges, str);
82*8ac646d6SZijun Hu 
83*8ac646d6SZijun Hu static int miscdev_find_minors(struct kunit_suite *suite)
84*8ac646d6SZijun Hu {
85*8ac646d6SZijun Hu 	int ret;
86*8ac646d6SZijun Hu 	struct miscdevice miscstat = {
87*8ac646d6SZijun Hu 		.name = "miscstat",
88*8ac646d6SZijun Hu 	};
89*8ac646d6SZijun Hu 	int i;
90*8ac646d6SZijun Hu 
91*8ac646d6SZijun Hu 	for (i = 15; i >= 0; i--) {
92*8ac646d6SZijun Hu 		miscstat.minor = i;
93*8ac646d6SZijun Hu 		ret = misc_register(&miscstat);
94*8ac646d6SZijun Hu 		if (ret == 0)
95*8ac646d6SZijun Hu 			break;
96*8ac646d6SZijun Hu 	}
97*8ac646d6SZijun Hu 
98*8ac646d6SZijun Hu 	if (ret == 0) {
99*8ac646d6SZijun Hu 		kunit_info(suite, "found misc device minor %d available\n",
100*8ac646d6SZijun Hu 				miscstat.minor);
101*8ac646d6SZijun Hu 		miscdev_test_ranges[0].minor = miscstat.minor;
102*8ac646d6SZijun Hu 		misc_deregister(&miscstat);
103*8ac646d6SZijun Hu 	} else {
104*8ac646d6SZijun Hu 		return ret;
105*8ac646d6SZijun Hu 	}
106*8ac646d6SZijun Hu 
107*8ac646d6SZijun Hu 	for (i = 128; i < MISC_DYNAMIC_MINOR; i++) {
108*8ac646d6SZijun Hu 		miscstat.minor = i;
109*8ac646d6SZijun Hu 		ret = misc_register(&miscstat);
110*8ac646d6SZijun Hu 		if (ret == 0)
111*8ac646d6SZijun Hu 			break;
112*8ac646d6SZijun Hu 	}
113*8ac646d6SZijun Hu 
114*8ac646d6SZijun Hu 	if (ret == 0) {
115*8ac646d6SZijun Hu 		kunit_info(suite, "found misc device minor %d available\n",
116*8ac646d6SZijun Hu 				miscstat.minor);
117*8ac646d6SZijun Hu 		miscdev_test_ranges[1].minor = miscstat.minor;
118*8ac646d6SZijun Hu 		misc_deregister(&miscstat);
119*8ac646d6SZijun Hu 	} else {
120*8ac646d6SZijun Hu 		return ret;
121*8ac646d6SZijun Hu 	}
122*8ac646d6SZijun Hu 
123*8ac646d6SZijun Hu 	for (i = 0; i < miscdev_test_ranges[0].minor; i++) {
124*8ac646d6SZijun Hu 		miscstat.minor = i;
125*8ac646d6SZijun Hu 		ret = misc_register(&miscstat);
126*8ac646d6SZijun Hu 		if (ret == 0)
127*8ac646d6SZijun Hu 			break;
128*8ac646d6SZijun Hu 	}
129*8ac646d6SZijun Hu 
130*8ac646d6SZijun Hu 	if (ret == 0) {
131*8ac646d6SZijun Hu 		kunit_info(suite, "found misc device minor %d available\n",
132*8ac646d6SZijun Hu 			miscstat.minor);
133*8ac646d6SZijun Hu 		miscdev_test_ranges[2].minor = miscstat.minor;
134*8ac646d6SZijun Hu 		misc_deregister(&miscstat);
135*8ac646d6SZijun Hu 	} else {
136*8ac646d6SZijun Hu 		return ret;
137*8ac646d6SZijun Hu 	}
138*8ac646d6SZijun Hu 
139*8ac646d6SZijun Hu 	for (i = MISC_DYNAMIC_MINOR - 1; i > miscdev_test_ranges[1].minor; i--) {
140*8ac646d6SZijun Hu 		miscstat.minor = i;
141*8ac646d6SZijun Hu 		ret = misc_register(&miscstat);
142*8ac646d6SZijun Hu 		if (ret == 0)
143*8ac646d6SZijun Hu 			break;
144*8ac646d6SZijun Hu 	}
145*8ac646d6SZijun Hu 
146*8ac646d6SZijun Hu 	if (ret == 0) {
147*8ac646d6SZijun Hu 		kunit_info(suite, "found misc device minor %d available\n",
148*8ac646d6SZijun Hu 			miscstat.minor);
149*8ac646d6SZijun Hu 		miscdev_test_ranges[3].minor = miscstat.minor;
150*8ac646d6SZijun Hu 		misc_deregister(&miscstat);
151*8ac646d6SZijun Hu 	}
152*8ac646d6SZijun Hu 
153*8ac646d6SZijun Hu 	return ret;
154*8ac646d6SZijun Hu }
155*8ac646d6SZijun Hu 
156*8ac646d6SZijun Hu static bool is_valid_dynamic_minor(int minor)
157*8ac646d6SZijun Hu {
158*8ac646d6SZijun Hu 	if (minor < 0)
159*8ac646d6SZijun Hu 		return false;
160*8ac646d6SZijun Hu 	if (minor == MISC_DYNAMIC_MINOR)
161*8ac646d6SZijun Hu 		return false;
162*8ac646d6SZijun Hu 	if (minor >= 0 && minor <= 15)
163*8ac646d6SZijun Hu 		return false;
164*8ac646d6SZijun Hu 	if (minor >= 128 && minor < MISC_DYNAMIC_MINOR)
165*8ac646d6SZijun Hu 		return false;
166*8ac646d6SZijun Hu 	return true;
167*8ac646d6SZijun Hu }
168*8ac646d6SZijun Hu 
169*8ac646d6SZijun Hu static int miscdev_test_open(struct inode *inode, struct file *file)
170*8ac646d6SZijun Hu {
171*8ac646d6SZijun Hu 	return 0;
172*8ac646d6SZijun Hu }
173*8ac646d6SZijun Hu 
174*8ac646d6SZijun Hu static const struct file_operations miscdev_test_fops = {
175*8ac646d6SZijun Hu 	.open	= miscdev_test_open,
176*8ac646d6SZijun Hu };
177*8ac646d6SZijun Hu 
178*8ac646d6SZijun Hu static void __init miscdev_test_can_open(struct kunit *test, struct miscdevice *misc)
179*8ac646d6SZijun Hu {
180*8ac646d6SZijun Hu 	int ret;
181*8ac646d6SZijun Hu 	struct file *filp;
182*8ac646d6SZijun Hu 	char *devname;
183*8ac646d6SZijun Hu 
184*8ac646d6SZijun Hu 	devname = kasprintf(GFP_KERNEL, "/dev/%s", misc->name);
185*8ac646d6SZijun Hu 	ret = init_mknod(devname, S_IFCHR | 0600,
186*8ac646d6SZijun Hu 			 new_encode_dev(MKDEV(MISC_MAJOR, misc->minor)));
187*8ac646d6SZijun Hu 	if (ret != 0)
188*8ac646d6SZijun Hu 		KUNIT_FAIL(test, "failed to create node\n");
189*8ac646d6SZijun Hu 
190*8ac646d6SZijun Hu 	filp = filp_open(devname, O_RDONLY, 0);
191*8ac646d6SZijun Hu 	if (IS_ERR_OR_NULL(filp))
192*8ac646d6SZijun Hu 		KUNIT_FAIL(test, "failed to open misc device: %ld\n", PTR_ERR(filp));
193*8ac646d6SZijun Hu 	else
194*8ac646d6SZijun Hu 		fput(filp);
195*8ac646d6SZijun Hu 
196*8ac646d6SZijun Hu 	init_unlink(devname);
197*8ac646d6SZijun Hu 	kfree(devname);
198*8ac646d6SZijun Hu }
199*8ac646d6SZijun Hu 
200*8ac646d6SZijun Hu static void __init miscdev_test_static_basic(struct kunit *test)
201*8ac646d6SZijun Hu {
202*8ac646d6SZijun Hu 	struct miscdevice misc_test = {
203*8ac646d6SZijun Hu 		.name = "misc_test",
204*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
205*8ac646d6SZijun Hu 	};
206*8ac646d6SZijun Hu 	int ret;
207*8ac646d6SZijun Hu 	const struct miscdev_test_case *params = test->param_value;
208*8ac646d6SZijun Hu 
209*8ac646d6SZijun Hu 	misc_test.minor = params->minor;
210*8ac646d6SZijun Hu 
211*8ac646d6SZijun Hu 	ret = misc_register(&misc_test);
212*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
213*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
214*8ac646d6SZijun Hu 
215*8ac646d6SZijun Hu 	if (ret == 0) {
216*8ac646d6SZijun Hu 		miscdev_test_can_open(test, &misc_test);
217*8ac646d6SZijun Hu 		misc_deregister(&misc_test);
218*8ac646d6SZijun Hu 	}
219*8ac646d6SZijun Hu }
220*8ac646d6SZijun Hu 
221*8ac646d6SZijun Hu static void __init miscdev_test_dynamic_basic(struct kunit *test)
222*8ac646d6SZijun Hu {
223*8ac646d6SZijun Hu 	struct miscdevice misc_test = {
224*8ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
225*8ac646d6SZijun Hu 		.name = "misc_test",
226*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
227*8ac646d6SZijun Hu 	};
228*8ac646d6SZijun Hu 	int ret;
229*8ac646d6SZijun Hu 
230*8ac646d6SZijun Hu 	ret = misc_register(&misc_test);
231*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
232*8ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc_test.minor));
233*8ac646d6SZijun Hu 
234*8ac646d6SZijun Hu 	if (ret == 0) {
235*8ac646d6SZijun Hu 		miscdev_test_can_open(test, &misc_test);
236*8ac646d6SZijun Hu 		misc_deregister(&misc_test);
237*8ac646d6SZijun Hu 	}
238*8ac646d6SZijun Hu }
239*8ac646d6SZijun Hu 
240*8ac646d6SZijun Hu static void miscdev_test_twice(struct kunit *test)
241*8ac646d6SZijun Hu {
242*8ac646d6SZijun Hu 	struct miscdevice misc_test = {
243*8ac646d6SZijun Hu 		.name = "misc_test",
244*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
245*8ac646d6SZijun Hu 	};
246*8ac646d6SZijun Hu 	int ret;
247*8ac646d6SZijun Hu 	const struct miscdev_test_case *params = test->param_value;
248*8ac646d6SZijun Hu 
249*8ac646d6SZijun Hu 	misc_test.minor = params->minor;
250*8ac646d6SZijun Hu 
251*8ac646d6SZijun Hu 	ret = misc_register(&misc_test);
252*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
253*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
254*8ac646d6SZijun Hu 	if (ret == 0)
255*8ac646d6SZijun Hu 		misc_deregister(&misc_test);
256*8ac646d6SZijun Hu 
257*8ac646d6SZijun Hu 	ret = misc_register(&misc_test);
258*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
259*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
260*8ac646d6SZijun Hu 	if (ret == 0)
261*8ac646d6SZijun Hu 		misc_deregister(&misc_test);
262*8ac646d6SZijun Hu }
263*8ac646d6SZijun Hu 
264*8ac646d6SZijun Hu static void miscdev_test_duplicate_minor(struct kunit *test)
265*8ac646d6SZijun Hu {
266*8ac646d6SZijun Hu 	struct miscdevice misc1 = {
267*8ac646d6SZijun Hu 		.name = "misc1",
268*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
269*8ac646d6SZijun Hu 	};
270*8ac646d6SZijun Hu 	struct miscdevice misc2 = {
271*8ac646d6SZijun Hu 		.name = "misc2",
272*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
273*8ac646d6SZijun Hu 	};
274*8ac646d6SZijun Hu 	int ret;
275*8ac646d6SZijun Hu 	const struct miscdev_test_case *params = test->param_value;
276*8ac646d6SZijun Hu 
277*8ac646d6SZijun Hu 	misc1.minor = params->minor;
278*8ac646d6SZijun Hu 	misc2.minor = params->minor;
279*8ac646d6SZijun Hu 
280*8ac646d6SZijun Hu 	ret = misc_register(&misc1);
281*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
282*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, misc1.minor, params->minor);
283*8ac646d6SZijun Hu 
284*8ac646d6SZijun Hu 	ret = misc_register(&misc2);
285*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EBUSY);
286*8ac646d6SZijun Hu 	if (ret == 0)
287*8ac646d6SZijun Hu 		misc_deregister(&misc2);
288*8ac646d6SZijun Hu 
289*8ac646d6SZijun Hu 	misc_deregister(&misc1);
290*8ac646d6SZijun Hu }
291*8ac646d6SZijun Hu 
292*8ac646d6SZijun Hu static void miscdev_test_duplicate_name(struct kunit *test)
293*8ac646d6SZijun Hu {
294*8ac646d6SZijun Hu 	struct miscdevice misc1 = {
295*8ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
296*8ac646d6SZijun Hu 		.name = "misc1",
297*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
298*8ac646d6SZijun Hu 	};
299*8ac646d6SZijun Hu 	struct miscdevice misc2 = {
300*8ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
301*8ac646d6SZijun Hu 		.name = "misc1",
302*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
303*8ac646d6SZijun Hu 	};
304*8ac646d6SZijun Hu 	int ret;
305*8ac646d6SZijun Hu 
306*8ac646d6SZijun Hu 	ret = misc_register(&misc1);
307*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
308*8ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor));
309*8ac646d6SZijun Hu 
310*8ac646d6SZijun Hu 	ret = misc_register(&misc2);
311*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EEXIST);
312*8ac646d6SZijun Hu 	if (ret == 0)
313*8ac646d6SZijun Hu 		misc_deregister(&misc2);
314*8ac646d6SZijun Hu 
315*8ac646d6SZijun Hu 	misc_deregister(&misc1);
316*8ac646d6SZijun Hu }
317*8ac646d6SZijun Hu 
318*8ac646d6SZijun Hu /*
319*8ac646d6SZijun Hu  * Test that after a duplicate name failure, the reserved minor number is
320*8ac646d6SZijun Hu  * freed to be allocated next.
321*8ac646d6SZijun Hu  */
322*8ac646d6SZijun Hu static void miscdev_test_duplicate_name_leak(struct kunit *test)
323*8ac646d6SZijun Hu {
324*8ac646d6SZijun Hu 	struct miscdevice misc1 = {
325*8ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
326*8ac646d6SZijun Hu 		.name = "misc1",
327*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
328*8ac646d6SZijun Hu 	};
329*8ac646d6SZijun Hu 	struct miscdevice misc2 = {
330*8ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
331*8ac646d6SZijun Hu 		.name = "misc1",
332*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
333*8ac646d6SZijun Hu 	};
334*8ac646d6SZijun Hu 	struct miscdevice misc3 = {
335*8ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
336*8ac646d6SZijun Hu 		.name = "misc3",
337*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
338*8ac646d6SZijun Hu 	};
339*8ac646d6SZijun Hu 	int ret;
340*8ac646d6SZijun Hu 	int dyn_minor;
341*8ac646d6SZijun Hu 
342*8ac646d6SZijun Hu 	ret = misc_register(&misc1);
343*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
344*8ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor));
345*8ac646d6SZijun Hu 
346*8ac646d6SZijun Hu 	/*
347*8ac646d6SZijun Hu 	 * Find out what is the next minor number available.
348*8ac646d6SZijun Hu 	 */
349*8ac646d6SZijun Hu 	ret = misc_register(&misc3);
350*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
351*8ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor));
352*8ac646d6SZijun Hu 	dyn_minor = misc3.minor;
353*8ac646d6SZijun Hu 	misc_deregister(&misc3);
354*8ac646d6SZijun Hu 	misc3.minor = MISC_DYNAMIC_MINOR;
355*8ac646d6SZijun Hu 
356*8ac646d6SZijun Hu 	ret = misc_register(&misc2);
357*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EEXIST);
358*8ac646d6SZijun Hu 	if (ret == 0)
359*8ac646d6SZijun Hu 		misc_deregister(&misc2);
360*8ac646d6SZijun Hu 
361*8ac646d6SZijun Hu 	/*
362*8ac646d6SZijun Hu 	 * Now check that we can still get the same minor we found before.
363*8ac646d6SZijun Hu 	 */
364*8ac646d6SZijun Hu 	ret = misc_register(&misc3);
365*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
366*8ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor));
367*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, misc3.minor, dyn_minor);
368*8ac646d6SZijun Hu 	misc_deregister(&misc3);
369*8ac646d6SZijun Hu 
370*8ac646d6SZijun Hu 	misc_deregister(&misc1);
371*8ac646d6SZijun Hu }
372*8ac646d6SZijun Hu 
373*8ac646d6SZijun Hu /*
374*8ac646d6SZijun Hu  * Try to register a static minor with a duplicate name. That might not
375*8ac646d6SZijun Hu  * deallocate the minor, preventing it from being used again.
376*8ac646d6SZijun Hu  */
377*8ac646d6SZijun Hu static void miscdev_test_duplicate_error(struct kunit *test)
378*8ac646d6SZijun Hu {
379*8ac646d6SZijun Hu 	struct miscdevice miscdyn = {
380*8ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
381*8ac646d6SZijun Hu 		.name = "name1",
382*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
383*8ac646d6SZijun Hu 	};
384*8ac646d6SZijun Hu 	struct miscdevice miscstat = {
385*8ac646d6SZijun Hu 		.name = "name1",
386*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
387*8ac646d6SZijun Hu 	};
388*8ac646d6SZijun Hu 	struct miscdevice miscnew = {
389*8ac646d6SZijun Hu 		.name = "name2",
390*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
391*8ac646d6SZijun Hu 	};
392*8ac646d6SZijun Hu 	int ret;
393*8ac646d6SZijun Hu 	const struct miscdev_test_case *params = test->param_value;
394*8ac646d6SZijun Hu 
395*8ac646d6SZijun Hu 	miscstat.minor = params->minor;
396*8ac646d6SZijun Hu 	miscnew.minor = params->minor;
397*8ac646d6SZijun Hu 
398*8ac646d6SZijun Hu 	ret = misc_register(&miscdyn);
399*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
400*8ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
401*8ac646d6SZijun Hu 
402*8ac646d6SZijun Hu 	ret = misc_register(&miscstat);
403*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EEXIST);
404*8ac646d6SZijun Hu 	if (ret == 0)
405*8ac646d6SZijun Hu 		misc_deregister(&miscstat);
406*8ac646d6SZijun Hu 
407*8ac646d6SZijun Hu 	ret = misc_register(&miscnew);
408*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
409*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, miscnew.minor, params->minor);
410*8ac646d6SZijun Hu 	if (ret == 0)
411*8ac646d6SZijun Hu 		misc_deregister(&miscnew);
412*8ac646d6SZijun Hu 
413*8ac646d6SZijun Hu 	misc_deregister(&miscdyn);
414*8ac646d6SZijun Hu }
415*8ac646d6SZijun Hu 
416*8ac646d6SZijun Hu static void __init miscdev_test_dynamic_only_range(struct kunit *test)
417*8ac646d6SZijun Hu {
418*8ac646d6SZijun Hu 	int ret;
419*8ac646d6SZijun Hu 	struct miscdevice *miscdev;
420*8ac646d6SZijun Hu 	const int dynamic_minors = 256;
421*8ac646d6SZijun Hu 	int i;
422*8ac646d6SZijun Hu 
423*8ac646d6SZijun Hu 	miscdev = kunit_kmalloc_array(test, dynamic_minors,
424*8ac646d6SZijun Hu 					sizeof(struct miscdevice),
425*8ac646d6SZijun Hu 					GFP_KERNEL | __GFP_ZERO);
426*8ac646d6SZijun Hu 
427*8ac646d6SZijun Hu 	for (i = 0; i < dynamic_minors; i++) {
428*8ac646d6SZijun Hu 		miscdev[i].minor = MISC_DYNAMIC_MINOR;
429*8ac646d6SZijun Hu 		miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
430*8ac646d6SZijun Hu 		miscdev[i].fops = &miscdev_test_fops;
431*8ac646d6SZijun Hu 		ret = misc_register(&miscdev[i]);
432*8ac646d6SZijun Hu 		if (ret != 0)
433*8ac646d6SZijun Hu 			break;
434*8ac646d6SZijun Hu 		/*
435*8ac646d6SZijun Hu 		 * This is the bug we are looking for!
436*8ac646d6SZijun Hu 		 * We asked for a dynamic minor and got a minor in the static range space.
437*8ac646d6SZijun Hu 		 */
438*8ac646d6SZijun Hu 		if (miscdev[i].minor >= 0 && miscdev[i].minor <= 15) {
439*8ac646d6SZijun Hu 			KUNIT_FAIL(test, "misc_register allocated minor %d\n", miscdev[i].minor);
440*8ac646d6SZijun Hu 			i++;
441*8ac646d6SZijun Hu 			break;
442*8ac646d6SZijun Hu 		}
443*8ac646d6SZijun Hu 		KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
444*8ac646d6SZijun Hu 	}
445*8ac646d6SZijun Hu 
446*8ac646d6SZijun Hu 	for (i--; i >= 0; i--) {
447*8ac646d6SZijun Hu 		miscdev_test_can_open(test, &miscdev[i]);
448*8ac646d6SZijun Hu 		misc_deregister(&miscdev[i]);
449*8ac646d6SZijun Hu 		kfree_const(miscdev[i].name);
450*8ac646d6SZijun Hu 	}
451*8ac646d6SZijun Hu 
452*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
453*8ac646d6SZijun Hu }
454*8ac646d6SZijun Hu 
455*8ac646d6SZijun Hu static void __init miscdev_test_collision(struct kunit *test)
456*8ac646d6SZijun Hu {
457*8ac646d6SZijun Hu 	int ret;
458*8ac646d6SZijun Hu 	struct miscdevice *miscdev;
459*8ac646d6SZijun Hu 	struct miscdevice miscstat = {
460*8ac646d6SZijun Hu 		.name = "miscstat",
461*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
462*8ac646d6SZijun Hu 	};
463*8ac646d6SZijun Hu 	const int dynamic_minors = 256;
464*8ac646d6SZijun Hu 	int i;
465*8ac646d6SZijun Hu 
466*8ac646d6SZijun Hu 	miscdev = kunit_kmalloc_array(test, dynamic_minors,
467*8ac646d6SZijun Hu 					sizeof(struct miscdevice),
468*8ac646d6SZijun Hu 					GFP_KERNEL | __GFP_ZERO);
469*8ac646d6SZijun Hu 
470*8ac646d6SZijun Hu 	miscstat.minor = miscdev_test_ranges[0].minor;
471*8ac646d6SZijun Hu 	ret = misc_register(&miscstat);
472*8ac646d6SZijun Hu 	KUNIT_ASSERT_EQ(test, ret, 0);
473*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor);
474*8ac646d6SZijun Hu 
475*8ac646d6SZijun Hu 	for (i = 0; i < dynamic_minors; i++) {
476*8ac646d6SZijun Hu 		miscdev[i].minor = MISC_DYNAMIC_MINOR;
477*8ac646d6SZijun Hu 		miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
478*8ac646d6SZijun Hu 		miscdev[i].fops = &miscdev_test_fops;
479*8ac646d6SZijun Hu 		ret = misc_register(&miscdev[i]);
480*8ac646d6SZijun Hu 		if (ret != 0)
481*8ac646d6SZijun Hu 			break;
482*8ac646d6SZijun Hu 		KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
483*8ac646d6SZijun Hu 	}
484*8ac646d6SZijun Hu 
485*8ac646d6SZijun Hu 	for (i--; i >= 0; i--) {
486*8ac646d6SZijun Hu 		miscdev_test_can_open(test, &miscdev[i]);
487*8ac646d6SZijun Hu 		misc_deregister(&miscdev[i]);
488*8ac646d6SZijun Hu 		kfree_const(miscdev[i].name);
489*8ac646d6SZijun Hu 	}
490*8ac646d6SZijun Hu 
491*8ac646d6SZijun Hu 	misc_deregister(&miscstat);
492*8ac646d6SZijun Hu 
493*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
494*8ac646d6SZijun Hu }
495*8ac646d6SZijun Hu 
496*8ac646d6SZijun Hu static void __init miscdev_test_collision_reverse(struct kunit *test)
497*8ac646d6SZijun Hu {
498*8ac646d6SZijun Hu 	int ret;
499*8ac646d6SZijun Hu 	struct miscdevice *miscdev;
500*8ac646d6SZijun Hu 	struct miscdevice miscstat = {
501*8ac646d6SZijun Hu 		.name = "miscstat",
502*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
503*8ac646d6SZijun Hu 	};
504*8ac646d6SZijun Hu 	const int dynamic_minors = 256;
505*8ac646d6SZijun Hu 	int i;
506*8ac646d6SZijun Hu 
507*8ac646d6SZijun Hu 	miscdev = kunit_kmalloc_array(test, dynamic_minors,
508*8ac646d6SZijun Hu 					sizeof(struct miscdevice),
509*8ac646d6SZijun Hu 					GFP_KERNEL | __GFP_ZERO);
510*8ac646d6SZijun Hu 
511*8ac646d6SZijun Hu 	for (i = 0; i < dynamic_minors; i++) {
512*8ac646d6SZijun Hu 		miscdev[i].minor = MISC_DYNAMIC_MINOR;
513*8ac646d6SZijun Hu 		miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
514*8ac646d6SZijun Hu 		miscdev[i].fops = &miscdev_test_fops;
515*8ac646d6SZijun Hu 		ret = misc_register(&miscdev[i]);
516*8ac646d6SZijun Hu 		if (ret != 0)
517*8ac646d6SZijun Hu 			break;
518*8ac646d6SZijun Hu 		KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
519*8ac646d6SZijun Hu 	}
520*8ac646d6SZijun Hu 
521*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
522*8ac646d6SZijun Hu 
523*8ac646d6SZijun Hu 	miscstat.minor = miscdev_test_ranges[0].minor;
524*8ac646d6SZijun Hu 	ret = misc_register(&miscstat);
525*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
526*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor);
527*8ac646d6SZijun Hu 	if (ret == 0)
528*8ac646d6SZijun Hu 		misc_deregister(&miscstat);
529*8ac646d6SZijun Hu 
530*8ac646d6SZijun Hu 	for (i--; i >= 0; i--) {
531*8ac646d6SZijun Hu 		miscdev_test_can_open(test, &miscdev[i]);
532*8ac646d6SZijun Hu 		misc_deregister(&miscdev[i]);
533*8ac646d6SZijun Hu 		kfree_const(miscdev[i].name);
534*8ac646d6SZijun Hu 	}
535*8ac646d6SZijun Hu }
536*8ac646d6SZijun Hu 
537*8ac646d6SZijun Hu static void __init miscdev_test_conflict(struct kunit *test)
538*8ac646d6SZijun Hu {
539*8ac646d6SZijun Hu 	int ret;
540*8ac646d6SZijun Hu 	struct miscdevice miscdyn = {
541*8ac646d6SZijun Hu 		.name = "miscdyn",
542*8ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
543*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
544*8ac646d6SZijun Hu 	};
545*8ac646d6SZijun Hu 	struct miscdevice miscstat = {
546*8ac646d6SZijun Hu 		.name = "miscstat",
547*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
548*8ac646d6SZijun Hu 	};
549*8ac646d6SZijun Hu 
550*8ac646d6SZijun Hu 	ret = misc_register(&miscdyn);
551*8ac646d6SZijun Hu 	KUNIT_ASSERT_EQ(test, ret, 0);
552*8ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
553*8ac646d6SZijun Hu 
554*8ac646d6SZijun Hu 	/*
555*8ac646d6SZijun Hu 	 * Try to register a static minor with the same minor as the
556*8ac646d6SZijun Hu 	 * dynamic one.
557*8ac646d6SZijun Hu 	 */
558*8ac646d6SZijun Hu 	miscstat.minor = miscdyn.minor;
559*8ac646d6SZijun Hu 	ret = misc_register(&miscstat);
560*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, -EBUSY);
561*8ac646d6SZijun Hu 	if (ret == 0)
562*8ac646d6SZijun Hu 		misc_deregister(&miscstat);
563*8ac646d6SZijun Hu 
564*8ac646d6SZijun Hu 	miscdev_test_can_open(test, &miscdyn);
565*8ac646d6SZijun Hu 
566*8ac646d6SZijun Hu 	misc_deregister(&miscdyn);
567*8ac646d6SZijun Hu }
568*8ac646d6SZijun Hu 
569*8ac646d6SZijun Hu static void __init miscdev_test_conflict_reverse(struct kunit *test)
570*8ac646d6SZijun Hu {
571*8ac646d6SZijun Hu 	int ret;
572*8ac646d6SZijun Hu 	struct miscdevice miscdyn = {
573*8ac646d6SZijun Hu 		.name = "miscdyn",
574*8ac646d6SZijun Hu 		.minor = MISC_DYNAMIC_MINOR,
575*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
576*8ac646d6SZijun Hu 	};
577*8ac646d6SZijun Hu 	struct miscdevice miscstat = {
578*8ac646d6SZijun Hu 		.name = "miscstat",
579*8ac646d6SZijun Hu 		.fops = &miscdev_test_fops,
580*8ac646d6SZijun Hu 	};
581*8ac646d6SZijun Hu 
582*8ac646d6SZijun Hu 	/*
583*8ac646d6SZijun Hu 	 * Find the first available dynamic minor to use it as a static
584*8ac646d6SZijun Hu 	 * minor later on.
585*8ac646d6SZijun Hu 	 */
586*8ac646d6SZijun Hu 	ret = misc_register(&miscdyn);
587*8ac646d6SZijun Hu 	KUNIT_ASSERT_EQ(test, ret, 0);
588*8ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
589*8ac646d6SZijun Hu 	miscstat.minor = miscdyn.minor;
590*8ac646d6SZijun Hu 	misc_deregister(&miscdyn);
591*8ac646d6SZijun Hu 
592*8ac646d6SZijun Hu 	ret = misc_register(&miscstat);
593*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
594*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, miscstat.minor, miscdyn.minor);
595*8ac646d6SZijun Hu 
596*8ac646d6SZijun Hu 	/*
597*8ac646d6SZijun Hu 	 * Try to register a dynamic minor after registering a static minor
598*8ac646d6SZijun Hu 	 * within the dynamic range. It should work but get a different
599*8ac646d6SZijun Hu 	 * minor.
600*8ac646d6SZijun Hu 	 */
601*8ac646d6SZijun Hu 	miscdyn.minor = MISC_DYNAMIC_MINOR;
602*8ac646d6SZijun Hu 	ret = misc_register(&miscdyn);
603*8ac646d6SZijun Hu 	KUNIT_EXPECT_EQ(test, ret, 0);
604*8ac646d6SZijun Hu 	KUNIT_EXPECT_NE(test, miscdyn.minor, miscstat.minor);
605*8ac646d6SZijun Hu 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
606*8ac646d6SZijun Hu 	if (ret == 0)
607*8ac646d6SZijun Hu 		misc_deregister(&miscdyn);
608*8ac646d6SZijun Hu 
609*8ac646d6SZijun Hu 	miscdev_test_can_open(test, &miscstat);
610*8ac646d6SZijun Hu 
611*8ac646d6SZijun Hu 	misc_deregister(&miscstat);
612*8ac646d6SZijun Hu }
613*8ac646d6SZijun Hu 
614*8ac646d6SZijun Hu static struct kunit_case test_cases[] = {
615*8ac646d6SZijun Hu 	KUNIT_CASE(kunit_dynamic_minor),
616*8ac646d6SZijun Hu 	KUNIT_CASE(kunit_static_minor),
617*8ac646d6SZijun Hu 	KUNIT_CASE(kunit_misc_dynamic_minor),
618*8ac646d6SZijun Hu 	KUNIT_CASE_PARAM(miscdev_test_twice, miscdev_gen_params),
619*8ac646d6SZijun Hu 	KUNIT_CASE_PARAM(miscdev_test_duplicate_minor, miscdev_gen_params),
620*8ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_duplicate_name),
621*8ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_duplicate_name_leak),
622*8ac646d6SZijun Hu 	KUNIT_CASE_PARAM(miscdev_test_duplicate_error, miscdev_gen_params),
623*8ac646d6SZijun Hu 	{}
624*8ac646d6SZijun Hu };
625*8ac646d6SZijun Hu 
626*8ac646d6SZijun Hu static struct kunit_suite test_suite = {
627*8ac646d6SZijun Hu 	.name = "miscdev",
628*8ac646d6SZijun Hu 	.suite_init = miscdev_find_minors,
629*8ac646d6SZijun Hu 	.test_cases = test_cases,
630*8ac646d6SZijun Hu };
631*8ac646d6SZijun Hu kunit_test_suite(test_suite);
632*8ac646d6SZijun Hu 
633*8ac646d6SZijun Hu static struct kunit_case __refdata test_init_cases[] = {
634*8ac646d6SZijun Hu 	KUNIT_CASE_PARAM(miscdev_test_static_basic, miscdev_gen_params),
635*8ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_dynamic_basic),
636*8ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_dynamic_only_range),
637*8ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_collision),
638*8ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_collision_reverse),
639*8ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_conflict),
640*8ac646d6SZijun Hu 	KUNIT_CASE(miscdev_test_conflict_reverse),
641*8ac646d6SZijun Hu 	{}
642*8ac646d6SZijun Hu };
643*8ac646d6SZijun Hu 
644*8ac646d6SZijun Hu static struct kunit_suite test_init_suite = {
645*8ac646d6SZijun Hu 	.name = "miscdev_init",
646*8ac646d6SZijun Hu 	.suite_init = miscdev_find_minors,
647*8ac646d6SZijun Hu 	.test_cases = test_init_cases,
648*8ac646d6SZijun Hu };
649*8ac646d6SZijun Hu kunit_test_init_section_suite(test_init_suite);
650*8ac646d6SZijun Hu 
651*8ac646d6SZijun Hu MODULE_LICENSE("GPL");
652*8ac646d6SZijun Hu MODULE_AUTHOR("Vimal Agrawal");
653*8ac646d6SZijun Hu MODULE_AUTHOR("Thadeu Lima de Souza Cascardo <cascardo@igalia.com>");
654*8ac646d6SZijun Hu MODULE_DESCRIPTION("Test module for misc character devices");
655