Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : /* 3 : : * Copyright (C) 2014 Davidlohr Bueso. 4 : : */ 5 : : #include <linux/sched/signal.h> 6 : : #include <linux/sched/task.h> 7 : : #include <linux/mm.h> 8 : : #include <linux/vmacache.h> 9 : : #include <asm/pgtable.h> 10 : : 11 : : /* 12 : : * Hash based on the pmd of addr if configured with MMU, which provides a good 13 : : * hit rate for workloads with spatial locality. Otherwise, use pages. 14 : : */ 15 : : #ifdef CONFIG_MMU 16 : : #define VMACACHE_SHIFT PMD_SHIFT 17 : : #else 18 : : #define VMACACHE_SHIFT PAGE_SHIFT 19 : : #endif 20 : : #define VMACACHE_HASH(addr) ((addr >> VMACACHE_SHIFT) & VMACACHE_MASK) 21 : : 22 : : /* 23 : : * This task may be accessing a foreign mm via (for example) 24 : : * get_user_pages()->find_vma(). The vmacache is task-local and this 25 : : * task's vmacache pertains to a different mm (ie, its own). There is 26 : : * nothing we can do here. 27 : : * 28 : : * Also handle the case where a kernel thread has adopted this mm via use_mm(). 29 : : * That kernel thread's vmacache is not applicable to this mm. 30 : : */ 31 : : static inline bool vmacache_valid_mm(struct mm_struct *mm) 32 : : { 33 [ + + + + : 84764478 : return current->mm == mm && !(current->flags & PF_KTHREAD); + + + + ] 34 : : } 35 : : 36 : 28224827 : void vmacache_update(unsigned long addr, struct vm_area_struct *newvma) 37 : : { 38 [ + + ]: 56449654 : if (vmacache_valid_mm(newvma->vm_mm)) 39 : 27427665 : current->vmacache.vmas[VMACACHE_HASH(addr)] = newvma; 40 : 28224827 : } 41 : : 42 : 56539651 : static bool vmacache_valid(struct mm_struct *mm) 43 : : { 44 : : struct task_struct *curr; 45 : : 46 [ + + ]: 56539651 : if (!vmacache_valid_mm(mm)) 47 : : return false; 48 : : 49 : : curr = current; 50 [ + + ]: 55721606 : if (mm->vmacache_seqnum != curr->vmacache.seqnum) { 51 : : /* 52 : : * First attempt will always be invalid, initialize 53 : : * the new cache for this task here. 54 : : */ 55 : 2925679 : curr->vmacache.seqnum = mm->vmacache_seqnum; 56 : : vmacache_flush(curr); 57 : 2925679 : return false; 58 : : } 59 : : return true; 60 : : } 61 : : 62 : 56530303 : struct vm_area_struct *vmacache_find(struct mm_struct *mm, unsigned long addr) 63 : : { 64 : 56530303 : int idx = VMACACHE_HASH(addr); 65 : : int i; 66 : : 67 : : count_vm_vmacache_event(VMACACHE_FIND_CALLS); 68 : : 69 [ + + ]: 56530303 : if (!vmacache_valid(mm)) 70 : : return NULL; 71 : : 72 [ + + ]: 116629806 : for (i = 0; i < VMACACHE_SIZE; i++) { 73 : 144529375 : struct vm_area_struct *vma = current->vmacache.vmas[idx]; 74 : : 75 [ + + ]: 144529375 : if (vma) { 76 : : #ifdef CONFIG_DEBUG_VM_VMACACHE 77 : : if (WARN_ON_ONCE(vma->vm_mm != mm)) 78 : : break; 79 : : #endif 80 [ + + + + ]: 91303496 : if (vma->vm_start <= addr && vma->vm_end > addr) { 81 : : count_vm_vmacache_event(VMACACHE_FIND_HITS); 82 : 27899569 : return vma; 83 : : } 84 : : } 85 [ + + ]: 116629806 : if (++idx == VMACACHE_SIZE) 86 : : idx = 0; 87 : : } 88 : : 89 : : return NULL; 90 : : } 91 : : 92 : : #ifndef CONFIG_MMU 93 : : struct vm_area_struct *vmacache_find_exact(struct mm_struct *mm, 94 : : unsigned long start, 95 : : unsigned long end) 96 : : { 97 : : int idx = VMACACHE_HASH(start); 98 : : int i; 99 : : 100 : : count_vm_vmacache_event(VMACACHE_FIND_CALLS); 101 : : 102 : : if (!vmacache_valid(mm)) 103 : : return NULL; 104 : : 105 : : for (i = 0; i < VMACACHE_SIZE; i++) { 106 : : struct vm_area_struct *vma = current->vmacache.vmas[idx]; 107 : : 108 : : if (vma && vma->vm_start == start && vma->vm_end == end) { 109 : : count_vm_vmacache_event(VMACACHE_FIND_HITS); 110 : : return vma; 111 : : } 112 : : if (++idx == VMACACHE_SIZE) 113 : : idx = 0; 114 : : } 115 : : 116 : : return NULL; 117 : : } 118 : : #endif