1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * lock.c -- IOCTLs for locking -*- linux-c -*-
8 * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com
9 */
10 /*
11 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
12 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
13 * All Rights Reserved.
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a
16 * copy of this software and associated documentation files (the "Software"),
17 * to deal in the Software without restriction, including without limitation
18 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19 * and/or sell copies of the Software, and to permit persons to whom the
20 * Software is furnished to do so, subject to the following conditions:
21 *
22 * The above copyright notice and this permission notice (including the next
23 * paragraph) shall be included in all copies or substantial portions of the
24 * Software.
25 *
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
29 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
30 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
31 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
32 * OTHER DEALINGS IN THE SOFTWARE.
33 *
34 * Authors:
35 * Rickard E. (Rik) Faith <faith@valinux.com>
36 * Gareth Hughes <gareth@valinux.com>
37 *
38 */
39
40 #include "drmP.h"
41
42 int
drm_lock_take(drm_lock_data_t * lock_data,unsigned int context)43 drm_lock_take(drm_lock_data_t *lock_data, unsigned int context)
44 {
45 unsigned int old, new;
46 volatile unsigned int *lock = &lock_data->hw_lock->lock;
47
48 do {
49 old = *lock;
50 if (old & _DRM_LOCK_HELD)
51 new = old | _DRM_LOCK_CONT;
52 else
53 new = context | _DRM_LOCK_HELD;
54 } while (!atomic_cmpset_int(lock, old, new));
55
56 if (_DRM_LOCKING_CONTEXT(old) == context) {
57 if (old & _DRM_LOCK_HELD) {
58 if (context != DRM_KERNEL_CONTEXT) {
59 DRM_ERROR("%d holds heavyweight lock\n",
60 context);
61 }
62 return (0);
63 }
64 }
65 if (new == (context | _DRM_LOCK_HELD)) {
66 /* Have lock */
67 return (1);
68 }
69 return (0);
70 }
71
72 /*
73 * This takes a lock forcibly and hands it to context. Should ONLY be used
74 * inside *_unlock to give lock to kernel before calling *_dma_schedule.
75 */
76 int
drm_lock_transfer(drm_device_t * dev,drm_lock_data_t * lock_data,unsigned int context)77 drm_lock_transfer(drm_device_t *dev, drm_lock_data_t *lock_data,
78 unsigned int context)
79 {
80 unsigned int old, new;
81 volatile unsigned int *lock = &lock_data->hw_lock->lock;
82
83 dev->lock.filp = NULL;
84 do {
85 old = *lock;
86 new = context | _DRM_LOCK_HELD;
87 } while (!atomic_cmpset_int(lock, old, new));
88
89 return (1);
90 }
91
92 int
drm_lock_free(drm_device_t * dev,volatile unsigned int * lock,unsigned int context)93 drm_lock_free(drm_device_t *dev, volatile unsigned int *lock,
94 unsigned int context)
95 {
96 unsigned int old, new;
97
98 mutex_enter(&(dev->lock.lock_mutex));
99 dev->lock.filp = NULL;
100 do {
101 old = *lock;
102 new = 0;
103 } while (!atomic_cmpset_int(lock, old, new));
104
105 if (_DRM_LOCK_IS_HELD(old) &&
106 (_DRM_LOCKING_CONTEXT(old) != context)) {
107 DRM_ERROR("%d freed heavyweight lock held by %d\n",
108 context, _DRM_LOCKING_CONTEXT(old));
109 mutex_exit(&(dev->lock.lock_mutex));
110 return (1);
111 }
112 cv_broadcast(&(dev->lock.lock_cv));
113 mutex_exit(&(dev->lock.lock_mutex));
114 return (0);
115 }
116
117 /*ARGSUSED*/
118 int
drm_lock(DRM_IOCTL_ARGS)119 drm_lock(DRM_IOCTL_ARGS)
120 {
121 DRM_DEVICE;
122 drm_lock_t lock;
123 int ret = 0;
124
125 DRM_COPYFROM_WITH_RETURN(&lock, (void *)data, sizeof (lock));
126
127 if (lock.context == DRM_KERNEL_CONTEXT) {
128 DRM_ERROR("Process %d using kernel context %d\n",
129 DRM_CURRENTPID, lock.context);
130 return (EINVAL);
131 }
132
133 DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
134 lock.context, DRM_CURRENTPID, dev->lock.hw_lock->lock,
135 lock.flags);
136 if (dev->driver->use_dma_queue && lock.context < 0)
137 return (EINVAL);
138
139 mutex_enter(&(dev->lock.lock_mutex));
140 for (;;) {
141 if (drm_lock_take(&dev->lock, lock.context)) {
142 dev->lock.filp = fpriv;
143 dev->lock.lock_time = ddi_get_lbolt();
144 break; /* Got lock */
145 }
146 ret = cv_wait_sig(&(dev->lock.lock_cv),
147 &(dev->lock.lock_mutex));
148
149 if (ret == 0) {
150 mutex_exit(&(dev->lock.lock_mutex));
151 return (EINTR);
152 }
153 }
154 mutex_exit(&(dev->lock.lock_mutex));
155 DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
156
157 if (dev->driver->dma_quiescent != NULL &&
158 (lock.flags & _DRM_LOCK_QUIESCENT))
159 dev->driver->dma_quiescent(dev);
160
161 return (0);
162 }
163
164 /*ARGSUSED*/
165 int
drm_unlock(DRM_IOCTL_ARGS)166 drm_unlock(DRM_IOCTL_ARGS)
167 {
168 DRM_DEVICE;
169 drm_lock_t lock;
170
171 DRM_COPYFROM_WITH_RETURN(&lock, (void *)data, sizeof (lock));
172
173 DRM_DEBUG("%d (pid %d) requests unlock (0x%08x), flags = 0x%08x\n",
174 lock.context, DRM_CURRENTPID, dev->lock.hw_lock->lock,
175 lock.flags);
176
177 if (lock.context == DRM_KERNEL_CONTEXT) {
178 DRM_ERROR("Process %d using kernel context %d\n",
179 DRM_CURRENTPID, lock.context);
180 return (EINVAL);
181 }
182 atomic_inc_32(&dev->counts[_DRM_STAT_UNLOCKS]);
183
184 DRM_LOCK();
185 if (drm_lock_free(dev, &dev->lock.hw_lock->lock, lock.context)) {
186 DRM_ERROR("drm_unlock\n");
187 }
188 DRM_UNLOCK();
189 return (0);
190 }
191