linux/mm/vmacache.c
<<
>>
Prefs
   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 */
  31static inline bool vmacache_valid_mm(struct mm_struct *mm)
  32{
  33        return current->mm == mm && !(current->flags & PF_KTHREAD);
  34}
  35
  36void vmacache_update(unsigned long addr, struct vm_area_struct *newvma)
  37{
  38        if (vmacache_valid_mm(newvma->vm_mm))
  39                current->vmacache.vmas[VMACACHE_HASH(addr)] = newvma;
  40}
  41
  42static bool vmacache_valid(struct mm_struct *mm)
  43{
  44        struct task_struct *curr;
  45
  46        if (!vmacache_valid_mm(mm))
  47                return false;
  48
  49        curr = current;
  50        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                curr->vmacache.seqnum = mm->vmacache_seqnum;
  56                vmacache_flush(curr);
  57                return false;
  58        }
  59        return true;
  60}
  61
  62struct vm_area_struct *vmacache_find(struct mm_struct *mm, unsigned long addr)
  63{
  64        int idx = VMACACHE_HASH(addr);
  65        int i;
  66
  67        count_vm_vmacache_event(VMACACHE_FIND_CALLS);
  68
  69        if (!vmacache_valid(mm))
  70                return NULL;
  71
  72        for (i = 0; i < VMACACHE_SIZE; i++) {
  73                struct vm_area_struct *vma = current->vmacache.vmas[idx];
  74
  75                if (vma) {
  76#ifdef CONFIG_DEBUG_VM_VMACACHE
  77                        if (WARN_ON_ONCE(vma->vm_mm != mm))
  78                                break;
  79#endif
  80                        if (vma->vm_start <= addr && vma->vm_end > addr) {
  81                                count_vm_vmacache_event(VMACACHE_FIND_HITS);
  82                                return vma;
  83                        }
  84                }
  85                if (++idx == VMACACHE_SIZE)
  86                        idx = 0;
  87        }
  88
  89        return NULL;
  90}
  91
  92#ifndef CONFIG_MMU
  93struct 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
 119