modesetting-101: rename modeflags, as to avoid conflicts with the xorg definitions
[platform/upstream/libdrm.git] / linux-core / drm_fence.c
index f656340..7c78e09 100644 (file)
@@ -1,8 +1,8 @@
 /**************************************************************************
- * 
- * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA
+ *
+ * Copyright (c) 2006-2007 Tungsten Graphics, Inc., Cedar Park, TX., USA
  * 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
  * 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:
- * 
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
  * 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 
+ * 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.
  *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- * 
- * 
  **************************************************************************/
 /*
  * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
 
 #include "drmP.h"
 
+
+/*
+ * Convenience function to be called by fence::wait methods that
+ * need polling.
+ */
+
+int drm_fence_wait_polling(struct drm_fence_object *fence, int lazy,
+                          int interruptible, uint32_t mask, 
+                          unsigned long end_jiffies)
+{
+       struct drm_device *dev = fence->dev;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_class_manager *fc = &fm->fence_class[fence->fence_class];
+       uint32_t count = 0;
+       int ret;
+
+       DECLARE_WAITQUEUE(entry, current);
+       add_wait_queue(&fc->fence_queue, &entry);
+
+       ret = 0;
+       
+       for (;;) {
+               __set_current_state((interruptible) ? 
+                                   TASK_INTERRUPTIBLE :
+                                   TASK_UNINTERRUPTIBLE);
+               if (drm_fence_object_signaled(fence, mask))
+                       break;
+               if (time_after_eq(jiffies, end_jiffies)) {
+                       ret = -EBUSY;
+                       break;
+               }
+               if (lazy)
+                       schedule_timeout(1);
+               else if ((++count & 0x0F) == 0){
+                       __set_current_state(TASK_RUNNING);
+                       schedule();
+                       __set_current_state((interruptible) ? 
+                                           TASK_INTERRUPTIBLE :
+                                           TASK_UNINTERRUPTIBLE);
+               }                       
+               if (interruptible && signal_pending(current)) {
+                       ret = -EAGAIN;
+                       break;
+               }
+       }
+       __set_current_state(TASK_RUNNING);
+       remove_wait_queue(&fc->fence_queue, &entry);
+       return ret;
+}
+EXPORT_SYMBOL(drm_fence_wait_polling);
+
 /*
  * Typically called by the IRQ handler.
  */
 
