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