2006-08-21 13:02:08 -06:00
|
|
|
|
/**************************************************************************
|
|
|
|
|
*
|
2007-02-08 16:07:29 -07:00
|
|
|
|
* Copyright (c) 2006-2007 Tungsten Graphics, Inc., Cedar Park, TX., USA
|
2006-08-21 13:02:08 -06:00
|
|
|
|
* All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, sub license, and/or sell copies of the Software, and to
|
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
|
* the following conditions:
|
|
|
|
|
*
|
2007-02-08 16:07:29 -07:00
|
|
|
|
* The above copyright notice and this permission notice (including the
|
|
|
|
|
* next paragraph) shall be included in all copies or substantial portions
|
|
|
|
|
* of the Software.
|
|
|
|
|
*
|
2006-08-21 13:02:08 -06:00
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
|
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
|
|
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
|
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
|
|
|
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
*
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
/*
|
|
|
|
|
* Authors: Thomas Hellstr<EFBFBD>m <thomas-at-tungstengraphics-dot-com>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "drmP.h"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Typically called by the IRQ handler.
|
|
|
|
|
*/
|
|
|
|
|
|
2007-02-14 05:31:35 -07:00
|
|
|
|
void drm_fence_handler(drm_device_t * dev, uint32_t class,
|
|
|
|
|
uint32_t sequence, uint32_t type)
|
2006-08-21 13:02:08 -06:00
|
|
|
|
{
|
|
|
|
|
int wake = 0;
|
|
|
|
|
uint32_t diff;
|
|
|
|
|
uint32_t relevant;
|
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
2007-02-15 04:10:33 -07:00
|
|
|
|
drm_fence_class_manager_t *fc = &fm->class[class];
|
2006-08-21 13:02:08 -06:00
|
|
|
|
drm_fence_driver_t *driver = dev->driver->fence_driver;
|
2006-09-15 08:47:09 -06:00
|
|
|
|
struct list_head *list, *prev;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
drm_fence_object_t *fence;
|
2006-09-18 13:50:00 -06:00
|
|
|
|
int found = 0;
|
|
|
|
|
|
2007-02-15 04:10:33 -07:00
|
|
|
|
if (list_empty(&fc->ring))
|
2006-09-18 13:50:00 -06:00
|
|
|
|
return;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
|
2007-02-15 04:10:33 -07:00
|
|
|
|
list_for_each_entry(fence, &fc->ring, ring) {
|
2006-08-21 13:02:08 -06:00
|
|
|
|
diff = (sequence - fence->sequence) & driver->sequence_mask;
|
2006-09-18 13:50:00 -06:00
|
|
|
|
if (diff > driver->wrap_diff) {
|
|
|
|
|
found = 1;
|
2006-09-15 08:47:09 -06:00
|
|
|
|
break;
|
2006-09-18 13:50:00 -06:00
|
|
|
|
}
|
2006-08-21 13:02:08 -06:00
|
|
|
|
}
|
|
|
|
|
|
2007-02-15 04:10:33 -07:00
|
|
|
|
list = (found) ? fence->ring.prev : fc->ring.prev;
|
2006-09-15 08:47:09 -06:00
|
|
|
|
prev = list->prev;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
|
2007-02-15 04:10:33 -07:00
|
|
|
|
for (; list != &fc->ring; list = prev, prev = list->prev) {
|
2006-08-21 13:02:08 -06:00
|
|
|
|
fence = list_entry(list, drm_fence_object_t, ring);
|
2006-09-15 08:47:09 -06:00
|
|
|
|
|
|
|
|
|
type |= fence->native_type;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
relevant = type & fence->type;
|
2006-09-15 08:47:09 -06:00
|
|
|
|
|
2006-08-21 13:02:08 -06:00
|
|
|
|
if ((fence->signaled | relevant) != fence->signaled) {
|
|
|
|
|
fence->signaled |= relevant;
|
2006-09-12 09:39:44 -06:00
|
|
|
|
DRM_DEBUG("Fence 0x%08lx signaled 0x%08x\n",
|
2006-09-08 09:24:38 -06:00
|
|
|
|
fence->base.hash.key, fence->signaled);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
fence->submitted_flush |= relevant;
|
|
|
|
|
wake = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
relevant = fence->flush_mask &
|
|
|
|
|
~(fence->signaled | fence->submitted_flush);
|
2006-09-15 08:47:09 -06:00
|
|
|
|
|
2006-08-21 13:02:08 -06:00
|
|
|
|
if (relevant) {
|
2007-02-15 04:10:33 -07:00
|
|
|
|
fc->pending_flush |= relevant;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
fence->submitted_flush = fence->flush_mask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(fence->type & ~fence->signaled)) {
|
2006-09-12 09:39:44 -06:00
|
|
|
|
DRM_DEBUG("Fence completely signaled 0x%08lx\n",
|
2006-09-08 09:24:38 -06:00
|
|
|
|
fence->base.hash.key);
|
2006-09-15 08:47:09 -06:00
|
|
|
|
list_del_init(&fence->ring);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
}
|
|
|
|
|
|
2006-09-15 08:47:09 -06:00
|
|
|
|
}
|
2006-10-17 11:57:06 -06:00
|
|
|
|
|
2006-08-21 13:02:08 -06:00
|
|
|
|
if (wake) {
|
2007-02-15 04:10:33 -07:00
|
|
|
|
DRM_WAKEUP(&fc->fence_queue);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EXPORT_SYMBOL(drm_fence_handler);
|
|
|
|
|
|
|
|
|
|
static void drm_fence_unring(drm_device_t * dev, struct list_head *ring)
|
|
|
|
|
{
|
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
write_lock_irqsave(&fm->lock, flags);
|
|
|
|
|
list_del_init(ring);
|
|
|
|
|
write_unlock_irqrestore(&fm->lock, flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void drm_fence_usage_deref_locked(drm_device_t * dev,
|
|
|
|
|
drm_fence_object_t * fence)
|
|
|
|
|
{
|
2006-09-25 03:51:08 -06:00
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
|
|
|
|
|
2006-08-21 13:02:08 -06:00
|
|
|
|
if (atomic_dec_and_test(&fence->usage)) {
|
|
|
|
|
drm_fence_unring(dev, &fence->ring);
|
2006-09-12 09:39:44 -06:00
|
|
|
|
DRM_DEBUG("Destroyed a fence object 0x%08lx\n",
|
2006-09-08 09:24:38 -06:00
|
|
|
|
fence->base.hash.key);
|
2006-09-25 03:51:08 -06:00
|
|
|
|
atomic_dec(&fm->count);
|
2006-12-15 04:37:24 -07:00
|
|
|
|
drm_ctl_free(fence, sizeof(*fence), DRM_MEM_FENCE);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void drm_fence_usage_deref_unlocked(drm_device_t * dev,
|
|
|
|
|
drm_fence_object_t * fence)
|
|
|
|
|
{
|
2006-09-25 03:51:08 -06:00
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
|
|
|
|
|
2006-08-21 13:02:08 -06:00
|
|
|
|
if (atomic_dec_and_test(&fence->usage)) {
|
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
|
|
|
if (atomic_read(&fence->usage) == 0) {
|
|
|
|
|
drm_fence_unring(dev, &fence->ring);
|
2006-10-17 11:57:06 -06:00
|
|
|
|
atomic_dec(&fm->count);
|
2006-12-15 04:37:24 -07:00
|
|
|
|
drm_ctl_free(fence, sizeof(*fence), DRM_MEM_FENCE);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
}
|
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void drm_fence_object_destroy(drm_file_t * priv,
|
|
|
|
|
drm_user_object_t * base)
|
|
|
|
|
{
|
|
|
|
|
drm_device_t *dev = priv->head->dev;
|
|
|
|
|
drm_fence_object_t *fence =
|
|
|
|
|
drm_user_object_entry(base, drm_fence_object_t, base);
|
|
|
|
|
|
|
|
|
|
drm_fence_usage_deref_locked(dev, fence);
|
|
|
|
|
}
|
|
|
|
|
|
2007-02-14 06:10:10 -07:00
|
|
|
|
static int fence_signaled(drm_device_t * dev,
|
2006-09-25 03:51:08 -06:00
|
|
|
|
drm_fence_object_t * fence,
|
2006-08-21 13:02:08 -06:00
|
|
|
|
uint32_t mask, int poke_flush)
|
|
|
|
|
{
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
int signaled;
|
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
|
|
|
|
drm_fence_driver_t *driver = dev->driver->fence_driver;
|
|
|
|
|
|
|
|
|
|
if (poke_flush)
|
2007-02-14 05:31:35 -07:00
|
|
|
|
driver->poke_flush(dev, fence->class);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
read_lock_irqsave(&fm->lock, flags);
|
|
|
|
|
signaled =
|
|
|
|
|
(fence->type & mask & fence->signaled) == (fence->type & mask);
|
|
|
|
|
read_unlock_irqrestore(&fm->lock, flags);
|
|
|
|
|
|
|
|
|
|
return signaled;
|
|
|
|
|
}
|
|
|
|
|
|
2007-02-15 04:10:33 -07:00
|
|
|
|
static void drm_fence_flush_exe(drm_fence_class_manager_t * fc,
|
2006-08-21 13:02:08 -06:00
|
|
|
|
drm_fence_driver_t * driver, uint32_t sequence)
|
|
|
|
|
{
|
|
|
|
|
uint32_t diff;
|
|
|
|
|
|
2007-02-15 04:10:33 -07:00
|
|
|
|
if (!fc->pending_exe_flush) {
|
2007-02-14 06:10:10 -07:00
|
|
|
|
struct list_head *list;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Last_exe_flush is invalid. Find oldest sequence.
|
|
|
|
|
*/
|
|
|
|
|
|
2007-02-15 04:10:33 -07:00
|
|
|
|
list = &fc->ring;
|
|
|
|
|
if (list->next == &fc->ring) {
|
2006-08-21 13:02:08 -06:00
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
drm_fence_object_t *fence =
|
|
|
|
|
list_entry(list->next, drm_fence_object_t, ring);
|
2007-02-15 04:10:33 -07:00
|
|
|
|
fc->last_exe_flush = (fence->sequence - 1) &
|
2006-08-21 13:02:08 -06:00
|
|
|
|
driver->sequence_mask;
|
|
|
|
|
}
|
2007-02-15 04:10:33 -07:00
|
|
|
|
diff = (sequence - fc->last_exe_flush) & driver->sequence_mask;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
if (diff >= driver->wrap_diff)
|
|
|
|
|
return;
|
2007-02-15 04:10:33 -07:00
|
|
|
|
fc->exe_flush_sequence = sequence;
|
|
|
|
|
fc->pending_exe_flush = 1;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
} else {
|
|
|
|
|
diff =
|
2007-02-15 04:10:33 -07:00
|
|
|
|
(sequence - fc->exe_flush_sequence) & driver->sequence_mask;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
if (diff < driver->wrap_diff) {
|
2007-02-15 04:10:33 -07:00
|
|
|
|
fc->exe_flush_sequence = sequence;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-02-14 06:10:10 -07:00
|
|
|
|
int drm_fence_object_signaled(drm_fence_object_t * fence,
|
2006-09-25 03:51:08 -06:00
|
|
|
|
uint32_t type)
|
2006-08-25 10:05:35 -06:00
|
|
|
|
{
|
|
|
|
|
return ((fence->signaled & type) == type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int drm_fence_object_flush(drm_device_t * dev,
|
2007-02-14 06:10:10 -07:00
|
|
|
|
drm_fence_object_t * fence,
|
|
|
|
|
uint32_t type)
|
2006-08-21 13:02:08 -06:00
|
|
|
|
{
|
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
2007-02-15 04:10:33 -07:00
|
|
|
|
drm_fence_class_manager_t *fc = &fm->class[fence->class];
|
2006-08-21 13:02:08 -06:00
|
|
|
|
drm_fence_driver_t *driver = dev->driver->fence_driver;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
if (type & ~fence->type) {
|
2006-09-18 08:02:33 -06:00
|
|
|
|
DRM_ERROR("Flush trying to extend fence type, "
|
2006-10-17 11:57:06 -06:00
|
|
|
|
"0x%x, 0x%x\n", type, fence->type);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
write_lock_irqsave(&fm->lock, flags);
|
|
|
|
|
fence->flush_mask |= type;
|
|
|
|
|
if (fence->submitted_flush == fence->signaled) {
|
2006-09-15 03:18:35 -06:00
|
|
|
|
if ((fence->type & DRM_FENCE_TYPE_EXE) &&
|
|
|
|
|
!(fence->submitted_flush & DRM_FENCE_TYPE_EXE)) {
|
2007-02-15 04:10:33 -07:00
|
|
|
|
drm_fence_flush_exe(fc, driver, fence->sequence);
|
2006-09-15 03:18:35 -06:00
|
|
|
|
fence->submitted_flush |= DRM_FENCE_TYPE_EXE;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
} else {
|
2007-02-15 04:10:33 -07:00
|
|
|
|
fc->pending_flush |= (fence->flush_mask &
|
2006-08-21 13:02:08 -06:00
|
|
|
|
~fence->submitted_flush);
|
|
|
|
|
fence->submitted_flush = fence->flush_mask;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
write_unlock_irqrestore(&fm->lock, flags);
|
2007-02-14 05:31:35 -07:00
|
|
|
|
driver->poke_flush(dev, fence->class);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-25 03:51:08 -06:00
|
|
|
|
/*
|
|
|
|
|
* Make sure old fence objects are signaled before their fence sequences are
|
|
|
|
|
* wrapped around and reused.
|
|
|
|
|
*/
|
|
|
|
|
|
2007-02-15 04:10:33 -07:00
|
|
|
|
void drm_fence_flush_old(drm_device_t * dev, uint32_t class, uint32_t sequence)
|
2006-08-21 13:02:08 -06:00
|
|
|
|
{
|
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
2007-02-15 04:10:33 -07:00
|
|
|
|
drm_fence_class_manager_t *fc = &fm->class[class];
|
2006-08-21 13:02:08 -06:00
|
|
|
|
drm_fence_driver_t *driver = dev->driver->fence_driver;
|
|
|
|
|
uint32_t old_sequence;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
drm_fence_object_t *fence;
|
|
|
|
|
uint32_t diff;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
|
|
|
read_lock_irqsave(&fm->lock, flags);
|
2007-02-15 04:10:33 -07:00
|
|
|
|
if (fc->ring.next == &fc->ring) {
|
2006-08-21 13:02:08 -06:00
|
|
|
|
read_unlock_irqrestore(&fm->lock, flags);
|
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
old_sequence = (sequence - driver->flush_diff) & driver->sequence_mask;
|
2007-02-15 04:10:33 -07:00
|
|
|
|
fence = list_entry(fc->ring.next, drm_fence_object_t, ring);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
atomic_inc(&fence->usage);
|
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
|
diff = (old_sequence - fence->sequence) & driver->sequence_mask;
|
|
|
|
|
read_unlock_irqrestore(&fm->lock, flags);
|
|
|
|
|
if (diff < driver->wrap_diff) {
|
|
|
|
|
drm_fence_object_flush(dev, fence, fence->type);
|
|
|
|
|
}
|
|
|
|
|
drm_fence_usage_deref_unlocked(dev, fence);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EXPORT_SYMBOL(drm_fence_flush_old);
|
|
|
|
|
|
2007-02-14 05:31:35 -07:00
|
|
|
|
static int drm_fence_lazy_wait(drm_device_t *dev,
|
2007-02-14 06:10:10 -07:00
|
|
|
|
drm_fence_object_t *fence,
|
2007-02-14 05:31:35 -07:00
|
|
|
|
int ignore_signals, uint32_t mask)
|
|
|
|
|
{
|
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
2007-02-15 04:10:33 -07:00
|
|
|
|
drm_fence_class_manager_t *fc = &fm->class[fence->class];
|
|
|
|
|
|
2007-02-14 05:31:35 -07:00
|
|
|
|
unsigned long _end = jiffies + 3*DRM_HZ;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
do {
|
2007-02-15 04:10:33 -07:00
|
|
|
|
DRM_WAIT_ON(ret, fc->fence_queue, 3 * DRM_HZ,
|
2007-02-14 05:31:35 -07:00
|
|
|
|
fence_signaled(dev, fence, mask, 1));
|
|
|
|
|
if (time_after_eq(jiffies, _end))
|
|
|
|
|
break;
|
|
|
|
|
} while (ret == -EINTR && ignore_signals);
|
|
|
|
|
if (time_after_eq(jiffies, _end) && (ret != 0))
|
|
|
|
|
ret = -EBUSY;
|
|
|
|
|
if (ret) {
|
|
|
|
|
if (ret == -EBUSY) {
|
|
|
|
|
DRM_ERROR("Fence timeout. "
|
|
|
|
|
"GPU lockup or fence driver was "
|
|
|
|
|
"taken down.\n");
|
|
|
|
|
}
|
|
|
|
|
return ((ret == -EINTR) ? -EAGAIN : ret);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2006-10-17 11:57:06 -06:00
|
|
|
|
int drm_fence_object_wait(drm_device_t * dev,
|
2007-02-14 06:10:10 -07:00
|
|
|
|
drm_fence_object_t * fence,
|
2006-08-25 10:05:35 -06:00
|
|
|
|
int lazy, int ignore_signals, uint32_t mask)
|
2006-08-21 13:02:08 -06:00
|
|
|
|
{
|
|
|
|
|
drm_fence_driver_t *driver = dev->driver->fence_driver;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
unsigned long _end;
|
2006-09-12 08:28:34 -06:00
|
|
|
|
int signaled;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
|
|
|
|
|
if (mask & ~fence->type) {
|
2006-09-18 08:02:33 -06:00
|
|
|
|
DRM_ERROR("Wait trying to extend fence type"
|
|
|
|
|
" 0x%08x 0x%08x\n", mask, fence->type);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fence_signaled(dev, fence, mask, 0))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
_end = jiffies + 3 * DRM_HZ;
|
|
|
|
|
|
|
|
|
|
drm_fence_object_flush(dev, fence, mask);
|
2006-09-12 08:28:34 -06:00
|
|
|
|
|
2006-08-21 13:02:08 -06:00
|
|
|
|
if (lazy && driver->lazy_capable) {
|
2006-09-12 08:28:34 -06:00
|
|
|
|
|
2007-02-14 05:31:35 -07:00
|
|
|
|
ret = drm_fence_lazy_wait(dev, fence, ignore_signals, mask);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
2006-09-12 08:28:34 -06:00
|
|
|
|
|
2007-02-14 05:31:35 -07:00
|
|
|
|
} else {
|
2006-09-12 08:28:34 -06:00
|
|
|
|
|
2007-02-14 05:31:35 -07:00
|
|
|
|
if (driver->has_irq(dev, fence->class,
|
|
|
|
|
DRM_FENCE_TYPE_EXE)) {
|
|
|
|
|
ret = drm_fence_lazy_wait(dev, fence, ignore_signals,
|
|
|
|
|
DRM_FENCE_TYPE_EXE);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2006-09-12 08:28:34 -06:00
|
|
|
|
|
2007-02-14 05:31:35 -07:00
|
|
|
|
if (driver->has_irq(dev, fence->class,
|
|
|
|
|
mask & ~DRM_FENCE_TYPE_EXE)) {
|
|
|
|
|
ret = drm_fence_lazy_wait(dev, fence, ignore_signals,
|
|
|
|
|
mask);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-09-12 08:28:34 -06:00
|
|
|
|
if (fence_signaled(dev, fence, mask, 0))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-02-14 05:31:35 -07:00
|
|
|
|
DRM_ERROR("Busy wait\n");
|
2006-09-15 03:18:35 -06:00
|
|
|
|
/*
|
|
|
|
|
* Avoid kernel-space busy-waits.
|
|
|
|
|
*/
|
|
|
|
|
#if 1
|
|
|
|
|
if (!ignore_signals)
|
2006-10-17 11:57:06 -06:00
|
|
|
|
return -EAGAIN;
|
2006-09-15 03:18:35 -06:00
|
|
|
|
#endif
|
2006-10-17 11:57:06 -06:00
|
|
|
|
do {
|
2006-09-15 03:18:35 -06:00
|
|
|
|
schedule();
|
2006-09-12 08:28:34 -06:00
|
|
|
|
signaled = fence_signaled(dev, fence, mask, 1);
|
|
|
|
|
} while (!signaled && !time_after_eq(jiffies, _end));
|
|
|
|
|
|
|
|
|
|
if (!signaled)
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
2006-08-21 13:02:08 -06:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int drm_fence_object_emit(drm_device_t * dev, drm_fence_object_t * fence,
|
2007-02-15 04:10:33 -07:00
|
|
|
|
uint32_t fence_flags, uint32_t class, uint32_t type)
|
2006-08-21 13:02:08 -06:00
|
|
|
|
{
|
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
|
|
|
|
drm_fence_driver_t *driver = dev->driver->fence_driver;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
uint32_t sequence;
|
2006-09-15 08:47:09 -06:00
|
|
|
|
uint32_t native_type;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
drm_fence_unring(dev, &fence->ring);
|
2007-02-15 04:10:33 -07:00
|
|
|
|
ret = driver->emit(dev, class, fence_flags, &sequence, &native_type);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
write_lock_irqsave(&fm->lock, flags);
|
2007-02-15 04:10:33 -07:00
|
|
|
|
fence->class = class;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
fence->type = type;
|
|
|
|
|
fence->flush_mask = 0x00;
|
|
|
|
|
fence->submitted_flush = 0x00;
|
|
|
|
|
fence->signaled = 0x00;
|
|
|
|
|
fence->sequence = sequence;
|
2006-09-15 08:47:09 -06:00
|
|
|
|
fence->native_type = native_type;
|
2007-02-15 04:10:33 -07:00
|
|
|
|
list_add_tail(&fence->ring, &fm->class[class].ring);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
write_unlock_irqrestore(&fm->lock, flags);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-02-15 04:10:33 -07:00
|
|
|
|
static int drm_fence_object_init(drm_device_t * dev, uint32_t class,
|
|
|
|
|
uint32_t type,
|
2006-09-15 08:47:09 -06:00
|
|
|
|
uint32_t fence_flags,
|
|
|
|
|
drm_fence_object_t * fence)
|
2006-08-21 13:02:08 -06:00
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
|
|
|
atomic_set(&fence->usage, 1);
|
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
|
|
|
|
|
|
write_lock_irqsave(&fm->lock, flags);
|
|
|
|
|
INIT_LIST_HEAD(&fence->ring);
|
2007-02-15 04:10:33 -07:00
|
|
|
|
fence->class = class;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
fence->type = type;
|
|
|
|
|
fence->flush_mask = 0;
|
|
|
|
|
fence->submitted_flush = 0;
|
|
|
|
|
fence->signaled = 0;
|
|
|
|
|
fence->sequence = 0;
|
|
|
|
|
write_unlock_irqrestore(&fm->lock, flags);
|
2006-09-15 08:47:09 -06:00
|
|
|
|
if (fence_flags & DRM_FENCE_FLAG_EMIT) {
|
2007-02-15 04:10:33 -07:00
|
|
|
|
ret = drm_fence_object_emit(dev, fence, fence_flags,
|
|
|
|
|
fence->class, type);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-12 08:28:34 -06:00
|
|
|
|
int drm_fence_add_user_object(drm_file_t * priv, drm_fence_object_t * fence,
|
2006-09-05 10:00:25 -06:00
|
|
|
|
int shareable)
|
2006-08-21 13:02:08 -06:00
|
|
|
|
{
|
|
|
|
|
drm_device_t *dev = priv->head->dev;
|
|
|
|
|
int ret;
|
2006-09-05 10:00:25 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
|
|
|
ret = drm_add_user_object(priv, &fence->base, shareable);
|
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
fence->base.type = drm_fence_type;
|
|
|
|
|
fence->base.remove = &drm_fence_object_destroy;
|
2006-09-12 09:39:44 -06:00
|
|
|
|
DRM_DEBUG("Fence 0x%08lx created\n", fence->base.hash.key);
|
2006-09-05 10:00:25 -06:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2006-09-12 08:28:34 -06:00
|
|
|
|
|
2006-09-05 10:00:25 -06:00
|
|
|
|
EXPORT_SYMBOL(drm_fence_add_user_object);
|
|
|
|
|
|
2007-02-15 04:10:33 -07:00
|
|
|
|
int drm_fence_object_create(drm_device_t * dev, uint32_t class, uint32_t type,
|
2006-09-15 08:47:09 -06:00
|
|
|
|
unsigned flags, drm_fence_object_t ** c_fence)
|
2006-09-05 10:00:25 -06:00
|
|
|
|
{
|
2006-08-21 13:02:08 -06:00
|
|
|
|
drm_fence_object_t *fence;
|
2006-09-05 10:00:25 -06:00
|
|
|
|
int ret;
|
2006-09-25 03:51:08 -06:00
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
|
2006-12-15 04:37:24 -07:00
|
|
|
|
fence = drm_ctl_alloc(sizeof(*fence), DRM_MEM_FENCE);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
if (!fence)
|
|
|
|
|
return -ENOMEM;
|
2007-02-15 04:10:33 -07:00
|
|
|
|
ret = drm_fence_object_init(dev, class, type, flags, fence);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
if (ret) {
|
|
|
|
|
drm_fence_usage_deref_unlocked(dev, fence);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2006-09-12 08:28:34 -06:00
|
|
|
|
*c_fence = fence;
|
2006-09-25 03:51:08 -06:00
|
|
|
|
atomic_inc(&fm->count);
|
|
|
|
|
|
2006-08-21 13:02:08 -06:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2006-09-05 10:00:25 -06:00
|
|
|
|
|
2006-09-12 08:28:34 -06:00
|
|
|
|
EXPORT_SYMBOL(drm_fence_object_create);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
|
|
|
|
|
void drm_fence_manager_init(drm_device_t * dev)
|
|
|
|
|
{
|
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
2007-02-15 04:10:33 -07:00
|
|
|
|
drm_fence_class_manager_t *class;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
drm_fence_driver_t *fed = dev->driver->fence_driver;
|
|
|
|
|
int i;
|
|
|
|
|
|
2007-02-15 04:10:33 -07:00
|
|
|
|
|
2006-08-21 13:02:08 -06:00
|
|
|
|
fm->lock = RW_LOCK_UNLOCKED;
|
2006-09-25 03:51:08 -06:00
|
|
|
|
write_lock(&fm->lock);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
fm->initialized = 0;
|
2007-02-15 04:10:33 -07:00
|
|
|
|
if (!fed)
|
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
|
|
fm->initialized = 1;
|
|
|
|
|
fm->num_classes = fed->num_classes;
|
|
|
|
|
BUG_ON(fm->num_classes > _DRM_FENCE_CLASSES);
|
|
|
|
|
|
|
|
|
|
for (i=0; i<fm->num_classes; ++i) {
|
|
|
|
|
class = &fm->class[i];
|
|
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&class->ring);
|
|
|
|
|
class->pending_flush = 0;
|
|
|
|
|
DRM_INIT_WAITQUEUE(&class->fence_queue);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
}
|
2007-02-15 04:10:33 -07:00
|
|
|
|
|
|
|
|
|
atomic_set(&fm->count, 0);
|
|
|
|
|
out_unlock:
|
2006-09-25 03:51:08 -06:00
|
|
|
|
write_unlock(&fm->lock);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void drm_fence_manager_takedown(drm_device_t * dev)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drm_fence_object_t *drm_lookup_fence_object(drm_file_t * priv, uint32_t handle)
|
|
|
|
|
{
|
|
|
|
|
drm_device_t *dev = priv->head->dev;
|
|
|
|
|
drm_user_object_t *uo;
|
|
|
|
|
drm_fence_object_t *fence;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
|
|
|
uo = drm_lookup_user_object(priv, handle);
|
|
|
|
|
if (!uo || (uo->type != drm_fence_type)) {
|
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
fence = drm_user_object_entry(uo, drm_fence_object_t, base);
|
|
|
|
|
atomic_inc(&fence->usage);
|
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
|
return fence;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int drm_fence_ioctl(DRM_IOCTL_ARGS)
|
|
|
|
|
{
|
|
|
|
|
DRM_DEVICE;
|
|
|
|
|
int ret;
|
|
|
|
|
drm_fence_manager_t *fm = &dev->fm;
|
|
|
|
|
drm_fence_arg_t arg;
|
|
|
|
|
drm_fence_object_t *fence;
|
|
|
|
|
drm_user_object_t *uo;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
|
|
if (!fm->initialized) {
|
|
|
|
|
DRM_ERROR("The DRM driver does not support fencing.\n");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DRM_COPY_FROM_USER_IOCTL(arg, (void __user *)data, sizeof(arg));
|
|
|
|
|
switch (arg.op) {
|
2006-09-05 10:00:25 -06:00
|
|
|
|
case drm_fence_create:
|
|
|
|
|
if (arg.flags & DRM_FENCE_FLAG_EMIT)
|
|
|
|
|
LOCK_TEST_WITH_RETURN(dev, filp);
|
2007-02-15 04:10:33 -07:00
|
|
|
|
ret = drm_fence_object_create(dev, arg.class,
|
|
|
|
|
arg.type, arg.flags, &fence);
|
2006-09-05 10:00:25 -06:00
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
2006-09-12 08:28:34 -06:00
|
|
|
|
ret = drm_fence_add_user_object(priv, fence,
|
|
|
|
|
arg.flags &
|
2006-09-05 10:00:25 -06:00
|
|
|
|
DRM_FENCE_FLAG_SHAREABLE);
|
|
|
|
|
if (ret) {
|
|
|
|
|
drm_fence_usage_deref_unlocked(dev, fence);
|
|
|
|
|
return ret;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
}
|
2006-09-05 10:00:25 -06:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* usage > 0. No need to lock dev->struct_mutex;
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
atomic_inc(&fence->usage);
|
|
|
|
|
arg.handle = fence->base.hash.key;
|
|
|
|
|
break;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
case drm_fence_destroy:
|
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
|
|
|
uo = drm_lookup_user_object(priv, arg.handle);
|
|
|
|
|
if (!uo || (uo->type != drm_fence_type) || uo->owner != priv) {
|
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
ret = drm_remove_user_object(priv, uo);
|
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
|
return ret;
|
|
|
|
|
case drm_fence_reference:
|
|
|
|
|
ret =
|
|
|
|
|
drm_user_object_ref(priv, arg.handle, drm_fence_type, &uo);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
fence = drm_lookup_fence_object(priv, arg.handle);
|
|
|
|
|
break;
|
|
|
|
|
case drm_fence_unreference:
|
|
|
|
|
ret = drm_user_object_unref(priv, arg.handle, drm_fence_type);
|
|
|
|
|
return ret;
|
|
|
|
|
case drm_fence_signaled:
|
|
|
|
|
fence = drm_lookup_fence_object(priv, arg.handle);
|
|
|
|
|
if (!fence)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
break;
|
|
|
|
|
case drm_fence_flush:
|
|
|
|
|
fence = drm_lookup_fence_object(priv, arg.handle);
|
|
|
|
|
if (!fence)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
ret = drm_fence_object_flush(dev, fence, arg.type);
|
|
|
|
|
break;
|
|
|
|
|
case drm_fence_wait:
|
|
|
|
|
fence = drm_lookup_fence_object(priv, arg.handle);
|
|
|
|
|
if (!fence)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
ret =
|
|
|
|
|
drm_fence_object_wait(dev, fence,
|
|
|
|
|
arg.flags & DRM_FENCE_FLAG_WAIT_LAZY,
|
2006-09-12 08:28:34 -06:00
|
|
|
|
0, arg.type);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
break;
|
|
|
|
|
case drm_fence_emit:
|
|
|
|
|
LOCK_TEST_WITH_RETURN(dev, filp);
|
|
|
|
|
fence = drm_lookup_fence_object(priv, arg.handle);
|
|
|
|
|
if (!fence)
|
|
|
|
|
return -EINVAL;
|
2007-02-15 04:10:33 -07:00
|
|
|
|
ret = drm_fence_object_emit(dev, fence, arg.flags, arg.class,
|
|
|
|
|
arg.type);
|
2006-08-21 13:02:08 -06:00
|
|
|
|
break;
|
2006-09-05 10:00:25 -06:00
|
|
|
|
case drm_fence_buffers:
|
|
|
|
|
if (!dev->bm.initialized) {
|
|
|
|
|
DRM_ERROR("Buffer object manager is not initialized\n");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
LOCK_TEST_WITH_RETURN(dev, filp);
|
2006-10-17 11:57:06 -06:00
|
|
|
|
ret = drm_fence_buffer_objects(priv, NULL, arg.flags,
|
2006-09-15 08:47:09 -06:00
|
|
|
|
NULL, &fence);
|
2006-09-12 08:28:34 -06:00
|
|
|
|
if (ret)
|
2006-09-05 10:00:25 -06:00
|
|
|
|
return ret;
|
2006-09-12 08:28:34 -06:00
|
|
|
|
ret = drm_fence_add_user_object(priv, fence,
|
|
|
|
|
arg.flags &
|
2006-09-05 10:00:25 -06:00
|
|
|
|
DRM_FENCE_FLAG_SHAREABLE);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
atomic_inc(&fence->usage);
|
|
|
|
|
arg.handle = fence->base.hash.key;
|
2006-09-12 08:28:34 -06:00
|
|
|
|
break;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
default:
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
read_lock_irqsave(&fm->lock, flags);
|
2006-08-22 02:44:09 -06:00
|
|
|
|
arg.class = fence->class;
|
2006-08-21 13:02:08 -06:00
|
|
|
|
arg.type = fence->type;
|
|
|
|
|
arg.signaled = fence->signaled;
|
|
|
|
|
read_unlock_irqrestore(&fm->lock, flags);
|
|
|
|
|
drm_fence_usage_deref_unlocked(dev, fence);
|
|
|
|
|
|
|
|
|
|
DRM_COPY_TO_USER_IOCTL((void __user *)data, arg, sizeof(arg));
|
|
|
|
|
return ret;
|
|
|
|
|
}
|