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, ¤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 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 ¶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