1 | /* |
2 | * Copyright 2015 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 <deathsimple@vodafone.de> |
29 | */ |
30 | |
31 | #include <drm/drmP.h> |
32 | #include "amdgpu.h" |
33 | #include "amdgpu_trace.h" |
34 | |
35 | #define AMDGPU_BO_LIST_MAX_PRIORITY 32u |
36 | #define AMDGPU_BO_LIST_NUM_BUCKETS (AMDGPU_BO_LIST_MAX_PRIORITY + 1) |
37 | |
38 | static void amdgpu_bo_list_free_rcu(struct rcu_head *rcu) |
39 | { |
40 | struct amdgpu_bo_list *list = container_of(rcu, struct amdgpu_bo_list, |
41 | rhead); |
42 | |
43 | kvfree(list); |
44 | } |
45 | |
46 | static void amdgpu_bo_list_free(struct kref *ref) |
47 | { |
48 | struct amdgpu_bo_list *list = container_of(ref, struct amdgpu_bo_list, |
49 | refcount); |
50 | struct amdgpu_bo_list_entry *e; |
51 | |
52 | amdgpu_bo_list_for_each_entry(e, list) { |
53 | struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo); |
54 | |
55 | amdgpu_bo_unref(&bo); |
56 | } |
57 | |
58 | call_rcu(&list->rhead, amdgpu_bo_list_free_rcu); |
59 | } |
60 | |
61 | int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp, |
62 | struct drm_amdgpu_bo_list_entry *info, |
63 | unsigned num_entries, struct amdgpu_bo_list **result) |
64 | { |
65 | unsigned last_entry = 0, first_userptr = num_entries; |
66 | struct amdgpu_bo_list_entry *array; |
67 | struct amdgpu_bo_list *list; |
68 | uint64_t total_size = 0; |
69 | size_t size; |
70 | unsigned i; |
71 | int r; |
72 | |
73 | if (num_entries > (SIZE_MAX - sizeof(struct amdgpu_bo_list)) |
74 | / sizeof(struct amdgpu_bo_list_entry)) |
75 | return -EINVAL; |
76 | |
77 | size = sizeof(struct amdgpu_bo_list); |
78 | size += num_entries * sizeof(struct amdgpu_bo_list_entry); |
79 | list = kvmalloc(size, GFP_KERNEL); |
80 | if (!list) |
81 | return -ENOMEM; |
82 | |
83 | kref_init(&list->refcount); |
84 | list->gds_obj = adev->gds.gds_gfx_bo; |
85 | list->gws_obj = adev->gds.gws_gfx_bo; |
86 | list->oa_obj = adev->gds.oa_gfx_bo; |
87 | |
88 | array = amdgpu_bo_list_array_entry(list, 0); |
89 | memset(array, 0, num_entries * sizeof(struct amdgpu_bo_list_entry)); |
90 | |
91 | for (i = 0; i < num_entries; ++i) { |
92 | struct amdgpu_bo_list_entry *entry; |
93 | struct drm_gem_object *gobj; |
94 | struct amdgpu_bo *bo; |
95 | struct mm_struct *usermm; |
96 | |
97 | gobj = drm_gem_object_lookup(filp, info[i].bo_handle); |
98 | if (!gobj) { |
99 | r = -ENOENT; |
100 | goto error_free; |
101 | } |
102 | |
103 | bo = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj)); |
104 | drm_gem_object_put_unlocked(gobj); |
105 | |
106 | usermm = amdgpu_ttm_tt_get_usermm(bo->tbo.ttm); |
107 | if (usermm) { |
108 | if (usermm != current->mm) { |
109 | amdgpu_bo_unref(&bo); |
110 | r = -EPERM; |
111 | goto error_free; |
112 | } |
113 | entry = &array[--first_userptr]; |
114 | } else { |
115 | entry = &array[last_entry++]; |
116 | } |
117 | |
118 | entry->priority = min(info[i].bo_priority, |
119 | AMDGPU_BO_LIST_MAX_PRIORITY); |
120 | entry->tv.bo = &bo->tbo; |
121 | |
122 | if (bo->preferred_domains == AMDGPU_GEM_DOMAIN_GDS) |
123 | list->gds_obj = bo; |
124 | if (bo->preferred_domains == AMDGPU_GEM_DOMAIN_GWS) |
125 | list->gws_obj = bo; |
126 | if (bo->preferred_domains == AMDGPU_GEM_DOMAIN_OA) |
127 | list->oa_obj = bo; |
128 | |
129 | total_size += amdgpu_bo_size(bo); |
130 | trace_amdgpu_bo_list_set(list, bo); |
131 | } |
132 | |
133 | list->first_userptr = first_userptr; |
134 | list->num_entries = num_entries; |
135 | |
136 | trace_amdgpu_cs_bo_status(list->num_entries, total_size); |
137 | |
138 | *result = list; |
139 | return 0; |
140 | |
141 | error_free: |
142 | while (i--) { |
143 | struct amdgpu_bo *bo = ttm_to_amdgpu_bo(array[i].tv.bo); |
144 | |
145 | amdgpu_bo_unref(&bo); |
146 | } |
147 | kvfree(list); |
148 | return r; |
149 | |
150 | } |
151 | |
152 | static void amdgpu_bo_list_destroy(struct amdgpu_fpriv *fpriv, int id) |
153 | { |
154 | struct amdgpu_bo_list *list; |
155 | |
156 | mutex_lock(&fpriv->bo_list_lock); |
157 | list = idr_remove(&fpriv->bo_list_handles, id); |
158 | mutex_unlock(&fpriv->bo_list_lock); |
159 | if (list) |
160 | kref_put(&list->refcount, amdgpu_bo_list_free); |
161 | } |
162 | |
163 | int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id, |
164 | struct amdgpu_bo_list **result) |
165 | { |
166 | rcu_read_lock(); |
167 | *result = idr_find(&fpriv->bo_list_handles, id); |
168 | |
169 | if (*result && kref_get_unless_zero(&(*result)->refcount)) { |
170 | rcu_read_unlock(); |
171 | return 0; |
172 | } |
173 | |
174 | rcu_read_unlock(); |
175 | return -ENOENT; |
176 | } |
177 | |
178 | void amdgpu_bo_list_get_list(struct amdgpu_bo_list *list, |
179 | struct list_head *validated) |
180 | { |
181 | /* This is based on the bucket sort with O(n) time complexity. |
182 | * An item with priority "i" is added to bucket[i]. The lists are then |
183 | * concatenated in descending order. |
184 | */ |
185 | struct list_head bucket[AMDGPU_BO_LIST_NUM_BUCKETS]; |
186 | struct amdgpu_bo_list_entry *e; |
187 | unsigned i; |
188 | |
189 | for (i = 0; i < AMDGPU_BO_LIST_NUM_BUCKETS; i++) |
190 | INIT_LIST_HEAD(&bucket[i]); |
191 | |
192 | /* Since buffers which appear sooner in the relocation list are |
193 | * likely to be used more often than buffers which appear later |
194 | * in the list, the sort mustn't change the ordering of buffers |
195 | * with the same priority, i.e. it must be stable. |
196 | */ |
197 | amdgpu_bo_list_for_each_entry(e, list) { |
198 | struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo); |
199 | unsigned priority = e->priority; |
200 | |
201 | if (!bo->parent) |
202 | list_add_tail(&e->tv.head, &bucket[priority]); |
203 | |
204 | e->user_pages = NULL; |
205 | } |
206 | |
207 | /* Connect the sorted buckets in the output list. */ |
208 | for (i = 0; i < AMDGPU_BO_LIST_NUM_BUCKETS; i++) |
209 | list_splice(&bucket[i], validated); |
210 | } |
211 | |
212 | void amdgpu_bo_list_put(struct amdgpu_bo_list *list) |
213 | { |
214 | kref_put(&list->refcount, amdgpu_bo_list_free); |
215 | } |
216 | |
217 | int amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in, |
218 | struct drm_amdgpu_bo_list_entry **info_param) |
219 | { |
220 | const void __user *uptr = u64_to_user_ptr(in->bo_info_ptr); |
221 | const uint32_t info_size = sizeof(struct drm_amdgpu_bo_list_entry); |
222 | struct drm_amdgpu_bo_list_entry *info; |
223 | int r; |
224 | |
225 | info = kvmalloc_array(in->bo_number, info_size, GFP_KERNEL); |
226 | if (!info) |
227 | return -ENOMEM; |
228 | |
229 | /* copy the handle array from userspace to a kernel buffer */ |
230 | r = -EFAULT; |
231 | if (likely(info_size == in->bo_info_size)) { |
232 | unsigned long bytes = in->bo_number * |
233 | in->bo_info_size; |
234 | |
235 | if (copy_from_user(info, uptr, bytes)) |
236 | goto error_free; |
237 | |
238 | } else { |
239 | unsigned long bytes = min(in->bo_info_size, info_size); |
240 | unsigned i; |
241 | |
242 | memset(info, 0, in->bo_number * info_size); |
243 | for (i = 0; i < in->bo_number; ++i) { |
244 | if (copy_from_user(&info[i], uptr, bytes)) |
245 | goto error_free; |
246 | |
247 | uptr += in->bo_info_size; |
248 | } |
249 | } |
250 | |
251 | *info_param = info; |
252 | return 0; |
253 | |
254 | error_free: |
255 | kvfree(info); |
256 | return r; |
257 | } |
258 | |
259 | int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, |
260 | struct drm_file *filp) |
261 | { |
262 | struct amdgpu_device *adev = dev->dev_private; |
263 | struct amdgpu_fpriv *fpriv = filp->driver_priv; |
264 | union drm_amdgpu_bo_list *args = data; |
265 | uint32_t handle = args->in.list_handle; |
266 | struct drm_amdgpu_bo_list_entry *info = NULL; |
267 | struct amdgpu_bo_list *list, *old; |
268 | int r; |
269 | |
270 | r = amdgpu_bo_create_list_entry_array(&args->in, &info); |
271 | if (r) |
272 | goto error_free; |
273 | |
274 | switch (args->in.operation) { |
275 | case AMDGPU_BO_LIST_OP_CREATE: |
276 | r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number, |
277 | &list); |
278 | if (r) |
279 | goto error_free; |
280 | |
281 | mutex_lock(&fpriv->bo_list_lock); |
282 | r = idr_alloc(&fpriv->bo_list_handles, list, 1, 0, GFP_KERNEL); |
283 | mutex_unlock(&fpriv->bo_list_lock); |
284 | if (r < 0) { |
285 | amdgpu_bo_list_put(list); |
286 | return r; |
287 | } |
288 | |
289 | handle = r; |
290 | break; |
291 | |
292 | case AMDGPU_BO_LIST_OP_DESTROY: |
293 | amdgpu_bo_list_destroy(fpriv, handle); |
294 | handle = 0; |
295 | break; |
296 | |
297 | case AMDGPU_BO_LIST_OP_UPDATE: |
298 | r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number, |
299 | &list); |
300 | if (r) |
301 | goto error_free; |
302 | |
303 | mutex_lock(&fpriv->bo_list_lock); |
304 | old = idr_replace(&fpriv->bo_list_handles, list, handle); |
305 | mutex_unlock(&fpriv->bo_list_lock); |
306 | |
307 | if (IS_ERR(old)) { |
308 | amdgpu_bo_list_put(list); |
309 | r = PTR_ERR(old); |
310 | goto error_free; |
311 | } |
312 | |
313 | amdgpu_bo_list_put(old); |
314 | break; |
315 | |
316 | default: |
317 | r = -EINVAL; |
318 | goto error_free; |
319 | } |
320 | |
321 | memset(args, 0, sizeof(*args)); |
322 | args->out.list_handle = handle; |
323 | kvfree(info); |
324 | |
325 | return 0; |
326 | |
327 | error_free: |
328 | if (info) |
329 | kvfree(info); |
330 | return r; |
331 | } |
332 | |