aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Hellstrom <thellstrom@vmware.com>2009-12-06 15:46:25 -0500
committerDave Airlie <airlied@redhat.com>2009-12-07 00:22:03 -0500
commit4aff1013f5e4ae08a24155c029a2c5e1a7929de6 (patch)
tree1317322a9e6e4c6094cfa32090c05c20db252ec0
parent88071539a3f5195f9e9dae38a3e35b3ce4b9f9fc (diff)
drm/ttm: Add ttm lock functionality.
This is intended to be used by ttm-aware drivers to 1) Block clients to inactive masters when they try to validate buffers for GPU use. 2) Optionally block clients to the current master when there is thrashing due to GPU memory shortage. Used by the vmwgfx driver. Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r--drivers/gpu/drm/ttm/Makefile2
-rw-r--r--drivers/gpu/drm/ttm/ttm_lock.c311
-rw-r--r--include/drm/ttm/ttm_lock.h247
3 files changed, 559 insertions, 1 deletions
diff --git a/drivers/gpu/drm/ttm/Makefile b/drivers/gpu/drm/ttm/Makefile
index a9cc9f895360..4473549bc5d2 100644
--- a/drivers/gpu/drm/ttm/Makefile
+++ b/drivers/gpu/drm/ttm/Makefile
@@ -4,6 +4,6 @@
4ccflags-y := -Iinclude/drm 4ccflags-y := -Iinclude/drm
5ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \ 5ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \
6 ttm_bo_util.o ttm_bo_vm.o ttm_module.o ttm_global.o \ 6 ttm_bo_util.o ttm_bo_vm.o ttm_module.o ttm_global.o \
7 ttm_object.o 7 ttm_object.o ttm_lock.o
8 8
9obj-$(CONFIG_DRM_TTM) += ttm.o 9obj-$(CONFIG_DRM_TTM) += ttm.o
diff --git a/drivers/gpu/drm/ttm/ttm_lock.c b/drivers/gpu/drm/ttm/ttm_lock.c
new file mode 100644
index 000000000000..f619ebcaa4ec
--- /dev/null
+++ b/drivers/gpu/drm/ttm/ttm_lock.c
@@ -0,0 +1,311 @@
1/**************************************************************************
2 *
3 * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27/*
28 * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
29 */
30
31#include "ttm/ttm_lock.h"
32#include "ttm/ttm_module.h"
33#include <asm/atomic.h>
34#include <linux/errno.h>
35#include <linux/wait.h>
36#include <linux/sched.h>
37#include <linux/module.h>
38
39#define TTM_WRITE_LOCK_PENDING (1 << 0)
40#define TTM_VT_LOCK_PENDING (1 << 1)
41#define TTM_SUSPEND_LOCK_PENDING (1 << 2)
42#define TTM_VT_LOCK (1 << 3)
43#define TTM_SUSPEND_LOCK (1 << 4)
44
45void ttm_lock_init(struct ttm_lock *lock)
46{
47 spin_lock_init(&lock->lock);
48 init_waitqueue_head(&lock->queue);
49 lock->rw = 0;
50 lock->flags = 0;
51 lock->kill_takers = false;
52 lock->signal = SIGKILL;
53}
54EXPORT_SYMBOL(ttm_lock_init);
55
56void ttm_read_unlock(struct ttm_lock *lock)
57{
58 spin_lock(&lock->lock);
59 if (--lock->rw == 0)
60 wake_up_all(&lock->queue);
61 spin_unlock(&lock->lock);
62}
63EXPORT_SYMBOL(ttm_read_unlock);
64
65static bool __ttm_read_lock(struct ttm_lock *lock)
66{
67 bool locked = false;
68
69 spin_lock(&lock->lock);
70 if (unlikely(lock->kill_takers)) {
71 send_sig(lock->signal, current, 0);
72 spin_unlock(&lock->lock);
73 return false;
74 }
75 if (lock->rw >= 0 && lock->flags == 0) {
76 ++lock->rw;
77 locked = true;
78 }
79 spin_unlock(&lock->lock);
80 return locked;
81}
82
83int ttm_read_lock(struct ttm_lock *lock, bool interruptible)
84{
85 int ret = 0;
86
87 if (interruptible)
88 ret = wait_event_interruptible(lock->queue,
89 __ttm_read_lock(lock));
90 else
91 wait_event(lock->queue, __ttm_read_lock(lock));
92 return ret;
93}
94EXPORT_SYMBOL(ttm_read_lock);
95
96static bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked)
97{
98 bool block = true;
99
100 *locked = false;
101
102 spin_lock(&lock->lock);
103 if (unlikely(lock->kill_takers)) {
104 send_sig(lock->signal, current, 0);
105 spin_unlock(&lock->lock);
106 return false;
107 }
108 if (lock->rw >= 0 && lock->flags == 0) {
109 ++lock->rw;
110 block = false;
111 *locked = true;
112 } else if (lock->flags == 0) {
113 block = false;
114 }
115 spin_unlock(&lock->lock);
116
117 return !block;
118}
119
120int ttm_read_trylock(struct ttm_lock *lock, bool interruptible)
121{
122 int ret = 0;
123 bool locked;
124
125 if (interruptible)
126 ret = wait_event_interruptible
127 (lock->queue, __ttm_read_trylock(lock, &locked));
128 else
129 wait_event(lock->queue, __ttm_read_trylock(lock, &locked));
130
131 if (unlikely(ret != 0)) {
132 BUG_ON(locked);
133 return ret;
134 }
135
136 return (locked) ? 0 : -EBUSY;
137}
138
139void ttm_write_unlock(struct ttm_lock *lock)
140{
141 spin_lock(&lock->lock);
142 lock->rw = 0;
143 wake_up_all(&lock->queue);
144 spin_unlock(&lock->lock);
145}
146EXPORT_SYMBOL(ttm_write_unlock);
147
148static bool __ttm_write_lock(struct ttm_lock *lock)
149{
150 bool locked = false;
151
152 spin_lock(&lock->lock);
153 if (unlikely(lock->kill_takers)) {
154 send_sig(lock->signal, current, 0);
155 spin_unlock(&lock->lock);
156 return false;
157 }
158 if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) {
159 lock->rw = -1;
160 lock->flags &= ~TTM_WRITE_LOCK_PENDING;
161 locked = true;
162 } else {
163 lock->flags |= TTM_WRITE_LOCK_PENDING;
164 }
165 spin_unlock(&lock->lock);
166 return locked;
167}
168
169int ttm_write_lock(struct ttm_lock *lock, bool interruptible)
170{
171 int ret = 0;
172
173 if (interruptible) {
174 ret = wait_event_interruptible(lock->queue,
175 __ttm_write_lock(lock));
176 if (unlikely(ret != 0)) {
177 spin_lock(&lock->lock);
178 lock->flags &= ~TTM_WRITE_LOCK_PENDING;
179 wake_up_all(&lock->queue);
180 spin_unlock(&lock->lock);
181 }
182 } else
183 wait_event(lock->queue, __ttm_read_lock(lock));
184
185 return ret;
186}
187EXPORT_SYMBOL(ttm_write_lock);
188
189void ttm_write_lock_downgrade(struct ttm_lock *lock)
190{
191 spin_lock(&lock->lock);
192 lock->rw = 1;
193 wake_up_all(&lock->queue);
194 spin_unlock(&lock->lock);
195}
196
197static int __ttm_vt_unlock(struct ttm_lock *lock)
198{
199 int ret = 0;
200
201 spin_lock(&lock->lock);
202 if (unlikely(!(lock->flags & TTM_VT_LOCK)))
203 ret = -EINVAL;
204 lock->flags &= ~TTM_VT_LOCK;
205 wake_up_all(&lock->queue);
206 spin_unlock(&lock->lock);
207 printk(KERN_INFO TTM_PFX "vt unlock.\n");
208
209 return ret;
210}
211
212static void ttm_vt_lock_remove(struct ttm_base_object **p_base)
213{
214 struct ttm_base_object *base = *p_base;
215 struct ttm_lock *lock = container_of(base, struct ttm_lock, base);
216 int ret;
217
218 *p_base = NULL;
219 ret = __ttm_vt_unlock(lock);
220 BUG_ON(ret != 0);
221}
222
223static bool __ttm_vt_lock(struct ttm_lock *lock)
224{
225 bool locked = false;
226
227 spin_lock(&lock->lock);
228 if (lock->rw == 0) {
229 lock->flags &= ~TTM_VT_LOCK_PENDING;
230 lock->flags |= TTM_VT_LOCK;
231 locked = true;
232 } else {
233 lock->flags |= TTM_VT_LOCK_PENDING;
234 }
235 spin_unlock(&lock->lock);
236 return locked;
237}
238
239int ttm_vt_lock(struct ttm_lock *lock,
240 bool interruptible,
241 struct ttm_object_file *tfile)
242{
243 int ret = 0;
244
245 if (interruptible) {
246 ret = wait_event_interruptible(lock->queue,
247 __ttm_vt_lock(lock));
248 if (unlikely(ret != 0)) {
249 spin_lock(&lock->lock);
250 lock->flags &= ~TTM_VT_LOCK_PENDING;
251 wake_up_all(&lock->queue);
252 spin_unlock(&lock->lock);
253 return ret;
254 }
255 } else
256 wait_event(lock->queue, __ttm_vt_lock(lock));
257
258 /*
259 * Add a base-object, the destructor of which will
260 * make sure the lock is released if the client dies
261 * while holding it.
262 */
263
264 ret = ttm_base_object_init(tfile, &lock->base, false,
265 ttm_lock_type, &ttm_vt_lock_remove, NULL);
266 if (ret)
267 (void)__ttm_vt_unlock(lock);
268 else {
269 lock->vt_holder = tfile;
270 printk(KERN_INFO TTM_PFX "vt lock.\n");
271 }
272
273 return ret;
274}
275EXPORT_SYMBOL(ttm_vt_lock);
276
277int ttm_vt_unlock(struct ttm_lock *lock)
278{
279 return ttm_ref_object_base_unref(lock->vt_holder,
280 lock->base.hash.key, TTM_REF_USAGE);
281}
282EXPORT_SYMBOL(ttm_vt_unlock);
283
284void ttm_suspend_unlock(struct ttm_lock *lock)
285{
286 spin_lock(&lock->lock);
287 lock->flags &= ~TTM_SUSPEND_LOCK;
288 wake_up_all(&lock->queue);
289 spin_unlock(&lock->lock);
290}
291
292static bool __ttm_suspend_lock(struct ttm_lock *lock)
293{
294 bool locked = false;
295
296 spin_lock(&lock->lock);
297 if (lock->rw == 0) {
298 lock->flags &= ~TTM_SUSPEND_LOCK_PENDING;
299 lock->flags |= TTM_SUSPEND_LOCK;
300 locked = true;
301 } else {
302 lock->flags |= TTM_SUSPEND_LOCK_PENDING;
303 }
304 spin_unlock(&lock->lock);
305 return locked;
306}
307
308void ttm_suspend_lock(struct ttm_lock *lock)
309{
310 wait_event(lock->queue, __ttm_suspend_lock(lock));
311}
diff --git a/include/drm/ttm/ttm_lock.h b/include/drm/ttm/ttm_lock.h
new file mode 100644
index 000000000000..81ba0b0b891a
--- /dev/null
+++ b/include/drm/ttm/ttm_lock.h
@@ -0,0 +1,247 @@
1/**************************************************************************
2 *
3 * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27/*
28 * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
29 */
30
31/** @file ttm_lock.h
32 * This file implements a simple replacement for the buffer manager use
33 * of the DRM heavyweight hardware lock.
34 * The lock is a read-write lock. Taking it in read mode and write mode
35 * is relatively fast, and intended for in-kernel use only.
36 *
37 * The vt mode is used only when there is a need to block all
38 * user-space processes from validating buffers.
39 * It's allowed to leave kernel space with the vt lock held.
40 * If a user-space process dies while having the vt-lock,
41 * it will be released during the file descriptor release. The vt lock
42 * excludes write lock and read lock.
43 *
44 * The suspend mode is used to lock out all TTM users when preparing for
45 * and executing suspend operations.
46 *
47 */
48
49#ifndef _TTM_LOCK_H_
50#define _TTM_LOCK_H_
51
52#include "ttm/ttm_object.h"
53#include <linux/wait.h>
54#include <asm/atomic.h>
55
56/**
57 * struct ttm_lock
58 *
59 * @base: ttm base object used solely to release the lock if the client
60 * holding the lock dies.
61 * @queue: Queue for processes waiting for lock change-of-status.
62 * @lock: Spinlock protecting some lock members.
63 * @rw: Read-write lock counter. Protected by @lock.
64 * @flags: Lock state. Protected by @lock.
65 * @kill_takers: Boolean whether to kill takers of the lock.
66 * @signal: Signal to send when kill_takers is true.
67 */
68
69struct ttm_lock {
70 struct ttm_base_object base;
71 wait_queue_head_t queue;
72 spinlock_t lock;
73 int32_t rw;
74 uint32_t flags;
75 bool kill_takers;
76 int signal;
77 struct ttm_object_file *vt_holder;
78};
79
80
81/**
82 * ttm_lock_init
83 *
84 * @lock: Pointer to a struct ttm_lock
85 * Initializes the lock.
86 */
87extern void ttm_lock_init(struct ttm_lock *lock);
88
89/**
90 * ttm_read_unlock
91 *
92 * @lock: Pointer to a struct ttm_lock
93 *
94 * Releases a read lock.
95 */
96extern void ttm_read_unlock(struct ttm_lock *lock);
97
98/**
99 * ttm_read_lock
100 *
101 * @lock: Pointer to a struct ttm_lock
102 * @interruptible: Interruptible sleeping while waiting for a lock.
103 *
104 * Takes the lock in read mode.
105 * Returns:
106 * -ERESTARTSYS If interrupted by a signal and interruptible is true.
107 */
108extern int ttm_read_lock(struct ttm_lock *lock, bool interruptible);
109
110/**
111 * ttm_read_trylock
112 *
113 * @lock: Pointer to a struct ttm_lock
114 * @interruptible: Interruptible sleeping while waiting for a lock.
115 *
116 * Tries to take the lock in read mode. If the lock is already held
117 * in write mode, the function will return -EBUSY. If the lock is held
118 * in vt or suspend mode, the function will sleep until these modes
119 * are unlocked.
120 *
121 * Returns:
122 * -EBUSY The lock was already held in write mode.
123 * -ERESTARTSYS If interrupted by a signal and interruptible is true.
124 */
125extern int ttm_read_trylock(struct ttm_lock *lock, bool interruptible);
126
127/**
128 * ttm_write_unlock
129 *
130 * @lock: Pointer to a struct ttm_lock
131 *
132 * Releases a write lock.
133 */
134extern void ttm_write_unlock(struct ttm_lock *lock);
135
136/**
137 * ttm_write_lock
138 *
139 * @lock: Pointer to a struct ttm_lock
140 * @interruptible: Interruptible sleeping while waiting for a lock.
141 *
142 * Takes the lock in write mode.
143 * Returns:
144 * -ERESTARTSYS If interrupted by a signal and interruptible is true.
145 */
146extern int ttm_write_lock(struct ttm_lock *lock, bool interruptible);
147
148/**
149 * ttm_lock_downgrade
150 *
151 * @lock: Pointer to a struct ttm_lock
152 *
153 * Downgrades a write lock to a read lock.
154 */
155extern void ttm_lock_downgrade(struct ttm_lock *lock);
156
157/**
158 * ttm_suspend_lock
159 *
160 * @lock: Pointer to a struct ttm_lock
161 *
162 * Takes the lock in suspend mode. Excludes read and write mode.
163 */
164extern void ttm_suspend_lock(struct ttm_lock *lock);
165
166/**
167 * ttm_suspend_unlock
168 *
169 * @lock: Pointer to a struct ttm_lock
170 *
171 * Releases a suspend lock
172 */
173extern void ttm_suspend_unlock(struct ttm_lock *lock);
174
175/**
176 * ttm_vt_lock
177 *
178 * @lock: Pointer to a struct ttm_lock
179 * @interruptible: Interruptible sleeping while waiting for a lock.
180 * @tfile: Pointer to a struct ttm_object_file to register the lock with.
181 *
182 * Takes the lock in vt mode.
183 * Returns:
184 * -ERESTARTSYS If interrupted by a signal and interruptible is true.
185 * -ENOMEM: Out of memory when locking.
186 */
187extern int ttm_vt_lock(struct ttm_lock *lock, bool interruptible,
188 struct ttm_object_file *tfile);
189
190/**
191 * ttm_vt_unlock
192 *
193 * @lock: Pointer to a struct ttm_lock
194 *
195 * Releases a vt lock.
196 * Returns:
197 * -EINVAL If the lock was not held.
198 */
199extern int ttm_vt_unlock(struct ttm_lock *lock);
200
201/**
202 * ttm_write_unlock
203 *
204 * @lock: Pointer to a struct ttm_lock
205 *
206 * Releases a write lock.
207 */
208extern void ttm_write_unlock(struct ttm_lock *lock);
209
210/**
211 * ttm_write_lock
212 *
213 * @lock: Pointer to a struct ttm_lock
214 * @interruptible: Interruptible sleeping while waiting for a lock.
215 *
216 * Takes the lock in write mode.
217 * Returns:
218 * -ERESTARTSYS If interrupted by a signal and interruptible is true.
219 */
220extern int ttm_write_lock(struct ttm_lock *lock, bool interruptible);
221
222/**
223 * ttm_lock_set_kill
224 *
225 * @lock: Pointer to a struct ttm_lock
226 * @val: Boolean whether to kill processes taking the lock.
227 * @signal: Signal to send to the process taking the lock.
228 *
229 * The kill-when-taking-lock functionality is used to kill processes that keep
230 * on using the TTM functionality when its resources has been taken down, for
231 * example when the X server exits. A typical sequence would look like this:
232 * - X server takes lock in write mode.
233 * - ttm_lock_set_kill() is called with @val set to true.
234 * - As part of X server exit, TTM resources are taken down.
235 * - X server releases the lock on file release.
236 * - Another dri client wants to render, takes the lock and is killed.
237 *
238 */
239static inline void ttm_lock_set_kill(struct ttm_lock *lock, bool val,
240 int signal)
241{
242 lock->kill_takers = val;
243 if (val)
244 lock->signal = signal;
245}
246
247#endif