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