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