xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c (revision bfed486ad8de8b8ebc6345a8e10accae08bf2f45)
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 borrowed from NFS.
31  * See: uts/common/fs/nfs/nfs_subr.c
32  */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/time.h>
37 #include <sys/vnode.h>
38 #include <sys/bitmap.h>
39 #include <sys/dnlc.h>
40 #include <sys/kmem.h>
41 #include <sys/sunddi.h>
42 
43 #ifdef APPLE
44 #include <sys/smb_apple.h>
45 #include <sys/utfconv.h>
46 #include <sys/smb_iconv.h>
47 #else
48 #include <netsmb/smb_osdep.h>
49 #endif
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 hash queues for the access to active and cached smbnodes
62  * are organized as doubly linked lists.  A reader/writer lock
63  * for each hash bucket is used to control access and to synchronize
64  * lookups, additions, and deletions from the hash queue.
65  *
66  * The smbnode freelist is organized as a doubly linked list with
67  * a head pointer.  Additions and deletions are synchronized via
68  * a single mutex.
69  *
70  * In order to add an smbnode to the free list, it must be hashed into
71  * a hash queue and the exclusive lock to the hash queue be held.
72  * If an smbnode is not hashed into a hash queue, then it is destroyed
73  * because it represents no valuable information that can be reused
74  * about the file.  The exclusive lock to the hash queue must be
75  * held in order to prevent a lookup in the hash queue from finding
76  * the smbnode and using it and assuming that the smbnode is not on the
77  * freelist.  The lookup in the hash queue will have the hash queue
78  * locked, either exclusive or shared.
79  *
80  * The vnode reference count for each smbnode is not allowed to drop
81  * below 1.  This prevents external entities, such as the VM
82  * subsystem, from acquiring references to vnodes already on the
83  * freelist and then trying to place them back on the freelist
84  * when their reference is released.  This means that the when an
85  * smbnode is looked up in the hash queues, then either the smbnode
86  * is removed from the freelist and that reference is tranfered to
87  * the new reference or the vnode reference count must be incremented
88  * accordingly.  The mutex for the freelist must be held in order to
89  * accurately test to see if the smbnode is on the freelist or not.
90  * The hash queue lock might be held shared and it is possible that
91  * two different threads may race to remove the smbnode from the
92  * freelist.  This race can be resolved by holding the mutex for the
93  * freelist.  Please note that the mutex for the freelist does not
94  * need to held if the smbnode is not on the freelist.  It can not be
95  * placed on the freelist due to the requirement that the thread
96  * putting the smbnode on the freelist must hold the exclusive lock
97  * to the hash queue and the thread doing the lookup in the hash
98  * queue is holding either a shared or exclusive lock to the hash
99  * queue.
100  *
101  * The lock ordering is:
102  *
103  *	hash bucket lock -> vnode lock
104  *	hash bucket lock -> freelist lock
105  */
106 static rhashq_t *smbtable;
107 
108 static kmutex_t smbfreelist_lock;
109 static smbnode_t *smbfreelist = NULL;
110 static ulong_t	smbnodenew = 0;
111 long	nsmbnode = 0;
112 
113 static int smbtablesize;
114 static int smbtablemask;
115 static int smbhashlen = 4;
116 
117 static struct kmem_cache *smbnode_cache;
118 
119 /*
120  * Mutex to protect the following variables:
121  *	smbfs_major
122  *	smbfs_minor
123  */
124 kmutex_t smbfs_minor_lock;
125 int smbfs_major;
126 int smbfs_minor;
127 
128 /*
129  * Local functions.
130  * Not static, to aid debugging.
131  */
132 void smb_rmfree(smbnode_t *);
133 void smbinactive(smbnode_t *);
134 void smb_rmhash_locked(smbnode_t *);
135 void smb_destroy_node(smbnode_t *);
136 void smbfs_kmem_reclaim(void *cdrarg);
137 
138 smbnode_t *smbhashfind(struct vfs *, const char *, int, rhashq_t *);
139 static vnode_t *make_smbnode(vfs_t *, char *, int, rhashq_t *, int *);
140 
141 
142 /*
143  * Free the resources associated with an smbnode.
144  * Note: This is different from smbfs_inactive
145  *
146  * NFS: nfs_subr.c:rinactive
147  */
148 void
149 smbinactive(smbnode_t *np)
150 {
151 
152 	if (np->n_rpath) {
153 		kmem_free(np->n_rpath, np->n_rplen + 1);
154 		np->n_rpath = NULL;
155 	}
156 }
157 
158 /*
159  * Return a vnode for the given CIFS directory and filename.
160  * If no smbnode exists for this fhandle, create one and put it
161  * into the hash queues.  If the smbnode for this fhandle
162  * already exists, return it.
163  *
164  * Note: make_smbnode() may upgrade the hash bucket lock to exclusive.
165  *
166  * NFS: nfs_subr.c:makenfsnode
167  */
168 vnode_t *
169 smbfs_make_node(
170 	vfs_t *vfsp,
171 	const char *dir,
172 	int dirlen,
173 	const char *name,
174 	int nmlen,
175 	char sep,
176 	struct smbfattr *fap)
177 {
178 	char *rpath;
179 	int rplen, idx;
180 	uint32_t hash;
181 	rhashq_t *rhtp;
182 	smbnode_t *np;
183 	vnode_t *vp;
184 #ifdef NOT_YET
185 	vattr_t va;
186 #endif
187 	int newnode;
188 
189 	/*
190 	 * Build the full path name in allocated memory
191 	 * so we have it for lookup, etc.  Note the
192 	 * special case at the root (dir=="\\", dirlen==1)
193 	 * where this does not add a slash separator.
194 	 * To do that would make a double slash, which
195 	 * has special meaning in CIFS.
196 	 *
197 	 * ToDo:  Would prefer to allocate a remote path
198 	 * only when we will create a new node.
199 	 */
200 	if (dirlen <= 1 && sep == '\\')
201 		sep = '\0';	/* no slash */
202 
203 	/* Compute the length of rpath and allocate. */
204 	rplen = dirlen;
205 	if (sep)
206 		rplen++;
207 	if (name)
208 		rplen += nmlen;
209 
210 	rpath = kmem_alloc(rplen + 1, KM_SLEEP);
211 
212 	/* Fill in rpath */
213 	bcopy(dir, rpath, dirlen);
214 	if (sep)
215 		rpath[dirlen++] = sep;
216 	if (name)
217 		bcopy(name, &rpath[dirlen], nmlen);
218 	rpath[rplen] = 0;
219 
220 	hash = smbfs_hash(rpath, rplen);
221 	idx = hash & smbtablemask;
222 	rhtp = &smbtable[idx];
223 	rw_enter(&rhtp->r_lock, RW_READER);
224 
225 	vp = make_smbnode(vfsp, rpath, rplen, rhtp, &newnode);
226 	np = VTOSMB(vp);
227 	np->n_ino = hash;	/* Equivalent to: smbfs_getino() */
228 
229 	/*
230 	 * Note: make_smbnode keeps a reference to rpath in
231 	 * new nodes it creates, so only free when we found
232 	 * an existing node.
233 	 */
234 	if (!newnode) {
235 		kmem_free(rpath, rplen + 1);
236 		rpath = NULL;
237 	}
238 
239 	if (fap == NULL) {
240 #ifdef NOT_YET
241 		if (newnode) {
242 			PURGE_ATTRCACHE(vp);
243 		}
244 #endif
245 		rw_exit(&rhtp->r_lock);
246 		return (vp);
247 	}
248 
249 	/* Have SMB attributes. */
250 	vp->v_type = (fap->fa_attr & SMB_FA_DIR) ? VDIR : VREG;
251 	/* XXX: np->n_ino = fap->fa_ino; see above */
252 	np->r_size = fap->fa_size;
253 	/* XXX: np->r_attr = *fap here instead? */
254 	np->r_atime = fap->fa_atime;
255 	np->r_ctime = fap->fa_ctime;
256 	np->r_mtime = fap->fa_mtime;
257 
258 #ifdef NOT_YET
259 	if (!newnode) {
260 		rw_exit(&rhtp->r_lock);
261 		(void) nfs_cache_fattr(vp, attr, &va, t, cr);
262 	} else {
263 		if (attr->na_type < NFNON || attr->na_type > NFSOC)
264 			vp->v_type = VBAD;
265 		else
266 			vp->v_type = n2v_type(attr);
267 		vp->v_rdev = makedevice(attr->rdev.specdata1,
268 		    attr->rdev.specdata2);
269 		nfs_attrcache(vp, attr, t);
270 		rw_exit(&rhtp->r_lock);
271 	}
272 #else
273 	rw_exit(&rhtp->r_lock);
274 #endif
275 
276 	return (vp);
277 }
278 
279 /*
280  * NFS: nfs_subr.c:rtablehash
281  * We use smbfs_hash().
282  */
283 
284 /*
285  * Find or create an smbnode.
286  * NFS: nfs_subr.c:make_rnode
287  */
288 static vnode_t *
289 make_smbnode(
290 	vfs_t *vfsp,
291 	char *rpath,
292 	int rplen,
293 	rhashq_t *rhtp,
294 	int *newnode)
295 {
296 	smbnode_t *np;
297 	smbnode_t *tnp;
298 	vnode_t *vp;
299 	smbmntinfo_t *mi;
300 
301 	ASSERT(RW_READ_HELD(&rhtp->r_lock));
302 
303 	mi = VFTOSMI(vfsp);
304 
305 start:
306 	np = smbhashfind(vfsp, rpath, rplen, rhtp);
307 	if (np != NULL) {
308 		vp = SMBTOV(np);
309 		*newnode = 0;
310 		return (vp);
311 	}
312 
313 	/* Note: will retake this lock below. */
314 	rw_exit(&rhtp->r_lock);
315 
316 	/*
317 	 * see if we can find something on the freelist
318 	 */
319 	mutex_enter(&smbfreelist_lock);
320 	if (smbfreelist != NULL && smbnodenew >= nsmbnode) {
321 		np = smbfreelist;
322 		smb_rmfree(np);
323 		mutex_exit(&smbfreelist_lock);
324 
325 		vp = SMBTOV(np);
326 
327 		if (np->r_flags & RHASHED) {
328 			rw_enter(&np->r_hashq->r_lock, RW_WRITER);
329 			mutex_enter(&vp->v_lock);
330 			if (vp->v_count > 1) {
331 				vp->v_count--;
332 				mutex_exit(&vp->v_lock);
333 				rw_exit(&np->r_hashq->r_lock);
334 				rw_enter(&rhtp->r_lock, RW_READER);
335 				goto start;
336 			}
337 			mutex_exit(&vp->v_lock);
338 			smb_rmhash_locked(np);
339 			rw_exit(&np->r_hashq->r_lock);
340 		}
341 
342 		smbinactive(np);
343 
344 		mutex_enter(&vp->v_lock);
345 		if (vp->v_count > 1) {
346 			vp->v_count--;
347 			mutex_exit(&vp->v_lock);
348 			rw_enter(&rhtp->r_lock, RW_READER);
349 			goto start;
350 		}
351 		mutex_exit(&vp->v_lock);
352 		vn_invalid(vp);
353 		/*
354 		 * destroy old locks before bzero'ing and
355 		 * recreating the locks below.
356 		 */
357 		smbfs_rw_destroy(&np->r_rwlock);
358 		smbfs_rw_destroy(&np->r_lkserlock);
359 		mutex_destroy(&np->r_statelock);
360 		cv_destroy(&np->r_cv);
361 		/*
362 		 * Make sure that if smbnode is recycled then
363 		 * VFS count is decremented properly before
364 		 * reuse.
365 		 */
366 		VFS_RELE(vp->v_vfsp);
367 		vn_reinit(vp);
368 	} else {
369 		/*
370 		 * allocate and initialize a new smbnode
371 		 */
372 		vnode_t *new_vp;
373 
374 		mutex_exit(&smbfreelist_lock);
375 
376 		np = kmem_cache_alloc(smbnode_cache, KM_SLEEP);
377 		new_vp = vn_alloc(KM_SLEEP);
378 
379 		atomic_add_long((ulong_t *)&smbnodenew, 1);
380 		vp = new_vp;
381 	}
382 
383 	/* Initialize smbnode_t */
384 	bzero(np, sizeof (*np));
385 
386 	smbfs_rw_init(&np->r_rwlock, NULL, RW_DEFAULT, NULL);
387 	smbfs_rw_init(&np->r_lkserlock, NULL, RW_DEFAULT, NULL);
388 	mutex_init(&np->r_statelock, NULL, MUTEX_DEFAULT, NULL);
389 	cv_init(&np->r_cv, NULL, CV_DEFAULT, NULL);
390 	/* cv_init(&np->r_commit.c_cv, NULL, CV_DEFAULT, NULL); */
391 
392 	np->r_vnode = vp;
393 	np->n_mount = mi;
394 	np->r_hashq = rhtp;
395 	np->n_direof = -1;
396 	np->n_fid = SMB_FID_UNUSED;
397 	np->n_uid = UID_NOBODY;
398 	np->n_gid = GID_NOBODY;
399 	/* XXX: make attributes stale? */
400 
401 #if 0 /* XXX dircache */
402 	/*
403 	 * We don't know if it's a directory yet.
404 	 * Let the caller do this?  XXX
405 	 */
406 	avl_create(&np->r_dir, compar, sizeof (rddir_cache),
407 	    offsetof(rddir_cache, tree));
408 #endif
409 
410 	/* Now fill in the vnode. */
411 	vn_setops(vp, smbfs_vnodeops);
412 	vp->v_data = (caddr_t)np;
413 	VFS_HOLD(vfsp);
414 	vp->v_vfsp = vfsp;
415 	vp->v_type = VNON;
416 
417 	/*
418 	 * There is a race condition if someone else
419 	 * alloc's the smbnode while no locks are held, so we
420 	 * check again and recover if found.
421 	 */
422 	rw_enter(&rhtp->r_lock, RW_WRITER);
423 	tnp = smbhashfind(vfsp, rpath, rplen, rhtp);
424 	if (tnp != NULL) {
425 		vp = SMBTOV(tnp);
426 		*newnode = 0;
427 		rw_exit(&rhtp->r_lock);
428 		/* The node we were building goes on the free list. */
429 		smb_addfree(np);
430 		rw_enter(&rhtp->r_lock, RW_READER);
431 		return (vp);
432 	}
433 
434 	/*
435 	 * Hash search identifies nodes by the full pathname,
436 	 * so store that before linking in the hash list.
437 	 * Note: caller allocates the rpath, and knows
438 	 * about this reference when *newnode is set.
439 	 */
440 	np->n_rpath = rpath;
441 	np->n_rplen = rplen;
442 
443 	smb_addhash(np);
444 	*newnode = 1;
445 	return (vp);
446 }
447 
448 /*
449  * smb_addfree
450  * Put a smbnode on the free list.
451  *
452  * Normally called by smbfs_inactive, but also
453  * called in here during cleanup operations.
454  *
455  * Smbnodes which were allocated above and beyond the normal limit
456  * are immediately freed.
457  *
458  * NFS: nfs_subr.c:rp_addfree
459  */
460 void
461 smb_addfree(smbnode_t *np)
462 {
463 	vnode_t *vp;
464 	struct vfs *vfsp;
465 
466 	vp = SMBTOV(np);
467 	ASSERT(vp->v_count >= 1);
468 	ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
469 
470 	/*
471 	 * If we have too many smbnodes allocated and there are no
472 	 * references to this smbnode, or if the smbnode is no longer
473 	 * accessible by it does not reside in the hash queues,
474 	 * or if an i/o error occurred while writing to the file,
475 	 * then just free it instead of putting it on the smbnode
476 	 * freelist.
477 	 */
478 	vfsp = vp->v_vfsp;
479 	if (((smbnodenew > nsmbnode || !(np->r_flags & RHASHED) ||
480 	    np->r_error || (vfsp->vfs_flag & VFS_UNMOUNTED)) &&
481 	    np->r_count == 0)) {
482 		if (np->r_flags & RHASHED) {
483 			rw_enter(&np->r_hashq->r_lock, RW_WRITER);
484 			mutex_enter(&vp->v_lock);
485 			if (vp->v_count > 1) {
486 				vp->v_count--;
487 				mutex_exit(&vp->v_lock);
488 				rw_exit(&np->r_hashq->r_lock);
489 				return;
490 				/*
491 				 * Will get another call later,
492 				 * via smbfs_inactive.
493 				 */
494 			}
495 			mutex_exit(&vp->v_lock);
496 			smb_rmhash_locked(np);
497 			rw_exit(&np->r_hashq->r_lock);
498 		}
499 
500 		smbinactive(np);
501 
502 		/*
503 		 * Recheck the vnode reference count.  We need to
504 		 * make sure that another reference has not been
505 		 * acquired while we were not holding v_lock.  The
506 		 * smbnode is not in the smbnode hash queues, so the
507 		 * only way for a reference to have been acquired
508 		 * is for a VOP_PUTPAGE because the smbnode was marked
509 		 * with RDIRTY or for a modified page.  This
510 		 * reference may have been acquired before our call
511 		 * to smbinactive.  The i/o may have been completed,
512 		 * thus allowing smbinactive to complete, but the
513 		 * reference to the vnode may not have been released
514 		 * yet.  In any case, the smbnode can not be destroyed
515 		 * until the other references to this vnode have been
516 		 * released.  The other references will take care of
517 		 * either destroying the smbnode or placing it on the
518 		 * smbnode freelist.  If there are no other references,
519 		 * then the smbnode may be safely destroyed.
520 		 */
521 		mutex_enter(&vp->v_lock);
522 		if (vp->v_count > 1) {
523 			vp->v_count--;
524 			mutex_exit(&vp->v_lock);
525 			return;
526 		}
527 		mutex_exit(&vp->v_lock);
528 
529 		smb_destroy_node(np);
530 		return;
531 	}
532 	/*
533 	 * Lock the hash queue and then recheck the reference count
534 	 * to ensure that no other threads have acquired a reference
535 	 * to indicate that the smbnode should not be placed on the
536 	 * freelist.  If another reference has been acquired, then
537 	 * just release this one and let the other thread complete
538 	 * the processing of adding this smbnode to the freelist.
539 	 */
540 	rw_enter(&np->r_hashq->r_lock, RW_WRITER);
541 
542 	mutex_enter(&vp->v_lock);
543 	if (vp->v_count > 1) {
544 		vp->v_count--;
545 		mutex_exit(&vp->v_lock);
546 		rw_exit(&np->r_hashq->r_lock);
547 		return;
548 	}
549 	mutex_exit(&vp->v_lock);
550 
551 	/*
552 	 * If there is no cached data or metadata for this file, then
553 	 * put the smbnode on the front of the freelist so that it will
554 	 * be reused before other smbnodes which may have cached data or
555 	 * metadata associated with them.
556 	 */
557 	mutex_enter(&smbfreelist_lock);
558 	if (smbfreelist == NULL) {
559 		np->r_freef = np;
560 		np->r_freeb = np;
561 		smbfreelist = np;
562 	} else {
563 		np->r_freef = smbfreelist;
564 		np->r_freeb = smbfreelist->r_freeb;
565 		smbfreelist->r_freeb->r_freef = np;
566 		smbfreelist->r_freeb = np;
567 	}
568 	mutex_exit(&smbfreelist_lock);
569 
570 	rw_exit(&np->r_hashq->r_lock);
571 }
572 
573 /*
574  * Remove an smbnode from the free list.
575  *
576  * The caller must be holding smbfreelist_lock and the smbnode
577  * must be on the freelist.
578  *
579  * NFS: nfs_subr.c:rp_rmfree
580  */
581 void
582 smb_rmfree(smbnode_t *np)
583 {
584 
585 	ASSERT(MUTEX_HELD(&smbfreelist_lock));
586 	ASSERT(np->r_freef != NULL && np->r_freeb != NULL);
587 
588 	if (np == smbfreelist) {
589 		smbfreelist = np->r_freef;
590 		if (np == smbfreelist)
591 			smbfreelist = NULL;
592 	}
593 
594 	np->r_freeb->r_freef = np->r_freef;
595 	np->r_freef->r_freeb = np->r_freeb;
596 
597 	np->r_freef = np->r_freeb = NULL;
598 }
599 
600 /*
601  * Put a smbnode in the hash table.
602  *
603  * The caller must be holding the exclusive hash queue lock.
604  *
605  * NFS: nfs_subr.c:rp_addhash
606  */
607 void
608 smb_addhash(smbnode_t *np)
609 {
610 
611 	ASSERT(RW_WRITE_HELD(&np->r_hashq->r_lock));
612 	ASSERT(!(np->r_flags & RHASHED));
613 
614 	np->r_hashf = np->r_hashq->r_hashf;
615 	np->r_hashq->r_hashf = np;
616 	np->r_hashb = (smbnode_t *)np->r_hashq;
617 	np->r_hashf->r_hashb = np;
618 
619 	mutex_enter(&np->r_statelock);
620 	np->r_flags |= RHASHED;
621 	mutex_exit(&np->r_statelock);
622 }
623 
624 /*
625  * Remove a smbnode from the hash table.
626  *
627  * The caller must be holding the hash queue lock.
628  *
629  * NFS: nfs_subr.c:rp_rmhash_locked
630  */
631 void
632 smb_rmhash_locked(smbnode_t *np)
633 {
634 
635 	ASSERT(RW_WRITE_HELD(&np->r_hashq->r_lock));
636 	ASSERT(np->r_flags & RHASHED);
637 
638 	np->r_hashb->r_hashf = np->r_hashf;
639 	np->r_hashf->r_hashb = np->r_hashb;
640 
641 	mutex_enter(&np->r_statelock);
642 	np->r_flags &= ~RHASHED;
643 	mutex_exit(&np->r_statelock);
644 }
645 
646 /*
647  * Remove a smbnode from the hash table.
648  *
649  * The caller must not be holding the hash queue lock.
650  */
651 void
652 smb_rmhash(smbnode_t *np)
653 {
654 
655 	rw_enter(&np->r_hashq->r_lock, RW_WRITER);
656 	smb_rmhash_locked(np);
657 	rw_exit(&np->r_hashq->r_lock);
658 }
659 
660 /*
661  * Lookup a smbnode by fhandle.
662  *
663  * The caller must be holding the hash queue lock, either shared or exclusive.
664  * XXX: make static?
665  *
666  * NFS: nfs_subr.c:rfind
667  */
668 smbnode_t *
669 smbhashfind(
670 	struct vfs *vfsp,
671 	const char *rpath,
672 	int rplen,
673 	rhashq_t *rhtp)
674 {
675 	smbnode_t *np;
676 	vnode_t *vp;
677 
678 	ASSERT(RW_LOCK_HELD(&rhtp->r_lock));
679 
680 	for (np = rhtp->r_hashf; np != (smbnode_t *)rhtp; np = np->r_hashf) {
681 		vp = SMBTOV(np);
682 		if (vp->v_vfsp == vfsp &&
683 		    np->n_rplen == rplen &&
684 		    bcmp(np->n_rpath, rpath, rplen) == 0) {
685 			/*
686 			 * remove smbnode from free list, if necessary.
687 			 */
688 			if (np->r_freef != NULL) {
689 				mutex_enter(&smbfreelist_lock);
690 				/*
691 				 * If the smbnode is on the freelist,
692 				 * then remove it and use that reference
693 				 * as the new reference.  Otherwise,
694 				 * need to increment the reference count.
695 				 */
696 				if (np->r_freef != NULL) {
697 					smb_rmfree(np);
698 					mutex_exit(&smbfreelist_lock);
699 				} else {
700 					mutex_exit(&smbfreelist_lock);
701 					VN_HOLD(vp);
702 				}
703 			} else
704 				VN_HOLD(vp);
705 			return (np);
706 		}
707 	}
708 	return (NULL);
709 }
710 
711 #ifdef SMB_VNODE_DEBUG
712 int smb_check_table_debug = 1;
713 #else /* SMB_VNODE_DEBUG */
714 int smb_check_table_debug = 0;
715 #endif /* SMB_VNODE_DEBUG */
716 
717 
718 /*
719  * Return 1 if there is a active vnode belonging to this vfs in the
720  * smbtable cache.
721  *
722  * Several of these checks are done without holding the usual
723  * locks.  This is safe because destroy_smbtable(), smb_addfree(),
724  * etc. will redo the necessary checks before actually destroying
725  * any smbnodes.
726  *
727  * NFS: nfs_subr.c:check_rtable
728  *
729  * Debugging changes here relative to NFS.
730  * Relatively harmless, so left 'em in.
731  */
732 int
733 smb_check_table(struct vfs *vfsp, smbnode_t *rtnp)
734 {
735 	smbnode_t *np;
736 	vnode_t *vp;
737 	int index;
738 	int busycnt = 0;
739 
740 	for (index = 0; index < smbtablesize; index++) {
741 		rw_enter(&smbtable[index].r_lock, RW_READER);
742 		for (np = smbtable[index].r_hashf;
743 		    np != (smbnode_t *)(&smbtable[index]);
744 		    np = np->r_hashf) {
745 			if (np == rtnp)
746 				continue; /* skip the root */
747 			vp = SMBTOV(np);
748 			if (vp->v_vfsp != vfsp)
749 				continue; /* skip other mount */
750 
751 			/* Now the 'busy' checks: */
752 			/* Not on the free list? */
753 			if (np->r_freef == NULL) {
754 				SMBVDEBUG("!r_freef: node=0x%p, v_path=%s\n",
755 				    (void *)np, vp->v_path);
756 				busycnt++;
757 			}
758 
759 			/* Has dirty pages? */
760 			if (vn_has_cached_data(vp) &&
761 			    (np->r_flags & RDIRTY)) {
762 				SMBVDEBUG("is dirty: node=0x%p, v_path=%s\n",
763 				    (void *)np, vp->v_path);
764 				busycnt++;
765 			}
766 
767 			/* Other refs? (not reflected in v_count) */
768 			if (np->r_count > 0) {
769 				SMBVDEBUG("+r_count: node=0x%p, v_path=%s\n",
770 				    (void *)np, vp->v_path);
771 				busycnt++;
772 			}
773 
774 			if (busycnt && !smb_check_table_debug)
775 				break;
776 
777 		}
778 		rw_exit(&smbtable[index].r_lock);
779 	}
780 	return (busycnt);
781 }
782 
783 /*
784  * Destroy inactive vnodes from the hash queues which belong to this
785  * vfs.  It is essential that we destroy all inactive vnodes during a
786  * forced unmount as well as during a normal unmount.
787  *
788  * NFS: nfs_subr.c:destroy_rtable
789  */
790 void
791 smbfs_destroy_table(struct vfs *vfsp)
792 {
793 	int index;
794 	smbnode_t *np;
795 	smbnode_t *rlist;
796 	smbnode_t *r_hashf;
797 	vnode_t *vp;
798 
799 	rlist = NULL;
800 
801 	for (index = 0; index < smbtablesize; index++) {
802 		rw_enter(&smbtable[index].r_lock, RW_WRITER);
803 		for (np = smbtable[index].r_hashf;
804 		    np != (smbnode_t *)(&smbtable[index]);
805 		    np = r_hashf) {
806 			/* save the hash pointer before destroying */
807 			r_hashf = np->r_hashf;
808 			vp = SMBTOV(np);
809 			if (vp->v_vfsp == vfsp) {
810 				mutex_enter(&smbfreelist_lock);
811 				if (np->r_freef != NULL) {
812 					smb_rmfree(np);
813 					mutex_exit(&smbfreelist_lock);
814 					smb_rmhash_locked(np);
815 					np->r_hashf = rlist;
816 					rlist = np;
817 				} else
818 					mutex_exit(&smbfreelist_lock);
819 			}
820 		}
821 		rw_exit(&smbtable[index].r_lock);
822 	}
823 
824 	for (np = rlist; np != NULL; np = rlist) {
825 		rlist = np->r_hashf;
826 		/*
827 		 * This call to smb_addfree will end up destroying the
828 		 * smbnode, but in a safe way with the appropriate set
829 		 * of checks done.
830 		 */
831 		smb_addfree(np);
832 	}
833 
834 }
835 
836 /*
837  * This routine destroys all the resources associated with the smbnode
838  * and then the smbnode itself.
839  *
840  * NFS: nfs_subr.c:destroy_rnode
841  */
842 void
843 smb_destroy_node(smbnode_t *np)
844 {
845 	vnode_t *vp;
846 	vfs_t *vfsp;
847 
848 	vp = SMBTOV(np);
849 	vfsp = vp->v_vfsp;
850 
851 	ASSERT(vp->v_count == 1);
852 	ASSERT(np->r_count == 0);
853 	ASSERT(np->r_mapcnt == 0);
854 	ASSERT(!(np->r_flags & RHASHED));
855 	ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
856 	atomic_add_long((ulong_t *)&smbnodenew, -1);
857 	vn_invalid(vp);
858 	vn_free(vp);
859 	kmem_cache_free(smbnode_cache, np);
860 	VFS_RELE(vfsp);
861 }
862 
863 /* rflush? */
864 /* access cache */
865 /* client handles */
866 
867 /*
868  * initialize resources that are used by smbfs_subr.c
869  * this is called from the _init() routine (by the way of smbfs_clntinit())
870  *
871  * allocate and initialze smbfs hash table
872  * NFS: nfs_subr.c:nfs_subrinit
873  */
874 int
875 smbfs_subrinit(void)
876 {
877 	int i;
878 	ulong_t nsmbnode_max;
879 
880 	/*
881 	 * Allocate and initialize the smbnode hash queues
882 	 */
883 	if (nsmbnode <= 0)
884 		nsmbnode = ncsize; /* dnlc.h */
885 	nsmbnode_max = (ulong_t)((kmem_maxavail() >> 2) /
886 	    sizeof (struct smbnode));
887 	if (nsmbnode > nsmbnode_max || (nsmbnode == 0 && ncsize == 0)) {
888 		zcmn_err(GLOBAL_ZONEID, CE_NOTE,
889 		    "setting nsmbnode to max value of %ld", nsmbnode_max);
890 		nsmbnode = nsmbnode_max;
891 	}
892 
893 	smbtablesize = 1 << highbit(nsmbnode / smbhashlen);
894 	smbtablemask = smbtablesize - 1;
895 	smbtable = kmem_alloc(smbtablesize * sizeof (*smbtable), KM_SLEEP);
896 	for (i = 0; i < smbtablesize; i++) {
897 		smbtable[i].r_hashf = (smbnode_t *)(&smbtable[i]);
898 		smbtable[i].r_hashb = (smbnode_t *)(&smbtable[i]);
899 		rw_init(&smbtable[i].r_lock, NULL, RW_DEFAULT, NULL);
900 	}
901 	smbnode_cache = kmem_cache_create("smbnode_cache", sizeof (smbnode_t),
902 	    0, NULL, NULL, smbfs_kmem_reclaim, NULL, NULL, 0);
903 
904 	/*
905 	 * Initialize the various mutexes and reader/writer locks
906 	 */
907 	mutex_init(&smbfreelist_lock, NULL, MUTEX_DEFAULT, NULL);
908 	mutex_init(&smbfs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
909 
910 	/*
911 	 * Assign unique major number for all smbfs mounts
912 	 */
913 	if ((smbfs_major = getudev()) == -1) {
914 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
915 		    "smbfs: init: can't get unique device number");
916 		smbfs_major = 0;
917 	}
918 	smbfs_minor = 0;
919 
920 	return (0);
921 }
922 
923 /*
924  * free smbfs hash table, etc.
925  * NFS: nfs_subr.c:nfs_subrfini
926  */
927 void
928 smbfs_subrfini(void)
929 {
930 	int i;
931 
932 	/*
933 	 * Deallocate the smbnode hash queues
934 	 */
935 	kmem_cache_destroy(smbnode_cache);
936 
937 	for (i = 0; i < smbtablesize; i++)
938 		rw_destroy(&smbtable[i].r_lock);
939 	kmem_free(smbtable, smbtablesize * sizeof (*smbtable));
940 
941 	/*
942 	 * Destroy the various mutexes and reader/writer locks
943 	 */
944 	mutex_destroy(&smbfreelist_lock);
945 	mutex_destroy(&smbfs_minor_lock);
946 }
947 
948 /* rddir_cache ? */
949 
950 /*
951  * Support functions for smbfs_kmem_reclaim
952  */
953 
954 static int
955 smbfs_node_reclaim(void)
956 {
957 	int freed;
958 	smbnode_t *np;
959 	vnode_t *vp;
960 
961 	freed = 0;
962 	mutex_enter(&smbfreelist_lock);
963 	while ((np = smbfreelist) != NULL) {
964 		smb_rmfree(np);
965 		mutex_exit(&smbfreelist_lock);
966 		if (np->r_flags & RHASHED) {
967 			vp = SMBTOV(np);
968 			rw_enter(&np->r_hashq->r_lock, RW_WRITER);
969 			mutex_enter(&vp->v_lock);
970 			if (vp->v_count > 1) {
971 				vp->v_count--;
972 				mutex_exit(&vp->v_lock);
973 				rw_exit(&np->r_hashq->r_lock);
974 				mutex_enter(&smbfreelist_lock);
975 				continue;
976 			}
977 			mutex_exit(&vp->v_lock);
978 			smb_rmhash_locked(np);
979 			rw_exit(&np->r_hashq->r_lock);
980 		}
981 		/*
982 		 * This call to smb_addfree will end up destroying the
983 		 * smbnode, but in a safe way with the appropriate set
984 		 * of checks done.
985 		 */
986 		smb_addfree(np);
987 		mutex_enter(&smbfreelist_lock);
988 	}
989 	mutex_exit(&smbfreelist_lock);
990 	return (freed);
991 }
992 
993 /*
994  * Called by kmem_cache_alloc ask us if we could
995  * "Please give back some memory!"
996  *
997  * Todo: dump nodes from the free list?
998  */
999 /*ARGSUSED*/
1000 void
1001 smbfs_kmem_reclaim(void *cdrarg)
1002 {
1003 	(void) smbfs_node_reclaim();
1004 }
1005 
1006 /* nfs failover stuff */
1007 /* nfs_rw_xxx - see smbfs_rwlock.c */
1008