1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/types.h>
27 #include <sys/debug.h>
28 #include <sys/ksynch.h>
29 #include <sys/cmn_err.h>
30 #include <sys/kmem.h>
31 #include <sys/ddi.h>
32
33 #define __NSC_GEN__
34 #include "nsc_gen.h"
35 #include "nsc_mem.h"
36 #include "nsc_rmspin.h"
37 #include "../nsctl.h"
38
39
40 static kmutex_t _nsc_rmspin_slp;
41
42 nsc_rmlock_t _nsc_lock_top;
43 kmutex_t _nsc_global_lock;
44 int _nsc_global_lock_init;
45
46 extern nsc_mem_t *_nsc_local_mem;
47
48 /*
49 * void
50 * _nsc_init_rmlock (void)
51 * Initialise global locks.
52 *
53 * Calling/Exit State:
54 * Called at driver initialisation time to allocate necessary
55 * data structures.
56 */
57 void
_nsc_init_rmlock()58 _nsc_init_rmlock()
59 {
60 mutex_init(&_nsc_rmspin_slp, NULL, MUTEX_DRIVER, NULL);
61
62 _nsc_lock_top.next = _nsc_lock_top.prev = &_nsc_lock_top;
63
64 mutex_init(&_nsc_global_lock, NULL, MUTEX_DRIVER, NULL);
65 _nsc_global_lock_init = 1;
66 }
67
68
69 /*
70 * void
71 * _nsc_deinit_rmlock (void)
72 * De-initialise global locks.
73 *
74 * Calling/Exit State:
75 * Called at driver unload time to de-allocate
76 * resources.
77 */
78 void
_nsc_deinit_rmlock()79 _nsc_deinit_rmlock()
80 {
81 _nsc_global_lock_init = 0;
82 mutex_destroy(&_nsc_global_lock);
83
84 ASSERT(_nsc_lock_top.next == &_nsc_lock_top);
85 ASSERT(_nsc_lock_top.prev == &_nsc_lock_top);
86
87 mutex_destroy(&_nsc_rmspin_slp);
88 }
89
90
91 /*
92 * int
93 * _nsc_lock_all_rm (void)
94 * Take all global locks in address order.
95 *
96 * Calling/Exit State:
97 * Returns 0 if _nsc_unlock_all_rm() should be called, or -1.
98 */
99 int
_nsc_lock_all_rm()100 _nsc_lock_all_rm()
101 {
102 nsc_rmlock_t *lp;
103
104 mutex_enter(&_nsc_rmspin_slp);
105
106 for (lp = _nsc_lock_top.next; lp != &_nsc_lock_top; lp = lp->next) {
107 (void) nsc_rm_lock(lp);
108 }
109
110 return (0);
111 }
112
113
114 /*
115 * void
116 * _nsc_unlock_all_rm (void)
117 * Release all global locks in reverse address order.
118 *
119 * Calling/Exit State:
120 */
121 void
_nsc_unlock_all_rm()122 _nsc_unlock_all_rm()
123 {
124 nsc_rmlock_t *lp;
125
126 for (lp = _nsc_lock_top.prev; lp != &_nsc_lock_top; lp = lp->prev) {
127 nsc_rm_unlock(lp);
128 }
129
130 mutex_exit(&_nsc_rmspin_slp);
131 }
132
133
134 /*
135 * nsc_rmlock_t *
136 * nsc_rm_lock_alloc(char *name, int flag, void *arg)
137 * Allocate and initialise a global lock.
138 *
139 * Calling/Exit State:
140 * The 'flag' parameter should be either KM_SLEEP or KM_NOSLEEP,
141 * depending on whether the caller is willing to sleep while memory
142 * is allocated or not.
143 *
144 * The 'arg' parameter is passed directly to the underlying
145 * mutex_init(9f) function call.
146 *
147 * Returns NULL if lock cannot be allocated.
148 */
149 nsc_rmlock_t *
nsc_rm_lock_alloc(char * name,int flag,void * arg)150 nsc_rm_lock_alloc(char *name, int flag, void *arg)
151 {
152 nsc_rmlock_t *lp, *lk;
153
154 if ((lk = (nsc_rmlock_t *)nsc_kmem_zalloc(sizeof (*lk),
155 flag, _nsc_local_mem)) == NULL)
156 return (NULL);
157
158 mutex_init(&lk->lockp, NULL, MUTEX_DRIVER, arg);
159
160 mutex_enter(&_nsc_rmspin_slp);
161
162 for (lp = _nsc_lock_top.next; lp != &_nsc_lock_top; lp = lp->next)
163 if (strcmp(lp->name, name) == 0)
164 break;
165
166 if (lp != &_nsc_lock_top) {
167 mutex_exit(&_nsc_rmspin_slp);
168
169 mutex_destroy(&lk->lockp);
170 nsc_kmem_free(lk, sizeof (*lk));
171
172 cmn_err(CE_WARN, "!nsctl: rmlock double allocation (%s)", name);
173 return (NULL);
174 }
175
176 lk->name = name;
177
178 lk->next = _nsc_lock_top.next;
179 lk->prev = &_nsc_lock_top;
180 _nsc_lock_top.next = lk;
181 lk->next->prev = lk;
182
183 mutex_exit(&_nsc_rmspin_slp);
184
185 return (lk);
186 }
187
188
189 /*
190 * void
191 * nsc_rm_lock_destroy(nsc_rmlock_t *rmlockp)
192 * Release the global lock.
193 *
194 * Remarks:
195 * The specified global lock is released and made
196 * available for reallocation.
197 */
198 void
nsc_rm_lock_dealloc(rmlockp)199 nsc_rm_lock_dealloc(rmlockp)
200 nsc_rmlock_t *rmlockp;
201 {
202 if (!rmlockp)
203 return;
204
205 mutex_enter(&_nsc_rmspin_slp);
206
207 rmlockp->next->prev = rmlockp->prev;
208 rmlockp->prev->next = rmlockp->next;
209
210 if (rmlockp->child) {
211 cmn_err(CE_WARN, "!nsctl: rmlock destroyed when locked (%s)",
212 rmlockp->name);
213 nsc_do_unlock(rmlockp->child);
214 rmlockp->child = NULL;
215 }
216
217 mutex_destroy(&rmlockp->lockp);
218 mutex_exit(&_nsc_rmspin_slp);
219
220 nsc_kmem_free(rmlockp, sizeof (*rmlockp));
221 }
222
223
224 /*
225 * void
226 * nsc_rm_lock(nsc_rmlock_t *rmlockp)
227 * Acquire a global lock.
228 *
229 * Calling/Exit State:
230 * rmlockp is the lock to be acquired.
231 * Returns 0 (success) or errno. Lock is not acquired if rc != 0.
232 */
233 int
nsc_rm_lock(nsc_rmlock_t * rmlockp)234 nsc_rm_lock(nsc_rmlock_t *rmlockp)
235 {
236 int rc;
237
238 mutex_enter(&rmlockp->lockp);
239
240 ASSERT(! rmlockp->child);
241
242 /* always use a write-lock */
243 rc = nsc_do_lock(1, &rmlockp->child);
244 if (rc) {
245 rmlockp->child = NULL;
246 mutex_exit(&rmlockp->lockp);
247 }
248
249 return (rc);
250 }
251
252
253 /*
254 * static void
255 * nsc_rm_unlock(nsc_rmlock_t *rmlockp)
256 * Unlock a global lock.
257 *
258 * Calling/Exit State:
259 * rmlockp is the lock to be released.
260 */
261 void
nsc_rm_unlock(nsc_rmlock_t * rmlockp)262 nsc_rm_unlock(nsc_rmlock_t *rmlockp)
263 {
264 if (rmlockp->child) {
265 ASSERT(MUTEX_HELD(&rmlockp->lockp));
266 nsc_do_unlock(rmlockp->child);
267 rmlockp->child = NULL;
268 mutex_exit(&rmlockp->lockp);
269 }
270 }
271