xref: /linux-6.15/lib/kunit/user_alloc.c (revision 51104c19)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * KUnit userspace memory allocation resource management.
4  */
5 #include <kunit/resource.h>
6 #include <kunit/test.h>
7 #include <linux/kthread.h>
8 #include <linux/mm.h>
9 
10 struct kunit_vm_mmap_resource {
11 	unsigned long addr;
12 	size_t size;
13 };
14 
15 /* vm_mmap() arguments */
16 struct kunit_vm_mmap_params {
17 	struct file *file;
18 	unsigned long addr;
19 	unsigned long len;
20 	unsigned long prot;
21 	unsigned long flag;
22 	unsigned long offset;
23 };
24 
25 /* Create and attach a new mm if it doesn't already exist. */
26 static int kunit_attach_mm(void)
27 {
28 	struct mm_struct *mm;
29 
30 	if (current->mm)
31 		return 0;
32 
33 	mm = mm_alloc();
34 	if (!mm)
35 		return -ENOMEM;
36 
37 	/* Define the task size. */
38 	mm->task_size = TASK_SIZE;
39 
40 	/* Make sure we can allocate new VMAs. */
41 	arch_pick_mmap_layout(mm, &current->signal->rlim[RLIMIT_STACK]);
42 
43 	/* Attach the mm. It will be cleaned up when the process dies. */
44 	kthread_use_mm(mm);
45 
46 	return 0;
47 }
48 
49 static int kunit_vm_mmap_init(struct kunit_resource *res, void *context)
50 {
51 	struct kunit_vm_mmap_params *p = context;
52 	struct kunit_vm_mmap_resource vres;
53 	int ret;
54 
55 	ret = kunit_attach_mm();
56 	if (ret)
57 		return ret;
58 
59 	vres.size = p->len;
60 	vres.addr = vm_mmap(p->file, p->addr, p->len, p->prot, p->flag, p->offset);
61 	if (!vres.addr)
62 		return -ENOMEM;
63 	res->data = kmemdup(&vres, sizeof(vres), GFP_KERNEL);
64 	if (!res->data) {
65 		vm_munmap(vres.addr, vres.size);
66 		return -ENOMEM;
67 	}
68 
69 	return 0;
70 }
71 
72 static void kunit_vm_mmap_free(struct kunit_resource *res)
73 {
74 	struct kunit_vm_mmap_resource *vres = res->data;
75 
76 	/*
77 	 * Since this is executed from the test monitoring process,
78 	 * the test's mm has already been torn down. We don't need
79 	 * to run vm_munmap(vres->addr, vres->size), only clean up
80 	 * the vres.
81 	 */
82 
83 	kfree(vres);
84 	res->data = NULL;
85 }
86 
87 unsigned long kunit_vm_mmap(struct kunit *test, struct file *file,
88 			    unsigned long addr, unsigned long len,
89 			    unsigned long prot, unsigned long flag,
90 			    unsigned long offset)
91 {
92 	struct kunit_vm_mmap_params params = {
93 		.file = file,
94 		.addr = addr,
95 		.len = len,
96 		.prot = prot,
97 		.flag = flag,
98 		.offset = offset,
99 	};
100 	struct kunit_vm_mmap_resource *vres;
101 
102 	vres = kunit_alloc_resource(test,
103 				    kunit_vm_mmap_init,
104 				    kunit_vm_mmap_free,
105 				    GFP_KERNEL,
106 				    &params);
107 	if (vres)
108 		return vres->addr;
109 	return 0;
110 }
111 EXPORT_SYMBOL_GPL(kunit_vm_mmap);
112 
113 MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
114