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.
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.
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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 #include <smbsrv/libsmb.h>
33 #define SMB_CACHE_RDLOCK 0
34 #define SMB_CACHE_WRLOCK 1
36 #define SMB_CACHE_STATE_NOCACHE 0
37 #define SMB_CACHE_STATE_READY 1
38 #define SMB_CACHE_STATE_REFRESHING 2
39 #define SMB_CACHE_STATE_DESTROYING 3
41 static int smb_cache_lock(smb_cache_t
*, int);
42 static int smb_cache_rdlock(smb_cache_t
*);
43 static int smb_cache_wrlock(smb_cache_t
*);
44 static void smb_cache_unlock(smb_cache_t
*);
45 static boolean_t
smb_cache_wait(smb_cache_t
*);
46 static void smb_cache_destroy_nodes(smb_cache_t
*);
49 * Creates an AVL tree and initializes the given cache handle.
50 * Transfers the cache to READY state.
52 * This function does not populate the cache.
54 * chandle pointer to a smb_cache_t structure
55 * waittime see smb_cache_refreshing() comments
56 * cmpfn compare function used by AVL tree
57 * freefn if set, it will be used to free any allocated
58 * memory for the node data stored in the cache when
59 * that node is removed.
60 * copyfn this function has to be set and it is used
61 * to provide a copy of the node data stored in the
62 * cache to the caller of smb_cache_iterate or any other
63 * function that is used to access nodes data.
64 * This can typically be 'bcopy' if data is fixed size.
65 * datasz Size of data stored in the cache if it's fixed size.
66 * This size will be passed to the copy function.
69 smb_cache_create(smb_cache_t
*chandle
, uint32_t waittime
,
70 int (*cmpfn
) (const void *, const void *),
71 void (*freefn
)(void *),
72 void (*copyfn
)(const void *, void *, size_t),
78 (void) mutex_lock(&chandle
->ch_mtx
);
79 if (chandle
->ch_state
!= SMB_CACHE_STATE_NOCACHE
) {
80 (void) mutex_unlock(&chandle
->ch_mtx
);
84 avl_create(&chandle
->ch_cache
, cmpfn
, sizeof (smb_cache_node_t
),
85 offsetof(smb_cache_node_t
, cn_link
));
87 chandle
->ch_state
= SMB_CACHE_STATE_READY
;
89 chandle
->ch_wait
= waittime
;
90 chandle
->ch_sequence
= random();
91 chandle
->ch_datasz
= datasz
;
92 chandle
->ch_free
= freefn
;
93 chandle
->ch_copy
= copyfn
;
94 (void) mutex_unlock(&chandle
->ch_mtx
);
100 * Transfers the cache to DESTROYING state while it's waiting for
101 * in-flight operation to finish, this will prevent any new operation
102 * to start. When all entries are removed the cache is transferred to
106 smb_cache_destroy(smb_cache_t
*chandle
)
108 (void) mutex_lock(&chandle
->ch_mtx
);
109 switch (chandle
->ch_state
) {
110 case SMB_CACHE_STATE_NOCACHE
:
111 case SMB_CACHE_STATE_DESTROYING
:
112 (void) mutex_unlock(&chandle
->ch_mtx
);
119 chandle
->ch_state
= SMB_CACHE_STATE_DESTROYING
;
121 while (chandle
->ch_nops
> 0)
122 (void) cond_wait(&chandle
->ch_cv
, &chandle
->ch_mtx
);
124 smb_cache_destroy_nodes(chandle
);
126 avl_destroy(&chandle
->ch_cache
);
127 chandle
->ch_state
= SMB_CACHE_STATE_NOCACHE
;
128 (void) mutex_unlock(&chandle
->ch_mtx
);
132 * Removes and frees all the cache entries without destroy
136 smb_cache_flush(smb_cache_t
*chandle
)
138 if (smb_cache_wrlock(chandle
) == 0) {
139 smb_cache_destroy_nodes(chandle
);
140 chandle
->ch_sequence
++;
141 smb_cache_unlock(chandle
);
146 * Based on the specified flag either add or replace given
147 * data. If ADD flag is specified and the item is already in
148 * the cache EEXIST error code is returned.
151 smb_cache_add(smb_cache_t
*chandle
, const void *data
, int flags
)
153 smb_cache_node_t
*newnode
;
154 smb_cache_node_t
*node
;
160 if ((rc
= smb_cache_wrlock(chandle
)) != 0)
163 if ((newnode
= malloc(sizeof (smb_cache_node_t
))) == NULL
) {
164 smb_cache_unlock(chandle
);
168 newnode
->cn_data
= (void *)data
;
169 node
= avl_find(&chandle
->ch_cache
, newnode
, &where
);
171 if (flags
& SMB_CACHE_REPLACE
) {
172 avl_remove(&chandle
->ch_cache
, node
);
173 if (chandle
->ch_free
)
174 chandle
->ch_free(node
->cn_data
);
178 smb_cache_unlock(chandle
);
183 avl_insert(&chandle
->ch_cache
, newnode
, where
);
184 chandle
->ch_sequence
++;
186 smb_cache_unlock(chandle
);
191 * Uses the given 'data' as key to find a cache entry
192 * and remove it. The memory allocated for the found node
193 * and its data is freed.
196 smb_cache_remove(smb_cache_t
*chandle
, const void *data
)
198 smb_cache_node_t keynode
;
199 smb_cache_node_t
*node
;
203 if (smb_cache_wrlock(chandle
) != 0)
206 keynode
.cn_data
= (void *)data
;
207 node
= avl_find(&chandle
->ch_cache
, &keynode
, NULL
);
209 chandle
->ch_sequence
++;
210 avl_remove(&chandle
->ch_cache
, node
);
211 if (chandle
->ch_free
)
212 chandle
->ch_free(node
->cn_data
);
216 smb_cache_unlock(chandle
);
220 * Initializes the given cursor for iterating the cache
223 smb_cache_iterinit(smb_cache_t
*chandle
, smb_cache_cursor_t
*cursor
)
225 cursor
->cc_sequence
= chandle
->ch_sequence
;
226 cursor
->cc_next
= NULL
;
230 * Iterate the cache using the given cursor.
232 * Data is copied to the given buffer ('data') using the copy function
233 * specified at cache creation time.
235 * If the cache is modified while an iteration is in progress it causes
236 * the iteration to finish prematurely. This is to avoid the need to lock
237 * the whole cache while it is being iterated.
240 smb_cache_iterate(smb_cache_t
*chandle
, smb_cache_cursor_t
*cursor
, void *data
)
242 smb_cache_node_t
*node
;
246 if (smb_cache_rdlock(chandle
) != 0)
249 if (cursor
->cc_sequence
!= chandle
->ch_sequence
) {
250 smb_cache_unlock(chandle
);
254 if (cursor
->cc_next
== NULL
)
255 node
= avl_first(&chandle
->ch_cache
);
257 node
= AVL_NEXT(&chandle
->ch_cache
, cursor
->cc_next
);
260 chandle
->ch_copy(node
->cn_data
, data
, chandle
->ch_datasz
);
262 cursor
->cc_next
= node
;
263 smb_cache_unlock(chandle
);
265 return (node
!= NULL
);
269 * Returns the number of cache entries
272 smb_cache_num(smb_cache_t
*chandle
)
276 if (smb_cache_rdlock(chandle
) == 0) {
277 num
= (uint32_t)avl_numnodes(&chandle
->ch_cache
);
278 smb_cache_unlock(chandle
);
285 * Transfers the cache into REFRESHING state. This function needs
286 * to be called when the whole cache is being populated or refereshed
287 * and not for individual changes.
289 * Calling this function will ensure any read access to the cache will
290 * be stalled until the update is finished, which is to avoid providing
291 * incomplete, inconsistent or stale information. Read accesses will be
292 * stalled for 'ch_wait' seconds (see smb_cache_lock), which is set at
293 * the cache creation time.
295 * If it is okay for the cache to be accessed while it's being populated
296 * or refreshed, then there is no need to call this function.
298 * If another thread is already updating the cache, other callers will wait
299 * until cache is no longer in REFRESHING state. The return code is decided
300 * based on the new state of the cache.
302 * This function does NOT perform the actual refresh.
305 smb_cache_refreshing(smb_cache_t
*chandle
)
309 (void) mutex_lock(&chandle
->ch_mtx
);
310 switch (chandle
->ch_state
) {
311 case SMB_CACHE_STATE_READY
:
312 chandle
->ch_state
= SMB_CACHE_STATE_REFRESHING
;
316 case SMB_CACHE_STATE_REFRESHING
:
317 while (chandle
->ch_state
== SMB_CACHE_STATE_REFRESHING
)
318 (void) cond_wait(&chandle
->ch_cv
,
321 if (chandle
->ch_state
== SMB_CACHE_STATE_READY
) {
322 chandle
->ch_state
= SMB_CACHE_STATE_REFRESHING
;
329 case SMB_CACHE_STATE_NOCACHE
:
330 case SMB_CACHE_STATE_DESTROYING
:
338 (void) mutex_unlock(&chandle
->ch_mtx
);
343 * Transfers the cache from REFRESHING to READY state.
345 * Nothing will happen if the cache is no longer available
346 * or it is being destroyed.
348 * This function should only be called if smb_cache_refreshing()
349 * has already been invoked.
352 smb_cache_ready(smb_cache_t
*chandle
)
354 (void) mutex_lock(&chandle
->ch_mtx
);
355 switch (chandle
->ch_state
) {
356 case SMB_CACHE_STATE_REFRESHING
:
357 chandle
->ch_state
= SMB_CACHE_STATE_READY
;
358 (void) cond_broadcast(&chandle
->ch_cv
);
361 case SMB_CACHE_STATE_NOCACHE
:
362 case SMB_CACHE_STATE_DESTROYING
:
365 case SMB_CACHE_STATE_READY
:
369 (void) mutex_unlock(&chandle
->ch_mtx
);
373 * Lock the cache with the specified mode.
374 * If the cache is in updating state and a read lock is
375 * requested, the lock won't be granted until either the
376 * update is finished or SMB_CACHE_UPDATE_WAIT has passed.
378 * Whenever a lock is granted, the number of inflight cache
379 * operations is incremented.
382 smb_cache_lock(smb_cache_t
*chandle
, int mode
)
384 (void) mutex_lock(&chandle
->ch_mtx
);
385 switch (chandle
->ch_state
) {
386 case SMB_CACHE_STATE_NOCACHE
:
387 case SMB_CACHE_STATE_DESTROYING
:
388 (void) mutex_unlock(&chandle
->ch_mtx
);
391 case SMB_CACHE_STATE_REFRESHING
:
393 * Read operations should wait until the update
396 if (mode
== SMB_CACHE_RDLOCK
) {
397 if (!smb_cache_wait(chandle
)) {
398 (void) mutex_unlock(&chandle
->ch_mtx
);
403 case SMB_CACHE_STATE_READY
:
410 (void) mutex_unlock(&chandle
->ch_mtx
);
413 * Lock has to be taken outside the mutex otherwise
414 * there could be a deadlock
416 if (mode
== SMB_CACHE_RDLOCK
)
417 (void) rw_rdlock(&chandle
->ch_cache_lck
);
419 (void) rw_wrlock(&chandle
->ch_cache_lck
);
425 * Lock the cache for reading
428 smb_cache_rdlock(smb_cache_t
*chandle
)
430 return (smb_cache_lock(chandle
, SMB_CACHE_RDLOCK
));
434 * Lock the cache for modification
437 smb_cache_wrlock(smb_cache_t
*chandle
)
439 return (smb_cache_lock(chandle
, SMB_CACHE_WRLOCK
));
446 smb_cache_unlock(smb_cache_t
*chandle
)
448 (void) mutex_lock(&chandle
->ch_mtx
);
449 assert(chandle
->ch_nops
> 0);
451 (void) cond_broadcast(&chandle
->ch_cv
);
452 (void) mutex_unlock(&chandle
->ch_mtx
);
454 (void) rw_unlock(&chandle
->ch_cache_lck
);
459 * Waits for ch_wait seconds if cache is in UPDATING state.
460 * Upon wake up returns true if cache is ready to be used,
461 * otherwise it returns false.
464 smb_cache_wait(smb_cache_t
*chandle
)
469 if (chandle
->ch_wait
== 0)
472 to
.tv_sec
= chandle
->ch_wait
;
474 while (chandle
->ch_state
== SMB_CACHE_STATE_REFRESHING
) {
475 err
= cond_reltimedwait(&chandle
->ch_cv
,
476 &chandle
->ch_mtx
, &to
);
481 return (chandle
->ch_state
== SMB_CACHE_STATE_READY
);
485 * Removes and frees all the cache entries
488 smb_cache_destroy_nodes(smb_cache_t
*chandle
)
491 smb_cache_node_t
*cnode
;
494 cache
= &chandle
->ch_cache
;
495 while ((cnode
= avl_destroy_nodes(cache
, &cookie
)) != NULL
) {
496 if (chandle
->ch_free
)
497 chandle
->ch_free(cnode
->cn_data
);