summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Hellstrom <thomas-at-tungstengraphics-dot-com>2007-10-21 12:20:56 +0200
committerThomas Hellstrom <thomas-at-tungstengraphics-dot-com>2007-10-21 12:20:56 +0200
commit3b19b50cb5cd31e60eb03e99dd1109b6d0f5b8a3 (patch)
treed117764ba0985fccb1398beb9613ba5d6d066467
parent48b5eaf303b60077faed09db77785d7a544ac335 (diff)
Remove the need for the hardware lock in the buffer manager.
Add interface entry cleaning a memory type without touching NO_EVICT buffers.
-rw-r--r--libdrm/xf86drm.c7
-rw-r--r--libdrm/xf86mm.h4
-rw-r--r--linux-core/Makefile.kernel2
-rw-r--r--linux-core/drm_bo.c11
-rw-r--r--linux-core/drm_bo_lock.c178
-rw-r--r--linux-core/drm_compat.c3
-rw-r--r--linux-core/drm_vm.c11
-rw-r--r--shared-core/drm.h5
8 files changed, 209 insertions, 12 deletions
diff --git a/libdrm/xf86drm.c b/libdrm/xf86drm.c
index b61c2250..ee0043cb 100644
--- a/libdrm/xf86drm.c
+++ b/libdrm/xf86drm.c
@@ -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);
diff --git a/libdrm/xf86mm.h b/libdrm/xf86mm.h
index f8ec1d75..0516bd32 100644
--- a/libdrm/xf86mm.h
+++ b/libdrm/xf86mm.h
@@ -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
diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel
index 86b225f3..79136431 100644
--- a/linux-core/Makefile.kernel
+++ b/linux-core/Makefile.kernel
@@ -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
diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c
index a2a0291d..e6eb6320 100644
--- a/linux-core/drm_bo.c
+++ b/linux-core/drm_bo.c
@@ -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
index 00000000..e5a86826
--- /dev/null
+++ b/linux-core/drm_bo_lock.c
@@ -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;
+}
diff --git a/linux-core/drm_compat.c b/linux-core/drm_compat.c
index e51aedb7..ae44e500 100644
--- a/linux-core/drm_compat.c
+++ b/linux-core/drm_compat.c
@@ -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;
}
diff --git a/linux-core/drm_vm.c b/linux-core/drm_vm.c
index c4e790ef..d2554f31 100644
--- a/linux-core/drm_vm.c
+++ b/linux-core/drm_vm.c
@@ -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
diff --git a/shared-core/drm.h b/shared-core/drm.h
index f88192ff..80c1a3e2 100644
--- a/shared-core/drm.h
+++ b/shared-core/drm.h
@@ -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 {