Initial support for fence object classes.
[platform/upstream/libdrm.git] / linux-core / drm_fence.c
1 /**************************************************************************
2  * 
3  * Copyright (c) 2006-2007 Tungsten Graphics, Inc., Cedar Park, TX., USA
4  * All Rights Reserved.
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  * 
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  * 
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
22  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
23  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 
24  * USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 /*
28  * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
29  */
30
31 #include "drmP.h"
32
33 /*
34  * Typically called by the IRQ handler.
35  */
36
37 void drm_fence_handler(drm_device_t * dev, uint32_t class,
38                        uint32_t sequence, uint32_t type)
39 {
40         int wake = 0;
41         uint32_t diff;
42         uint32_t relevant;
43         drm_fence_manager_t *fm = &dev->fm;
44         drm_fence_class_manager_t *fc = &fm->class[class];
45         drm_fence_driver_t *driver = dev->driver->fence_driver;
46         struct list_head *list, *prev;
47         drm_fence_object_t *fence;
48         int found = 0;
49
50         if (list_empty(&fc->ring))
51                 return;
52
53         list_for_each_entry(fence, &fc->ring, ring) {
54                 diff = (sequence - fence->sequence) & driver->sequence_mask;
55                 if (diff > driver->wrap_diff) {
56                         found = 1;
57                         break;
58                 }
59         }
60
61         list = (found) ? fence->ring.prev : fc->ring.prev;
62         prev = list->prev;
63
64         for (; list != &fc->ring; list = prev, prev = list->prev) {
65                 fence = list_entry(list, drm_fence_object_t, ring);
66
67                 type |= fence->native_type;
68                 relevant = type & fence->type;
69
70                 if ((fence->signaled | relevant) != fence->signaled) {
71                         fence->signaled |= relevant;
72                         DRM_DEBUG("Fence 0x%08lx signaled 0x%08x\n",
73                                   fence->base.hash.key, fence->signaled);
74                         fence->submitted_flush |= relevant;
75                         wake = 1;
76                 }
77
78                 relevant = fence->flush_mask &
79                     ~(fence->signaled | fence->submitted_flush);
80
81                 if (relevant) {
82                         fc->pending_flush |= relevant;
83                         fence->submitted_flush = fence->flush_mask;
84                 }
85
86                 if (!(fence->type & ~fence->signaled)) {
87                         DRM_DEBUG("Fence completely signaled 0x%08lx\n",
88                                   fence->base.hash.key);
89                         list_del_init(&fence->ring);
90                 }
91
92         }
93
94         if (wake) {
95                 DRM_WAKEUP(&fc->fence_queue);
96         }
97 }
98
99 EXPORT_SYMBOL(drm_fence_handler);
100
101 static void drm_fence_unring(drm_device_t * dev, struct list_head *ring)
102 {
103         drm_fence_manager_t *fm = &dev->fm;
104         unsigned long flags;
105
106         write_lock_irqsave(&fm->lock, flags);
107         list_del_init(ring);
108         write_unlock_irqrestore(&fm->lock, flags);
109 }
110
111 void drm_fence_usage_deref_locked(drm_device_t * dev,
112                                   drm_fence_object_t * fence)
113 {
114         drm_fence_manager_t *fm = &dev->fm;
115
116         if (atomic_dec_and_test(&fence->usage)) {
117                 drm_fence_unring(dev, &fence->ring);
118                 DRM_DEBUG("Destroyed a fence object 0x%08lx\n",
119                           fence->base.hash.key);
120                 atomic_dec(&fm->count);
121                 drm_ctl_free(fence, sizeof(*fence), DRM_MEM_FENCE);
122         }
123 }
124
125 void drm_fence_usage_deref_unlocked(drm_device_t * dev,
126                                     drm_fence_object_t * fence)
127 {
128         drm_fence_manager_t *fm = &dev->fm;
129
130         if (atomic_dec_and_test(&fence->usage)) {
131                 mutex_lock(&dev->struct_mutex);
132                 if (atomic_read(&fence->usage) == 0) {
133                         drm_fence_unring(dev, &fence->ring);
134                         atomic_dec(&fm->count);
135                         drm_ctl_free(fence, sizeof(*fence), DRM_MEM_FENCE);
136                 }
137                 mutex_unlock(&dev->struct_mutex);
138         }
139 }
140
141 static void drm_fence_object_destroy(drm_file_t * priv,
142                                      drm_user_object_t * base)
143 {
144         drm_device_t *dev = priv->head->dev;
145         drm_fence_object_t *fence =
146             drm_user_object_entry(base, drm_fence_object_t, base);
147
148         drm_fence_usage_deref_locked(dev, fence);
149 }
150
151 static int fence_signaled(drm_device_t * dev,
152                           drm_fence_object_t * fence,
153                           uint32_t mask, int poke_flush)
154 {
155         unsigned long flags;
156         int signaled;
157         drm_fence_manager_t *fm = &dev->fm;
158         drm_fence_driver_t *driver = dev->driver->fence_driver;
159
160         if (poke_flush)
161                 driver->poke_flush(dev, fence->class);
162         read_lock_irqsave(&fm->lock, flags);
163         signaled =
164             (fence->type & mask & fence->signaled) == (fence->type & mask);
165         read_unlock_irqrestore(&fm->lock, flags);
166
167         return signaled;
168 }
169
170 static void drm_fence_flush_exe(drm_fence_class_manager_t * fc,
171                                 drm_fence_driver_t * driver, uint32_t sequence)
172 {
173         uint32_t diff;
174
175         if (!fc->pending_exe_flush) {
176                 struct list_head *list;
177
178                 /*
179                  * Last_exe_flush is invalid. Find oldest sequence.
180                  */
181
182                 list = &fc->ring;
183                 if (list->next == &fc->ring) {
184                         return;
185                 } else {
186                         drm_fence_object_t *fence =
187                             list_entry(list->next, drm_fence_object_t, ring);
188                         fc->last_exe_flush = (fence->sequence - 1) &
189                             driver->sequence_mask;
190                 }
191                 diff = (sequence - fc->last_exe_flush) & driver->sequence_mask;
192                 if (diff >= driver->wrap_diff)
193                         return;
194                 fc->exe_flush_sequence = sequence;
195                 fc->pending_exe_flush = 1;
196         } else {
197                 diff =
198                     (sequence - fc->exe_flush_sequence) & driver->sequence_mask;
199                 if (diff < driver->wrap_diff) {
200                         fc->exe_flush_sequence = sequence;
201                 }
202         }
203 }
204
205 int drm_fence_object_signaled(drm_fence_object_t * fence,
206                               uint32_t type)
207 {
208         return ((fence->signaled & type) == type);
209 }
210
211 int drm_fence_object_flush(drm_device_t * dev,
212                            drm_fence_object_t * fence,
213                            uint32_t type)
214 {
215         drm_fence_manager_t *fm = &dev->fm;
216         drm_fence_class_manager_t *fc = &fm->class[fence->class];
217         drm_fence_driver_t *driver = dev->driver->fence_driver;
218         unsigned long flags;
219
220         if (type & ~fence->type) {
221                 DRM_ERROR("Flush trying to extend fence type, "
222                           "0x%x, 0x%x\n", type, fence->type);
223                 return -EINVAL;
224         }
225
226         write_lock_irqsave(&fm->lock, flags);
227         fence->flush_mask |= type;
228         if (fence->submitted_flush == fence->signaled) {
229                 if ((fence->type & DRM_FENCE_TYPE_EXE) &&
230                     !(fence->submitted_flush & DRM_FENCE_TYPE_EXE)) {
231                         drm_fence_flush_exe(fc, driver, fence->sequence);
232                         fence->submitted_flush |= DRM_FENCE_TYPE_EXE;
233                 } else {
234                         fc->pending_flush |= (fence->flush_mask &
235                                               ~fence->submitted_flush);
236                         fence->submitted_flush = fence->flush_mask;
237                 }
238         }
239         write_unlock_irqrestore(&fm->lock, flags);
240         driver->poke_flush(dev, fence->class);
241         return 0;
242 }
243
244 /*
245  * Make sure old fence objects are signaled before their fence sequences are
246  * wrapped around and reused.
247  */
248
249 void drm_fence_flush_old(drm_device_t * dev, uint32_t class, uint32_t sequence)
250 {
251         drm_fence_manager_t *fm = &dev->fm;
252         drm_fence_class_manager_t *fc = &fm->class[class];
253         drm_fence_driver_t *driver = dev->driver->fence_driver;
254         uint32_t old_sequence;
255         unsigned long flags;
256         drm_fence_object_t *fence;
257         uint32_t diff;
258
259         mutex_lock(&dev->struct_mutex);
260         read_lock_irqsave(&fm->lock, flags);
261         if (fc->ring.next == &fc->ring) {
262                 read_unlock_irqrestore(&fm->lock, flags);
263                 mutex_unlock(&dev->struct_mutex);
264                 return;
265         }
266         old_sequence = (sequence - driver->flush_diff) & driver->sequence_mask;
267         fence = list_entry(fc->ring.next, drm_fence_object_t, ring);
268         atomic_inc(&fence->usage);
269         mutex_unlock(&dev->struct_mutex);
270         diff = (old_sequence - fence->sequence) & driver->sequence_mask;
271         read_unlock_irqrestore(&fm->lock, flags);
272         if (diff < driver->wrap_diff) {
273                 drm_fence_object_flush(dev, fence, fence->type);
274         }
275         drm_fence_usage_deref_unlocked(dev, fence);
276 }
277
278 EXPORT_SYMBOL(drm_fence_flush_old);
279
280 static int drm_fence_lazy_wait(drm_device_t *dev,
281                                drm_fence_object_t *fence,
282                                int ignore_signals, uint32_t mask)
283 {
284         drm_fence_manager_t *fm = &dev->fm;
285         drm_fence_class_manager_t *fc = &fm->class[fence->class];
286
287         unsigned long _end = jiffies + 3*DRM_HZ;
288         int ret = 0;
289
290         do {
291                 DRM_WAIT_ON(ret, fc->fence_queue, 3 * DRM_HZ,
292                             fence_signaled(dev, fence, mask, 1));
293                 if (time_after_eq(jiffies, _end))
294                         break;
295         } while (ret == -EINTR && ignore_signals);
296         if (time_after_eq(jiffies, _end) && (ret != 0))
297                 ret = -EBUSY;
298         if (ret) {
299                 if (ret == -EBUSY) {
300                         DRM_ERROR("Fence timeout. "
301                                   "GPU lockup or fence driver was "
302                                   "taken down.\n");
303                 }
304                 return ((ret == -EINTR) ? -EAGAIN : ret);
305         }
306         return 0;
307 }
308
309 int drm_fence_object_wait(drm_device_t * dev,
310                           drm_fence_object_t * fence,
311                           int lazy, int ignore_signals, uint32_t mask)
312 {
313         drm_fence_driver_t *driver = dev->driver->fence_driver;
314         int ret = 0;
315         unsigned long _end;
316         int signaled;
317
318         if (mask & ~fence->type) {
319                 DRM_ERROR("Wait trying to extend fence type"
320                           " 0x%08x 0x%08x\n", mask, fence->type);
321                 return -EINVAL;
322         }
323
324         if (fence_signaled(dev, fence, mask, 0))
325                 return 0;
326
327         _end = jiffies + 3 * DRM_HZ;
328
329         drm_fence_object_flush(dev, fence, mask);
330
331         if (lazy && driver->lazy_capable) {
332
333                 ret = drm_fence_lazy_wait(dev, fence, ignore_signals, mask);
334                 if (ret)
335                         return ret;
336
337         } else {
338
339                 if (driver->has_irq(dev, fence->class,
340                                     DRM_FENCE_TYPE_EXE)) {
341                         ret = drm_fence_lazy_wait(dev, fence, ignore_signals,
342                                                   DRM_FENCE_TYPE_EXE);
343                         if (ret)
344                                 return ret;
345                 }
346
347                 if (driver->has_irq(dev, fence->class,
348                                     mask & ~DRM_FENCE_TYPE_EXE)) {
349                         ret = drm_fence_lazy_wait(dev, fence, ignore_signals,
350                                                   mask);
351                         if (ret)
352                                 return ret;
353                 }
354         }
355         if (fence_signaled(dev, fence, mask, 0))
356                 return 0;
357
358         DRM_ERROR("Busy wait\n");
359         /*
360          * Avoid kernel-space busy-waits.
361          */
362 #if 1
363         if (!ignore_signals)
364                 return -EAGAIN;
365 #endif
366         do {
367                 schedule();
368                 signaled = fence_signaled(dev, fence, mask, 1);
369         } while (!signaled && !time_after_eq(jiffies, _end));
370
371         if (!signaled)
372                 return -EBUSY;
373
374         return 0;
375 }
376
377 int drm_fence_object_emit(drm_device_t * dev, drm_fence_object_t * fence,
378                           uint32_t fence_flags, uint32_t class, uint32_t type)
379 {
380         drm_fence_manager_t *fm = &dev->fm;
381         drm_fence_driver_t *driver = dev->driver->fence_driver;
382         unsigned long flags;
383         uint32_t sequence;
384         uint32_t native_type;
385         int ret;
386
387         drm_fence_unring(dev, &fence->ring);
388         ret = driver->emit(dev, class, fence_flags, &sequence, &native_type);
389         if (ret)
390                 return ret;
391
392         write_lock_irqsave(&fm->lock, flags);
393         fence->class = class;
394         fence->type = type;
395         fence->flush_mask = 0x00;
396         fence->submitted_flush = 0x00;
397         fence->signaled = 0x00;
398         fence->sequence = sequence;
399         fence->native_type = native_type;
400         list_add_tail(&fence->ring, &fm->class[class].ring);
401         write_unlock_irqrestore(&fm->lock, flags);
402         return 0;
403 }
404
405 static int drm_fence_object_init(drm_device_t * dev, uint32_t class,
406                                  uint32_t type,
407                                  uint32_t fence_flags,
408                                  drm_fence_object_t * fence)
409 {
410         int ret = 0;
411         unsigned long flags;
412         drm_fence_manager_t *fm = &dev->fm;
413
414         mutex_lock(&dev->struct_mutex);
415         atomic_set(&fence->usage, 1);
416         mutex_unlock(&dev->struct_mutex);
417
418         write_lock_irqsave(&fm->lock, flags);
419         INIT_LIST_HEAD(&fence->ring);
420         fence->class = class;
421         fence->type = type;
422         fence->flush_mask = 0;
423         fence->submitted_flush = 0;
424         fence->signaled = 0;
425         fence->sequence = 0;
426         write_unlock_irqrestore(&fm->lock, flags);
427         if (fence_flags & DRM_FENCE_FLAG_EMIT) {
428                 ret = drm_fence_object_emit(dev, fence, fence_flags,
429                                             fence->class, type);
430         }
431         return ret;
432 }
433
434 int drm_fence_add_user_object(drm_file_t * priv, drm_fence_object_t * fence,
435                               int shareable)
436 {
437         drm_device_t *dev = priv->head->dev;
438         int ret;
439
440         mutex_lock(&dev->struct_mutex);
441         ret = drm_add_user_object(priv, &fence->base, shareable);
442         mutex_unlock(&dev->struct_mutex);
443         if (ret)
444                 return ret;
445         fence->base.type = drm_fence_type;
446         fence->base.remove = &drm_fence_object_destroy;
447         DRM_DEBUG("Fence 0x%08lx created\n", fence->base.hash.key);
448         return 0;
449 }
450
451 EXPORT_SYMBOL(drm_fence_add_user_object);
452
453 int drm_fence_object_create(drm_device_t * dev, uint32_t class, uint32_t type,
454                             unsigned flags, drm_fence_object_t ** c_fence)
455 {
456         drm_fence_object_t *fence;
457         int ret;
458         drm_fence_manager_t *fm = &dev->fm;
459
460         fence = drm_ctl_alloc(sizeof(*fence), DRM_MEM_FENCE);
461         if (!fence)
462                 return -ENOMEM;
463         ret = drm_fence_object_init(dev, class, type, flags, fence);
464         if (ret) {
465                 drm_fence_usage_deref_unlocked(dev, fence);
466                 return ret;
467         }
468         *c_fence = fence;
469         atomic_inc(&fm->count);
470
471         return 0;
472 }
473
474 EXPORT_SYMBOL(drm_fence_object_create);
475
476 void drm_fence_manager_init(drm_device_t * dev)
477 {
478         drm_fence_manager_t *fm = &dev->fm;
479         drm_fence_class_manager_t *class;
480         drm_fence_driver_t *fed = dev->driver->fence_driver;
481         int i;
482
483
484         fm->lock = RW_LOCK_UNLOCKED;
485         write_lock(&fm->lock);
486         fm->initialized = 0;
487         if (!fed)
488             goto out_unlock;
489
490         fm->initialized = 1;
491         fm->num_classes = fed->num_classes;
492         BUG_ON(fm->num_classes > _DRM_FENCE_CLASSES);
493
494         for (i=0; i<fm->num_classes; ++i) {
495             class = &fm->class[i];
496
497             INIT_LIST_HEAD(&class->ring);
498             class->pending_flush = 0;
499             DRM_INIT_WAITQUEUE(&class->fence_queue);
500         }
501
502         atomic_set(&fm->count, 0);
503  out_unlock:
504         write_unlock(&fm->lock);
505 }
506
507 void drm_fence_manager_takedown(drm_device_t * dev)
508 {
509 }
510
511 drm_fence_object_t *drm_lookup_fence_object(drm_file_t * priv, uint32_t handle)
512 {
513         drm_device_t *dev = priv->head->dev;
514         drm_user_object_t *uo;
515         drm_fence_object_t *fence;
516
517         mutex_lock(&dev->struct_mutex);
518         uo = drm_lookup_user_object(priv, handle);
519         if (!uo || (uo->type != drm_fence_type)) {
520                 mutex_unlock(&dev->struct_mutex);
521                 return NULL;
522         }
523         fence = drm_user_object_entry(uo, drm_fence_object_t, base);
524         atomic_inc(&fence->usage);
525         mutex_unlock(&dev->struct_mutex);
526         return fence;
527 }
528
529 int drm_fence_ioctl(DRM_IOCTL_ARGS)
530 {
531         DRM_DEVICE;
532         int ret;
533         drm_fence_manager_t *fm = &dev->fm;
534         drm_fence_arg_t arg;
535         drm_fence_object_t *fence;
536         drm_user_object_t *uo;
537         unsigned long flags;
538         ret = 0;
539
540         if (!fm->initialized) {
541                 DRM_ERROR("The DRM driver does not support fencing.\n");
542                 return -EINVAL;
543         }
544
545         DRM_COPY_FROM_USER_IOCTL(arg, (void __user *)data, sizeof(arg));
546         switch (arg.op) {
547         case drm_fence_create:
548                 if (arg.flags & DRM_FENCE_FLAG_EMIT)
549                         LOCK_TEST_WITH_RETURN(dev, filp);
550                 ret = drm_fence_object_create(dev, arg.class,
551                                               arg.type, arg.flags, &fence);
552                 if (ret)
553                         return ret;
554                 ret = drm_fence_add_user_object(priv, fence,
555                                                 arg.flags &
556                                                 DRM_FENCE_FLAG_SHAREABLE);
557                 if (ret) {
558                         drm_fence_usage_deref_unlocked(dev, fence);
559                         return ret;
560                 }
561
562                 /*
563                  * usage > 0. No need to lock dev->struct_mutex;
564                  */
565
566                 atomic_inc(&fence->usage);
567                 arg.handle = fence->base.hash.key;
568                 break;
569         case drm_fence_destroy:
570                 mutex_lock(&dev->struct_mutex);
571                 uo = drm_lookup_user_object(priv, arg.handle);
572                 if (!uo || (uo->type != drm_fence_type) || uo->owner != priv) {
573                         mutex_unlock(&dev->struct_mutex);
574                         return -EINVAL;
575                 }
576                 ret = drm_remove_user_object(priv, uo);
577                 mutex_unlock(&dev->struct_mutex);
578                 return ret;
579         case drm_fence_reference:
580                 ret =
581                     drm_user_object_ref(priv, arg.handle, drm_fence_type, &uo);
582                 if (ret)
583                         return ret;
584                 fence = drm_lookup_fence_object(priv, arg.handle);
585                 break;
586         case drm_fence_unreference:
587                 ret = drm_user_object_unref(priv, arg.handle, drm_fence_type);
588                 return ret;
589         case drm_fence_signaled:
590                 fence = drm_lookup_fence_object(priv, arg.handle);
591                 if (!fence)
592                         return -EINVAL;
593                 break;
594         case drm_fence_flush:
595                 fence = drm_lookup_fence_object(priv, arg.handle);
596                 if (!fence)
597                         return -EINVAL;
598                 ret = drm_fence_object_flush(dev, fence, arg.type);
599                 break;
600         case drm_fence_wait:
601                 fence = drm_lookup_fence_object(priv, arg.handle);
602                 if (!fence)
603                         return -EINVAL;
604                 ret =
605                     drm_fence_object_wait(dev, fence,
606                                           arg.flags & DRM_FENCE_FLAG_WAIT_LAZY,
607                                           0, arg.type);
608                 break;
609         case drm_fence_emit:
610                 LOCK_TEST_WITH_RETURN(dev, filp);
611                 fence = drm_lookup_fence_object(priv, arg.handle);
612                 if (!fence)
613                         return -EINVAL;
614                 ret = drm_fence_object_emit(dev, fence, arg.flags, arg.class,
615                                             arg.type);
616                 break;
617         case drm_fence_buffers:
618                 if (!dev->bm.initialized) {
619                         DRM_ERROR("Buffer object manager is not initialized\n");
620                         return -EINVAL;
621                 }
622                 LOCK_TEST_WITH_RETURN(dev, filp);
623                 ret = drm_fence_buffer_objects(priv, NULL, arg.flags,
624                                                NULL, &fence);
625                 if (ret)
626                         return ret;
627                 ret = drm_fence_add_user_object(priv, fence,
628                                                 arg.flags &
629                                                 DRM_FENCE_FLAG_SHAREABLE);
630                 if (ret)
631                         return ret;
632                 atomic_inc(&fence->usage);
633                 arg.handle = fence->base.hash.key;
634                 break;
635         default:
636                 return -EINVAL;
637         }
638         read_lock_irqsave(&fm->lock, flags);
639         arg.class = fence->class;
640         arg.type = fence->type;
641         arg.signaled = fence->signaled;
642         read_unlock_irqrestore(&fm->lock, flags);
643         drm_fence_usage_deref_unlocked(dev, fence);
644
645         DRM_COPY_TO_USER_IOCTL((void __user *)data, arg, sizeof(arg));
646         return ret;
647 }