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