VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / drivers / char / drm / drm_context.h
1 /**
2  * \file drm_context.h 
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 "drmP.h"
44
45 #if !__HAVE_CTX_BITMAP
46 #error "__HAVE_CTX_BITMAP must be defined"
47 #endif
48
49
50 /******************************************************************/
51 /** \name Context bitmap support */
52 /*@{*/
53
54 /**
55  * Free a handle from the context bitmap.
56  *
57  * \param dev DRM device.
58  * \param ctx_handle context handle.
59  *
60  * Clears the bit specified by \p ctx_handle in drm_device::ctx_bitmap and the entry
61  * in drm_device::context_sareas, while holding the drm_device::struct_sem
62  * lock.
63  */
64 void DRM(ctxbitmap_free)( drm_device_t *dev, int ctx_handle )
65 {
66         if ( ctx_handle < 0 ) goto failed;
67         if ( !dev->ctx_bitmap ) goto failed;
68
69         if ( ctx_handle < DRM_MAX_CTXBITMAP ) {
70                 down(&dev->struct_sem);
71                 clear_bit( ctx_handle, dev->ctx_bitmap );
72                 dev->context_sareas[ctx_handle] = NULL;
73                 up(&dev->struct_sem);
74                 return;
75         }
76 failed:
77         DRM_ERROR( "Attempt to free invalid context handle: %d\n",
78                    ctx_handle );
79         return;
80 }
81
82 /** 
83  * Context bitmap allocation.
84  *
85  * \param dev DRM device.
86  * \return (non-negative) context handle on success or a negative number on failure.
87  *
88  * Find the first zero bit in drm_device::ctx_bitmap and (re)allocates
89  * drm_device::context_sareas to accommodate the new entry while holding the
90  * drm_device::struct_sem lock.
91  */
92 int DRM(ctxbitmap_next)( drm_device_t *dev )
93 {
94         int bit;
95
96         if(!dev->ctx_bitmap) return -1;
97
98         down(&dev->struct_sem);
99         bit = find_first_zero_bit( dev->ctx_bitmap, DRM_MAX_CTXBITMAP );
100         if ( bit < DRM_MAX_CTXBITMAP ) {
101                 set_bit( bit, dev->ctx_bitmap );
102                 DRM_DEBUG( "drm_ctxbitmap_next bit : %d\n", bit );
103                 if((bit+1) > dev->max_context) {
104                         dev->max_context = (bit+1);
105                         if(dev->context_sareas) {
106                                 drm_map_t **ctx_sareas;
107
108                                 ctx_sareas = DRM(realloc)(dev->context_sareas,
109                                                 (dev->max_context - 1) * 
110                                                 sizeof(*dev->context_sareas),
111                                                 dev->max_context * 
112                                                 sizeof(*dev->context_sareas),
113                                                 DRM_MEM_MAPS);
114                                 if(!ctx_sareas) {
115                                         clear_bit(bit, dev->ctx_bitmap);
116                                         up(&dev->struct_sem);
117                                         return -1;
118                                 }
119                                 dev->context_sareas = ctx_sareas;
120                                 dev->context_sareas[bit] = NULL;
121                         } else {
122                                 /* max_context == 1 at this point */
123                                 dev->context_sareas = DRM(alloc)(
124                                                 dev->max_context * 
125                                                 sizeof(*dev->context_sareas),
126                                                 DRM_MEM_MAPS);
127                                 if(!dev->context_sareas) {
128                                         clear_bit(bit, dev->ctx_bitmap);
129                                         up(&dev->struct_sem);
130                                         return -1;
131                                 }
132                                 dev->context_sareas[bit] = NULL;
133                         }
134                 }
135                 up(&dev->struct_sem);
136                 return bit;
137         }
138         up(&dev->struct_sem);
139         return -1;
140 }
141
142 /**
143  * Context bitmap initialization.
144  *
145  * \param dev DRM device.
146  *
147  * Allocates and initialize drm_device::ctx_bitmap and drm_device::context_sareas, while holding
148  * the drm_device::struct_sem lock.
149  */
150 int DRM(ctxbitmap_init)( drm_device_t *dev )
151 {
152         int i;
153         int temp;
154
155         down(&dev->struct_sem);
156         dev->ctx_bitmap = (unsigned long *) DRM(alloc)( PAGE_SIZE,
157                                                         DRM_MEM_CTXBITMAP );
158         if ( dev->ctx_bitmap == NULL ) {
159                 up(&dev->struct_sem);
160                 return -ENOMEM;
161         }
162         memset( (void *)dev->ctx_bitmap, 0, PAGE_SIZE );
163         dev->context_sareas = NULL;
164         dev->max_context = -1;
165         up(&dev->struct_sem);
166
167         for ( i = 0 ; i < DRM_RESERVED_CONTEXTS ; i++ ) {
168                 temp = DRM(ctxbitmap_next)( dev );
169                 DRM_DEBUG( "drm_ctxbitmap_init : %d\n", temp );
170         }
171
172         return 0;
173 }
174
175 /**
176  * Context bitmap cleanup.
177  *
178  * \param dev DRM device.
179  *
180  * Frees drm_device::ctx_bitmap and drm_device::context_sareas, while holding
181  * the drm_device::struct_sem lock.
182  */
183 void DRM(ctxbitmap_cleanup)( drm_device_t *dev )
184 {
185         down(&dev->struct_sem);
186         if( dev->context_sareas ) DRM(free)( dev->context_sareas,
187                                              sizeof(*dev->context_sareas) * 
188                                              dev->max_context,
189                                              DRM_MEM_MAPS );
190         DRM(free)( (void *)dev->ctx_bitmap, PAGE_SIZE, DRM_MEM_CTXBITMAP );
191         up(&dev->struct_sem);
192 }
193
194 /*@}*/
195
196 /******************************************************************/
197 /** \name Per Context SAREA Support */
198 /*@{*/
199
200 /**
201  * Get per-context SAREA.
202  * 
203  * \param inode device inode.
204  * \param filp file pointer.
205  * \param cmd command.
206  * \param arg user argument pointing to a drm_ctx_priv_map structure.
207  * \return zero on success or a negative number on failure.
208  *
209  * Gets the map from drm_device::context_sareas with the handle specified and
210  * returns its handle.
211  */
212 int DRM(getsareactx)(struct inode *inode, struct file *filp,
213                      unsigned int cmd, unsigned long arg)
214 {
215         drm_file_t      *priv   = filp->private_data;
216         drm_device_t    *dev    = priv->dev;
217         drm_ctx_priv_map_t __user *argp = (void __user *)arg;
218         drm_ctx_priv_map_t request;
219         drm_map_t *map;
220
221         if (copy_from_user(&request, argp, sizeof(request)))
222                 return -EFAULT;
223
224         down(&dev->struct_sem);
225         if (dev->max_context < 0 || request.ctx_id >= (unsigned) dev->max_context) {
226                 up(&dev->struct_sem);
227                 return -EINVAL;
228         }
229
230         map = dev->context_sareas[request.ctx_id];
231         up(&dev->struct_sem);
232
233         request.handle = map->handle;
234         if (copy_to_user(argp, &request, sizeof(request)))
235                 return -EFAULT;
236         return 0;
237 }
238
239 /**
240  * Set per-context SAREA.
241  * 
242  * \param inode device inode.
243  * \param filp file pointer.
244  * \param cmd command.
245  * \param arg user argument pointing to a drm_ctx_priv_map structure.
246  * \return zero on success or a negative number on failure.
247  *
248  * Searches the mapping specified in \p arg and update the entry in
249  * drm_device::context_sareas with it.
250  */
251 int DRM(setsareactx)(struct inode *inode, struct file *filp,
252                      unsigned int cmd, unsigned long arg)
253 {
254         drm_file_t      *priv   = filp->private_data;
255         drm_device_t    *dev    = priv->dev;
256         drm_ctx_priv_map_t request;
257         drm_map_t *map = NULL;
258         drm_map_list_t *r_list = NULL;
259         struct list_head *list;
260
261         if (copy_from_user(&request,
262                            (drm_ctx_priv_map_t __user *)arg,
263                            sizeof(request)))
264                 return -EFAULT;
265
266         down(&dev->struct_sem);
267         list_for_each(list, &dev->maplist->head) {
268                 r_list = list_entry(list, drm_map_list_t, head);
269                 if(r_list->map &&
270                    r_list->map->handle == request.handle)
271                         goto found;
272         }
273 bad:
274         up(&dev->struct_sem);
275         return -EINVAL;
276
277 found:
278         map = r_list->map;
279         if (!map) goto bad;
280         if (dev->max_context < 0)
281                 goto bad;
282         if (request.ctx_id >= (unsigned) dev->max_context)
283                 goto bad;
284         dev->context_sareas[request.ctx_id] = map;
285         up(&dev->struct_sem);
286         return 0;
287 }
288
289 /*@}*/
290
291 /******************************************************************/
292 /** \name The actual DRM context handling routines */
293 /*@{*/
294
295 /**
296  * Switch context.
297  *
298  * \param dev DRM device.
299  * \param old old context handle.
300  * \param new new context handle.
301  * \return zero on success or a negative number on failure.
302  *
303  * Attempt to set drm_device::context_flag.
304  */
305 int DRM(context_switch)( drm_device_t *dev, int old, int new )
306 {
307         if ( test_and_set_bit( 0, &dev->context_flag ) ) {
308                 DRM_ERROR( "Reentering -- FIXME\n" );
309                 return -EBUSY;
310         }
311
312
313         DRM_DEBUG( "Context switch from %d to %d\n", old, new );
314
315         if ( new == dev->last_context ) {
316                 clear_bit( 0, &dev->context_flag );
317                 return 0;
318         }
319
320         return 0;
321 }
322
323 /**
324  * Complete context switch.
325  *
326  * \param dev DRM device.
327  * \param new new context handle.
328  * \return zero on success or a negative number on failure.
329  *
330  * Updates drm_device::last_context and drm_device::last_switch. Verifies the
331  * hardware lock is held, clears the drm_device::context_flag and wakes up
332  * drm_device::context_wait.
333  */
334 int DRM(context_switch_complete)( drm_device_t *dev, int new )
335 {
336         dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
337         dev->last_switch  = jiffies;
338
339         if ( !_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ) {
340                 DRM_ERROR( "Lock isn't held after context switch\n" );
341         }
342
343                                 /* If a context switch is ever initiated
344                                    when the kernel holds the lock, release
345                                    that lock here. */
346         clear_bit( 0, &dev->context_flag );
347         wake_up( &dev->context_wait );
348
349         return 0;
350 }
351
352 /**
353  * Reserve contexts.
354  *
355  * \param inode device inode.
356  * \param filp file pointer.
357  * \param cmd command.
358  * \param arg user argument pointing to a drm_ctx_res structure.
359  * \return zero on success or a negative number on failure.
360  */
361 int DRM(resctx)( struct inode *inode, struct file *filp,
362                  unsigned int cmd, unsigned long arg )
363 {
364         drm_ctx_res_t res;
365         drm_ctx_t __user *argp = (void __user *)arg;
366         drm_ctx_t ctx;
367         int i;
368
369         if ( copy_from_user( &res, argp, sizeof(res) ) )
370                 return -EFAULT;
371
372         if ( res.count >= DRM_RESERVED_CONTEXTS ) {
373                 memset( &ctx, 0, sizeof(ctx) );
374                 for ( i = 0 ; i < DRM_RESERVED_CONTEXTS ; i++ ) {
375                         ctx.handle = i;
376                         if ( copy_to_user( &res.contexts[i],
377                                            &i, sizeof(i) ) )
378                                 return -EFAULT;
379                 }
380         }
381         res.count = DRM_RESERVED_CONTEXTS;
382
383         if ( copy_to_user( argp, &res, sizeof(res) ) )
384                 return -EFAULT;
385         return 0;
386 }
387
388 /**
389  * Add context.
390  *
391  * \param inode device inode.
392  * \param filp file pointer.
393  * \param cmd command.
394  * \param arg user argument pointing to a drm_ctx structure.
395  * \return zero on success or a negative number on failure.
396  *
397  * Get a new handle for the context and copy to userspace.
398  */
399 int DRM(addctx)( struct inode *inode, struct file *filp,
400                  unsigned int cmd, unsigned long arg )
401 {
402         drm_file_t *priv = filp->private_data;
403         drm_device_t *dev = priv->dev;
404         drm_ctx_list_t * ctx_entry;
405         drm_ctx_t __user *argp = (void __user *)arg;
406         drm_ctx_t ctx;
407
408         if ( copy_from_user( &ctx, argp, sizeof(ctx) ) )
409                 return -EFAULT;
410
411         ctx.handle = DRM(ctxbitmap_next)( dev );
412         if ( ctx.handle == DRM_KERNEL_CONTEXT ) {
413                                 /* Skip kernel's context and get a new one. */
414                 ctx.handle = DRM(ctxbitmap_next)( dev );
415         }
416         DRM_DEBUG( "%d\n", ctx.handle );
417         if ( ctx.handle == -1 ) {
418                 DRM_DEBUG( "Not enough free contexts.\n" );
419                                 /* Should this return -EBUSY instead? */
420                 return -ENOMEM;
421         }
422 #ifdef DRIVER_CTX_CTOR
423         if ( ctx.handle != DRM_KERNEL_CONTEXT )
424                 DRIVER_CTX_CTOR(ctx.handle); /* XXX: also pass dev ? */
425 #endif
426         ctx_entry = DRM(alloc)( sizeof(*ctx_entry), DRM_MEM_CTXLIST );
427         if ( !ctx_entry ) {
428                 DRM_DEBUG("out of memory\n");
429                 return -ENOMEM;
430         }
431
432         INIT_LIST_HEAD( &ctx_entry->head );
433         ctx_entry->handle = ctx.handle;
434         ctx_entry->tag = priv;
435
436         down( &dev->ctxlist_sem );
437         list_add( &ctx_entry->head, &dev->ctxlist->head );
438         ++dev->ctx_count;
439         up( &dev->ctxlist_sem );
440
441         if ( copy_to_user( argp, &ctx, sizeof(ctx) ) )
442                 return -EFAULT;
443         return 0;
444 }
445
446 int DRM(modctx)( struct inode *inode, struct file *filp,
447                  unsigned int cmd, unsigned long arg )
448 {
449         /* This does nothing */
450         return 0;
451 }
452
453 /**
454  * Get context.
455  *
456  * \param inode device inode.
457  * \param filp file pointer.
458  * \param cmd command.
459  * \param arg user argument pointing to a drm_ctx structure.
460  * \return zero on success or a negative number on failure.
461  */
462 int DRM(getctx)( struct inode *inode, struct file *filp,
463                  unsigned int cmd, unsigned long arg )
464 {
465         drm_ctx_t __user *argp = (void __user *)arg;
466         drm_ctx_t ctx;
467
468         if ( copy_from_user( &ctx, argp, sizeof(ctx) ) )
469                 return -EFAULT;
470
471         /* This is 0, because we don't handle any context flags */
472         ctx.flags = 0;
473
474         if ( copy_to_user( argp, &ctx, sizeof(ctx) ) )
475                 return -EFAULT;
476         return 0;
477 }
478
479 /**
480  * Switch context.
481  *
482  * \param inode device inode.
483  * \param filp file pointer.
484  * \param cmd command.
485  * \param arg user argument pointing to a drm_ctx structure.
486  * \return zero on success or a negative number on failure.
487  *
488  * Calls context_switch().
489  */
490 int DRM(switchctx)( struct inode *inode, struct file *filp,
491                     unsigned int cmd, unsigned long arg )
492 {
493         drm_file_t *priv = filp->private_data;
494         drm_device_t *dev = priv->dev;
495         drm_ctx_t ctx;
496
497         if ( copy_from_user( &ctx, (drm_ctx_t __user *)arg, sizeof(ctx) ) )
498                 return -EFAULT;
499
500         DRM_DEBUG( "%d\n", ctx.handle );
501         return DRM(context_switch)( dev, dev->last_context, ctx.handle );
502 }
503
504 /**
505  * New context.
506  *
507  * \param inode device inode.
508  * \param filp file pointer.
509  * \param cmd command.
510  * \param arg user argument pointing to a drm_ctx structure.
511  * \return zero on success or a negative number on failure.
512  *
513  * Calls context_switch_complete().
514  */
515 int DRM(newctx)( struct inode *inode, struct file *filp,
516                  unsigned int cmd, unsigned long arg )
517 {
518         drm_file_t *priv = filp->private_data;
519         drm_device_t *dev = priv->dev;
520         drm_ctx_t ctx;
521
522         if ( copy_from_user( &ctx, (drm_ctx_t __user *)arg, sizeof(ctx) ) )
523                 return -EFAULT;
524
525         DRM_DEBUG( "%d\n", ctx.handle );
526         DRM(context_switch_complete)( dev, ctx.handle );
527
528         return 0;
529 }
530
531 /**
532  * Remove context.
533  *
534  * \param inode device inode.
535  * \param filp file pointer.
536  * \param cmd command.
537  * \param arg user argument pointing to a drm_ctx structure.
538  * \return zero on success or a negative number on failure.
539  *
540  * If not the special kernel context, calls ctxbitmap_free() to free the specified context.
541  */
542 int DRM(rmctx)( struct inode *inode, struct file *filp,
543                 unsigned int cmd, unsigned long arg )
544 {
545         drm_file_t *priv = filp->private_data;
546         drm_device_t *dev = priv->dev;
547         drm_ctx_t ctx;
548
549         if ( copy_from_user( &ctx, (drm_ctx_t __user *)arg, sizeof(ctx) ) )
550                 return -EFAULT;
551
552         DRM_DEBUG( "%d\n", ctx.handle );
553         if ( ctx.handle == DRM_KERNEL_CONTEXT + 1 ) {
554                 priv->remove_auth_on_close = 1;
555         }
556         if ( ctx.handle != DRM_KERNEL_CONTEXT ) {
557 #ifdef DRIVER_CTX_DTOR
558                 DRIVER_CTX_DTOR(ctx.handle); /* XXX: also pass dev ? */
559 #endif
560                 DRM(ctxbitmap_free)( dev, ctx.handle );
561         }
562
563         down( &dev->ctxlist_sem );
564         if ( !list_empty( &dev->ctxlist->head ) ) {
565                 drm_ctx_list_t *pos, *n;
566
567                 list_for_each_entry_safe( pos, n, &dev->ctxlist->head, head ) {
568                         if ( pos->handle == ctx.handle ) {
569                                 list_del( &pos->head );
570                                 DRM(free)( pos, sizeof(*pos), DRM_MEM_CTXLIST );
571                                 --dev->ctx_count;
572                         }
573                 }
574         }
575         up( &dev->ctxlist_sem );
576
577         return 0;
578 }
579
580 /*@}*/