xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c (revision ea4ea50f064c5468142b24627acad09a41f060cb)
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 #include <sys/fcntl.h>
48 
49 #include <netsmb/smb_osdep.h>
50 
51 #include <netsmb/smb.h>
52 #include <netsmb/smb_conn.h>
53 #include <netsmb/smb_subr.h>
54 #include <netsmb/smb_rq.h>
55 
56 #include <smbfs/smbfs.h>
57 #include <smbfs/smbfs_node.h>
58 #include <smbfs/smbfs_subr.h>
59 
60 /*
61  * The AVL trees (now per-mount) allow finding an smbfs node by its
62  * full remote path name.  It also allows easy traversal of all nodes
63  * below (path wise) any given node.  A reader/writer lock for each
64  * (per mount) AVL tree is used to control access and to synchronize
65  * lookups, additions, and deletions from that AVL tree.
66  *
67  * Previously, this code use a global array of hash chains, each with
68  * its own rwlock.  A few struct members, functions, and comments may
69  * still refer to a "hash", and those should all now be considered to
70  * refer to the per-mount AVL tree that replaced the old hash chains.
71  * (i.e. member smi_hash_lk, function sn_hashfind, etc.)
72  *
73  * The smbnode freelist is organized as a doubly linked list with
74  * a head pointer.  Additions and deletions are synchronized via
75  * a single mutex.
76  *
77  * In order to add an smbnode to the free list, it must be linked into
78  * the mount's AVL tree and the exclusive lock for the AVL must be held.
79  * If an smbnode is not linked into the AVL tree, then it is destroyed
80  * because it represents no valuable information that can be reused
81  * about the file.  The exclusive lock for the AVL tree must be held
82  * in order to prevent a lookup in the AVL tree from finding the
83  * smbnode and using it and assuming that the smbnode is not on the
84  * freelist.  The lookup in the AVL tree will have the AVL tree lock
85  * held, either exclusive or shared.
86  *
87  * The vnode reference count for each smbnode is not allowed to drop
88  * below 1.  This prevents external entities, such as the VM
89  * subsystem, from acquiring references to vnodes already on the
90  * freelist and then trying to place them back on the freelist
91  * when their reference is released.  This means that the when an
92  * smbnode is looked up in the AVL tree, then either the smbnode
93  * is removed from the freelist and that reference is tranfered to
94  * the new reference or the vnode reference count must be incremented
95  * accordingly.  The mutex for the freelist must be held in order to
96  * accurately test to see if the smbnode is on the freelist or not.
97  * The AVL tree lock might be held shared and it is possible that
98  * two different threads may race to remove the smbnode from the
99  * freelist.  This race can be resolved by holding the mutex for the
100  * freelist.  Please note that the mutex for the freelist does not
101  * need to held if the smbnode is not on the freelist.  It can not be
102  * placed on the freelist due to the requirement that the thread
103  * putting the smbnode on the freelist must hold the exclusive lock
104  * for the AVL tree and the thread doing the lookup in the AVL tree
105  * is holding either a shared or exclusive lock for the AVL tree.
106  *
107  * The lock ordering is:
108  *
109  *	AVL tree lock -> vnode lock
110  *	AVL tree lock -> freelist lock
111  */
112 
113 static kmutex_t smbfreelist_lock;
114 static smbnode_t *smbfreelist = NULL;
115 static ulong_t	smbnodenew = 0;
116 long	nsmbnode = 0;
117 
118 static struct kmem_cache *smbnode_cache;
119 
120 static const vsecattr_t smbfs_vsa0 = { 0 };
121 
122 /*
123  * Mutex to protect the following variables:
124  *	smbfs_major
125  *	smbfs_minor
126  */
127 kmutex_t smbfs_minor_lock;
128 int smbfs_major;
129 int smbfs_minor;
130 
131 /* See smbfs_node_findcreate() */
132 struct smbfattr smbfs_fattr0;
133 
134 /*
135  * Local functions.
136  * SN for Smb Node
137  */
138 static void sn_rmfree(smbnode_t *);
139 static void sn_inactive(smbnode_t *);
140 static void sn_addhash_locked(smbnode_t *, avl_index_t);
141 static void sn_rmhash_locked(smbnode_t *);
142 static void sn_destroy_node(smbnode_t *);
143 void smbfs_kmem_reclaim(void *cdrarg);
144 
145 static smbnode_t *
146 sn_hashfind(smbmntinfo_t *, const char *, int, avl_index_t *);
147 
148 static smbnode_t *
149 make_smbnode(smbmntinfo_t *, const char *, int, int *);
150 
151 /*
152  * Free the resources associated with an smbnode.
153  * Note: This is different from smbfs_inactive
154  *
155  * From NFS: nfs_subr.c:rinactive
156  */
157 static void
158 sn_inactive(smbnode_t *np)
159 {
160 	vsecattr_t	ovsa;
161 	cred_t		*oldcr;
162 	char		*orpath;
163 	int		orplen;
164 	vnode_t		*vp;
165 
166 	/*
167 	 * Here NFS has:
168 	 * Flush and invalidate all pages (done by caller)
169 	 * Free any held credentials and caches...
170 	 * etc.  (See NFS code)
171 	 */
172 	mutex_enter(&np->r_statelock);
173 
174 	ovsa = np->r_secattr;
175 	np->r_secattr = smbfs_vsa0;
176 	np->r_sectime = 0;
177 
178 	oldcr = np->r_cred;
179 	np->r_cred = NULL;
180 
181 	orpath = np->n_rpath;
182 	orplen = np->n_rplen;
183 	np->n_rpath = NULL;
184 	np->n_rplen = 0;
185 
186 	mutex_exit(&np->r_statelock);
187 
188 	vp = SMBTOV(np);
189 	if (vn_has_cached_data(vp)) {
190 		ASSERT3P(vp,==,NULL);
191 	}
192 
193 	if (ovsa.vsa_aclentp != NULL)
194 		kmem_free(ovsa.vsa_aclentp, ovsa.vsa_aclentsz);
195 
196 	if (oldcr != NULL)
197 		crfree(oldcr);
198 
199 	if (orpath != NULL)
200 		kmem_free(orpath, orplen + 1);
201 }
202 
203 /*
204  * Find and optionally create an smbnode for the passed
205  * mountinfo, directory, separator, and name.  If the
206  * desired smbnode already exists, return a reference.
207  * If the file attributes pointer is non-null, the node
208  * is created if necessary and linked into the AVL tree.
209  *
210  * Callers that need a node created but don't have the
211  * real attributes pass smbfs_fattr0 to force creation.
212  *
213  * Note: make_smbnode() may upgrade the "hash" lock to exclusive.
214  *
215  * Based on NFS: nfs_subr.c:makenfsnode
216  */
217 smbnode_t *
218 smbfs_node_findcreate(
219 	smbmntinfo_t *mi,
220 	const char *dirnm,
221 	int dirlen,
222 	const char *name,
223 	int nmlen,
224 	char sep,
225 	struct smbfattr *fap)
226 {
227 	char tmpbuf[256];
228 	size_t rpalloc;
229 	char *p, *rpath;
230 	int rplen;
231 	smbnode_t *np;
232 	vnode_t *vp;
233 	int newnode;
234 
235 	/*
236 	 * Build the search string, either in tmpbuf or
237 	 * in allocated memory if larger than tmpbuf.
238 	 */
239 	rplen = dirlen;
240 	if (sep != '\0')
241 		rplen++;
242 	rplen += nmlen;
243 	if (rplen < sizeof (tmpbuf)) {
244 		/* use tmpbuf */
245 		rpalloc = 0;
246 		rpath = tmpbuf;
247 	} else {
248 		rpalloc = rplen + 1;
249 		rpath = kmem_alloc(rpalloc, KM_SLEEP);
250 	}
251 	p = rpath;
252 	bcopy(dirnm, p, dirlen);
253 	p += dirlen;
254 	if (sep != '\0')
255 		*p++ = sep;
256 	if (name != NULL) {
257 		bcopy(name, p, nmlen);
258 		p += nmlen;
259 	}
260 	ASSERT(p == rpath + rplen);
261 
262 	/*
263 	 * Find or create a node with this path.
264 	 */
265 	rw_enter(&mi->smi_hash_lk, RW_READER);
266 	if (fap == NULL)
267 		np = sn_hashfind(mi, rpath, rplen, NULL);
268 	else
269 		np = make_smbnode(mi, rpath, rplen, &newnode);
270 	rw_exit(&mi->smi_hash_lk);
271 
272 	if (rpalloc)
273 		kmem_free(rpath, rpalloc);
274 
275 	if (fap == NULL) {
276 		/*
277 		 * Caller is "just looking" (no create)
278 		 * so np may or may not be NULL here.
279 		 * Either way, we're done.
280 		 */
281 		return (np);
282 	}
283 
284 	/*
285 	 * We should have a node, possibly created.
286 	 * Do we have (real) attributes to apply?
287 	 */
288 	ASSERT(np != NULL);
289 	if (fap == &smbfs_fattr0)
290 		return (np);
291 
292 	/*
293 	 * Apply the given attributes to this node,
294 	 * dealing with any cache impact, etc.
295 	 */
296 	vp = SMBTOV(np);
297 	smbfs_attrcache_fa(vp, fap);
298 
299 	/*
300 	 * Note NFS sets vp->v_type here, assuming it
301 	 * can never change for the life of a node.
302 	 * We allow v_type to change, and set it in
303 	 * smbfs_attrcache().  Also: mode, uid, gid
304 	 */
305 	return (np);
306 }
307 
308 /*
309  * Here NFS has: nfs_subr.c:rtablehash
310  * We use smbfs_hash().
311  */
312 
313 /*
314  * Find or create an smbnode.
315  * From NFS: nfs_subr.c:make_rnode
316  */
317 static smbnode_t *
318 make_smbnode(
319 	smbmntinfo_t *mi,
320 	const char *rpath,
321 	int rplen,
322 	int *newnode)
323 {
324 	smbnode_t *np;
325 	smbnode_t *tnp;
326 	vnode_t *vp;
327 	vfs_t *vfsp;
328 	avl_index_t where;
329 	char *new_rpath = NULL;
330 
331 	ASSERT(RW_READ_HELD(&mi->smi_hash_lk));
332 	vfsp = mi->smi_vfsp;
333 
334 start:
335 	np = sn_hashfind(mi, rpath, rplen, NULL);
336 	if (np != NULL) {
337 		*newnode = 0;
338 		return (np);
339 	}
340 
341 	/* Note: will retake this lock below. */
342 	rw_exit(&mi->smi_hash_lk);
343 
344 	/*
345 	 * see if we can find something on the freelist
346 	 */
347 	mutex_enter(&smbfreelist_lock);
348 	if (smbfreelist != NULL && smbnodenew >= nsmbnode) {
349 		np = smbfreelist;
350 		sn_rmfree(np);
351 		mutex_exit(&smbfreelist_lock);
352 
353 		vp = SMBTOV(np);
354 
355 		if (np->r_flags & RHASHED) {
356 			smbmntinfo_t *tmp_mi = np->n_mount;
357 			ASSERT(tmp_mi != NULL);
358 			rw_enter(&tmp_mi->smi_hash_lk, RW_WRITER);
359 			mutex_enter(&vp->v_lock);
360 			if (vp->v_count > 1) {
361 				VN_RELE_LOCKED(vp);
362 				mutex_exit(&vp->v_lock);
363 				rw_exit(&tmp_mi->smi_hash_lk);
364 				/* start over */
365 				rw_enter(&mi->smi_hash_lk, RW_READER);
366 				goto start;
367 			}
368 			mutex_exit(&vp->v_lock);
369 			sn_rmhash_locked(np);
370 			rw_exit(&tmp_mi->smi_hash_lk);
371 		}
372 
373 		sn_inactive(np);
374 
375 		mutex_enter(&vp->v_lock);
376 		if (vp->v_count > 1) {
377 			VN_RELE_LOCKED(vp);
378 			mutex_exit(&vp->v_lock);
379 			rw_enter(&mi->smi_hash_lk, RW_READER);
380 			goto start;
381 		}
382 		mutex_exit(&vp->v_lock);
383 		vn_invalid(vp);
384 		/*
385 		 * destroy old locks before bzero'ing and
386 		 * recreating the locks below.
387 		 */
388 		smbfs_rw_destroy(&np->r_rwlock);
389 		smbfs_rw_destroy(&np->r_lkserlock);
390 		mutex_destroy(&np->r_statelock);
391 		cv_destroy(&np->r_cv);
392 		/*
393 		 * Make sure that if smbnode is recycled then
394 		 * VFS count is decremented properly before
395 		 * reuse.
396 		 */
397 		VFS_RELE(vp->v_vfsp);
398 		vn_reinit(vp);
399 	} else {
400 		/*
401 		 * allocate and initialize a new smbnode
402 		 */
403 		vnode_t *new_vp;
404 
405 		mutex_exit(&smbfreelist_lock);
406 
407 		np = kmem_cache_alloc(smbnode_cache, KM_SLEEP);
408 		new_vp = vn_alloc(KM_SLEEP);
409 
410 		atomic_inc_ulong((ulong_t *)&smbnodenew);
411 		vp = new_vp;
412 	}
413 
414 	/*
415 	 * Allocate and copy the rpath we'll need below.
416 	 */
417 	new_rpath = kmem_alloc(rplen + 1, KM_SLEEP);
418 	bcopy(rpath, new_rpath, rplen);
419 	new_rpath[rplen] = '\0';
420 
421 	/* Initialize smbnode_t */
422 	bzero(np, sizeof (*np));
423 
424 	smbfs_rw_init(&np->r_rwlock, NULL, RW_DEFAULT, NULL);
425 	smbfs_rw_init(&np->r_lkserlock, NULL, RW_DEFAULT, NULL);
426 	mutex_init(&np->r_statelock, NULL, MUTEX_DEFAULT, NULL);
427 	cv_init(&np->r_cv, NULL, CV_DEFAULT, NULL);
428 	/* cv_init(&np->r_commit.c_cv, NULL, CV_DEFAULT, NULL); */
429 
430 	np->r_vnode = vp;
431 	np->n_mount = mi;
432 
433 	np->n_fid = SMB_FID_UNUSED;
434 	np->n_uid = mi->smi_uid;
435 	np->n_gid = mi->smi_gid;
436 	/* Leave attributes "stale." */
437 
438 	/*
439 	 * Here NFS has avl_create(&np->r_dir, ...)
440 	 * for the readdir cache (not used here).
441 	 */
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  * From 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 				VN_RELE_LOCKED(vp);
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 			VN_RELE_LOCKED(vp);
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 		VN_RELE_LOCKED(vp);
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  * From 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  * From 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 
662 	mutex_enter(&np->r_statelock);
663 	if ((np->r_flags & RHASHED) == 0) {
664 		avl_insert(&mi->smi_hash_avl, np, where);
665 		np->r_flags |= RHASHED;
666 	}
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  * From 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 
684 	mutex_enter(&np->r_statelock);
685 	if ((np->r_flags & RHASHED) != 0) {
686 		np->r_flags &= ~RHASHED;
687 		avl_remove(&mi->smi_hash_avl, np);
688 	}
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  * From 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  * From 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  * Based on 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  * From 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_dec_ulong((ulong_t *)&smbnodenew);
1031 	vn_invalid(vp);
1032 	vn_free(vp);
1033 	kmem_cache_free(smbnode_cache, np);
1034 	VFS_RELE(vfsp);
1035 }
1036 
1037 /*
1038  * From NFS rflush()
1039  * Flush all vnodes in this (or every) vfs.
1040  * Used by smbfs_sync and by smbfs_unmount.
1041  */
1042 /*ARGSUSED*/
1043 void
1044 smbfs_rflush(struct vfs *vfsp, cred_t *cr)
1045 {
1046 	smbmntinfo_t *mi;
1047 	smbnode_t *np;
1048 	vnode_t *vp, **vplist;
1049 	long num, cnt;
1050 
1051 	mi = VFTOSMI(vfsp);
1052 
1053 	/*
1054 	 * Check to see whether there is anything to do.
1055 	 */
1056 	num = avl_numnodes(&mi->smi_hash_avl);
1057 	if (num == 0)
1058 		return;
1059 
1060 	/*
1061 	 * Allocate a slot for all currently active rnodes on the
1062 	 * supposition that they all may need flushing.
1063 	 */
1064 	vplist = kmem_alloc(num * sizeof (*vplist), KM_SLEEP);
1065 	cnt = 0;
1066 
1067 	/*
1068 	 * Walk the AVL tree looking for rnodes with page
1069 	 * lists associated with them.  Make a list of these
1070 	 * files.
1071 	 */
1072 	rw_enter(&mi->smi_hash_lk, RW_READER);
1073 	for (np = avl_first(&mi->smi_hash_avl); np != NULL;
1074 	    np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER)) {
1075 		vp = SMBTOV(np);
1076 		/*
1077 		 * Don't bother sync'ing a vp if it
1078 		 * is part of virtual swap device or
1079 		 * if VFS is read-only
1080 		 */
1081 		if (IS_SWAPVP(vp) || vn_is_readonly(vp))
1082 			continue;
1083 		/*
1084 		 * If the vnode has pages and is marked as either
1085 		 * dirty or mmap'd, hold and add this vnode to the
1086 		 * list of vnodes to flush.
1087 		 */
1088 		if (vn_has_cached_data(vp) &&
1089 		    ((np->r_flags & RDIRTY) || np->r_mapcnt > 0)) {
1090 			VN_HOLD(vp);
1091 			vplist[cnt++] = vp;
1092 			if (cnt == num)
1093 				break;
1094 		}
1095 	}
1096 	rw_exit(&mi->smi_hash_lk);
1097 
1098 	/*
1099 	 * Flush and release all of the files on the list.
1100 	 */
1101 	while (cnt-- > 0) {
1102 		vp = vplist[cnt];
1103 		(void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_ASYNC, cr, NULL);
1104 		VN_RELE(vp);
1105 	}
1106 
1107 	kmem_free(vplist, num * sizeof (vnode_t *));
1108 }
1109 
1110 /* Here NFS has access cache stuff (nfs_subr.c) not used here */
1111 
1112 /*
1113  * Set or Clear direct I/O flag
1114  * VOP_RWLOCK() is held for write access to prevent a race condition
1115  * which would occur if a process is in the middle of a write when
1116  * directio flag gets set. It is possible that all pages may not get flushed.
1117  * From nfs_common.c
1118  */
1119 
1120 /* ARGSUSED */
1121 int
1122 smbfs_directio(vnode_t *vp, int cmd, cred_t *cr)
1123 {
1124 	int	error = 0;
1125 	smbnode_t	*np;
1126 
1127 	np = VTOSMB(vp);
1128 
1129 	if (cmd == DIRECTIO_ON) {
1130 
1131 		if (np->r_flags & RDIRECTIO)
1132 			return (0);
1133 
1134 		/*
1135 		 * Flush the page cache.
1136 		 */
1137 
1138 		(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
1139 
1140 		if (np->r_flags & RDIRECTIO) {
1141 			VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
1142 			return (0);
1143 		}
1144 
1145 		/* Here NFS also checks ->r_awcount */
1146 		if (vn_has_cached_data(vp) &&
1147 		    (np->r_flags & RDIRTY) != 0) {
1148 			error = VOP_PUTPAGE(vp, (offset_t)0, (uint_t)0,
1149 			    B_INVAL, cr, NULL);
1150 			if (error) {
1151 				if (error == ENOSPC || error == EDQUOT) {
1152 					mutex_enter(&np->r_statelock);
1153 					if (!np->r_error)
1154 						np->r_error = error;
1155 					mutex_exit(&np->r_statelock);
1156 				}
1157 				VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
1158 				return (error);
1159 			}
1160 		}
1161 
1162 		mutex_enter(&np->r_statelock);
1163 		np->r_flags |= RDIRECTIO;
1164 		mutex_exit(&np->r_statelock);
1165 		VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
1166 		return (0);
1167 	}
1168 
1169 	if (cmd == DIRECTIO_OFF) {
1170 		mutex_enter(&np->r_statelock);
1171 		np->r_flags &= ~RDIRECTIO;	/* disable direct mode */
1172 		mutex_exit(&np->r_statelock);
1173 		return (0);
1174 	}
1175 
1176 	return (EINVAL);
1177 }
1178 
1179 static kmutex_t smbfs_newnum_lock;
1180 static uint32_t smbfs_newnum_val = 0;
1181 
1182 /*
1183  * Return a number 0..0xffffffff that's different from the last
1184  * 0xffffffff numbers this returned.  Used for unlinked files.
1185  * From NFS nfs_subr.c newnum
1186  */
1187 uint32_t
1188 smbfs_newnum(void)
1189 {
1190 	uint32_t id;
1191 
1192 	mutex_enter(&smbfs_newnum_lock);
1193 	if (smbfs_newnum_val == 0)
1194 		smbfs_newnum_val = (uint32_t)gethrestime_sec();
1195 	id = smbfs_newnum_val++;
1196 	mutex_exit(&smbfs_newnum_lock);
1197 	return (id);
1198 }
1199 
1200 /*
1201  * Fill in a temporary name at buf
1202  */
1203 int
1204 smbfs_newname(char *buf, size_t buflen)
1205 {
1206 	uint_t id;
1207 	int n;
1208 
1209 	id = smbfs_newnum();
1210 	n = snprintf(buf, buflen, "~$smbfs%08X", id);
1211 	return (n);
1212 }
1213 
1214 
1215 /*
1216  * initialize resources that are used by smbfs_subr.c
1217  * this is called from the _init() routine (by the way of smbfs_clntinit())
1218  *
1219  * From NFS: nfs_subr.c:nfs_subrinit
1220  */
1221 int
1222 smbfs_subrinit(void)
1223 {
1224 	ulong_t nsmbnode_max;
1225 
1226 	/*
1227 	 * Allocate and initialize the smbnode cache
1228 	 */
1229 	if (nsmbnode <= 0)
1230 		nsmbnode = ncsize; /* dnlc.h */
1231 	nsmbnode_max = (ulong_t)((kmem_maxavail() >> 2) /
1232 	    sizeof (struct smbnode));
1233 	if (nsmbnode > nsmbnode_max || (nsmbnode == 0 && ncsize == 0)) {
1234 		zcmn_err(GLOBAL_ZONEID, CE_NOTE,
1235 		    "setting nsmbnode to max value of %ld", nsmbnode_max);
1236 		nsmbnode = nsmbnode_max;
1237 	}
1238 
1239 	smbnode_cache = kmem_cache_create("smbnode_cache", sizeof (smbnode_t),
1240 	    0, NULL, NULL, smbfs_kmem_reclaim, NULL, NULL, 0);
1241 
1242 	/*
1243 	 * Initialize the various mutexes and reader/writer locks
1244 	 */
1245 	mutex_init(&smbfreelist_lock, NULL, MUTEX_DEFAULT, NULL);
1246 	mutex_init(&smbfs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
1247 
1248 	/*
1249 	 * Assign unique major number for all smbfs mounts
1250 	 */
1251 	if ((smbfs_major = getudev()) == -1) {
1252 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
1253 		    "smbfs: init: can't get unique device number");
1254 		smbfs_major = 0;
1255 	}
1256 	smbfs_minor = 0;
1257 
1258 	return (0);
1259 }
1260 
1261 /*
1262  * free smbfs hash table, etc.
1263  * From NFS: nfs_subr.c:nfs_subrfini
1264  */
1265 void
1266 smbfs_subrfini(void)
1267 {
1268 
1269 	/*
1270 	 * Destroy the smbnode cache
1271 	 */
1272 	kmem_cache_destroy(smbnode_cache);
1273 
1274 	/*
1275 	 * Destroy the various mutexes and reader/writer locks
1276 	 */
1277 	mutex_destroy(&smbfreelist_lock);
1278 	mutex_destroy(&smbfs_minor_lock);
1279 }
1280 
1281 /* rddir_cache ? */
1282 
1283 /*
1284  * Support functions for smbfs_kmem_reclaim
1285  */
1286 
1287 static void
1288 smbfs_node_reclaim(void)
1289 {
1290 	smbmntinfo_t *mi;
1291 	smbnode_t *np;
1292 	vnode_t *vp;
1293 
1294 	mutex_enter(&smbfreelist_lock);
1295 	while ((np = smbfreelist) != NULL) {
1296 		sn_rmfree(np);
1297 		mutex_exit(&smbfreelist_lock);
1298 		if (np->r_flags & RHASHED) {
1299 			vp = SMBTOV(np);
1300 			mi = np->n_mount;
1301 			rw_enter(&mi->smi_hash_lk, RW_WRITER);
1302 			mutex_enter(&vp->v_lock);
1303 			if (vp->v_count > 1) {
1304 				VN_RELE_LOCKED(vp);
1305 				mutex_exit(&vp->v_lock);
1306 				rw_exit(&mi->smi_hash_lk);
1307 				mutex_enter(&smbfreelist_lock);
1308 				continue;
1309 			}
1310 			mutex_exit(&vp->v_lock);
1311 			sn_rmhash_locked(np);
1312 			rw_exit(&mi->smi_hash_lk);
1313 		}
1314 		/*
1315 		 * This call to smbfs_addfree will end up destroying the
1316 		 * smbnode, but in a safe way with the appropriate set
1317 		 * of checks done.
1318 		 */
1319 		smbfs_addfree(np);
1320 		mutex_enter(&smbfreelist_lock);
1321 	}
1322 	mutex_exit(&smbfreelist_lock);
1323 }
1324 
1325 /*
1326  * Called by kmem_cache_alloc ask us if we could
1327  * "Please give back some memory!"
1328  *
1329  * Todo: dump nodes from the free list?
1330  */
1331 /*ARGSUSED*/
1332 void
1333 smbfs_kmem_reclaim(void *cdrarg)
1334 {
1335 	smbfs_node_reclaim();
1336 }
1337 
1338 /*
1339  * Here NFS has failover stuff and
1340  * nfs_rw_xxx - see smbfs_rwlock.c
1341  */
1342