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 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <sys/systm.h>
28 #include <sys/sdt.h>
29 #include <sys/atomic.h>
30 #include <rpc/types.h>
31 #include <rpc/auth.h>
32 #include <rpc/auth_unix.h>
33 #include <rpc/auth_des.h>
34 #include <rpc/svc.h>
35 #include <rpc/xdr.h>
36 #include <nfs/nfs4.h>
37 #include <nfs/nfs_dispatch.h>
38 #include <sys/cmn_err.h>
39 #include <sys/modctl.h>
40
41 void sltab_create(stok_t **, int);
42 int sltab_resize(stok_t *, int);
43 void sltab_query(stok_t *, slt_query_t, void *);
44 void sltab_destroy(stok_t *);
45 void sltab_set_cleanup(stok_t *, void (*)(slot_ent_t *));
46 int slot_alloc(stok_t *, slt_wait_t, slot_ent_t **);
47 int slot_delete(stok_t *handle, slot_ent_t *node);
48
49 /*
50 * Slot Table and Slot Cache Management Support
51 */
52
53 /*
54 * session
55 * .- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -.
56 * | sltab |
57 * | +---------------------------------------------------------+ |
58 * |sl0 | se_state se_lock se_wait se_sltno se_seqid se_clnt | slot_ent_t|
59 * | +---------------------------------------------------------+ |
60 * ` _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _'
61 *
62 *
63 * Design Notes:
64 *
65 * 1) slot table token (stok), created via sltab_create(), is the token by
66 * which the interface consumer instantiates further calls to the API.
67 *
68 * 2) The stok contains metadata pertinent to _that_ slot table (current
69 * width, current free slots, caller ctxt, state). It also has an overall
70 * cache lock and cv for slot usage and synchronization along with cache
71 * wide manipulation (growth, shrink, etc).
72 *
73 * Hence, locking order is as follows for the pertinent interfaces:
74 *
75 * sltab_create:
76 * No locking required <lock initialization>
77 * sltab_destroy:
78 * sltab_resize:
79 * st_lock <resizing/destruction>
80 * slot_alloc:
81 * slot_free:
82 * st_lock -> se_lock <slot acquisition/release>
83 *
84 * 3) sltab_resize will be used to grow/shrink the cache. This will result
85 * in the entire cache being quiesced while a new array of pointers
86 * (reflecting the new "width") is allocated. These new ptrs will then
87 * be set to the values pointed to by stok->sltab[n]. At this point,
88 * the old slrc pointers will be freed and stok->sltab updated to point
89 * to the newly allocated array of slot pointers.
90 *
91 * Bottom line: Consumers of the interface can continue to treat stok
92 * as an opaque token, since resizing (and reallocation) of
93 * the cache happens deep w/in the interfaces, so user remains
94 * happily oblivious.
95 */
96
97 static int
sltab_slot_cmp(const void * a,const void * b)98 sltab_slot_cmp(const void *a, const void *b)
99 {
100 const slot_ent_t *ra = (slot_ent_t *)a;
101 const slot_ent_t *rb = (slot_ent_t *)b;
102
103 /*
104 * Comparision is with slot id.
105 */
106 if (ra->se_sltno < rb->se_sltno)
107 return (-1);
108 if (ra->se_sltno > rb->se_sltno)
109 return (+1);
110 return (0);
111 }
112
113
114 void
sltab_create(stok_t ** handle,int max_slots)115 sltab_create(stok_t **handle, int max_slots)
116 {
117 avl_tree_t *tree = NULL;
118 stok_t *tok;
119
120 tok = kmem_alloc(sizeof (stok_t), KM_SLEEP);
121 tree = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP);
122 avl_create(tree,
123 sltab_slot_cmp,
124 sizeof (slot_ent_t),
125 offsetof(slot_ent_t, se_node));
126 mutex_init(&tok->st_lock, NULL, MUTEX_DEFAULT, NULL);
127 cv_init(&tok->st_wait, NULL, CV_DEFAULT, NULL);
128 tok->st_sltab = tree;
129 tok->st_currw = max_slots;
130 tok->st_fslots = max_slots;
131 tok->cleanup_entry = NULL;
132 *handle = tok;
133 }
134
135 void
sltab_destroy(stok_t * handle)136 sltab_destroy(stok_t *handle)
137 {
138 avl_tree_t *replaytree = handle->st_sltab;
139 slot_ent_t *tmp, *next;
140
141 mutex_enter(&handle->st_lock);
142 for (tmp = avl_first(replaytree); tmp != NULL; tmp = next) {
143 next = AVL_NEXT(replaytree, tmp);
144 slot_delete(handle, tmp);
145 }
146 mutex_exit(&handle->st_lock);
147 avl_destroy(replaytree);
148 kmem_free(replaytree, sizeof (avl_tree_t));
149 cv_destroy(&handle->st_wait);
150 mutex_destroy(&handle->st_lock);
151 kmem_free(handle, sizeof (stok_t));
152 }
153
154 /*
155 * Resize the tree.
156 * If the maxslots are decreased, remove the nodes.
157 * Set the sc_maxslot entry to the new maxslots.
158 */
159 int
sltab_resize(stok_t * handle,int maxslots)160 sltab_resize(stok_t *handle, int maxslots)
161 {
162 slot_ent_t *tmp, *phd;
163 avl_tree_t *replaytree = handle->st_sltab;
164 int more = 0;
165
166 mutex_enter(&handle->st_lock);
167 /* Max slots are reduced.. */
168 if (handle->st_currw > maxslots) {
169 for (tmp = avl_first(replaytree); tmp != NULL;
170 tmp = phd) {
171 phd = AVL_NEXT(replaytree, tmp);
172 if (tmp->se_sltno > maxslots) {
173 slot_delete(handle, tmp);
174 }
175 }
176 } else {
177 more = maxslots - handle->st_currw;
178 handle->st_fslots += more;
179 handle->st_currw = maxslots;
180 }
181 mutex_exit(&handle->st_lock);
182 return (0);
183 }
184
185 void
sltab_query(stok_t * handle,slt_query_t qf,void * res)186 sltab_query(stok_t *handle, slt_query_t qf, void *res)
187 {
188 ASSERT(handle != NULL);
189 ASSERT(res != NULL);
190
191 mutex_enter(&handle->st_lock);
192 switch (qf) {
193 case SLT_MAXSLOT:
194 {
195 uint_t *p = (uint_t *)res;
196 *p = (slotid4)handle->st_currw;
197 break;
198 }
199 default:
200 break;
201 }
202 mutex_exit(&handle->st_lock);
203 }
204
205 void
sltab_set_cleanup(stok_t * handle,void (* cleanup)(slot_ent_t *))206 sltab_set_cleanup(stok_t *handle, void (*cleanup)(slot_ent_t *))
207 {
208 mutex_enter(&handle->st_lock);
209 handle->cleanup_entry = cleanup;
210 mutex_exit(&handle->st_lock);
211 }
212
213 int
slot_delete(stok_t * handle,slot_ent_t * node)214 slot_delete(stok_t *handle, slot_ent_t *node)
215 {
216 avl_tree_t *replaytree = handle->st_sltab;
217
218 ASSERT(MUTEX_HELD(&handle->st_lock));
219 cv_destroy(&node->se_wait);
220 avl_remove(replaytree, node);
221 if (handle->cleanup_entry) {
222 mutex_enter(&node->se_lock);
223 handle->cleanup_entry(node);
224 mutex_exit(&node->se_lock);
225 }
226 mutex_destroy(&node->se_lock);
227 kmem_free(node, sizeof (*node));
228 node = NULL;
229 handle->st_fslots -= 1;
230 return (0);
231 }
232
233 slot_ent_t *
sltab_get(stok_t * handle,slotid4 slot)234 sltab_get(stok_t *handle, slotid4 slot)
235 {
236 slot_ent_t *node = NULL, tmp;
237 avl_index_t where;
238 avl_tree_t *replaytree = handle->st_sltab;
239
240 tmp.se_sltno = slot;
241 mutex_enter(&handle->st_lock);
242 node = (slot_ent_t *)avl_find(replaytree, &tmp, &where);
243 mutex_exit(&handle->st_lock);
244 return (node);
245 }
246
247 int
slot_alloc(stok_t * handle,slt_wait_t f,slot_ent_t ** res)248 slot_alloc(stok_t *handle, slt_wait_t f, slot_ent_t **res)
249 {
250 avl_tree_t *replaytree = handle->st_sltab;
251 slot_ent_t *tmp = NULL, *phd, *tnode = NULL;
252 uint_t i, ret = 0;
253 avl_index_t where;
254
255 ASSERT(handle != NULL);
256 ASSERT(handle->st_sltab->avl_numnodes <= handle->st_currw);
257
258 retry:
259 for (i = 0; i < handle->st_currw; i++) {
260 tmp = slot_get(handle, i);
261 if (tmp == NULL) {
262 tnode = kmem_zalloc(sizeof (slot_ent_t),
263 KM_SLEEP);
264 mutex_enter(&handle->st_lock);
265 tnode->se_seqid = 1;
266 tnode->se_sltno = i;
267 tnode->se_state = SLOT_INUSE;
268 mutex_init(&tnode->se_lock, NULL, MUTEX_DEFAULT, NULL);
269 cv_init(&tnode->se_wait, NULL, CV_DEFAULT, NULL);
270 phd = (slot_ent_t *)avl_find(replaytree,
271 tnode, &where);
272 if (phd == NULL) {
273 avl_insert(replaytree, tnode, where);
274 handle->st_fslots -= 1;
275 *res = tnode;
276 mutex_exit(&handle->st_lock);
277 return (ret);
278 } else {
279 /*
280 * Somebody already snuck in a slot
281 * continue with the search
282 */
283 cv_destroy(&tnode->se_wait);
284 mutex_destroy(&tnode->se_lock);
285 kmem_free(tnode, sizeof (slot_ent_t));
286 mutex_exit(&handle->st_lock);
287 continue;
288 }
289 } else {
290 mutex_enter(&handle->st_lock);
291 mutex_enter(&tmp->se_lock);
292 if (tmp->se_state & SLOT_FREE) {
293 tmp->se_state = SLOT_INUSE;
294 handle->st_fslots -= 1;
295 *res = tmp;
296 mutex_exit(&tmp->se_lock);
297 mutex_exit(&handle->st_lock);
298 return (ret);
299 }
300 mutex_exit(&tmp->se_lock);
301 mutex_exit(&handle->st_lock);
302 }
303 }
304 if (f == SLT_NOSLEEP) {
305 *res = NULL;
306 return (-1);
307 }
308
309 ASSERT(f == SLT_SLEEP);
310
311 /*
312 * wait for a free slot
313 */
314 mutex_enter(&handle->st_lock);
315 while (handle->st_fslots < 1)
316 cv_wait(&handle->st_wait, &handle->st_lock);
317 mutex_exit(&handle->st_lock);
318
319 /*
320 * try for a free slot again
321 */
322 goto retry;
323 }
324
325 void
slot_incr_seq(slot_ent_t * p)326 slot_incr_seq(slot_ent_t *p)
327 {
328 atomic_inc_32(&p->se_seqid);
329 }
330
331 void
slot_free(stok_t * handle,slot_ent_t * p)332 slot_free(stok_t *handle, slot_ent_t *p)
333 {
334 ASSERT(handle != NULL);
335
336 mutex_enter(&handle->st_lock);
337 mutex_enter(&p->se_lock);
338
339 p->se_state = SLOT_FREE;
340 handle->st_fslots += 1;
341 ASSERT(handle->st_fslots <= handle->st_currw);
342 mutex_exit(&p->se_lock);
343 cv_signal(&handle->st_wait);
344 mutex_exit(&handle->st_lock);
345 }
346
347
348 nfsstat4
slot_cb_status(stok_t * handle)349 slot_cb_status(stok_t *handle)
350 {
351 avl_tree_t *replaytree = handle->st_sltab;
352 nfsstat4 status = NFS4_OK;
353 slot_ent_t *tmp = NULL, *next;
354
355 /*
356 * If there is even one CB call outstanding, error off;
357 * Slot is still in use, session cannot be destroyed.
358 */
359 ASSERT(handle != NULL);
360
361 mutex_enter(&handle->st_lock);
362 for (tmp = avl_first(replaytree); tmp != NULL; tmp = next) {
363 next = AVL_NEXT(replaytree, tmp);
364 mutex_enter(&tmp->se_lock);
365 if (tmp->se_state & SLOT_INUSE) {
366 status = NFS4ERR_BACK_CHAN_BUSY;
367 mutex_exit(&tmp->se_lock);
368 break;
369 }
370 tmp->se_state = SLOT_FREE;
371 handle->st_fslots += 1;
372 mutex_exit(&tmp->se_lock);
373 }
374 mutex_exit(&handle->st_lock);
375 return (status);
376 }
377
378 void
slot_set_state(slot_ent_t * slot,int state)379 slot_set_state(slot_ent_t *slot, int state)
380 {
381 mutex_enter(&slot->se_lock); /* grab slot lock */
382 slot->se_state |= state;
383 mutex_exit(&slot->se_lock);
384 }
385
386 void
slot_error_to_inuse(slot_ent_t * slot)387 slot_error_to_inuse(slot_ent_t *slot)
388 {
389 mutex_enter(&slot->se_lock);
390 ASSERT(slot->se_state & SLOT_ERROR);
391 ASSERT(slot->se_state & SLOT_INUSE);
392 slot->se_state &= ~SLOT_ERROR;
393 mutex_exit(&slot->se_lock);
394 }
395
396 void
slot_table_create(stok_t ** handle,int max_slots)397 slot_table_create(stok_t **handle, int max_slots)
398 {
399 sltab_create(handle, max_slots);
400 }
401
402 void
slot_table_destroy(stok_t * handle)403 slot_table_destroy(stok_t *handle)
404 {
405 sltab_destroy(handle);
406 }
407
408 void
slot_table_resize(stok_t * handle,int max_slots)409 slot_table_resize(stok_t *handle, int max_slots)
410 {
411 (void) sltab_resize(handle, max_slots);
412 }
413
414 void
slot_table_query(stok_t * handle,slt_query_t q,void * res)415 slot_table_query(stok_t *handle, slt_query_t q, void *res)
416 {
417 sltab_query(handle, q, res);
418 }
419
420 slot_ent_t *
slot_get(stok_t * handle,slotid4 slot)421 slot_get(stok_t *handle, slotid4 slot)
422 {
423 return (sltab_get(handle, slot));
424 }
425