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