xref: /titanic_44/usr/src/uts/common/fs/smbsrv/smb_kutil.c (revision b6f078ed90209a1b0e143e0abd10e14a6a2a7397)
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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25  */
26 
27 #include <sys/param.h>
28 #include <sys/types.h>
29 #include <sys/tzfile.h>
30 #include <sys/atomic.h>
31 #include <sys/time.h>
32 #include <sys/spl.h>
33 #include <sys/random.h>
34 #include <smbsrv/smb_kproto.h>
35 #include <smbsrv/smb_fsops.h>
36 #include <smbsrv/smbinfo.h>
37 #include <smbsrv/smb_xdr.h>
38 #include <smbsrv/smb_vops.h>
39 #include <smbsrv/smb_idmap.h>
40 
41 #include <sys/sid.h>
42 #include <sys/priv_names.h>
43 
44 static kmem_cache_t	*smb_dtor_cache = NULL;
45 
46 static boolean_t smb_avl_hold(smb_avl_t *);
47 static void smb_avl_rele(smb_avl_t *);
48 
49 time_t tzh_leapcnt = 0;
50 
51 struct tm
52 *smb_gmtime_r(time_t *clock, struct tm *result);
53 
54 time_t
55 smb_timegm(struct tm *tm);
56 
57 struct	tm {
58 	int	tm_sec;
59 	int	tm_min;
60 	int	tm_hour;
61 	int	tm_mday;
62 	int	tm_mon;
63 	int	tm_year;
64 	int	tm_wday;
65 	int	tm_yday;
66 	int	tm_isdst;
67 };
68 
69 static const int days_in_month[] = {
70 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
71 };
72 
73 int
smb_ascii_or_unicode_strlen(struct smb_request * sr,char * str)74 smb_ascii_or_unicode_strlen(struct smb_request *sr, char *str)
75 {
76 	if (sr->smb_flg2 & SMB_FLAGS2_UNICODE)
77 		return (smb_wcequiv_strlen(str));
78 	return (strlen(str));
79 }
80 
81 int
smb_ascii_or_unicode_strlen_null(struct smb_request * sr,char * str)82 smb_ascii_or_unicode_strlen_null(struct smb_request *sr, char *str)
83 {
84 	if (sr->smb_flg2 & SMB_FLAGS2_UNICODE)
85 		return (smb_wcequiv_strlen(str) + 2);
86 	return (strlen(str) + 1);
87 }
88 
89 int
smb_ascii_or_unicode_null_len(struct smb_request * sr)90 smb_ascii_or_unicode_null_len(struct smb_request *sr)
91 {
92 	if (sr->smb_flg2 & SMB_FLAGS2_UNICODE)
93 		return (2);
94 	return (1);
95 }
96 
97 /*
98  *
99  * Convert old-style (DOS, LanMan) wildcard strings to NT style.
100  * This should ONLY happen to patterns that come from old clients,
101  * meaning dialect LANMAN2_1 etc. (dialect < NT_LM_0_12).
102  *
103  *	? is converted to >
104  *	* is converted to < if it is followed by .
105  *	. is converted to " if it is followed by ? or * or end of pattern
106  *
107  * Note: modifies pattern in place.
108  */
109 void
smb_convert_wildcards(char * pattern)110 smb_convert_wildcards(char *pattern)
111 {
112 	char	*p;
113 
114 	for (p = pattern; *p != '\0'; p++) {
115 		switch (*p) {
116 		case '?':
117 			*p = '>';
118 			break;
119 		case '*':
120 			if (p[1] == '.')
121 				*p = '<';
122 			break;
123 		case '.':
124 			if (p[1] == '?' || p[1] == '*' || p[1] == '\0')
125 				*p = '\"';
126 			break;
127 		}
128 	}
129 }
130 
131 /*
132  * smb_sattr_check
133  *
134  * Check file attributes against a search attribute (sattr) mask.
135  *
136  * Normal files, which includes READONLY and ARCHIVE, always pass
137  * this check.  If the DIRECTORY, HIDDEN or SYSTEM special attributes
138  * are set then they must appear in the search mask.  The special
139  * attributes are inclusive, i.e. all special attributes that appear
140  * in sattr must also appear in the file attributes for the check to
141  * pass.
142  *
143  * The following examples show how this works:
144  *
145  *		fileA:	READONLY
146  *		fileB:	0 (no attributes = normal file)
147  *		fileC:	READONLY, ARCHIVE
148  *		fileD:	HIDDEN
149  *		fileE:	READONLY, HIDDEN, SYSTEM
150  *		dirA:	DIRECTORY
151  *
152  * search attribute: 0
153  *		Returns: fileA, fileB and fileC.
154  * search attribute: HIDDEN
155  *		Returns: fileA, fileB, fileC and fileD.
156  * search attribute: SYSTEM
157  *		Returns: fileA, fileB and fileC.
158  * search attribute: DIRECTORY
159  *		Returns: fileA, fileB, fileC and dirA.
160  * search attribute: HIDDEN and SYSTEM
161  *		Returns: fileA, fileB, fileC, fileD and fileE.
162  *
163  * Returns true if the file and sattr match; otherwise, returns false.
164  */
165 boolean_t
smb_sattr_check(uint16_t dosattr,uint16_t sattr)166 smb_sattr_check(uint16_t dosattr, uint16_t sattr)
167 {
168 	if ((dosattr & FILE_ATTRIBUTE_DIRECTORY) &&
169 	    !(sattr & FILE_ATTRIBUTE_DIRECTORY))
170 		return (B_FALSE);
171 
172 	if ((dosattr & FILE_ATTRIBUTE_HIDDEN) &&
173 	    !(sattr & FILE_ATTRIBUTE_HIDDEN))
174 		return (B_FALSE);
175 
176 	if ((dosattr & FILE_ATTRIBUTE_SYSTEM) &&
177 	    !(sattr & FILE_ATTRIBUTE_SYSTEM))
178 		return (B_FALSE);
179 
180 	return (B_TRUE);
181 }
182 
183 int
microtime(timestruc_t * tvp)184 microtime(timestruc_t *tvp)
185 {
186 	tvp->tv_sec = gethrestime_sec();
187 	tvp->tv_nsec = 0;
188 	return (0);
189 }
190 
191 int32_t
clock_get_milli_uptime()192 clock_get_milli_uptime()
193 {
194 	return (TICK_TO_MSEC(ddi_get_lbolt()));
195 }
196 
197 /*
198  * smb_idpool_increment
199  *
200  * This function increments the ID pool by doubling the current size. This
201  * function assumes the caller entered the mutex of the pool.
202  */
203 static int
smb_idpool_increment(smb_idpool_t * pool)204 smb_idpool_increment(
205     smb_idpool_t	*pool)
206 {
207 	uint8_t		*new_pool;
208 	uint32_t	new_size;
209 
210 	ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC);
211 
212 	new_size = pool->id_size * 2;
213 	if (new_size <= SMB_IDPOOL_MAX_SIZE) {
214 		new_pool = kmem_alloc(new_size / 8, KM_NOSLEEP);
215 		if (new_pool) {
216 			bzero(new_pool, new_size / 8);
217 			bcopy(pool->id_pool, new_pool, pool->id_size / 8);
218 			kmem_free(pool->id_pool, pool->id_size / 8);
219 			pool->id_pool = new_pool;
220 			pool->id_free_counter += new_size - pool->id_size;
221 			pool->id_max_free_counter += new_size - pool->id_size;
222 			pool->id_size = new_size;
223 			pool->id_idx_msk = (new_size / 8) - 1;
224 			if (new_size >= SMB_IDPOOL_MAX_SIZE) {
225 				/* id -1 made unavailable */
226 				pool->id_pool[pool->id_idx_msk] = 0x80;
227 				pool->id_free_counter--;
228 				pool->id_max_free_counter--;
229 			}
230 			return (0);
231 		}
232 	}
233 	return (-1);
234 }
235 
236 /*
237  * smb_idpool_constructor
238  *
239  * This function initializes the pool structure provided.
240  */
241 int
smb_idpool_constructor(smb_idpool_t * pool)242 smb_idpool_constructor(
243     smb_idpool_t	*pool)
244 {
245 
246 	ASSERT(pool->id_magic != SMB_IDPOOL_MAGIC);
247 
248 	pool->id_size = SMB_IDPOOL_MIN_SIZE;
249 	pool->id_idx_msk = (SMB_IDPOOL_MIN_SIZE / 8) - 1;
250 	pool->id_free_counter = SMB_IDPOOL_MIN_SIZE - 1;
251 	pool->id_max_free_counter = SMB_IDPOOL_MIN_SIZE - 1;
252 	pool->id_bit = 0x02;
253 	pool->id_bit_idx = 1;
254 	pool->id_idx = 0;
255 	pool->id_pool = (uint8_t *)kmem_alloc((SMB_IDPOOL_MIN_SIZE / 8),
256 	    KM_SLEEP);
257 	bzero(pool->id_pool, (SMB_IDPOOL_MIN_SIZE / 8));
258 	/* -1 id made unavailable */
259 	pool->id_pool[0] = 0x01;		/* id 0 made unavailable */
260 	mutex_init(&pool->id_mutex, NULL, MUTEX_DEFAULT, NULL);
261 	pool->id_magic = SMB_IDPOOL_MAGIC;
262 	return (0);
263 }
264 
265 /*
266  * smb_idpool_destructor
267  *
268  * This function tears down and frees the resources associated with the
269  * pool provided.
270  */
271 void
smb_idpool_destructor(smb_idpool_t * pool)272 smb_idpool_destructor(
273     smb_idpool_t	*pool)
274 {
275 	ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC);
276 	ASSERT(pool->id_free_counter == pool->id_max_free_counter);
277 	pool->id_magic = (uint32_t)~SMB_IDPOOL_MAGIC;
278 	mutex_destroy(&pool->id_mutex);
279 	kmem_free(pool->id_pool, (size_t)(pool->id_size / 8));
280 }
281 
282 /*
283  * smb_idpool_alloc
284  *
285  * This function allocates an ID from the pool provided.
286  */
287 int
smb_idpool_alloc(smb_idpool_t * pool,uint16_t * id)288 smb_idpool_alloc(
289     smb_idpool_t	*pool,
290     uint16_t		*id)
291 {
292 	uint32_t	i;
293 	uint8_t		bit;
294 	uint8_t		bit_idx;
295 	uint8_t		byte;
296 
297 	ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC);
298 
299 	mutex_enter(&pool->id_mutex);
300 	if ((pool->id_free_counter == 0) && smb_idpool_increment(pool)) {
301 		mutex_exit(&pool->id_mutex);
302 		return (-1);
303 	}
304 
305 	i = pool->id_size;
306 	while (i) {
307 		bit = pool->id_bit;
308 		bit_idx = pool->id_bit_idx;
309 		byte = pool->id_pool[pool->id_idx];
310 		while (bit) {
311 			if (byte & bit) {
312 				bit = bit << 1;
313 				bit_idx++;
314 				continue;
315 			}
316 			pool->id_pool[pool->id_idx] |= bit;
317 			*id = (uint16_t)(pool->id_idx * 8 + (uint32_t)bit_idx);
318 			pool->id_free_counter--;
319 			pool->id_bit = bit;
320 			pool->id_bit_idx = bit_idx;
321 			mutex_exit(&pool->id_mutex);
322 			return (0);
323 		}
324 		pool->id_bit = 1;
325 		pool->id_bit_idx = 0;
326 		pool->id_idx++;
327 		pool->id_idx &= pool->id_idx_msk;
328 		--i;
329 	}
330 	/*
331 	 * This section of code shouldn't be reached. If there are IDs
332 	 * available and none could be found there's a problem.
333 	 */
334 	ASSERT(0);
335 	mutex_exit(&pool->id_mutex);
336 	return (-1);
337 }
338 
339 /*
340  * smb_idpool_free
341  *
342  * This function frees the ID provided.
343  */
344 void
smb_idpool_free(smb_idpool_t * pool,uint16_t id)345 smb_idpool_free(
346     smb_idpool_t	*pool,
347     uint16_t		id)
348 {
349 	ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC);
350 	ASSERT(id != 0);
351 	ASSERT(id != 0xFFFF);
352 
353 	mutex_enter(&pool->id_mutex);
354 	if (pool->id_pool[id >> 3] & (1 << (id & 7))) {
355 		pool->id_pool[id >> 3] &= ~(1 << (id & 7));
356 		pool->id_free_counter++;
357 		ASSERT(pool->id_free_counter <= pool->id_max_free_counter);
358 		mutex_exit(&pool->id_mutex);
359 		return;
360 	}
361 	/* Freeing a free ID. */
362 	ASSERT(0);
363 	mutex_exit(&pool->id_mutex);
364 }
365 
366 /*
367  * Initialize the llist delete queue object cache.
368  */
369 void
smb_llist_init(void)370 smb_llist_init(void)
371 {
372 	if (smb_dtor_cache != NULL)
373 		return;
374 
375 	smb_dtor_cache = kmem_cache_create("smb_dtor_cache",
376 	    sizeof (smb_dtor_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
377 }
378 
379 /*
380  * Destroy the llist delete queue object cache.
381  */
382 void
smb_llist_fini(void)383 smb_llist_fini(void)
384 {
385 	if (smb_dtor_cache != NULL) {
386 		kmem_cache_destroy(smb_dtor_cache);
387 		smb_dtor_cache = NULL;
388 	}
389 }
390 
391 /*
392  * smb_llist_constructor
393  *
394  * This function initializes a locked list.
395  */
396 void
smb_llist_constructor(smb_llist_t * ll,size_t size,size_t offset)397 smb_llist_constructor(
398     smb_llist_t	*ll,
399     size_t	size,
400     size_t	offset)
401 {
402 	rw_init(&ll->ll_lock, NULL, RW_DEFAULT, NULL);
403 	mutex_init(&ll->ll_mutex, NULL, MUTEX_DEFAULT, NULL);
404 	list_create(&ll->ll_list, size, offset);
405 	list_create(&ll->ll_deleteq, sizeof (smb_dtor_t),
406 	    offsetof(smb_dtor_t, dt_lnd));
407 	ll->ll_count = 0;
408 	ll->ll_wrop = 0;
409 	ll->ll_deleteq_count = 0;
410 	ll->ll_flushing = B_FALSE;
411 }
412 
413 /*
414  * Flush the delete queue and destroy a locked list.
415  */
416 void
smb_llist_destructor(smb_llist_t * ll)417 smb_llist_destructor(
418     smb_llist_t	*ll)
419 {
420 	smb_llist_flush(ll);
421 
422 	ASSERT(ll->ll_count == 0);
423 	ASSERT(ll->ll_deleteq_count == 0);
424 
425 	rw_destroy(&ll->ll_lock);
426 	list_destroy(&ll->ll_list);
427 	list_destroy(&ll->ll_deleteq);
428 	mutex_destroy(&ll->ll_mutex);
429 }
430 
431 /*
432  * Post an object to the delete queue.  The delete queue will be processed
433  * during list exit or list destruction.  Objects are often posted for
434  * deletion during list iteration (while the list is locked) but that is
435  * not required, and an object can be posted at any time.
436  */
437 void
smb_llist_post(smb_llist_t * ll,void * object,smb_dtorproc_t dtorproc)438 smb_llist_post(smb_llist_t *ll, void *object, smb_dtorproc_t dtorproc)
439 {
440 	smb_dtor_t	*dtor;
441 
442 	ASSERT((object != NULL) && (dtorproc != NULL));
443 
444 	dtor = kmem_cache_alloc(smb_dtor_cache, KM_SLEEP);
445 	bzero(dtor, sizeof (smb_dtor_t));
446 	dtor->dt_magic = SMB_DTOR_MAGIC;
447 	dtor->dt_object = object;
448 	dtor->dt_proc = dtorproc;
449 
450 	mutex_enter(&ll->ll_mutex);
451 	list_insert_tail(&ll->ll_deleteq, dtor);
452 	++ll->ll_deleteq_count;
453 	mutex_exit(&ll->ll_mutex);
454 }
455 
456 /*
457  * Exit the list lock and process the delete queue.
458  */
459 void
smb_llist_exit(smb_llist_t * ll)460 smb_llist_exit(smb_llist_t *ll)
461 {
462 	rw_exit(&ll->ll_lock);
463 	smb_llist_flush(ll);
464 }
465 
466 /*
467  * Flush the list delete queue.  The mutex is dropped across the destructor
468  * call in case this leads to additional objects being posted to the delete
469  * queue.
470  */
471 void
smb_llist_flush(smb_llist_t * ll)472 smb_llist_flush(smb_llist_t *ll)
473 {
474 	smb_dtor_t    *dtor;
475 
476 	mutex_enter(&ll->ll_mutex);
477 	if (ll->ll_flushing) {
478 		mutex_exit(&ll->ll_mutex);
479 		return;
480 	}
481 	ll->ll_flushing = B_TRUE;
482 
483 	dtor = list_head(&ll->ll_deleteq);
484 	while (dtor != NULL) {
485 		SMB_DTOR_VALID(dtor);
486 		ASSERT((dtor->dt_object != NULL) && (dtor->dt_proc != NULL));
487 		list_remove(&ll->ll_deleteq, dtor);
488 		--ll->ll_deleteq_count;
489 		mutex_exit(&ll->ll_mutex);
490 
491 		dtor->dt_proc(dtor->dt_object);
492 
493 		dtor->dt_magic = (uint32_t)~SMB_DTOR_MAGIC;
494 		kmem_cache_free(smb_dtor_cache, dtor);
495 		mutex_enter(&ll->ll_mutex);
496 		dtor = list_head(&ll->ll_deleteq);
497 	}
498 	ll->ll_flushing = B_FALSE;
499 
500 	mutex_exit(&ll->ll_mutex);
501 }
502 
503 /*
504  * smb_llist_upgrade
505  *
506  * This function tries to upgrade the lock of the locked list. It assumes the
507  * locked has already been entered in RW_READER mode. It first tries using the
508  * Solaris function rw_tryupgrade(). If that call fails the lock is released
509  * and reentered in RW_WRITER mode. In that last case a window is opened during
510  * which the contents of the list may have changed. The return code indicates
511  * whether or not the list was modified when the lock was exited.
512  */
smb_llist_upgrade(smb_llist_t * ll)513 int smb_llist_upgrade(
514     smb_llist_t *ll)
515 {
516 	uint64_t	wrop;
517 
518 	if (rw_tryupgrade(&ll->ll_lock) != 0) {
519 		return (0);
520 	}
521 	wrop = ll->ll_wrop;
522 	rw_exit(&ll->ll_lock);
523 	rw_enter(&ll->ll_lock, RW_WRITER);
524 	return (wrop != ll->ll_wrop);
525 }
526 
527 /*
528  * smb_llist_insert_head
529  *
530  * This function inserts the object passed a the beginning of the list. This
531  * function assumes the lock of the list has already been entered.
532  */
533 void
smb_llist_insert_head(smb_llist_t * ll,void * obj)534 smb_llist_insert_head(
535     smb_llist_t	*ll,
536     void	*obj)
537 {
538 	list_insert_head(&ll->ll_list, obj);
539 	++ll->ll_wrop;
540 	++ll->ll_count;
541 }
542 
543 /*
544  * smb_llist_insert_tail
545  *
546  * This function appends to the object passed to the list. This function assumes
547  * the lock of the list has already been entered.
548  *
549  */
550 void
smb_llist_insert_tail(smb_llist_t * ll,void * obj)551 smb_llist_insert_tail(
552     smb_llist_t	*ll,
553     void	*obj)
554 {
555 	list_insert_tail(&ll->ll_list, obj);
556 	++ll->ll_wrop;
557 	++ll->ll_count;
558 }
559 
560 /*
561  * smb_llist_remove
562  *
563  * This function removes the object passed from the list. This function assumes
564  * the lock of the list has already been entered.
565  */
566 void
smb_llist_remove(smb_llist_t * ll,void * obj)567 smb_llist_remove(
568     smb_llist_t	*ll,
569     void	*obj)
570 {
571 	list_remove(&ll->ll_list, obj);
572 	++ll->ll_wrop;
573 	--ll->ll_count;
574 }
575 
576 /*
577  * smb_llist_get_count
578  *
579  * This function returns the number of elements in the specified list.
580  */
581 uint32_t
smb_llist_get_count(smb_llist_t * ll)582 smb_llist_get_count(
583     smb_llist_t *ll)
584 {
585 	return (ll->ll_count);
586 }
587 
588 /*
589  * smb_slist_constructor
590  *
591  * Synchronized list constructor.
592  */
593 void
smb_slist_constructor(smb_slist_t * sl,size_t size,size_t offset)594 smb_slist_constructor(
595     smb_slist_t	*sl,
596     size_t	size,
597     size_t	offset)
598 {
599 	mutex_init(&sl->sl_mutex, NULL, MUTEX_DEFAULT, NULL);
600 	cv_init(&sl->sl_cv, NULL, CV_DEFAULT, NULL);
601 	list_create(&sl->sl_list, size, offset);
602 	sl->sl_count = 0;
603 	sl->sl_waiting = B_FALSE;
604 }
605 
606 /*
607  * smb_slist_destructor
608  *
609  * Synchronized list destructor.
610  */
611 void
smb_slist_destructor(smb_slist_t * sl)612 smb_slist_destructor(
613     smb_slist_t	*sl)
614 {
615 	VERIFY(sl->sl_count == 0);
616 
617 	mutex_destroy(&sl->sl_mutex);
618 	cv_destroy(&sl->sl_cv);
619 	list_destroy(&sl->sl_list);
620 }
621 
622 /*
623  * smb_slist_insert_head
624  *
625  * This function inserts the object passed a the beginning of the list.
626  */
627 void
smb_slist_insert_head(smb_slist_t * sl,void * obj)628 smb_slist_insert_head(
629     smb_slist_t	*sl,
630     void	*obj)
631 {
632 	mutex_enter(&sl->sl_mutex);
633 	list_insert_head(&sl->sl_list, obj);
634 	++sl->sl_count;
635 	mutex_exit(&sl->sl_mutex);
636 }
637 
638 /*
639  * smb_slist_insert_tail
640  *
641  * This function appends the object passed to the list.
642  */
643 void
smb_slist_insert_tail(smb_slist_t * sl,void * obj)644 smb_slist_insert_tail(
645     smb_slist_t	*sl,
646     void	*obj)
647 {
648 	mutex_enter(&sl->sl_mutex);
649 	list_insert_tail(&sl->sl_list, obj);
650 	++sl->sl_count;
651 	mutex_exit(&sl->sl_mutex);
652 }
653 
654 /*
655  * smb_llist_remove
656  *
657  * This function removes the object passed by the caller from the list.
658  */
659 void
smb_slist_remove(smb_slist_t * sl,void * obj)660 smb_slist_remove(
661     smb_slist_t	*sl,
662     void	*obj)
663 {
664 	mutex_enter(&sl->sl_mutex);
665 	list_remove(&sl->sl_list, obj);
666 	if ((--sl->sl_count == 0) && (sl->sl_waiting)) {
667 		sl->sl_waiting = B_FALSE;
668 		cv_broadcast(&sl->sl_cv);
669 	}
670 	mutex_exit(&sl->sl_mutex);
671 }
672 
673 /*
674  * smb_slist_move_tail
675  *
676  * This function transfers all the contents of the synchronized list to the
677  * list_t provided. It returns the number of objects transferred.
678  */
679 uint32_t
smb_slist_move_tail(list_t * lst,smb_slist_t * sl)680 smb_slist_move_tail(
681     list_t	*lst,
682     smb_slist_t	*sl)
683 {
684 	uint32_t	rv;
685 
686 	mutex_enter(&sl->sl_mutex);
687 	rv = sl->sl_count;
688 	if (sl->sl_count) {
689 		list_move_tail(lst, &sl->sl_list);
690 		sl->sl_count = 0;
691 		if (sl->sl_waiting) {
692 			sl->sl_waiting = B_FALSE;
693 			cv_broadcast(&sl->sl_cv);
694 		}
695 	}
696 	mutex_exit(&sl->sl_mutex);
697 	return (rv);
698 }
699 
700 /*
701  * smb_slist_obj_move
702  *
703  * This function moves an object from one list to the end of the other list. It
704  * assumes the mutex of each list has been entered.
705  */
706 void
smb_slist_obj_move(smb_slist_t * dst,smb_slist_t * src,void * obj)707 smb_slist_obj_move(
708     smb_slist_t	*dst,
709     smb_slist_t	*src,
710     void	*obj)
711 {
712 	ASSERT(dst->sl_list.list_offset == src->sl_list.list_offset);
713 	ASSERT(dst->sl_list.list_size == src->sl_list.list_size);
714 
715 	list_remove(&src->sl_list, obj);
716 	list_insert_tail(&dst->sl_list, obj);
717 	dst->sl_count++;
718 	src->sl_count--;
719 	if ((src->sl_count == 0) && (src->sl_waiting)) {
720 		src->sl_waiting = B_FALSE;
721 		cv_broadcast(&src->sl_cv);
722 	}
723 }
724 
725 /*
726  * smb_slist_wait_for_empty
727  *
728  * This function waits for a list to be emptied.
729  */
730 void
smb_slist_wait_for_empty(smb_slist_t * sl)731 smb_slist_wait_for_empty(
732     smb_slist_t	*sl)
733 {
734 	mutex_enter(&sl->sl_mutex);
735 	while (sl->sl_count) {
736 		sl->sl_waiting = B_TRUE;
737 		cv_wait(&sl->sl_cv, &sl->sl_mutex);
738 	}
739 	mutex_exit(&sl->sl_mutex);
740 }
741 
742 /*
743  * smb_slist_exit
744  *
745  * This function exits the muetx of the list and signal the condition variable
746  * if the list is empty.
747  */
748 void
smb_slist_exit(smb_slist_t * sl)749 smb_slist_exit(smb_slist_t *sl)
750 {
751 	if ((sl->sl_count == 0) && (sl->sl_waiting)) {
752 		sl->sl_waiting = B_FALSE;
753 		cv_broadcast(&sl->sl_cv);
754 	}
755 	mutex_exit(&sl->sl_mutex);
756 }
757 
758 /* smb_thread_... moved to smb_thread.c */
759 
760 /*
761  * smb_rwx_init
762  */
763 void
smb_rwx_init(smb_rwx_t * rwx)764 smb_rwx_init(
765     smb_rwx_t	*rwx)
766 {
767 	bzero(rwx, sizeof (smb_rwx_t));
768 	cv_init(&rwx->rwx_cv, NULL, CV_DEFAULT, NULL);
769 	mutex_init(&rwx->rwx_mutex, NULL, MUTEX_DEFAULT, NULL);
770 	rw_init(&rwx->rwx_lock, NULL, RW_DEFAULT, NULL);
771 }
772 
773 /*
774  * smb_rwx_destroy
775  */
776 void
smb_rwx_destroy(smb_rwx_t * rwx)777 smb_rwx_destroy(
778     smb_rwx_t	*rwx)
779 {
780 	mutex_destroy(&rwx->rwx_mutex);
781 	cv_destroy(&rwx->rwx_cv);
782 	rw_destroy(&rwx->rwx_lock);
783 }
784 
785 /*
786  * smb_rwx_rwexit
787  */
788 void
smb_rwx_rwexit(smb_rwx_t * rwx)789 smb_rwx_rwexit(
790     smb_rwx_t	*rwx)
791 {
792 	if (rw_write_held(&rwx->rwx_lock)) {
793 		ASSERT(rw_owner(&rwx->rwx_lock) == curthread);
794 		mutex_enter(&rwx->rwx_mutex);
795 		if (rwx->rwx_waiting) {
796 			rwx->rwx_waiting = B_FALSE;
797 			cv_broadcast(&rwx->rwx_cv);
798 		}
799 		mutex_exit(&rwx->rwx_mutex);
800 	}
801 	rw_exit(&rwx->rwx_lock);
802 }
803 
804 /*
805  * smb_rwx_rwupgrade
806  */
807 krw_t
smb_rwx_rwupgrade(smb_rwx_t * rwx)808 smb_rwx_rwupgrade(
809     smb_rwx_t	*rwx)
810 {
811 	if (rw_write_held(&rwx->rwx_lock)) {
812 		ASSERT(rw_owner(&rwx->rwx_lock) == curthread);
813 		return (RW_WRITER);
814 	}
815 	if (!rw_tryupgrade(&rwx->rwx_lock)) {
816 		rw_exit(&rwx->rwx_lock);
817 		rw_enter(&rwx->rwx_lock, RW_WRITER);
818 	}
819 	return (RW_READER);
820 }
821 
822 /*
823  * smb_rwx_rwrestore
824  */
825 void
smb_rwx_rwdowngrade(smb_rwx_t * rwx,krw_t mode)826 smb_rwx_rwdowngrade(
827     smb_rwx_t	*rwx,
828     krw_t	mode)
829 {
830 	ASSERT(rw_write_held(&rwx->rwx_lock));
831 	ASSERT(rw_owner(&rwx->rwx_lock) == curthread);
832 
833 	if (mode == RW_WRITER) {
834 		return;
835 	}
836 	ASSERT(mode == RW_READER);
837 	mutex_enter(&rwx->rwx_mutex);
838 	if (rwx->rwx_waiting) {
839 		rwx->rwx_waiting = B_FALSE;
840 		cv_broadcast(&rwx->rwx_cv);
841 	}
842 	mutex_exit(&rwx->rwx_mutex);
843 	rw_downgrade(&rwx->rwx_lock);
844 }
845 
846 /*
847  * smb_rwx_wait
848  *
849  * This function assumes the smb_rwx lock was enter in RW_READER or RW_WRITER
850  * mode. It will:
851  *
852  *	1) release the lock and save its current mode.
853  *	2) wait until the condition variable is signaled. This can happen for
854  *	   2 reasons: When a writer releases the lock or when the time out (if
855  *	   provided) expires.
856  *	3) re-acquire the lock in the mode saved in (1).
857  */
858 int
smb_rwx_rwwait(smb_rwx_t * rwx,clock_t timeout)859 smb_rwx_rwwait(
860     smb_rwx_t	*rwx,
861     clock_t	timeout)
862 {
863 	krw_t	mode;
864 	int	rc = 1;
865 
866 	mutex_enter(&rwx->rwx_mutex);
867 	rwx->rwx_waiting = B_TRUE;
868 	mutex_exit(&rwx->rwx_mutex);
869 
870 	if (rw_write_held(&rwx->rwx_lock)) {
871 		ASSERT(rw_owner(&rwx->rwx_lock) == curthread);
872 		mode = RW_WRITER;
873 	} else {
874 		ASSERT(rw_read_held(&rwx->rwx_lock));
875 		mode = RW_READER;
876 	}
877 	rw_exit(&rwx->rwx_lock);
878 
879 	mutex_enter(&rwx->rwx_mutex);
880 	if (rwx->rwx_waiting) {
881 		if (timeout == -1) {
882 			cv_wait(&rwx->rwx_cv, &rwx->rwx_mutex);
883 		} else {
884 			rc = cv_reltimedwait(&rwx->rwx_cv, &rwx->rwx_mutex,
885 			    timeout, TR_CLOCK_TICK);
886 		}
887 	}
888 	mutex_exit(&rwx->rwx_mutex);
889 
890 	rw_enter(&rwx->rwx_lock, mode);
891 	return (rc);
892 }
893 
894 /* smb_idmap_... moved to smb_idmap.c */
895 
896 uint64_t
smb_time_unix_to_nt(timestruc_t * unix_time)897 smb_time_unix_to_nt(timestruc_t *unix_time)
898 {
899 	uint64_t nt_time;
900 
901 	if ((unix_time->tv_sec == 0) && (unix_time->tv_nsec == 0))
902 		return (0);
903 
904 	nt_time = unix_time->tv_sec;
905 	nt_time *= 10000000;  /* seconds to 100ns */
906 	nt_time += unix_time->tv_nsec / 100;
907 	return (nt_time + NT_TIME_BIAS);
908 }
909 
910 void
smb_time_nt_to_unix(uint64_t nt_time,timestruc_t * unix_time)911 smb_time_nt_to_unix(uint64_t nt_time, timestruc_t *unix_time)
912 {
913 	uint32_t seconds;
914 
915 	ASSERT(unix_time);
916 
917 	if ((nt_time == 0) || (nt_time == -1)) {
918 		unix_time->tv_sec = 0;
919 		unix_time->tv_nsec = 0;
920 		return;
921 	}
922 
923 	/*
924 	 * Can't represent times less than or equal NT_TIME_BIAS,
925 	 * so convert them to the oldest date we can store.
926 	 * Note that time zero is "special" being converted
927 	 * both directions as 0:0 (unix-to-nt, nt-to-unix).
928 	 */
929 	if (nt_time <= NT_TIME_BIAS) {
930 		unix_time->tv_sec = 0;
931 		unix_time->tv_nsec = 100;
932 		return;
933 	}
934 
935 	nt_time -= NT_TIME_BIAS;
936 	seconds = nt_time / 10000000;
937 	unix_time->tv_sec = seconds;
938 	unix_time->tv_nsec = (nt_time  % 10000000) * 100;
939 }
940 
941 /*
942  * smb_time_gmt_to_local, smb_time_local_to_gmt
943  *
944  * Apply the gmt offset to convert between local time and gmt
945  */
946 int32_t
smb_time_gmt_to_local(smb_request_t * sr,int32_t gmt)947 smb_time_gmt_to_local(smb_request_t *sr, int32_t gmt)
948 {
949 	if ((gmt == 0) || (gmt == -1))
950 		return (0);
951 
952 	return (gmt - sr->sr_gmtoff);
953 }
954 
955 int32_t
smb_time_local_to_gmt(smb_request_t * sr,int32_t local)956 smb_time_local_to_gmt(smb_request_t *sr, int32_t local)
957 {
958 	if ((local == 0) || (local == -1))
959 		return (0);
960 
961 	return (local + sr->sr_gmtoff);
962 }
963 
964 
965 /*
966  * smb_time_dos_to_unix
967  *
968  * Convert SMB_DATE & SMB_TIME values to a unix timestamp.
969  *
970  * A date/time field of 0 means that that server file system
971  * assigned value need not be changed. The behaviour when the
972  * date/time field is set to -1 is not documented but is
973  * generally treated like 0.
974  * If date or time is 0 or -1 the unix time is returned as 0
975  * so that the caller can identify and handle this special case.
976  */
977 int32_t
smb_time_dos_to_unix(int16_t date,int16_t time)978 smb_time_dos_to_unix(int16_t date, int16_t time)
979 {
980 	struct tm	atm;
981 
982 	if (((date == 0) || (time == 0)) ||
983 	    ((date == -1) || (time == -1))) {
984 		return (0);
985 	}
986 
987 	atm.tm_year = ((date >>  9) & 0x3F) + 80;
988 	atm.tm_mon  = ((date >>  5) & 0x0F) - 1;
989 	atm.tm_mday = ((date >>  0) & 0x1F);
990 	atm.tm_hour = ((time >> 11) & 0x1F);
991 	atm.tm_min  = ((time >>  5) & 0x3F);
992 	atm.tm_sec  = ((time >>  0) & 0x1F) << 1;
993 
994 	return (smb_timegm(&atm));
995 }
996 
997 void
smb_time_unix_to_dos(int32_t ux_time,int16_t * date_p,int16_t * time_p)998 smb_time_unix_to_dos(int32_t ux_time, int16_t *date_p, int16_t *time_p)
999 {
1000 	struct tm	atm;
1001 	int		i;
1002 	time_t		tmp_time;
1003 
1004 	if (ux_time == 0) {
1005 		*date_p = 0;
1006 		*time_p = 0;
1007 		return;
1008 	}
1009 
1010 	tmp_time = (time_t)ux_time;
1011 	(void) smb_gmtime_r(&tmp_time, &atm);
1012 
1013 	if (date_p) {
1014 		i = 0;
1015 		i += atm.tm_year - 80;
1016 		i <<= 4;
1017 		i += atm.tm_mon + 1;
1018 		i <<= 5;
1019 		i += atm.tm_mday;
1020 
1021 		*date_p = (short)i;
1022 	}
1023 	if (time_p) {
1024 		i = 0;
1025 		i += atm.tm_hour;
1026 		i <<= 6;
1027 		i += atm.tm_min;
1028 		i <<= 5;
1029 		i += atm.tm_sec >> 1;
1030 
1031 		*time_p = (short)i;
1032 	}
1033 }
1034 
1035 
1036 /*
1037  * smb_gmtime_r
1038  *
1039  * Thread-safe version of smb_gmtime. Returns a null pointer if either
1040  * input parameter is a null pointer. Otherwise returns a pointer
1041  * to result.
1042  *
1043  * Day of the week calculation: the Epoch was a thursday.
1044  *
1045  * There are no timezone corrections so tm_isdst and tm_gmtoff are
1046  * always zero, and the zone is always WET.
1047  */
1048 struct tm *
smb_gmtime_r(time_t * clock,struct tm * result)1049 smb_gmtime_r(time_t *clock, struct tm *result)
1050 {
1051 	time_t tsec;
1052 	int year;
1053 	int month;
1054 	int sec_per_month;
1055 
1056 	if (clock == 0 || result == 0)
1057 		return (0);
1058 
1059 	bzero(result, sizeof (struct tm));
1060 	tsec = *clock;
1061 	tsec -= tzh_leapcnt;
1062 
1063 	result->tm_wday = tsec / SECSPERDAY;
1064 	result->tm_wday = (result->tm_wday + TM_THURSDAY) % DAYSPERWEEK;
1065 
1066 	year = EPOCH_YEAR;
1067 	while (tsec >= (isleap(year) ? (SECSPERDAY * DAYSPERLYEAR) :
1068 	    (SECSPERDAY * DAYSPERNYEAR))) {
1069 		if (isleap(year))
1070 			tsec -= SECSPERDAY * DAYSPERLYEAR;
1071 		else
1072 			tsec -= SECSPERDAY * DAYSPERNYEAR;
1073 
1074 		++year;
1075 	}
1076 
1077 	result->tm_year = year - TM_YEAR_BASE;
1078 	result->tm_yday = tsec / SECSPERDAY;
1079 
1080 	for (month = TM_JANUARY; month <= TM_DECEMBER; ++month) {
1081 		sec_per_month = days_in_month[month] * SECSPERDAY;
1082 
1083 		if (month == TM_FEBRUARY && isleap(year))
1084 			sec_per_month += SECSPERDAY;
1085 
1086 		if (tsec < sec_per_month)
1087 			break;
1088 
1089 		tsec -= sec_per_month;
1090 	}
1091 
1092 	result->tm_mon = month;
1093 	result->tm_mday = (tsec / SECSPERDAY) + 1;
1094 	tsec %= SECSPERDAY;
1095 	result->tm_sec = tsec % 60;
1096 	tsec /= 60;
1097 	result->tm_min = tsec % 60;
1098 	tsec /= 60;
1099 	result->tm_hour = (int)tsec;
1100 
1101 	return (result);
1102 }
1103 
1104 
1105 /*
1106  * smb_timegm
1107  *
1108  * Converts the broken-down time in tm to a time value, i.e. the number
1109  * of seconds since the Epoch (00:00:00 UTC, January 1, 1970). This is
1110  * not a POSIX or ANSI function. Per the man page, the input values of
1111  * tm_wday and tm_yday are ignored and, as the input data is assumed to
1112  * represent GMT, we force tm_isdst and tm_gmtoff to 0.
1113  *
1114  * Before returning the clock time, we use smb_gmtime_r to set up tm_wday
1115  * and tm_yday, and bring the other fields within normal range. I don't
1116  * think this is really how it should be done but it's convenient for
1117  * now.
1118  */
1119 time_t
smb_timegm(struct tm * tm)1120 smb_timegm(struct tm *tm)
1121 {
1122 	time_t tsec;
1123 	int dd;
1124 	int mm;
1125 	int yy;
1126 	int year;
1127 
1128 	if (tm == 0)
1129 		return (-1);
1130 
1131 	year = tm->tm_year + TM_YEAR_BASE;
1132 	tsec = tzh_leapcnt;
1133 
1134 	for (yy = EPOCH_YEAR; yy < year; ++yy) {
1135 		if (isleap(yy))
1136 			tsec += SECSPERDAY * DAYSPERLYEAR;
1137 		else
1138 			tsec += SECSPERDAY * DAYSPERNYEAR;
1139 	}
1140 
1141 	for (mm = TM_JANUARY; mm < tm->tm_mon; ++mm) {
1142 		dd = days_in_month[mm] * SECSPERDAY;
1143 
1144 		if (mm == TM_FEBRUARY && isleap(year))
1145 			dd += SECSPERDAY;
1146 
1147 		tsec += dd;
1148 	}
1149 
1150 	tsec += (tm->tm_mday - 1) * SECSPERDAY;
1151 	tsec += tm->tm_sec;
1152 	tsec += tm->tm_min * SECSPERMIN;
1153 	tsec += tm->tm_hour * SECSPERHOUR;
1154 
1155 	tm->tm_isdst = 0;
1156 	(void) smb_gmtime_r(&tsec, tm);
1157 	return (tsec);
1158 }
1159 
1160 /*
1161  * smb_pad_align
1162  *
1163  * Returns the number of bytes required to pad an offset to the
1164  * specified alignment.
1165  */
1166 uint32_t
smb_pad_align(uint32_t offset,uint32_t align)1167 smb_pad_align(uint32_t offset, uint32_t align)
1168 {
1169 	uint32_t pad = offset % align;
1170 
1171 	if (pad != 0)
1172 		pad = align - pad;
1173 
1174 	return (pad);
1175 }
1176 
1177 /*
1178  * smb_panic
1179  *
1180  * Logs the file name, function name and line number passed in and panics the
1181  * system.
1182  */
1183 void
smb_panic(char * file,const char * func,int line)1184 smb_panic(char *file, const char *func, int line)
1185 {
1186 	cmn_err(CE_PANIC, "%s:%s:%d\n", file, func, line);
1187 }
1188 
1189 /*
1190  * Creates an AVL tree and initializes the given smb_avl_t
1191  * structure using the passed args
1192  */
1193 void
smb_avl_create(smb_avl_t * avl,size_t size,size_t offset,const smb_avl_nops_t * ops)1194 smb_avl_create(smb_avl_t *avl, size_t size, size_t offset,
1195 	const smb_avl_nops_t *ops)
1196 {
1197 	ASSERT(avl);
1198 	ASSERT(ops);
1199 
1200 	rw_init(&avl->avl_lock, NULL, RW_DEFAULT, NULL);
1201 	mutex_init(&avl->avl_mutex, NULL, MUTEX_DEFAULT, NULL);
1202 
1203 	avl->avl_nops = ops;
1204 	avl->avl_state = SMB_AVL_STATE_READY;
1205 	avl->avl_refcnt = 0;
1206 	(void) random_get_pseudo_bytes((uint8_t *)&avl->avl_sequence,
1207 	    sizeof (uint32_t));
1208 
1209 	avl_create(&avl->avl_tree, ops->avln_cmp, size, offset);
1210 }
1211 
1212 /*
1213  * Destroys the specified AVL tree.
1214  * It waits for all the in-flight operations to finish
1215  * before destroying the AVL.
1216  */
1217 void
smb_avl_destroy(smb_avl_t * avl)1218 smb_avl_destroy(smb_avl_t *avl)
1219 {
1220 	void *cookie = NULL;
1221 	void *node;
1222 
1223 	ASSERT(avl);
1224 
1225 	mutex_enter(&avl->avl_mutex);
1226 	if (avl->avl_state != SMB_AVL_STATE_READY) {
1227 		mutex_exit(&avl->avl_mutex);
1228 		return;
1229 	}
1230 
1231 	avl->avl_state = SMB_AVL_STATE_DESTROYING;
1232 
1233 	while (avl->avl_refcnt > 0)
1234 		(void) cv_wait(&avl->avl_cv, &avl->avl_mutex);
1235 	mutex_exit(&avl->avl_mutex);
1236 
1237 	rw_enter(&avl->avl_lock, RW_WRITER);
1238 	while ((node = avl_destroy_nodes(&avl->avl_tree, &cookie)) != NULL)
1239 		avl->avl_nops->avln_destroy(node);
1240 
1241 	avl_destroy(&avl->avl_tree);
1242 	rw_exit(&avl->avl_lock);
1243 
1244 	rw_destroy(&avl->avl_lock);
1245 
1246 	mutex_destroy(&avl->avl_mutex);
1247 	bzero(avl, sizeof (smb_avl_t));
1248 }
1249 
1250 /*
1251  * Adds the given item to the AVL if it's
1252  * not already there.
1253  *
1254  * Returns:
1255  *
1256  * 	ENOTACTIVE	AVL is not in READY state
1257  * 	EEXIST		The item is already in AVL
1258  */
1259 int
smb_avl_add(smb_avl_t * avl,void * item)1260 smb_avl_add(smb_avl_t *avl, void *item)
1261 {
1262 	avl_index_t where;
1263 
1264 	ASSERT(avl);
1265 	ASSERT(item);
1266 
1267 	if (!smb_avl_hold(avl))
1268 		return (ENOTACTIVE);
1269 
1270 	rw_enter(&avl->avl_lock, RW_WRITER);
1271 	if (avl_find(&avl->avl_tree, item, &where) != NULL) {
1272 		rw_exit(&avl->avl_lock);
1273 		smb_avl_rele(avl);
1274 		return (EEXIST);
1275 	}
1276 
1277 	avl_insert(&avl->avl_tree, item, where);
1278 	avl->avl_sequence++;
1279 	rw_exit(&avl->avl_lock);
1280 
1281 	smb_avl_rele(avl);
1282 	return (0);
1283 }
1284 
1285 /*
1286  * Removes the given item from the AVL.
1287  * If no reference is left on the item
1288  * it will also be destroyed by calling the
1289  * registered destroy operation.
1290  */
1291 void
smb_avl_remove(smb_avl_t * avl,void * item)1292 smb_avl_remove(smb_avl_t *avl, void *item)
1293 {
1294 	avl_index_t where;
1295 	void *rm_item;
1296 
1297 	ASSERT(avl);
1298 	ASSERT(item);
1299 
1300 	if (!smb_avl_hold(avl))
1301 		return;
1302 
1303 	rw_enter(&avl->avl_lock, RW_WRITER);
1304 	if ((rm_item = avl_find(&avl->avl_tree, item, &where)) == NULL) {
1305 		rw_exit(&avl->avl_lock);
1306 		smb_avl_rele(avl);
1307 		return;
1308 	}
1309 
1310 	avl_remove(&avl->avl_tree, rm_item);
1311 	if (avl->avl_nops->avln_rele(rm_item))
1312 		avl->avl_nops->avln_destroy(rm_item);
1313 	avl->avl_sequence++;
1314 	rw_exit(&avl->avl_lock);
1315 
1316 	smb_avl_rele(avl);
1317 }
1318 
1319 /*
1320  * Looks up the AVL for the given item.
1321  * If the item is found a hold on the object
1322  * is taken before the pointer to it is
1323  * returned to the caller. The caller MUST
1324  * always call smb_avl_release() after it's done
1325  * using the returned object to release the hold
1326  * taken on the object.
1327  */
1328 void *
smb_avl_lookup(smb_avl_t * avl,void * item)1329 smb_avl_lookup(smb_avl_t *avl, void *item)
1330 {
1331 	void *node = NULL;
1332 
1333 	ASSERT(avl);
1334 	ASSERT(item);
1335 
1336 	if (!smb_avl_hold(avl))
1337 		return (NULL);
1338 
1339 	rw_enter(&avl->avl_lock, RW_READER);
1340 	node = avl_find(&avl->avl_tree, item, NULL);
1341 	if (node != NULL)
1342 		avl->avl_nops->avln_hold(node);
1343 	rw_exit(&avl->avl_lock);
1344 
1345 	if (node == NULL)
1346 		smb_avl_rele(avl);
1347 
1348 	return (node);
1349 }
1350 
1351 /*
1352  * The hold on the given object is released.
1353  * This function MUST always be called after
1354  * smb_avl_lookup() and smb_avl_iterate() for
1355  * the returned object.
1356  *
1357  * If AVL is in DESTROYING state, the destroying
1358  * thread will be notified.
1359  */
1360 void
smb_avl_release(smb_avl_t * avl,void * item)1361 smb_avl_release(smb_avl_t *avl, void *item)
1362 {
1363 	ASSERT(avl);
1364 	ASSERT(item);
1365 
1366 	if (avl->avl_nops->avln_rele(item))
1367 		avl->avl_nops->avln_destroy(item);
1368 
1369 	smb_avl_rele(avl);
1370 }
1371 
1372 /*
1373  * Initializes the given cursor for the AVL.
1374  * The cursor will be used to iterate through the AVL
1375  */
1376 void
smb_avl_iterinit(smb_avl_t * avl,smb_avl_cursor_t * cursor)1377 smb_avl_iterinit(smb_avl_t *avl, smb_avl_cursor_t *cursor)
1378 {
1379 	ASSERT(avl);
1380 	ASSERT(cursor);
1381 
1382 	cursor->avlc_next = NULL;
1383 	cursor->avlc_sequence = avl->avl_sequence;
1384 }
1385 
1386 /*
1387  * Iterates through the AVL using the given cursor.
1388  * It always starts at the beginning and then returns
1389  * a pointer to the next object on each subsequent call.
1390  *
1391  * If a new object is added to or removed from the AVL
1392  * between two calls to this function, the iteration
1393  * will terminate prematurely.
1394  *
1395  * The caller MUST always call smb_avl_release() after it's
1396  * done using the returned object to release the hold taken
1397  * on the object.
1398  */
1399 void *
smb_avl_iterate(smb_avl_t * avl,smb_avl_cursor_t * cursor)1400 smb_avl_iterate(smb_avl_t *avl, smb_avl_cursor_t *cursor)
1401 {
1402 	void *node;
1403 
1404 	ASSERT(avl);
1405 	ASSERT(cursor);
1406 
1407 	if (!smb_avl_hold(avl))
1408 		return (NULL);
1409 
1410 	rw_enter(&avl->avl_lock, RW_READER);
1411 	if (cursor->avlc_sequence != avl->avl_sequence) {
1412 		rw_exit(&avl->avl_lock);
1413 		smb_avl_rele(avl);
1414 		return (NULL);
1415 	}
1416 
1417 	if (cursor->avlc_next == NULL)
1418 		node = avl_first(&avl->avl_tree);
1419 	else
1420 		node = AVL_NEXT(&avl->avl_tree, cursor->avlc_next);
1421 
1422 	if (node != NULL)
1423 		avl->avl_nops->avln_hold(node);
1424 
1425 	cursor->avlc_next = node;
1426 	rw_exit(&avl->avl_lock);
1427 
1428 	if (node == NULL)
1429 		smb_avl_rele(avl);
1430 
1431 	return (node);
1432 }
1433 
1434 /*
1435  * Increments the AVL reference count in order to
1436  * prevent the avl from being destroyed while it's
1437  * being accessed.
1438  */
1439 static boolean_t
smb_avl_hold(smb_avl_t * avl)1440 smb_avl_hold(smb_avl_t *avl)
1441 {
1442 	mutex_enter(&avl->avl_mutex);
1443 	if (avl->avl_state != SMB_AVL_STATE_READY) {
1444 		mutex_exit(&avl->avl_mutex);
1445 		return (B_FALSE);
1446 	}
1447 	avl->avl_refcnt++;
1448 	mutex_exit(&avl->avl_mutex);
1449 
1450 	return (B_TRUE);
1451 }
1452 
1453 /*
1454  * Decrements the AVL reference count to release the
1455  * hold. If another thread is trying to destroy the
1456  * AVL and is waiting for the reference count to become
1457  * 0, it is signaled to wake up.
1458  */
1459 static void
smb_avl_rele(smb_avl_t * avl)1460 smb_avl_rele(smb_avl_t *avl)
1461 {
1462 	mutex_enter(&avl->avl_mutex);
1463 	ASSERT(avl->avl_refcnt > 0);
1464 	avl->avl_refcnt--;
1465 	if (avl->avl_state == SMB_AVL_STATE_DESTROYING)
1466 		cv_broadcast(&avl->avl_cv);
1467 	mutex_exit(&avl->avl_mutex);
1468 }
1469 
1470 /*
1471  * smb_latency_init
1472  */
1473 void
smb_latency_init(smb_latency_t * lat)1474 smb_latency_init(smb_latency_t *lat)
1475 {
1476 	bzero(lat, sizeof (*lat));
1477 	mutex_init(&lat->ly_mutex, NULL, MUTEX_SPIN, (void *)ipltospl(SPL7));
1478 }
1479 
1480 /*
1481  * smb_latency_destroy
1482  */
1483 void
smb_latency_destroy(smb_latency_t * lat)1484 smb_latency_destroy(smb_latency_t *lat)
1485 {
1486 	mutex_destroy(&lat->ly_mutex);
1487 }
1488 
1489 /*
1490  * smb_latency_add_sample
1491  *
1492  * Uses the new sample to calculate the new mean and standard deviation. The
1493  * sample must be a scaled value.
1494  */
1495 void
smb_latency_add_sample(smb_latency_t * lat,hrtime_t sample)1496 smb_latency_add_sample(smb_latency_t *lat, hrtime_t sample)
1497 {
1498 	hrtime_t	a_mean;
1499 	hrtime_t	d_mean;
1500 
1501 	mutex_enter(&lat->ly_mutex);
1502 	lat->ly_a_nreq++;
1503 	lat->ly_a_sum += sample;
1504 	if (lat->ly_a_nreq != 0) {
1505 		a_mean = lat->ly_a_sum / lat->ly_a_nreq;
1506 		lat->ly_a_stddev =
1507 		    (sample - a_mean) * (sample - lat->ly_a_mean);
1508 		lat->ly_a_mean = a_mean;
1509 	}
1510 	lat->ly_d_nreq++;
1511 	lat->ly_d_sum += sample;
1512 	if (lat->ly_d_nreq != 0) {
1513 		d_mean = lat->ly_d_sum / lat->ly_d_nreq;
1514 		lat->ly_d_stddev =
1515 		    (sample - d_mean) * (sample - lat->ly_d_mean);
1516 		lat->ly_d_mean = d_mean;
1517 	}
1518 	mutex_exit(&lat->ly_mutex);
1519 }
1520 
1521 /*
1522  * smb_srqueue_init
1523  */
1524 void
smb_srqueue_init(smb_srqueue_t * srq)1525 smb_srqueue_init(smb_srqueue_t *srq)
1526 {
1527 	bzero(srq, sizeof (*srq));
1528 	mutex_init(&srq->srq_mutex, NULL, MUTEX_SPIN, (void *)ipltospl(SPL7));
1529 	srq->srq_wlastupdate = srq->srq_rlastupdate = gethrtime_unscaled();
1530 }
1531 
1532 /*
1533  * smb_srqueue_destroy
1534  */
1535 void
smb_srqueue_destroy(smb_srqueue_t * srq)1536 smb_srqueue_destroy(smb_srqueue_t *srq)
1537 {
1538 	mutex_destroy(&srq->srq_mutex);
1539 }
1540 
1541 /*
1542  * smb_srqueue_waitq_enter
1543  */
1544 void
smb_srqueue_waitq_enter(smb_srqueue_t * srq)1545 smb_srqueue_waitq_enter(smb_srqueue_t *srq)
1546 {
1547 	hrtime_t	new;
1548 	hrtime_t	delta;
1549 	uint32_t	wcnt;
1550 
1551 	mutex_enter(&srq->srq_mutex);
1552 	new = gethrtime_unscaled();
1553 	delta = new - srq->srq_wlastupdate;
1554 	srq->srq_wlastupdate = new;
1555 	wcnt = srq->srq_wcnt++;
1556 	if (wcnt != 0) {
1557 		srq->srq_wlentime += delta * wcnt;
1558 		srq->srq_wtime += delta;
1559 	}
1560 	mutex_exit(&srq->srq_mutex);
1561 }
1562 
1563 /*
1564  * smb_srqueue_runq_exit
1565  */
1566 void
smb_srqueue_runq_exit(smb_srqueue_t * srq)1567 smb_srqueue_runq_exit(smb_srqueue_t *srq)
1568 {
1569 	hrtime_t	new;
1570 	hrtime_t	delta;
1571 	uint32_t	rcnt;
1572 
1573 	mutex_enter(&srq->srq_mutex);
1574 	new = gethrtime_unscaled();
1575 	delta = new - srq->srq_rlastupdate;
1576 	srq->srq_rlastupdate = new;
1577 	rcnt = srq->srq_rcnt--;
1578 	ASSERT(rcnt > 0);
1579 	srq->srq_rlentime += delta * rcnt;
1580 	srq->srq_rtime += delta;
1581 	mutex_exit(&srq->srq_mutex);
1582 }
1583 
1584 /*
1585  * smb_srqueue_waitq_to_runq
1586  */
1587 void
smb_srqueue_waitq_to_runq(smb_srqueue_t * srq)1588 smb_srqueue_waitq_to_runq(smb_srqueue_t *srq)
1589 {
1590 	hrtime_t	new;
1591 	hrtime_t	delta;
1592 	uint32_t	wcnt;
1593 	uint32_t	rcnt;
1594 
1595 	mutex_enter(&srq->srq_mutex);
1596 	new = gethrtime_unscaled();
1597 	delta = new - srq->srq_wlastupdate;
1598 	srq->srq_wlastupdate = new;
1599 	wcnt = srq->srq_wcnt--;
1600 	ASSERT(wcnt > 0);
1601 	srq->srq_wlentime += delta * wcnt;
1602 	srq->srq_wtime += delta;
1603 	delta = new - srq->srq_rlastupdate;
1604 	srq->srq_rlastupdate = new;
1605 	rcnt = srq->srq_rcnt++;
1606 	if (rcnt != 0) {
1607 		srq->srq_rlentime += delta * rcnt;
1608 		srq->srq_rtime += delta;
1609 	}
1610 	mutex_exit(&srq->srq_mutex);
1611 }
1612 
1613 /*
1614  * smb_srqueue_update
1615  *
1616  * Takes a snapshot of the smb_sr_stat_t structure passed in.
1617  */
1618 void
smb_srqueue_update(smb_srqueue_t * srq,smb_kstat_utilization_t * kd)1619 smb_srqueue_update(smb_srqueue_t *srq, smb_kstat_utilization_t *kd)
1620 {
1621 	hrtime_t	delta;
1622 	hrtime_t	snaptime;
1623 
1624 	mutex_enter(&srq->srq_mutex);
1625 	snaptime = gethrtime_unscaled();
1626 	delta = snaptime - srq->srq_wlastupdate;
1627 	srq->srq_wlastupdate = snaptime;
1628 	if (srq->srq_wcnt != 0) {
1629 		srq->srq_wlentime += delta * srq->srq_wcnt;
1630 		srq->srq_wtime += delta;
1631 	}
1632 	delta = snaptime - srq->srq_rlastupdate;
1633 	srq->srq_rlastupdate = snaptime;
1634 	if (srq->srq_rcnt != 0) {
1635 		srq->srq_rlentime += delta * srq->srq_rcnt;
1636 		srq->srq_rtime += delta;
1637 	}
1638 	kd->ku_rlentime = srq->srq_rlentime;
1639 	kd->ku_rtime = srq->srq_rtime;
1640 	kd->ku_wlentime = srq->srq_wlentime;
1641 	kd->ku_wtime = srq->srq_wtime;
1642 	mutex_exit(&srq->srq_mutex);
1643 	scalehrtime(&kd->ku_rlentime);
1644 	scalehrtime(&kd->ku_rtime);
1645 	scalehrtime(&kd->ku_wlentime);
1646 	scalehrtime(&kd->ku_wtime);
1647 }
1648 
1649 void
smb_threshold_init(smb_cmd_threshold_t * ct,char * cmd,uint_t threshold,uint_t timeout)1650 smb_threshold_init(smb_cmd_threshold_t *ct, char *cmd,
1651     uint_t threshold, uint_t timeout)
1652 {
1653 	bzero(ct, sizeof (smb_cmd_threshold_t));
1654 	mutex_init(&ct->ct_mutex, NULL, MUTEX_DEFAULT, NULL);
1655 	cv_init(&ct->ct_cond, NULL, CV_DEFAULT, NULL);
1656 
1657 	ct->ct_cmd = cmd;
1658 	ct->ct_threshold = threshold;
1659 	ct->ct_timeout = timeout;
1660 }
1661 
1662 void
smb_threshold_fini(smb_cmd_threshold_t * ct)1663 smb_threshold_fini(smb_cmd_threshold_t *ct)
1664 {
1665 	cv_destroy(&ct->ct_cond);
1666 	mutex_destroy(&ct->ct_mutex);
1667 }
1668 
1669 /*
1670  * This threshold mechanism is used to limit the number of simultaneous
1671  * named pipe connections, concurrent authentication conversations, etc.
1672  * Requests that would take us over the threshold wait until either the
1673  * resources are available (return zero) or timeout (return error).
1674  */
1675 int
smb_threshold_enter(smb_cmd_threshold_t * ct)1676 smb_threshold_enter(smb_cmd_threshold_t *ct)
1677 {
1678 	clock_t	time, rem;
1679 
1680 	time = MSEC_TO_TICK(ct->ct_timeout) + ddi_get_lbolt();
1681 	mutex_enter(&ct->ct_mutex);
1682 
1683 	while (ct->ct_threshold != 0 &&
1684 	    ct->ct_threshold <= ct->ct_active_cnt) {
1685 		ct->ct_blocked_cnt++;
1686 		rem = cv_timedwait(&ct->ct_cond, &ct->ct_mutex, time);
1687 		ct->ct_blocked_cnt--;
1688 		if (rem < 0) {
1689 			mutex_exit(&ct->ct_mutex);
1690 			return (ETIME);
1691 		}
1692 	}
1693 	if (ct->ct_threshold == 0) {
1694 		mutex_exit(&ct->ct_mutex);
1695 		return (ECANCELED);
1696 	}
1697 
1698 	ASSERT3U(ct->ct_active_cnt, <, ct->ct_threshold);
1699 	ct->ct_active_cnt++;
1700 
1701 	mutex_exit(&ct->ct_mutex);
1702 	return (0);
1703 }
1704 
1705 void
smb_threshold_exit(smb_cmd_threshold_t * ct)1706 smb_threshold_exit(smb_cmd_threshold_t *ct)
1707 {
1708 	mutex_enter(&ct->ct_mutex);
1709 	ASSERT3U(ct->ct_active_cnt, >, 0);
1710 	ct->ct_active_cnt--;
1711 	if (ct->ct_blocked_cnt)
1712 		cv_signal(&ct->ct_cond);
1713 	mutex_exit(&ct->ct_mutex);
1714 }
1715 
1716 void
smb_threshold_wake_all(smb_cmd_threshold_t * ct)1717 smb_threshold_wake_all(smb_cmd_threshold_t *ct)
1718 {
1719 	mutex_enter(&ct->ct_mutex);
1720 	ct->ct_threshold = 0;
1721 	cv_broadcast(&ct->ct_cond);
1722 	mutex_exit(&ct->ct_mutex);
1723 }
1724