1 | /** |
2 | * \file drm_context.c |
3 | * IOCTLs for generic contexts |
4 | * |
5 | * \author Rickard E. (Rik) Faith <faith@valinux.com> |
6 | * \author Gareth Hughes <gareth@valinux.com> |
7 | */ |
8 | |
9 | /* |
10 | * Created: Fri Nov 24 18:31:37 2000 by gareth@valinux.com |
11 | * |
12 | * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. |
13 | * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. |
14 | * All Rights Reserved. |
15 | * |
16 | * Permission is hereby granted, free of charge, to any person obtaining a |
17 | * copy of this software and associated documentation files (the "Software"), |
18 | * to deal in the Software without restriction, including without limitation |
19 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
20 | * and/or sell copies of the Software, and to permit persons to whom the |
21 | * Software is furnished to do so, subject to the following conditions: |
22 | * |
23 | * The above copyright notice and this permission notice (including the next |
24 | * paragraph) shall be included in all copies or substantial portions of the |
25 | * Software. |
26 | * |
27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
28 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
29 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
30 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
31 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
32 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
33 | * OTHER DEALINGS IN THE SOFTWARE. |
34 | */ |
35 | |
36 | /* |
37 | * ChangeLog: |
38 | * 2001-11-16 Torsten Duwe <duwe@caldera.de> |
39 | * added context constructor/destructor hooks, |
40 | * needed by SiS driver's memory management. |
41 | */ |
42 | |
43 | #include <linux/err.h> |
44 | |
45 | #include <drm/drmP.h> |
46 | |
47 | /******************************************************************/ |
48 | /** \name Context bitmap support */ |
49 | /*@{*/ |
50 | |
51 | /** |
52 | * Free a handle from the context bitmap. |
53 | * |
54 | * \param dev DRM device. |
55 | * \param ctx_handle context handle. |
56 | * |
57 | * Clears the bit specified by \p ctx_handle in drm_device::ctx_bitmap and the entry |
58 | * in drm_device::ctx_idr, while holding the drm_device::struct_mutex |
59 | * lock. |
60 | */ |
61 | void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle) |
62 | { |
63 | mutex_lock(&dev->struct_mutex); |
64 | idr_remove(&dev->ctx_idr, ctx_handle); |
65 | mutex_unlock(&dev->struct_mutex); |
66 | } |
67 | |
68 | /** |
69 | * Context bitmap allocation. |
70 | * |
71 | * \param dev DRM device. |
72 | * \return (non-negative) context handle on success or a negative number on failure. |
73 | * |
74 | * Allocate a new idr from drm_device::ctx_idr while holding the |
75 | * drm_device::struct_mutex lock. |
76 | */ |
77 | static int drm_ctxbitmap_next(struct drm_device * dev) |
78 | { |
79 | int ret; |
80 | |
81 | mutex_lock(&dev->struct_mutex); |
82 | ret = idr_alloc(&dev->ctx_idr, NULL, DRM_RESERVED_CONTEXTS, 0, |
83 | GFP_KERNEL); |
84 | mutex_unlock(&dev->struct_mutex); |
85 | return ret; |
86 | } |
87 | |
88 | /** |
89 | * Context bitmap initialization. |
90 | * |
91 | * \param dev DRM device. |
92 | * |
93 | * Initialise the drm_device::ctx_idr |
94 | */ |
95 | int drm_ctxbitmap_init(struct drm_device * dev) |
96 | { |
97 | idr_init(&dev->ctx_idr); |
98 | return 0; |
99 | } |
100 | |
101 | /** |
102 | * Context bitmap cleanup. |
103 | * |
104 | * \param dev DRM device. |
105 | * |
106 | * Free all idr members using drm_ctx_sarea_free helper function |
107 | * while holding the drm_device::struct_mutex lock. |
108 | */ |
109 | void drm_ctxbitmap_cleanup(struct drm_device * dev) |
110 | { |
111 | mutex_lock(&dev->struct_mutex); |
112 | idr_destroy(&dev->ctx_idr); |
113 | mutex_unlock(&dev->struct_mutex); |
114 | } |
115 | |
116 | /*@}*/ |
117 | |
118 | /******************************************************************/ |
119 | /** \name Per Context SAREA Support */ |
120 | /*@{*/ |
121 | |
122 | /** |
123 | * Get per-context SAREA. |
124 | * |
125 | * \param inode device inode. |
126 | * \param file_priv DRM file private. |
127 | * \param cmd command. |
128 | * \param arg user argument pointing to a drm_ctx_priv_map structure. |
129 | * \return zero on success or a negative number on failure. |
130 | * |
131 | * Gets the map from drm_device::ctx_idr with the handle specified and |
132 | * returns its handle. |
133 | */ |
134 | int drm_getsareactx(struct drm_device *dev, void *data, |
135 | struct drm_file *file_priv) |
136 | { |
137 | struct drm_ctx_priv_map *request = data; |
138 | struct drm_local_map *map; |
139 | struct drm_map_list *_entry; |
140 | |
141 | mutex_lock(&dev->struct_mutex); |
142 | |
143 | map = idr_find(&dev->ctx_idr, request->ctx_id); |
144 | if (!map) { |
145 | mutex_unlock(&dev->struct_mutex); |
146 | return -EINVAL; |
147 | } |
148 | |
149 | request->handle = NULL; |
150 | list_for_each_entry(_entry, &dev->maplist, head) { |
151 | if (_entry->map == map) { |
152 | request->handle = |
153 | (void *)(unsigned long)_entry->user_token; |
154 | break; |
155 | } |
156 | } |
157 | |
158 | mutex_unlock(&dev->struct_mutex); |
159 | |
160 | if (request->handle == NULL) |
161 | return -EINVAL; |
162 | |
163 | return 0; |
164 | } |
165 | |
166 | /** |
167 | * Set per-context SAREA. |
168 | * |
169 | * \param inode device inode. |
170 | * \param file_priv DRM file private. |
171 | * \param cmd command. |
172 | * \param arg user argument pointing to a drm_ctx_priv_map structure. |
173 | * \return zero on success or a negative number on failure. |
174 | * |
175 | * Searches the mapping specified in \p arg and update the entry in |
176 | * drm_device::ctx_idr with it. |
177 | */ |
178 | int drm_setsareactx(struct drm_device *dev, void *data, |
179 | struct drm_file *file_priv) |
180 | { |
181 | struct drm_ctx_priv_map *request = data; |
182 | struct drm_local_map *map = NULL; |
183 | struct drm_map_list *r_list = NULL; |
184 | |
185 | mutex_lock(&dev->struct_mutex); |
186 | list_for_each_entry(r_list, &dev->maplist, head) { |
187 | if (r_list->map |
188 | && r_list->user_token == (unsigned long) request->handle) |
189 | goto found; |
190 | } |
191 | bad: |
192 | mutex_unlock(&dev->struct_mutex); |
193 | return -EINVAL; |
194 | |
195 | found: |
196 | map = r_list->map; |
197 | if (!map) |
198 | goto bad; |
199 | |
200 | if (IS_ERR(idr_replace(&dev->ctx_idr, map, request->ctx_id))) |
201 | goto bad; |
202 | |
203 | mutex_unlock(&dev->struct_mutex); |
204 | |
205 | return 0; |
206 | } |
207 | |
208 | /*@}*/ |
209 | |
210 | /******************************************************************/ |
211 | /** \name The actual DRM context handling routines */ |
212 | /*@{*/ |
213 | |
214 | /** |
215 | * Switch context. |
216 | * |
217 | * \param dev DRM device. |
218 | * \param old old context handle. |
219 | * \param new new context handle. |
220 | * \return zero on success or a negative number on failure. |
221 | * |
222 | * Attempt to set drm_device::context_flag. |
223 | */ |
224 | static int drm_context_switch(struct drm_device * dev, int old, int new) |
225 | { |
226 | if (test_and_set_bit(0, &dev->context_flag)) { |
227 | DRM_ERROR("Reentering -- FIXME\n" ); |
228 | return -EBUSY; |
229 | } |
230 | |
231 | DRM_DEBUG("Context switch from %d to %d\n" , old, new); |
232 | |
233 | if (new == dev->last_context) { |
234 | clear_bit(0, &dev->context_flag); |
235 | return 0; |
236 | } |
237 | |
238 | return 0; |
239 | } |
240 | |
241 | /** |
242 | * Complete context switch. |
243 | * |
244 | * \param dev DRM device. |
245 | * \param new new context handle. |
246 | * \return zero on success or a negative number on failure. |
247 | * |
248 | * Updates drm_device::last_context and drm_device::last_switch. Verifies the |
249 | * hardware lock is held, clears the drm_device::context_flag and wakes up |
250 | * drm_device::context_wait. |
251 | */ |
252 | static int drm_context_switch_complete(struct drm_device *dev, |
253 | struct drm_file *file_priv, int new) |
254 | { |
255 | dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ |
256 | |
257 | spin_lock(&file_priv->master->lock.spinlock); |
258 | if (file_priv->master->lock.hw_lock == NULL || |
259 | !_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock)) { |
260 | DRM_ERROR("Lock isn't held after context switch\n" ); |
261 | } |
262 | spin_unlock(&file_priv->master->lock.spinlock); |
263 | |
264 | /* If a context switch is ever initiated |
265 | when the kernel holds the lock, release |
266 | that lock here. */ |
267 | clear_bit(0, &dev->context_flag); |
268 | |
269 | return 0; |
270 | } |
271 | |
272 | /** |
273 | * Reserve contexts. |
274 | * |
275 | * \param inode device inode. |
276 | * \param file_priv DRM file private. |
277 | * \param cmd command. |
278 | * \param arg user argument pointing to a drm_ctx_res structure. |
279 | * \return zero on success or a negative number on failure. |
280 | */ |
281 | int drm_resctx(struct drm_device *dev, void *data, |
282 | struct drm_file *file_priv) |
283 | { |
284 | struct drm_ctx_res *res = data; |
285 | struct drm_ctx ctx; |
286 | int i; |
287 | |
288 | if (res->count >= DRM_RESERVED_CONTEXTS) { |
289 | memset(&ctx, 0, sizeof(ctx)); |
290 | for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { |
291 | ctx.handle = i; |
292 | if (copy_to_user(&res->contexts[i], &ctx, sizeof(ctx))) |
293 | return -EFAULT; |
294 | } |
295 | } |
296 | res->count = DRM_RESERVED_CONTEXTS; |
297 | |
298 | return 0; |
299 | } |
300 | |
301 | /** |
302 | * Add context. |
303 | * |
304 | * \param inode device inode. |
305 | * \param file_priv DRM file private. |
306 | * \param cmd command. |
307 | * \param arg user argument pointing to a drm_ctx structure. |
308 | * \return zero on success or a negative number on failure. |
309 | * |
310 | * Get a new handle for the context and copy to userspace. |
311 | */ |
312 | int drm_addctx(struct drm_device *dev, void *data, |
313 | struct drm_file *file_priv) |
314 | { |
315 | struct drm_ctx_list *ctx_entry; |
316 | struct drm_ctx *ctx = data; |
317 | |
318 | ctx->handle = drm_ctxbitmap_next(dev); |
319 | if (ctx->handle == DRM_KERNEL_CONTEXT) { |
320 | /* Skip kernel's context and get a new one. */ |
321 | ctx->handle = drm_ctxbitmap_next(dev); |
322 | } |
323 | DRM_DEBUG("%d\n" , ctx->handle); |
324 | if (ctx->handle == -1) { |
325 | DRM_DEBUG("Not enough free contexts.\n" ); |
326 | /* Should this return -EBUSY instead? */ |
327 | return -ENOMEM; |
328 | } |
329 | |
330 | ctx_entry = kmalloc(sizeof(*ctx_entry), GFP_KERNEL); |
331 | if (!ctx_entry) { |
332 | DRM_DEBUG("out of memory\n" ); |
333 | return -ENOMEM; |
334 | } |
335 | |
336 | INIT_LIST_HEAD(&ctx_entry->head); |
337 | ctx_entry->handle = ctx->handle; |
338 | ctx_entry->tag = file_priv; |
339 | |
340 | mutex_lock(&dev->ctxlist_mutex); |
341 | list_add(&ctx_entry->head, &dev->ctxlist); |
342 | mutex_unlock(&dev->ctxlist_mutex); |
343 | |
344 | return 0; |
345 | } |
346 | |
347 | /** |
348 | * Get context. |
349 | * |
350 | * \param inode device inode. |
351 | * \param file_priv DRM file private. |
352 | * \param cmd command. |
353 | * \param arg user argument pointing to a drm_ctx structure. |
354 | * \return zero on success or a negative number on failure. |
355 | */ |
356 | int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv) |
357 | { |
358 | struct drm_ctx *ctx = data; |
359 | |
360 | /* This is 0, because we don't handle any context flags */ |
361 | ctx->flags = 0; |
362 | |
363 | return 0; |
364 | } |
365 | |
366 | /** |
367 | * Switch context. |
368 | * |
369 | * \param inode device inode. |
370 | * \param file_priv DRM file private. |
371 | * \param cmd command. |
372 | * \param arg user argument pointing to a drm_ctx structure. |
373 | * \return zero on success or a negative number on failure. |
374 | * |
375 | * Calls context_switch(). |
376 | */ |
377 | int drm_switchctx(struct drm_device *dev, void *data, |
378 | struct drm_file *file_priv) |
379 | { |
380 | struct drm_ctx *ctx = data; |
381 | |
382 | DRM_DEBUG("%d\n" , ctx->handle); |
383 | return drm_context_switch(dev, dev->last_context, ctx->handle); |
384 | } |
385 | |
386 | /** |
387 | * New context. |
388 | * |
389 | * \param inode device inode. |
390 | * \param file_priv DRM file private. |
391 | * \param cmd command. |
392 | * \param arg user argument pointing to a drm_ctx structure. |
393 | * \return zero on success or a negative number on failure. |
394 | * |
395 | * Calls context_switch_complete(). |
396 | */ |
397 | int drm_newctx(struct drm_device *dev, void *data, |
398 | struct drm_file *file_priv) |
399 | { |
400 | struct drm_ctx *ctx = data; |
401 | |
402 | DRM_DEBUG("%d\n" , ctx->handle); |
403 | drm_context_switch_complete(dev, file_priv, ctx->handle); |
404 | |
405 | return 0; |
406 | } |
407 | |
408 | /** |
409 | * Remove context. |
410 | * |
411 | * \param inode device inode. |
412 | * \param file_priv DRM file private. |
413 | * \param cmd command. |
414 | * \param arg user argument pointing to a drm_ctx structure. |
415 | * \return zero on success or a negative number on failure. |
416 | * |
417 | * If not the special kernel context, calls ctxbitmap_free() to free the specified context. |
418 | */ |
419 | int drm_rmctx(struct drm_device *dev, void *data, |
420 | struct drm_file *file_priv) |
421 | { |
422 | struct drm_ctx *ctx = data; |
423 | |
424 | DRM_DEBUG("%d\n" , ctx->handle); |
425 | if (ctx->handle != DRM_KERNEL_CONTEXT) { |
426 | if (dev->driver->context_dtor) |
427 | dev->driver->context_dtor(dev, ctx->handle); |
428 | drm_ctxbitmap_free(dev, ctx->handle); |
429 | } |
430 | |
431 | mutex_lock(&dev->ctxlist_mutex); |
432 | if (!list_empty(&dev->ctxlist)) { |
433 | struct drm_ctx_list *pos, *n; |
434 | |
435 | list_for_each_entry_safe(pos, n, &dev->ctxlist, head) { |
436 | if (pos->handle == ctx->handle) { |
437 | list_del(&pos->head); |
438 | kfree(pos); |
439 | } |
440 | } |
441 | } |
442 | mutex_unlock(&dev->ctxlist_mutex); |
443 | |
444 | return 0; |
445 | } |
446 | |
447 | /*@}*/ |
448 | |