1133ff0eaSJérôme Glisse /* 2133ff0eaSJérôme Glisse * Copyright 2013 Red Hat Inc. 3133ff0eaSJérôme Glisse * 4133ff0eaSJérôme Glisse * This program is free software; you can redistribute it and/or modify 5133ff0eaSJérôme Glisse * it under the terms of the GNU General Public License as published by 6133ff0eaSJérôme Glisse * the Free Software Foundation; either version 2 of the License, or 7133ff0eaSJérôme Glisse * (at your option) any later version. 8133ff0eaSJérôme Glisse * 9133ff0eaSJérôme Glisse * This program is distributed in the hope that it will be useful, 10133ff0eaSJérôme Glisse * but WITHOUT ANY WARRANTY; without even the implied warranty of 11133ff0eaSJérôme Glisse * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12133ff0eaSJérôme Glisse * GNU General Public License for more details. 13133ff0eaSJérôme Glisse * 14133ff0eaSJérôme Glisse * Authors: Jérôme Glisse <jglisse@redhat.com> 15133ff0eaSJérôme Glisse */ 16133ff0eaSJérôme Glisse /* 17133ff0eaSJérôme Glisse * Refer to include/linux/hmm.h for information about heterogeneous memory 18133ff0eaSJérôme Glisse * management or HMM for short. 19133ff0eaSJérôme Glisse */ 20133ff0eaSJérôme Glisse #include <linux/mm.h> 21133ff0eaSJérôme Glisse #include <linux/hmm.h> 22133ff0eaSJérôme Glisse #include <linux/slab.h> 23133ff0eaSJérôme Glisse #include <linux/sched.h> 24*c0b12405SJérôme Glisse #include <linux/mmu_notifier.h> 25133ff0eaSJérôme Glisse 26133ff0eaSJérôme Glisse 27133ff0eaSJérôme Glisse #ifdef CONFIG_HMM 28*c0b12405SJérôme Glisse static const struct mmu_notifier_ops hmm_mmu_notifier_ops; 29*c0b12405SJérôme Glisse 30133ff0eaSJérôme Glisse /* 31133ff0eaSJérôme Glisse * struct hmm - HMM per mm struct 32133ff0eaSJérôme Glisse * 33133ff0eaSJérôme Glisse * @mm: mm struct this HMM struct is bound to 34*c0b12405SJérôme Glisse * @sequence: we track updates to the CPU page table with a sequence number 35*c0b12405SJérôme Glisse * @mirrors: list of mirrors for this mm 36*c0b12405SJérôme Glisse * @mmu_notifier: mmu notifier to track updates to CPU page table 37*c0b12405SJérôme Glisse * @mirrors_sem: read/write semaphore protecting the mirrors list 38133ff0eaSJérôme Glisse */ 39133ff0eaSJérôme Glisse struct hmm { 40133ff0eaSJérôme Glisse struct mm_struct *mm; 41*c0b12405SJérôme Glisse atomic_t sequence; 42*c0b12405SJérôme Glisse struct list_head mirrors; 43*c0b12405SJérôme Glisse struct mmu_notifier mmu_notifier; 44*c0b12405SJérôme Glisse struct rw_semaphore mirrors_sem; 45133ff0eaSJérôme Glisse }; 46133ff0eaSJérôme Glisse 47133ff0eaSJérôme Glisse /* 48133ff0eaSJérôme Glisse * hmm_register - register HMM against an mm (HMM internal) 49133ff0eaSJérôme Glisse * 50133ff0eaSJérôme Glisse * @mm: mm struct to attach to 51133ff0eaSJérôme Glisse * 52133ff0eaSJérôme Glisse * This is not intended to be used directly by device drivers. It allocates an 53133ff0eaSJérôme Glisse * HMM struct if mm does not have one, and initializes it. 54133ff0eaSJérôme Glisse */ 55133ff0eaSJérôme Glisse static struct hmm *hmm_register(struct mm_struct *mm) 56133ff0eaSJérôme Glisse { 57*c0b12405SJérôme Glisse struct hmm *hmm = READ_ONCE(mm->hmm); 58*c0b12405SJérôme Glisse bool cleanup = false; 59133ff0eaSJérôme Glisse 60133ff0eaSJérôme Glisse /* 61133ff0eaSJérôme Glisse * The hmm struct can only be freed once the mm_struct goes away, 62133ff0eaSJérôme Glisse * hence we should always have pre-allocated an new hmm struct 63133ff0eaSJérôme Glisse * above. 64133ff0eaSJérôme Glisse */ 65*c0b12405SJérôme Glisse if (hmm) 66*c0b12405SJérôme Glisse return hmm; 67*c0b12405SJérôme Glisse 68*c0b12405SJérôme Glisse hmm = kmalloc(sizeof(*hmm), GFP_KERNEL); 69*c0b12405SJérôme Glisse if (!hmm) 70*c0b12405SJérôme Glisse return NULL; 71*c0b12405SJérôme Glisse INIT_LIST_HEAD(&hmm->mirrors); 72*c0b12405SJérôme Glisse init_rwsem(&hmm->mirrors_sem); 73*c0b12405SJérôme Glisse atomic_set(&hmm->sequence, 0); 74*c0b12405SJérôme Glisse hmm->mmu_notifier.ops = NULL; 75*c0b12405SJérôme Glisse hmm->mm = mm; 76*c0b12405SJérôme Glisse 77*c0b12405SJérôme Glisse /* 78*c0b12405SJérôme Glisse * We should only get here if hold the mmap_sem in write mode ie on 79*c0b12405SJérôme Glisse * registration of first mirror through hmm_mirror_register() 80*c0b12405SJérôme Glisse */ 81*c0b12405SJérôme Glisse hmm->mmu_notifier.ops = &hmm_mmu_notifier_ops; 82*c0b12405SJérôme Glisse if (__mmu_notifier_register(&hmm->mmu_notifier, mm)) { 83*c0b12405SJérôme Glisse kfree(hmm); 84*c0b12405SJérôme Glisse return NULL; 85*c0b12405SJérôme Glisse } 86*c0b12405SJérôme Glisse 87*c0b12405SJérôme Glisse spin_lock(&mm->page_table_lock); 88*c0b12405SJérôme Glisse if (!mm->hmm) 89*c0b12405SJérôme Glisse mm->hmm = hmm; 90*c0b12405SJérôme Glisse else 91*c0b12405SJérôme Glisse cleanup = true; 92*c0b12405SJérôme Glisse spin_unlock(&mm->page_table_lock); 93*c0b12405SJérôme Glisse 94*c0b12405SJérôme Glisse if (cleanup) { 95*c0b12405SJérôme Glisse mmu_notifier_unregister(&hmm->mmu_notifier, mm); 96*c0b12405SJérôme Glisse kfree(hmm); 97*c0b12405SJérôme Glisse } 98*c0b12405SJérôme Glisse 99133ff0eaSJérôme Glisse return mm->hmm; 100133ff0eaSJérôme Glisse } 101133ff0eaSJérôme Glisse 102133ff0eaSJérôme Glisse void hmm_mm_destroy(struct mm_struct *mm) 103133ff0eaSJérôme Glisse { 104133ff0eaSJérôme Glisse kfree(mm->hmm); 105133ff0eaSJérôme Glisse } 106133ff0eaSJérôme Glisse #endif /* CONFIG_HMM */ 107*c0b12405SJérôme Glisse 108*c0b12405SJérôme Glisse #if IS_ENABLED(CONFIG_HMM_MIRROR) 109*c0b12405SJérôme Glisse static void hmm_invalidate_range(struct hmm *hmm, 110*c0b12405SJérôme Glisse enum hmm_update_type action, 111*c0b12405SJérôme Glisse unsigned long start, 112*c0b12405SJérôme Glisse unsigned long end) 113*c0b12405SJérôme Glisse { 114*c0b12405SJérôme Glisse struct hmm_mirror *mirror; 115*c0b12405SJérôme Glisse 116*c0b12405SJérôme Glisse down_read(&hmm->mirrors_sem); 117*c0b12405SJérôme Glisse list_for_each_entry(mirror, &hmm->mirrors, list) 118*c0b12405SJérôme Glisse mirror->ops->sync_cpu_device_pagetables(mirror, action, 119*c0b12405SJérôme Glisse start, end); 120*c0b12405SJérôme Glisse up_read(&hmm->mirrors_sem); 121*c0b12405SJérôme Glisse } 122*c0b12405SJérôme Glisse 123*c0b12405SJérôme Glisse static void hmm_invalidate_range_start(struct mmu_notifier *mn, 124*c0b12405SJérôme Glisse struct mm_struct *mm, 125*c0b12405SJérôme Glisse unsigned long start, 126*c0b12405SJérôme Glisse unsigned long end) 127*c0b12405SJérôme Glisse { 128*c0b12405SJérôme Glisse struct hmm *hmm = mm->hmm; 129*c0b12405SJérôme Glisse 130*c0b12405SJérôme Glisse VM_BUG_ON(!hmm); 131*c0b12405SJérôme Glisse 132*c0b12405SJérôme Glisse atomic_inc(&hmm->sequence); 133*c0b12405SJérôme Glisse } 134*c0b12405SJérôme Glisse 135*c0b12405SJérôme Glisse static void hmm_invalidate_range_end(struct mmu_notifier *mn, 136*c0b12405SJérôme Glisse struct mm_struct *mm, 137*c0b12405SJérôme Glisse unsigned long start, 138*c0b12405SJérôme Glisse unsigned long end) 139*c0b12405SJérôme Glisse { 140*c0b12405SJérôme Glisse struct hmm *hmm = mm->hmm; 141*c0b12405SJérôme Glisse 142*c0b12405SJérôme Glisse VM_BUG_ON(!hmm); 143*c0b12405SJérôme Glisse 144*c0b12405SJérôme Glisse hmm_invalidate_range(mm->hmm, HMM_UPDATE_INVALIDATE, start, end); 145*c0b12405SJérôme Glisse } 146*c0b12405SJérôme Glisse 147*c0b12405SJérôme Glisse static const struct mmu_notifier_ops hmm_mmu_notifier_ops = { 148*c0b12405SJérôme Glisse .invalidate_range_start = hmm_invalidate_range_start, 149*c0b12405SJérôme Glisse .invalidate_range_end = hmm_invalidate_range_end, 150*c0b12405SJérôme Glisse }; 151*c0b12405SJérôme Glisse 152*c0b12405SJérôme Glisse /* 153*c0b12405SJérôme Glisse * hmm_mirror_register() - register a mirror against an mm 154*c0b12405SJérôme Glisse * 155*c0b12405SJérôme Glisse * @mirror: new mirror struct to register 156*c0b12405SJérôme Glisse * @mm: mm to register against 157*c0b12405SJérôme Glisse * 158*c0b12405SJérôme Glisse * To start mirroring a process address space, the device driver must register 159*c0b12405SJérôme Glisse * an HMM mirror struct. 160*c0b12405SJérôme Glisse * 161*c0b12405SJérôme Glisse * THE mm->mmap_sem MUST BE HELD IN WRITE MODE ! 162*c0b12405SJérôme Glisse */ 163*c0b12405SJérôme Glisse int hmm_mirror_register(struct hmm_mirror *mirror, struct mm_struct *mm) 164*c0b12405SJérôme Glisse { 165*c0b12405SJérôme Glisse /* Sanity check */ 166*c0b12405SJérôme Glisse if (!mm || !mirror || !mirror->ops) 167*c0b12405SJérôme Glisse return -EINVAL; 168*c0b12405SJérôme Glisse 169*c0b12405SJérôme Glisse mirror->hmm = hmm_register(mm); 170*c0b12405SJérôme Glisse if (!mirror->hmm) 171*c0b12405SJérôme Glisse return -ENOMEM; 172*c0b12405SJérôme Glisse 173*c0b12405SJérôme Glisse down_write(&mirror->hmm->mirrors_sem); 174*c0b12405SJérôme Glisse list_add(&mirror->list, &mirror->hmm->mirrors); 175*c0b12405SJérôme Glisse up_write(&mirror->hmm->mirrors_sem); 176*c0b12405SJérôme Glisse 177*c0b12405SJérôme Glisse return 0; 178*c0b12405SJérôme Glisse } 179*c0b12405SJérôme Glisse EXPORT_SYMBOL(hmm_mirror_register); 180*c0b12405SJérôme Glisse 181*c0b12405SJérôme Glisse /* 182*c0b12405SJérôme Glisse * hmm_mirror_unregister() - unregister a mirror 183*c0b12405SJérôme Glisse * 184*c0b12405SJérôme Glisse * @mirror: new mirror struct to register 185*c0b12405SJérôme Glisse * 186*c0b12405SJérôme Glisse * Stop mirroring a process address space, and cleanup. 187*c0b12405SJérôme Glisse */ 188*c0b12405SJérôme Glisse void hmm_mirror_unregister(struct hmm_mirror *mirror) 189*c0b12405SJérôme Glisse { 190*c0b12405SJérôme Glisse struct hmm *hmm = mirror->hmm; 191*c0b12405SJérôme Glisse 192*c0b12405SJérôme Glisse down_write(&hmm->mirrors_sem); 193*c0b12405SJérôme Glisse list_del(&mirror->list); 194*c0b12405SJérôme Glisse up_write(&hmm->mirrors_sem); 195*c0b12405SJérôme Glisse } 196*c0b12405SJérôme Glisse EXPORT_SYMBOL(hmm_mirror_unregister); 197*c0b12405SJérôme Glisse #endif /* IS_ENABLED(CONFIG_HMM_MIRROR) */ 198