ce161dc38f8b3368eeb503d3d1bf5ea69f8e9a68
[profile/ivi/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 *head;
47         drm_fence_object_t *fence, *next;
48         int found = 0;
49         int is_exe = (type & DRM_FENCE_TYPE_EXE);
50         int ge_last_exe;
51
52         diff = (sequence - fc->exe_flush_sequence) & driver->sequence_mask;
53
54         if (fc->pending_exe_flush && is_exe && diff < driver->wrap_diff)
55                 fc->pending_exe_flush = 0;
56
57         diff = (sequence - fc->last_exe_flush) & driver->sequence_mask;
58         ge_last_exe = diff < driver->wrap_diff;
59
60         if (ge_last_exe)
61                 fc->pending_flush &= ~type;
62
63         if (is_exe && ge_last_exe) {
64                 fc->last_exe_flush = sequence;
65         }
66
67         if (list_empty(&fc->ring))
68                 return;
69
70         list_for_each_entry(fence, &fc->ring, ring) {
71                 diff = (sequence - fence->sequence) & driver->sequence_mask;
72                 if (diff > driver->wrap_diff) {
73                         found = 1;
74                         break;
75                 }
76         }
77
78         head = (found) ? &fence->ring : &fc->ring;
79
80         list_for_each_entry_safe_reverse(fence, next, head, ring) {
81                 if (&fence->ring == &fc->ring)
82                         break;
83
84                 type |= fence->native_type;
85                 relevant = type & fence->type;
86
87                 if ((fence->signaled | relevant) != fence->signaled) {
88                         fence->signaled |= relevant;
89                         DRM_DEBUG("Fence 0x%08lx signaled 0x%08x\n",
90                                   fence->base.hash.key, fence->signaled);
91                         fence->submitted_flush |= relevant;
92                         wake = 1;
93                 }
94
95                 relevant = fence->flush_mask &
96                     ~(fence->signaled | fence->submitted_flush);
97
98                 if (relevant) {
99                         fc->pending_flush |= relevant;
100                         fence->submitted_flush = fence->flush_mask;
101                 }
102
103                 if (!(fence->type & ~fence->signaled)) {
104                         DRM_DEBUG("Fence completely signaled 0x%08lx\n",
105                                   fence->base.hash.key);
106                         list_del_init(&fence->ring);
107                 }
108         }
109
110         if (wake) {
111                 DRM_WAKEUP(&fc->fence_queue);
112         }
113 }
114
115 EXPORT_SYMBOL(drm_fence_handler);
116
117 static void drm_fence_unring(drm_device_t * dev, struct list_head *ring)
118 {
119         drm_fence_manager_t *fm = &dev->fm;
120         unsigned long flags;
121
122         write_lock_irqsave(&fm->lock, flags);
123         list_del_init(ring);
124         write_unlock_irqrestore(&fm->lock, flags);
125 }
126
127 void drm_fence_usage_deref_locked(drm_device_t * dev,
128                                   drm_fence_object_t * fence)
129 {
130         drm_fence_manager_t *fm = &dev->fm;
131
132         if (atomic_dec_and_test(&fence->usage)) {
133                 drm_fence_unring(dev, &fence->ring);
134                 DRM_DEBUG("Destroyed a fence object 0x%08lx\n",
135                           fence->base.hash.key);
136                 atomic_dec(&fm->count);
137                 drm_ctl_free(fence, sizeof(*fence), DRM_MEM_FENCE);
138         }
139 }
140
141 void drm_fence_usage_deref_unlocked(drm_device_t * dev,
142                                     drm_fence_object_t * fence)
143 {
144         drm_fence_manager_t *fm = &dev->fm;
145
146         if (atomic_dec_and_test(&fence->usage)) {
147                 mutex_lock(&dev->struct_mutex);
148                 if (atomic_read(&fence->usage) == 0) {
149                         drm_fence_unring(dev, &fence->ring);
150                         atomic_dec(&fm->count);
151                         drm_ctl_free(fence, sizeof(*fence), DRM_MEM_FENCE);
152                 }
153                 mutex_unlock(&dev->struct_mutex);
154         }
155 }
156
157 static void drm_fence_object_destroy(drm_file_t * priv,
158                                      drm_user_object_t * base)
159 {
160         drm_device_t *dev = priv->head->dev;
161         drm_fence_object_t *fence =
162             drm_user_object_entry(base, drm_fence_object_t, base);
163
164         drm_fence_usage_deref_locked(dev, fence);
165 }
166
167 static int fence_signaled(drm_device_t * dev,
168                           drm_fence_object_t * fence,
169                           uint32_t mask, int poke_flush)
170 {
171         unsigned long flags;
172         int signaled;
173         drm_fence_manager_t *fm = &dev->fm;
174         drm_fence_driver_t *driver = dev->driver->fence_driver;
175
176         if (poke_flush)
177                 driver->poke_flush(dev, fence->class);
178         read_lock_irqsave(&fm->lock, flags);
179         signaled =
180             (fence->type & mask & fence->signaled) == (fence->type & mask);
181         read_unlock_irqrestore(&fm->lock, flags);
182
183         return signaled;
184 }
185
186 static void drm_fence_flush_exe(drm_fence_class_manager_t * fc,
187                                 drm_fence_driver_t * driver, uint32_t sequence)
188 {
189         uint32_t diff;
190
191         if (!fc->pending_exe_flush) {
192                 fc->exe_flush_sequence = sequence;
193                 fc->pending_exe_flush = 1;
194         } else {
195                 diff =
196                     (sequence - fc->exe_flush_sequence) & driver->sequence_mask;
197                 if (diff < driver->wrap_diff) {
198                         fc->exe_flush_sequence = sequence;
199                 }
200         }
201 }
202
203 int drm_fence_object_signaled(drm_fence_object_t * fence,
204                               uint32_t type)
205 {
206         return ((fence->signaled & type) == type);
207 }
208
209 int drm_fence_object_flush(drm_device_t * dev,
210                            drm_fence_object_t * fence,
211                            uint32_t type)
212 {
213         drm_fence_manager_t *fm = &dev->fm;
214         drm_fence_class_manager_t *fc = &fm->class[fence->class];
215         drm_fence_driver_t *driver = dev->driver->fence_driver;
216         unsigned long flags;
217
218         if (type & ~fence->type) {
219                 DRM_ERROR("Flush trying to extend fence type, "
220                           "0x%x, 0x%x\n", type, fence->type);
221                 return -EINVAL;
222         }
223
224         write_lock_irqsave(&fm->lock, flags);
225         fence->flush_mask |= type;
226         if (fence->submitted_flush == fence->signaled) {
227                 if ((fence->type & DRM_FENCE_TYPE_EXE) &&
228                     !(fence->submitted_flush & DRM_FENCE_TYPE_EXE)) {
229                         drm_fence_flush_exe(fc, driver, fence->sequence);
230                         fence->submitted_flush |= DRM_FENCE_TYPE_EXE;
231                 } else {
232                         fc->pending_flush |= (fence->flush_mask &
233                                               ~fence->submitted_flush);
234                         fence->submitted_flush = fence->flush_mask;
235                 }
236         }
237         write_unlock_irqrestore(&fm->lock, flags);
238         driver->poke_flush(dev, fence->class);
239         return 0;
240 }
241
242 /*
243  * Make sure old fence objects are signaled before their fence sequences are
244  * wrapped around and reused.
245  */
246
247 void drm_fence_flush_old(drm_device_t * dev, uint32_t class, uint32_t sequence)
248 {
249         drm_fence_manager_t *fm = &dev->fm;
250         drm_fence_class_manager_t *fc = &fm->class[class];
251         drm_fence_driver_t *driver = dev->driver->fence_driver;
252         uint32_t old_sequence;
253         unsigned long flags;
254         drm_fence_object_t *fence;
255         uint32_t diff;
256
257         write_lock_irqsave(&fm->lock, flags);
258         old_sequence = (sequence - driver->flush_diff) & driver->sequence_mask;
259         diff = (old_sequence - fc->last_exe_flush) & driver->sequence_mask;
260
261         if ((diff < driver->wrap_diff) && !fc->pending_exe_flush) {
262                 fc->pending_exe_flush = 1;
263                 fc->exe_flush_sequence = sequence - (driver->flush_diff / 2);
264         }
265         write_unlock_irqrestore(&fm->lock, flags);
266
267         mutex_lock(&dev->struct_mutex);
268         read_lock_irqsave(&fm->lock, flags);
269
270         if (list_empty(&fc->ring)) {
271                 read_unlock_irqrestore(&fm->lock, flags);
272                 mutex_unlock(&dev->struct_mutex);
273                 return;
274         }
275         fence = list_entry(fc->ring.next, drm_fence_object_t, ring);
276         atomic_inc(&fence->usage);
277         mutex_unlock(&dev->struct_mutex);
278         diff = (old_sequence - fence->sequence) & driver->sequence_mask;
279         read_unlock_irqrestore(&fm->lock, flags);
280         if (diff < driver->wrap_diff) {
281                 drm_fence_object_flush(dev, fence, fence->type);
282         }
283         drm_fence_usage_deref_unlocked(dev, fence);
284 }
285
286 EXPORT_SYMBOL(drm_fence_flush_old);
287
288 static int drm_fence_lazy_wait(drm_device_t *dev,
289                                drm_fence_object_t *fence,
290                                int ignore_signals,
291                                uint32_t mask)
292 {
293         drm_fence_manager_t *fm = &dev->fm;
294         drm_fence_class_manager_t *fc = &fm->class[fence->class];
295         int signaled;
296         unsigned long _end = jiffies + 3*DRM_HZ;
297         int ret = 0;
298
299         do {
300                 DRM_WAIT_ON(ret, fc->fence_queue, 3 * DRM_HZ,
301                             (signaled = fence_signaled(dev, fence, mask, 1)));
302                 if (signaled)
303                         return 0;
304                 if (time_after_eq(jiffies, _end))
305                         break;
306         } while (ret == -EINTR && ignore_signals);
307         if (fence_signaled(dev, fence, mask, 0))
308                 return 0;
309         if (time_after_eq(jiffies, _end))
310                 ret = -EBUSY;
311         if (ret) {
312                 if (ret == -EBUSY) {
313                         DRM_ERROR("Fence timeout. "
314                                   "GPU lockup or fence driver was "
315                                   "taken down.\n");
316                 }
317                 return ((ret == -EINTR) ? -EAGAIN : ret);
318         }
319         return 0;
320 }
321
322 int drm_fence_object_wait(drm_device_t * dev,
323                           drm_fence_object_t * fence,
324                           int lazy, int ignore_signals, uint32_t mask)
325 {
326         drm_fence_driver_t *driver = dev->driver->fence_driver;
327         int ret = 0;
328         unsigned long _end;
329         int signaled;
330
331         if (mask & ~fence->type) {
332                 DRM_ERROR("Wait trying to extend fence type"
333                           " 0x%08x 0x%08x\n", mask, fence->type);
334                 return -EINVAL;
335         }
336
337         if (fence_signaled(dev, fence, mask, 0))
338                 return 0;
339
340         _end = jiffies + 3 * DRM_HZ;
341
342         drm_fence_object_flush(dev, fence, mask);
343
344         if (lazy && driver->lazy_capable) {
345
346                 ret = drm_fence_lazy_wait(dev, fence, ignore_signals, mask);
347                 if (ret)
348                         return ret;
349
350         } else {
351
352                 if (driver->has_irq(dev, fence->class,
353                                     DRM_FENCE_TYPE_EXE)) {
354                         ret = drm_fence_lazy_wait(dev, fence, ignore_signals,
355                                                   DRM_FENCE_TYPE_EXE);
356                         if (ret)
357                                 return ret;
358                 }
359
360                 if (driver->has_irq(dev, fence->class,
361                                     mask & ~DRM_FENCE_TYPE_EXE)) {
362                         ret = drm_fence_lazy_wait(dev, fence, ignore_signals,
363                                                   mask);
364                         if (ret)
365                                 return ret;
366                 }
367         }
368         if (drm_fence_object_signaled(fence, mask))
369                 return 0;
370
371         /*
372          * Avoid kernel-space busy-waits.
373          */
374 #if 1
375         if (!ignore_signals)
376                 return -EAGAIN;
377 #endif
378         do {
379                 schedule();
380                 signaled = fence_signaled(dev, fence, mask, 1);
381         } while (!signaled && !time_after_eq(jiffies, _end));
382
383         if (!signaled)
384                 return -EBUSY;
385
386         return 0;
387 }
388
389 int drm_fence_object_emit(drm_device_t * dev, drm_fence_object_t * fence,
390                           uint32_t fence_flags, uint32_t class, uint32_t type)
391 {
392         drm_fence_manager_t *fm = &dev->fm;
393         drm_fence_driver_t *driver = dev->driver->fence_driver;
394         drm_fence_class_manager_t *fc = &fm->class[fence->class];
395         unsigned long flags;
396         uint32_t sequence;
397         uint32_t native_type;
398         int ret;
399
400         drm_fence_unring(dev, &fence->ring);
401         ret = driver->emit(dev, class, fence_flags, &sequence, &native_type);
402         if (ret)
403                 return ret;
404
405         write_lock_irqsave(&fm->lock, flags);
406         fence->class = class;
407         fence->type = type;
408         fence->flush_mask = 0x00;
409         fence->submitted_flush = 0x00;
410         fence->signaled = 0x00;
411         fence->sequence = sequence;
412         fence->native_type = native_type;
413         if (list_empty(&fc->ring))
414                 fc->last_exe_flush = sequence - 1;
415         list_add_tail(&fence->ring, &fc->ring);
416         write_unlock_irqrestore(&fm->lock, flags);
417         return 0;
418 }
419
420 static int drm_fence_object_init(drm_device_t * dev, uint32_t class,
421                                  uint32_t type,
422                                  uint32_t fence_flags,
423                                  drm_fence_object_t * fence)
424 {
425         int ret = 0;
426         unsigned long flags;
427         drm_fence_manager_t *fm = &dev->fm;
428
429         mutex_lock(&dev->struct_mutex);
430         atomic_set(&fence->usage, 1);
431         mutex_unlock(&dev->struct_mutex);
432
433         write_lock_irqsave(&fm->lock, flags);
434         INIT_LIST_HEAD(&fence->ring);
435         fence->class = class;
436         fence->type = type;
437         fence->flush_mask = 0;
438         fence->submitted_flush = 0;
439         fence->signaled = 0;
440         fence->sequence = 0;
441         write_unlock_irqrestore(&fm->lock, flags);
442         if (fence_flags & DRM_FENCE_FLAG_EMIT) {
443                 ret = drm_fence_object_emit(dev, fence, fence_flags,
444                                             fence->class, type);
445         }
446         return ret;
447 }
448
449 int drm_fence_add_user_object(drm_file_t * priv, drm_fence_object_t * fence,
450                               int shareable)
451 {
452         drm_device_t *dev = priv->head->dev;
453         int ret;
454
455         mutex_lock(&dev->struct_mutex);
456         ret = drm_add_user_object(priv, &fence->base, shareable);
457         mutex_unlock(&dev->struct_mutex);
458         if (ret)
459                 return ret;
460         fence->base.type = drm_fence_type;
461         fence->base.remove = &drm_fence_object_destroy;
462         DRM_DEBUG("Fence 0x%08lx created\n", fence->base.hash.key);
463         return 0;
464 }
465
466 EXPORT_SYMBOL(drm_fence_add_user_object);
467
468 int drm_fence_object_create(drm_device_t * dev, uint32_t class, uint32_t type,
469                             unsigned flags, drm_fence_object_t ** c_fence)
470 {
471         drm_fence_object_t *fence;
472         int ret;
473         drm_fence_manager_t *fm = &dev->fm;
474
475         fence = drm_ctl_alloc(sizeof(*fence), DRM_MEM_FENCE);
476         if (!fence)
477                 return -ENOMEM;
478         ret = drm_fence_object_init(dev, class, type, flags, fence);
479         if (ret) {
480                 drm_fence_usage_deref_unlocked(dev, fence);
481                 return ret;
482         }
483         *c_fence = fence;
484         atomic_inc(&fm->count);
485
486         return 0;
487 }
488
489 EXPORT_SYMBOL(drm_fence_object_create);
490
491 void drm_fence_manager_init(drm_device_t * dev)
492 {
493         drm_fence_manager_t *fm = &dev->fm;
494         drm_fence_class_manager_t *class;
495         drm_fence_driver_t *fed = dev->driver->fence_driver;
496         int i;
497
498         rwlock_init(&fm->lock);
499         write_lock(&fm->lock);
500         fm->initialized = 0;
501         if (!fed)
502             goto out_unlock;
503
504         fm->initialized = 1;
505         fm->num_classes = fed->num_classes;
506         BUG_ON(fm->num_classes > _DRM_FENCE_CLASSES);
507
508         for (i=0; i<fm->num_classes; ++i) {
509             class = &fm->class[i];
510
511             INIT_LIST_HEAD(&class->ring);
512             class->pending_flush = 0;
513             DRM_INIT_WAITQUEUE(&class->fence_queue);
514         }
515
516         atomic_set(&fm->count, 0);
517  out_unlock:
518         write_unlock(&fm->lock);
519 }
520
521 void drm_fence_manager_takedown(drm_device_t * dev)
522 {
523 }
524
525 drm_fence_object_t *drm_lookup_fence_object(drm_file_t * priv, uint32_t handle)
526 {
527         drm_device_t *dev = priv->head->dev;
528         drm_user_object_t *uo;
529         drm_fence_object_t *fence;
530
531         mutex_lock(&dev->struct_mutex);
532         uo = drm_lookup_user_object(priv, handle);
533         if (!uo || (uo->type != drm_fence_type)) {
534                 mutex_unlock(&dev->struct_mutex);
535                 return NULL;
536         }
537         fence = drm_user_object_entry(uo, drm_fence_object_t, base);
538         atomic_inc(&fence->usage);
539         mutex_unlock(&dev->struct_mutex);
540         return fence;
541 }
542
543 int drm_fence_ioctl(DRM_IOCTL_ARGS)
544 {
545         DRM_DEVICE;
546         int ret;
547         drm_fence_manager_t *fm = &dev->fm;
548         drm_fence_arg_t arg;
549         drm_fence_object_t *fence;
550         drm_user_object_t *uo;
551         unsigned long flags;
552         ret = 0;
553
554         if (!fm->initialized) {
555                 DRM_ERROR("The DRM driver does not support fencing.\n");
556                 return -EINVAL;
557         }
558
559         DRM_COPY_FROM_USER_IOCTL(arg, (void __user *)data, sizeof(arg));
560         switch (arg.op) {
561         case drm_fence_create:
562                 if (arg.flags & DRM_FENCE_FLAG_EMIT)
563                         LOCK_TEST_WITH_RETURN(dev, filp);
564                 ret = drm_fence_object_create(dev, arg.class,
565                                               arg.type, arg.flags, &fence);
566                 if (ret)
567                         return ret;
568                 ret = drm_fence_add_user_object(priv, fence,
569                                                 arg.flags &
570                                                 DRM_FENCE_FLAG_SHAREABLE);
571                 if (ret) {
572                         drm_fence_usage_deref_unlocked(dev, fence);
573                         return ret;
574                 }
575
576                 /*
577                  * usage > 0. No need to lock dev->struct_mutex;
578                  */
579
580                 atomic_inc(&fence->usage);
581                 arg.handle = fence->base.hash.key;
582                 break;
583         case drm_fence_destroy:
584                 mutex_lock(&dev->struct_mutex);
585                 uo = drm_lookup_user_object(priv, arg.handle);
586                 if (!uo || (uo->type != drm_fence_type) || uo->owner != priv) {
587                         mutex_unlock(&dev->struct_mutex);
588                         return -EINVAL;
589                 }
590                 ret = drm_remove_user_object(priv, uo);
591                 mutex_unlock(&dev->struct_mutex);
592                 return ret;
593         case drm_fence_reference:
594                 ret =
595                     drm_user_object_ref(priv, arg.handle, drm_fence_type, &uo);
596                 if (ret)
597                         return ret;
598                 fence = drm_lookup_fence_object(priv, arg.handle);
599                 break;
600         case drm_fence_unreference:
601                 ret = drm_user_object_unref(priv, arg.handle, drm_fence_type);
602                 return ret;
603         case drm_fence_signaled:
604                 fence = drm_lookup_fence_object(priv, arg.handle);
605                 if (!fence)
606                         return -EINVAL;
607                 break;
608         case drm_fence_flush:
609                 fence = drm_lookup_fence_object(priv, arg.handle);
610                 if (!fence)
611                         return -EINVAL;
612                 ret = drm_fence_object_flush(dev, fence, arg.type);
613                 break;
614         case drm_fence_wait:
615                 fence = drm_lookup_fence_object(priv, arg.handle);
616                 if (!fence)
617                         return -EINVAL;
618                 ret =
619                     drm_fence_object_wait(dev, fence,
620                                           arg.flags & DRM_FENCE_FLAG_WAIT_LAZY,
621                                           0, arg.type);
622                 break;
623         case drm_fence_emit:
624                 LOCK_TEST_WITH_RETURN(dev, filp);
625                 fence = drm_lookup_fence_object(priv, arg.handle);
626                 if (!fence)
627                         return -EINVAL;
628                 ret = drm_fence_object_emit(dev, fence, arg.flags, arg.class,
629                                             arg.type);
630                 break;
631         case drm_fence_buffers:
632                 if (!dev->bm.initialized) {
633                         DRM_ERROR("Buffer object manager is not initialized\n");
634                         return -EINVAL;
635                 }
636                 LOCK_TEST_WITH_RETURN(dev, filp);
637                 ret = drm_fence_buffer_objects(priv, NULL, arg.flags,
638                                                NULL, &fence);
639                 if (ret)
640                         return ret;
641                 ret = drm_fence_add_user_object(priv, fence,
642                                                 arg.flags &
643                                                 DRM_FENCE_FLAG_SHAREABLE);
644                 if (ret)
645                         return ret;
646                 atomic_inc(&fence->usage);
647                 arg.handle = fence->base.hash.key;
648                 break;
649         default:
650                 return -EINVAL;
651         }
652         read_lock_irqsave(&fm->lock, flags);
653         arg.class = fence->class;
654         arg.type = fence->type;
655         arg.signaled = fence->signaled;
656         read_unlock_irqrestore(&fm->lock, flags);
657         drm_fence_usage_deref_unlocked(dev, fence);
658
659         DRM_COPY_TO_USER_IOCTL((void __user *)data, arg, sizeof(arg));
660         return ret;
661 }