xref: /onnv-gate/usr/src/lib/smbsrv/libsmb/common/smb_cache.c (revision 11963:061945695ce1)
1*11963SAfshin.Ardakani@Sun.COM /*
2*11963SAfshin.Ardakani@Sun.COM  * CDDL HEADER START
3*11963SAfshin.Ardakani@Sun.COM  *
4*11963SAfshin.Ardakani@Sun.COM  * The contents of this file are subject to the terms of the
5*11963SAfshin.Ardakani@Sun.COM  * Common Development and Distribution License (the "License").
6*11963SAfshin.Ardakani@Sun.COM  * You may not use this file except in compliance with the License.
7*11963SAfshin.Ardakani@Sun.COM  *
8*11963SAfshin.Ardakani@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*11963SAfshin.Ardakani@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*11963SAfshin.Ardakani@Sun.COM  * See the License for the specific language governing permissions
11*11963SAfshin.Ardakani@Sun.COM  * and limitations under the License.
12*11963SAfshin.Ardakani@Sun.COM  *
13*11963SAfshin.Ardakani@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*11963SAfshin.Ardakani@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*11963SAfshin.Ardakani@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*11963SAfshin.Ardakani@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*11963SAfshin.Ardakani@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*11963SAfshin.Ardakani@Sun.COM  *
19*11963SAfshin.Ardakani@Sun.COM  * CDDL HEADER END
20*11963SAfshin.Ardakani@Sun.COM  */
21*11963SAfshin.Ardakani@Sun.COM /*
22*11963SAfshin.Ardakani@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23*11963SAfshin.Ardakani@Sun.COM  * Use is subject to license terms.
24*11963SAfshin.Ardakani@Sun.COM  */
25*11963SAfshin.Ardakani@Sun.COM 
26*11963SAfshin.Ardakani@Sun.COM #include <assert.h>
27*11963SAfshin.Ardakani@Sun.COM #include <sys/avl.h>
28*11963SAfshin.Ardakani@Sun.COM #include <smbsrv/libsmb.h>
29*11963SAfshin.Ardakani@Sun.COM 
30*11963SAfshin.Ardakani@Sun.COM /*
31*11963SAfshin.Ardakani@Sun.COM  * Cache lock modes
32*11963SAfshin.Ardakani@Sun.COM  */
33*11963SAfshin.Ardakani@Sun.COM #define	SMB_CACHE_RDLOCK	0
34*11963SAfshin.Ardakani@Sun.COM #define	SMB_CACHE_WRLOCK	1
35*11963SAfshin.Ardakani@Sun.COM 
36*11963SAfshin.Ardakani@Sun.COM #define	SMB_CACHE_STATE_NOCACHE		0
37*11963SAfshin.Ardakani@Sun.COM #define	SMB_CACHE_STATE_READY		1
38*11963SAfshin.Ardakani@Sun.COM #define	SMB_CACHE_STATE_REFRESHING	2
39*11963SAfshin.Ardakani@Sun.COM #define	SMB_CACHE_STATE_DESTROYING	3
40*11963SAfshin.Ardakani@Sun.COM 
41*11963SAfshin.Ardakani@Sun.COM static int smb_cache_lock(smb_cache_t *, int);
42*11963SAfshin.Ardakani@Sun.COM static int smb_cache_rdlock(smb_cache_t *);
43*11963SAfshin.Ardakani@Sun.COM static int smb_cache_wrlock(smb_cache_t *);
44*11963SAfshin.Ardakani@Sun.COM static void smb_cache_unlock(smb_cache_t *);
45*11963SAfshin.Ardakani@Sun.COM static boolean_t smb_cache_wait(smb_cache_t *);
46*11963SAfshin.Ardakani@Sun.COM static void smb_cache_destroy_nodes(smb_cache_t *);
47*11963SAfshin.Ardakani@Sun.COM 
48*11963SAfshin.Ardakani@Sun.COM /*
49*11963SAfshin.Ardakani@Sun.COM  * Creates an AVL tree and initializes the given cache handle.
50*11963SAfshin.Ardakani@Sun.COM  * Transfers the cache to READY state.
51*11963SAfshin.Ardakani@Sun.COM  *
52*11963SAfshin.Ardakani@Sun.COM  * This function does not populate the cache.
53*11963SAfshin.Ardakani@Sun.COM  *
54*11963SAfshin.Ardakani@Sun.COM  * chandle	pointer to a smb_cache_t structure
55*11963SAfshin.Ardakani@Sun.COM  * waittime	see smb_cache_refreshing() comments
56*11963SAfshin.Ardakani@Sun.COM  * cmpfn	compare function used by AVL tree
57*11963SAfshin.Ardakani@Sun.COM  * freefn	if set, it will be used to free any allocated
58*11963SAfshin.Ardakani@Sun.COM  * 		memory for the node data stored in the cache when
59*11963SAfshin.Ardakani@Sun.COM  * 		that node is removed.
60*11963SAfshin.Ardakani@Sun.COM  * copyfn	this function has to be set and it is used
61*11963SAfshin.Ardakani@Sun.COM  * 		to provide a copy of the node data stored in the
62*11963SAfshin.Ardakani@Sun.COM  * 		cache to the caller of smb_cache_iterate or any other
63*11963SAfshin.Ardakani@Sun.COM  * 		function that is used to access nodes data.
64*11963SAfshin.Ardakani@Sun.COM  * 		This can typically be 'bcopy' if data is fixed size.
65*11963SAfshin.Ardakani@Sun.COM  * datasz	Size of data stored in the cache if it's fixed size.
66*11963SAfshin.Ardakani@Sun.COM  * 		This size will be passed to the copy function.
67*11963SAfshin.Ardakani@Sun.COM  */
68*11963SAfshin.Ardakani@Sun.COM void
smb_cache_create(smb_cache_t * chandle,uint32_t waittime,int (* cmpfn)(const void *,const void *),void (* freefn)(void *),void (* copyfn)(const void *,void *,size_t),size_t datasz)69*11963SAfshin.Ardakani@Sun.COM smb_cache_create(smb_cache_t *chandle, uint32_t waittime,
70*11963SAfshin.Ardakani@Sun.COM     int (*cmpfn) (const void *, const void *),
71*11963SAfshin.Ardakani@Sun.COM     void (*freefn)(void *),
72*11963SAfshin.Ardakani@Sun.COM     void (*copyfn)(const void *, void *, size_t),
73*11963SAfshin.Ardakani@Sun.COM     size_t datasz)
74*11963SAfshin.Ardakani@Sun.COM {
75*11963SAfshin.Ardakani@Sun.COM 	assert(chandle);
76*11963SAfshin.Ardakani@Sun.COM 	assert(copyfn);
77*11963SAfshin.Ardakani@Sun.COM 
78*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&chandle->ch_mtx);
79*11963SAfshin.Ardakani@Sun.COM 	if (chandle->ch_state != SMB_CACHE_STATE_NOCACHE) {
80*11963SAfshin.Ardakani@Sun.COM 		(void) mutex_unlock(&chandle->ch_mtx);
81*11963SAfshin.Ardakani@Sun.COM 		return;
82*11963SAfshin.Ardakani@Sun.COM 	}
83*11963SAfshin.Ardakani@Sun.COM 
84*11963SAfshin.Ardakani@Sun.COM 	avl_create(&chandle->ch_cache, cmpfn, sizeof (smb_cache_node_t),
85*11963SAfshin.Ardakani@Sun.COM 	    offsetof(smb_cache_node_t, cn_link));
86*11963SAfshin.Ardakani@Sun.COM 
87*11963SAfshin.Ardakani@Sun.COM 	chandle->ch_state = SMB_CACHE_STATE_READY;
88*11963SAfshin.Ardakani@Sun.COM 	chandle->ch_nops = 0;
89*11963SAfshin.Ardakani@Sun.COM 	chandle->ch_wait = waittime;
90*11963SAfshin.Ardakani@Sun.COM 	chandle->ch_sequence = random();
91*11963SAfshin.Ardakani@Sun.COM 	chandle->ch_datasz = datasz;
92*11963SAfshin.Ardakani@Sun.COM 	chandle->ch_free = freefn;
93*11963SAfshin.Ardakani@Sun.COM 	chandle->ch_copy = copyfn;
94*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&chandle->ch_mtx);
95*11963SAfshin.Ardakani@Sun.COM }
96*11963SAfshin.Ardakani@Sun.COM 
97*11963SAfshin.Ardakani@Sun.COM /*
98*11963SAfshin.Ardakani@Sun.COM  * Destroys the cache.
99*11963SAfshin.Ardakani@Sun.COM  *
100*11963SAfshin.Ardakani@Sun.COM  * Transfers the cache to DESTROYING state while it's waiting for
101*11963SAfshin.Ardakani@Sun.COM  * in-flight operation to finish, this will prevent any new operation
102*11963SAfshin.Ardakani@Sun.COM  * to start. When all entries are removed the cache is transferred to
103*11963SAfshin.Ardakani@Sun.COM  * NOCACHE state.
104*11963SAfshin.Ardakani@Sun.COM  */
105*11963SAfshin.Ardakani@Sun.COM void
smb_cache_destroy(smb_cache_t * chandle)106*11963SAfshin.Ardakani@Sun.COM smb_cache_destroy(smb_cache_t *chandle)
107*11963SAfshin.Ardakani@Sun.COM {
108*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&chandle->ch_mtx);
109*11963SAfshin.Ardakani@Sun.COM 	switch (chandle->ch_state) {
110*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_NOCACHE:
111*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_DESTROYING:
112*11963SAfshin.Ardakani@Sun.COM 		(void) mutex_unlock(&chandle->ch_mtx);
113*11963SAfshin.Ardakani@Sun.COM 		return;
114*11963SAfshin.Ardakani@Sun.COM 
115*11963SAfshin.Ardakani@Sun.COM 	default:
116*11963SAfshin.Ardakani@Sun.COM 		break;
117*11963SAfshin.Ardakani@Sun.COM 	}
118*11963SAfshin.Ardakani@Sun.COM 
119*11963SAfshin.Ardakani@Sun.COM 	chandle->ch_state = SMB_CACHE_STATE_DESTROYING;
120*11963SAfshin.Ardakani@Sun.COM 
121*11963SAfshin.Ardakani@Sun.COM 	while (chandle->ch_nops > 0)
122*11963SAfshin.Ardakani@Sun.COM 		(void) cond_wait(&chandle->ch_cv, &chandle->ch_mtx);
123*11963SAfshin.Ardakani@Sun.COM 
124*11963SAfshin.Ardakani@Sun.COM 	smb_cache_destroy_nodes(chandle);
125*11963SAfshin.Ardakani@Sun.COM 
126*11963SAfshin.Ardakani@Sun.COM 	avl_destroy(&chandle->ch_cache);
127*11963SAfshin.Ardakani@Sun.COM 	chandle->ch_state = SMB_CACHE_STATE_NOCACHE;
128*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&chandle->ch_mtx);
129*11963SAfshin.Ardakani@Sun.COM }
130*11963SAfshin.Ardakani@Sun.COM 
131*11963SAfshin.Ardakani@Sun.COM /*
132*11963SAfshin.Ardakani@Sun.COM  * Removes and frees all the cache entries without destroy
133*11963SAfshin.Ardakani@Sun.COM  * the cache itself.
134*11963SAfshin.Ardakani@Sun.COM  */
135*11963SAfshin.Ardakani@Sun.COM void
smb_cache_flush(smb_cache_t * chandle)136*11963SAfshin.Ardakani@Sun.COM smb_cache_flush(smb_cache_t *chandle)
137*11963SAfshin.Ardakani@Sun.COM {
138*11963SAfshin.Ardakani@Sun.COM 	if (smb_cache_wrlock(chandle) == 0) {
139*11963SAfshin.Ardakani@Sun.COM 		smb_cache_destroy_nodes(chandle);
140*11963SAfshin.Ardakani@Sun.COM 		chandle->ch_sequence++;
141*11963SAfshin.Ardakani@Sun.COM 		smb_cache_unlock(chandle);
142*11963SAfshin.Ardakani@Sun.COM 	}
143*11963SAfshin.Ardakani@Sun.COM }
144*11963SAfshin.Ardakani@Sun.COM 
145*11963SAfshin.Ardakani@Sun.COM /*
146*11963SAfshin.Ardakani@Sun.COM  * Based on the specified flag either add or replace given
147*11963SAfshin.Ardakani@Sun.COM  * data. If ADD flag is specified and the item is already in
148*11963SAfshin.Ardakani@Sun.COM  * the cache EEXIST error code is returned.
149*11963SAfshin.Ardakani@Sun.COM  */
150*11963SAfshin.Ardakani@Sun.COM int
smb_cache_add(smb_cache_t * chandle,const void * data,int flags)151*11963SAfshin.Ardakani@Sun.COM smb_cache_add(smb_cache_t *chandle, const void *data, int flags)
152*11963SAfshin.Ardakani@Sun.COM {
153*11963SAfshin.Ardakani@Sun.COM 	smb_cache_node_t *newnode;
154*11963SAfshin.Ardakani@Sun.COM 	smb_cache_node_t *node;
155*11963SAfshin.Ardakani@Sun.COM 	avl_index_t where;
156*11963SAfshin.Ardakani@Sun.COM 	int rc = 0;
157*11963SAfshin.Ardakani@Sun.COM 
158*11963SAfshin.Ardakani@Sun.COM 	assert(data);
159*11963SAfshin.Ardakani@Sun.COM 
160*11963SAfshin.Ardakani@Sun.COM 	if ((rc = smb_cache_wrlock(chandle)) != 0)
161*11963SAfshin.Ardakani@Sun.COM 		return (rc);
162*11963SAfshin.Ardakani@Sun.COM 
163*11963SAfshin.Ardakani@Sun.COM 	if ((newnode = malloc(sizeof (smb_cache_node_t))) == NULL) {
164*11963SAfshin.Ardakani@Sun.COM 		smb_cache_unlock(chandle);
165*11963SAfshin.Ardakani@Sun.COM 		return (ENOMEM);
166*11963SAfshin.Ardakani@Sun.COM 	}
167*11963SAfshin.Ardakani@Sun.COM 
168*11963SAfshin.Ardakani@Sun.COM 	newnode->cn_data = (void *)data;
169*11963SAfshin.Ardakani@Sun.COM 	node = avl_find(&chandle->ch_cache, newnode, &where);
170*11963SAfshin.Ardakani@Sun.COM 	if (node != NULL) {
171*11963SAfshin.Ardakani@Sun.COM 		if (flags & SMB_CACHE_REPLACE) {
172*11963SAfshin.Ardakani@Sun.COM 			avl_remove(&chandle->ch_cache, node);
173*11963SAfshin.Ardakani@Sun.COM 			if (chandle->ch_free)
174*11963SAfshin.Ardakani@Sun.COM 				chandle->ch_free(node->cn_data);
175*11963SAfshin.Ardakani@Sun.COM 			free(node);
176*11963SAfshin.Ardakani@Sun.COM 		} else {
177*11963SAfshin.Ardakani@Sun.COM 			free(newnode);
178*11963SAfshin.Ardakani@Sun.COM 			smb_cache_unlock(chandle);
179*11963SAfshin.Ardakani@Sun.COM 			return (EEXIST);
180*11963SAfshin.Ardakani@Sun.COM 		}
181*11963SAfshin.Ardakani@Sun.COM 	}
182*11963SAfshin.Ardakani@Sun.COM 
183*11963SAfshin.Ardakani@Sun.COM 	avl_insert(&chandle->ch_cache, newnode, where);
184*11963SAfshin.Ardakani@Sun.COM 	chandle->ch_sequence++;
185*11963SAfshin.Ardakani@Sun.COM 
186*11963SAfshin.Ardakani@Sun.COM 	smb_cache_unlock(chandle);
187*11963SAfshin.Ardakani@Sun.COM 	return (rc);
188*11963SAfshin.Ardakani@Sun.COM }
189*11963SAfshin.Ardakani@Sun.COM 
190*11963SAfshin.Ardakani@Sun.COM /*
191*11963SAfshin.Ardakani@Sun.COM  * Uses the given 'data' as key to find a cache entry
192*11963SAfshin.Ardakani@Sun.COM  * and remove it. The memory allocated for the found node
193*11963SAfshin.Ardakani@Sun.COM  * and its data is freed.
194*11963SAfshin.Ardakani@Sun.COM  */
195*11963SAfshin.Ardakani@Sun.COM void
smb_cache_remove(smb_cache_t * chandle,const void * data)196*11963SAfshin.Ardakani@Sun.COM smb_cache_remove(smb_cache_t *chandle, const void *data)
197*11963SAfshin.Ardakani@Sun.COM {
198*11963SAfshin.Ardakani@Sun.COM 	smb_cache_node_t keynode;
199*11963SAfshin.Ardakani@Sun.COM 	smb_cache_node_t *node;
200*11963SAfshin.Ardakani@Sun.COM 
201*11963SAfshin.Ardakani@Sun.COM 	assert(data);
202*11963SAfshin.Ardakani@Sun.COM 
203*11963SAfshin.Ardakani@Sun.COM 	if (smb_cache_wrlock(chandle) != 0)
204*11963SAfshin.Ardakani@Sun.COM 		return;
205*11963SAfshin.Ardakani@Sun.COM 
206*11963SAfshin.Ardakani@Sun.COM 	keynode.cn_data = (void *)data;
207*11963SAfshin.Ardakani@Sun.COM 	node = avl_find(&chandle->ch_cache, &keynode, NULL);
208*11963SAfshin.Ardakani@Sun.COM 	if (node) {
209*11963SAfshin.Ardakani@Sun.COM 		chandle->ch_sequence++;
210*11963SAfshin.Ardakani@Sun.COM 		avl_remove(&chandle->ch_cache, node);
211*11963SAfshin.Ardakani@Sun.COM 		if (chandle->ch_free)
212*11963SAfshin.Ardakani@Sun.COM 			chandle->ch_free(node->cn_data);
213*11963SAfshin.Ardakani@Sun.COM 		free(node);
214*11963SAfshin.Ardakani@Sun.COM 	}
215*11963SAfshin.Ardakani@Sun.COM 
216*11963SAfshin.Ardakani@Sun.COM 	smb_cache_unlock(chandle);
217*11963SAfshin.Ardakani@Sun.COM }
218*11963SAfshin.Ardakani@Sun.COM 
219*11963SAfshin.Ardakani@Sun.COM /*
220*11963SAfshin.Ardakani@Sun.COM  * Initializes the given cursor for iterating the cache
221*11963SAfshin.Ardakani@Sun.COM  */
222*11963SAfshin.Ardakani@Sun.COM void
smb_cache_iterinit(smb_cache_t * chandle,smb_cache_cursor_t * cursor)223*11963SAfshin.Ardakani@Sun.COM smb_cache_iterinit(smb_cache_t *chandle, smb_cache_cursor_t *cursor)
224*11963SAfshin.Ardakani@Sun.COM {
225*11963SAfshin.Ardakani@Sun.COM 	cursor->cc_sequence = chandle->ch_sequence;
226*11963SAfshin.Ardakani@Sun.COM 	cursor->cc_next = NULL;
227*11963SAfshin.Ardakani@Sun.COM }
228*11963SAfshin.Ardakani@Sun.COM 
229*11963SAfshin.Ardakani@Sun.COM /*
230*11963SAfshin.Ardakani@Sun.COM  * Iterate the cache using the given cursor.
231*11963SAfshin.Ardakani@Sun.COM  *
232*11963SAfshin.Ardakani@Sun.COM  * Data is copied to the given buffer ('data') using the copy function
233*11963SAfshin.Ardakani@Sun.COM  * specified at cache creation time.
234*11963SAfshin.Ardakani@Sun.COM  *
235*11963SAfshin.Ardakani@Sun.COM  * If the cache is modified while an iteration is in progress it causes
236*11963SAfshin.Ardakani@Sun.COM  * the iteration to finish prematurely. This is to avoid the need to lock
237*11963SAfshin.Ardakani@Sun.COM  * the whole cache while it is being iterated.
238*11963SAfshin.Ardakani@Sun.COM  */
239*11963SAfshin.Ardakani@Sun.COM boolean_t
smb_cache_iterate(smb_cache_t * chandle,smb_cache_cursor_t * cursor,void * data)240*11963SAfshin.Ardakani@Sun.COM smb_cache_iterate(smb_cache_t *chandle, smb_cache_cursor_t *cursor, void *data)
241*11963SAfshin.Ardakani@Sun.COM {
242*11963SAfshin.Ardakani@Sun.COM 	smb_cache_node_t *node;
243*11963SAfshin.Ardakani@Sun.COM 
244*11963SAfshin.Ardakani@Sun.COM 	assert(data);
245*11963SAfshin.Ardakani@Sun.COM 
246*11963SAfshin.Ardakani@Sun.COM 	if (smb_cache_rdlock(chandle) != 0)
247*11963SAfshin.Ardakani@Sun.COM 		return (B_FALSE);
248*11963SAfshin.Ardakani@Sun.COM 
249*11963SAfshin.Ardakani@Sun.COM 	if (cursor->cc_sequence != chandle->ch_sequence) {
250*11963SAfshin.Ardakani@Sun.COM 		smb_cache_unlock(chandle);
251*11963SAfshin.Ardakani@Sun.COM 		return (B_FALSE);
252*11963SAfshin.Ardakani@Sun.COM 	}
253*11963SAfshin.Ardakani@Sun.COM 
254*11963SAfshin.Ardakani@Sun.COM 	if (cursor->cc_next == NULL)
255*11963SAfshin.Ardakani@Sun.COM 		node = avl_first(&chandle->ch_cache);
256*11963SAfshin.Ardakani@Sun.COM 	else
257*11963SAfshin.Ardakani@Sun.COM 		node = AVL_NEXT(&chandle->ch_cache, cursor->cc_next);
258*11963SAfshin.Ardakani@Sun.COM 
259*11963SAfshin.Ardakani@Sun.COM 	if (node != NULL)
260*11963SAfshin.Ardakani@Sun.COM 		chandle->ch_copy(node->cn_data, data, chandle->ch_datasz);
261*11963SAfshin.Ardakani@Sun.COM 
262*11963SAfshin.Ardakani@Sun.COM 	cursor->cc_next = node;
263*11963SAfshin.Ardakani@Sun.COM 	smb_cache_unlock(chandle);
264*11963SAfshin.Ardakani@Sun.COM 
265*11963SAfshin.Ardakani@Sun.COM 	return (node != NULL);
266*11963SAfshin.Ardakani@Sun.COM }
267*11963SAfshin.Ardakani@Sun.COM 
268*11963SAfshin.Ardakani@Sun.COM /*
269*11963SAfshin.Ardakani@Sun.COM  * Returns the number of cache entries
270*11963SAfshin.Ardakani@Sun.COM  */
271*11963SAfshin.Ardakani@Sun.COM uint32_t
smb_cache_num(smb_cache_t * chandle)272*11963SAfshin.Ardakani@Sun.COM smb_cache_num(smb_cache_t *chandle)
273*11963SAfshin.Ardakani@Sun.COM {
274*11963SAfshin.Ardakani@Sun.COM 	uint32_t num = 0;
275*11963SAfshin.Ardakani@Sun.COM 
276*11963SAfshin.Ardakani@Sun.COM 	if (smb_cache_rdlock(chandle) == 0) {
277*11963SAfshin.Ardakani@Sun.COM 		num = (uint32_t)avl_numnodes(&chandle->ch_cache);
278*11963SAfshin.Ardakani@Sun.COM 		smb_cache_unlock(chandle);
279*11963SAfshin.Ardakani@Sun.COM 	}
280*11963SAfshin.Ardakani@Sun.COM 
281*11963SAfshin.Ardakani@Sun.COM 	return (num);
282*11963SAfshin.Ardakani@Sun.COM }
283*11963SAfshin.Ardakani@Sun.COM 
284*11963SAfshin.Ardakani@Sun.COM /*
285*11963SAfshin.Ardakani@Sun.COM  * Transfers the cache into REFRESHING state. This function needs
286*11963SAfshin.Ardakani@Sun.COM  * to be called when the whole cache is being populated or refereshed
287*11963SAfshin.Ardakani@Sun.COM  * and not for individual changes.
288*11963SAfshin.Ardakani@Sun.COM  *
289*11963SAfshin.Ardakani@Sun.COM  * Calling this function will ensure any read access to the cache will
290*11963SAfshin.Ardakani@Sun.COM  * be stalled until the update is finished, which is to avoid providing
291*11963SAfshin.Ardakani@Sun.COM  * incomplete, inconsistent or stale information. Read accesses will be
292*11963SAfshin.Ardakani@Sun.COM  * stalled for 'ch_wait' seconds (see smb_cache_lock), which is set at
293*11963SAfshin.Ardakani@Sun.COM  * the cache creation time.
294*11963SAfshin.Ardakani@Sun.COM  *
295*11963SAfshin.Ardakani@Sun.COM  * If it is okay for the cache to be accessed while it's being populated
296*11963SAfshin.Ardakani@Sun.COM  * or refreshed, then there is no need to call this function.
297*11963SAfshin.Ardakani@Sun.COM  *
298*11963SAfshin.Ardakani@Sun.COM  * If another thread is already updating the cache, other callers will wait
299*11963SAfshin.Ardakani@Sun.COM  * until cache is no longer in REFRESHING state. The return code is decided
300*11963SAfshin.Ardakani@Sun.COM  * based on the new state of the cache.
301*11963SAfshin.Ardakani@Sun.COM  *
302*11963SAfshin.Ardakani@Sun.COM  * This function does NOT perform the actual refresh.
303*11963SAfshin.Ardakani@Sun.COM  */
304*11963SAfshin.Ardakani@Sun.COM int
smb_cache_refreshing(smb_cache_t * chandle)305*11963SAfshin.Ardakani@Sun.COM smb_cache_refreshing(smb_cache_t *chandle)
306*11963SAfshin.Ardakani@Sun.COM {
307*11963SAfshin.Ardakani@Sun.COM 	int rc = 0;
308*11963SAfshin.Ardakani@Sun.COM 
309*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&chandle->ch_mtx);
310*11963SAfshin.Ardakani@Sun.COM 	switch (chandle->ch_state) {
311*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_READY:
312*11963SAfshin.Ardakani@Sun.COM 		chandle->ch_state = SMB_CACHE_STATE_REFRESHING;
313*11963SAfshin.Ardakani@Sun.COM 		rc = 0;
314*11963SAfshin.Ardakani@Sun.COM 		break;
315*11963SAfshin.Ardakani@Sun.COM 
316*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_REFRESHING:
317*11963SAfshin.Ardakani@Sun.COM 		while (chandle->ch_state == SMB_CACHE_STATE_REFRESHING)
318*11963SAfshin.Ardakani@Sun.COM 			(void) cond_wait(&chandle->ch_cv,
319*11963SAfshin.Ardakani@Sun.COM 			    &chandle->ch_mtx);
320*11963SAfshin.Ardakani@Sun.COM 
321*11963SAfshin.Ardakani@Sun.COM 		if (chandle->ch_state == SMB_CACHE_STATE_READY) {
322*11963SAfshin.Ardakani@Sun.COM 			chandle->ch_state = SMB_CACHE_STATE_REFRESHING;
323*11963SAfshin.Ardakani@Sun.COM 			rc = 0;
324*11963SAfshin.Ardakani@Sun.COM 		} else {
325*11963SAfshin.Ardakani@Sun.COM 			rc = ENODATA;
326*11963SAfshin.Ardakani@Sun.COM 		}
327*11963SAfshin.Ardakani@Sun.COM 		break;
328*11963SAfshin.Ardakani@Sun.COM 
329*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_NOCACHE:
330*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_DESTROYING:
331*11963SAfshin.Ardakani@Sun.COM 		rc = ENODATA;
332*11963SAfshin.Ardakani@Sun.COM 		break;
333*11963SAfshin.Ardakani@Sun.COM 
334*11963SAfshin.Ardakani@Sun.COM 	default:
335*11963SAfshin.Ardakani@Sun.COM 		assert(0);
336*11963SAfshin.Ardakani@Sun.COM 	}
337*11963SAfshin.Ardakani@Sun.COM 
338*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&chandle->ch_mtx);
339*11963SAfshin.Ardakani@Sun.COM 	return (rc);
340*11963SAfshin.Ardakani@Sun.COM }
341*11963SAfshin.Ardakani@Sun.COM 
342*11963SAfshin.Ardakani@Sun.COM /*
343*11963SAfshin.Ardakani@Sun.COM  * Transfers the cache from REFRESHING to READY state.
344*11963SAfshin.Ardakani@Sun.COM  *
345*11963SAfshin.Ardakani@Sun.COM  * Nothing will happen if the cache is no longer available
346*11963SAfshin.Ardakani@Sun.COM  * or it is being destroyed.
347*11963SAfshin.Ardakani@Sun.COM  *
348*11963SAfshin.Ardakani@Sun.COM  * This function should only be called if smb_cache_refreshing()
349*11963SAfshin.Ardakani@Sun.COM  * has already been invoked.
350*11963SAfshin.Ardakani@Sun.COM  */
351*11963SAfshin.Ardakani@Sun.COM void
smb_cache_ready(smb_cache_t * chandle)352*11963SAfshin.Ardakani@Sun.COM smb_cache_ready(smb_cache_t *chandle)
353*11963SAfshin.Ardakani@Sun.COM {
354*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&chandle->ch_mtx);
355*11963SAfshin.Ardakani@Sun.COM 	switch (chandle->ch_state) {
356*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_REFRESHING:
357*11963SAfshin.Ardakani@Sun.COM 		chandle->ch_state = SMB_CACHE_STATE_READY;
358*11963SAfshin.Ardakani@Sun.COM 		(void) cond_broadcast(&chandle->ch_cv);
359*11963SAfshin.Ardakani@Sun.COM 		break;
360*11963SAfshin.Ardakani@Sun.COM 
361*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_NOCACHE:
362*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_DESTROYING:
363*11963SAfshin.Ardakani@Sun.COM 		break;
364*11963SAfshin.Ardakani@Sun.COM 
365*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_READY:
366*11963SAfshin.Ardakani@Sun.COM 	default:
367*11963SAfshin.Ardakani@Sun.COM 		assert(0);
368*11963SAfshin.Ardakani@Sun.COM 	}
369*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&chandle->ch_mtx);
370*11963SAfshin.Ardakani@Sun.COM }
371*11963SAfshin.Ardakani@Sun.COM 
372*11963SAfshin.Ardakani@Sun.COM /*
373*11963SAfshin.Ardakani@Sun.COM  * Lock the cache with the specified mode.
374*11963SAfshin.Ardakani@Sun.COM  * If the cache is in updating state and a read lock is
375*11963SAfshin.Ardakani@Sun.COM  * requested, the lock won't be granted until either the
376*11963SAfshin.Ardakani@Sun.COM  * update is finished or SMB_CACHE_UPDATE_WAIT has passed.
377*11963SAfshin.Ardakani@Sun.COM  *
378*11963SAfshin.Ardakani@Sun.COM  * Whenever a lock is granted, the number of inflight cache
379*11963SAfshin.Ardakani@Sun.COM  * operations is incremented.
380*11963SAfshin.Ardakani@Sun.COM  */
381*11963SAfshin.Ardakani@Sun.COM static int
smb_cache_lock(smb_cache_t * chandle,int mode)382*11963SAfshin.Ardakani@Sun.COM smb_cache_lock(smb_cache_t *chandle, int mode)
383*11963SAfshin.Ardakani@Sun.COM {
384*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&chandle->ch_mtx);
385*11963SAfshin.Ardakani@Sun.COM 	switch (chandle->ch_state) {
386*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_NOCACHE:
387*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_DESTROYING:
388*11963SAfshin.Ardakani@Sun.COM 		(void) mutex_unlock(&chandle->ch_mtx);
389*11963SAfshin.Ardakani@Sun.COM 		return (ENODATA);
390*11963SAfshin.Ardakani@Sun.COM 
391*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_REFRESHING:
392*11963SAfshin.Ardakani@Sun.COM 		/*
393*11963SAfshin.Ardakani@Sun.COM 		 * Read operations should wait until the update
394*11963SAfshin.Ardakani@Sun.COM 		 * is completed.
395*11963SAfshin.Ardakani@Sun.COM 		 */
396*11963SAfshin.Ardakani@Sun.COM 		if (mode == SMB_CACHE_RDLOCK) {
397*11963SAfshin.Ardakani@Sun.COM 			if (!smb_cache_wait(chandle)) {
398*11963SAfshin.Ardakani@Sun.COM 				(void) mutex_unlock(&chandle->ch_mtx);
399*11963SAfshin.Ardakani@Sun.COM 				return (ETIME);
400*11963SAfshin.Ardakani@Sun.COM 			}
401*11963SAfshin.Ardakani@Sun.COM 		}
402*11963SAfshin.Ardakani@Sun.COM 	/* FALLTHROUGH */
403*11963SAfshin.Ardakani@Sun.COM 	case SMB_CACHE_STATE_READY:
404*11963SAfshin.Ardakani@Sun.COM 		chandle->ch_nops++;
405*11963SAfshin.Ardakani@Sun.COM 		break;
406*11963SAfshin.Ardakani@Sun.COM 
407*11963SAfshin.Ardakani@Sun.COM 	default:
408*11963SAfshin.Ardakani@Sun.COM 		assert(0);
409*11963SAfshin.Ardakani@Sun.COM 	}
410*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&chandle->ch_mtx);
411*11963SAfshin.Ardakani@Sun.COM 
412*11963SAfshin.Ardakani@Sun.COM 	/*
413*11963SAfshin.Ardakani@Sun.COM 	 * Lock has to be taken outside the mutex otherwise
414*11963SAfshin.Ardakani@Sun.COM 	 * there could be a deadlock
415*11963SAfshin.Ardakani@Sun.COM 	 */
416*11963SAfshin.Ardakani@Sun.COM 	if (mode == SMB_CACHE_RDLOCK)
417*11963SAfshin.Ardakani@Sun.COM 		(void) rw_rdlock(&chandle->ch_cache_lck);
418*11963SAfshin.Ardakani@Sun.COM 	else
419*11963SAfshin.Ardakani@Sun.COM 		(void) rw_wrlock(&chandle->ch_cache_lck);
420*11963SAfshin.Ardakani@Sun.COM 
421*11963SAfshin.Ardakani@Sun.COM 	return (0);
422*11963SAfshin.Ardakani@Sun.COM }
423*11963SAfshin.Ardakani@Sun.COM 
424*11963SAfshin.Ardakani@Sun.COM /*
425*11963SAfshin.Ardakani@Sun.COM  * Lock the cache for reading
426*11963SAfshin.Ardakani@Sun.COM  */
427*11963SAfshin.Ardakani@Sun.COM static int
smb_cache_rdlock(smb_cache_t * chandle)428*11963SAfshin.Ardakani@Sun.COM smb_cache_rdlock(smb_cache_t *chandle)
429*11963SAfshin.Ardakani@Sun.COM {
430*11963SAfshin.Ardakani@Sun.COM 	return (smb_cache_lock(chandle, SMB_CACHE_RDLOCK));
431*11963SAfshin.Ardakani@Sun.COM }
432*11963SAfshin.Ardakani@Sun.COM 
433*11963SAfshin.Ardakani@Sun.COM /*
434*11963SAfshin.Ardakani@Sun.COM  * Lock the cache for modification
435*11963SAfshin.Ardakani@Sun.COM  */
436*11963SAfshin.Ardakani@Sun.COM static int
smb_cache_wrlock(smb_cache_t * chandle)437*11963SAfshin.Ardakani@Sun.COM smb_cache_wrlock(smb_cache_t *chandle)
438*11963SAfshin.Ardakani@Sun.COM {
439*11963SAfshin.Ardakani@Sun.COM 	return (smb_cache_lock(chandle, SMB_CACHE_WRLOCK));
440*11963SAfshin.Ardakani@Sun.COM }
441*11963SAfshin.Ardakani@Sun.COM 
442*11963SAfshin.Ardakani@Sun.COM /*
443*11963SAfshin.Ardakani@Sun.COM  * Unlock the cache
444*11963SAfshin.Ardakani@Sun.COM  */
445*11963SAfshin.Ardakani@Sun.COM static void
smb_cache_unlock(smb_cache_t * chandle)446*11963SAfshin.Ardakani@Sun.COM smb_cache_unlock(smb_cache_t *chandle)
447*11963SAfshin.Ardakani@Sun.COM {
448*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&chandle->ch_mtx);
449*11963SAfshin.Ardakani@Sun.COM 	assert(chandle->ch_nops > 0);
450*11963SAfshin.Ardakani@Sun.COM 	chandle->ch_nops--;
451*11963SAfshin.Ardakani@Sun.COM 	(void) cond_broadcast(&chandle->ch_cv);
452*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&chandle->ch_mtx);
453*11963SAfshin.Ardakani@Sun.COM 
454*11963SAfshin.Ardakani@Sun.COM 	(void) rw_unlock(&chandle->ch_cache_lck);
455*11963SAfshin.Ardakani@Sun.COM }
456*11963SAfshin.Ardakani@Sun.COM 
457*11963SAfshin.Ardakani@Sun.COM 
458*11963SAfshin.Ardakani@Sun.COM /*
459*11963SAfshin.Ardakani@Sun.COM  * Waits for ch_wait seconds if cache is in UPDATING state.
460*11963SAfshin.Ardakani@Sun.COM  * Upon wake up returns true if cache is ready to be used,
461*11963SAfshin.Ardakani@Sun.COM  * otherwise it returns false.
462*11963SAfshin.Ardakani@Sun.COM  */
463*11963SAfshin.Ardakani@Sun.COM static boolean_t
smb_cache_wait(smb_cache_t * chandle)464*11963SAfshin.Ardakani@Sun.COM smb_cache_wait(smb_cache_t *chandle)
465*11963SAfshin.Ardakani@Sun.COM {
466*11963SAfshin.Ardakani@Sun.COM 	timestruc_t to;
467*11963SAfshin.Ardakani@Sun.COM 	int err;
468*11963SAfshin.Ardakani@Sun.COM 
469*11963SAfshin.Ardakani@Sun.COM 	if (chandle->ch_wait == 0)
470*11963SAfshin.Ardakani@Sun.COM 		return (B_TRUE);
471*11963SAfshin.Ardakani@Sun.COM 
472*11963SAfshin.Ardakani@Sun.COM 	to.tv_sec = chandle->ch_wait;
473*11963SAfshin.Ardakani@Sun.COM 	to.tv_nsec = 0;
474*11963SAfshin.Ardakani@Sun.COM 	while (chandle->ch_state == SMB_CACHE_STATE_REFRESHING) {
475*11963SAfshin.Ardakani@Sun.COM 		err = cond_reltimedwait(&chandle->ch_cv,
476*11963SAfshin.Ardakani@Sun.COM 		    &chandle->ch_mtx, &to);
477*11963SAfshin.Ardakani@Sun.COM 		if (err == ETIME)
478*11963SAfshin.Ardakani@Sun.COM 			break;
479*11963SAfshin.Ardakani@Sun.COM 	}
480*11963SAfshin.Ardakani@Sun.COM 
481*11963SAfshin.Ardakani@Sun.COM 	return (chandle->ch_state == SMB_CACHE_STATE_READY);
482*11963SAfshin.Ardakani@Sun.COM }
483*11963SAfshin.Ardakani@Sun.COM 
484*11963SAfshin.Ardakani@Sun.COM /*
485*11963SAfshin.Ardakani@Sun.COM  * Removes and frees all the cache entries
486*11963SAfshin.Ardakani@Sun.COM  */
487*11963SAfshin.Ardakani@Sun.COM static void
smb_cache_destroy_nodes(smb_cache_t * chandle)488*11963SAfshin.Ardakani@Sun.COM smb_cache_destroy_nodes(smb_cache_t *chandle)
489*11963SAfshin.Ardakani@Sun.COM {
490*11963SAfshin.Ardakani@Sun.COM 	void *cookie = NULL;
491*11963SAfshin.Ardakani@Sun.COM 	smb_cache_node_t *cnode;
492*11963SAfshin.Ardakani@Sun.COM 	avl_tree_t *cache;
493*11963SAfshin.Ardakani@Sun.COM 
494*11963SAfshin.Ardakani@Sun.COM 	cache = &chandle->ch_cache;
495*11963SAfshin.Ardakani@Sun.COM 	while ((cnode = avl_destroy_nodes(cache, &cookie)) != NULL) {
496*11963SAfshin.Ardakani@Sun.COM 		if (chandle->ch_free)
497*11963SAfshin.Ardakani@Sun.COM 			chandle->ch_free(cnode->cn_data);
498*11963SAfshin.Ardakani@Sun.COM 		free(cnode);
499*11963SAfshin.Ardakani@Sun.COM 	}
500*11963SAfshin.Ardakani@Sun.COM }
501