summaryrefslogtreecommitdiff
path: root/linux-core
diff options
context:
space:
mode:
Diffstat (limited to 'linux-core')
-rw-r--r--linux-core/Makefile.kernel4
-rw-r--r--linux-core/README.drm23
-rw-r--r--linux-core/drmP.h16
-rw-r--r--linux-core/drm_fops.c57
-rw-r--r--linux-core/drm_irq.c4
-rw-r--r--linux-core/drm_lock.c159
-rw-r--r--linux-core/drm_stub.c1
l---------linux-core/nv04_graph.c1
-rw-r--r--linux-core/sis_drv.c2
9 files changed, 143 insertions, 124 deletions
diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel
index a6910d73..c2bfb671 100644
--- a/linux-core/Makefile.kernel
+++ b/linux-core/Makefile.kernel
@@ -22,8 +22,8 @@ i830-objs := i830_drv.o i830_dma.o i830_irq.o
i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o \
i915_buffer.o
nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \
- nouveau_object.o nouveau_irq.o nv10_graph.o nv30_graph.o nv40_graph.o \
- nv20_graph.o
+ nouveau_object.o nouveau_irq.o nv04_graph.o nv10_graph.o nv20_graph.o nv30_graph.o \
+ nv40_graph.o
radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o
sis-objs := sis_drv.o sis_mm.o
ffb-objs := ffb_drv.o ffb_context.o
diff --git a/linux-core/README.drm b/linux-core/README.drm
index 6441e01e..7bcd6191 100644
--- a/linux-core/README.drm
+++ b/linux-core/README.drm
@@ -1,6 +1,6 @@
************************************************************
* For the very latest on DRI development, please see: *
-* http://dri.sourceforge.net/ *
+* http://dri.freedesktop.org/ *
************************************************************
The Direct Rendering Manager (drm) is a device-independent kernel-level
@@ -23,24 +23,3 @@ ways:
4. The DRM is extensible via the use of small device-specific modules
that rely extensively on the API exported by the DRM module.
-
-
-Documentation on the DRI is available from:
- http://precisioninsight.com/piinsights.html
-
-For specific information about kernel-level support, see:
-
- The Direct Rendering Manager, Kernel Support for the Direct Rendering
- Infrastructure
- http://precisioninsight.com/dr/drm.html
-
- Hardware Locking for the Direct Rendering Infrastructure
- http://precisioninsight.com/dr/locking.html
-
- A Security Analysis of the Direct Rendering Infrastructure
- http://precisioninsight.com/dr/security.html
-
-************************************************************
-* For the very latest on DRI development, please see: *
-* http://dri.sourceforge.net/ *
-************************************************************
diff --git a/linux-core/drmP.h b/linux-core/drmP.h
index 0bf71c49..9b5f5bdd 100644
--- a/linux-core/drmP.h
+++ b/linux-core/drmP.h
@@ -458,6 +458,10 @@ typedef struct drm_lock_data {
struct file *filp; /**< File descr of lock holder (0=kernel) */
wait_queue_head_t lock_queue; /**< Queue of blocked processes */
unsigned long lock_time; /**< Time of last lock in jiffies */
+ spinlock_t spinlock;
+ uint32_t kernel_waiters;
+ uint32_t user_waiters;
+ int idle_has_lock;
} drm_lock_data_t;
/**
@@ -642,6 +646,8 @@ struct drm_driver {
void (*reclaim_buffers) (struct drm_device *dev, struct file * filp);
void (*reclaim_buffers_locked) (struct drm_device *dev,
struct file * filp);
+ void (*reclaim_buffers_idlelocked) (struct drm_device *dev,
+ struct file * filp);
unsigned long (*get_map_ofs) (drm_map_t * map);
unsigned long (*get_reg_ofs) (struct drm_device * dev);
void (*set_version) (struct drm_device * dev, drm_set_version_t * sv);
@@ -1010,12 +1016,14 @@ extern int drm_lock(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
extern int drm_unlock(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
-extern int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context);
-extern int drm_lock_free(drm_device_t * dev,
- __volatile__ unsigned int *lock, unsigned int context);
+extern int drm_lock_take(drm_lock_data_t *lock_data, unsigned int context);
+extern int drm_lock_free(drm_lock_data_t *lock_data, unsigned int context);
+extern void drm_idlelock_take(drm_lock_data_t *lock_data);
+extern void drm_idlelock_release(drm_lock_data_t *lock_data);
+
/*
* These are exported to drivers so that they can implement fencing using
- * DMA quiscent + idle. DMA quiescent usually requires the hardware lock.
+ * DMA quiscent + idle. DMA quiescent usually requires the hardware lock.
*/
extern int drm_i_have_hw_lock(struct file *filp);
diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c
index 84e06c87..faf76726 100644
--- a/linux-core/drm_fops.c
+++ b/linux-core/drm_fops.c
@@ -427,38 +427,51 @@ int drm_release(struct inode *inode, struct file *filp)
dev->open_count);
if (dev->driver->reclaim_buffers_locked && dev->lock.hw_lock) {
- unsigned long _end = jiffies + DRM_HZ*3;
-
- do {
- retcode = drm_kernel_take_hw_lock(filp);
- } while(retcode && !time_after_eq(jiffies,_end));
-
- if (!retcode) {
+ if (drm_i_have_hw_lock(filp)) {
dev->driver->reclaim_buffers_locked(dev, filp);
-
- drm_lock_free(dev, &dev->lock.hw_lock->lock,
- _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
} else {
+ unsigned long _end=jiffies + 3*DRM_HZ;
+ int locked = 0;
+
+ drm_idlelock_take(&dev->lock);
/*
- * FIXME: This is not a good solution. We should perhaps associate the
- * DRM lock with a process context, and check whether the current process
- * holds the lock. Then we can run reclaim buffers locked anyway.
+ * Wait for a while.
*/
- DRM_ERROR("Reclaim buffers locked deadlock.\n"
- "\tThis is probably a single thread having multiple\n"
- "\tDRM file descriptors open either dying or"
- " closing file descriptors\n"
- "\twhile having the lock. I will not reclaim buffers.\n"
- "\tLocking context is 0x%08x\n",
- _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+ do{
+ spin_lock(&dev->lock.spinlock);
+ locked = dev->lock.idle_has_lock;
+ spin_unlock(&dev->lock.spinlock);
+ if (locked)
+ break;
+ schedule();
+ } while (!time_after_eq(jiffies, _end));
+
+ if (!locked) {
+ DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n"
+ "\tdriver to use reclaim_buffers_idlelocked() instead.\n"
+ "\tI will go on reclaiming the buffers anyway.\n");
+ }
+
+ dev->driver->reclaim_buffers_locked(dev, filp);
+ drm_idlelock_release(&dev->lock);
}
- } else if (drm_i_have_hw_lock(filp)) {
+ }
+
+ if (dev->driver->reclaim_buffers_idlelocked && dev->lock.hw_lock) {
+
+ drm_idlelock_take(&dev->lock);
+ dev->driver->reclaim_buffers_idlelocked(dev, filp);
+ drm_idlelock_release(&dev->lock);
+
+ }
+
+ if (drm_i_have_hw_lock(filp)) {
DRM_DEBUG("File %p released, freeing lock for context %d\n",
filp, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
- drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ drm_lock_free(&dev->lock,
_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
}
diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c
index c365c08e..92cf7f5c 100644
--- a/linux-core/drm_irq.c
+++ b/linux-core/drm_irq.c
@@ -422,7 +422,7 @@ static void drm_locked_tasklet_func(unsigned long data)
spin_lock_irqsave(&dev->tasklet_lock, irqflags);
if (!dev->locked_tasklet_func ||
- !drm_lock_take(&dev->lock.hw_lock->lock,
+ !drm_lock_take(&dev->lock,
DRM_KERNEL_CONTEXT)) {
spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
return;
@@ -433,7 +433,7 @@ static void drm_locked_tasklet_func(unsigned long data)
dev->locked_tasklet_func(dev);
- drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ drm_lock_free(&dev->lock,
DRM_KERNEL_CONTEXT);
dev->locked_tasklet_func = NULL;
diff --git a/linux-core/drm_lock.c b/linux-core/drm_lock.c
index d11c570e..f02df36b 100644
--- a/linux-core/drm_lock.c
+++ b/linux-core/drm_lock.c
@@ -35,12 +35,6 @@
#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);
/**
@@ -83,6 +77,9 @@ int drm_lock(struct inode *inode, struct file *filp,
return -EINVAL;
add_wait_queue(&dev->lock.lock_queue, &entry);
+ spin_lock(&dev->lock.spinlock);
+ dev->lock.user_waiters++;
+ spin_unlock(&dev->lock.spinlock);
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
if (!dev->lock.hw_lock) {
@@ -90,7 +87,7 @@ int drm_lock(struct inode *inode, struct file *filp,
ret = -EINTR;
break;
}
- if (drm_lock_take(&dev->lock.hw_lock->lock, lock.context)) {
+ if (drm_lock_take(&dev->lock, lock.context)) {
dev->lock.filp = filp;
dev->lock.lock_time = jiffies;
atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
@@ -104,6 +101,9 @@ int drm_lock(struct inode *inode, struct file *filp,
break;
}
}
+ spin_lock(&dev->lock.spinlock);
+ dev->lock.user_waiters--;
+ spin_unlock(&dev->lock.spinlock);
__set_current_state(TASK_RUNNING);
remove_wait_queue(&dev->lock.lock_queue, &entry);
@@ -184,8 +184,7 @@ int drm_unlock(struct inode *inode, struct file *filp,
if (dev->driver->kernel_context_switch_unlock)
dev->driver->kernel_context_switch_unlock(dev);
else {
- if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
- lock.context)) {
+ if (drm_lock_free(&dev->lock,lock.context)) {
/* FIXME: Should really bail out here. */
}
}
@@ -203,18 +202,26 @@ int drm_unlock(struct inode *inode, struct file *filp,
*
* Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction.
*/
-int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
+int drm_lock_take(drm_lock_data_t *lock_data,
+ unsigned int context)
{
unsigned int old, new, prev;
+ volatile unsigned int *lock = &lock_data->hw_lock->lock;
+ spin_lock(&lock_data->spinlock);
do {
old = *lock;
if (old & _DRM_LOCK_HELD)
new = old | _DRM_LOCK_CONT;
- else
- new = context | _DRM_LOCK_HELD | _DRM_LOCK_CONT;
+ else {
+ new = context | _DRM_LOCK_HELD |
+ ((lock_data->user_waiters + lock_data->kernel_waiters > 1) ?
+ _DRM_LOCK_CONT : 0);
+ }
prev = cmpxchg(lock, old, new);
} while (prev != old);
+ spin_unlock(&lock_data->spinlock);
+
if (_DRM_LOCKING_CONTEXT(old) == context) {
if (old & _DRM_LOCK_HELD) {
if (context != DRM_KERNEL_CONTEXT) {
@@ -224,14 +231,15 @@ int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
return 0;
}
}
- if (new == (context | _DRM_LOCK_HELD | _DRM_LOCK_CONT)) {
+
+ if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) {
/* 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.
@@ -244,13 +252,13 @@ int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
* Resets the lock file pointer.
* Marks the lock as held by the given context, via the \p cmpxchg instruction.
*/
-static int drm_lock_transfer(drm_device_t * dev,
- __volatile__ unsigned int *lock,
+static int drm_lock_transfer(drm_lock_data_t *lock_data,
unsigned int context)
{
unsigned int old, new, prev;
+ volatile unsigned int *lock = &lock_data->hw_lock->lock;
- dev->lock.filp = NULL;
+ lock_data->filp = NULL;
do {
old = *lock;
new = context | _DRM_LOCK_HELD;
@@ -258,7 +266,6 @@ static int drm_lock_transfer(drm_device_t * dev,
} while (prev != old);
return 1;
}
-#endif
/**
* Free lock.
@@ -271,10 +278,19 @@ static int drm_lock_transfer(drm_device_t * dev,
* Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task
* waiting on the lock queue.
*/
-int drm_lock_free(drm_device_t * dev,
- __volatile__ unsigned int *lock, unsigned int context)
+int drm_lock_free(drm_lock_data_t *lock_data, unsigned int context)
{
unsigned int old, new, prev;
+ volatile unsigned int *lock = &lock_data->hw_lock->lock;
+
+ spin_lock(&lock_data->spinlock);
+ if (lock_data->kernel_waiters != 0) {
+ drm_lock_transfer(lock_data, 0);
+ lock_data->idle_has_lock = 1;
+ spin_unlock(&lock_data->spinlock);
+ return 1;
+ }
+ spin_unlock(&lock_data->spinlock);
do {
old = *lock;
@@ -287,7 +303,7 @@ int drm_lock_free(drm_device_t * dev,
context, _DRM_LOCKING_CONTEXT(old));
return 1;
}
- wake_up_interruptible(&dev->lock.lock_queue);
+ wake_up_interruptible(&lock_data->lock_queue);
return 0;
}
@@ -322,65 +338,66 @@ static int drm_notifier(void *priv)
return 0;
}
-/*
- * Can be used by drivers to take the hardware lock if necessary.
- * (Waiting for idle before reclaiming buffers etc.)
+/**
+ * This function returns immediately and takes the hw lock
+ * with the kernel context if it is free, otherwise it gets the highest priority when and if
+ * it is eventually released.
+ *
+ * This guarantees that the kernel will _eventually_ have the lock _unless_ it is held
+ * by a blocked process. (In the latter case an explicit wait for the hardware lock would cause
+ * a deadlock, which is why the "idlelock" was invented).
+ *
+ * This should be sufficient to wait for GPU idle without
+ * having to worry about starvation.
*/
-int drm_i_have_hw_lock(struct file *filp)
+void drm_idlelock_take(drm_lock_data_t *lock_data)
{
- DRM_DEVICE;
-
- return (priv->lock_count && dev->lock.hw_lock &&
- _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
- dev->lock.filp == filp);
-}
+ int ret = 0;
-EXPORT_SYMBOL(drm_i_have_hw_lock);
+ spin_lock(&lock_data->spinlock);
+ lock_data->kernel_waiters++;
+ if (!lock_data->idle_has_lock) {
-int drm_kernel_take_hw_lock(struct file *filp)
-{
- DRM_DEVICE;
+ spin_unlock(&lock_data->spinlock);
+ ret = drm_lock_take(lock_data, DRM_KERNEL_CONTEXT);
+ spin_lock(&lock_data->spinlock);
- 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;
- }
+ if (ret == 1)
+ lock_data->idle_has_lock = 1;
+ }
+ spin_unlock(&lock_data->spinlock);
+}
+EXPORT_SYMBOL(drm_idlelock_take);
- schedule_timeout(1);
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
+void drm_idlelock_release(drm_lock_data_t *lock_data)
+{
+ unsigned int old, prev;
+ volatile unsigned int *lock = &lock_data->hw_lock->lock;
+
+ spin_lock(&lock_data->spinlock);
+ if (--lock_data->kernel_waiters == 0) {
+ if (lock_data->idle_has_lock) {
+ do {
+ old = *lock;
+ prev = cmpxchg(lock, old, DRM_KERNEL_CONTEXT);
+ } while (prev != old);
+ wake_up_interruptible(&lock_data->lock_queue);
+ lock_data->idle_has_lock = 0;
}
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&dev->lock.lock_queue, &entry);
}
- return ret;
+ spin_unlock(&lock_data->spinlock);
}
+EXPORT_SYMBOL(drm_idlelock_release);
-EXPORT_SYMBOL(drm_kernel_take_hw_lock);
+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);
diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c
index 22592324..348cd2c6 100644
--- a/linux-core/drm_stub.c
+++ b/linux-core/drm_stub.c
@@ -63,6 +63,7 @@ static int drm_fill_in_dev(drm_device_t * dev, struct pci_dev *pdev,
spin_lock_init(&dev->count_lock);
spin_lock_init(&dev->drw_lock);
spin_lock_init(&dev->tasklet_lock);
+ spin_lock_init(&dev->lock.spinlock);
init_timer(&dev->timer);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->ctxlist_mutex);
diff --git a/linux-core/nv04_graph.c b/linux-core/nv04_graph.c
new file mode 120000
index 00000000..0d7a0b35
--- /dev/null
+++ b/linux-core/nv04_graph.c
@@ -0,0 +1 @@
+../shared-core/nv04_graph.c \ No newline at end of file
diff --git a/linux-core/sis_drv.c b/linux-core/sis_drv.c
index 9b0b9830..114ec8f9 100644
--- a/linux-core/sis_drv.c
+++ b/linux-core/sis_drv.c
@@ -74,7 +74,7 @@ static struct drm_driver driver = {
.context_dtor = NULL,
.dma_quiescent = sis_idle,
.reclaim_buffers = NULL,
- .reclaim_buffers_locked = sis_reclaim_buffers_locked,
+ .reclaim_buffers_idlelocked = sis_reclaim_buffers_locked,
.lastclose = sis_lastclose,
.get_map_ofs = drm_core_get_map_ofs,
.get_reg_ofs = drm_core_get_reg_ofs,