1 | /* |
2 | * Copyright 2014 Advanced Micro Devices, Inc. |
3 | * All Rights Reserved. |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining a |
6 | * copy of this software and associated documentation files (the |
7 | * "Software"), to deal in the Software without restriction, including |
8 | * without limitation the rights to use, copy, modify, merge, publish, |
9 | * distribute, sub license, and/or sell copies of the Software, and to |
10 | * permit persons to whom the Software is furnished to do so, subject to |
11 | * the following conditions: |
12 | * |
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
16 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, |
17 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
19 | * USE OR OTHER DEALINGS IN THE SOFTWARE. |
20 | * |
21 | * The above copyright notice and this permission notice (including the |
22 | * next paragraph) shall be included in all copies or substantial portions |
23 | * of the Software. |
24 | * |
25 | */ |
26 | /* |
27 | * Authors: |
28 | * Christian König <christian.koenig@amd.com> |
29 | */ |
30 | |
31 | #include <linux/firmware.h> |
32 | #include <linux/module.h> |
33 | #include <linux/mmu_notifier.h> |
34 | |
35 | #include <drm/drm.h> |
36 | |
37 | #include "radeon.h" |
38 | |
39 | /** |
40 | * radeon_mn_invalidate - callback to notify about mm change |
41 | * |
42 | * @mn: our notifier |
43 | * @range: the VMA under invalidation |
44 | * @cur_seq: Value to pass to mmu_interval_set_seq() |
45 | * |
46 | * We block for all BOs between start and end to be idle and |
47 | * unmap them by move them into system domain again. |
48 | */ |
49 | static bool radeon_mn_invalidate(struct mmu_interval_notifier *mn, |
50 | const struct mmu_notifier_range *range, |
51 | unsigned long cur_seq) |
52 | { |
53 | struct radeon_bo *bo = container_of(mn, struct radeon_bo, notifier); |
54 | struct ttm_operation_ctx ctx = { false, false }; |
55 | long r; |
56 | |
57 | if (!bo->tbo.ttm || !radeon_ttm_tt_is_bound(bdev: bo->tbo.bdev, ttm: bo->tbo.ttm)) |
58 | return true; |
59 | |
60 | if (!mmu_notifier_range_blockable(range)) |
61 | return false; |
62 | |
63 | r = radeon_bo_reserve(bo, no_intr: true); |
64 | if (r) { |
65 | DRM_ERROR("(%ld) failed to reserve user bo\n" , r); |
66 | return true; |
67 | } |
68 | |
69 | r = dma_resv_wait_timeout(obj: bo->tbo.base.resv, usage: DMA_RESV_USAGE_BOOKKEEP, |
70 | intr: false, MAX_SCHEDULE_TIMEOUT); |
71 | if (r <= 0) |
72 | DRM_ERROR("(%ld) failed to wait for user bo\n" , r); |
73 | |
74 | radeon_ttm_placement_from_domain(rbo: bo, RADEON_GEM_DOMAIN_CPU); |
75 | r = ttm_bo_validate(bo: &bo->tbo, placement: &bo->placement, ctx: &ctx); |
76 | if (r) |
77 | DRM_ERROR("(%ld) failed to validate user bo\n" , r); |
78 | |
79 | radeon_bo_unreserve(bo); |
80 | return true; |
81 | } |
82 | |
83 | static const struct mmu_interval_notifier_ops radeon_mn_ops = { |
84 | .invalidate = radeon_mn_invalidate, |
85 | }; |
86 | |
87 | /** |
88 | * radeon_mn_register - register a BO for notifier updates |
89 | * |
90 | * @bo: radeon buffer object |
91 | * @addr: userptr addr we should monitor |
92 | * |
93 | * Registers an MMU notifier for the given BO at the specified address. |
94 | * Returns 0 on success, -ERRNO if anything goes wrong. |
95 | */ |
96 | int radeon_mn_register(struct radeon_bo *bo, unsigned long addr) |
97 | { |
98 | int ret; |
99 | |
100 | ret = mmu_interval_notifier_insert(interval_sub: &bo->notifier, current->mm, start: addr, |
101 | length: radeon_bo_size(bo), ops: &radeon_mn_ops); |
102 | if (ret) |
103 | return ret; |
104 | |
105 | /* |
106 | * FIXME: radeon appears to allow get_user_pages to run during |
107 | * invalidate_range_start/end, which is not a safe way to read the |
108 | * PTEs. It should use the mmu_interval_read_begin() scheme around the |
109 | * get_user_pages to ensure that the PTEs are read properly |
110 | */ |
111 | mmu_interval_read_begin(interval_sub: &bo->notifier); |
112 | return 0; |
113 | } |
114 | |
115 | /** |
116 | * radeon_mn_unregister - unregister a BO for notifier updates |
117 | * |
118 | * @bo: radeon buffer object |
119 | * |
120 | * Remove any registration of MMU notifier updates from the buffer object. |
121 | */ |
122 | void radeon_mn_unregister(struct radeon_bo *bo) |
123 | { |
124 | if (!bo->notifier.mm) |
125 | return; |
126 | mmu_interval_notifier_remove(interval_sub: &bo->notifier); |
127 | bo->notifier.mm = NULL; |
128 | } |
129 | |