aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/drm/drm_lock.c
diff options
context:
space:
mode:
authorThomas Hellstrom <thomas-at-tungstengraphics-dot-com>2007-03-22 22:28:33 -0400
committerDave Airlie <airlied@linux.ie>2007-03-22 22:28:33 -0400
commit040ac32048d5efabd557c1e0a6ab8aec2c710c56 (patch)
treeba714a33105695c7d57ff833dbd18875ddb82492 /drivers/char/drm/drm_lock.c
parent4b560fde06aeb342f3ff0bce924627ab722d251a (diff)
drm: fix driver deadlock with AIGLX and reclaim_buffers_locked
Bugzilla Bug #9457 Add refcounting of user waiters to the DRM hardware lock, so that we can use DRM_LOCK_CONT flag more conservatively. Also add a kernel waiter refcount that if nonzero transfers the lock for the kernel context when it is released. This is useful when waiting for idle and can be used for very simple fence object driver implementations for the new memory manager Signed-off-by: Dave Airlie <airlied@linux.ie>
Diffstat (limited to 'drivers/char/drm/drm_lock.c')
-rw-r--r--drivers/char/drm/drm_lock.c134
1 files changed, 106 insertions, 28 deletions
diff --git a/drivers/char/drm/drm_lock.c b/drivers/char/drm/drm_lock.c
index e9993ba461a2..befd1af19dfe 100644
--- a/drivers/char/drm/drm_lock.c
+++ b/drivers/char/drm/drm_lock.c
@@ -35,9 +35,6 @@
35 35
36#include "drmP.h" 36#include "drmP.h"
37 37
38static int drm_lock_transfer(drm_device_t * dev,
39 __volatile__ unsigned int *lock,
40 unsigned int context);
41static int drm_notifier(void *priv); 38static int drm_notifier(void *priv);
42 39
43/** 40/**
@@ -80,6 +77,9 @@ int drm_lock(struct inode *inode, struct file *filp,
80 return -EINVAL; 77 return -EINVAL;
81 78
82 add_wait_queue(&dev->lock.lock_queue, &entry); 79 add_wait_queue(&dev->lock.lock_queue, &entry);
80 spin_lock(&dev->lock.spinlock);
81 dev->lock.user_waiters++;
82 spin_unlock(&dev->lock.spinlock);
83 for (;;) { 83 for (;;) {
84 __set_current_state(TASK_INTERRUPTIBLE); 84 __set_current_state(TASK_INTERRUPTIBLE);
85 if (!dev->lock.hw_lock) { 85 if (!dev->lock.hw_lock) {
@@ -87,7 +87,7 @@ int drm_lock(struct inode *inode, struct file *filp,
87 ret = -EINTR; 87 ret = -EINTR;
88 break; 88 break;
89 } 89 }
90 if (drm_lock_take(&dev->lock.hw_lock->lock, lock.context)) { 90 if (drm_lock_take(&dev->lock, lock.context)) {
91 dev->lock.filp = filp; 91 dev->lock.filp = filp;
92 dev->lock.lock_time = jiffies; 92 dev->lock.lock_time = jiffies;
93 atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); 93 atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
@@ -101,12 +101,14 @@ int drm_lock(struct inode *inode, struct file *filp,
101 break; 101 break;
102 } 102 }
103 } 103 }
104 spin_lock(&dev->lock.spinlock);
105 dev->lock.user_waiters--;
106 spin_unlock(&dev->lock.spinlock);
104 __set_current_state(TASK_RUNNING); 107 __set_current_state(TASK_RUNNING);
105 remove_wait_queue(&dev->lock.lock_queue, &entry); 108 remove_wait_queue(&dev->lock.lock_queue, &entry);
106 109
107 DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); 110 DRM_DEBUG( "%d %s\n", lock.context, ret ? "interrupted" : "has lock" );
108 if (ret) 111 if (ret) return ret;
109 return ret;
110 112
111 sigemptyset(&dev->sigmask); 113 sigemptyset(&dev->sigmask);
112 sigaddset(&dev->sigmask, SIGSTOP); 114 sigaddset(&dev->sigmask, SIGSTOP);
@@ -127,14 +129,12 @@ int drm_lock(struct inode *inode, struct file *filp,
127 } 129 }
128 } 130 }
129 131
130 /* dev->driver->kernel_context_switch isn't used by any of the x86
131 * drivers but is used by the Sparc driver.
132 */
133 if (dev->driver->kernel_context_switch && 132 if (dev->driver->kernel_context_switch &&
134 dev->last_context != lock.context) { 133 dev->last_context != lock.context) {
135 dev->driver->kernel_context_switch(dev, dev->last_context, 134 dev->driver->kernel_context_switch(dev, dev->last_context,
136 lock.context); 135 lock.context);
137 } 136 }
137
138 return 0; 138 return 0;
139} 139}
140 140
@@ -184,12 +184,8 @@ int drm_unlock(struct inode *inode, struct file *filp,
184 if (dev->driver->kernel_context_switch_unlock) 184 if (dev->driver->kernel_context_switch_unlock)
185 dev->driver->kernel_context_switch_unlock(dev); 185 dev->driver->kernel_context_switch_unlock(dev);
186 else { 186 else {
187 drm_lock_transfer(dev, &dev->lock.hw_lock->lock, 187 if (drm_lock_free(&dev->lock,lock.context)) {
188 DRM_KERNEL_CONTEXT); 188 /* FIXME: Should really bail out here. */
189
190 if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
191 DRM_KERNEL_CONTEXT)) {
192 DRM_ERROR("\n");
193 } 189 }
194 } 190 }
195 191
@@ -206,18 +202,26 @@ int drm_unlock(struct inode *inode, struct file *filp,
206 * 202 *
207 * Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction. 203 * Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction.
208 */ 204 */
209int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context) 205int drm_lock_take(drm_lock_data_t *lock_data,
206 unsigned int context)
210{ 207{
211 unsigned int old, new, prev; 208 unsigned int old, new, prev;
209 volatile unsigned int *lock = &lock_data->hw_lock->lock;
212 210
211 spin_lock(&lock_data->spinlock);
213 do { 212 do {
214 old = *lock; 213 old = *lock;
215 if (old & _DRM_LOCK_HELD) 214 if (old & _DRM_LOCK_HELD)
216 new = old | _DRM_LOCK_CONT; 215 new = old | _DRM_LOCK_CONT;
217 else 216 else {
218 new = context | _DRM_LOCK_HELD; 217 new = context | _DRM_LOCK_HELD |
218 ((lock_data->user_waiters + lock_data->kernel_waiters > 1) ?
219 _DRM_LOCK_CONT : 0);
220 }
219 prev = cmpxchg(lock, old, new); 221 prev = cmpxchg(lock, old, new);
220 } while (prev != old); 222 } while (prev != old);
223 spin_unlock(&lock_data->spinlock);
224
221 if (_DRM_LOCKING_CONTEXT(old) == context) { 225 if (_DRM_LOCKING_CONTEXT(old) == context) {
222 if (old & _DRM_LOCK_HELD) { 226 if (old & _DRM_LOCK_HELD) {
223 if (context != DRM_KERNEL_CONTEXT) { 227 if (context != DRM_KERNEL_CONTEXT) {
@@ -227,7 +231,8 @@ int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
227 return 0; 231 return 0;
228 } 232 }
229 } 233 }
230 if (new == (context | _DRM_LOCK_HELD)) { 234
235 if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) {
231 /* Have lock */ 236 /* Have lock */
232 return 1; 237 return 1;
233 } 238 }
@@ -246,13 +251,13 @@ int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
246 * Resets the lock file pointer. 251 * Resets the lock file pointer.
247 * Marks the lock as held by the given context, via the \p cmpxchg instruction. 252 * Marks the lock as held by the given context, via the \p cmpxchg instruction.
248 */ 253 */
249static int drm_lock_transfer(drm_device_t * dev, 254static int drm_lock_transfer(drm_lock_data_t *lock_data,
250 __volatile__ unsigned int *lock,
251 unsigned int context) 255 unsigned int context)
252{ 256{
253 unsigned int old, new, prev; 257 unsigned int old, new, prev;
258 volatile unsigned int *lock = &lock_data->hw_lock->lock;
254 259
255 dev->lock.filp = NULL; 260 lock_data->filp = NULL;
256 do { 261 do {
257 old = *lock; 262 old = *lock;
258 new = context | _DRM_LOCK_HELD; 263 new = context | _DRM_LOCK_HELD;
@@ -272,23 +277,32 @@ static int drm_lock_transfer(drm_device_t * dev,
272 * Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task 277 * Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task
273 * waiting on the lock queue. 278 * waiting on the lock queue.
274 */ 279 */
275int drm_lock_free(drm_device_t * dev, 280int drm_lock_free(drm_lock_data_t *lock_data, unsigned int context)
276 __volatile__ unsigned int *lock, unsigned int context)
277{ 281{
278 unsigned int old, new, prev; 282 unsigned int old, new, prev;
283 volatile unsigned int *lock = &lock_data->hw_lock->lock;
284
285 spin_lock(&lock_data->spinlock);
286 if (lock_data->kernel_waiters != 0) {
287 drm_lock_transfer(lock_data, 0);
288 lock_data->idle_has_lock = 1;
289 spin_unlock(&lock_data->spinlock);
290 return 1;
291 }
292 spin_unlock(&lock_data->spinlock);
279 293
280 dev->lock.filp = NULL;
281 do { 294 do {
282 old = *lock; 295 old = *lock;
283 new = 0; 296 new = _DRM_LOCKING_CONTEXT(old);
284 prev = cmpxchg(lock, old, new); 297 prev = cmpxchg(lock, old, new);
285 } while (prev != old); 298 } while (prev != old);
299
286 if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { 300 if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
287 DRM_ERROR("%d freed heavyweight lock held by %d\n", 301 DRM_ERROR("%d freed heavyweight lock held by %d\n",
288 context, _DRM_LOCKING_CONTEXT(old)); 302 context, _DRM_LOCKING_CONTEXT(old));
289 return 1; 303 return 1;
290 } 304 }
291 wake_up_interruptible(&dev->lock.lock_queue); 305 wake_up_interruptible(&lock_data->lock_queue);
292 return 0; 306 return 0;
293} 307}
294 308
@@ -322,3 +336,67 @@ static int drm_notifier(void *priv)
322 } while (prev != old); 336 } while (prev != old);
323 return 0; 337 return 0;
324} 338}
339
340/**
341 * This function returns immediately and takes the hw lock
342 * with the kernel context if it is free, otherwise it gets the highest priority when and if
343 * it is eventually released.
344 *
345 * This guarantees that the kernel will _eventually_ have the lock _unless_ it is held
346 * by a blocked process. (In the latter case an explicit wait for the hardware lock would cause
347 * a deadlock, which is why the "idlelock" was invented).
348 *
349 * This should be sufficient to wait for GPU idle without
350 * having to worry about starvation.
351 */
352
353void drm_idlelock_take(drm_lock_data_t *lock_data)
354{
355 int ret = 0;
356
357 spin_lock(&lock_data->spinlock);
358 lock_data->kernel_waiters++;
359 if (!lock_data->idle_has_lock) {
360
361 spin_unlock(&lock_data->spinlock);
362 ret = drm_lock_take(lock_data, DRM_KERNEL_CONTEXT);
363 spin_lock(&lock_data->spinlock);
364
365 if (ret == 1)
366 lock_data->idle_has_lock = 1;
367 }
368 spin_unlock(&lock_data->spinlock);
369}
370EXPORT_SYMBOL(drm_idlelock_take);
371
372void drm_idlelock_release(drm_lock_data_t *lock_data)
373{
374 unsigned int old, prev;
375 volatile unsigned int *lock = &lock_data->hw_lock->lock;
376
377 spin_lock(&lock_data->spinlock);
378 if (--lock_data->kernel_waiters == 0) {
379 if (lock_data->idle_has_lock) {
380 do {
381 old = *lock;
382 prev = cmpxchg(lock, old, DRM_KERNEL_CONTEXT);
383 } while (prev != old);
384 wake_up_interruptible(&lock_data->lock_queue);
385 lock_data->idle_has_lock = 0;
386 }
387 }
388 spin_unlock(&lock_data->spinlock);
389}
390EXPORT_SYMBOL(drm_idlelock_release);
391
392
393int drm_i_have_hw_lock(struct file *filp)
394{
395 DRM_DEVICE;
396
397 return (priv->lock_count && dev->lock.hw_lock &&
398 _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
399 dev->lock.filp == filp);
400}
401
402EXPORT_SYMBOL(drm_i_have_hw_lock);