| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * KUnit userspace memory allocation resource management. | |
| */ | |
| #include <kunit/resource.h> | |
| #include <kunit/test.h> | |
| #include <linux/kthread.h> | |
| #include <linux/mm.h> | |
| struct kunit_vm_mmap_resource { | |
| unsigned long addr; | |
| size_t size; | |
| }; | |
| /* vm_mmap() arguments */ | |
| struct kunit_vm_mmap_params { | |
| struct file *file; | |
| unsigned long addr; | |
| unsigned long len; | |
| unsigned long prot; | |
| unsigned long flag; | |
| unsigned long offset; | |
| }; | |
| int kunit_attach_mm(void) | |
| { | |
| struct mm_struct *mm; | |
| if (current->mm) | |
| return 0; | |
| /* arch_pick_mmap_layout() is only sane with MMU systems. */ | |
| if (!IS_ENABLED(CONFIG_MMU)) | |
| return -EINVAL; | |
| mm = mm_alloc(); | |
| if (!mm) | |
| return -ENOMEM; | |
| /* Define the task size. */ | |
| mm->task_size = TASK_SIZE; | |
| /* Make sure we can allocate new VMAs. */ | |
| arch_pick_mmap_layout(mm, ¤t->signal->rlim[RLIMIT_STACK]); | |
| /* Attach the mm. It will be cleaned up when the process dies. */ | |
| kthread_use_mm(mm); | |
| return 0; | |
| } | |
| EXPORT_SYMBOL_GPL(kunit_attach_mm); | |
| static int kunit_vm_mmap_init(struct kunit_resource *res, void *context) | |
| { | |
| struct kunit_vm_mmap_params *p = context; | |
| struct kunit_vm_mmap_resource vres; | |
| int ret; | |
| ret = kunit_attach_mm(); | |
| if (ret) | |
| return ret; | |
| vres.size = p->len; | |
| vres.addr = vm_mmap(p->file, p->addr, p->len, p->prot, p->flag, p->offset); | |
| if (!vres.addr) | |
| return -ENOMEM; | |
| res->data = kmemdup(&vres, sizeof(vres), GFP_KERNEL); | |
| if (!res->data) { | |
| vm_munmap(vres.addr, vres.size); | |
| return -ENOMEM; | |
| } | |
| return 0; | |
| } | |
| static void kunit_vm_mmap_free(struct kunit_resource *res) | |
| { | |
| struct kunit_vm_mmap_resource *vres = res->data; | |
| /* | |
| * Since this is executed from the test monitoring process, | |
| * the test's mm has already been torn down. We don't need | |
| * to run vm_munmap(vres->addr, vres->size), only clean up | |
| * the vres. | |
| */ | |
| kfree(vres); | |
| res->data = NULL; | |
| } | |
| unsigned long kunit_vm_mmap(struct kunit *test, struct file *file, | |
| unsigned long addr, unsigned long len, | |
| unsigned long prot, unsigned long flag, | |
| unsigned long offset) | |
| { | |
| struct kunit_vm_mmap_params params = { | |
| .file = file, | |
| .addr = addr, | |
| .len = len, | |
| .prot = prot, | |
| .flag = flag, | |
| .offset = offset, | |
| }; | |
| struct kunit_vm_mmap_resource *vres; | |
| vres = kunit_alloc_resource(test, | |
| kunit_vm_mmap_init, | |
| kunit_vm_mmap_free, | |
| GFP_KERNEL, | |
| ¶ms); | |
| if (vres) | |
| return vres->addr; | |
| return 0; | |
| } | |
| EXPORT_SYMBOL_GPL(kunit_vm_mmap); | |
| MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); |