diff --git a/drivers/moto_mmap_fault/Android.mk b/drivers/moto_mmap_fault/Android.mk new file mode 100644 index 000000000000..c29e1c9b7afb --- /dev/null +++ b/drivers/moto_mmap_fault/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := moto_mmap_fault.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/moto_mmap_fault/Kbuild b/drivers/moto_mmap_fault/Kbuild new file mode 100644 index 000000000000..5282f8338622 --- /dev/null +++ b/drivers/moto_mmap_fault/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += moto_mmap_fault.o diff --git a/drivers/moto_mmap_fault/Makefile b/drivers/moto_mmap_fault/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/moto_mmap_fault/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install + +%: + $(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS) + +clean: + rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers + rm -rf .tmp_versions diff --git a/drivers/moto_mmap_fault/moto_mmap_fault.c b/drivers/moto_mmap_fault/moto_mmap_fault.c new file mode 100644 index 000000000000..b610bf3dec33 --- /dev/null +++ b/drivers/moto_mmap_fault/moto_mmap_fault.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2023 Motorola Mobility LLC + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include // struct mm_struct, apply_to_page_range() +#include // kallsyms_lookup_name() +#include // vm_unmap_aliases() +#include +#include +#include + +typedef struct page *(*page_cache_get_page_funct_t)(struct address_space *mapping, pgoff_t offset, int fgp_flags, gfp_t gfp_mask); +static page_cache_get_page_funct_t page_cache_get_page_func; +typedef unsigned long (*kallsyms_lookup_name_func_t)(const char *name); +static kallsyms_lookup_name_func_t kallsyms_lookup_name_func; +static int max_ra_pages = 8; +module_param(max_ra_pages, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(max_ra_pages, "Max read ahead pages"); +static void __nocfi filemap_fault_get_page(void *p, struct vm_fault *vmf, struct page **page_out, bool *retry) +{ + struct file *file = vmf->vma->vm_file; + pgoff_t offset = vmf->pgoff; + struct page *page = NULL; + struct file_ra_state *ra = &file->f_ra; + struct address_space *mapping = file->f_mapping; + unsigned int mmap_miss; + unsigned int old_ra_pages = 0; + + if (page_cache_get_page_func) + page = page_cache_get_page_func(mapping, offset, 0, 0); + + if (likely(page) && !(vmf->flags & FAULT_FLAG_TRIED)) { + put_page(page); + } else if (!page) { + mmap_miss = READ_ONCE(ra->mmap_miss); + if ((vmf->vma->vm_flags & VM_RAND_READ) || + (!ra->ra_pages) || + (vmf->vma->vm_flags & VM_SEQ_READ) || + mmap_miss > 100) { + return; + } else { + old_ra_pages = ra->ra_pages; + if (ra->ra_pages > max_ra_pages) { + ra->ra_pages = max_ra_pages; // reduce the read ahead limit to 8 pages + vmf->android_oem_data1[0] = old_ra_pages; + vmf->android_oem_data1[1] = max_ra_pages; + } + return; + } + } else { + put_page(page); + } + + return; +} + +static void __nocfi filemap_fault_cache_page(void *p, struct vm_fault *vmf, struct page *page) +{ + struct file *file = vmf->vma->vm_file; + struct file_ra_state *ra = &file->f_ra; + if ((ra->ra_pages == max_ra_pages) && (vmf->android_oem_data1[0] != 0) && (vmf->android_oem_data1[1] != 0)) { + ra->ra_pages = (unsigned long)vmf->android_oem_data1[0]; //restore the old ra_pages + vmf->android_oem_data1[0] = 0; + vmf->android_oem_data1[1] = 0; + } +} + +static int __nocfi __init moto_mmap_fault_init(void) +{ + int ret = 0; + struct kprobe kp = { + .symbol_name = "kallsyms_lookup_name", + }; + + ret = register_kprobe(&kp); + if (ret < 0) { + pr_err("Failed to register kprobe, %d\n", ret); + return ret; + } + unregister_kprobe(&kp); + kallsyms_lookup_name_func = (kallsyms_lookup_name_func_t)kp.addr; + if (!kallsyms_lookup_name_func) { + pr_err("Failed to get kallsyms_lookup_name address\n"); + return -ENXIO; + } + + page_cache_get_page_func = (void *)kallsyms_lookup_name_func("pagecache_get_page"); + if (!page_cache_get_page_func) { + pr_err("Failed to get required function\n"); + return -ENXIO; + } + ret = register_trace_android_vh_filemap_fault_get_page(filemap_fault_get_page, NULL) ?: + register_trace_android_vh_filemap_fault_cache_page(filemap_fault_cache_page, NULL); + + if (ret != 0) + return -ENXIO; + else + return 0; +} +static void __nocfi __exit moto_mmap_fault_exit(void) +{ + unregister_trace_android_vh_filemap_fault_get_page(filemap_fault_get_page, NULL); + unregister_trace_android_vh_filemap_fault_cache_page(filemap_fault_cache_page, NULL); +} + +module_init(moto_mmap_fault_init); +module_exit(moto_mmap_fault_exit); +MODULE_DESCRIPTION("Motorola vendor mmap fault driver"); +MODULE_LICENSE("GPL v2");