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 57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 77c478bd9Sstevel@tonic-gate * with the License. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 127c478bd9Sstevel@tonic-gate * and limitations under the License. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 197c478bd9Sstevel@tonic-gate * 207c478bd9Sstevel@tonic-gate * CDDL HEADER END 217c478bd9Sstevel@tonic-gate */ 227c478bd9Sstevel@tonic-gate /* 23*b431137cSowenr * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 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 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 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 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 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 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 246*b431137cSowenr * table when lo_inactive calls freelonode. The creation of 247*b431137cSowenr * a new lnode can be forced via the LOF_FORCE flag even if 248*b431137cSowenr * the vnode exists in the table. This is used in the creation 249*b431137cSowenr * of a terminating lnode when looping is detected. A unique 250*b431137cSowenr * lnode is required for the correct evaluation of the current 251*b431137cSowenr * working directory. 2527c478bd9Sstevel@tonic-gate * NOTE: vp is assumed to be a held vnode. 2537c478bd9Sstevel@tonic-gate */ 2547c478bd9Sstevel@tonic-gate struct vnode * 255*b431137cSowenr 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 261*b431137cSowenr lp = NULL; 2627c478bd9Sstevel@tonic-gate TABLE_LOCK_ENTER(vp, li); 263*b431137cSowenr if (flag != LOF_FORCE) 264*b431137cSowenr lp = lfind(vp, li); 265*b431137cSowenr 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 } 281*b431137cSowenr lp = NULL; 2827c478bd9Sstevel@tonic-gate TABLE_LOCK_ENTER(vp, li); 283*b431137cSowenr if (flag != LOF_FORCE) 284*b431137cSowenr lp = lfind(vp, li); 285*b431137cSowenr 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 } 2937c478bd9Sstevel@tonic-gate atomic_add_32(&li->li_refct, 1); 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 * 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 = 3487c478bd9Sstevel@tonic-gate ((vfsp->vfs_flag | li->li_mflag) & ~li->li_dflag) & 3497c478bd9Sstevel@tonic-gate 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; 3707c478bd9Sstevel@tonic-gate } 3717c478bd9Sstevel@tonic-gate 3727c478bd9Sstevel@tonic-gate found_lfs: 3737c478bd9Sstevel@tonic-gate VFS_HOLD(&lfs->lfs_vfs); 3747c478bd9Sstevel@tonic-gate mutex_exit(&li->li_lfslock); 3757c478bd9Sstevel@tonic-gate return (&lfs->lfs_vfs); 3767c478bd9Sstevel@tonic-gate } 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate /* 3797c478bd9Sstevel@tonic-gate * Free lfs node since no longer in use 3807c478bd9Sstevel@tonic-gate */ 3817c478bd9Sstevel@tonic-gate static void 3827c478bd9Sstevel@tonic-gate freelfsnode(struct lfsnode *lfs, struct loinfo *li) 3837c478bd9Sstevel@tonic-gate { 3847c478bd9Sstevel@tonic-gate struct lfsnode *prev = NULL; 3857c478bd9Sstevel@tonic-gate struct lfsnode *this; 3867c478bd9Sstevel@tonic-gate 3877c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&li->li_lfslock)); 3887c478bd9Sstevel@tonic-gate ASSERT(li->li_refct > 0); 3897c478bd9Sstevel@tonic-gate for (this = li->li_lfs; this != NULL; this = this->lfs_next) { 3907c478bd9Sstevel@tonic-gate if (this == lfs) { 3917c478bd9Sstevel@tonic-gate ASSERT(lfs->lfs_vfs.vfs_count == 1); 3927c478bd9Sstevel@tonic-gate if (prev == NULL) 3937c478bd9Sstevel@tonic-gate li->li_lfs = lfs->lfs_next; 3947c478bd9Sstevel@tonic-gate else 3957c478bd9Sstevel@tonic-gate prev->lfs_next = lfs->lfs_next; 3967c478bd9Sstevel@tonic-gate if (lfs->lfs_realrootvp != NULL) { 3977c478bd9Sstevel@tonic-gate VN_RELE(lfs->lfs_realrootvp); 3987c478bd9Sstevel@tonic-gate } 3997c478bd9Sstevel@tonic-gate if (lfs->lfs_vfs.vfs_mntpt != NULL) 4007c478bd9Sstevel@tonic-gate refstr_rele(lfs->lfs_vfs.vfs_mntpt); 4017c478bd9Sstevel@tonic-gate sema_destroy(&lfs->lfs_vfs.vfs_reflock); 4027c478bd9Sstevel@tonic-gate kmem_free(lfs, sizeof (struct lfsnode)); 4037c478bd9Sstevel@tonic-gate return; 4047c478bd9Sstevel@tonic-gate } 4057c478bd9Sstevel@tonic-gate prev = this; 4067c478bd9Sstevel@tonic-gate } 4077c478bd9Sstevel@tonic-gate panic("freelfsnode"); 4087c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 4097c478bd9Sstevel@tonic-gate } 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate /* 4127c478bd9Sstevel@tonic-gate * Find lfs given real vfs and mount instance(li) 4137c478bd9Sstevel@tonic-gate */ 4147c478bd9Sstevel@tonic-gate static struct lfsnode * 4157c478bd9Sstevel@tonic-gate lfsfind(struct vfs *vfsp, struct loinfo *li) 4167c478bd9Sstevel@tonic-gate { 4177c478bd9Sstevel@tonic-gate struct lfsnode *lfs; 4187c478bd9Sstevel@tonic-gate 4197c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&li->li_lfslock)); 4207c478bd9Sstevel@tonic-gate 4217c478bd9Sstevel@tonic-gate /* 4227c478bd9Sstevel@tonic-gate * We need to handle the case where a UFS filesystem was forced 4237c478bd9Sstevel@tonic-gate * unmounted and then a subsequent mount got the same vfs 4247c478bd9Sstevel@tonic-gate * structure. If the new mount lies in the lofs hierarchy, then 4257c478bd9Sstevel@tonic-gate * this will confuse lofs, because the original vfsp (of the 4267c478bd9Sstevel@tonic-gate * forced unmounted filesystem) is still around. We check for 4277c478bd9Sstevel@tonic-gate * this condition here. 4287c478bd9Sstevel@tonic-gate * 4297c478bd9Sstevel@tonic-gate * If we find a cache vfsp hit, then we check to see if the 4307c478bd9Sstevel@tonic-gate * cached filesystem was forced unmounted. Skip all such 4317c478bd9Sstevel@tonic-gate * entries. This should be safe to do since no 4327c478bd9Sstevel@tonic-gate * makelonode()->makelfsnode()->lfsfind() calls should be 4337c478bd9Sstevel@tonic-gate * generated for such force-unmounted filesystems (because (ufs) 4347c478bd9Sstevel@tonic-gate * lookup would've returned an error). 4357c478bd9Sstevel@tonic-gate */ 4367c478bd9Sstevel@tonic-gate for (lfs = li->li_lfs; lfs != NULL; lfs = lfs->lfs_next) { 4377c478bd9Sstevel@tonic-gate if (lfs->lfs_realvfs == vfsp) { 4387c478bd9Sstevel@tonic-gate struct vnode *realvp; 4397c478bd9Sstevel@tonic-gate 4407c478bd9Sstevel@tonic-gate realvp = lfs->lfs_realrootvp; 4417c478bd9Sstevel@tonic-gate if (realvp == NULL) 4427c478bd9Sstevel@tonic-gate continue; 4437c478bd9Sstevel@tonic-gate if (realvp->v_vfsp == NULL || realvp->v_type == VBAD) 4447c478bd9Sstevel@tonic-gate continue; 4457c478bd9Sstevel@tonic-gate return (lfs); 4467c478bd9Sstevel@tonic-gate } 4477c478bd9Sstevel@tonic-gate } 4487c478bd9Sstevel@tonic-gate return (NULL); 4497c478bd9Sstevel@tonic-gate } 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate /* 4527c478bd9Sstevel@tonic-gate * Find real vfs given loopback vfs 4537c478bd9Sstevel@tonic-gate */ 4547c478bd9Sstevel@tonic-gate struct vfs * 4557c478bd9Sstevel@tonic-gate lo_realvfs(struct vfs *vfsp, struct vnode **realrootvpp) 4567c478bd9Sstevel@tonic-gate { 4577c478bd9Sstevel@tonic-gate struct loinfo *li = vtoli(vfsp); 4587c478bd9Sstevel@tonic-gate struct lfsnode *lfs; 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate ASSERT(li->li_refct > 0); 4617c478bd9Sstevel@tonic-gate if (vfsp == li->li_mountvfs) { 4627c478bd9Sstevel@tonic-gate if (realrootvpp != NULL) 4637c478bd9Sstevel@tonic-gate *realrootvpp = vtol(li->li_rootvp)->lo_vp; 4647c478bd9Sstevel@tonic-gate return (li->li_realvfs); 4657c478bd9Sstevel@tonic-gate } 4667c478bd9Sstevel@tonic-gate mutex_enter(&li->li_lfslock); 4677c478bd9Sstevel@tonic-gate for (lfs = li->li_lfs; lfs != NULL; lfs = lfs->lfs_next) { 4687c478bd9Sstevel@tonic-gate if (vfsp == &lfs->lfs_vfs) { 4697c478bd9Sstevel@tonic-gate if (realrootvpp != NULL) 4707c478bd9Sstevel@tonic-gate *realrootvpp = lfs->lfs_realrootvp; 4717c478bd9Sstevel@tonic-gate mutex_exit(&li->li_lfslock); 4727c478bd9Sstevel@tonic-gate return (lfs->lfs_realvfs); 4737c478bd9Sstevel@tonic-gate } 4747c478bd9Sstevel@tonic-gate } 4757c478bd9Sstevel@tonic-gate panic("lo_realvfs"); 4767c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 4777c478bd9Sstevel@tonic-gate } 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate /* 4807c478bd9Sstevel@tonic-gate * Lnode lookup stuff. 4817c478bd9Sstevel@tonic-gate * These routines maintain a table of lnodes hashed by vp so 4827c478bd9Sstevel@tonic-gate * that the lnode for a vp can be found if it already exists. 4837c478bd9Sstevel@tonic-gate * 4847c478bd9Sstevel@tonic-gate * NB: A lofs shadow vnode causes exactly one VN_HOLD() on the 4857c478bd9Sstevel@tonic-gate * underlying vnode. 4867c478bd9Sstevel@tonic-gate */ 4877c478bd9Sstevel@tonic-gate 4887c478bd9Sstevel@tonic-gate /* 4897c478bd9Sstevel@tonic-gate * Retire old hashtables. 4907c478bd9Sstevel@tonic-gate */ 4917c478bd9Sstevel@tonic-gate static void 4927c478bd9Sstevel@tonic-gate lretire(struct loinfo *li, struct lobucket *table, uint_t size) 4937c478bd9Sstevel@tonic-gate { 4947c478bd9Sstevel@tonic-gate struct lo_retired_ht *lrhp; 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate lrhp = kmem_alloc(sizeof (*lrhp), KM_SLEEP); 4977c478bd9Sstevel@tonic-gate lrhp->lrh_table = table; 4987c478bd9Sstevel@tonic-gate lrhp->lrh_size = size; 4997c478bd9Sstevel@tonic-gate 5007c478bd9Sstevel@tonic-gate mutex_enter(&li->li_htlock); 5017c478bd9Sstevel@tonic-gate lrhp->lrh_next = li->li_retired; 5027c478bd9Sstevel@tonic-gate li->li_retired = lrhp; 5037c478bd9Sstevel@tonic-gate mutex_exit(&li->li_htlock); 5047c478bd9Sstevel@tonic-gate } 5057c478bd9Sstevel@tonic-gate 5067c478bd9Sstevel@tonic-gate /* 5077c478bd9Sstevel@tonic-gate * Grow the hashtable. 5087c478bd9Sstevel@tonic-gate */ 5097c478bd9Sstevel@tonic-gate static void 5107c478bd9Sstevel@tonic-gate lgrow(struct loinfo *li, uint_t newsize) 5117c478bd9Sstevel@tonic-gate { 5127c478bd9Sstevel@tonic-gate uint_t oldsize; 5137c478bd9Sstevel@tonic-gate uint_t i; 5147c478bd9Sstevel@tonic-gate struct lobucket *oldtable, *newtable; 5157c478bd9Sstevel@tonic-gate 5167c478bd9Sstevel@tonic-gate /* 5177c478bd9Sstevel@tonic-gate * It's OK to not have enough memory to resize the hashtable. 5187c478bd9Sstevel@tonic-gate * We'll go down this path the next time we add something to the 5197c478bd9Sstevel@tonic-gate * table, and retry the allocation then. 5207c478bd9Sstevel@tonic-gate */ 5217c478bd9Sstevel@tonic-gate if ((newtable = kmem_zalloc(newsize * sizeof (*li->li_hashtable), 5227c478bd9Sstevel@tonic-gate KM_NOSLEEP)) == NULL) 5237c478bd9Sstevel@tonic-gate return; 5247c478bd9Sstevel@tonic-gate 5257c478bd9Sstevel@tonic-gate mutex_enter(&li->li_htlock); 5267c478bd9Sstevel@tonic-gate if (newsize <= li->li_htsize) { 5277c478bd9Sstevel@tonic-gate mutex_exit(&li->li_htlock); 5287c478bd9Sstevel@tonic-gate kmem_free(newtable, newsize * sizeof (*li->li_hashtable)); 5297c478bd9Sstevel@tonic-gate return; 5307c478bd9Sstevel@tonic-gate } 5317c478bd9Sstevel@tonic-gate oldsize = li->li_htsize; 5327c478bd9Sstevel@tonic-gate oldtable = li->li_hashtable; 5337c478bd9Sstevel@tonic-gate 5347c478bd9Sstevel@tonic-gate /* 5357c478bd9Sstevel@tonic-gate * Grab all locks so TABLE_LOCK_ENTER() calls block until the 5367c478bd9Sstevel@tonic-gate * resize is complete. 5377c478bd9Sstevel@tonic-gate */ 5387c478bd9Sstevel@tonic-gate for (i = 0; i < oldsize; i++) 5397c478bd9Sstevel@tonic-gate mutex_enter(&oldtable[i].lh_lock); 5407c478bd9Sstevel@tonic-gate /* 5417c478bd9Sstevel@tonic-gate * li->li_hashtable gets set before li->li_htsize, so in the 5427c478bd9Sstevel@tonic-gate * time between the two assignments, callers of 5437c478bd9Sstevel@tonic-gate * TABLE_LOCK_ENTER() cannot hash to a bucket beyond oldsize, 5447c478bd9Sstevel@tonic-gate * hence we only need to grab the locks up to oldsize. 5457c478bd9Sstevel@tonic-gate */ 5467c478bd9Sstevel@tonic-gate for (i = 0; i < oldsize; i++) 5477c478bd9Sstevel@tonic-gate mutex_enter(&newtable[i].lh_lock); 5487c478bd9Sstevel@tonic-gate /* 5497c478bd9Sstevel@tonic-gate * Rehash. 5507c478bd9Sstevel@tonic-gate */ 5517c478bd9Sstevel@tonic-gate for (i = 0; i < oldsize; i++) { 5527c478bd9Sstevel@tonic-gate lnode_t *tlp, *nlp; 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate for (tlp = oldtable[i].lh_chain; tlp != NULL; tlp = nlp) { 5557c478bd9Sstevel@tonic-gate uint_t hash = ltablehash(tlp->lo_vp, newsize); 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate nlp = tlp->lo_next; 5587c478bd9Sstevel@tonic-gate tlp->lo_next = newtable[hash].lh_chain; 5597c478bd9Sstevel@tonic-gate newtable[hash].lh_chain = tlp; 5607c478bd9Sstevel@tonic-gate newtable[hash].lh_count++; 5617c478bd9Sstevel@tonic-gate } 5627c478bd9Sstevel@tonic-gate } 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate /* 5657c478bd9Sstevel@tonic-gate * As soon as we store the new hashtable, future locking operations 5667c478bd9Sstevel@tonic-gate * will use it. Therefore, we must ensure that all the state we've 5677c478bd9Sstevel@tonic-gate * just established reaches global visibility before the new hashtable 5687c478bd9Sstevel@tonic-gate * does. 5697c478bd9Sstevel@tonic-gate */ 5707c478bd9Sstevel@tonic-gate membar_producer(); 5717c478bd9Sstevel@tonic-gate li->li_hashtable = newtable; 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate /* 5747c478bd9Sstevel@tonic-gate * table_lock_enter() relies on the fact that li->li_hashtable 5757c478bd9Sstevel@tonic-gate * is set to its new value before li->li_htsize. 5767c478bd9Sstevel@tonic-gate */ 5777c478bd9Sstevel@tonic-gate membar_producer(); 5787c478bd9Sstevel@tonic-gate li->li_htsize = newsize; 5797c478bd9Sstevel@tonic-gate 5807c478bd9Sstevel@tonic-gate /* 5817c478bd9Sstevel@tonic-gate * The new state is consistent now, so we can drop all the locks. 5827c478bd9Sstevel@tonic-gate */ 5837c478bd9Sstevel@tonic-gate for (i = 0; i < oldsize; i++) { 5847c478bd9Sstevel@tonic-gate mutex_exit(&newtable[i].lh_lock); 5857c478bd9Sstevel@tonic-gate mutex_exit(&oldtable[i].lh_lock); 5867c478bd9Sstevel@tonic-gate } 5877c478bd9Sstevel@tonic-gate mutex_exit(&li->li_htlock); 5887c478bd9Sstevel@tonic-gate 5897c478bd9Sstevel@tonic-gate lretire(li, oldtable, oldsize); 5907c478bd9Sstevel@tonic-gate } 5917c478bd9Sstevel@tonic-gate 5927c478bd9Sstevel@tonic-gate /* 5937c478bd9Sstevel@tonic-gate * Put a lnode in the table 5947c478bd9Sstevel@tonic-gate */ 5957c478bd9Sstevel@tonic-gate static void 5967c478bd9Sstevel@tonic-gate lsave(lnode_t *lp, struct loinfo *li) 5977c478bd9Sstevel@tonic-gate { 5987c478bd9Sstevel@tonic-gate ASSERT(lp->lo_vp); 5997c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(TABLE_LOCK(lp->lo_vp, li))); 6007c478bd9Sstevel@tonic-gate 6017c478bd9Sstevel@tonic-gate #ifdef LODEBUG 6027c478bd9Sstevel@tonic-gate lo_dprint(4, "lsave lp %p hash %d\n", 6037c478bd9Sstevel@tonic-gate lp, ltablehash(lp->lo_vp, li)); 6047c478bd9Sstevel@tonic-gate #endif 6057c478bd9Sstevel@tonic-gate 6067c478bd9Sstevel@tonic-gate TABLE_COUNT(lp->lo_vp, li)++; 6077c478bd9Sstevel@tonic-gate lp->lo_next = TABLE_BUCKET(lp->lo_vp, li); 6087c478bd9Sstevel@tonic-gate TABLE_BUCKET(lp->lo_vp, li) = lp; 6097c478bd9Sstevel@tonic-gate 6107c478bd9Sstevel@tonic-gate if (li->li_refct > (li->li_htsize << lo_resize_threshold)) { 6117c478bd9Sstevel@tonic-gate TABLE_LOCK_EXIT(lp->lo_vp, li); 6127c478bd9Sstevel@tonic-gate lgrow(li, li->li_htsize << lo_resize_factor); 6137c478bd9Sstevel@tonic-gate TABLE_LOCK_ENTER(lp->lo_vp, li); 6147c478bd9Sstevel@tonic-gate } 6157c478bd9Sstevel@tonic-gate } 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate /* 6187c478bd9Sstevel@tonic-gate * Our version of vfs_rele() that stops at 1 instead of 0, and calls 6197c478bd9Sstevel@tonic-gate * freelfsnode() instead of kmem_free(). 6207c478bd9Sstevel@tonic-gate */ 6217c478bd9Sstevel@tonic-gate static void 6227c478bd9Sstevel@tonic-gate lfs_rele(struct lfsnode *lfs, struct loinfo *li) 6237c478bd9Sstevel@tonic-gate { 6247c478bd9Sstevel@tonic-gate vfs_t *vfsp = &lfs->lfs_vfs; 6257c478bd9Sstevel@tonic-gate 6267c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&li->li_lfslock)); 6277c478bd9Sstevel@tonic-gate ASSERT(vfsp->vfs_count > 1); 6287c478bd9Sstevel@tonic-gate if (atomic_add_32_nv(&vfsp->vfs_count, -1) == 1) 6297c478bd9Sstevel@tonic-gate freelfsnode(lfs, li); 6307c478bd9Sstevel@tonic-gate } 6317c478bd9Sstevel@tonic-gate 6327c478bd9Sstevel@tonic-gate /* 6337c478bd9Sstevel@tonic-gate * Remove a lnode from the table 6347c478bd9Sstevel@tonic-gate */ 6357c478bd9Sstevel@tonic-gate void 6367c478bd9Sstevel@tonic-gate freelonode(lnode_t *lp) 6377c478bd9Sstevel@tonic-gate { 6387c478bd9Sstevel@tonic-gate lnode_t *lt; 6397c478bd9Sstevel@tonic-gate lnode_t *ltprev = NULL; 6407c478bd9Sstevel@tonic-gate struct lfsnode *lfs, *nextlfs; 6417c478bd9Sstevel@tonic-gate struct vfs *vfsp; 6427c478bd9Sstevel@tonic-gate struct vnode *vp = ltov(lp); 6437c478bd9Sstevel@tonic-gate struct vnode *realvp = realvp(vp); 6447c478bd9Sstevel@tonic-gate struct loinfo *li = vtoli(vp->v_vfsp); 6457c478bd9Sstevel@tonic-gate 6467c478bd9Sstevel@tonic-gate #ifdef LODEBUG 6477c478bd9Sstevel@tonic-gate lo_dprint(4, "freelonode lp %p hash %d\n", 6487c478bd9Sstevel@tonic-gate lp, ltablehash(lp->lo_vp, li)); 6497c478bd9Sstevel@tonic-gate #endif 6507c478bd9Sstevel@tonic-gate TABLE_LOCK_ENTER(lp->lo_vp, li); 6517c478bd9Sstevel@tonic-gate 6527c478bd9Sstevel@tonic-gate mutex_enter(&vp->v_lock); 6537c478bd9Sstevel@tonic-gate if (vp->v_count > 1) { 6547c478bd9Sstevel@tonic-gate vp->v_count--; /* release our hold from vn_rele */ 6557c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 6567c478bd9Sstevel@tonic-gate TABLE_LOCK_EXIT(lp->lo_vp, li); 6577c478bd9Sstevel@tonic-gate return; 6587c478bd9Sstevel@tonic-gate } 6597c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 6607c478bd9Sstevel@tonic-gate 6617c478bd9Sstevel@tonic-gate for (lt = TABLE_BUCKET(lp->lo_vp, li); lt != NULL; 6627c478bd9Sstevel@tonic-gate ltprev = lt, lt = lt->lo_next) { 6637c478bd9Sstevel@tonic-gate if (lt == lp) { 6647c478bd9Sstevel@tonic-gate #ifdef LODEBUG 6657c478bd9Sstevel@tonic-gate lo_dprint(4, "freeing %p, vfsp %p\n", 6667c478bd9Sstevel@tonic-gate vp, vp->v_vfsp); 6677c478bd9Sstevel@tonic-gate #endif 6687c478bd9Sstevel@tonic-gate atomic_add_32(&li->li_refct, -1); 6697c478bd9Sstevel@tonic-gate vfsp = vp->v_vfsp; 6707c478bd9Sstevel@tonic-gate vn_invalid(vp); 6717c478bd9Sstevel@tonic-gate if (vfsp != li->li_mountvfs) { 6727c478bd9Sstevel@tonic-gate mutex_enter(&li->li_lfslock); 6737c478bd9Sstevel@tonic-gate /* 6747c478bd9Sstevel@tonic-gate * Check for unused lfs 6757c478bd9Sstevel@tonic-gate */ 6767c478bd9Sstevel@tonic-gate lfs = li->li_lfs; 6777c478bd9Sstevel@tonic-gate while (lfs != NULL) { 6787c478bd9Sstevel@tonic-gate nextlfs = lfs->lfs_next; 6797c478bd9Sstevel@tonic-gate if (vfsp == &lfs->lfs_vfs) { 6807c478bd9Sstevel@tonic-gate lfs_rele(lfs, li); 6817c478bd9Sstevel@tonic-gate break; 6827c478bd9Sstevel@tonic-gate } 6837c478bd9Sstevel@tonic-gate if (lfs->lfs_vfs.vfs_count == 1) { 6847c478bd9Sstevel@tonic-gate /* 6857c478bd9Sstevel@tonic-gate * Lfs is idle 6867c478bd9Sstevel@tonic-gate */ 6877c478bd9Sstevel@tonic-gate freelfsnode(lfs, li); 6887c478bd9Sstevel@tonic-gate } 6897c478bd9Sstevel@tonic-gate lfs = nextlfs; 6907c478bd9Sstevel@tonic-gate } 6917c478bd9Sstevel@tonic-gate mutex_exit(&li->li_lfslock); 6927c478bd9Sstevel@tonic-gate } 6937c478bd9Sstevel@tonic-gate if (ltprev == NULL) { 6947c478bd9Sstevel@tonic-gate TABLE_BUCKET(lt->lo_vp, li) = lt->lo_next; 6957c478bd9Sstevel@tonic-gate } else { 6967c478bd9Sstevel@tonic-gate ltprev->lo_next = lt->lo_next; 6977c478bd9Sstevel@tonic-gate } 6987c478bd9Sstevel@tonic-gate TABLE_COUNT(lt->lo_vp, li)--; 6997c478bd9Sstevel@tonic-gate TABLE_LOCK_EXIT(lt->lo_vp, li); 7007c478bd9Sstevel@tonic-gate kmem_cache_free(lnode_cache, lt); 7017c478bd9Sstevel@tonic-gate vn_free(vp); 7027c478bd9Sstevel@tonic-gate VN_RELE(realvp); 7037c478bd9Sstevel@tonic-gate return; 7047c478bd9Sstevel@tonic-gate } 7057c478bd9Sstevel@tonic-gate } 7067c478bd9Sstevel@tonic-gate panic("freelonode"); 7077c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 7087c478bd9Sstevel@tonic-gate } 7097c478bd9Sstevel@tonic-gate 7107c478bd9Sstevel@tonic-gate /* 7117c478bd9Sstevel@tonic-gate * Lookup a lnode by vp 7127c478bd9Sstevel@tonic-gate */ 7137c478bd9Sstevel@tonic-gate static lnode_t * 7147c478bd9Sstevel@tonic-gate lfind(struct vnode *vp, struct loinfo *li) 7157c478bd9Sstevel@tonic-gate { 7167c478bd9Sstevel@tonic-gate lnode_t *lt; 7177c478bd9Sstevel@tonic-gate 7187c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(TABLE_LOCK(vp, li))); 7197c478bd9Sstevel@tonic-gate 7207c478bd9Sstevel@tonic-gate lt = TABLE_BUCKET(vp, li); 7217c478bd9Sstevel@tonic-gate while (lt != NULL) { 7227c478bd9Sstevel@tonic-gate if (lt->lo_vp == vp) { 7237c478bd9Sstevel@tonic-gate VN_HOLD(ltov(lt)); 7247c478bd9Sstevel@tonic-gate return (lt); 7257c478bd9Sstevel@tonic-gate } 7267c478bd9Sstevel@tonic-gate lt = lt->lo_next; 7277c478bd9Sstevel@tonic-gate } 7287c478bd9Sstevel@tonic-gate return (NULL); 7297c478bd9Sstevel@tonic-gate } 7307c478bd9Sstevel@tonic-gate 7317c478bd9Sstevel@tonic-gate #ifdef LODEBUG 7327c478bd9Sstevel@tonic-gate static int lofsdebug; 7337c478bd9Sstevel@tonic-gate #endif /* LODEBUG */ 7347c478bd9Sstevel@tonic-gate 7357c478bd9Sstevel@tonic-gate /* 7367c478bd9Sstevel@tonic-gate * Utilities used by both client and server 7377c478bd9Sstevel@tonic-gate * Standard levels: 7387c478bd9Sstevel@tonic-gate * 0) no debugging 7397c478bd9Sstevel@tonic-gate * 1) hard failures 7407c478bd9Sstevel@tonic-gate * 2) soft failures 7417c478bd9Sstevel@tonic-gate * 3) current test software 7427c478bd9Sstevel@tonic-gate * 4) main procedure entry points 7437c478bd9Sstevel@tonic-gate * 5) main procedure exit points 7447c478bd9Sstevel@tonic-gate * 6) utility procedure entry points 7457c478bd9Sstevel@tonic-gate * 7) utility procedure exit points 7467c478bd9Sstevel@tonic-gate * 8) obscure procedure entry points 7477c478bd9Sstevel@tonic-gate * 9) obscure procedure exit points 7487c478bd9Sstevel@tonic-gate * 10) random stuff 7497c478bd9Sstevel@tonic-gate * 11) all <= 1 7507c478bd9Sstevel@tonic-gate * 12) all <= 2 7517c478bd9Sstevel@tonic-gate * 13) all <= 3 7527c478bd9Sstevel@tonic-gate * ... 7537c478bd9Sstevel@tonic-gate */ 7547c478bd9Sstevel@tonic-gate 7557c478bd9Sstevel@tonic-gate #ifdef LODEBUG 7567c478bd9Sstevel@tonic-gate /*VARARGS2*/ 7577c478bd9Sstevel@tonic-gate lo_dprint(level, str, a1, a2, a3, a4, a5, a6, a7, a8, a9) 7587c478bd9Sstevel@tonic-gate int level; 7597c478bd9Sstevel@tonic-gate char *str; 7607c478bd9Sstevel@tonic-gate int a1, a2, a3, a4, a5, a6, a7, a8, a9; 7617c478bd9Sstevel@tonic-gate { 7627c478bd9Sstevel@tonic-gate 7637c478bd9Sstevel@tonic-gate if (lofsdebug == level || (lofsdebug > 10 && (lofsdebug - 10) >= level)) 7647c478bd9Sstevel@tonic-gate printf(str, a1, a2, a3, a4, a5, a6, a7, a8, a9); 7657c478bd9Sstevel@tonic-gate } 7667c478bd9Sstevel@tonic-gate #endif 767