xref: /linux/lib/kunit/user_alloc.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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 	/* arch_pick_mmap_layout() is only sane with MMU systems. */
34 	if (!IS_ENABLED(CONFIG_MMU))
35 		return -EINVAL;
36 
37 	mm = mm_alloc();
38 	if (!mm)
39 		return -ENOMEM;
40 
41 	/* Define the task size. */
42 	mm->task_size = TASK_SIZE;
43 
44 	/* Make sure we can allocate new VMAs. */
45 	arch_pick_mmap_layout(mm, &current->signal->rlim[RLIMIT_STACK]);
46 
47 	/* Attach the mm. It will be cleaned up when the process dies. */
48 	kthread_use_mm(mm);
49 
50 	return 0;
51 }
52 
53 static int kunit_vm_mmap_init(struct kunit_resource *res, void *context)
54 {
55 	struct kunit_vm_mmap_params *p = context;
56 	struct kunit_vm_mmap_resource vres;
57 	int ret;
58 
59 	ret = kunit_attach_mm();
60 	if (ret)
61 		return ret;
62 
63 	vres.size = p->len;
64 	vres.addr = vm_mmap(p->file, p->addr, p->len, p->prot, p->flag, p->offset);
65 	if (!vres.addr)
66 		return -ENOMEM;
67 	res->data = kmemdup(&vres, sizeof(vres), GFP_KERNEL);
68 	if (!res->data) {
69 		vm_munmap(vres.addr, vres.size);
70 		return -ENOMEM;
71 	}
72 
73 	return 0;
74 }
75 
76 static void kunit_vm_mmap_free(struct kunit_resource *res)
77 {
78 	struct kunit_vm_mmap_resource *vres = res->data;
79 
80 	/*
81 	 * Since this is executed from the test monitoring process,
82 	 * the test's mm has already been torn down. We don't need
83 	 * to run vm_munmap(vres->addr, vres->size), only clean up
84 	 * the vres.
85 	 */
86 
87 	kfree(vres);
88 	res->data = NULL;
89 }
90 
91 unsigned long kunit_vm_mmap(struct kunit *test, struct file *file,
92 			    unsigned long addr, unsigned long len,
93 			    unsigned long prot, unsigned long flag,
94 			    unsigned long offset)
95 {
96 	struct kunit_vm_mmap_params params = {
97 		.file = file,
98 		.addr = addr,
99 		.len = len,
100 		.prot = prot,
101 		.flag = flag,
102 		.offset = offset,
103 	};
104 	struct kunit_vm_mmap_resource *vres;
105 
106 	vres = kunit_alloc_resource(test,
107 				    kunit_vm_mmap_init,
108 				    kunit_vm_mmap_free,
109 				    GFP_KERNEL,
110 				    &params);
111 	if (vres)
112 		return vres->addr;
113 	return 0;
114 }
115 EXPORT_SYMBOL_GPL(kunit_vm_mmap);
116 
117 MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
118