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