OSDN Git Service

Remove the need for the hardware lock in the buffer manager.
authorThomas Hellstrom <thomas-at-tungstengraphics-dot-com>
Sun, 21 Oct 2007 10:20:56 +0000 (12:20 +0200)
committerThomas Hellstrom <thomas-at-tungstengraphics-dot-com>
Sun, 21 Oct 2007 10:20:56 +0000 (12:20 +0200)
Add interface entry cleaning a memory type without touching NO_EVICT buffers.

libdrm/xf86drm.c
libdrm/xf86mm.h
linux-core/Makefile.kernel
linux-core/drm_bo.c
linux-core/drm_bo_lock.c [new file with mode: 0644]
linux-core/drm_compat.c
linux-core/drm_vm.c
shared-core/drm.h

index b61c225..ee0043c 100644 (file)
@@ -2820,14 +2820,15 @@ int drmMMTakedown(int fd, unsigned memType)
  * the buffer manager is NOT locked.
  */
 
-int drmMMLock(int fd, unsigned memType, int lockBM)
+int drmMMLock(int fd, unsigned memType, int lockBM, int ignoreNoEvict)
 {
     struct drm_mm_type_arg arg;
     int ret;
 
     memset(&arg, 0, sizeof(arg));
     arg.mem_type = memType;
-    arg.lock_unlock_bm = lock_bm;
+    arg.lock_flags |= (lockBM) ? DRM_BO_LOCK_UNLOCK_BM : 0;
+    arg.lock_flags |= (ignoreNoEvict) = DRM_BO_LOCK_IGNORE_NO_EVICT;
 
     do{
         ret = ioctl(fd, DRM_IOCTL_MM_LOCK, &arg);
@@ -2844,7 +2845,7 @@ int drmMMUnlock(int fd, unsigned memType, int unlockBM)
     memset(&arg, 0, sizeof(arg));
     
     arg.mem_type = memType;
-    arg.lock_unlock_bm = unlockBM;
+    arg.lock_flags |= (unlockBM) ? DRM_BO_LOCK_UNLOCK_BM : 0;
 
     do{
        ret = ioctl(fd, DRM_IOCTL_MM_UNLOCK, &arg);
index f8ec1d7..0516bd3 100644 (file)
@@ -172,8 +172,8 @@ extern int drmBOWaitIdle(int fd, drmBO *buf, unsigned hint);
 extern int drmMMInit(int fd, unsigned long pOffset, unsigned long pSize,
                     unsigned memType);
 extern int drmMMTakedown(int fd, unsigned memType);
-extern int drmMMLock(int fd, unsigned memType);
-extern int drmMMUnlock(int fd, unsigned memType);
+extern int drmMMLock(int fd, unsigned memType, int lockBM, int ignoreNoEvict);
+extern int drmMMUnlock(int fd, unsigned memType, int unlockBM);
 
 
 #endif
index 86b225f..7913643 100644 (file)
@@ -13,7 +13,7 @@ drm-objs    := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \
                drm_sysfs.o drm_pci.o drm_agpsupport.o drm_scatter.o \
                drm_memory_debug.o ati_pcigart.o drm_sman.o \
                drm_hashtab.o drm_mm.o drm_object.o drm_compat.o \
-               drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o drm_bo_lock2.o
+               drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o drm_bo_lock.o
 tdfx-objs   := tdfx_drv.o
 r128-objs   := r128_drv.o r128_cce.o r128_state.o r128_irq.o
 mga-objs    := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o
index a2a0291..e6eb632 100644 (file)
@@ -2255,11 +2255,11 @@ int drm_mm_init_ioctl(struct drm_device *dev, void *data, struct drm_file *file_
                return -EINVAL;
        }
 
-       ret = -EINVAL;
        ret = drm_bo_write_lock(&bm->bm_lock, file_priv);
        if (ret)
                return ret;
 
+       ret = -EINVAL;
        if (arg->magic != DRM_BO_INIT_MAGIC) {
                DRM_ERROR("You are using an old libdrm that is not compatible with\n"
                          "\tthe kernel DRM module. Please upgrade your libdrm.\n");
@@ -2353,7 +2353,12 @@ int drm_mm_lock_ioctl(struct drm_device *dev, void *data, struct drm_file *file_
                return -EINVAL;
        }
 
-       if (arg->lock_unlock_bm) {
+       if (arg->lock_flags & DRM_BO_LOCK_IGNORE_NO_EVICT) {
+               DRM_ERROR("Lock flag DRM_BO_LOCK_IGNORE_NO_EVICT not supported yet.\n");
+               return -EINVAL;
+       }
+               
+       if (arg->lock_flags & DRM_BO_LOCK_UNLOCK_BM) {
                ret = drm_bo_write_lock(&dev->bm.bm_lock, file_priv);
                if (ret)
                        return ret;
@@ -2383,7 +2388,7 @@ int drm_mm_unlock_ioctl(struct drm_device *dev,
                return -EINVAL;
        }
 
-       if (arg->lock_unlock_bm) {
+       if (arg->lock_flags & DRM_BO_LOCK_UNLOCK_BM) {
                ret = drm_bo_write_unlock(&dev->bm.bm_lock, file_priv);
                if (ret)
                        return ret;
diff --git a/linux-core/drm_bo_lock.c b/linux-core/drm_bo_lock.c
new file mode 100644 (file)
index 0000000..e5a8682
--- /dev/null
@@ -0,0 +1,178 @@
+/**************************************************************************
+ *
+ * Copyright (c) 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
+ * 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:
+ *
+ * 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
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+/*
+ * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
+/*
+ * This file implements a simple replacement for the buffer manager use
+ * of the heavyweight hardware lock.
+ * The lock is a read-write lock. Taking it in read mode is fast, and 
+ * intended for in-kernel use only.
+ * Taking it in write mode is slow.
+ *
+ * The write mode is used only when there is a need to block all 
+ * user-space processes from allocating a 
+ * new memory area.
+ * Typical use in write mode is X server VT switching, and it's allowed
+ * to leave kernel space with the write lock held. If a user-space process
+ * dies while having the write-lock, it will be released during the file
+ * descriptor release.
+ *
+ * The read lock is typically placed at the start of an IOCTL- or 
+ * user-space callable function that may end up allocating a memory area.
+ * This includes setstatus, super-ioctls and no_pfn; the latter may move
+ * unmappable regions to mappable. It's a bug to leave kernel space with the
+ * read lock held.
+ *
+ * Both read- and write lock taking is interruptible for low signal-delivery
+ * latency. The locking functions will return -EAGAIN if interrupted by a
+ * signal.
+ *
+ * Locking order: The lock should be taken BEFORE any kernel mutexes 
+ * or spinlocks.
+ */
+
+#include "drmP.h"
+
+void drm_bo_init_lock(struct drm_bo_lock *lock)
+{
+       DRM_INIT_WAITQUEUE(&lock->queue);
+       atomic_set(&lock->write_lock_pending, 0);
+       atomic_set(&lock->readers, 0);
+}
+
+void drm_bo_read_unlock(struct drm_bo_lock *lock)
+{
+       if (unlikely(atomic_add_negative(-1, &lock->readers)))
+               BUG();
+       if (atomic_read(&lock->readers) == 0)
+               wake_up_interruptible(&lock->queue);
+}
+
+EXPORT_SYMBOL(drm_bo_read_unlock);
+
+int drm_bo_read_lock(struct drm_bo_lock *lock)
+{
+       while (unlikely(atomic_read(&lock->write_lock_pending) != 0)) {
+               int ret;
+               ret = wait_event_interruptible
+                   (lock->queue, atomic_read(&lock->write_lock_pending) == 0);
+               if (ret)
+                       return -EAGAIN;
+       }
+
+       while (unlikely(!atomic_add_unless(&lock->readers, 1, -1))) {
+               int ret;
+               ret = wait_event_interruptible
+                   (lock->queue, atomic_add_unless(&lock->readers, 1, -1));
+               if (ret)
+                       return -EAGAIN;
+       }
+       return 0;
+}
+
+EXPORT_SYMBOL(drm_bo_read_lock);
+
+static int __drm_bo_write_unlock(struct drm_bo_lock *lock)
+{
+       if (unlikely(atomic_cmpxchg(&lock->readers, -1, 0) != -1))
+               return -EINVAL;
+       if (unlikely(atomic_cmpxchg(&lock->write_lock_pending, 1, 0) != 1))
+               return -EINVAL;
+       wake_up_interruptible(&lock->queue);
+       return 0;
+}
+
+static void drm_bo_write_lock_remove(struct drm_file *file_priv,
+                                    struct drm_user_object *item)
+{
+       struct drm_bo_lock *lock = container_of(item, struct drm_bo_lock, base);
+       int ret;
+
+       ret = __drm_bo_write_unlock(lock);
+       BUG_ON(ret);
+}
+
+int drm_bo_write_lock(struct drm_bo_lock *lock, struct drm_file *file_priv)
+{
+       int ret = 0;
+       struct drm_device *dev;
+
+       if (unlikely(atomic_cmpxchg(&lock->write_lock_pending, 0, 1) != 0)) {
+               return -EINVAL;
+       }
+
+       while (unlikely(atomic_cmpxchg(&lock->readers, 0, -1) != 0)) {
+               ret = wait_event_interruptible
+                   (lock->queue, atomic_cmpxchg(&lock->readers, 0, -1) == 0);
+
+               if (ret) {
+                       atomic_set(&lock->write_lock_pending, 0);
+                       wake_up_interruptible(&lock->queue);
+                       return -EAGAIN;
+               }
+       }
+
+       /*
+        * Add a dummy user-object, the destructor of which will
+        * make sure the lock is released if the client dies 
+        * while holding it.
+        */
+
+       dev = file_priv->head->dev;
+       mutex_lock(&dev->struct_mutex);
+       ret = drm_add_user_object(file_priv, &lock->base, 0);
+       lock->base.remove = &drm_bo_write_lock_remove;
+       lock->base.type = drm_lock_type;
+       if (ret) {
+               (void)__drm_bo_write_unlock(lock);
+       }
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+}
+
+int drm_bo_write_unlock(struct drm_bo_lock *lock, struct drm_file *file_priv)
+{
+       struct drm_device *dev = file_priv->head->dev;
+       struct drm_ref_object *ro;
+
+       mutex_lock(&dev->struct_mutex);
+
+       if (lock->base.owner != file_priv) {
+               mutex_unlock(&dev->struct_mutex);
+               return -EINVAL;
+       }
+       ro = drm_lookup_ref_object(file_priv, &lock->base, _DRM_REF_USE);
+       BUG_ON(!ro);
+       drm_remove_ref_object(file_priv, ro);
+       lock->base.owner = NULL;
+
+       mutex_unlock(&dev->struct_mutex);
+       return 0;
+}
index e51aedb..ae44e50 100644 (file)
@@ -212,6 +212,8 @@ static struct page *drm_bo_vm_fault(struct vm_area_struct *vma,
        unsigned long bus_offset;
        unsigned long bus_size;
        
+       dev = bo->dev;
+       while(drm_bo_read_lock(&dev->bm.bm_lock));
 
        mutex_lock(&bo->mutex);
 
@@ -289,6 +291,7 @@ static struct page *drm_bo_vm_fault(struct vm_area_struct *vma,
                data->type = VM_FAULT_OOM;
 out_unlock:
        mutex_unlock(&bo->mutex);
+       drm_bo_read_unlock(&dev->bm.bm_lock);
        return NULL;
 }
 
index c4e790e..d2554f3 100644 (file)
@@ -728,10 +728,17 @@ static unsigned long drm_bo_vm_nopfn(struct vm_area_struct *vma,
        if (address > vma->vm_end)
                return NOPFN_SIGBUS;
 
-       err = mutex_lock_interruptible(&bo->mutex);
+       dev = bo->dev;
+       err = drm_bo_read_lock(&dev->bm.bm_lock);
        if (err)
                return NOPFN_REFAULT;
 
+       err = mutex_lock_interruptible(&bo->mutex);
+       if (err) {
+               drm_bo_read_unlock(&dev->bm.bm_lock);
+               return NOPFN_REFAULT;
+       }
+
        err = drm_bo_wait(bo, 0, 0, 0);
        if (err) {
                ret = (err != -EAGAIN) ? NOPFN_SIGBUS : NOPFN_REFAULT;
@@ -754,7 +761,6 @@ static unsigned long drm_bo_vm_nopfn(struct vm_area_struct *vma,
                }
        }
 
-       dev = bo->dev;
        err = drm_bo_pci_offset(dev, &bo->mem, &bus_base, &bus_offset,
                                &bus_size);
 
@@ -792,6 +798,7 @@ static unsigned long drm_bo_vm_nopfn(struct vm_area_struct *vma,
        }
 out_unlock:
        mutex_unlock(&bo->mutex);
+       drm_bo_read_unlock(&dev->bm.bm_lock);
        return ret;
 }
 #endif
index f88192f..80c1a3e 100644 (file)
@@ -870,9 +870,12 @@ struct drm_bo_op_arg {
 
 #define DRM_BO_MEM_TYPES 8 /* For now. */
 
+#define DRM_BO_LOCK_UNLOCK_BM       (1 << 0)
+#define DRM_BO_LOCK_IGNORE_NO_EVICT (1 << 1)
+
 struct drm_mm_type_arg {
        unsigned int mem_type;
-        int lock_unlock_bm;
+        unsigned int lock_flags;
 };
 
 struct drm_mm_init_arg {