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