| rjw | 1f88458 | 2022-01-06 17:20:42 +0800 | [diff] [blame] | 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 |  | 
 | 10 | /* | 
 | 11 |  * This task may be accessing a foreign mm via (for example) | 
 | 12 |  * get_user_pages()->find_vma().  The vmacache is task-local and this | 
 | 13 |  * task's vmacache pertains to a different mm (ie, its own).  There is | 
 | 14 |  * nothing we can do here. | 
 | 15 |  * | 
 | 16 |  * Also handle the case where a kernel thread has adopted this mm via use_mm(). | 
 | 17 |  * That kernel thread's vmacache is not applicable to this mm. | 
 | 18 |  */ | 
 | 19 | static inline bool vmacache_valid_mm(struct mm_struct *mm) | 
 | 20 | { | 
 | 21 | 	return current->mm == mm && !(current->flags & PF_KTHREAD); | 
 | 22 | } | 
 | 23 |  | 
 | 24 | void vmacache_update(unsigned long addr, struct vm_area_struct *newvma) | 
 | 25 | { | 
 | 26 | 	if (vmacache_valid_mm(newvma->vm_mm)) | 
 | 27 | 		current->vmacache.vmas[VMACACHE_HASH(addr)] = newvma; | 
 | 28 | } | 
 | 29 |  | 
 | 30 | static bool vmacache_valid(struct mm_struct *mm) | 
 | 31 | { | 
 | 32 | 	struct task_struct *curr; | 
 | 33 |  | 
 | 34 | 	if (!vmacache_valid_mm(mm)) | 
 | 35 | 		return false; | 
 | 36 |  | 
 | 37 | 	curr = current; | 
 | 38 | 	if (mm->vmacache_seqnum != curr->vmacache.seqnum) { | 
 | 39 | 		/* | 
 | 40 | 		 * First attempt will always be invalid, initialize | 
 | 41 | 		 * the new cache for this task here. | 
 | 42 | 		 */ | 
 | 43 | 		curr->vmacache.seqnum = mm->vmacache_seqnum; | 
 | 44 | 		vmacache_flush(curr); | 
 | 45 | 		return false; | 
 | 46 | 	} | 
 | 47 | 	return true; | 
 | 48 | } | 
 | 49 |  | 
 | 50 | struct vm_area_struct *vmacache_find(struct mm_struct *mm, unsigned long addr) | 
 | 51 | { | 
 | 52 | 	int i; | 
 | 53 |  | 
 | 54 | 	count_vm_vmacache_event(VMACACHE_FIND_CALLS); | 
 | 55 |  | 
 | 56 | 	if (!vmacache_valid(mm)) | 
 | 57 | 		return NULL; | 
 | 58 |  | 
 | 59 | 	for (i = 0; i < VMACACHE_SIZE; i++) { | 
 | 60 | 		struct vm_area_struct *vma = current->vmacache.vmas[i]; | 
 | 61 |  | 
 | 62 | 		if (!vma) | 
 | 63 | 			continue; | 
 | 64 | 		if (WARN_ON_ONCE(vma->vm_mm != mm)) | 
 | 65 | 			break; | 
 | 66 | 		if (vma->vm_start <= addr && vma->vm_end > addr) { | 
 | 67 | 			count_vm_vmacache_event(VMACACHE_FIND_HITS); | 
 | 68 | 			return vma; | 
 | 69 | 		} | 
 | 70 | 	} | 
 | 71 |  | 
 | 72 | 	return NULL; | 
 | 73 | } | 
 | 74 |  | 
 | 75 | #ifndef CONFIG_MMU | 
 | 76 | struct vm_area_struct *vmacache_find_exact(struct mm_struct *mm, | 
 | 77 | 					   unsigned long start, | 
 | 78 | 					   unsigned long end) | 
 | 79 | { | 
 | 80 | 	int i; | 
 | 81 |  | 
 | 82 | 	count_vm_vmacache_event(VMACACHE_FIND_CALLS); | 
 | 83 |  | 
 | 84 | 	if (!vmacache_valid(mm)) | 
 | 85 | 		return NULL; | 
 | 86 |  | 
 | 87 | 	for (i = 0; i < VMACACHE_SIZE; i++) { | 
 | 88 | 		struct vm_area_struct *vma = current->vmacache.vmas[i]; | 
 | 89 |  | 
 | 90 | 		if (vma && vma->vm_start == start && vma->vm_end == end) { | 
 | 91 | 			count_vm_vmacache_event(VMACACHE_FIND_HITS); | 
 | 92 | 			return vma; | 
 | 93 | 		} | 
 | 94 | 	} | 
 | 95 |  | 
 | 96 | 	return NULL; | 
 | 97 | } | 
 | 98 | #endif |