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