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