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