-void drm_fence_handler(drm_device_t * dev, uint32_t sequence, uint32_t type)
+void drm_fence_handler(struct drm_device *dev, uint32_t fence_class,
+                      uint32_t sequence, uint32_t type, uint32_t error)
 {
        int wake = 0;
        uint32_t diff;
-       uint32_t relevant;
-       drm_fence_manager_t *fm = &dev->fm;
-       drm_fence_driver_t *driver = dev->driver->fence_driver;
-       struct list_head *list, *prev;
-       drm_fence_object_t *fence;
+       uint32_t relevant_type;
+       uint32_t new_type;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_class_manager *fc = &fm->fence_class[fence_class];
+       struct drm_fence_driver *driver = dev->driver->fence_driver;
+       struct list_head *head;
+       struct drm_fence_object *fence, *next;
        int found = 0;
 
-       if (list_empty(&fm->ring))
+       if (list_empty(&fc->ring))
                return;
 
-       list_for_each_entry(fence, &fm->ring, ring) {
+       list_for_each_entry(fence, &fc->ring, ring) {
                diff = (sequence - fence->sequence) & driver->sequence_mask;
                if (diff > driver->wrap_diff) {
                        found = 1;
@@ -57,49 +110,75 @@ void drm_fence_handler(drm_device_t * dev, uint32_t sequence, uint32_t type)
                }
        }
 
-       list = (found) ? fence->ring.prev : fm->ring.prev;
-       prev = list->prev;
-
-       for (; list != &fm->ring; list = prev, prev = list->prev) {
-               fence = list_entry(list, drm_fence_object_t, ring);
+       fc->waiting_types &= ~type;
+       head = (found) ? &fence->ring : &fc->ring;
 
-               type |= fence->native_type;
-               relevant = type & fence->type;
+       list_for_each_entry_safe_reverse(fence, next, head, ring) {
+               if (&fence->ring == &fc->ring)
+                       break;
 
-               if ((fence->signaled | relevant) != fence->signaled) {
-                       fence->signaled |= relevant;
-                       DRM_DEBUG("Fence 0x%08lx signaled 0x%08x\n",
-                                 fence->base.hash.key, fence->signaled);
-                       fence->submitted_flush |= relevant;
+               if (error) {
+                       fence->error = error;
+                       fence->signaled_types = fence->type;
+                       list_del_init(&fence->ring);
                        wake = 1;
+                       break;
                }
 
-               relevant = fence->flush_mask &
-                   ~(fence->signaled | fence->submitted_flush);
+               if (type & DRM_FENCE_TYPE_EXE)
+                       type |= fence->native_types;
 
-               if (relevant) {
-                       fm->pending_flush |= relevant;
-                       fence->submitted_flush = fence->flush_mask;
+               relevant_type = type & fence->type;
+               new_type = (fence->signaled_types | relevant_type) ^
+                       fence->signaled_types;
+
+               if (new_type) {
+                       fence->signaled_types |= new_type;
+                       DRM_DEBUG("Fence 0x%08lx signaled 0x%08x\n",
+                                 fence->base.hash.key, fence->signaled_types);
+
+                       if (driver->needed_flush)
+                               fc->pending_flush |= driver->needed_flush(fence);
+
+                       if (new_type & fence->waiting_types)
+                               wake = 1;
                }
 
-               if (!(fence->type & ~fence->signaled)) {
+               fc->waiting_types |= fence->waiting_types & ~fence->signaled_types;
+
+               if (!(fence->type & ~fence->signaled_types)) {
                        DRM_DEBUG("Fence completely signaled 0x%08lx\n",
                                  fence->base.hash.key);
                        list_del_init(&fence->ring);
                }
-
        }
 
-       if (wake) {
-               DRM_WAKEUP(&fm->fence_queue);
+       /*
+        * Reinstate lost waiting types.
+        */
+
+       if ((fc->waiting_types & type) != type) {
+               head = head->prev;
+               list_for_each_entry(fence, head, ring) {
+                       if (&fence->ring == &fc->ring)
+                               break;
+                       diff = (fc->highest_waiting_sequence - fence->sequence) &
+                               driver->sequence_mask;
+                       if (diff > driver->wrap_diff)
+                               break;
+                       
+                       fc->waiting_types |= fence->waiting_types & ~fence->signaled_types;
+               }
        }
-}
 
+       if (wake) 
+               wake_up_all(&fc->fence_queue);
+}
 EXPORT_SYMBOL(drm_fence_handler);
 
-static void drm_fence_unring(drm_device_t * dev, struct list_head *ring)
+static void drm_fence_unring(struct drm_device *dev, struct list_head *ring)
 {
-       drm_fence_manager_t *fm = &dev->fm;
+       struct drm_fence_manager *fm = &dev->fm;
        unsigned long flags;
 
        write_lock_irqsave(&fm->lock, flags);
@@ -107,115 +186,107 @@ static void drm_fence_unring(drm_device_t * dev, struct list_head *ring)
        write_unlock_irqrestore(&fm->lock, flags);
 }
 
-void drm_fence_usage_deref_locked(drm_device_t * dev,
-                                 drm_fence_object_t * fence)
+void drm_fence_usage_deref_locked(struct drm_fence_object **fence)
 {
-       drm_fence_manager_t *fm = &dev->fm;
-
-       if (atomic_dec_and_test(&fence->usage)) {
-               drm_fence_unring(dev, &fence->ring);
+       struct drm_fence_object *tmp_fence = *fence;
+       struct drm_device *dev = tmp_fence->dev;
+       struct drm_fence_manager *fm = &dev->fm;
+
+       DRM_ASSERT_LOCKED(&dev->struct_mutex);
+       *fence = NULL;
+       if (atomic_dec_and_test(&tmp_fence->usage)) {
+               drm_fence_unring(dev, &tmp_fence->ring);
                DRM_DEBUG("Destroyed a fence object 0x%08lx\n",
-                         fence->base.hash.key);
+                         tmp_fence->base.hash.key);
                atomic_dec(&fm->count);
-               drm_ctl_cache_free(drm_cache.fence_object, sizeof(*fence),
-                                  fence);
+               BUG_ON(!list_empty(&tmp_fence->base.list));
+               drm_ctl_free(tmp_fence, sizeof(*tmp_fence), DRM_MEM_FENCE);
        }
 }
+EXPORT_SYMBOL(drm_fence_usage_deref_locked);
 
-void drm_fence_usage_deref_unlocked(drm_device_t * dev,
-                                   drm_fence_object_t * fence)
+void drm_fence_usage_deref_unlocked(struct drm_fence_object **fence)
 {
-       drm_fence_manager_t *fm = &dev->fm;
+       struct drm_fence_object *tmp_fence = *fence;
+       struct drm_device *dev = tmp_fence->dev;
+       struct drm_fence_manager *fm = &dev->fm;
 
-       if (atomic_dec_and_test(&fence->usage)) {
+       *fence = NULL;
+       if (atomic_dec_and_test(&tmp_fence->usage)) {
                mutex_lock(&dev->struct_mutex);
-               if (atomic_read(&fence->usage) == 0) {
-                       drm_fence_unring(dev, &fence->ring);
+               if (atomic_read(&tmp_fence->usage) == 0) {
+                       drm_fence_unring(dev, &tmp_fence->ring);
                        atomic_dec(&fm->count);
-                       drm_ctl_cache_free(drm_cache.fence_object,
-                                          sizeof(*fence), fence);
+                       BUG_ON(!list_empty(&tmp_fence->base.list));
+                       drm_ctl_free(tmp_fence, sizeof(*tmp_fence), DRM_MEM_FENCE);
                }
                mutex_unlock(&dev->struct_mutex);
        }
 }
+EXPORT_SYMBOL(drm_fence_usage_deref_unlocked);
 
-static void drm_fence_object_destroy(drm_file_t * priv,
-                                    drm_user_object_t * base)
+struct drm_fence_object
+*drm_fence_reference_locked(struct drm_fence_object *src)
 {
-       drm_device_t *dev = priv->head->dev;
-       drm_fence_object_t *fence =
-           drm_user_object_entry(base, drm_fence_object_t, base);
+       DRM_ASSERT_LOCKED(&src->dev->struct_mutex);
 
-       drm_fence_usage_deref_locked(dev, fence);
+       atomic_inc(&src->usage);
+       return src;
 }
 
-static int fence_signaled(drm_device_t * dev, volatile
-                         drm_fence_object_t * fence,
-                         uint32_t mask, int poke_flush)
+void drm_fence_reference_unlocked(struct drm_fence_object **dst,
+                                 struct drm_fence_object *src)
 {
-       unsigned long flags;
-       int signaled;
-       drm_fence_manager_t *fm = &dev->fm;
-       drm_fence_driver_t *driver = dev->driver->fence_driver;
-
-       if (poke_flush)
-               driver->poke_flush(dev);
-       read_lock_irqsave(&fm->lock, flags);
-       signaled =
-           (fence->type & mask & fence->signaled) == (fence->type & mask);
-       read_unlock_irqrestore(&fm->lock, flags);
-
-       return signaled;
+       mutex_lock(&src->dev->struct_mutex);
+       *dst = src;
+       atomic_inc(&src->usage);
+       mutex_unlock(&src->dev->struct_mutex);
 }
+EXPORT_SYMBOL(drm_fence_reference_unlocked);
 
-static void drm_fence_flush_exe(drm_fence_manager_t * fm,
-                               drm_fence_driver_t * driver, uint32_t sequence)
+static void drm_fence_object_destroy(struct drm_file *priv,
+                                    struct drm_user_object *base)
 {
-       uint32_t diff;
+       struct drm_fence_object *fence =
+           drm_user_object_entry(base, struct drm_fence_object, base);
 
-       if (!fm->pending_exe_flush) {
-               volatile struct list_head *list;
-
-               /*
-                * Last_exe_flush is invalid. Find oldest sequence.
-                */
-
-/*             list = fm->fence_types[_DRM_FENCE_TYPE_EXE];*/
-               list = &fm->ring;
-               if (list->next == &fm->ring) {
-                       return;
-               } else {
-                       drm_fence_object_t *fence =
-                           list_entry(list->next, drm_fence_object_t, ring);
-                       fm->last_exe_flush = (fence->sequence - 1) &
-                           driver->sequence_mask;
-               }
-               diff = (sequence - fm->last_exe_flush) & driver->sequence_mask;
-               if (diff >= driver->wrap_diff)
-                       return;
-               fm->exe_flush_sequence = sequence;
-               fm->pending_exe_flush = 1;
-       } else {
-               diff =
-                   (sequence - fm->exe_flush_sequence) & driver->sequence_mask;
-               if (diff < driver->wrap_diff) {
-                       fm->exe_flush_sequence = sequence;
-               }
-       }
+       drm_fence_usage_deref_locked(&fence);
 }
 
-int drm_fence_object_signaled(volatile drm_fence_object_t * fence,
-                             uint32_t type)
+int drm_fence_object_signaled(struct drm_fence_object *fence, uint32_t mask)
 {
-       return ((fence->signaled & type) == type);
+       unsigned long flags;
+       int signaled;
+       struct drm_device *dev = fence->dev;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_driver *driver = dev->driver->fence_driver;
+       
+       mask &= fence->type;
+       read_lock_irqsave(&fm->lock, flags);
+       signaled = (mask & fence->signaled_types) == mask;
+       read_unlock_irqrestore(&fm->lock, flags);
+       if (!signaled && driver->poll) {
+               write_lock_irqsave(&fm->lock, flags);
+               driver->poll(dev, fence->fence_class, mask);
+               signaled = (mask & fence->signaled_types) == mask;
+               write_unlock_irqrestore(&fm->lock, flags);
+       }
+       return signaled;
 }
+EXPORT_SYMBOL(drm_fence_object_signaled);
+
 
-int drm_fence_object_flush(drm_device_t * dev,
-                          volatile drm_fence_object_t * fence, uint32_t type)
+int drm_fence_object_flush(struct drm_fence_object *fence,
+                          uint32_t type)
 {
-       drm_fence_manager_t *fm = &dev->fm;
-       drm_fence_driver_t *driver = dev->driver->fence_driver;
-       unsigned long flags;
+       struct drm_device *dev = fence->dev;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_class_manager *fc = &fm->fence_class[fence->fence_class];
+       struct drm_fence_driver *driver = dev->driver->fence_driver;
+       unsigned long irq_flags;
+       uint32_t saved_pending_flush;
+       uint32_t diff;
+       int call_flush;
 
        if (type & ~fence->type) {
                DRM_ERROR("Flush trying to extend fence type, "
@@ -223,176 +294,177 @@ int drm_fence_object_flush(drm_device_t * dev,
                return -EINVAL;
        }
 
-       write_lock_irqsave(&fm->lock, flags);
-       fence->flush_mask |= type;
-       if (fence->submitted_flush == fence->signaled) {
-               if ((fence->type & DRM_FENCE_TYPE_EXE) &&
-                   !(fence->submitted_flush & DRM_FENCE_TYPE_EXE)) {
-                       drm_fence_flush_exe(fm, driver, fence->sequence);
-                       fence->submitted_flush |= DRM_FENCE_TYPE_EXE;
-               } else {
-                       fm->pending_flush |= (fence->flush_mask &
-                                             ~fence->submitted_flush);
-                       fence->submitted_flush = fence->flush_mask;
-               }
-       }
-       write_unlock_irqrestore(&fm->lock, flags);
-       driver->poke_flush(dev);
+       write_lock_irqsave(&fm->lock, irq_flags);
+       fence->waiting_types |= type;
+       fc->waiting_types |= fence->waiting_types;
+       diff = (fence->sequence - fc->highest_waiting_sequence) & 
+               driver->sequence_mask;
+
+       if (diff < driver->wrap_diff)
+               fc->highest_waiting_sequence = fence->sequence;
+
+       /*
+        * fence->waiting_types has changed. Determine whether
+        * we need to initiate some kind of flush as a result of this.
+        */
+
+       saved_pending_flush = fc->pending_flush;
+       if (driver->needed_flush) 
+               fc->pending_flush |= driver->needed_flush(fence);
+
+       if (driver->poll)
+               driver->poll(dev, fence->fence_class, fence->waiting_types);
+
+       call_flush = fc->pending_flush;
+       write_unlock_irqrestore(&fm->lock, irq_flags);
+
+       if (call_flush && driver->flush)
+               driver->flush(dev, fence->fence_class);
+
        return 0;
 }
+EXPORT_SYMBOL(drm_fence_object_flush);
 
 /*
  * Make sure old fence objects are signaled before their fence sequences are
  * wrapped around and reused.
  */
 
-void drm_fence_flush_old(drm_device_t * dev, uint32_t sequence)
+void drm_fence_flush_old(struct drm_device *dev, uint32_t fence_class,
+                        uint32_t sequence)
 {
-       drm_fence_manager_t *fm = &dev->fm;
-       drm_fence_driver_t *driver = dev->driver->fence_driver;
-       uint32_t old_sequence;
-       unsigned long flags;
-       drm_fence_object_t *fence;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_class_manager *fc = &fm->fence_class[fence_class];
+       struct drm_fence_object *fence;
+       unsigned long irq_flags;
+       struct drm_fence_driver *driver = dev->driver->fence_driver;
+       int call_flush;
+
        uint32_t diff;
 
-       mutex_lock(&dev->struct_mutex);
-       read_lock_irqsave(&fm->lock, flags);
-       if (fm->ring.next == &fm->ring) {
-               read_unlock_irqrestore(&fm->lock, flags);
-               mutex_unlock(&dev->struct_mutex);
-               return;
-       }
-       old_sequence = (sequence - driver->flush_diff) & driver->sequence_mask;
-       fence = list_entry(fm->ring.next, drm_fence_object_t, ring);
-       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);
-}
+       write_lock_irqsave(&fm->lock, irq_flags);
+
+       list_for_each_entry_reverse(fence, &fc->ring, ring) {
+               diff = (sequence - fence->sequence) & driver->sequence_mask;
+               if (diff <= driver->flush_diff)
+                       break;
+       
+               fence->waiting_types = fence->type;
+               fc->waiting_types |= fence->type;
+
+               if (driver->needed_flush)
+                       fc->pending_flush |= driver->needed_flush(fence);
+       }       
+       
+       if (driver->poll)
+               driver->poll(dev, fence_class, fc->waiting_types);
 
+       call_flush = fc->pending_flush;
+       write_unlock_irqrestore(&fm->lock, irq_flags);
+
+       if (call_flush && driver->flush)
+               driver->flush(dev, fence->fence_class);
+
+       /*
+        * FIXME: Shold we implement a wait here for really old fences?
+        */
+
+}
 EXPORT_SYMBOL(drm_fence_flush_old);
 
-int drm_fence_object_wait(drm_device_t * dev,
-                         volatile drm_fence_object_t * fence,
+int drm_fence_object_wait(struct drm_fence_object *fence,
                          int lazy, int ignore_signals, uint32_t mask)
 {
-       drm_fence_manager_t *fm = &dev->fm;
-       drm_fence_driver_t *driver = dev->driver->fence_driver;
+       struct drm_device *dev = fence->dev;
+       struct drm_fence_driver *driver = dev->driver->fence_driver;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_class_manager *fc = &fm->fence_class[fence->fence_class];
        int ret = 0;
-       unsigned long _end;
-       int signaled;
+       unsigned long _end = 3 * DRM_HZ;
 
        if (mask & ~fence->type) {
                DRM_ERROR("Wait trying to extend fence type"
                          " 0x%08x 0x%08x\n", mask, fence->type);
+               BUG();
                return -EINVAL;
        }
 
-       if (fence_signaled(dev, fence, mask, 0))
-               return 0;
+       if (driver->wait)
+               return driver->wait(fence, lazy, !ignore_signals, mask);
 
-       _end = jiffies + 3 * DRM_HZ;
 
-       drm_fence_object_flush(dev, fence, mask);
+       drm_fence_object_flush(fence, mask);
+       if (driver->has_irq(dev, fence->fence_class, mask)) {
+               if (!ignore_signals)
+                       ret = wait_event_interruptible_timeout
+                               (fc->fence_queue, 
+                                drm_fence_object_signaled(fence, mask), 
+                                3 * DRM_HZ);
+               else 
+                       ret = wait_event_timeout
+                               (fc->fence_queue, 
+                                drm_fence_object_signaled(fence, mask), 
+                                3 * DRM_HZ);
 
-       if (lazy && driver->lazy_capable) {
+               if (unlikely(ret == -ERESTARTSYS))
+                       return -EAGAIN;
 
-               do {
-                       DRM_WAIT_ON(ret, fm->fence_queue, 3 * DRM_HZ,
-                                   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);
-               }
-       } else if ((fence->class == 0) && (mask & DRM_FENCE_TYPE_EXE) &&
-                  driver->lazy_capable) {
-
-               /*
-                * We use IRQ wait for EXE fence if available to gain 
-                * CPU in some cases.
-                */
-
-               do {
-                       DRM_WAIT_ON(ret, fm->fence_queue, 3 * DRM_HZ,
-                                   fence_signaled(dev, fence,
-                                                  DRM_FENCE_TYPE_EXE, 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)
-                       return ((ret == -EINTR) ? -EAGAIN : ret);
-       }
+               if (unlikely(ret == 0))
+                       return -EBUSY;
 
-       if (fence_signaled(dev, fence, mask, 0))
                return 0;
+       }
 
-       /*
-        * Avoid kernel-space busy-waits.
-        */
-#if 1
-       if (!ignore_signals)
-               return -EAGAIN;
-#endif
-       do {
-               schedule();
-               signaled = fence_signaled(dev, fence, mask, 1);
-       } while (!signaled && !time_after_eq(jiffies, _end));
-
-       if (!signaled)
-               return -EBUSY;
-
-       return 0;
+       return drm_fence_wait_polling(fence, lazy, !ignore_signals, mask,
+                                     _end);
 }
+EXPORT_SYMBOL(drm_fence_object_wait);
+
+
 
-int drm_fence_object_emit(drm_device_t * dev, drm_fence_object_t * fence,
-                         uint32_t fence_flags, uint32_t type)
+int drm_fence_object_emit(struct drm_fence_object *fence, uint32_t fence_flags,
+                         uint32_t fence_class, uint32_t type)
 {
-       drm_fence_manager_t *fm = &dev->fm;
-       drm_fence_driver_t *driver = dev->driver->fence_driver;
+       struct drm_device *dev = fence->dev;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_driver *driver = dev->driver->fence_driver;
+       struct drm_fence_class_manager *fc = &fm->fence_class[fence->fence_class];
        unsigned long flags;
        uint32_t sequence;
-       uint32_t native_type;
+       uint32_t native_types;
        int ret;
 
        drm_fence_unring(dev, &fence->ring);
-       ret = driver->emit(dev, fence_flags, &sequence, &native_type);
+       ret = driver->emit(dev, fence_class, fence_flags, &sequence,
+                          &native_types);
        if (ret)
                return ret;
 
        write_lock_irqsave(&fm->lock, flags);
+       fence->fence_class = fence_class;
        fence->type = type;
-       fence->flush_mask = 0x00;
-       fence->submitted_flush = 0x00;
-       fence->signaled = 0x00;
+       fence->waiting_types = 0;
+       fence->signaled_types = 0;
+       fence->error = 0;
        fence->sequence = sequence;
-       fence->native_type = native_type;
-       list_add_tail(&fence->ring, &fm->ring);
+       fence->native_types = native_types;
+       if (list_empty(&fc->ring))
+               fc->highest_waiting_sequence = sequence - 1;
+       list_add_tail(&fence->ring, &fc->ring);
+       fc->latest_queued_sequence = sequence;
        write_unlock_irqrestore(&fm->lock, flags);
        return 0;
 }
+EXPORT_SYMBOL(drm_fence_object_emit);
 
-static int drm_fence_object_init(drm_device_t * dev, uint32_t type,
+static int drm_fence_object_init(struct drm_device *dev, uint32_t fence_class,
+                                uint32_t type,
                                 uint32_t fence_flags,
-                                drm_fence_object_t * fence)
+                                struct drm_fence_object *fence)
 {
        int ret = 0;
        unsigned long flags;
-       drm_fence_manager_t *fm = &dev->fm;
+       struct drm_fence_manager *fm = &dev->fm;
 
        mutex_lock(&dev->struct_mutex);
        atomic_set(&fence->usage, 1);
@@ -400,52 +472,63 @@ static int drm_fence_object_init(drm_device_t * dev, uint32_t type,
 
        write_lock_irqsave(&fm->lock, flags);
        INIT_LIST_HEAD(&fence->ring);
-       fence->class = 0;
+
+       /*
+        *  Avoid hitting BUG() for kernel-only fence objects.
+        */
+
+       INIT_LIST_HEAD(&fence->base.list);
+       fence->fence_class = fence_class;
        fence->type = type;
-       fence->flush_mask = 0;
-       fence->submitted_flush = 0;
-       fence->signaled = 0;
+       fence->signaled_types = 0;
+       fence->waiting_types = 0;
        fence->sequence = 0;
+       fence->error = 0;
+       fence->dev = dev;
        write_unlock_irqrestore(&fm->lock, flags);
        if (fence_flags & DRM_FENCE_FLAG_EMIT) {
-               ret = drm_fence_object_emit(dev, fence, fence_flags, type);
+               ret = drm_fence_object_emit(fence, fence_flags,
+                                           fence->fence_class, type);
        }
        return ret;
 }
 
-int drm_fence_add_user_object(drm_file_t * priv, drm_fence_object_t * fence,
-                             int shareable)
+int drm_fence_add_user_object(struct drm_file *priv,
+                             struct drm_fence_object *fence, int shareable)
 {
-       drm_device_t *dev = priv->head->dev;
+       struct drm_device *dev = priv->minor->dev;
        int ret;
 
        mutex_lock(&dev->struct_mutex);
        ret = drm_add_user_object(priv, &fence->base, shareable);
-       mutex_unlock(&dev->struct_mutex);
        if (ret)
-               return ret;
+               goto out;
+       atomic_inc(&fence->usage);
        fence->base.type = drm_fence_type;
        fence->base.remove = &drm_fence_object_destroy;
        DRM_DEBUG("Fence 0x%08lx created\n", fence->base.hash.key);
-       return 0;
+out:
+       mutex_unlock(&dev->struct_mutex);
+       return ret;
 }
-
 EXPORT_SYMBOL(drm_fence_add_user_object);
 
-int drm_fence_object_create(drm_device_t * dev, uint32_t type,
-                           unsigned flags, drm_fence_object_t ** c_fence)
+int drm_fence_object_create(struct drm_device *dev, uint32_t fence_class,
+                           uint32_t type, unsigned flags,
+                           struct drm_fence_object **c_fence)
 {
-       drm_fence_object_t *fence;
+       struct drm_fence_object *fence;
        int ret;
-       drm_fence_manager_t *fm = &dev->fm;
+       struct drm_fence_manager *fm = &dev->fm;
 
-       fence = drm_ctl_cache_alloc(drm_cache.fence_object,
-                                   sizeof(*fence), GFP_KERNEL);
-       if (!fence)
+       fence = drm_ctl_calloc(1, sizeof(*fence), DRM_MEM_FENCE);
+       if (!fence) {
+               DRM_ERROR("Out of memory creating fence object\n");
                return -ENOMEM;
-       ret = drm_fence_object_init(dev, type, flags, fence);
+       }
+       ret = drm_fence_object_init(dev, fence_class, type, flags, fence);
        if (ret) {
-               drm_fence_usage_deref_unlocked(dev, fence);
+               drm_fence_usage_deref_unlocked(&fence);
                return ret;
        }
        *c_fence = fence;
@@ -453,40 +536,67 @@ int drm_fence_object_create(drm_device_t * dev, uint32_t type,
 
        return 0;
 }
-
 EXPORT_SYMBOL(drm_fence_object_create);
 
-void drm_fence_manager_init(drm_device_t * dev)
+void drm_fence_manager_init(struct drm_device *dev)
 {
-       drm_fence_manager_t *fm = &dev->fm;
-       drm_fence_driver_t *fed = dev->driver->fence_driver;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_class_manager *fence_class;
+       struct drm_fence_driver *fed = dev->driver->fence_driver;
        int i;
+       unsigned long flags;
 
-       fm->lock = RW_LOCK_UNLOCKED;
-       write_lock(&fm->lock);
-       INIT_LIST_HEAD(&fm->ring);
-       fm->pending_flush = 0;
-       DRM_INIT_WAITQUEUE(&fm->fence_queue);
+       rwlock_init(&fm->lock);
+       write_lock_irqsave(&fm->lock, flags);
        fm->initialized = 0;
-       if (fed) {
-               fm->initialized = 1;
-               atomic_set(&fm->count, 0);
-               for (i = 0; i < fed->no_types; ++i) {
-                       fm->fence_types[i] = &fm->ring;
-               }
+       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) {
+           fence_class = &fm->fence_class[i];
+
+           memset(fence_class, 0, sizeof(*fence_class));
+           INIT_LIST_HEAD(&fence_class->ring);
+           DRM_INIT_WAITQUEUE(&fence_class->fence_queue);
        }
-       write_unlock(&fm->lock);
+
+       atomic_set(&fm->count, 0);
+ out_unlock:
+       write_unlock_irqrestore(&fm->lock, flags);
 }
 
-void drm_fence_manager_takedown(drm_device_t * dev)
+void drm_fence_fill_arg(struct drm_fence_object *fence,
+                       struct drm_fence_arg *arg)
 {
+       struct drm_device *dev = fence->dev;
+       struct drm_fence_manager *fm = &dev->fm;
+       unsigned long irq_flags;
+
+       read_lock_irqsave(&fm->lock, irq_flags);
+       arg->handle = fence->base.hash.key;
+       arg->fence_class = fence->fence_class;
+       arg->type = fence->type;
+       arg->signaled = fence->signaled_types;
+       arg->error = fence->error;
+       arg->sequence = fence->sequence;
+       read_unlock_irqrestore(&fm->lock, irq_flags);
 }
+EXPORT_SYMBOL(drm_fence_fill_arg);
 
-drm_fence_object_t *drm_lookup_fence_object(drm_file_t * priv, uint32_t handle)
+void drm_fence_manager_takedown(struct drm_device *dev)
 {
-       drm_device_t *dev = priv->head->dev;
-       drm_user_object_t *uo;
-       drm_fence_object_t *fence;
+}
+
+struct drm_fence_object *drm_lookup_fence_object(struct drm_file *priv,
+                                                uint32_t handle)
+{
+       struct drm_device *dev = priv->minor->dev;
+       struct drm_user_object *uo;
+       struct drm_fence_object *fence;
 
        mutex_lock(&dev->struct_mutex);
        uo = drm_lookup_user_object(priv, handle);
@@ -494,21 +604,17 @@ drm_fence_object_t *drm_lookup_fence_object(drm_file_t * priv, uint32_t handle)
                mutex_unlock(&dev->struct_mutex);
                return NULL;
        }
-       fence = drm_user_object_entry(uo, drm_fence_object_t, base);
-       atomic_inc(&fence->usage);
+       fence = drm_fence_reference_locked(drm_user_object_entry(uo, struct drm_fence_object, base));
        mutex_unlock(&dev->struct_mutex);
        return fence;
 }
 
-int drm_fence_ioctl(DRM_IOCTL_ARGS)
+int drm_fence_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
-       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;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_arg *arg = data;
+       struct drm_fence_object *fence;
        ret = 0;
 
        if (!fm->initialized) {
@@ -516,104 +622,208 @@ int drm_fence_ioctl(DRM_IOCTL_ARGS)
                return -EINVAL;
        }
 
-       DRM_COPY_FROM_USER_IOCTL(arg, (void __user *)data, sizeof(arg));
-       switch (arg.op) {
-       case drm_fence_create:
-               if (arg.flags & DRM_FENCE_FLAG_EMIT)
-                       LOCK_TEST_WITH_RETURN(dev, filp);
-               ret = drm_fence_object_create(dev, arg.type, arg.flags, &fence);
-               if (ret)
-                       return ret;
-               ret = drm_fence_add_user_object(priv, fence,
-                                               arg.flags &
-                                               DRM_FENCE_FLAG_SHAREABLE);
-               if (ret) {
-                       drm_fence_usage_deref_unlocked(dev, fence);
-                       return ret;
-               }
+       if (arg->flags & DRM_FENCE_FLAG_EMIT)
+               LOCK_TEST_WITH_RETURN(dev, file_priv);
+       ret = drm_fence_object_create(dev, arg->fence_class,
+                                     arg->type, arg->flags, &fence);
+       if (ret)
+               return ret;
+       ret = drm_fence_add_user_object(file_priv, fence,
+                                       arg->flags &
+                                       DRM_FENCE_FLAG_SHAREABLE);
+       if (ret) {
+               drm_fence_usage_deref_unlocked(&fence);
+               return ret;
+       }
+
+       /*
+        * usage > 0. No need to lock dev->struct_mutex;
+        */
 
-               /*
-                * usage > 0. No need to lock dev->struct_mutex;
-                */
+       arg->handle = fence->base.hash.key;
 
-               atomic_inc(&fence->usage);
-               arg.handle = fence->base.hash.key;
-               break;
-       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);
+       drm_fence_fill_arg(fence, arg);
+       drm_fence_usage_deref_unlocked(&fence);
+
+       return ret;
+}
+
+int drm_fence_reference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+       int ret;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_arg *arg = data;
+       struct drm_fence_object *fence;
+       struct drm_user_object *uo;
+       ret = 0;
+
+       if (!fm->initialized) {
+               DRM_ERROR("The DRM driver does not support fencing.\n");
+               return -EINVAL;
+       }
+
+       ret = drm_user_object_ref(file_priv, arg->handle, drm_fence_type, &uo);
+       if (ret)
                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);
+       fence = drm_lookup_fence_object(file_priv, arg->handle);
+       drm_fence_fill_arg(fence, arg);
+       drm_fence_usage_deref_unlocked(&fence);
+
+       return ret;
+}
+
+
+int drm_fence_unreference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+       int ret;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_arg *arg = data;
+       ret = 0;
+
+       if (!fm->initialized) {
+               DRM_ERROR("The DRM driver does not support fencing.\n");
+               return -EINVAL;
+       }
+
+       return drm_user_object_unref(file_priv, arg->handle, drm_fence_type);
+}
+
+int drm_fence_signaled_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+       int ret;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_arg *arg = data;
+       struct drm_fence_object *fence;
+       ret = 0;
+
+       if (!fm->initialized) {
+               DRM_ERROR("The DRM driver does not support fencing.\n");
+               return -EINVAL;
+       }
+
+       fence = drm_lookup_fence_object(file_priv, arg->handle);
+       if (!fence)
+               return -EINVAL;
+
+       drm_fence_fill_arg(fence, arg);
+       drm_fence_usage_deref_unlocked(&fence);
+
+       return ret;
+}
+
+int drm_fence_flush_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+       int ret;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_arg *arg = data;
+       struct drm_fence_object *fence;
+       ret = 0;
+
+       if (!fm->initialized) {
+               DRM_ERROR("The DRM driver does not support fencing.\n");
+               return -EINVAL;
+       }
+
+       fence = drm_lookup_fence_object(file_priv, arg->handle);
+       if (!fence)
+               return -EINVAL;
+       ret = drm_fence_object_flush(fence, arg->type);
+
+       drm_fence_fill_arg(fence, arg);
+       drm_fence_usage_deref_unlocked(&fence);
+
+       return ret;
+}
+
+
+int drm_fence_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+       int ret;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_arg *arg = data;
+       struct drm_fence_object *fence;
+       ret = 0;
+
+       if (!fm->initialized) {
+               DRM_ERROR("The DRM driver does not support fencing.\n");
+               return -EINVAL;
+       }
+
+       fence = drm_lookup_fence_object(file_priv, arg->handle);
+       if (!fence)
+               return -EINVAL;
+       ret = drm_fence_object_wait(fence,
+                                   arg->flags & DRM_FENCE_FLAG_WAIT_LAZY,
+                                   0, arg->type);
+
+       drm_fence_fill_arg(fence, arg);
+       drm_fence_usage_deref_unlocked(&fence);
+
+       return ret;
+}
+
+
+int drm_fence_emit_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+       int ret;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_arg *arg = data;
+       struct drm_fence_object *fence;
+       ret = 0;
+
+       if (!fm->initialized) {
+               DRM_ERROR("The DRM driver does not support fencing.\n");
+               return -EINVAL;
+       }
+
+       LOCK_TEST_WITH_RETURN(dev, file_priv);
+       fence = drm_lookup_fence_object(file_priv, arg->handle);
+       if (!fence)
+               return -EINVAL;
+       ret = drm_fence_object_emit(fence, arg->flags, arg->fence_class,
+                                   arg->type);
+
+       drm_fence_fill_arg(fence, arg);
+       drm_fence_usage_deref_unlocked(&fence);
+
+       return ret;
+}
+
+int drm_fence_buffers_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+       int ret;
+       struct drm_fence_manager *fm = &dev->fm;
+       struct drm_fence_arg *arg = data;
+       struct drm_fence_object *fence;
+       ret = 0;
+
+       if (!fm->initialized) {
+               DRM_ERROR("The DRM driver does not support fencing.\n");
+               return -EINVAL;
+       }
+
+       if (!dev->bm.initialized) {
+               DRM_ERROR("Buffer object manager is not initialized\n");
+               return -EINVAL;
+       }
+       LOCK_TEST_WITH_RETURN(dev, file_priv);
+       ret = drm_fence_buffer_objects(dev, NULL, arg->flags,
+                                      NULL, &fence);
+       if (ret)
                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,
-                                         0, arg.type);
-               break;
-       case drm_fence_emit:
-               LOCK_TEST_WITH_RETURN(dev, filp);
-               fence = drm_lookup_fence_object(priv, arg.handle);
-               if (!fence)
-                       return -EINVAL;
-               ret = drm_fence_object_emit(dev, fence, arg.flags, arg.type);
-               break;
-       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);
-               ret = drm_fence_buffer_objects(priv, NULL, arg.flags,
-                                              NULL, &fence);
-               if (ret)
-                       return ret;
-               ret = drm_fence_add_user_object(priv, fence,
-                                               arg.flags &
+
+       if (!(arg->flags & DRM_FENCE_FLAG_NO_USER)) {
+               ret = drm_fence_add_user_object(file_priv, fence,
+                                               arg->flags &
                                                DRM_FENCE_FLAG_SHAREABLE);
                if (ret)
                        return ret;
-               atomic_inc(&fence->usage);
-               arg.handle = fence->base.hash.key;
-               break;
-       default:
-               return -EINVAL;
        }
-       read_lock_irqsave(&fm->lock, flags);
-       arg.class = fence->class;
-       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));
+       arg->handle = fence->base.hash.key;
+
+       drm_fence_fill_arg(fence, arg);
+       drm_fence_usage_deref_unlocked(&fence);
+
        return ret;
 }