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. */
kunit_attach_mm(void)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, ¤t->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
kunit_vm_mmap_init(struct kunit_resource * res,void * context)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
kunit_vm_mmap_free(struct kunit_resource * res)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
kunit_vm_mmap(struct kunit * test,struct file * file,unsigned long addr,unsigned long len,unsigned long prot,unsigned long flag,unsigned long offset)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 ¶ms);
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