summaryrefslogtreecommitdiff
path: root/linux-core/drm_lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-core/drm_lock.c')
-rw-r--r--linux-core/drm_lock.c96
1 files changed, 86 insertions, 10 deletions
diff --git a/linux-core/drm_lock.c b/linux-core/drm_lock.c
index a268d8ee..d11c570e 100644
--- a/linux-core/drm_lock.c
+++ b/linux-core/drm_lock.c
@@ -35,9 +35,12 @@
#include "drmP.h"
+#if 0
static int drm_lock_transfer(drm_device_t * dev,
__volatile__ unsigned int *lock,
unsigned int context);
+#endif
+
static int drm_notifier(void *priv);
/**
@@ -104,7 +107,7 @@ int drm_lock(struct inode *inode, struct file *filp,
__set_current_state(TASK_RUNNING);
remove_wait_queue(&dev->lock.lock_queue, &entry);
- DRM_DEBUG( "%d %s\n", lock.context, ret ? "interrupted" : "has lock" );
+ DRM_DEBUG( "%d %s\n", lock.context, ret ? "interrupted" : "has lock" );
if (ret) return ret;
sigemptyset(&dev->sigmask);
@@ -152,6 +155,7 @@ int drm_unlock(struct inode *inode, struct file *filp,
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->head->dev;
drm_lock_t lock;
+ unsigned long irqflags;
if (copy_from_user(&lock, (drm_lock_t __user *) arg, sizeof(lock)))
return -EFAULT;
@@ -162,6 +166,16 @@ int drm_unlock(struct inode *inode, struct file *filp,
return -EINVAL;
}
+ spin_lock_irqsave(&dev->tasklet_lock, irqflags);
+
+ if (dev->locked_tasklet_func) {
+ dev->locked_tasklet_func(dev);
+
+ dev->locked_tasklet_func = NULL;
+ }
+
+ spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
+
atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]);
/* kernel_context_switch isn't used by any of the x86 drm
@@ -170,12 +184,9 @@ int drm_unlock(struct inode *inode, struct file *filp,
if (dev->driver->kernel_context_switch_unlock)
dev->driver->kernel_context_switch_unlock(dev);
else {
- drm_lock_transfer(dev, &dev->lock.hw_lock->lock,
- DRM_KERNEL_CONTEXT);
-
if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
- DRM_KERNEL_CONTEXT)) {
- DRM_ERROR("\n");
+ lock.context)) {
+ /* FIXME: Should really bail out here. */
}
}
@@ -201,7 +212,7 @@ int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
if (old & _DRM_LOCK_HELD)
new = old | _DRM_LOCK_CONT;
else
- new = context | _DRM_LOCK_HELD;
+ new = context | _DRM_LOCK_HELD | _DRM_LOCK_CONT;
prev = cmpxchg(lock, old, new);
} while (prev != old);
if (_DRM_LOCKING_CONTEXT(old) == context) {
@@ -213,13 +224,14 @@ int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
return 0;
}
}
- if (new == (context | _DRM_LOCK_HELD)) {
+ if (new == (context | _DRM_LOCK_HELD | _DRM_LOCK_CONT)) {
/* Have lock */
return 1;
}
return 0;
}
+#if 0
/**
* This takes a lock forcibly and hands it to context. Should ONLY be used
* inside *_unlock to give lock to kernel before calling *_dma_schedule.
@@ -246,6 +258,7 @@ static int drm_lock_transfer(drm_device_t * dev,
} while (prev != old);
return 1;
}
+#endif
/**
* Free lock.
@@ -263,12 +276,12 @@ int drm_lock_free(drm_device_t * dev,
{
unsigned int old, new, prev;
- dev->lock.filp = NULL;
do {
old = *lock;
- new = 0;
+ new = _DRM_LOCKING_CONTEXT(old);
prev = cmpxchg(lock, old, new);
} while (prev != old);
+
if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
DRM_ERROR("%d freed heavyweight lock held by %d\n",
context, _DRM_LOCKING_CONTEXT(old));
@@ -308,3 +321,66 @@ static int drm_notifier(void *priv)
} while (prev != old);
return 0;
}
+
+/*
+ * Can be used by drivers to take the hardware lock if necessary.
+ * (Waiting for idle before reclaiming buffers etc.)
+ */
+
+int drm_i_have_hw_lock(struct file *filp)
+{
+ DRM_DEVICE;
+
+ return (priv->lock_count && dev->lock.hw_lock &&
+ _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
+ dev->lock.filp == filp);
+}
+
+EXPORT_SYMBOL(drm_i_have_hw_lock);
+
+int drm_kernel_take_hw_lock(struct file *filp)
+{
+ DRM_DEVICE;
+
+ int ret = 0;
+ unsigned long _end = jiffies + 3*DRM_HZ;
+
+ if (!drm_i_have_hw_lock(filp)) {
+
+ DECLARE_WAITQUEUE(entry, current);
+
+ add_wait_queue(&dev->lock.lock_queue, &entry);
+ for (;;) {
+ __set_current_state(TASK_INTERRUPTIBLE);
+ if (!dev->lock.hw_lock) {
+ /* Device has been unregistered */
+ ret = -EINTR;
+ break;
+ }
+ if (drm_lock_take(&dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT)) {
+ dev->lock.filp = filp;
+ dev->lock.lock_time = jiffies;
+ atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
+ break; /* Got lock */
+ }
+ /* Contention */
+ if (time_after_eq(jiffies,_end)) {
+ ret = -EBUSY;
+ break;
+ }
+
+ schedule_timeout(1);
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ }
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dev->lock.lock_queue, &entry);
+ }
+ return ret;
+}
+
+EXPORT_SYMBOL(drm_kernel_take_hw_lock);
+