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 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T. 26 * All rights reserved. 27 */ 28 /* 29 * Copyright (c) 2017 by Delphix. All rights reserved. 30 */ 31 32 /* 33 * Node hash implementation initially borrowed from NFS (nfs_subr.c) 34 * but then heavily modified. It's no longer an array of hash lists, 35 * but an AVL tree per mount point. More on this below. 36 */ 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/time.h> 41 #include <sys/vnode.h> 42 #include <sys/bitmap.h> 43 #include <sys/dnlc.h> 44 #include <sys/kmem.h> 45 #include <sys/sunddi.h> 46 #include <sys/sysmacros.h> 47 48 #include <netsmb/smb_osdep.h> 49 50 #include <netsmb/smb.h> 51 #include <netsmb/smb_conn.h> 52 #include <netsmb/smb_subr.h> 53 #include <netsmb/smb_rq.h> 54 55 #include <smbfs/smbfs.h> 56 #include <smbfs/smbfs_node.h> 57 #include <smbfs/smbfs_subr.h> 58 59 /* 60 * The AVL trees (now per-mount) allow finding an smbfs node by its 61 * full remote path name. It also allows easy traversal of all nodes 62 * below (path wise) any given node. A reader/writer lock for each 63 * (per mount) AVL tree is used to control access and to synchronize 64 * lookups, additions, and deletions from that AVL tree. 65 * 66 * Previously, this code use a global array of hash chains, each with 67 * its own rwlock. A few struct members, functions, and comments may 68 * still refer to a "hash", and those should all now be considered to 69 * refer to the per-mount AVL tree that replaced the old hash chains. 70 * (i.e. member smi_hash_lk, function sn_hashfind, etc.) 71 * 72 * The smbnode freelist is organized as a doubly linked list with 73 * a head pointer. Additions and deletions are synchronized via 74 * a single mutex. 75 * 76 * In order to add an smbnode to the free list, it must be linked into 77 * the mount's AVL tree and the exclusive lock for the AVL must be held. 78 * If an smbnode is not linked into the AVL tree, then it is destroyed 79 * because it represents no valuable information that can be reused 80 * about the file. The exclusive lock for the AVL tree must be held 81 * in order to prevent a lookup in the AVL tree from finding the 82 * smbnode and using it and assuming that the smbnode is not on the 83 * freelist. The lookup in the AVL tree will have the AVL tree lock 84 * held, either exclusive or shared. 85 * 86 * The vnode reference count for each smbnode is not allowed to drop 87 * below 1. This prevents external entities, such as the VM 88 * subsystem, from acquiring references to vnodes already on the 89 * freelist and then trying to place them back on the freelist 90 * when their reference is released. This means that the when an 91 * smbnode is looked up in the AVL tree, then either the smbnode 92 * is removed from the freelist and that reference is tranfered to 93 * the new reference or the vnode reference count must be incremented 94 * accordingly. The mutex for the freelist must be held in order to 95 * accurately test to see if the smbnode is on the freelist or not. 96 * The AVL tree lock might be held shared and it is possible that 97 * two different threads may race to remove the smbnode from the 98 * freelist. This race can be resolved by holding the mutex for the 99 * freelist. Please note that the mutex for the freelist does not 100 * need to held if the smbnode is not on the freelist. It can not be 101 * placed on the freelist due to the requirement that the thread 102 * putting the smbnode on the freelist must hold the exclusive lock 103 * for the AVL tree and the thread doing the lookup in the AVL tree 104 * is holding either a shared or exclusive lock for the AVL tree. 105 * 106 * The lock ordering is: 107 * 108 * AVL tree lock -> vnode lock 109 * AVL tree lock -> freelist lock 110 */ 111 112 static kmutex_t smbfreelist_lock; 113 static smbnode_t *smbfreelist = NULL; 114 static ulong_t smbnodenew = 0; 115 long nsmbnode = 0; 116 117 static struct kmem_cache *smbnode_cache; 118 119 static const vsecattr_t smbfs_vsa0 = { 0 }; 120 121 /* 122 * Mutex to protect the following variables: 123 * smbfs_major 124 * smbfs_minor 125 */ 126 kmutex_t smbfs_minor_lock; 127 int smbfs_major; 128 int smbfs_minor; 129 130 /* See smbfs_node_findcreate() */ 131 struct smbfattr smbfs_fattr0; 132 133 /* 134 * Local functions. 135 * SN for Smb Node 136 */ 137 static void sn_rmfree(smbnode_t *); 138 static void sn_inactive(smbnode_t *); 139 static void sn_addhash_locked(smbnode_t *, avl_index_t); 140 static void sn_rmhash_locked(smbnode_t *); 141 static void sn_destroy_node(smbnode_t *); 142 void smbfs_kmem_reclaim(void *cdrarg); 143 144 static smbnode_t * 145 sn_hashfind(smbmntinfo_t *, const char *, int, avl_index_t *); 146 147 static smbnode_t * 148 make_smbnode(smbmntinfo_t *, const char *, int, int *); 149 150 /* 151 * Free the resources associated with an smbnode. 152 * Note: This is different from smbfs_inactive 153 * 154 * NFS: nfs_subr.c:rinactive 155 */ 156 static void 157 sn_inactive(smbnode_t *np) 158 { 159 vsecattr_t ovsa; 160 cred_t *oldcr; 161 char *orpath; 162 int orplen; 163 164 /* 165 * Flush and invalidate all pages (todo) 166 * Free any held credentials and caches... 167 * etc. (See NFS code) 168 */ 169 mutex_enter(&np->r_statelock); 170 171 ovsa = np->r_secattr; 172 np->r_secattr = smbfs_vsa0; 173 np->r_sectime = 0; 174 175 oldcr = np->r_cred; 176 np->r_cred = NULL; 177 178 orpath = np->n_rpath; 179 orplen = np->n_rplen; 180 np->n_rpath = NULL; 181 np->n_rplen = 0; 182 183 mutex_exit(&np->r_statelock); 184 185 if (ovsa.vsa_aclentp != NULL) 186 kmem_free(ovsa.vsa_aclentp, ovsa.vsa_aclentsz); 187 188 if (oldcr != NULL) 189 crfree(oldcr); 190 191 if (orpath != NULL) 192 kmem_free(orpath, orplen + 1); 193 } 194 195 /* 196 * Find and optionally create an smbnode for the passed 197 * mountinfo, directory, separator, and name. If the 198 * desired smbnode already exists, return a reference. 199 * If the file attributes pointer is non-null, the node 200 * is created if necessary and linked into the AVL tree. 201 * 202 * Callers that need a node created but don't have the 203 * real attributes pass smbfs_fattr0 to force creation. 204 * 205 * Note: make_smbnode() may upgrade the "hash" lock to exclusive. 206 * 207 * NFS: nfs_subr.c:makenfsnode 208 */ 209 smbnode_t * 210 smbfs_node_findcreate( 211 smbmntinfo_t *mi, 212 const char *dirnm, 213 int dirlen, 214 const char *name, 215 int nmlen, 216 char sep, 217 struct smbfattr *fap) 218 { 219 char tmpbuf[256]; 220 size_t rpalloc; 221 char *p, *rpath; 222 int rplen; 223 smbnode_t *np; 224 vnode_t *vp; 225 int newnode; 226 227 /* 228 * Build the search string, either in tmpbuf or 229 * in allocated memory if larger than tmpbuf. 230 */ 231 rplen = dirlen; 232 if (sep != '\0') 233 rplen++; 234 rplen += nmlen; 235 if (rplen < sizeof (tmpbuf)) { 236 /* use tmpbuf */ 237 rpalloc = 0; 238 rpath = tmpbuf; 239 } else { 240 rpalloc = rplen + 1; 241 rpath = kmem_alloc(rpalloc, KM_SLEEP); 242 } 243 p = rpath; 244 bcopy(dirnm, p, dirlen); 245 p += dirlen; 246 if (sep != '\0') 247 *p++ = sep; 248 if (name != NULL) { 249 bcopy(name, p, nmlen); 250 p += nmlen; 251 } 252 ASSERT(p == rpath + rplen); 253 254 /* 255 * Find or create a node with this path. 256 */ 257 rw_enter(&mi->smi_hash_lk, RW_READER); 258 if (fap == NULL) 259 np = sn_hashfind(mi, rpath, rplen, NULL); 260 else 261 np = make_smbnode(mi, rpath, rplen, &newnode); 262 rw_exit(&mi->smi_hash_lk); 263 264 if (rpalloc) 265 kmem_free(rpath, rpalloc); 266 267 if (fap == NULL) { 268 /* 269 * Caller is "just looking" (no create) 270 * so np may or may not be NULL here. 271 * Either way, we're done. 272 */ 273 return (np); 274 } 275 276 /* 277 * We should have a node, possibly created. 278 * Do we have (real) attributes to apply? 279 */ 280 ASSERT(np != NULL); 281 if (fap == &smbfs_fattr0) 282 return (np); 283 284 /* 285 * Apply the given attributes to this node, 286 * dealing with any cache impact, etc. 287 */ 288 vp = SMBTOV(np); 289 if (!newnode) { 290 /* 291 * Found an existing node. 292 * Maybe purge caches... 293 */ 294 smbfs_cache_check(vp, fap); 295 } 296 smbfs_attrcache_fa(vp, fap); 297 298 /* 299 * Note NFS sets vp->v_type here, assuming it 300 * can never change for the life of a node. 301 * We allow v_type to change, and set it in 302 * smbfs_attrcache(). Also: mode, uid, gid 303 */ 304 return (np); 305 } 306 307 /* 308 * NFS: nfs_subr.c:rtablehash 309 * We use smbfs_hash(). 310 */ 311 312 /* 313 * Find or create an smbnode. 314 * NFS: nfs_subr.c:make_rnode 315 */ 316 static smbnode_t * 317 make_smbnode( 318 smbmntinfo_t *mi, 319 const char *rpath, 320 int rplen, 321 int *newnode) 322 { 323 smbnode_t *np; 324 smbnode_t *tnp; 325 vnode_t *vp; 326 vfs_t *vfsp; 327 avl_index_t where; 328 char *new_rpath = NULL; 329 330 ASSERT(RW_READ_HELD(&mi->smi_hash_lk)); 331 vfsp = mi->smi_vfsp; 332 333 start: 334 np = sn_hashfind(mi, rpath, rplen, NULL); 335 if (np != NULL) { 336 *newnode = 0; 337 return (np); 338 } 339 340 /* Note: will retake this lock below. */ 341 rw_exit(&mi->smi_hash_lk); 342 343 /* 344 * see if we can find something on the freelist 345 */ 346 mutex_enter(&smbfreelist_lock); 347 if (smbfreelist != NULL && smbnodenew >= nsmbnode) { 348 np = smbfreelist; 349 sn_rmfree(np); 350 mutex_exit(&smbfreelist_lock); 351 352 vp = SMBTOV(np); 353 354 if (np->r_flags & RHASHED) { 355 smbmntinfo_t *tmp_mi = np->n_mount; 356 ASSERT(tmp_mi != NULL); 357 rw_enter(&tmp_mi->smi_hash_lk, RW_WRITER); 358 mutex_enter(&vp->v_lock); 359 if (vp->v_count > 1) { 360 VN_RELE_LOCKED(vp); 361 mutex_exit(&vp->v_lock); 362 rw_exit(&tmp_mi->smi_hash_lk); 363 /* start over */ 364 rw_enter(&mi->smi_hash_lk, RW_READER); 365 goto start; 366 } 367 mutex_exit(&vp->v_lock); 368 sn_rmhash_locked(np); 369 rw_exit(&tmp_mi->smi_hash_lk); 370 } 371 372 sn_inactive(np); 373 374 mutex_enter(&vp->v_lock); 375 if (vp->v_count > 1) { 376 VN_RELE_LOCKED(vp); 377 mutex_exit(&vp->v_lock); 378 rw_enter(&mi->smi_hash_lk, RW_READER); 379 goto start; 380 } 381 mutex_exit(&vp->v_lock); 382 vn_invalid(vp); 383 /* 384 * destroy old locks before bzero'ing and 385 * recreating the locks below. 386 */ 387 smbfs_rw_destroy(&np->r_rwlock); 388 smbfs_rw_destroy(&np->r_lkserlock); 389 mutex_destroy(&np->r_statelock); 390 cv_destroy(&np->r_cv); 391 /* 392 * Make sure that if smbnode is recycled then 393 * VFS count is decremented properly before 394 * reuse. 395 */ 396 VFS_RELE(vp->v_vfsp); 397 vn_reinit(vp); 398 } else { 399 /* 400 * allocate and initialize a new smbnode 401 */ 402 vnode_t *new_vp; 403 404 mutex_exit(&smbfreelist_lock); 405 406 np = kmem_cache_alloc(smbnode_cache, KM_SLEEP); 407 new_vp = vn_alloc(KM_SLEEP); 408 409 atomic_inc_ulong((ulong_t *)&smbnodenew); 410 vp = new_vp; 411 } 412 413 /* 414 * Allocate and copy the rpath we'll need below. 415 */ 416 new_rpath = kmem_alloc(rplen + 1, KM_SLEEP); 417 bcopy(rpath, new_rpath, rplen); 418 new_rpath[rplen] = '\0'; 419 420 /* Initialize smbnode_t */ 421 bzero(np, sizeof (*np)); 422 423 smbfs_rw_init(&np->r_rwlock, NULL, RW_DEFAULT, NULL); 424 smbfs_rw_init(&np->r_lkserlock, NULL, RW_DEFAULT, NULL); 425 mutex_init(&np->r_statelock, NULL, MUTEX_DEFAULT, NULL); 426 cv_init(&np->r_cv, NULL, CV_DEFAULT, NULL); 427 /* cv_init(&np->r_commit.c_cv, NULL, CV_DEFAULT, NULL); */ 428 429 np->r_vnode = vp; 430 np->n_mount = mi; 431 432 np->n_fid = SMB_FID_UNUSED; 433 np->n_uid = mi->smi_uid; 434 np->n_gid = mi->smi_gid; 435 /* Leave attributes "stale." */ 436 437 #if 0 /* XXX dircache */ 438 /* 439 * We don't know if it's a directory yet. 440 * Let the caller do this? XXX 441 */ 442 avl_create(&np->r_dir, compar, sizeof (rddir_cache), 443 offsetof(rddir_cache, tree)); 444 #endif 445 446 /* Now fill in the vnode. */ 447 vn_setops(vp, smbfs_vnodeops); 448 vp->v_data = (caddr_t)np; 449 VFS_HOLD(vfsp); 450 vp->v_vfsp = vfsp; 451 vp->v_type = VNON; 452 453 /* 454 * We entered with mi->smi_hash_lk held (reader). 455 * Retake it now, (as the writer). 456 * Will return with it held. 457 */ 458 rw_enter(&mi->smi_hash_lk, RW_WRITER); 459 460 /* 461 * There is a race condition where someone else 462 * may alloc the smbnode while no locks are held, 463 * so check again and recover if found. 464 */ 465 tnp = sn_hashfind(mi, rpath, rplen, &where); 466 if (tnp != NULL) { 467 /* 468 * Lost the race. Put the node we were building 469 * on the free list and return the one we found. 470 */ 471 rw_exit(&mi->smi_hash_lk); 472 kmem_free(new_rpath, rplen + 1); 473 smbfs_addfree(np); 474 rw_enter(&mi->smi_hash_lk, RW_READER); 475 *newnode = 0; 476 return (tnp); 477 } 478 479 /* 480 * Hash search identifies nodes by the remote path 481 * (n_rpath) so fill that in now, before linking 482 * this node into the node cache (AVL tree). 483 */ 484 np->n_rpath = new_rpath; 485 np->n_rplen = rplen; 486 np->n_ino = smbfs_gethash(new_rpath, rplen); 487 488 sn_addhash_locked(np, where); 489 *newnode = 1; 490 return (np); 491 } 492 493 /* 494 * smbfs_addfree 495 * Put an smbnode on the free list, or destroy it immediately 496 * if it offers no value were it to be reclaimed later. Also 497 * destroy immediately when we have too many smbnodes, etc. 498 * 499 * Normally called by smbfs_inactive, but also 500 * called in here during cleanup operations. 501 * 502 * NFS: nfs_subr.c:rp_addfree 503 */ 504 void 505 smbfs_addfree(smbnode_t *np) 506 { 507 vnode_t *vp; 508 struct vfs *vfsp; 509 smbmntinfo_t *mi; 510 511 ASSERT(np->r_freef == NULL && np->r_freeb == NULL); 512 513 vp = SMBTOV(np); 514 ASSERT(vp->v_count >= 1); 515 516 vfsp = vp->v_vfsp; 517 mi = VFTOSMI(vfsp); 518 519 /* 520 * If there are no more references to this smbnode and: 521 * we have too many smbnodes allocated, or if the node 522 * is no longer accessible via the AVL tree (!RHASHED), 523 * or an i/o error occurred while writing to the file, 524 * or it's part of an unmounted FS, then try to destroy 525 * it instead of putting it on the smbnode freelist. 526 */ 527 if (np->r_count == 0 && ( 528 (np->r_flags & RHASHED) == 0 || 529 (np->r_error != 0) || 530 (vfsp->vfs_flag & VFS_UNMOUNTED) || 531 (smbnodenew > nsmbnode))) { 532 533 /* Try to destroy this node. */ 534 535 if (np->r_flags & RHASHED) { 536 rw_enter(&mi->smi_hash_lk, RW_WRITER); 537 mutex_enter(&vp->v_lock); 538 if (vp->v_count > 1) { 539 VN_RELE_LOCKED(vp); 540 mutex_exit(&vp->v_lock); 541 rw_exit(&mi->smi_hash_lk); 542 return; 543 /* 544 * Will get another call later, 545 * via smbfs_inactive. 546 */ 547 } 548 mutex_exit(&vp->v_lock); 549 sn_rmhash_locked(np); 550 rw_exit(&mi->smi_hash_lk); 551 } 552 553 sn_inactive(np); 554 555 /* 556 * Recheck the vnode reference count. We need to 557 * make sure that another reference has not been 558 * acquired while we were not holding v_lock. The 559 * smbnode is not in the smbnode "hash" AVL tree, so 560 * the only way for a reference to have been acquired 561 * is for a VOP_PUTPAGE because the smbnode was marked 562 * with RDIRTY or for a modified page. This vnode 563 * reference may have been acquired before our call 564 * to sn_inactive. The i/o may have been completed, 565 * thus allowing sn_inactive to complete, but the 566 * reference to the vnode may not have been released 567 * yet. In any case, the smbnode can not be destroyed 568 * until the other references to this vnode have been 569 * released. The other references will take care of 570 * either destroying the smbnode or placing it on the 571 * smbnode freelist. If there are no other references, 572 * then the smbnode may be safely destroyed. 573 */ 574 mutex_enter(&vp->v_lock); 575 if (vp->v_count > 1) { 576 VN_RELE_LOCKED(vp); 577 mutex_exit(&vp->v_lock); 578 return; 579 } 580 mutex_exit(&vp->v_lock); 581 582 sn_destroy_node(np); 583 return; 584 } 585 586 /* 587 * Lock the AVL tree and then recheck the reference count 588 * to ensure that no other threads have acquired a reference 589 * to indicate that the smbnode should not be placed on the 590 * freelist. If another reference has been acquired, then 591 * just release this one and let the other thread complete 592 * the processing of adding this smbnode to the freelist. 593 */ 594 rw_enter(&mi->smi_hash_lk, RW_WRITER); 595 596 mutex_enter(&vp->v_lock); 597 if (vp->v_count > 1) { 598 VN_RELE_LOCKED(vp); 599 mutex_exit(&vp->v_lock); 600 rw_exit(&mi->smi_hash_lk); 601 return; 602 } 603 mutex_exit(&vp->v_lock); 604 605 /* 606 * Put this node on the free list. 607 */ 608 mutex_enter(&smbfreelist_lock); 609 if (smbfreelist == NULL) { 610 np->r_freef = np; 611 np->r_freeb = np; 612 smbfreelist = np; 613 } else { 614 np->r_freef = smbfreelist; 615 np->r_freeb = smbfreelist->r_freeb; 616 smbfreelist->r_freeb->r_freef = np; 617 smbfreelist->r_freeb = np; 618 } 619 mutex_exit(&smbfreelist_lock); 620 621 rw_exit(&mi->smi_hash_lk); 622 } 623 624 /* 625 * Remove an smbnode from the free list. 626 * 627 * The caller must be holding smbfreelist_lock and the smbnode 628 * must be on the freelist. 629 * 630 * NFS: nfs_subr.c:rp_rmfree 631 */ 632 static void 633 sn_rmfree(smbnode_t *np) 634 { 635 636 ASSERT(MUTEX_HELD(&smbfreelist_lock)); 637 ASSERT(np->r_freef != NULL && np->r_freeb != NULL); 638 639 if (np == smbfreelist) { 640 smbfreelist = np->r_freef; 641 if (np == smbfreelist) 642 smbfreelist = NULL; 643 } 644 645 np->r_freeb->r_freef = np->r_freef; 646 np->r_freef->r_freeb = np->r_freeb; 647 648 np->r_freef = np->r_freeb = NULL; 649 } 650 651 /* 652 * Put an smbnode in the "hash" AVL tree. 653 * 654 * The caller must be hold the rwlock as writer. 655 * 656 * NFS: nfs_subr.c:rp_addhash 657 */ 658 static void 659 sn_addhash_locked(smbnode_t *np, avl_index_t where) 660 { 661 smbmntinfo_t *mi = np->n_mount; 662 663 ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk)); 664 665 mutex_enter(&np->r_statelock); 666 if ((np->r_flags & RHASHED) == 0) { 667 avl_insert(&mi->smi_hash_avl, np, where); 668 np->r_flags |= RHASHED; 669 } 670 mutex_exit(&np->r_statelock); 671 } 672 673 /* 674 * Remove an smbnode from the "hash" AVL tree. 675 * 676 * The caller must hold the rwlock as writer. 677 * 678 * NFS: nfs_subr.c:rp_rmhash_locked 679 */ 680 static void 681 sn_rmhash_locked(smbnode_t *np) 682 { 683 smbmntinfo_t *mi = np->n_mount; 684 685 ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk)); 686 687 mutex_enter(&np->r_statelock); 688 if ((np->r_flags & RHASHED) != 0) { 689 np->r_flags &= ~RHASHED; 690 avl_remove(&mi->smi_hash_avl, np); 691 } 692 mutex_exit(&np->r_statelock); 693 } 694 695 /* 696 * Remove an smbnode from the "hash" AVL tree. 697 * 698 * The caller must not be holding the rwlock. 699 */ 700 void 701 smbfs_rmhash(smbnode_t *np) 702 { 703 smbmntinfo_t *mi = np->n_mount; 704 705 rw_enter(&mi->smi_hash_lk, RW_WRITER); 706 sn_rmhash_locked(np); 707 rw_exit(&mi->smi_hash_lk); 708 } 709 710 /* 711 * Lookup an smbnode by remote pathname 712 * 713 * The caller must be holding the AVL rwlock, either shared or exclusive. 714 * 715 * NFS: nfs_subr.c:rfind 716 */ 717 static smbnode_t * 718 sn_hashfind( 719 smbmntinfo_t *mi, 720 const char *rpath, 721 int rplen, 722 avl_index_t *pwhere) /* optional */ 723 { 724 smbfs_node_hdr_t nhdr; 725 smbnode_t *np; 726 vnode_t *vp; 727 728 ASSERT(RW_LOCK_HELD(&mi->smi_hash_lk)); 729 730 bzero(&nhdr, sizeof (nhdr)); 731 nhdr.hdr_n_rpath = (char *)rpath; 732 nhdr.hdr_n_rplen = rplen; 733 734 /* See smbfs_node_cmp below. */ 735 np = avl_find(&mi->smi_hash_avl, &nhdr, pwhere); 736 737 if (np == NULL) 738 return (NULL); 739 740 /* 741 * Found it in the "hash" AVL tree. 742 * Remove from free list, if necessary. 743 */ 744 vp = SMBTOV(np); 745 if (np->r_freef != NULL) { 746 mutex_enter(&smbfreelist_lock); 747 /* 748 * If the smbnode is on the freelist, 749 * then remove it and use that reference 750 * as the new reference. Otherwise, 751 * need to increment the reference count. 752 */ 753 if (np->r_freef != NULL) { 754 sn_rmfree(np); 755 mutex_exit(&smbfreelist_lock); 756 } else { 757 mutex_exit(&smbfreelist_lock); 758 VN_HOLD(vp); 759 } 760 } else 761 VN_HOLD(vp); 762 763 return (np); 764 } 765 766 static int 767 smbfs_node_cmp(const void *va, const void *vb) 768 { 769 const smbfs_node_hdr_t *a = va; 770 const smbfs_node_hdr_t *b = vb; 771 int clen, diff; 772 773 /* 774 * Same semantics as strcmp, but does not 775 * assume the strings are null terminated. 776 */ 777 clen = (a->hdr_n_rplen < b->hdr_n_rplen) ? 778 a->hdr_n_rplen : b->hdr_n_rplen; 779 diff = strncmp(a->hdr_n_rpath, b->hdr_n_rpath, clen); 780 if (diff < 0) 781 return (-1); 782 if (diff > 0) 783 return (1); 784 /* they match through clen */ 785 if (b->hdr_n_rplen > clen) 786 return (-1); 787 if (a->hdr_n_rplen > clen) 788 return (1); 789 return (0); 790 } 791 792 /* 793 * Setup the "hash" AVL tree used for our node cache. 794 * See: smbfs_mount, smbfs_destroy_table. 795 */ 796 void 797 smbfs_init_hash_avl(avl_tree_t *avl) 798 { 799 avl_create(avl, smbfs_node_cmp, sizeof (smbnode_t), 800 offsetof(smbnode_t, r_avl_node)); 801 } 802 803 /* 804 * Invalidate the cached attributes for all nodes "under" the 805 * passed-in node. Note: the passed-in node is NOT affected by 806 * this call. This is used both for files under some directory 807 * after the directory is deleted or renamed, and for extended 808 * attribute files (named streams) under a plain file after that 809 * file is renamed or deleted. 810 * 811 * Do this by walking the AVL tree starting at the passed in node, 812 * and continuing while the visited nodes have a path prefix matching 813 * the entire path of the passed-in node, and a separator just after 814 * that matching path prefix. Watch out for cases where the AVL tree 815 * order may not exactly match the order of an FS walk, i.e. 816 * consider this sequence: 817 * "foo" (directory) 818 * "foo bar" (name containing a space) 819 * "foo/bar" 820 * The walk needs to skip "foo bar" and keep going until it finds 821 * something that doesn't match the "foo" name prefix. 822 */ 823 void 824 smbfs_attrcache_prune(smbnode_t *top_np) 825 { 826 smbmntinfo_t *mi; 827 smbnode_t *np; 828 char *rpath; 829 int rplen; 830 831 mi = top_np->n_mount; 832 rw_enter(&mi->smi_hash_lk, RW_READER); 833 834 np = top_np; 835 rpath = top_np->n_rpath; 836 rplen = top_np->n_rplen; 837 for (;;) { 838 np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER); 839 if (np == NULL) 840 break; 841 if (np->n_rplen < rplen) 842 break; 843 if (0 != strncmp(np->n_rpath, rpath, rplen)) 844 break; 845 if (np->n_rplen > rplen && ( 846 np->n_rpath[rplen] == ':' || 847 np->n_rpath[rplen] == '\\')) 848 smbfs_attrcache_remove(np); 849 } 850 851 rw_exit(&mi->smi_hash_lk); 852 } 853 854 #ifdef SMB_VNODE_DEBUG 855 int smbfs_check_table_debug = 1; 856 #else /* SMB_VNODE_DEBUG */ 857 int smbfs_check_table_debug = 0; 858 #endif /* SMB_VNODE_DEBUG */ 859 860 861 /* 862 * Return 1 if there is a active vnode belonging to this vfs in the 863 * smbnode cache. 864 * 865 * Several of these checks are done without holding the usual 866 * locks. This is safe because destroy_smbtable(), smbfs_addfree(), 867 * etc. will redo the necessary checks before actually destroying 868 * any smbnodes. 869 * 870 * NFS: nfs_subr.c:check_rtable 871 * 872 * Debugging changes here relative to NFS. 873 * Relatively harmless, so left 'em in. 874 */ 875 int 876 smbfs_check_table(struct vfs *vfsp, smbnode_t *rtnp) 877 { 878 smbmntinfo_t *mi; 879 smbnode_t *np; 880 vnode_t *vp; 881 int busycnt = 0; 882 883 mi = VFTOSMI(vfsp); 884 rw_enter(&mi->smi_hash_lk, RW_READER); 885 for (np = avl_first(&mi->smi_hash_avl); np != NULL; 886 np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER)) { 887 888 if (np == rtnp) 889 continue; /* skip the root */ 890 vp = SMBTOV(np); 891 892 /* Now the 'busy' checks: */ 893 /* Not on the free list? */ 894 if (np->r_freef == NULL) { 895 SMBVDEBUG("!r_freef: node=0x%p, rpath=%s\n", 896 (void *)np, np->n_rpath); 897 busycnt++; 898 } 899 900 /* Has dirty pages? */ 901 if (vn_has_cached_data(vp) && 902 (np->r_flags & RDIRTY)) { 903 SMBVDEBUG("is dirty: node=0x%p, rpath=%s\n", 904 (void *)np, np->n_rpath); 905 busycnt++; 906 } 907 908 /* Other refs? (not reflected in v_count) */ 909 if (np->r_count > 0) { 910 SMBVDEBUG("+r_count: node=0x%p, rpath=%s\n", 911 (void *)np, np->n_rpath); 912 busycnt++; 913 } 914 915 if (busycnt && !smbfs_check_table_debug) 916 break; 917 918 } 919 rw_exit(&mi->smi_hash_lk); 920 921 return (busycnt); 922 } 923 924 /* 925 * Destroy inactive vnodes from the AVL tree which belong to this 926 * vfs. It is essential that we destroy all inactive vnodes during a 927 * forced unmount as well as during a normal unmount. 928 * 929 * NFS: nfs_subr.c:destroy_rtable 930 * 931 * In here, we're normally destrying all or most of the AVL tree, 932 * so the natural choice is to use avl_destroy_nodes. However, 933 * there may be a few busy nodes that should remain in the AVL 934 * tree when we're done. The solution: use a temporary tree to 935 * hold the busy nodes until we're done destroying the old tree, 936 * then copy the temporary tree over the (now emtpy) real tree. 937 */ 938 void 939 smbfs_destroy_table(struct vfs *vfsp) 940 { 941 avl_tree_t tmp_avl; 942 smbmntinfo_t *mi; 943 smbnode_t *np; 944 smbnode_t *rlist; 945 void *v; 946 947 mi = VFTOSMI(vfsp); 948 rlist = NULL; 949 smbfs_init_hash_avl(&tmp_avl); 950 951 rw_enter(&mi->smi_hash_lk, RW_WRITER); 952 v = NULL; 953 while ((np = avl_destroy_nodes(&mi->smi_hash_avl, &v)) != NULL) { 954 955 mutex_enter(&smbfreelist_lock); 956 if (np->r_freef == NULL) { 957 /* 958 * Busy node (not on the free list). 959 * Will keep in the final AVL tree. 960 */ 961 mutex_exit(&smbfreelist_lock); 962 avl_add(&tmp_avl, np); 963 } else { 964 /* 965 * It's on the free list. Remove and 966 * arrange for it to be destroyed. 967 */ 968 sn_rmfree(np); 969 mutex_exit(&smbfreelist_lock); 970 971 /* 972 * Last part of sn_rmhash_locked(). 973 * NB: avl_destroy_nodes has already 974 * removed this from the "hash" AVL. 975 */ 976 mutex_enter(&np->r_statelock); 977 np->r_flags &= ~RHASHED; 978 mutex_exit(&np->r_statelock); 979 980 /* 981 * Add to the list of nodes to destroy. 982 * Borrowing avl_child[0] for this list. 983 */ 984 np->r_avl_node.avl_child[0] = 985 (struct avl_node *)rlist; 986 rlist = np; 987 } 988 } 989 avl_destroy(&mi->smi_hash_avl); 990 991 /* 992 * Replace the (now destroyed) "hash" AVL with the 993 * temporary AVL, which restores the busy nodes. 994 */ 995 mi->smi_hash_avl = tmp_avl; 996 rw_exit(&mi->smi_hash_lk); 997 998 /* 999 * Now destroy the nodes on our temporary list (rlist). 1000 * This call to smbfs_addfree will end up destroying the 1001 * smbnode, but in a safe way with the appropriate set 1002 * of checks done. 1003 */ 1004 while ((np = rlist) != NULL) { 1005 rlist = (smbnode_t *)np->r_avl_node.avl_child[0]; 1006 smbfs_addfree(np); 1007 } 1008 } 1009 1010 /* 1011 * This routine destroys all the resources associated with the smbnode 1012 * and then the smbnode itself. Note: sn_inactive has been called. 1013 * 1014 * NFS: nfs_subr.c:destroy_rnode 1015 */ 1016 static void 1017 sn_destroy_node(smbnode_t *np) 1018 { 1019 vnode_t *vp; 1020 vfs_t *vfsp; 1021 1022 vp = SMBTOV(np); 1023 vfsp = vp->v_vfsp; 1024 1025 ASSERT(vp->v_count == 1); 1026 ASSERT(np->r_count == 0); 1027 ASSERT(np->r_mapcnt == 0); 1028 ASSERT(np->r_secattr.vsa_aclentp == NULL); 1029 ASSERT(np->r_cred == NULL); 1030 ASSERT(np->n_rpath == NULL); 1031 ASSERT(!(np->r_flags & RHASHED)); 1032 ASSERT(np->r_freef == NULL && np->r_freeb == NULL); 1033 atomic_dec_ulong((ulong_t *)&smbnodenew); 1034 vn_invalid(vp); 1035 vn_free(vp); 1036 kmem_cache_free(smbnode_cache, np); 1037 VFS_RELE(vfsp); 1038 } 1039 1040 /* 1041 * Flush all vnodes in this (or every) vfs. 1042 * Used by nfs_sync and by nfs_unmount. 1043 */ 1044 /*ARGSUSED*/ 1045 void 1046 smbfs_rflush(struct vfs *vfsp, cred_t *cr) 1047 { 1048 /* Todo: mmap support. */ 1049 } 1050 1051 /* access cache (nfs_subr.c) not used here */ 1052 1053 static kmutex_t smbfs_newnum_lock; 1054 static uint32_t smbfs_newnum_val = 0; 1055 1056 /* 1057 * Return a number 0..0xffffffff that's different from the last 1058 * 0xffffffff numbers this returned. Used for unlinked files. 1059 * (This too was copied from nfs_subr.c) 1060 */ 1061 uint32_t 1062 smbfs_newnum(void) 1063 { 1064 uint32_t id; 1065 1066 mutex_enter(&smbfs_newnum_lock); 1067 if (smbfs_newnum_val == 0) 1068 smbfs_newnum_val = (uint32_t)gethrestime_sec(); 1069 id = smbfs_newnum_val++; 1070 mutex_exit(&smbfs_newnum_lock); 1071 return (id); 1072 } 1073 1074 /* 1075 * Fill in a temporary name at buf 1076 */ 1077 int 1078 smbfs_newname(char *buf, size_t buflen) 1079 { 1080 uint_t id; 1081 int n; 1082 1083 id = smbfs_newnum(); 1084 n = snprintf(buf, buflen, "~$smbfs%08X", id); 1085 return (n); 1086 } 1087 1088 1089 /* 1090 * initialize resources that are used by smbfs_subr.c 1091 * this is called from the _init() routine (by the way of smbfs_clntinit()) 1092 * 1093 * NFS: nfs_subr.c:nfs_subrinit 1094 */ 1095 int 1096 smbfs_subrinit(void) 1097 { 1098 ulong_t nsmbnode_max; 1099 1100 /* 1101 * Allocate and initialize the smbnode cache 1102 */ 1103 if (nsmbnode <= 0) 1104 nsmbnode = ncsize; /* dnlc.h */ 1105 nsmbnode_max = (ulong_t)((kmem_maxavail() >> 2) / 1106 sizeof (struct smbnode)); 1107 if (nsmbnode > nsmbnode_max || (nsmbnode == 0 && ncsize == 0)) { 1108 zcmn_err(GLOBAL_ZONEID, CE_NOTE, 1109 "setting nsmbnode to max value of %ld", nsmbnode_max); 1110 nsmbnode = nsmbnode_max; 1111 } 1112 1113 smbnode_cache = kmem_cache_create("smbnode_cache", sizeof (smbnode_t), 1114 0, NULL, NULL, smbfs_kmem_reclaim, NULL, NULL, 0); 1115 1116 /* 1117 * Initialize the various mutexes and reader/writer locks 1118 */ 1119 mutex_init(&smbfreelist_lock, NULL, MUTEX_DEFAULT, NULL); 1120 mutex_init(&smbfs_minor_lock, NULL, MUTEX_DEFAULT, NULL); 1121 1122 /* 1123 * Assign unique major number for all smbfs mounts 1124 */ 1125 if ((smbfs_major = getudev()) == -1) { 1126 zcmn_err(GLOBAL_ZONEID, CE_WARN, 1127 "smbfs: init: can't get unique device number"); 1128 smbfs_major = 0; 1129 } 1130 smbfs_minor = 0; 1131 1132 return (0); 1133 } 1134 1135 /* 1136 * free smbfs hash table, etc. 1137 * NFS: nfs_subr.c:nfs_subrfini 1138 */ 1139 void 1140 smbfs_subrfini(void) 1141 { 1142 1143 /* 1144 * Destroy the smbnode cache 1145 */ 1146 kmem_cache_destroy(smbnode_cache); 1147 1148 /* 1149 * Destroy the various mutexes and reader/writer locks 1150 */ 1151 mutex_destroy(&smbfreelist_lock); 1152 mutex_destroy(&smbfs_minor_lock); 1153 } 1154 1155 /* rddir_cache ? */ 1156 1157 /* 1158 * Support functions for smbfs_kmem_reclaim 1159 */ 1160 1161 static void 1162 smbfs_node_reclaim(void) 1163 { 1164 smbmntinfo_t *mi; 1165 smbnode_t *np; 1166 vnode_t *vp; 1167 1168 mutex_enter(&smbfreelist_lock); 1169 while ((np = smbfreelist) != NULL) { 1170 sn_rmfree(np); 1171 mutex_exit(&smbfreelist_lock); 1172 if (np->r_flags & RHASHED) { 1173 vp = SMBTOV(np); 1174 mi = np->n_mount; 1175 rw_enter(&mi->smi_hash_lk, RW_WRITER); 1176 mutex_enter(&vp->v_lock); 1177 if (vp->v_count > 1) { 1178 VN_RELE_LOCKED(vp); 1179 mutex_exit(&vp->v_lock); 1180 rw_exit(&mi->smi_hash_lk); 1181 mutex_enter(&smbfreelist_lock); 1182 continue; 1183 } 1184 mutex_exit(&vp->v_lock); 1185 sn_rmhash_locked(np); 1186 rw_exit(&mi->smi_hash_lk); 1187 } 1188 /* 1189 * This call to smbfs_addfree will end up destroying the 1190 * smbnode, but in a safe way with the appropriate set 1191 * of checks done. 1192 */ 1193 smbfs_addfree(np); 1194 mutex_enter(&smbfreelist_lock); 1195 } 1196 mutex_exit(&smbfreelist_lock); 1197 } 1198 1199 /* 1200 * Called by kmem_cache_alloc ask us if we could 1201 * "Please give back some memory!" 1202 * 1203 * Todo: dump nodes from the free list? 1204 */ 1205 /*ARGSUSED*/ 1206 void 1207 smbfs_kmem_reclaim(void *cdrarg) 1208 { 1209 smbfs_node_reclaim(); 1210 } 1211 1212 /* nfs failover stuff */ 1213 /* nfs_rw_xxx - see smbfs_rwlock.c */