xref: /dflybsd-src/sys/dev/drm/amd/amdgpu/amdgpu_mn.c (revision b843c749addef9340ee7d4e250b09fdd492602a1)
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