diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c')
| -rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c | 207 | 
1 files changed, 125 insertions, 82 deletions
| diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c index 83e344fbb50a..a365ea2383d1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c @@ -28,6 +28,21 @@   *    Christian König <[email protected]>   */ +/** + * DOC: MMU Notifier + * + * For coherent userptr handling registers an MMU notifier to inform the driver + * about updates on the page tables of a process. + * + * When somebody tries to invalidate the page tables we block the update until + * all operations on the pages in question are completed, then those pages are + * marked as accessed and also dirty if it wasn't a read only access. + * + * New command submissions using the userptrs in question are delayed until all + * page table invalidation are completed and we once more see a coherent process + * address space. + */ +  #include <linux/firmware.h>  #include <linux/module.h>  #include <linux/mmu_notifier.h> @@ -38,6 +53,22 @@  #include "amdgpu.h"  #include "amdgpu_amdkfd.h" +/** + * struct amdgpu_mn + * + * @adev: amdgpu device pointer + * @mm: process address space + * @mn: MMU notifier structure + * @type: type of MMU notifier + * @work: destruction work item + * @node: hash table node to find structure by adev and mn + * @lock: rw semaphore protecting the notifier nodes + * @objects: interval tree containing amdgpu_mn_nodes + * @read_lock: mutex for recursive locking of @lock + * @recursion: depth of recursion + * + * Data for each amdgpu device and process address space. + */  struct amdgpu_mn {  	/* constant after initialisation */  	struct amdgpu_device	*adev; @@ -58,13 +89,21 @@ struct amdgpu_mn {  	atomic_t		recursion;  }; +/** + * struct amdgpu_mn_node + * + * @it: interval node defining start-last of the affected address range + * @bos: list of all BOs in the affected address range + * + * Manages all BOs which are affected of a certain range of address space. + */  struct amdgpu_mn_node {  	struct interval_tree_node	it;  	struct list_head		bos;  };  /** - * amdgpu_mn_destroy - destroy the rmn + * amdgpu_mn_destroy - destroy the MMU notifier   *   * @work: previously sheduled work item   * @@ -72,47 +111,50 @@ struct amdgpu_mn_node {   */  static void amdgpu_mn_destroy(struct work_struct *work)  { -	struct amdgpu_mn *rmn = container_of(work, struct amdgpu_mn, work); -	struct amdgpu_device *adev = rmn->adev; +	struct amdgpu_mn *amn = container_of(work, struct amdgpu_mn, work); +	struct amdgpu_device *adev = amn->adev;  	struct amdgpu_mn_node *node, *next_node;  	struct amdgpu_bo *bo, *next_bo;  	mutex_lock(&adev->mn_lock); -	down_write(&rmn->lock); -	hash_del(&rmn->node); +	down_write(&amn->lock); +	hash_del(&amn->node);  	rbtree_postorder_for_each_entry_safe(node, next_node, -					     &rmn->objects.rb_root, it.rb) { +					     &amn->objects.rb_root, it.rb) {  		list_for_each_entry_safe(bo, next_bo, &node->bos, mn_list) {  			bo->mn = NULL;  			list_del_init(&bo->mn_list);  		}  		kfree(node);  	} -	up_write(&rmn->lock); +	up_write(&amn->lock);  	mutex_unlock(&adev->mn_lock); -	mmu_notifier_unregister_no_release(&rmn->mn, rmn->mm); -	kfree(rmn); +	mmu_notifier_unregister_no_release(&amn->mn, amn->mm); +	kfree(amn);  }  /**   * amdgpu_mn_release - callback to notify about mm destruction   *   * @mn: our notifier - * @mn: the mm this callback is about + * @mm: the mm this callback is about   *   * Shedule a work item to lazy destroy our notifier.   */  static void amdgpu_mn_release(struct mmu_notifier *mn,  			      struct mm_struct *mm)  { -	struct amdgpu_mn *rmn = container_of(mn, struct amdgpu_mn, mn); -	INIT_WORK(&rmn->work, amdgpu_mn_destroy); -	schedule_work(&rmn->work); +	struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn); + +	INIT_WORK(&amn->work, amdgpu_mn_destroy); +	schedule_work(&amn->work);  }  /** - * amdgpu_mn_lock - take the write side lock for this mn + * amdgpu_mn_lock - take the write side lock for this notifier + * + * @mn: our notifier   */  void amdgpu_mn_lock(struct amdgpu_mn *mn)  { @@ -121,7 +163,9 @@ void amdgpu_mn_lock(struct amdgpu_mn *mn)  }  /** - * amdgpu_mn_unlock - drop the write side lock for this mn + * amdgpu_mn_unlock - drop the write side lock for this notifier + * + * @mn: our notifier   */  void amdgpu_mn_unlock(struct amdgpu_mn *mn)  { @@ -130,40 +174,38 @@ void amdgpu_mn_unlock(struct amdgpu_mn *mn)  }  /** - * amdgpu_mn_read_lock - take the rmn read lock - * - * @rmn: our notifier + * amdgpu_mn_read_lock - take the read side lock for this notifier   * - * Take the rmn read side lock. + * @amn: our notifier   */ -static void amdgpu_mn_read_lock(struct amdgpu_mn *rmn) +static void amdgpu_mn_read_lock(struct amdgpu_mn *amn)  { -	mutex_lock(&rmn->read_lock); -	if (atomic_inc_return(&rmn->recursion) == 1) -		down_read_non_owner(&rmn->lock); -	mutex_unlock(&rmn->read_lock); +	mutex_lock(&amn->read_lock); +	if (atomic_inc_return(&amn->recursion) == 1) +		down_read_non_owner(&amn->lock); +	mutex_unlock(&amn->read_lock);  }  /** - * amdgpu_mn_read_unlock - drop the rmn read lock + * amdgpu_mn_read_unlock - drop the read side lock for this notifier   * - * @rmn: our notifier - * - * Drop the rmn read side lock. + * @amn: our notifier   */ -static void amdgpu_mn_read_unlock(struct amdgpu_mn *rmn) +static void amdgpu_mn_read_unlock(struct amdgpu_mn *amn)  { -	if (atomic_dec_return(&rmn->recursion) == 0) -		up_read_non_owner(&rmn->lock); +	if (atomic_dec_return(&amn->recursion) == 0) +		up_read_non_owner(&amn->lock);  }  /**   * amdgpu_mn_invalidate_node - unmap all BOs of a node   *   * @node: the node with the BOs to unmap + * @start: start of address range affected + * @end: end of address range affected   * - * We block for all BOs and unmap them by move them - * into system domain again. + * Block for operations on BOs to finish and mark pages as accessed and + * potentially dirty.   */  static void amdgpu_mn_invalidate_node(struct amdgpu_mn_node *node,  				      unsigned long start, @@ -190,27 +232,27 @@ static void amdgpu_mn_invalidate_node(struct amdgpu_mn_node *node,   * amdgpu_mn_invalidate_range_start_gfx - callback to notify about mm change   *   * @mn: our notifier - * @mn: the mm this callback is about + * @mm: the mm this callback is about   * @start: start of updated range   * @end: end of updated range   * - * We block for all BOs between start and end to be idle and - * unmap them by move them into system domain again. + * Block for operations on BOs to finish and mark pages as accessed and + * potentially dirty.   */  static void amdgpu_mn_invalidate_range_start_gfx(struct mmu_notifier *mn,  						 struct mm_struct *mm,  						 unsigned long start,  						 unsigned long end)  { -	struct amdgpu_mn *rmn = container_of(mn, struct amdgpu_mn, mn); +	struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn);  	struct interval_tree_node *it;  	/* notification is exclusive, but interval is inclusive */  	end -= 1; -	amdgpu_mn_read_lock(rmn); +	amdgpu_mn_read_lock(amn); -	it = interval_tree_iter_first(&rmn->objects, start, end); +	it = interval_tree_iter_first(&amn->objects, start, end);  	while (it) {  		struct amdgpu_mn_node *node; @@ -225,7 +267,7 @@ static void amdgpu_mn_invalidate_range_start_gfx(struct mmu_notifier *mn,   * amdgpu_mn_invalidate_range_start_hsa - callback to notify about mm change   *   * @mn: our notifier - * @mn: the mm this callback is about + * @mm: the mm this callback is about   * @start: start of updated range   * @end: end of updated range   * @@ -238,15 +280,15 @@ static void amdgpu_mn_invalidate_range_start_hsa(struct mmu_notifier *mn,  						 unsigned long start,  						 unsigned long end)  { -	struct amdgpu_mn *rmn = container_of(mn, struct amdgpu_mn, mn); +	struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn);  	struct interval_tree_node *it;  	/* notification is exclusive, but interval is inclusive */  	end -= 1; -	amdgpu_mn_read_lock(rmn); +	amdgpu_mn_read_lock(amn); -	it = interval_tree_iter_first(&rmn->objects, start, end); +	it = interval_tree_iter_first(&amn->objects, start, end);  	while (it) {  		struct amdgpu_mn_node *node;  		struct amdgpu_bo *bo; @@ -268,7 +310,7 @@ static void amdgpu_mn_invalidate_range_start_hsa(struct mmu_notifier *mn,   * amdgpu_mn_invalidate_range_end - callback to notify about mm change   *   * @mn: our notifier - * @mn: the mm this callback is about + * @mm: the mm this callback is about   * @start: start of updated range   * @end: end of updated range   * @@ -279,9 +321,9 @@ static void amdgpu_mn_invalidate_range_end(struct mmu_notifier *mn,  					   unsigned long start,  					   unsigned long end)  { -	struct amdgpu_mn *rmn = container_of(mn, struct amdgpu_mn, mn); +	struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn); -	amdgpu_mn_read_unlock(rmn); +	amdgpu_mn_read_unlock(amn);  }  static const struct mmu_notifier_ops amdgpu_mn_ops[] = { @@ -315,7 +357,7 @@ struct amdgpu_mn *amdgpu_mn_get(struct amdgpu_device *adev,  				enum amdgpu_mn_type type)  {  	struct mm_struct *mm = current->mm; -	struct amdgpu_mn *rmn; +	struct amdgpu_mn *amn;  	unsigned long key = AMDGPU_MN_KEY(mm, type);  	int r; @@ -325,41 +367,41 @@ struct amdgpu_mn *amdgpu_mn_get(struct amdgpu_device *adev,  		return ERR_PTR(-EINTR);  	} -	hash_for_each_possible(adev->mn_hash, rmn, node, key) -		if (AMDGPU_MN_KEY(rmn->mm, rmn->type) == key) +	hash_for_each_possible(adev->mn_hash, amn, node, key) +		if (AMDGPU_MN_KEY(amn->mm, amn->type) == key)  			goto release_locks; -	rmn = kzalloc(sizeof(*rmn), GFP_KERNEL); -	if (!rmn) { -		rmn = ERR_PTR(-ENOMEM); +	amn = kzalloc(sizeof(*amn), GFP_KERNEL); +	if (!amn) { +		amn = ERR_PTR(-ENOMEM);  		goto release_locks;  	} -	rmn->adev = adev; -	rmn->mm = mm; -	init_rwsem(&rmn->lock); -	rmn->type = type; -	rmn->mn.ops = &amdgpu_mn_ops[type]; -	rmn->objects = RB_ROOT_CACHED; -	mutex_init(&rmn->read_lock); -	atomic_set(&rmn->recursion, 0); +	amn->adev = adev; +	amn->mm = mm; +	init_rwsem(&amn->lock); +	amn->type = type; +	amn->mn.ops = &amdgpu_mn_ops[type]; +	amn->objects = RB_ROOT_CACHED; +	mutex_init(&amn->read_lock); +	atomic_set(&amn->recursion, 0); -	r = __mmu_notifier_register(&rmn->mn, mm); +	r = __mmu_notifier_register(&amn->mn, mm);  	if (r) -		goto free_rmn; +		goto free_amn; -	hash_add(adev->mn_hash, &rmn->node, AMDGPU_MN_KEY(mm, type)); +	hash_add(adev->mn_hash, &amn->node, AMDGPU_MN_KEY(mm, type));  release_locks:  	up_write(&mm->mmap_sem);  	mutex_unlock(&adev->mn_lock); -	return rmn; +	return amn; -free_rmn: +free_amn:  	up_write(&mm->mmap_sem);  	mutex_unlock(&adev->mn_lock); -	kfree(rmn); +	kfree(amn);  	return ERR_PTR(r);  } @@ -379,14 +421,14 @@ int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr)  	struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);  	enum amdgpu_mn_type type =  		bo->kfd_bo ? AMDGPU_MN_TYPE_HSA : AMDGPU_MN_TYPE_GFX; -	struct amdgpu_mn *rmn; +	struct amdgpu_mn *amn;  	struct amdgpu_mn_node *node = NULL, *new_node;  	struct list_head bos;  	struct interval_tree_node *it; -	rmn = amdgpu_mn_get(adev, type); -	if (IS_ERR(rmn)) -		return PTR_ERR(rmn); +	amn = amdgpu_mn_get(adev, type); +	if (IS_ERR(amn)) +		return PTR_ERR(amn);  	new_node = kmalloc(sizeof(*new_node), GFP_KERNEL);  	if (!new_node) @@ -394,12 +436,12 @@ int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr)  	INIT_LIST_HEAD(&bos); -	down_write(&rmn->lock); +	down_write(&amn->lock); -	while ((it = interval_tree_iter_first(&rmn->objects, addr, end))) { +	while ((it = interval_tree_iter_first(&amn->objects, addr, end))) {  		kfree(node);  		node = container_of(it, struct amdgpu_mn_node, it); -		interval_tree_remove(&node->it, &rmn->objects); +		interval_tree_remove(&node->it, &amn->objects);  		addr = min(it->start, addr);  		end = max(it->last, end);  		list_splice(&node->bos, &bos); @@ -410,7 +452,7 @@ int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr)  	else  		kfree(new_node); -	bo->mn = rmn; +	bo->mn = amn;  	node->it.start = addr;  	node->it.last = end; @@ -418,9 +460,9 @@ int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr)  	list_splice(&bos, &node->bos);  	list_add(&bo->mn_list, &node->bos); -	interval_tree_insert(&node->it, &rmn->objects); +	interval_tree_insert(&node->it, &amn->objects); -	up_write(&rmn->lock); +	up_write(&amn->lock);  	return 0;  } @@ -435,18 +477,18 @@ int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr)  void amdgpu_mn_unregister(struct amdgpu_bo *bo)  {  	struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); -	struct amdgpu_mn *rmn; +	struct amdgpu_mn *amn;  	struct list_head *head;  	mutex_lock(&adev->mn_lock); -	rmn = bo->mn; -	if (rmn == NULL) { +	amn = bo->mn; +	if (amn == NULL) {  		mutex_unlock(&adev->mn_lock);  		return;  	} -	down_write(&rmn->lock); +	down_write(&amn->lock);  	/* save the next list entry for later */  	head = bo->mn_list.next; @@ -456,12 +498,13 @@ void amdgpu_mn_unregister(struct amdgpu_bo *bo)  	if (list_empty(head)) {  		struct amdgpu_mn_node *node; +  		node = container_of(head, struct amdgpu_mn_node, bos); -		interval_tree_remove(&node->it, &rmn->objects); +		interval_tree_remove(&node->it, &amn->objects);  		kfree(node);  	} -	up_write(&rmn->lock); +	up_write(&amn->lock);  	mutex_unlock(&adev->mn_lock);  } |