Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/amdgpu: use HMM callback to replace mmu notifier

Replace our MMU notifier with hmm_mirror_ops.sync_cpu_device_pagetables
callback. Enable CONFIG_HMM and CONFIG_HMM_MIRROR as a dependency in
DRM_AMDGPU_USERPTR Kconfig.

It supports both KFD userptr and gfx userptr paths.

Signed-off-by: Philip Yang <Philip.Yang@amd.com>
Reviewed-by: Felix Kuehling <Felix.Kuehling@amd.com>
Reviewed-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>

authored by

Philip Yang and committed by
Alex Deucher
2c5a51f5 e14ba95b

+74 -100
+3 -3
drivers/gpu/drm/amd/amdgpu/Kconfig
··· 26 26 config DRM_AMDGPU_USERPTR 27 27 bool "Always enable userptr write support" 28 28 depends on DRM_AMDGPU 29 - select MMU_NOTIFIER 29 + select HMM_MIRROR 30 30 help 31 - This option selects CONFIG_MMU_NOTIFIER if it isn't already 32 - selected to enabled full userptr support. 31 + This option selects CONFIG_HMM and CONFIG_HMM_MIRROR if it 32 + isn't already selected to enabled full userptr support. 33 33 34 34 config DRM_AMDGPU_GART_DEBUGFS 35 35 bool "Allow GART access through debugfs"
+1 -1
drivers/gpu/drm/amd/amdgpu/Makefile
··· 173 173 amdgpu-$(CONFIG_COMPAT) += amdgpu_ioc32.o 174 174 amdgpu-$(CONFIG_VGA_SWITCHEROO) += amdgpu_atpx_handler.o 175 175 amdgpu-$(CONFIG_ACPI) += amdgpu_acpi.o 176 - amdgpu-$(CONFIG_MMU_NOTIFIER) += amdgpu_mn.o 176 + amdgpu-$(CONFIG_HMM_MIRROR) += amdgpu_mn.o 177 177 178 178 include $(FULL_AMD_PATH)/powerplay/Makefile 179 179
+69 -95
drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c
··· 45 45 46 46 #include <linux/firmware.h> 47 47 #include <linux/module.h> 48 - #include <linux/mmu_notifier.h> 48 + #include <linux/hmm.h> 49 49 #include <linux/interval_tree.h> 50 50 #include <drm/drmP.h> 51 51 #include <drm/drm.h> ··· 58 58 * 59 59 * @adev: amdgpu device pointer 60 60 * @mm: process address space 61 - * @mn: MMU notifier structure 62 61 * @type: type of MMU notifier 63 62 * @work: destruction work item 64 63 * @node: hash table node to find structure by adev and mn 65 64 * @lock: rw semaphore protecting the notifier nodes 66 65 * @objects: interval tree containing amdgpu_mn_nodes 67 - * @read_lock: mutex for recursive locking of @lock 68 - * @recursion: depth of recursion 66 + * @mirror: HMM mirror function support 69 67 * 70 68 * Data for each amdgpu device and process address space. 71 69 */ ··· 71 73 /* constant after initialisation */ 72 74 struct amdgpu_device *adev; 73 75 struct mm_struct *mm; 74 - struct mmu_notifier mn; 75 76 enum amdgpu_mn_type type; 76 77 77 78 /* only used on destruction */ ··· 82 85 /* objects protected by lock */ 83 86 struct rw_semaphore lock; 84 87 struct rb_root_cached objects; 85 - struct mutex read_lock; 86 - atomic_t recursion; 88 + 89 + /* HMM mirror */ 90 + struct hmm_mirror mirror; 87 91 }; 88 92 89 93 /** ··· 101 103 }; 102 104 103 105 /** 104 - * amdgpu_mn_destroy - destroy the MMU notifier 106 + * amdgpu_mn_destroy - destroy the HMM mirror 105 107 * 106 108 * @work: previously sheduled work item 107 109 * ··· 127 129 } 128 130 up_write(&amn->lock); 129 131 mutex_unlock(&adev->mn_lock); 130 - mmu_notifier_unregister_no_release(&amn->mn, amn->mm); 132 + 133 + hmm_mirror_unregister(&amn->mirror); 131 134 kfree(amn); 132 135 } 133 136 134 137 /** 135 - * amdgpu_mn_release - callback to notify about mm destruction 138 + * amdgpu_hmm_mirror_release - callback to notify about mm destruction 136 139 * 137 - * @mn: our notifier 138 - * @mm: the mm this callback is about 140 + * @mirror: the HMM mirror (mm) this callback is about 139 141 * 140 - * Shedule a work item to lazy destroy our notifier. 142 + * Shedule a work item to lazy destroy HMM mirror. 141 143 */ 142 - static void amdgpu_mn_release(struct mmu_notifier *mn, 143 - struct mm_struct *mm) 144 + static void amdgpu_hmm_mirror_release(struct hmm_mirror *mirror) 144 145 { 145 - struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn); 146 + struct amdgpu_mn *amn = container_of(mirror, struct amdgpu_mn, mirror); 146 147 147 148 INIT_WORK(&amn->work, amdgpu_mn_destroy); 148 149 schedule_work(&amn->work); 149 150 } 150 - 151 151 152 152 /** 153 153 * amdgpu_mn_lock - take the write side lock for this notifier ··· 177 181 static int amdgpu_mn_read_lock(struct amdgpu_mn *amn, bool blockable) 178 182 { 179 183 if (blockable) 180 - mutex_lock(&amn->read_lock); 181 - else if (!mutex_trylock(&amn->read_lock)) 184 + down_read(&amn->lock); 185 + else if (!down_read_trylock(&amn->lock)) 182 186 return -EAGAIN; 183 - 184 - if (atomic_inc_return(&amn->recursion) == 1) 185 - down_read_non_owner(&amn->lock); 186 - mutex_unlock(&amn->read_lock); 187 187 188 188 return 0; 189 189 } ··· 191 199 */ 192 200 static void amdgpu_mn_read_unlock(struct amdgpu_mn *amn) 193 201 { 194 - if (atomic_dec_return(&amn->recursion) == 0) 195 - up_read_non_owner(&amn->lock); 202 + up_read(&amn->lock); 196 203 } 197 204 198 205 /** ··· 226 235 } 227 236 228 237 /** 229 - * amdgpu_mn_invalidate_range_start_gfx - callback to notify about mm change 238 + * amdgpu_mn_sync_pagetables_gfx - callback to notify about mm change 230 239 * 231 - * @mn: our notifier 232 - * @range: mmu notifier context 240 + * @mirror: the hmm_mirror (mm) is about to update 241 + * @update: the update start, end address 233 242 * 234 243 * Block for operations on BOs to finish and mark pages as accessed and 235 244 * potentially dirty. 236 245 */ 237 - static int amdgpu_mn_invalidate_range_start_gfx(struct mmu_notifier *mn, 238 - const struct mmu_notifier_range *range) 246 + static int amdgpu_mn_sync_pagetables_gfx(struct hmm_mirror *mirror, 247 + const struct hmm_update *update) 239 248 { 240 - struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn); 249 + struct amdgpu_mn *amn = container_of(mirror, struct amdgpu_mn, mirror); 250 + unsigned long start = update->start; 251 + unsigned long end = update->end; 252 + bool blockable = update->blockable; 241 253 struct interval_tree_node *it; 242 - unsigned long end; 243 254 244 255 /* notification is exclusive, but interval is inclusive */ 245 - end = range->end - 1; 256 + end -= 1; 246 257 247 258 /* TODO we should be able to split locking for interval tree and 248 259 * amdgpu_mn_invalidate_node 249 260 */ 250 - if (amdgpu_mn_read_lock(amn, mmu_notifier_range_blockable(range))) 261 + if (amdgpu_mn_read_lock(amn, blockable)) 251 262 return -EAGAIN; 252 263 253 - it = interval_tree_iter_first(&amn->objects, range->start, end); 264 + it = interval_tree_iter_first(&amn->objects, start, end); 254 265 while (it) { 255 266 struct amdgpu_mn_node *node; 256 267 257 - if (!mmu_notifier_range_blockable(range)) { 268 + if (!blockable) { 258 269 amdgpu_mn_read_unlock(amn); 259 270 return -EAGAIN; 260 271 } 261 272 262 273 node = container_of(it, struct amdgpu_mn_node, it); 263 - it = interval_tree_iter_next(it, range->start, end); 274 + it = interval_tree_iter_next(it, start, end); 264 275 265 - amdgpu_mn_invalidate_node(node, range->start, end); 276 + amdgpu_mn_invalidate_node(node, start, end); 266 277 } 278 + 279 + amdgpu_mn_read_unlock(amn); 267 280 268 281 return 0; 269 282 } 270 283 271 284 /** 272 - * amdgpu_mn_invalidate_range_start_hsa - callback to notify about mm change 285 + * amdgpu_mn_sync_pagetables_hsa - callback to notify about mm change 273 286 * 274 - * @mn: our notifier 275 - * @mm: the mm this callback is about 276 - * @start: start of updated range 277 - * @end: end of updated range 287 + * @mirror: the hmm_mirror (mm) is about to update 288 + * @update: the update start, end address 278 289 * 279 290 * We temporarily evict all BOs between start and end. This 280 291 * necessitates evicting all user-mode queues of the process. The BOs 281 292 * are restorted in amdgpu_mn_invalidate_range_end_hsa. 282 293 */ 283 - static int amdgpu_mn_invalidate_range_start_hsa(struct mmu_notifier *mn, 284 - const struct mmu_notifier_range *range) 294 + static int amdgpu_mn_sync_pagetables_hsa(struct hmm_mirror *mirror, 295 + const struct hmm_update *update) 285 296 { 286 - struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn); 297 + struct amdgpu_mn *amn = container_of(mirror, struct amdgpu_mn, mirror); 298 + unsigned long start = update->start; 299 + unsigned long end = update->end; 300 + bool blockable = update->blockable; 287 301 struct interval_tree_node *it; 288 - unsigned long end; 289 302 290 303 /* notification is exclusive, but interval is inclusive */ 291 - end = range->end - 1; 304 + end -= 1; 292 305 293 - if (amdgpu_mn_read_lock(amn, mmu_notifier_range_blockable(range))) 306 + if (amdgpu_mn_read_lock(amn, blockable)) 294 307 return -EAGAIN; 295 308 296 - it = interval_tree_iter_first(&amn->objects, range->start, end); 309 + it = interval_tree_iter_first(&amn->objects, start, end); 297 310 while (it) { 298 311 struct amdgpu_mn_node *node; 299 312 struct amdgpu_bo *bo; 300 313 301 - if (!mmu_notifier_range_blockable(range)) { 314 + if (!blockable) { 302 315 amdgpu_mn_read_unlock(amn); 303 316 return -EAGAIN; 304 317 } 305 318 306 319 node = container_of(it, struct amdgpu_mn_node, it); 307 - it = interval_tree_iter_next(it, range->start, end); 320 + it = interval_tree_iter_next(it, start, end); 308 321 309 322 list_for_each_entry(bo, &node->bos, mn_list) { 310 323 struct kgd_mem *mem = bo->kfd_bo; 311 324 312 325 if (amdgpu_ttm_tt_affect_userptr(bo->tbo.ttm, 313 - range->start, 314 - end)) 315 - amdgpu_amdkfd_evict_userptr(mem, range->mm); 326 + start, end)) 327 + amdgpu_amdkfd_evict_userptr(mem, amn->mm); 316 328 } 317 329 } 318 330 331 + amdgpu_mn_read_unlock(amn); 332 + 319 333 return 0; 320 334 } 321 - 322 - /** 323 - * amdgpu_mn_invalidate_range_end - callback to notify about mm change 324 - * 325 - * @mn: our notifier 326 - * @mm: the mm this callback is about 327 - * @start: start of updated range 328 - * @end: end of updated range 329 - * 330 - * Release the lock again to allow new command submissions. 331 - */ 332 - static void amdgpu_mn_invalidate_range_end(struct mmu_notifier *mn, 333 - const struct mmu_notifier_range *range) 334 - { 335 - struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn); 336 - 337 - amdgpu_mn_read_unlock(amn); 338 - } 339 - 340 - static const struct mmu_notifier_ops amdgpu_mn_ops[] = { 341 - [AMDGPU_MN_TYPE_GFX] = { 342 - .release = amdgpu_mn_release, 343 - .invalidate_range_start = amdgpu_mn_invalidate_range_start_gfx, 344 - .invalidate_range_end = amdgpu_mn_invalidate_range_end, 345 - }, 346 - [AMDGPU_MN_TYPE_HSA] = { 347 - .release = amdgpu_mn_release, 348 - .invalidate_range_start = amdgpu_mn_invalidate_range_start_hsa, 349 - .invalidate_range_end = amdgpu_mn_invalidate_range_end, 350 - }, 351 - }; 352 335 353 336 /* Low bits of any reasonable mm pointer will be unused due to struct 354 337 * alignment. Use these bits to make a unique key from the mm pointer ··· 330 365 */ 331 366 #define AMDGPU_MN_KEY(mm, type) ((unsigned long)(mm) + (type)) 332 367 368 + static struct hmm_mirror_ops amdgpu_hmm_mirror_ops[] = { 369 + [AMDGPU_MN_TYPE_GFX] = { 370 + .sync_cpu_device_pagetables = amdgpu_mn_sync_pagetables_gfx, 371 + .release = amdgpu_hmm_mirror_release 372 + }, 373 + [AMDGPU_MN_TYPE_HSA] = { 374 + .sync_cpu_device_pagetables = amdgpu_mn_sync_pagetables_hsa, 375 + .release = amdgpu_hmm_mirror_release 376 + }, 377 + }; 378 + 333 379 /** 334 - * amdgpu_mn_get - create notifier context 380 + * amdgpu_mn_get - create HMM mirror context 335 381 * 336 382 * @adev: amdgpu device pointer 337 383 * @type: type of MMU notifier context 338 384 * 339 - * Creates a notifier context for current->mm. 385 + * Creates a HMM mirror context for current->mm. 340 386 */ 341 387 struct amdgpu_mn *amdgpu_mn_get(struct amdgpu_device *adev, 342 388 enum amdgpu_mn_type type) ··· 377 401 amn->mm = mm; 378 402 init_rwsem(&amn->lock); 379 403 amn->type = type; 380 - amn->mn.ops = &amdgpu_mn_ops[type]; 381 404 amn->objects = RB_ROOT_CACHED; 382 - mutex_init(&amn->read_lock); 383 - atomic_set(&amn->recursion, 0); 384 405 385 - r = __mmu_notifier_register(&amn->mn, mm); 406 + amn->mirror.ops = &amdgpu_hmm_mirror_ops[type]; 407 + r = hmm_mirror_register(&amn->mirror, mm); 386 408 if (r) 387 409 goto free_amn; 388 410 ··· 406 432 * @bo: amdgpu buffer object 407 433 * @addr: userptr addr we should monitor 408 434 * 409 - * Registers an MMU notifier for the given BO at the specified address. 435 + * Registers an HMM mirror for the given BO at the specified address. 410 436 * Returns 0 on success, -ERRNO if anything goes wrong. 411 437 */ 412 438 int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr) ··· 462 488 } 463 489 464 490 /** 465 - * amdgpu_mn_unregister - unregister a BO for notifier updates 491 + * amdgpu_mn_unregister - unregister a BO for HMM mirror updates 466 492 * 467 493 * @bo: amdgpu buffer object 468 494 * 469 - * Remove any registration of MMU notifier updates from the buffer object. 495 + * Remove any registration of HMM mirror updates from the buffer object. 470 496 */ 471 497 void amdgpu_mn_unregister(struct amdgpu_bo *bo) 472 498 {
+1 -1
drivers/gpu/drm/amd/amdgpu/amdgpu_mn.h
··· 34 34 AMDGPU_MN_TYPE_HSA, 35 35 }; 36 36 37 - #if defined(CONFIG_MMU_NOTIFIER) 37 + #if defined(CONFIG_HMM_MIRROR) 38 38 void amdgpu_mn_lock(struct amdgpu_mn *mn); 39 39 void amdgpu_mn_unlock(struct amdgpu_mn *mn); 40 40 struct amdgpu_mn *amdgpu_mn_get(struct amdgpu_device *adev,