xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs4x_slrc.c (revision ec14ab0836cae822dfbc08b0d971d65179a2d06b)
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