18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2014 Davidlohr Bueso. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 68c2ecf20Sopenharmony_ci#include <linux/sched/task.h> 78c2ecf20Sopenharmony_ci#include <linux/mm.h> 88c2ecf20Sopenharmony_ci#include <linux/vmacache.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci * Hash based on the pmd of addr if configured with MMU, which provides a good 128c2ecf20Sopenharmony_ci * hit rate for workloads with spatial locality. Otherwise, use pages. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 158c2ecf20Sopenharmony_ci#define VMACACHE_SHIFT PMD_SHIFT 168c2ecf20Sopenharmony_ci#else 178c2ecf20Sopenharmony_ci#define VMACACHE_SHIFT PAGE_SHIFT 188c2ecf20Sopenharmony_ci#endif 198c2ecf20Sopenharmony_ci#define VMACACHE_HASH(addr) ((addr >> VMACACHE_SHIFT) & VMACACHE_MASK) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * This task may be accessing a foreign mm via (for example) 238c2ecf20Sopenharmony_ci * get_user_pages()->find_vma(). The vmacache is task-local and this 248c2ecf20Sopenharmony_ci * task's vmacache pertains to a different mm (ie, its own). There is 258c2ecf20Sopenharmony_ci * nothing we can do here. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Also handle the case where a kernel thread has adopted this mm via 288c2ecf20Sopenharmony_ci * kthread_use_mm(). That kernel thread's vmacache is not applicable to this mm. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_cistatic inline bool vmacache_valid_mm(struct mm_struct *mm) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci return current->mm == mm && !(current->flags & PF_KTHREAD); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_civoid vmacache_update(unsigned long addr, struct vm_area_struct *newvma) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci if (vmacache_valid_mm(newvma->vm_mm)) 388c2ecf20Sopenharmony_ci current->vmacache.vmas[VMACACHE_HASH(addr)] = newvma; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic bool vmacache_valid(struct mm_struct *mm) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct task_struct *curr; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (!vmacache_valid_mm(mm)) 468c2ecf20Sopenharmony_ci return false; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci curr = current; 498c2ecf20Sopenharmony_ci if (mm->vmacache_seqnum != curr->vmacache.seqnum) { 508c2ecf20Sopenharmony_ci /* 518c2ecf20Sopenharmony_ci * First attempt will always be invalid, initialize 528c2ecf20Sopenharmony_ci * the new cache for this task here. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci curr->vmacache.seqnum = mm->vmacache_seqnum; 558c2ecf20Sopenharmony_ci vmacache_flush(curr); 568c2ecf20Sopenharmony_ci return false; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci return true; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct vm_area_struct *vmacache_find(struct mm_struct *mm, unsigned long addr) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci int idx = VMACACHE_HASH(addr); 648c2ecf20Sopenharmony_ci int i; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci count_vm_vmacache_event(VMACACHE_FIND_CALLS); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (!vmacache_valid(mm)) 698c2ecf20Sopenharmony_ci return NULL; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci for (i = 0; i < VMACACHE_SIZE; i++) { 728c2ecf20Sopenharmony_ci struct vm_area_struct *vma = current->vmacache.vmas[idx]; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (vma) { 758c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_VM_VMACACHE 768c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(vma->vm_mm != mm)) 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci#endif 798c2ecf20Sopenharmony_ci if (vma->vm_start <= addr && vma->vm_end > addr) { 808c2ecf20Sopenharmony_ci count_vm_vmacache_event(VMACACHE_FIND_HITS); 818c2ecf20Sopenharmony_ci return vma; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci if (++idx == VMACACHE_SIZE) 858c2ecf20Sopenharmony_ci idx = 0; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return NULL; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU 928c2ecf20Sopenharmony_cistruct vm_area_struct *vmacache_find_exact(struct mm_struct *mm, 938c2ecf20Sopenharmony_ci unsigned long start, 948c2ecf20Sopenharmony_ci unsigned long end) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci int idx = VMACACHE_HASH(start); 978c2ecf20Sopenharmony_ci int i; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci count_vm_vmacache_event(VMACACHE_FIND_CALLS); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (!vmacache_valid(mm)) 1028c2ecf20Sopenharmony_ci return NULL; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci for (i = 0; i < VMACACHE_SIZE; i++) { 1058c2ecf20Sopenharmony_ci struct vm_area_struct *vma = current->vmacache.vmas[idx]; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (vma && vma->vm_start == start && vma->vm_end == end) { 1088c2ecf20Sopenharmony_ci count_vm_vmacache_event(VMACACHE_FIND_HITS); 1098c2ecf20Sopenharmony_ci return vma; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci if (++idx == VMACACHE_SIZE) 1128c2ecf20Sopenharmony_ci idx = 0; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return NULL; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci#endif 118