1*b843c749SSergey Zigachev /* 2*b843c749SSergey Zigachev * Copyright 2014 Advanced Micro Devices, Inc. 3*b843c749SSergey Zigachev * All Rights Reserved. 4*b843c749SSergey Zigachev * 5*b843c749SSergey Zigachev * Permission is hereby granted, free of charge, to any person obtaining a 6*b843c749SSergey Zigachev * copy of this software and associated documentation files (the 7*b843c749SSergey Zigachev * "Software"), to deal in the Software without restriction, including 8*b843c749SSergey Zigachev * without limitation the rights to use, copy, modify, merge, publish, 9*b843c749SSergey Zigachev * distribute, sub license, and/or sell copies of the Software, and to 10*b843c749SSergey Zigachev * permit persons to whom the Software is furnished to do so, subject to 11*b843c749SSergey Zigachev * the following conditions: 12*b843c749SSergey Zigachev * 13*b843c749SSergey Zigachev * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14*b843c749SSergey Zigachev * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15*b843c749SSergey Zigachev * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 16*b843c749SSergey Zigachev * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 17*b843c749SSergey Zigachev * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18*b843c749SSergey Zigachev * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 19*b843c749SSergey Zigachev * USE OR OTHER DEALINGS IN THE SOFTWARE. 20*b843c749SSergey Zigachev * 21*b843c749SSergey Zigachev * The above copyright notice and this permission notice (including the 22*b843c749SSergey Zigachev * next paragraph) shall be included in all copies or substantial portions 23*b843c749SSergey Zigachev * of the Software. 24*b843c749SSergey Zigachev * 25*b843c749SSergey Zigachev */ 26*b843c749SSergey Zigachev /* 27*b843c749SSergey Zigachev * Authors: 28*b843c749SSergey Zigachev * Christian König <christian.koenig@amd.com> 29*b843c749SSergey Zigachev */ 30*b843c749SSergey Zigachev 31*b843c749SSergey Zigachev /** 32*b843c749SSergey Zigachev * DOC: MMU Notifier 33*b843c749SSergey Zigachev * 34*b843c749SSergey Zigachev * For coherent userptr handling registers an MMU notifier to inform the driver 35*b843c749SSergey Zigachev * about updates on the page tables of a process. 36*b843c749SSergey Zigachev * 37*b843c749SSergey Zigachev * When somebody tries to invalidate the page tables we block the update until 38*b843c749SSergey Zigachev * all operations on the pages in question are completed, then those pages are 39*b843c749SSergey Zigachev * marked as accessed and also dirty if it wasn't a read only access. 40*b843c749SSergey Zigachev * 41*b843c749SSergey Zigachev * New command submissions using the userptrs in question are delayed until all 42*b843c749SSergey Zigachev * page table invalidation are completed and we once more see a coherent process 43*b843c749SSergey Zigachev * address space. 44*b843c749SSergey Zigachev */ 45*b843c749SSergey Zigachev 46*b843c749SSergey Zigachev #include <linux/firmware.h> 47*b843c749SSergey Zigachev #include <linux/module.h> 48*b843c749SSergey Zigachev #include <linux/mmu_notifier.h> 49*b843c749SSergey Zigachev #include <linux/interval_tree.h> 50*b843c749SSergey Zigachev #include <drm/drmP.h> 51*b843c749SSergey Zigachev #include <drm/drm.h> 52*b843c749SSergey Zigachev 53*b843c749SSergey Zigachev #include "amdgpu.h" 54*b843c749SSergey Zigachev #include "amdgpu_amdkfd.h" 55*b843c749SSergey Zigachev 56*b843c749SSergey Zigachev /** 57*b843c749SSergey Zigachev * struct amdgpu_mn 58*b843c749SSergey Zigachev * 59*b843c749SSergey Zigachev * @adev: amdgpu device pointer 60*b843c749SSergey Zigachev * @mm: process address space 61*b843c749SSergey Zigachev * @mn: MMU notifier structure 62*b843c749SSergey Zigachev * @type: type of MMU notifier 63*b843c749SSergey Zigachev * @work: destruction work item 64*b843c749SSergey Zigachev * @node: hash table node to find structure by adev and mn 65*b843c749SSergey Zigachev * @lock: rw semaphore protecting the notifier nodes 66*b843c749SSergey Zigachev * @objects: interval tree containing amdgpu_mn_nodes 67*b843c749SSergey Zigachev * @read_lock: mutex for recursive locking of @lock 68*b843c749SSergey Zigachev * @recursion: depth of recursion 69*b843c749SSergey Zigachev * 70*b843c749SSergey Zigachev * Data for each amdgpu device and process address space. 71*b843c749SSergey Zigachev */ 72*b843c749SSergey Zigachev struct amdgpu_mn { 73*b843c749SSergey Zigachev /* constant after initialisation */ 74*b843c749SSergey Zigachev struct amdgpu_device *adev; 75*b843c749SSergey Zigachev struct mm_struct *mm; 76*b843c749SSergey Zigachev struct mmu_notifier mn; 77*b843c749SSergey Zigachev enum amdgpu_mn_type type; 78*b843c749SSergey Zigachev 79*b843c749SSergey Zigachev /* only used on destruction */ 80*b843c749SSergey Zigachev struct work_struct work; 81*b843c749SSergey Zigachev 82*b843c749SSergey Zigachev /* protected by adev->mn_lock */ 83*b843c749SSergey Zigachev struct hlist_node node; 84*b843c749SSergey Zigachev 85*b843c749SSergey Zigachev /* objects protected by lock */ 86*b843c749SSergey Zigachev struct rw_semaphore lock; 87*b843c749SSergey Zigachev struct rb_root_cached objects; 88*b843c749SSergey Zigachev struct mutex read_lock; 89*b843c749SSergey Zigachev atomic_t recursion; 90*b843c749SSergey Zigachev }; 91*b843c749SSergey Zigachev 92*b843c749SSergey Zigachev /** 93*b843c749SSergey Zigachev * struct amdgpu_mn_node 94*b843c749SSergey Zigachev * 95*b843c749SSergey Zigachev * @it: interval node defining start-last of the affected address range 96*b843c749SSergey Zigachev * @bos: list of all BOs in the affected address range 97*b843c749SSergey Zigachev * 98*b843c749SSergey Zigachev * Manages all BOs which are affected of a certain range of address space. 99*b843c749SSergey Zigachev */ 100*b843c749SSergey Zigachev struct amdgpu_mn_node { 101*b843c749SSergey Zigachev struct interval_tree_node it; 102*b843c749SSergey Zigachev struct list_head bos; 103*b843c749SSergey Zigachev }; 104*b843c749SSergey Zigachev 105*b843c749SSergey Zigachev /** 106*b843c749SSergey Zigachev * amdgpu_mn_destroy - destroy the MMU notifier 107*b843c749SSergey Zigachev * 108*b843c749SSergey Zigachev * @work: previously sheduled work item 109*b843c749SSergey Zigachev * 110*b843c749SSergey Zigachev * Lazy destroys the notifier from a work item 111*b843c749SSergey Zigachev */ 112*b843c749SSergey Zigachev static void amdgpu_mn_destroy(struct work_struct *work) 113*b843c749SSergey Zigachev { 114*b843c749SSergey Zigachev struct amdgpu_mn *amn = container_of(work, struct amdgpu_mn, work); 115*b843c749SSergey Zigachev struct amdgpu_device *adev = amn->adev; 116*b843c749SSergey Zigachev struct amdgpu_mn_node *node, *next_node; 117*b843c749SSergey Zigachev struct amdgpu_bo *bo, *next_bo; 118*b843c749SSergey Zigachev 119*b843c749SSergey Zigachev mutex_lock(&adev->mn_lock); 120*b843c749SSergey Zigachev down_write(&amn->lock); 121*b843c749SSergey Zigachev hash_del(&amn->node); 122*b843c749SSergey Zigachev rbtree_postorder_for_each_entry_safe(node, next_node, 123*b843c749SSergey Zigachev &amn->objects.rb_root, it.rb) { 124*b843c749SSergey Zigachev list_for_each_entry_safe(bo, next_bo, &node->bos, mn_list) { 125*b843c749SSergey Zigachev bo->mn = NULL; 126*b843c749SSergey Zigachev list_del_init(&bo->mn_list); 127*b843c749SSergey Zigachev } 128*b843c749SSergey Zigachev kfree(node); 129*b843c749SSergey Zigachev } 130*b843c749SSergey Zigachev up_write(&amn->lock); 131*b843c749SSergey Zigachev mutex_unlock(&adev->mn_lock); 132*b843c749SSergey Zigachev mmu_notifier_unregister_no_release(&amn->mn, amn->mm); 133*b843c749SSergey Zigachev kfree(amn); 134*b843c749SSergey Zigachev } 135*b843c749SSergey Zigachev 136*b843c749SSergey Zigachev /** 137*b843c749SSergey Zigachev * amdgpu_mn_release - callback to notify about mm destruction 138*b843c749SSergey Zigachev * 139*b843c749SSergey Zigachev * @mn: our notifier 140*b843c749SSergey Zigachev * @mm: the mm this callback is about 141*b843c749SSergey Zigachev * 142*b843c749SSergey Zigachev * Shedule a work item to lazy destroy our notifier. 143*b843c749SSergey Zigachev */ 144*b843c749SSergey Zigachev static void amdgpu_mn_release(struct mmu_notifier *mn, 145*b843c749SSergey Zigachev struct mm_struct *mm) 146*b843c749SSergey Zigachev { 147*b843c749SSergey Zigachev struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn); 148*b843c749SSergey Zigachev 149*b843c749SSergey Zigachev INIT_WORK(&amn->work, amdgpu_mn_destroy); 150*b843c749SSergey Zigachev schedule_work(&amn->work); 151*b843c749SSergey Zigachev } 152*b843c749SSergey Zigachev 153*b843c749SSergey Zigachev 154*b843c749SSergey Zigachev /** 155*b843c749SSergey Zigachev * amdgpu_mn_lock - take the write side lock for this notifier 156*b843c749SSergey Zigachev * 157*b843c749SSergey Zigachev * @mn: our notifier 158*b843c749SSergey Zigachev */ 159*b843c749SSergey Zigachev void amdgpu_mn_lock(struct amdgpu_mn *mn) 160*b843c749SSergey Zigachev { 161*b843c749SSergey Zigachev if (mn) 162*b843c749SSergey Zigachev down_write(&mn->lock); 163*b843c749SSergey Zigachev } 164*b843c749SSergey Zigachev 165*b843c749SSergey Zigachev /** 166*b843c749SSergey Zigachev * amdgpu_mn_unlock - drop the write side lock for this notifier 167*b843c749SSergey Zigachev * 168*b843c749SSergey Zigachev * @mn: our notifier 169*b843c749SSergey Zigachev */ 170*b843c749SSergey Zigachev void amdgpu_mn_unlock(struct amdgpu_mn *mn) 171*b843c749SSergey Zigachev { 172*b843c749SSergey Zigachev if (mn) 173*b843c749SSergey Zigachev up_write(&mn->lock); 174*b843c749SSergey Zigachev } 175*b843c749SSergey Zigachev 176*b843c749SSergey Zigachev /** 177*b843c749SSergey Zigachev * amdgpu_mn_read_lock - take the read side lock for this notifier 178*b843c749SSergey Zigachev * 179*b843c749SSergey Zigachev * @amn: our notifier 180*b843c749SSergey Zigachev */ 181*b843c749SSergey Zigachev static int amdgpu_mn_read_lock(struct amdgpu_mn *amn, bool blockable) 182*b843c749SSergey Zigachev { 183*b843c749SSergey Zigachev if (blockable) 184*b843c749SSergey Zigachev mutex_lock(&amn->read_lock); 185*b843c749SSergey Zigachev else if (!mutex_trylock(&amn->read_lock)) 186*b843c749SSergey Zigachev return -EAGAIN; 187*b843c749SSergey Zigachev 188*b843c749SSergey Zigachev if (atomic_inc_return(&amn->recursion) == 1) 189*b843c749SSergey Zigachev down_read_non_owner(&amn->lock); 190*b843c749SSergey Zigachev mutex_unlock(&amn->read_lock); 191*b843c749SSergey Zigachev 192*b843c749SSergey Zigachev return 0; 193*b843c749SSergey Zigachev } 194*b843c749SSergey Zigachev 195*b843c749SSergey Zigachev /** 196*b843c749SSergey Zigachev * amdgpu_mn_read_unlock - drop the read side lock for this notifier 197*b843c749SSergey Zigachev * 198*b843c749SSergey Zigachev * @amn: our notifier 199*b843c749SSergey Zigachev */ 200*b843c749SSergey Zigachev static void amdgpu_mn_read_unlock(struct amdgpu_mn *amn) 201*b843c749SSergey Zigachev { 202*b843c749SSergey Zigachev if (atomic_dec_return(&amn->recursion) == 0) 203*b843c749SSergey Zigachev up_read_non_owner(&amn->lock); 204*b843c749SSergey Zigachev } 205*b843c749SSergey Zigachev 206*b843c749SSergey Zigachev /** 207*b843c749SSergey Zigachev * amdgpu_mn_invalidate_node - unmap all BOs of a node 208*b843c749SSergey Zigachev * 209*b843c749SSergey Zigachev * @node: the node with the BOs to unmap 210*b843c749SSergey Zigachev * @start: start of address range affected 211*b843c749SSergey Zigachev * @end: end of address range affected 212*b843c749SSergey Zigachev * 213*b843c749SSergey Zigachev * Block for operations on BOs to finish and mark pages as accessed and 214*b843c749SSergey Zigachev * potentially dirty. 215*b843c749SSergey Zigachev */ 216*b843c749SSergey Zigachev static void amdgpu_mn_invalidate_node(struct amdgpu_mn_node *node, 217*b843c749SSergey Zigachev unsigned long start, 218*b843c749SSergey Zigachev unsigned long end) 219*b843c749SSergey Zigachev { 220*b843c749SSergey Zigachev struct amdgpu_bo *bo; 221*b843c749SSergey Zigachev long r; 222*b843c749SSergey Zigachev 223*b843c749SSergey Zigachev list_for_each_entry(bo, &node->bos, mn_list) { 224*b843c749SSergey Zigachev 225*b843c749SSergey Zigachev if (!amdgpu_ttm_tt_affect_userptr(bo->tbo.ttm, start, end)) 226*b843c749SSergey Zigachev continue; 227*b843c749SSergey Zigachev 228*b843c749SSergey Zigachev r = reservation_object_wait_timeout_rcu(bo->tbo.resv, 229*b843c749SSergey Zigachev true, false, MAX_SCHEDULE_TIMEOUT); 230*b843c749SSergey Zigachev if (r <= 0) 231*b843c749SSergey Zigachev DRM_ERROR("(%ld) failed to wait for user bo\n", r); 232*b843c749SSergey Zigachev 233*b843c749SSergey Zigachev amdgpu_ttm_tt_mark_user_pages(bo->tbo.ttm); 234*b843c749SSergey Zigachev } 235*b843c749SSergey Zigachev } 236*b843c749SSergey Zigachev 237*b843c749SSergey Zigachev /** 238*b843c749SSergey Zigachev * amdgpu_mn_invalidate_range_start_gfx - callback to notify about mm change 239*b843c749SSergey Zigachev * 240*b843c749SSergey Zigachev * @mn: our notifier 241*b843c749SSergey Zigachev * @mm: the mm this callback is about 242*b843c749SSergey Zigachev * @start: start of updated range 243*b843c749SSergey Zigachev * @end: end of updated range 244*b843c749SSergey Zigachev * 245*b843c749SSergey Zigachev * Block for operations on BOs to finish and mark pages as accessed and 246*b843c749SSergey Zigachev * potentially dirty. 247*b843c749SSergey Zigachev */ 248*b843c749SSergey Zigachev static int amdgpu_mn_invalidate_range_start_gfx(struct mmu_notifier *mn, 249*b843c749SSergey Zigachev struct mm_struct *mm, 250*b843c749SSergey Zigachev unsigned long start, 251*b843c749SSergey Zigachev unsigned long end, 252*b843c749SSergey Zigachev bool blockable) 253*b843c749SSergey Zigachev { 254*b843c749SSergey Zigachev struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn); 255*b843c749SSergey Zigachev struct interval_tree_node *it; 256*b843c749SSergey Zigachev 257*b843c749SSergey Zigachev /* notification is exclusive, but interval is inclusive */ 258*b843c749SSergey Zigachev end -= 1; 259*b843c749SSergey Zigachev 260*b843c749SSergey Zigachev /* TODO we should be able to split locking for interval tree and 261*b843c749SSergey Zigachev * amdgpu_mn_invalidate_node 262*b843c749SSergey Zigachev */ 263*b843c749SSergey Zigachev if (amdgpu_mn_read_lock(amn, blockable)) 264*b843c749SSergey Zigachev return -EAGAIN; 265*b843c749SSergey Zigachev 266*b843c749SSergey Zigachev it = interval_tree_iter_first(&amn->objects, start, end); 267*b843c749SSergey Zigachev while (it) { 268*b843c749SSergey Zigachev struct amdgpu_mn_node *node; 269*b843c749SSergey Zigachev 270*b843c749SSergey Zigachev if (!blockable) { 271*b843c749SSergey Zigachev amdgpu_mn_read_unlock(amn); 272*b843c749SSergey Zigachev return -EAGAIN; 273*b843c749SSergey Zigachev } 274*b843c749SSergey Zigachev 275*b843c749SSergey Zigachev node = container_of(it, struct amdgpu_mn_node, it); 276*b843c749SSergey Zigachev it = interval_tree_iter_next(it, start, end); 277*b843c749SSergey Zigachev 278*b843c749SSergey Zigachev amdgpu_mn_invalidate_node(node, start, end); 279*b843c749SSergey Zigachev } 280*b843c749SSergey Zigachev 281*b843c749SSergey Zigachev return 0; 282*b843c749SSergey Zigachev } 283*b843c749SSergey Zigachev 284*b843c749SSergey Zigachev /** 285*b843c749SSergey Zigachev * amdgpu_mn_invalidate_range_start_hsa - callback to notify about mm change 286*b843c749SSergey Zigachev * 287*b843c749SSergey Zigachev * @mn: our notifier 288*b843c749SSergey Zigachev * @mm: the mm this callback is about 289*b843c749SSergey Zigachev * @start: start of updated range 290*b843c749SSergey Zigachev * @end: end of updated range 291*b843c749SSergey Zigachev * 292*b843c749SSergey Zigachev * We temporarily evict all BOs between start and end. This 293*b843c749SSergey Zigachev * necessitates evicting all user-mode queues of the process. The BOs 294*b843c749SSergey Zigachev * are restorted in amdgpu_mn_invalidate_range_end_hsa. 295*b843c749SSergey Zigachev */ 296*b843c749SSergey Zigachev static int amdgpu_mn_invalidate_range_start_hsa(struct mmu_notifier *mn, 297*b843c749SSergey Zigachev struct mm_struct *mm, 298*b843c749SSergey Zigachev unsigned long start, 299*b843c749SSergey Zigachev unsigned long end, 300*b843c749SSergey Zigachev bool blockable) 301*b843c749SSergey Zigachev { 302*b843c749SSergey Zigachev struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn); 303*b843c749SSergey Zigachev struct interval_tree_node *it; 304*b843c749SSergey Zigachev 305*b843c749SSergey Zigachev /* notification is exclusive, but interval is inclusive */ 306*b843c749SSergey Zigachev end -= 1; 307*b843c749SSergey Zigachev 308*b843c749SSergey Zigachev if (amdgpu_mn_read_lock(amn, blockable)) 309*b843c749SSergey Zigachev return -EAGAIN; 310*b843c749SSergey Zigachev 311*b843c749SSergey Zigachev it = interval_tree_iter_first(&amn->objects, start, end); 312*b843c749SSergey Zigachev while (it) { 313*b843c749SSergey Zigachev struct amdgpu_mn_node *node; 314*b843c749SSergey Zigachev struct amdgpu_bo *bo; 315*b843c749SSergey Zigachev 316*b843c749SSergey Zigachev if (!blockable) { 317*b843c749SSergey Zigachev amdgpu_mn_read_unlock(amn); 318*b843c749SSergey Zigachev return -EAGAIN; 319*b843c749SSergey Zigachev } 320*b843c749SSergey Zigachev 321*b843c749SSergey Zigachev node = container_of(it, struct amdgpu_mn_node, it); 322*b843c749SSergey Zigachev it = interval_tree_iter_next(it, start, end); 323*b843c749SSergey Zigachev 324*b843c749SSergey Zigachev list_for_each_entry(bo, &node->bos, mn_list) { 325*b843c749SSergey Zigachev struct kgd_mem *mem = bo->kfd_bo; 326*b843c749SSergey Zigachev 327*b843c749SSergey Zigachev if (amdgpu_ttm_tt_affect_userptr(bo->tbo.ttm, 328*b843c749SSergey Zigachev start, end)) 329*b843c749SSergey Zigachev amdgpu_amdkfd_evict_userptr(mem, mm); 330*b843c749SSergey Zigachev } 331*b843c749SSergey Zigachev } 332*b843c749SSergey Zigachev 333*b843c749SSergey Zigachev return 0; 334*b843c749SSergey Zigachev } 335*b843c749SSergey Zigachev 336*b843c749SSergey Zigachev /** 337*b843c749SSergey Zigachev * amdgpu_mn_invalidate_range_end - callback to notify about mm change 338*b843c749SSergey Zigachev * 339*b843c749SSergey Zigachev * @mn: our notifier 340*b843c749SSergey Zigachev * @mm: the mm this callback is about 341*b843c749SSergey Zigachev * @start: start of updated range 342*b843c749SSergey Zigachev * @end: end of updated range 343*b843c749SSergey Zigachev * 344*b843c749SSergey Zigachev * Release the lock again to allow new command submissions. 345*b843c749SSergey Zigachev */ 346*b843c749SSergey Zigachev static void amdgpu_mn_invalidate_range_end(struct mmu_notifier *mn, 347*b843c749SSergey Zigachev struct mm_struct *mm, 348*b843c749SSergey Zigachev unsigned long start, 349*b843c749SSergey Zigachev unsigned long end) 350*b843c749SSergey Zigachev { 351*b843c749SSergey Zigachev struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn); 352*b843c749SSergey Zigachev 353*b843c749SSergey Zigachev amdgpu_mn_read_unlock(amn); 354*b843c749SSergey Zigachev } 355*b843c749SSergey Zigachev 356*b843c749SSergey Zigachev static const struct mmu_notifier_ops amdgpu_mn_ops[] = { 357*b843c749SSergey Zigachev [AMDGPU_MN_TYPE_GFX] = { 358*b843c749SSergey Zigachev .release = amdgpu_mn_release, 359*b843c749SSergey Zigachev .invalidate_range_start = amdgpu_mn_invalidate_range_start_gfx, 360*b843c749SSergey Zigachev .invalidate_range_end = amdgpu_mn_invalidate_range_end, 361*b843c749SSergey Zigachev }, 362*b843c749SSergey Zigachev [AMDGPU_MN_TYPE_HSA] = { 363*b843c749SSergey Zigachev .release = amdgpu_mn_release, 364*b843c749SSergey Zigachev .invalidate_range_start = amdgpu_mn_invalidate_range_start_hsa, 365*b843c749SSergey Zigachev .invalidate_range_end = amdgpu_mn_invalidate_range_end, 366*b843c749SSergey Zigachev }, 367*b843c749SSergey Zigachev }; 368*b843c749SSergey Zigachev 369*b843c749SSergey Zigachev /* Low bits of any reasonable mm pointer will be unused due to struct 370*b843c749SSergey Zigachev * alignment. Use these bits to make a unique key from the mm pointer 371*b843c749SSergey Zigachev * and notifier type. 372*b843c749SSergey Zigachev */ 373*b843c749SSergey Zigachev #define AMDGPU_MN_KEY(mm, type) ((unsigned long)(mm) + (type)) 374*b843c749SSergey Zigachev 375*b843c749SSergey Zigachev /** 376*b843c749SSergey Zigachev * amdgpu_mn_get - create notifier context 377*b843c749SSergey Zigachev * 378*b843c749SSergey Zigachev * @adev: amdgpu device pointer 379*b843c749SSergey Zigachev * @type: type of MMU notifier context 380*b843c749SSergey Zigachev * 381*b843c749SSergey Zigachev * Creates a notifier context for current->mm. 382*b843c749SSergey Zigachev */ 383*b843c749SSergey Zigachev struct amdgpu_mn *amdgpu_mn_get(struct amdgpu_device *adev, 384*b843c749SSergey Zigachev enum amdgpu_mn_type type) 385*b843c749SSergey Zigachev { 386*b843c749SSergey Zigachev struct mm_struct *mm = current->mm; 387*b843c749SSergey Zigachev struct amdgpu_mn *amn; 388*b843c749SSergey Zigachev unsigned long key = AMDGPU_MN_KEY(mm, type); 389*b843c749SSergey Zigachev int r; 390*b843c749SSergey Zigachev 391*b843c749SSergey Zigachev mutex_lock(&adev->mn_lock); 392*b843c749SSergey Zigachev if (down_write_killable(&mm->mmap_sem)) { 393*b843c749SSergey Zigachev mutex_unlock(&adev->mn_lock); 394*b843c749SSergey Zigachev return ERR_PTR(-EINTR); 395*b843c749SSergey Zigachev } 396*b843c749SSergey Zigachev 397*b843c749SSergey Zigachev hash_for_each_possible(adev->mn_hash, amn, node, key) 398*b843c749SSergey Zigachev if (AMDGPU_MN_KEY(amn->mm, amn->type) == key) 399*b843c749SSergey Zigachev goto release_locks; 400*b843c749SSergey Zigachev 401*b843c749SSergey Zigachev amn = kzalloc(sizeof(*amn), GFP_KERNEL); 402*b843c749SSergey Zigachev if (!amn) { 403*b843c749SSergey Zigachev amn = ERR_PTR(-ENOMEM); 404*b843c749SSergey Zigachev goto release_locks; 405*b843c749SSergey Zigachev } 406*b843c749SSergey Zigachev 407*b843c749SSergey Zigachev amn->adev = adev; 408*b843c749SSergey Zigachev amn->mm = mm; 409*b843c749SSergey Zigachev init_rwsem(&amn->lock); 410*b843c749SSergey Zigachev amn->type = type; 411*b843c749SSergey Zigachev amn->mn.ops = &amdgpu_mn_ops[type]; 412*b843c749SSergey Zigachev amn->objects = RB_ROOT_CACHED; 413*b843c749SSergey Zigachev mutex_init(&amn->read_lock); 414*b843c749SSergey Zigachev atomic_set(&amn->recursion, 0); 415*b843c749SSergey Zigachev 416*b843c749SSergey Zigachev r = __mmu_notifier_register(&amn->mn, mm); 417*b843c749SSergey Zigachev if (r) 418*b843c749SSergey Zigachev goto free_amn; 419*b843c749SSergey Zigachev 420*b843c749SSergey Zigachev hash_add(adev->mn_hash, &amn->node, AMDGPU_MN_KEY(mm, type)); 421*b843c749SSergey Zigachev 422*b843c749SSergey Zigachev release_locks: 423*b843c749SSergey Zigachev up_write(&mm->mmap_sem); 424*b843c749SSergey Zigachev mutex_unlock(&adev->mn_lock); 425*b843c749SSergey Zigachev 426*b843c749SSergey Zigachev return amn; 427*b843c749SSergey Zigachev 428*b843c749SSergey Zigachev free_amn: 429*b843c749SSergey Zigachev up_write(&mm->mmap_sem); 430*b843c749SSergey Zigachev mutex_unlock(&adev->mn_lock); 431*b843c749SSergey Zigachev kfree(amn); 432*b843c749SSergey Zigachev 433*b843c749SSergey Zigachev return ERR_PTR(r); 434*b843c749SSergey Zigachev } 435*b843c749SSergey Zigachev 436*b843c749SSergey Zigachev /** 437*b843c749SSergey Zigachev * amdgpu_mn_register - register a BO for notifier updates 438*b843c749SSergey Zigachev * 439*b843c749SSergey Zigachev * @bo: amdgpu buffer object 440*b843c749SSergey Zigachev * @addr: userptr addr we should monitor 441*b843c749SSergey Zigachev * 442*b843c749SSergey Zigachev * Registers an MMU notifier for the given BO at the specified address. 443*b843c749SSergey Zigachev * Returns 0 on success, -ERRNO if anything goes wrong. 444*b843c749SSergey Zigachev */ 445*b843c749SSergey Zigachev int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr) 446*b843c749SSergey Zigachev { 447*b843c749SSergey Zigachev unsigned long end = addr + amdgpu_bo_size(bo) - 1; 448*b843c749SSergey Zigachev struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); 449*b843c749SSergey Zigachev enum amdgpu_mn_type type = 450*b843c749SSergey Zigachev bo->kfd_bo ? AMDGPU_MN_TYPE_HSA : AMDGPU_MN_TYPE_GFX; 451*b843c749SSergey Zigachev struct amdgpu_mn *amn; 452*b843c749SSergey Zigachev struct amdgpu_mn_node *node = NULL, *new_node; 453*b843c749SSergey Zigachev struct list_head bos; 454*b843c749SSergey Zigachev struct interval_tree_node *it; 455*b843c749SSergey Zigachev 456*b843c749SSergey Zigachev amn = amdgpu_mn_get(adev, type); 457*b843c749SSergey Zigachev if (IS_ERR(amn)) 458*b843c749SSergey Zigachev return PTR_ERR(amn); 459*b843c749SSergey Zigachev 460*b843c749SSergey Zigachev new_node = kmalloc(sizeof(*new_node), GFP_KERNEL); 461*b843c749SSergey Zigachev if (!new_node) 462*b843c749SSergey Zigachev return -ENOMEM; 463*b843c749SSergey Zigachev 464*b843c749SSergey Zigachev INIT_LIST_HEAD(&bos); 465*b843c749SSergey Zigachev 466*b843c749SSergey Zigachev down_write(&amn->lock); 467*b843c749SSergey Zigachev 468*b843c749SSergey Zigachev while ((it = interval_tree_iter_first(&amn->objects, addr, end))) { 469*b843c749SSergey Zigachev kfree(node); 470*b843c749SSergey Zigachev node = container_of(it, struct amdgpu_mn_node, it); 471*b843c749SSergey Zigachev interval_tree_remove(&node->it, &amn->objects); 472*b843c749SSergey Zigachev addr = min(it->start, addr); 473*b843c749SSergey Zigachev end = max(it->last, end); 474*b843c749SSergey Zigachev list_splice(&node->bos, &bos); 475*b843c749SSergey Zigachev } 476*b843c749SSergey Zigachev 477*b843c749SSergey Zigachev if (!node) 478*b843c749SSergey Zigachev node = new_node; 479*b843c749SSergey Zigachev else 480*b843c749SSergey Zigachev kfree(new_node); 481*b843c749SSergey Zigachev 482*b843c749SSergey Zigachev bo->mn = amn; 483*b843c749SSergey Zigachev 484*b843c749SSergey Zigachev node->it.start = addr; 485*b843c749SSergey Zigachev node->it.last = end; 486*b843c749SSergey Zigachev INIT_LIST_HEAD(&node->bos); 487*b843c749SSergey Zigachev list_splice(&bos, &node->bos); 488*b843c749SSergey Zigachev list_add(&bo->mn_list, &node->bos); 489*b843c749SSergey Zigachev 490*b843c749SSergey Zigachev interval_tree_insert(&node->it, &amn->objects); 491*b843c749SSergey Zigachev 492*b843c749SSergey Zigachev up_write(&amn->lock); 493*b843c749SSergey Zigachev 494*b843c749SSergey Zigachev return 0; 495*b843c749SSergey Zigachev } 496*b843c749SSergey Zigachev 497*b843c749SSergey Zigachev /** 498*b843c749SSergey Zigachev * amdgpu_mn_unregister - unregister a BO for notifier updates 499*b843c749SSergey Zigachev * 500*b843c749SSergey Zigachev * @bo: amdgpu buffer object 501*b843c749SSergey Zigachev * 502*b843c749SSergey Zigachev * Remove any registration of MMU notifier updates from the buffer object. 503*b843c749SSergey Zigachev */ 504*b843c749SSergey Zigachev void amdgpu_mn_unregister(struct amdgpu_bo *bo) 505*b843c749SSergey Zigachev { 506*b843c749SSergey Zigachev struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); 507*b843c749SSergey Zigachev struct amdgpu_mn *amn; 508*b843c749SSergey Zigachev struct list_head *head; 509*b843c749SSergey Zigachev 510*b843c749SSergey Zigachev mutex_lock(&adev->mn_lock); 511*b843c749SSergey Zigachev 512*b843c749SSergey Zigachev amn = bo->mn; 513*b843c749SSergey Zigachev if (amn == NULL) { 514*b843c749SSergey Zigachev mutex_unlock(&adev->mn_lock); 515*b843c749SSergey Zigachev return; 516*b843c749SSergey Zigachev } 517*b843c749SSergey Zigachev 518*b843c749SSergey Zigachev down_write(&amn->lock); 519*b843c749SSergey Zigachev 520*b843c749SSergey Zigachev /* save the next list entry for later */ 521*b843c749SSergey Zigachev head = bo->mn_list.next; 522*b843c749SSergey Zigachev 523*b843c749SSergey Zigachev bo->mn = NULL; 524*b843c749SSergey Zigachev list_del_init(&bo->mn_list); 525*b843c749SSergey Zigachev 526*b843c749SSergey Zigachev if (list_empty(head)) { 527*b843c749SSergey Zigachev struct amdgpu_mn_node *node; 528*b843c749SSergey Zigachev 529*b843c749SSergey Zigachev node = container_of(head, struct amdgpu_mn_node, bos); 530*b843c749SSergey Zigachev interval_tree_remove(&node->it, &amn->objects); 531*b843c749SSergey Zigachev kfree(node); 532*b843c749SSergey Zigachev } 533*b843c749SSergey Zigachev 534*b843c749SSergey Zigachev up_write(&amn->lock); 535*b843c749SSergey Zigachev mutex_unlock(&adev->mn_lock); 536*b843c749SSergey Zigachev } 537*b843c749SSergey Zigachev 538