xref: /illumos-gate/usr/src/uts/common/fs/lofs/lofs_subr.c (revision ade42b55)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5ddfcde86Srsb  * Common Development and Distribution License (the "License").
6ddfcde86Srsb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2279a28c7aSmarks  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
25*ade42b55SSebastien Roy /*
26*ade42b55SSebastien Roy  * Copyright (c) 2017 by Delphix. All rights reserved.
27*ade42b55SSebastien Roy  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * The idea behind composition-based stacked filesystems is to add a
317c478bd9Sstevel@tonic-gate  * vnode to the stack of vnodes for each mount. These vnodes have their
327c478bd9Sstevel@tonic-gate  * own set of mount options and filesystem-specific functions, so they
337c478bd9Sstevel@tonic-gate  * can modify data or operations before they are passed along. Such a
347c478bd9Sstevel@tonic-gate  * filesystem must maintain a mapping from the underlying vnodes to its
357c478bd9Sstevel@tonic-gate  * interposing vnodes.
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  * In lofs, this mapping is implemented by a hashtable. Each bucket
387c478bd9Sstevel@tonic-gate  * contains a count of the number of nodes currently contained, the
397c478bd9Sstevel@tonic-gate  * chain of vnodes, and a lock to protect the list of vnodes. The
407c478bd9Sstevel@tonic-gate  * hashtable dynamically grows if the number of vnodes in the table as a
417c478bd9Sstevel@tonic-gate  * whole exceeds the size of the table left-shifted by
427c478bd9Sstevel@tonic-gate  * lo_resize_threshold. In order to minimize lock contention, there is
437c478bd9Sstevel@tonic-gate  * no global lock protecting the hashtable, hence obtaining the
447c478bd9Sstevel@tonic-gate  * per-bucket locks consists of a dance to make sure we've actually
457c478bd9Sstevel@tonic-gate  * locked the correct bucket. Acquiring a bucket lock doesn't involve
467c478bd9Sstevel@tonic-gate  * locking the hashtable itself, so we refrain from freeing old
477c478bd9Sstevel@tonic-gate  * hashtables, and store them in a linked list of retired hashtables;
487c478bd9Sstevel@tonic-gate  * the list is freed when the filesystem is unmounted.
497c478bd9Sstevel@tonic-gate  */
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate #include <sys/param.h>
527c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
537c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
547c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
557c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
567c478bd9Sstevel@tonic-gate #include <sys/systm.h>
577c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
587c478bd9Sstevel@tonic-gate #include <sys/debug.h>
597c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate #include <sys/fs/lofs_node.h>
627c478bd9Sstevel@tonic-gate #include <sys/fs/lofs_info.h>
637c478bd9Sstevel@tonic-gate /*
647c478bd9Sstevel@tonic-gate  * Due to the hashing algorithm, the size of the hash table needs to be a
657c478bd9Sstevel@tonic-gate  * power of 2.
667c478bd9Sstevel@tonic-gate  */
677c478bd9Sstevel@tonic-gate #define	LOFS_DEFAULT_HTSIZE	(1 << 6)
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #define	ltablehash(vp, tblsz)	((((intptr_t)(vp))>>10) & ((tblsz)-1))
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate /*
727c478bd9Sstevel@tonic-gate  * The following macros can only be safely used when the desired bucket
737c478bd9Sstevel@tonic-gate  * is already locked.
747c478bd9Sstevel@tonic-gate  */
757c478bd9Sstevel@tonic-gate /*
767c478bd9Sstevel@tonic-gate  * The lock in the hashtable associated with the given vnode.
777c478bd9Sstevel@tonic-gate  */
787c478bd9Sstevel@tonic-gate #define	TABLE_LOCK(vp, li)      \
797c478bd9Sstevel@tonic-gate 	(&(li)->li_hashtable[ltablehash((vp), (li)->li_htsize)].lh_lock)
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate /*
827c478bd9Sstevel@tonic-gate  * The bucket in the hashtable that the given vnode hashes to.
837c478bd9Sstevel@tonic-gate  */
847c478bd9Sstevel@tonic-gate #define	TABLE_BUCKET(vp, li)    \
857c478bd9Sstevel@tonic-gate 	((li)->li_hashtable[ltablehash((vp), (li)->li_htsize)].lh_chain)
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * Number of elements currently in the bucket that the vnode hashes to.
897c478bd9Sstevel@tonic-gate  */
907c478bd9Sstevel@tonic-gate #define	TABLE_COUNT(vp, li)	\
917c478bd9Sstevel@tonic-gate 	((li)->li_hashtable[ltablehash((vp), (li)->li_htsize)].lh_count)
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*
947c478bd9Sstevel@tonic-gate  * Grab/Drop the lock for the bucket this vnode hashes to.
957c478bd9Sstevel@tonic-gate  */
967c478bd9Sstevel@tonic-gate #define	TABLE_LOCK_ENTER(vp, li)	table_lock_enter(vp, li)
977c478bd9Sstevel@tonic-gate #define	TABLE_LOCK_EXIT(vp, li)		\
987c478bd9Sstevel@tonic-gate 	mutex_exit(&(li)->li_hashtable[ltablehash((vp),	\
997c478bd9Sstevel@tonic-gate 	    (li)->li_htsize)].lh_lock)
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate static lnode_t *lfind(struct vnode *, struct loinfo *);
1027c478bd9Sstevel@tonic-gate static void lsave(lnode_t *, struct loinfo *);
1037c478bd9Sstevel@tonic-gate static struct vfs *makelfsnode(struct vfs *, struct loinfo *);
1047c478bd9Sstevel@tonic-gate static struct lfsnode *lfsfind(struct vfs *, struct loinfo *);
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate uint_t lo_resize_threshold = 1;
1077c478bd9Sstevel@tonic-gate uint_t lo_resize_factor = 2;
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate static kmem_cache_t *lnode_cache;
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate /*
1127c478bd9Sstevel@tonic-gate  * Since the hashtable itself isn't protected by a lock, obtaining a
1137c478bd9Sstevel@tonic-gate  * per-bucket lock proceeds as follows:
1147c478bd9Sstevel@tonic-gate  *
1157c478bd9Sstevel@tonic-gate  * (a) li->li_htlock protects li->li_hashtable, li->li_htsize, and
1167c478bd9Sstevel@tonic-gate  * li->li_retired.
1177c478bd9Sstevel@tonic-gate  *
1187c478bd9Sstevel@tonic-gate  * (b) Per-bucket locks (lh_lock) protect the contents of the bucket.
1197c478bd9Sstevel@tonic-gate  *
1207c478bd9Sstevel@tonic-gate  * (c) Locking order for resizing the hashtable is li_htlock then
1217c478bd9Sstevel@tonic-gate  * lh_lock.
1227c478bd9Sstevel@tonic-gate  *
1237c478bd9Sstevel@tonic-gate  * To grab the bucket lock we:
1247c478bd9Sstevel@tonic-gate  *
1257c478bd9Sstevel@tonic-gate  * (1) Stash away the htsize and the pointer to the hashtable to make
1267c478bd9Sstevel@tonic-gate  * sure neither change while we're using them.
1277c478bd9Sstevel@tonic-gate  *
1287c478bd9Sstevel@tonic-gate  * (2) lgrow() updates the pointer to the hashtable before it updates
1297c478bd9Sstevel@tonic-gate  * the size: the worst case scenario is that we have the wrong size (but
1307c478bd9Sstevel@tonic-gate  * the correct table), so we hash to the wrong bucket, grab the wrong
1317c478bd9Sstevel@tonic-gate  * lock, and then realize that things have changed, rewind and start
1327c478bd9Sstevel@tonic-gate  * again. If both the size and the table changed since we loaded them,
1337c478bd9Sstevel@tonic-gate  * we'll realize that too and restart.
1347c478bd9Sstevel@tonic-gate  *
1357c478bd9Sstevel@tonic-gate  * (3) The protocol for growing the hashtable involves holding *all* the
1367c478bd9Sstevel@tonic-gate  * locks in the table, hence the unlocking code (TABLE_LOCK_EXIT())
1377c478bd9Sstevel@tonic-gate  * doesn't need to do any dances, since neither the table nor the size
1387c478bd9Sstevel@tonic-gate  * can change while any bucket lock is held.
1397c478bd9Sstevel@tonic-gate  *
1407c478bd9Sstevel@tonic-gate  * (4) If the hashtable is growing (by thread t1) while another thread
1417c478bd9Sstevel@tonic-gate  * (t2) is trying to grab a bucket lock, t2 might have a stale reference
1427c478bd9Sstevel@tonic-gate  * to li->li_htsize:
1437c478bd9Sstevel@tonic-gate  *
1447c478bd9Sstevel@tonic-gate  * - t1 grabs all locks in lgrow()
1457c478bd9Sstevel@tonic-gate  * 	- t2 loads li->li_htsize and li->li_hashtable
1467c478bd9Sstevel@tonic-gate  * - t1 changes li->hashtable
1477c478bd9Sstevel@tonic-gate  * 	- t2 loads from an offset in the "stale" hashtable and tries to grab
1487c478bd9Sstevel@tonic-gate  * 	the relevant mutex.
1497c478bd9Sstevel@tonic-gate  *
1507c478bd9Sstevel@tonic-gate  * If t1 had free'd the stale hashtable, t2 would be in trouble. Hence,
1517c478bd9Sstevel@tonic-gate  * stale hashtables are not freed but stored in a list of "retired"
1527c478bd9Sstevel@tonic-gate  * hashtables, which is emptied when the filesystem is unmounted.
1537c478bd9Sstevel@tonic-gate  */
1547c478bd9Sstevel@tonic-gate static void
table_lock_enter(vnode_t * vp,struct loinfo * li)1557c478bd9Sstevel@tonic-gate table_lock_enter(vnode_t *vp, struct loinfo *li)
1567c478bd9Sstevel@tonic-gate {
1577c478bd9Sstevel@tonic-gate 	struct lobucket *chain;
1587c478bd9Sstevel@tonic-gate 	uint_t htsize;
1597c478bd9Sstevel@tonic-gate 	uint_t hash;
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	for (;;) {
1627c478bd9Sstevel@tonic-gate 		htsize = li->li_htsize;
1637c478bd9Sstevel@tonic-gate 		membar_consumer();
1647c478bd9Sstevel@tonic-gate 		chain = (struct lobucket *)li->li_hashtable;
1657c478bd9Sstevel@tonic-gate 		hash = ltablehash(vp, htsize);
1667c478bd9Sstevel@tonic-gate 		mutex_enter(&chain[hash].lh_lock);
1677c478bd9Sstevel@tonic-gate 		if (li->li_hashtable == chain && li->li_htsize == htsize)
1687c478bd9Sstevel@tonic-gate 			break;
1697c478bd9Sstevel@tonic-gate 		mutex_exit(&chain[hash].lh_lock);
1707c478bd9Sstevel@tonic-gate 	}
1717c478bd9Sstevel@tonic-gate }
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate void
lofs_subrinit(void)1747c478bd9Sstevel@tonic-gate lofs_subrinit(void)
1757c478bd9Sstevel@tonic-gate {
1767c478bd9Sstevel@tonic-gate 	/*
1777c478bd9Sstevel@tonic-gate 	 * Initialize the cache.
1787c478bd9Sstevel@tonic-gate 	 */
1797c478bd9Sstevel@tonic-gate 	lnode_cache = kmem_cache_create("lnode_cache", sizeof (lnode_t),
1807c478bd9Sstevel@tonic-gate 	    0, NULL, NULL, NULL, NULL, NULL, 0);
1817c478bd9Sstevel@tonic-gate }
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate void
lofs_subrfini(void)1847c478bd9Sstevel@tonic-gate lofs_subrfini(void)
1857c478bd9Sstevel@tonic-gate {
1867c478bd9Sstevel@tonic-gate 	kmem_cache_destroy(lnode_cache);
1877c478bd9Sstevel@tonic-gate }
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate /*
1907c478bd9Sstevel@tonic-gate  * Initialize a (struct loinfo), and initialize the hashtable to have
1917c478bd9Sstevel@tonic-gate  * htsize buckets.
1927c478bd9Sstevel@tonic-gate  */
1937c478bd9Sstevel@tonic-gate void
lsetup(struct loinfo * li,uint_t htsize)1947c478bd9Sstevel@tonic-gate lsetup(struct loinfo *li, uint_t htsize)
1957c478bd9Sstevel@tonic-gate {
1967c478bd9Sstevel@tonic-gate 	li->li_refct = 0;
1977c478bd9Sstevel@tonic-gate 	li->li_lfs = NULL;
1987c478bd9Sstevel@tonic-gate 	if (htsize == 0)
1997c478bd9Sstevel@tonic-gate 		htsize = LOFS_DEFAULT_HTSIZE;
2007c478bd9Sstevel@tonic-gate 	li->li_htsize = htsize;
2017c478bd9Sstevel@tonic-gate 	li->li_hashtable = kmem_zalloc(htsize * sizeof (*li->li_hashtable),
2027c478bd9Sstevel@tonic-gate 	    KM_SLEEP);
2037c478bd9Sstevel@tonic-gate 	mutex_init(&li->li_lfslock, NULL, MUTEX_DEFAULT, NULL);
2047c478bd9Sstevel@tonic-gate 	mutex_init(&li->li_htlock, NULL, MUTEX_DEFAULT, NULL);
2057c478bd9Sstevel@tonic-gate 	li->li_retired = NULL;
2067c478bd9Sstevel@tonic-gate }
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate /*
2097c478bd9Sstevel@tonic-gate  * Destroy a (struct loinfo)
2107c478bd9Sstevel@tonic-gate  */
2117c478bd9Sstevel@tonic-gate void
ldestroy(struct loinfo * li)2127c478bd9Sstevel@tonic-gate ldestroy(struct loinfo *li)
2137c478bd9Sstevel@tonic-gate {
2147c478bd9Sstevel@tonic-gate 	uint_t i, htsize;
2157c478bd9Sstevel@tonic-gate 	struct lobucket *table;
2167c478bd9Sstevel@tonic-gate 	struct lo_retired_ht *lrhp, *trhp;
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 	mutex_destroy(&li->li_htlock);
2197c478bd9Sstevel@tonic-gate 	mutex_destroy(&li->li_lfslock);
2207c478bd9Sstevel@tonic-gate 	htsize = li->li_htsize;
2217c478bd9Sstevel@tonic-gate 	table = li->li_hashtable;
2227c478bd9Sstevel@tonic-gate 	for (i = 0; i < htsize; i++)
2237c478bd9Sstevel@tonic-gate 		mutex_destroy(&table[i].lh_lock);
2247c478bd9Sstevel@tonic-gate 	kmem_free(table, htsize * sizeof (*li->li_hashtable));
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 	/*
2277c478bd9Sstevel@tonic-gate 	 * Free the retired hashtables.
2287c478bd9Sstevel@tonic-gate 	 */
2297c478bd9Sstevel@tonic-gate 	lrhp = li->li_retired;
2307c478bd9Sstevel@tonic-gate 	while (lrhp != NULL) {
2317c478bd9Sstevel@tonic-gate 		trhp = lrhp;
2327c478bd9Sstevel@tonic-gate 		lrhp = lrhp->lrh_next;
2337c478bd9Sstevel@tonic-gate 		kmem_free(trhp->lrh_table,
2347c478bd9Sstevel@tonic-gate 		    trhp->lrh_size * sizeof (*li->li_hashtable));
2357c478bd9Sstevel@tonic-gate 		kmem_free(trhp, sizeof (*trhp));
2367c478bd9Sstevel@tonic-gate 	}
2377c478bd9Sstevel@tonic-gate 	li->li_retired = NULL;
2387c478bd9Sstevel@tonic-gate }
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate /*
2417c478bd9Sstevel@tonic-gate  * Return a looped back vnode for the given vnode.
2427c478bd9Sstevel@tonic-gate  * If no lnode exists for this vnode create one and put it
2437c478bd9Sstevel@tonic-gate  * in a table hashed by vnode.  If the lnode for
2447c478bd9Sstevel@tonic-gate  * this vnode is already in the table return it (ref count is
2457c478bd9Sstevel@tonic-gate  * incremented by lfind).  The lnode will be flushed from the
246b431137cSowenr  * table when lo_inactive calls freelonode.  The creation of
247b431137cSowenr  * a new lnode can be forced via the LOF_FORCE flag even if
248b431137cSowenr  * the vnode exists in the table.  This is used in the creation
249b431137cSowenr  * of a terminating lnode when looping is detected.  A unique
250b431137cSowenr  * lnode is required for the correct evaluation of the current
251b431137cSowenr  * working directory.
2527c478bd9Sstevel@tonic-gate  * NOTE: vp is assumed to be a held vnode.
2537c478bd9Sstevel@tonic-gate  */
2547c478bd9Sstevel@tonic-gate struct vnode *
makelonode(struct vnode * vp,struct loinfo * li,int flag)255b431137cSowenr makelonode(struct vnode *vp, struct loinfo *li, int flag)
2567c478bd9Sstevel@tonic-gate {
2577c478bd9Sstevel@tonic-gate 	lnode_t *lp, *tlp;
2587c478bd9Sstevel@tonic-gate 	struct vfs *vfsp;
2597c478bd9Sstevel@tonic-gate 	vnode_t *nvp;
2607c478bd9Sstevel@tonic-gate 
261b431137cSowenr 	lp = NULL;
2627c478bd9Sstevel@tonic-gate 	TABLE_LOCK_ENTER(vp, li);
263b431137cSowenr 	if (flag != LOF_FORCE)
264b431137cSowenr 		lp = lfind(vp, li);
265b431137cSowenr 	if ((flag == LOF_FORCE) || (lp == NULL)) {
2667c478bd9Sstevel@tonic-gate 		/*
2677c478bd9Sstevel@tonic-gate 		 * Optimistically assume that we won't need to sleep.
2687c478bd9Sstevel@tonic-gate 		 */
2697c478bd9Sstevel@tonic-gate 		lp = kmem_cache_alloc(lnode_cache, KM_NOSLEEP);
2707c478bd9Sstevel@tonic-gate 		nvp = vn_alloc(KM_NOSLEEP);
2717c478bd9Sstevel@tonic-gate 		if (lp == NULL || nvp == NULL) {
2727c478bd9Sstevel@tonic-gate 			TABLE_LOCK_EXIT(vp, li);
2737c478bd9Sstevel@tonic-gate 			/* The lnode allocation may have succeeded, save it */
2747c478bd9Sstevel@tonic-gate 			tlp = lp;
2757c478bd9Sstevel@tonic-gate 			if (tlp == NULL) {
2767c478bd9Sstevel@tonic-gate 				tlp = kmem_cache_alloc(lnode_cache, KM_SLEEP);
2777c478bd9Sstevel@tonic-gate 			}
2787c478bd9Sstevel@tonic-gate 			if (nvp == NULL) {
2797c478bd9Sstevel@tonic-gate 				nvp = vn_alloc(KM_SLEEP);
2807c478bd9Sstevel@tonic-gate 			}
281b431137cSowenr 			lp = NULL;
2827c478bd9Sstevel@tonic-gate 			TABLE_LOCK_ENTER(vp, li);
283b431137cSowenr 			if (flag != LOF_FORCE)
284b431137cSowenr 				lp = lfind(vp, li);
285b431137cSowenr 			if (lp != NULL) {
2867c478bd9Sstevel@tonic-gate 				kmem_cache_free(lnode_cache, tlp);
2877c478bd9Sstevel@tonic-gate 				vn_free(nvp);
2887c478bd9Sstevel@tonic-gate 				VN_RELE(vp);
2897c478bd9Sstevel@tonic-gate 				goto found_lnode;
2907c478bd9Sstevel@tonic-gate 			}
2917c478bd9Sstevel@tonic-gate 			lp = tlp;
2927c478bd9Sstevel@tonic-gate 		}
2931a5e258fSJosef 'Jeff' Sipek 		atomic_inc_32(&li->li_refct);
2947c478bd9Sstevel@tonic-gate 		vfsp = makelfsnode(vp->v_vfsp, li);
2957c478bd9Sstevel@tonic-gate 		lp->lo_vnode = nvp;
2967c478bd9Sstevel@tonic-gate 		VN_SET_VFS_TYPE_DEV(nvp, vfsp, vp->v_type, vp->v_rdev);
2977c478bd9Sstevel@tonic-gate 		nvp->v_flag |= (vp->v_flag & (VNOMOUNT|VNOMAP|VDIROPEN));
2987c478bd9Sstevel@tonic-gate 		vn_setops(nvp, lo_vnodeops);
2997c478bd9Sstevel@tonic-gate 		nvp->v_data = (caddr_t)lp;
3007c478bd9Sstevel@tonic-gate 		lp->lo_vp = vp;
3017c478bd9Sstevel@tonic-gate 		lp->lo_looping = 0;
3027c478bd9Sstevel@tonic-gate 		lsave(lp, li);
3037c478bd9Sstevel@tonic-gate 		vn_exists(vp);
3047c478bd9Sstevel@tonic-gate 	} else {
3057c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
3067c478bd9Sstevel@tonic-gate 	}
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate found_lnode:
3097c478bd9Sstevel@tonic-gate 	TABLE_LOCK_EXIT(vp, li);
3107c478bd9Sstevel@tonic-gate 	return (ltov(lp));
3117c478bd9Sstevel@tonic-gate }
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate /*
3147c478bd9Sstevel@tonic-gate  * Get/Make vfs structure for given real vfs
3157c478bd9Sstevel@tonic-gate  */
3167c478bd9Sstevel@tonic-gate static struct vfs *
makelfsnode(struct vfs * vfsp,struct loinfo * li)3177c478bd9Sstevel@tonic-gate makelfsnode(struct vfs *vfsp, struct loinfo *li)
3187c478bd9Sstevel@tonic-gate {
3197c478bd9Sstevel@tonic-gate 	struct lfsnode *lfs;
3207c478bd9Sstevel@tonic-gate 	struct lfsnode *tlfs;
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate 	/*
3237c478bd9Sstevel@tonic-gate 	 * Don't grab any locks for the fast (common) case.
3247c478bd9Sstevel@tonic-gate 	 */
3257c478bd9Sstevel@tonic-gate 	if (vfsp == li->li_realvfs)
3267c478bd9Sstevel@tonic-gate 		return (li->li_mountvfs);
3277c478bd9Sstevel@tonic-gate 	ASSERT(li->li_refct > 0);
3287c478bd9Sstevel@tonic-gate 	mutex_enter(&li->li_lfslock);
3297c478bd9Sstevel@tonic-gate 	if ((lfs = lfsfind(vfsp, li)) == NULL) {
3307c478bd9Sstevel@tonic-gate 		mutex_exit(&li->li_lfslock);
3317c478bd9Sstevel@tonic-gate 		lfs = kmem_zalloc(sizeof (*lfs), KM_SLEEP);
3327c478bd9Sstevel@tonic-gate 		mutex_enter(&li->li_lfslock);
3337c478bd9Sstevel@tonic-gate 		if ((tlfs = lfsfind(vfsp, li)) != NULL) {
3347c478bd9Sstevel@tonic-gate 			kmem_free(lfs, sizeof (*lfs));
3357c478bd9Sstevel@tonic-gate 			lfs = tlfs;
3367c478bd9Sstevel@tonic-gate 			goto found_lfs;
3377c478bd9Sstevel@tonic-gate 		}
3387c478bd9Sstevel@tonic-gate 		lfs->lfs_realvfs = vfsp;
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 		/*
3417c478bd9Sstevel@tonic-gate 		 * Even though the lfsnode is strictly speaking a private
3427c478bd9Sstevel@tonic-gate 		 * implementation detail of lofs, it should behave as a regular
3437c478bd9Sstevel@tonic-gate 		 * vfs_t for the benefit of the rest of the kernel.
3447c478bd9Sstevel@tonic-gate 		 */
3457c478bd9Sstevel@tonic-gate 		VFS_INIT(&lfs->lfs_vfs, lo_vfsops, (caddr_t)li);
3467c478bd9Sstevel@tonic-gate 		lfs->lfs_vfs.vfs_fstype = li->li_mountvfs->vfs_fstype;
3477c478bd9Sstevel@tonic-gate 		lfs->lfs_vfs.vfs_flag =
34879a28c7aSmarks 		    ((vfsp->vfs_flag | li->li_mflag) & ~li->li_dflag) &
34979a28c7aSmarks 		    INHERIT_VFS_FLAG;
3507c478bd9Sstevel@tonic-gate 		lfs->lfs_vfs.vfs_bsize = vfsp->vfs_bsize;
3517c478bd9Sstevel@tonic-gate 		lfs->lfs_vfs.vfs_dev = vfsp->vfs_dev;
3527c478bd9Sstevel@tonic-gate 		lfs->lfs_vfs.vfs_fsid = vfsp->vfs_fsid;
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 		if (vfsp->vfs_mntpt != NULL) {
3557c478bd9Sstevel@tonic-gate 			lfs->lfs_vfs.vfs_mntpt = vfs_getmntpoint(vfsp);
3567c478bd9Sstevel@tonic-gate 			/* Leave a reference to the mountpoint */
3577c478bd9Sstevel@tonic-gate 		}
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 		(void) VFS_ROOT(vfsp, &lfs->lfs_realrootvp);
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 		/*
3627c478bd9Sstevel@tonic-gate 		 * We use 1 instead of 0 as the value to associate with
3637c478bd9Sstevel@tonic-gate 		 * an idle lfs_vfs.  This is to prevent VFS_RELE()
3647c478bd9Sstevel@tonic-gate 		 * trying to kmem_free() our lfs_t (which is the wrong
3657c478bd9Sstevel@tonic-gate 		 * size).
3667c478bd9Sstevel@tonic-gate 		 */
3677c478bd9Sstevel@tonic-gate 		VFS_HOLD(&lfs->lfs_vfs);
3687c478bd9Sstevel@tonic-gate 		lfs->lfs_next = li->li_lfs;
3697c478bd9Sstevel@tonic-gate 		li->li_lfs = lfs;
37079a28c7aSmarks 		vfs_propagate_features(vfsp, &lfs->lfs_vfs);
3717c478bd9Sstevel@tonic-gate 	}
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate found_lfs:
3747c478bd9Sstevel@tonic-gate 	VFS_HOLD(&lfs->lfs_vfs);
3757c478bd9Sstevel@tonic-gate 	mutex_exit(&li->li_lfslock);
3767c478bd9Sstevel@tonic-gate 	return (&lfs->lfs_vfs);
3777c478bd9Sstevel@tonic-gate }
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate /*
3807c478bd9Sstevel@tonic-gate  * Free lfs node since no longer in use
3817c478bd9Sstevel@tonic-gate  */
3827c478bd9Sstevel@tonic-gate static void
freelfsnode(struct lfsnode * lfs,struct loinfo * li)3837c478bd9Sstevel@tonic-gate freelfsnode(struct lfsnode *lfs, struct loinfo *li)
3847c478bd9Sstevel@tonic-gate {
3857c478bd9Sstevel@tonic-gate 	struct lfsnode *prev = NULL;
3867c478bd9Sstevel@tonic-gate 	struct lfsnode *this;
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&li->li_lfslock));
3897c478bd9Sstevel@tonic-gate 	ASSERT(li->li_refct > 0);
3907c478bd9Sstevel@tonic-gate 	for (this = li->li_lfs; this != NULL; this = this->lfs_next) {
3917c478bd9Sstevel@tonic-gate 		if (this == lfs) {
3927c478bd9Sstevel@tonic-gate 			ASSERT(lfs->lfs_vfs.vfs_count == 1);
3937c478bd9Sstevel@tonic-gate 			if (prev == NULL)
3947c478bd9Sstevel@tonic-gate 				li->li_lfs = lfs->lfs_next;
3957c478bd9Sstevel@tonic-gate 			else
3967c478bd9Sstevel@tonic-gate 				prev->lfs_next = lfs->lfs_next;
3977c478bd9Sstevel@tonic-gate 			if (lfs->lfs_realrootvp != NULL) {
3987c478bd9Sstevel@tonic-gate 				VN_RELE(lfs->lfs_realrootvp);
3997c478bd9Sstevel@tonic-gate 			}
4007c478bd9Sstevel@tonic-gate 			if (lfs->lfs_vfs.vfs_mntpt != NULL)
4017c478bd9Sstevel@tonic-gate 				refstr_rele(lfs->lfs_vfs.vfs_mntpt);
402ddfcde86Srsb 			if (lfs->lfs_vfs.vfs_implp != NULL) {
403ddfcde86Srsb 				ASSERT(lfs->lfs_vfs.vfs_femhead == NULL);
404ddfcde86Srsb 				ASSERT(lfs->lfs_vfs.vfs_vskap == NULL);
405ddfcde86Srsb 				ASSERT(lfs->lfs_vfs.vfs_fstypevsp == NULL);
406ddfcde86Srsb 				kmem_free(lfs->lfs_vfs.vfs_implp,
407ddfcde86Srsb 				    sizeof (vfs_impl_t));
408ddfcde86Srsb 			}
4097c478bd9Sstevel@tonic-gate 			sema_destroy(&lfs->lfs_vfs.vfs_reflock);
4107c478bd9Sstevel@tonic-gate 			kmem_free(lfs, sizeof (struct lfsnode));
4117c478bd9Sstevel@tonic-gate 			return;
4127c478bd9Sstevel@tonic-gate 		}
4137c478bd9Sstevel@tonic-gate 		prev = this;
4147c478bd9Sstevel@tonic-gate 	}
4157c478bd9Sstevel@tonic-gate 	panic("freelfsnode");
4167c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
4177c478bd9Sstevel@tonic-gate }
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate /*
4207c478bd9Sstevel@tonic-gate  * Find lfs given real vfs and mount instance(li)
4217c478bd9Sstevel@tonic-gate  */
4227c478bd9Sstevel@tonic-gate static struct lfsnode *
lfsfind(struct vfs * vfsp,struct loinfo * li)4237c478bd9Sstevel@tonic-gate lfsfind(struct vfs *vfsp, struct loinfo *li)
4247c478bd9Sstevel@tonic-gate {
4257c478bd9Sstevel@tonic-gate 	struct lfsnode *lfs;
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&li->li_lfslock));
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	/*
4307c478bd9Sstevel@tonic-gate 	 * We need to handle the case where a UFS filesystem was forced
4317c478bd9Sstevel@tonic-gate 	 * unmounted and then a subsequent mount got the same vfs
4327c478bd9Sstevel@tonic-gate 	 * structure.  If the new mount lies in the lofs hierarchy, then
4337c478bd9Sstevel@tonic-gate 	 * this will confuse lofs, because the original vfsp (of the
4347c478bd9Sstevel@tonic-gate 	 * forced unmounted filesystem) is still around. We check for
4357c478bd9Sstevel@tonic-gate 	 * this condition here.
4367c478bd9Sstevel@tonic-gate 	 *
4377c478bd9Sstevel@tonic-gate 	 * If we find a cache vfsp hit, then we check to see if the
4387c478bd9Sstevel@tonic-gate 	 * cached filesystem was forced unmounted. Skip all such
4397c478bd9Sstevel@tonic-gate 	 * entries. This should be safe to do since no
4407c478bd9Sstevel@tonic-gate 	 * makelonode()->makelfsnode()->lfsfind() calls should be
4417c478bd9Sstevel@tonic-gate 	 * generated for such force-unmounted filesystems (because (ufs)
4427c478bd9Sstevel@tonic-gate 	 * lookup would've returned an error).
4437c478bd9Sstevel@tonic-gate 	 */
4447c478bd9Sstevel@tonic-gate 	for (lfs = li->li_lfs; lfs != NULL; lfs = lfs->lfs_next) {
4457c478bd9Sstevel@tonic-gate 		if (lfs->lfs_realvfs == vfsp) {
4467c478bd9Sstevel@tonic-gate 			struct vnode *realvp;
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate 			realvp = lfs->lfs_realrootvp;
4497c478bd9Sstevel@tonic-gate 			if (realvp == NULL)
4507c478bd9Sstevel@tonic-gate 				continue;
4517c478bd9Sstevel@tonic-gate 			if (realvp->v_vfsp == NULL || realvp->v_type == VBAD)
4527c478bd9Sstevel@tonic-gate 				continue;
4537c478bd9Sstevel@tonic-gate 			return (lfs);
4547c478bd9Sstevel@tonic-gate 		}
4557c478bd9Sstevel@tonic-gate 	}
4567c478bd9Sstevel@tonic-gate 	return (NULL);
4577c478bd9Sstevel@tonic-gate }
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate /*
4607c478bd9Sstevel@tonic-gate  * Find real vfs given loopback vfs
4617c478bd9Sstevel@tonic-gate  */
4627c478bd9Sstevel@tonic-gate struct vfs *
lo_realvfs(struct vfs * vfsp,struct vnode ** realrootvpp)4637c478bd9Sstevel@tonic-gate lo_realvfs(struct vfs *vfsp, struct vnode **realrootvpp)
4647c478bd9Sstevel@tonic-gate {
4657c478bd9Sstevel@tonic-gate 	struct loinfo *li = vtoli(vfsp);
4667c478bd9Sstevel@tonic-gate 	struct lfsnode *lfs;
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	ASSERT(li->li_refct > 0);
4697c478bd9Sstevel@tonic-gate 	if (vfsp == li->li_mountvfs) {
4707c478bd9Sstevel@tonic-gate 		if (realrootvpp != NULL)
4717c478bd9Sstevel@tonic-gate 			*realrootvpp = vtol(li->li_rootvp)->lo_vp;
4727c478bd9Sstevel@tonic-gate 		return (li->li_realvfs);
4737c478bd9Sstevel@tonic-gate 	}
4747c478bd9Sstevel@tonic-gate 	mutex_enter(&li->li_lfslock);
4757c478bd9Sstevel@tonic-gate 	for (lfs = li->li_lfs; lfs != NULL; lfs = lfs->lfs_next) {
4767c478bd9Sstevel@tonic-gate 		if (vfsp == &lfs->lfs_vfs) {
4777c478bd9Sstevel@tonic-gate 			if (realrootvpp != NULL)
4787c478bd9Sstevel@tonic-gate 				*realrootvpp = lfs->lfs_realrootvp;
4797c478bd9Sstevel@tonic-gate 			mutex_exit(&li->li_lfslock);
4807c478bd9Sstevel@tonic-gate 			return (lfs->lfs_realvfs);
4817c478bd9Sstevel@tonic-gate 		}
4827c478bd9Sstevel@tonic-gate 	}
4837c478bd9Sstevel@tonic-gate 	panic("lo_realvfs");
4847c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
4857c478bd9Sstevel@tonic-gate }
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate /*
4887c478bd9Sstevel@tonic-gate  * Lnode lookup stuff.
4897c478bd9Sstevel@tonic-gate  * These routines maintain a table of lnodes hashed by vp so
4907c478bd9Sstevel@tonic-gate  * that the lnode for a vp can be found if it already exists.
4917c478bd9Sstevel@tonic-gate  *
4927c478bd9Sstevel@tonic-gate  * NB: A lofs shadow vnode causes exactly one VN_HOLD() on the
4937c478bd9Sstevel@tonic-gate  * underlying vnode.
4947c478bd9Sstevel@tonic-gate  */
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate /*
4977c478bd9Sstevel@tonic-gate  * Retire old hashtables.
4987c478bd9Sstevel@tonic-gate  */
4997c478bd9Sstevel@tonic-gate static void
lretire(struct loinfo * li,struct lobucket * table,uint_t size)5007c478bd9Sstevel@tonic-gate lretire(struct loinfo *li, struct lobucket *table, uint_t size)
5017c478bd9Sstevel@tonic-gate {
5027c478bd9Sstevel@tonic-gate 	struct lo_retired_ht *lrhp;
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	lrhp = kmem_alloc(sizeof (*lrhp), KM_SLEEP);
5057c478bd9Sstevel@tonic-gate 	lrhp->lrh_table = table;
5067c478bd9Sstevel@tonic-gate 	lrhp->lrh_size = size;
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	mutex_enter(&li->li_htlock);
5097c478bd9Sstevel@tonic-gate 	lrhp->lrh_next = li->li_retired;
5107c478bd9Sstevel@tonic-gate 	li->li_retired = lrhp;
5117c478bd9Sstevel@tonic-gate 	mutex_exit(&li->li_htlock);
5127c478bd9Sstevel@tonic-gate }
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate /*
5157c478bd9Sstevel@tonic-gate  * Grow the hashtable.
5167c478bd9Sstevel@tonic-gate  */
5177c478bd9Sstevel@tonic-gate static void
lgrow(struct loinfo * li,uint_t newsize)5187c478bd9Sstevel@tonic-gate lgrow(struct loinfo *li, uint_t newsize)
5197c478bd9Sstevel@tonic-gate {
5207c478bd9Sstevel@tonic-gate 	uint_t oldsize;
5217c478bd9Sstevel@tonic-gate 	uint_t i;
5227c478bd9Sstevel@tonic-gate 	struct lobucket *oldtable, *newtable;
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 	/*
5257c478bd9Sstevel@tonic-gate 	 * It's OK to not have enough memory to resize the hashtable.
5267c478bd9Sstevel@tonic-gate 	 * We'll go down this path the next time we add something to the
5277c478bd9Sstevel@tonic-gate 	 * table, and retry the allocation then.
5287c478bd9Sstevel@tonic-gate 	 */
5297c478bd9Sstevel@tonic-gate 	if ((newtable = kmem_zalloc(newsize * sizeof (*li->li_hashtable),
5307c478bd9Sstevel@tonic-gate 	    KM_NOSLEEP)) == NULL)
5317c478bd9Sstevel@tonic-gate 		return;
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 	mutex_enter(&li->li_htlock);
5347c478bd9Sstevel@tonic-gate 	if (newsize <= li->li_htsize) {
5357c478bd9Sstevel@tonic-gate 		mutex_exit(&li->li_htlock);
5367c478bd9Sstevel@tonic-gate 		kmem_free(newtable, newsize * sizeof (*li->li_hashtable));
5377c478bd9Sstevel@tonic-gate 		return;
5387c478bd9Sstevel@tonic-gate 	}
5397c478bd9Sstevel@tonic-gate 	oldsize = li->li_htsize;
5407c478bd9Sstevel@tonic-gate 	oldtable = li->li_hashtable;
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	/*
5437c478bd9Sstevel@tonic-gate 	 * Grab all locks so TABLE_LOCK_ENTER() calls block until the
5447c478bd9Sstevel@tonic-gate 	 * resize is complete.
5457c478bd9Sstevel@tonic-gate 	 */
5467c478bd9Sstevel@tonic-gate 	for (i = 0; i < oldsize; i++)
5477c478bd9Sstevel@tonic-gate 		mutex_enter(&oldtable[i].lh_lock);
5487c478bd9Sstevel@tonic-gate 	/*
5497c478bd9Sstevel@tonic-gate 	 * li->li_hashtable gets set before li->li_htsize, so in the
5507c478bd9Sstevel@tonic-gate 	 * time between the two assignments, callers of
5517c478bd9Sstevel@tonic-gate 	 * TABLE_LOCK_ENTER() cannot hash to a bucket beyond oldsize,
5527c478bd9Sstevel@tonic-gate 	 * hence we only need to grab the locks up to oldsize.
5537c478bd9Sstevel@tonic-gate 	 */
5547c478bd9Sstevel@tonic-gate 	for (i = 0; i < oldsize; i++)
5557c478bd9Sstevel@tonic-gate 		mutex_enter(&newtable[i].lh_lock);
5567c478bd9Sstevel@tonic-gate 	/*
5577c478bd9Sstevel@tonic-gate 	 * Rehash.
5587c478bd9Sstevel@tonic-gate 	 */
5597c478bd9Sstevel@tonic-gate 	for (i = 0; i < oldsize; i++) {
5607c478bd9Sstevel@tonic-gate 		lnode_t *tlp, *nlp;
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 		for (tlp = oldtable[i].lh_chain; tlp != NULL; tlp = nlp) {
5637c478bd9Sstevel@tonic-gate 			uint_t hash = ltablehash(tlp->lo_vp, newsize);
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 			nlp = tlp->lo_next;
5667c478bd9Sstevel@tonic-gate 			tlp->lo_next = newtable[hash].lh_chain;
5677c478bd9Sstevel@tonic-gate 			newtable[hash].lh_chain = tlp;
5687c478bd9Sstevel@tonic-gate 			newtable[hash].lh_count++;
5697c478bd9Sstevel@tonic-gate 		}
5707c478bd9Sstevel@tonic-gate 	}
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 	/*
5737c478bd9Sstevel@tonic-gate 	 * As soon as we store the new hashtable, future locking operations
5747c478bd9Sstevel@tonic-gate 	 * will use it.  Therefore, we must ensure that all the state we've
5757c478bd9Sstevel@tonic-gate 	 * just established reaches global visibility before the new hashtable
5767c478bd9Sstevel@tonic-gate 	 * does.
5777c478bd9Sstevel@tonic-gate 	 */
5787c478bd9Sstevel@tonic-gate 	membar_producer();
5797c478bd9Sstevel@tonic-gate 	li->li_hashtable = newtable;
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 	/*
5827c478bd9Sstevel@tonic-gate 	 * table_lock_enter() relies on the fact that li->li_hashtable
5837c478bd9Sstevel@tonic-gate 	 * is set to its new value before li->li_htsize.
5847c478bd9Sstevel@tonic-gate 	 */
5857c478bd9Sstevel@tonic-gate 	membar_producer();
5867c478bd9Sstevel@tonic-gate 	li->li_htsize = newsize;
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	/*
5897c478bd9Sstevel@tonic-gate 	 * The new state is consistent now, so we can drop all the locks.
5907c478bd9Sstevel@tonic-gate 	 */
5917c478bd9Sstevel@tonic-gate 	for (i = 0; i < oldsize; i++) {
5927c478bd9Sstevel@tonic-gate 		mutex_exit(&newtable[i].lh_lock);
5937c478bd9Sstevel@tonic-gate 		mutex_exit(&oldtable[i].lh_lock);
5947c478bd9Sstevel@tonic-gate 	}
5957c478bd9Sstevel@tonic-gate 	mutex_exit(&li->li_htlock);
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	lretire(li, oldtable, oldsize);
5987c478bd9Sstevel@tonic-gate }
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate /*
6017c478bd9Sstevel@tonic-gate  * Put a lnode in the table
6027c478bd9Sstevel@tonic-gate  */
6037c478bd9Sstevel@tonic-gate static void
lsave(lnode_t * lp,struct loinfo * li)6047c478bd9Sstevel@tonic-gate lsave(lnode_t *lp, struct loinfo *li)
6057c478bd9Sstevel@tonic-gate {
6067c478bd9Sstevel@tonic-gate 	ASSERT(lp->lo_vp);
6077c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(TABLE_LOCK(lp->lo_vp, li)));
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate #ifdef LODEBUG
6107c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lsave lp %p hash %d\n",
61179a28c7aSmarks 	    lp, ltablehash(lp->lo_vp, li));
6127c478bd9Sstevel@tonic-gate #endif
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	TABLE_COUNT(lp->lo_vp, li)++;
6157c478bd9Sstevel@tonic-gate 	lp->lo_next = TABLE_BUCKET(lp->lo_vp, li);
6167c478bd9Sstevel@tonic-gate 	TABLE_BUCKET(lp->lo_vp, li) = lp;
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 	if (li->li_refct > (li->li_htsize << lo_resize_threshold)) {
6197c478bd9Sstevel@tonic-gate 		TABLE_LOCK_EXIT(lp->lo_vp, li);
6207c478bd9Sstevel@tonic-gate 		lgrow(li, li->li_htsize << lo_resize_factor);
6217c478bd9Sstevel@tonic-gate 		TABLE_LOCK_ENTER(lp->lo_vp, li);
6227c478bd9Sstevel@tonic-gate 	}
6237c478bd9Sstevel@tonic-gate }
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate /*
6267c478bd9Sstevel@tonic-gate  * Our version of vfs_rele() that stops at 1 instead of 0, and calls
6277c478bd9Sstevel@tonic-gate  * freelfsnode() instead of kmem_free().
6287c478bd9Sstevel@tonic-gate  */
6297c478bd9Sstevel@tonic-gate static void
lfs_rele(struct lfsnode * lfs,struct loinfo * li)6307c478bd9Sstevel@tonic-gate lfs_rele(struct lfsnode *lfs, struct loinfo *li)
6317c478bd9Sstevel@tonic-gate {
6327c478bd9Sstevel@tonic-gate 	vfs_t *vfsp = &lfs->lfs_vfs;
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&li->li_lfslock));
6357c478bd9Sstevel@tonic-gate 	ASSERT(vfsp->vfs_count > 1);
6361a5e258fSJosef 'Jeff' Sipek 	if (atomic_dec_32_nv(&vfsp->vfs_count) == 1)
6377c478bd9Sstevel@tonic-gate 		freelfsnode(lfs, li);
6387c478bd9Sstevel@tonic-gate }
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate /*
6417c478bd9Sstevel@tonic-gate  * Remove a lnode from the table
6427c478bd9Sstevel@tonic-gate  */
6437c478bd9Sstevel@tonic-gate void
freelonode(lnode_t * lp)6447c478bd9Sstevel@tonic-gate freelonode(lnode_t *lp)
6457c478bd9Sstevel@tonic-gate {
6467c478bd9Sstevel@tonic-gate 	lnode_t *lt;
6477c478bd9Sstevel@tonic-gate 	lnode_t *ltprev = NULL;
6487c478bd9Sstevel@tonic-gate 	struct lfsnode *lfs, *nextlfs;
6497c478bd9Sstevel@tonic-gate 	struct vfs *vfsp;
6507c478bd9Sstevel@tonic-gate 	struct vnode *vp = ltov(lp);
6517c478bd9Sstevel@tonic-gate 	struct vnode *realvp = realvp(vp);
6527c478bd9Sstevel@tonic-gate 	struct loinfo *li = vtoli(vp->v_vfsp);
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate #ifdef LODEBUG
6557c478bd9Sstevel@tonic-gate 	lo_dprint(4, "freelonode lp %p hash %d\n",
65679a28c7aSmarks 	    lp, ltablehash(lp->lo_vp, li));
6577c478bd9Sstevel@tonic-gate #endif
6587c478bd9Sstevel@tonic-gate 	TABLE_LOCK_ENTER(lp->lo_vp, li);
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
6617c478bd9Sstevel@tonic-gate 	if (vp->v_count > 1) {
662*ade42b55SSebastien Roy 		VN_RELE_LOCKED(vp);
6637c478bd9Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
6647c478bd9Sstevel@tonic-gate 		TABLE_LOCK_EXIT(lp->lo_vp, li);
6657c478bd9Sstevel@tonic-gate 		return;
6667c478bd9Sstevel@tonic-gate 	}
6677c478bd9Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 	for (lt = TABLE_BUCKET(lp->lo_vp, li); lt != NULL;
6707c478bd9Sstevel@tonic-gate 	    ltprev = lt, lt = lt->lo_next) {
6717c478bd9Sstevel@tonic-gate 		if (lt == lp) {
6727c478bd9Sstevel@tonic-gate #ifdef LODEBUG
6737c478bd9Sstevel@tonic-gate 			lo_dprint(4, "freeing %p, vfsp %p\n",
67479a28c7aSmarks 			    vp, vp->v_vfsp);
6757c478bd9Sstevel@tonic-gate #endif
6761a5e258fSJosef 'Jeff' Sipek 			atomic_dec_32(&li->li_refct);
6777c478bd9Sstevel@tonic-gate 			vfsp = vp->v_vfsp;
6787c478bd9Sstevel@tonic-gate 			vn_invalid(vp);
6797c478bd9Sstevel@tonic-gate 			if (vfsp != li->li_mountvfs) {
6807c478bd9Sstevel@tonic-gate 				mutex_enter(&li->li_lfslock);
6817c478bd9Sstevel@tonic-gate 				/*
6827c478bd9Sstevel@tonic-gate 				 * Check for unused lfs
6837c478bd9Sstevel@tonic-gate 				 */
6847c478bd9Sstevel@tonic-gate 				lfs = li->li_lfs;
6857c478bd9Sstevel@tonic-gate 				while (lfs != NULL) {
6867c478bd9Sstevel@tonic-gate 					nextlfs = lfs->lfs_next;
6877c478bd9Sstevel@tonic-gate 					if (vfsp == &lfs->lfs_vfs) {
6887c478bd9Sstevel@tonic-gate 						lfs_rele(lfs, li);
6897c478bd9Sstevel@tonic-gate 						break;
6907c478bd9Sstevel@tonic-gate 					}
6917c478bd9Sstevel@tonic-gate 					if (lfs->lfs_vfs.vfs_count == 1) {
6927c478bd9Sstevel@tonic-gate 						/*
6937c478bd9Sstevel@tonic-gate 						 * Lfs is idle
6947c478bd9Sstevel@tonic-gate 						 */
6957c478bd9Sstevel@tonic-gate 						freelfsnode(lfs, li);
6967c478bd9Sstevel@tonic-gate 					}
6977c478bd9Sstevel@tonic-gate 					lfs = nextlfs;
6987c478bd9Sstevel@tonic-gate 				}
6997c478bd9Sstevel@tonic-gate 				mutex_exit(&li->li_lfslock);
7007c478bd9Sstevel@tonic-gate 			}
7017c478bd9Sstevel@tonic-gate 			if (ltprev == NULL) {
7027c478bd9Sstevel@tonic-gate 				TABLE_BUCKET(lt->lo_vp, li) = lt->lo_next;
7037c478bd9Sstevel@tonic-gate 			} else {
7047c478bd9Sstevel@tonic-gate 				ltprev->lo_next = lt->lo_next;
7057c478bd9Sstevel@tonic-gate 			}
7067c478bd9Sstevel@tonic-gate 			TABLE_COUNT(lt->lo_vp, li)--;
7077c478bd9Sstevel@tonic-gate 			TABLE_LOCK_EXIT(lt->lo_vp, li);
7087c478bd9Sstevel@tonic-gate 			kmem_cache_free(lnode_cache, lt);
7097c478bd9Sstevel@tonic-gate 			vn_free(vp);
7107c478bd9Sstevel@tonic-gate 			VN_RELE(realvp);
7117c478bd9Sstevel@tonic-gate 			return;
7127c478bd9Sstevel@tonic-gate 		}
7137c478bd9Sstevel@tonic-gate 	}
7147c478bd9Sstevel@tonic-gate 	panic("freelonode");
7157c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
7167c478bd9Sstevel@tonic-gate }
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate /*
7197c478bd9Sstevel@tonic-gate  * Lookup a lnode by vp
7207c478bd9Sstevel@tonic-gate  */
7217c478bd9Sstevel@tonic-gate static lnode_t *
lfind(struct vnode * vp,struct loinfo * li)7227c478bd9Sstevel@tonic-gate lfind(struct vnode *vp, struct loinfo *li)
7237c478bd9Sstevel@tonic-gate {
7247c478bd9Sstevel@tonic-gate 	lnode_t *lt;
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(TABLE_LOCK(vp, li)));
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 	lt = TABLE_BUCKET(vp, li);
7297c478bd9Sstevel@tonic-gate 	while (lt != NULL) {
7307c478bd9Sstevel@tonic-gate 		if (lt->lo_vp == vp) {
7317c478bd9Sstevel@tonic-gate 			VN_HOLD(ltov(lt));
7327c478bd9Sstevel@tonic-gate 			return (lt);
7337c478bd9Sstevel@tonic-gate 		}
7347c478bd9Sstevel@tonic-gate 		lt = lt->lo_next;
7357c478bd9Sstevel@tonic-gate 	}
7367c478bd9Sstevel@tonic-gate 	return (NULL);
7377c478bd9Sstevel@tonic-gate }
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate #ifdef	LODEBUG
7407c478bd9Sstevel@tonic-gate static int lofsdebug;
7417c478bd9Sstevel@tonic-gate #endif	/* LODEBUG */
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate /*
7447c478bd9Sstevel@tonic-gate  * Utilities used by both client and server
7457c478bd9Sstevel@tonic-gate  * Standard levels:
7467c478bd9Sstevel@tonic-gate  * 0) no debugging
7477c478bd9Sstevel@tonic-gate  * 1) hard failures
7487c478bd9Sstevel@tonic-gate  * 2) soft failures
7497c478bd9Sstevel@tonic-gate  * 3) current test software
7507c478bd9Sstevel@tonic-gate  * 4) main procedure entry points
7517c478bd9Sstevel@tonic-gate  * 5) main procedure exit points
7527c478bd9Sstevel@tonic-gate  * 6) utility procedure entry points
7537c478bd9Sstevel@tonic-gate  * 7) utility procedure exit points
7547c478bd9Sstevel@tonic-gate  * 8) obscure procedure entry points
7557c478bd9Sstevel@tonic-gate  * 9) obscure procedure exit points
7567c478bd9Sstevel@tonic-gate  * 10) random stuff
7577c478bd9Sstevel@tonic-gate  * 11) all <= 1
7587c478bd9Sstevel@tonic-gate  * 12) all <= 2
7597c478bd9Sstevel@tonic-gate  * 13) all <= 3
7607c478bd9Sstevel@tonic-gate  * ...
7617c478bd9Sstevel@tonic-gate  */
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate #ifdef LODEBUG
7647c478bd9Sstevel@tonic-gate /*VARARGS2*/
lo_dprint(int level,char * str,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8,int a9)765*ade42b55SSebastien Roy lo_dprint(int level, char *str, int a1, int a2, int a3, int a4, int a5, int a6,
766*ade42b55SSebastien Roy     int a7, int a8, int a9)
7677c478bd9Sstevel@tonic-gate {
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate 	if (lofsdebug == level || (lofsdebug > 10 && (lofsdebug - 10) >= level))
7707c478bd9Sstevel@tonic-gate 		printf(str, a1, a2, a3, a4, a5, a6, a7, a8, a9);
7717c478bd9Sstevel@tonic-gate }
7727c478bd9Sstevel@tonic-gate #endif
773