diff options
Diffstat (limited to 'drivers/char/drm/drm_lock.c')
-rw-r--r-- | drivers/char/drm/drm_lock.c | 134 |
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 | ||
38 | static int drm_lock_transfer(drm_device_t * dev, | ||
39 | __volatile__ unsigned int *lock, | ||
40 | unsigned int context); | ||
41 | static int drm_notifier(void *priv); | 38 | static 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 | */ |
209 | int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context) | 205 | int 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 | */ |
249 | static int drm_lock_transfer(drm_device_t * dev, | 254 | static 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 | */ |
275 | int drm_lock_free(drm_device_t * dev, | 280 | int 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 | |||
353 | void 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 | } | ||
370 | EXPORT_SYMBOL(drm_idlelock_take); | ||
371 | |||
372 | void 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 | } | ||
390 | EXPORT_SYMBOL(drm_idlelock_release); | ||
391 | |||
392 | |||
393 | int 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 | |||
402 | EXPORT_SYMBOL(drm_i_have_hw_lock); | ||