6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 /*
29 * The idea behind composition-based stacked filesystems is to add a
30 * vnode to the stack of vnodes for each mount. These vnodes have their
31 * own set of mount options and filesystem-specific functions, so they
32 * can modify data or operations before they are passed along. Such a
33 * filesystem must maintain a mapping from the underlying vnodes to its
34 * interposing vnodes.
35 *
36 * In lofs, this mapping is implemented by a hashtable. Each bucket
37 * contains a count of the number of nodes currently contained, the
38 * chain of vnodes, and a lock to protect the list of vnodes. The
39 * hashtable dynamically grows if the number of vnodes in the table as a
40 * whole exceeds the size of the table left-shifted by
41 * lo_resize_threshold. In order to minimize lock contention, there is
42 * no global lock protecting the hashtable, hence obtaining the
43 * per-bucket locks consists of a dance to make sure we've actually
44 * locked the correct bucket. Acquiring a bucket lock doesn't involve
45 * locking the hashtable itself, so we refrain from freeing old
46 * hashtables, and store them in a linked list of retired hashtables;
47 * the list is freed when the filesystem is unmounted.
272 /* The lnode allocation may have succeeded, save it */
273 tlp = lp;
274 if (tlp == NULL) {
275 tlp = kmem_cache_alloc(lnode_cache, KM_SLEEP);
276 }
277 if (nvp == NULL) {
278 nvp = vn_alloc(KM_SLEEP);
279 }
280 lp = NULL;
281 TABLE_LOCK_ENTER(vp, li);
282 if (flag != LOF_FORCE)
283 lp = lfind(vp, li);
284 if (lp != NULL) {
285 kmem_cache_free(lnode_cache, tlp);
286 vn_free(nvp);
287 VN_RELE(vp);
288 goto found_lnode;
289 }
290 lp = tlp;
291 }
292 atomic_add_32(&li->li_refct, 1);
293 vfsp = makelfsnode(vp->v_vfsp, li);
294 lp->lo_vnode = nvp;
295 VN_SET_VFS_TYPE_DEV(nvp, vfsp, vp->v_type, vp->v_rdev);
296 nvp->v_flag |= (vp->v_flag & (VNOMOUNT|VNOMAP|VDIROPEN));
297 vn_setops(nvp, lo_vnodeops);
298 nvp->v_data = (caddr_t)lp;
299 lp->lo_vp = vp;
300 lp->lo_looping = 0;
301 lsave(lp, li);
302 vn_exists(vp);
303 } else {
304 VN_RELE(vp);
305 }
306
307 found_lnode:
308 TABLE_LOCK_EXIT(vp, li);
309 return (ltov(lp));
310 }
311
312 /*
615 TABLE_BUCKET(lp->lo_vp, li) = lp;
616
617 if (li->li_refct > (li->li_htsize << lo_resize_threshold)) {
618 TABLE_LOCK_EXIT(lp->lo_vp, li);
619 lgrow(li, li->li_htsize << lo_resize_factor);
620 TABLE_LOCK_ENTER(lp->lo_vp, li);
621 }
622 }
623
624 /*
625 * Our version of vfs_rele() that stops at 1 instead of 0, and calls
626 * freelfsnode() instead of kmem_free().
627 */
628 static void
629 lfs_rele(struct lfsnode *lfs, struct loinfo *li)
630 {
631 vfs_t *vfsp = &lfs->lfs_vfs;
632
633 ASSERT(MUTEX_HELD(&li->li_lfslock));
634 ASSERT(vfsp->vfs_count > 1);
635 if (atomic_add_32_nv(&vfsp->vfs_count, -1) == 1)
636 freelfsnode(lfs, li);
637 }
638
639 /*
640 * Remove a lnode from the table
641 */
642 void
643 freelonode(lnode_t *lp)
644 {
645 lnode_t *lt;
646 lnode_t *ltprev = NULL;
647 struct lfsnode *lfs, *nextlfs;
648 struct vfs *vfsp;
649 struct vnode *vp = ltov(lp);
650 struct vnode *realvp = realvp(vp);
651 struct loinfo *li = vtoli(vp->v_vfsp);
652
653 #ifdef LODEBUG
654 lo_dprint(4, "freelonode lp %p hash %d\n",
655 lp, ltablehash(lp->lo_vp, li));
656 #endif
657 TABLE_LOCK_ENTER(lp->lo_vp, li);
658
659 mutex_enter(&vp->v_lock);
660 if (vp->v_count > 1) {
661 vp->v_count--; /* release our hold from vn_rele */
662 mutex_exit(&vp->v_lock);
663 TABLE_LOCK_EXIT(lp->lo_vp, li);
664 return;
665 }
666 mutex_exit(&vp->v_lock);
667
668 for (lt = TABLE_BUCKET(lp->lo_vp, li); lt != NULL;
669 ltprev = lt, lt = lt->lo_next) {
670 if (lt == lp) {
671 #ifdef LODEBUG
672 lo_dprint(4, "freeing %p, vfsp %p\n",
673 vp, vp->v_vfsp);
674 #endif
675 atomic_add_32(&li->li_refct, -1);
676 vfsp = vp->v_vfsp;
677 vn_invalid(vp);
678 if (vfsp != li->li_mountvfs) {
679 mutex_enter(&li->li_lfslock);
680 /*
681 * Check for unused lfs
682 */
683 lfs = li->li_lfs;
684 while (lfs != NULL) {
685 nextlfs = lfs->lfs_next;
686 if (vfsp == &lfs->lfs_vfs) {
687 lfs_rele(lfs, li);
688 break;
689 }
690 if (lfs->lfs_vfs.vfs_count == 1) {
691 /*
692 * Lfs is idle
693 */
694 freelfsnode(lfs, li);
695 }
|
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * The idea behind composition-based stacked filesystems is to add a
28 * vnode to the stack of vnodes for each mount. These vnodes have their
29 * own set of mount options and filesystem-specific functions, so they
30 * can modify data or operations before they are passed along. Such a
31 * filesystem must maintain a mapping from the underlying vnodes to its
32 * interposing vnodes.
33 *
34 * In lofs, this mapping is implemented by a hashtable. Each bucket
35 * contains a count of the number of nodes currently contained, the
36 * chain of vnodes, and a lock to protect the list of vnodes. The
37 * hashtable dynamically grows if the number of vnodes in the table as a
38 * whole exceeds the size of the table left-shifted by
39 * lo_resize_threshold. In order to minimize lock contention, there is
40 * no global lock protecting the hashtable, hence obtaining the
41 * per-bucket locks consists of a dance to make sure we've actually
42 * locked the correct bucket. Acquiring a bucket lock doesn't involve
43 * locking the hashtable itself, so we refrain from freeing old
44 * hashtables, and store them in a linked list of retired hashtables;
45 * the list is freed when the filesystem is unmounted.
270 /* The lnode allocation may have succeeded, save it */
271 tlp = lp;
272 if (tlp == NULL) {
273 tlp = kmem_cache_alloc(lnode_cache, KM_SLEEP);
274 }
275 if (nvp == NULL) {
276 nvp = vn_alloc(KM_SLEEP);
277 }
278 lp = NULL;
279 TABLE_LOCK_ENTER(vp, li);
280 if (flag != LOF_FORCE)
281 lp = lfind(vp, li);
282 if (lp != NULL) {
283 kmem_cache_free(lnode_cache, tlp);
284 vn_free(nvp);
285 VN_RELE(vp);
286 goto found_lnode;
287 }
288 lp = tlp;
289 }
290 atomic_inc_32(&li->li_refct);
291 vfsp = makelfsnode(vp->v_vfsp, li);
292 lp->lo_vnode = nvp;
293 VN_SET_VFS_TYPE_DEV(nvp, vfsp, vp->v_type, vp->v_rdev);
294 nvp->v_flag |= (vp->v_flag & (VNOMOUNT|VNOMAP|VDIROPEN));
295 vn_setops(nvp, lo_vnodeops);
296 nvp->v_data = (caddr_t)lp;
297 lp->lo_vp = vp;
298 lp->lo_looping = 0;
299 lsave(lp, li);
300 vn_exists(vp);
301 } else {
302 VN_RELE(vp);
303 }
304
305 found_lnode:
306 TABLE_LOCK_EXIT(vp, li);
307 return (ltov(lp));
308 }
309
310 /*
613 TABLE_BUCKET(lp->lo_vp, li) = lp;
614
615 if (li->li_refct > (li->li_htsize << lo_resize_threshold)) {
616 TABLE_LOCK_EXIT(lp->lo_vp, li);
617 lgrow(li, li->li_htsize << lo_resize_factor);
618 TABLE_LOCK_ENTER(lp->lo_vp, li);
619 }
620 }
621
622 /*
623 * Our version of vfs_rele() that stops at 1 instead of 0, and calls
624 * freelfsnode() instead of kmem_free().
625 */
626 static void
627 lfs_rele(struct lfsnode *lfs, struct loinfo *li)
628 {
629 vfs_t *vfsp = &lfs->lfs_vfs;
630
631 ASSERT(MUTEX_HELD(&li->li_lfslock));
632 ASSERT(vfsp->vfs_count > 1);
633 if (atomic_dec_32_nv(&vfsp->vfs_count) == 1)
634 freelfsnode(lfs, li);
635 }
636
637 /*
638 * Remove a lnode from the table
639 */
640 void
641 freelonode(lnode_t *lp)
642 {
643 lnode_t *lt;
644 lnode_t *ltprev = NULL;
645 struct lfsnode *lfs, *nextlfs;
646 struct vfs *vfsp;
647 struct vnode *vp = ltov(lp);
648 struct vnode *realvp = realvp(vp);
649 struct loinfo *li = vtoli(vp->v_vfsp);
650
651 #ifdef LODEBUG
652 lo_dprint(4, "freelonode lp %p hash %d\n",
653 lp, ltablehash(lp->lo_vp, li));
654 #endif
655 TABLE_LOCK_ENTER(lp->lo_vp, li);
656
657 mutex_enter(&vp->v_lock);
658 if (vp->v_count > 1) {
659 vp->v_count--; /* release our hold from vn_rele */
660 mutex_exit(&vp->v_lock);
661 TABLE_LOCK_EXIT(lp->lo_vp, li);
662 return;
663 }
664 mutex_exit(&vp->v_lock);
665
666 for (lt = TABLE_BUCKET(lp->lo_vp, li); lt != NULL;
667 ltprev = lt, lt = lt->lo_next) {
668 if (lt == lp) {
669 #ifdef LODEBUG
670 lo_dprint(4, "freeing %p, vfsp %p\n",
671 vp, vp->v_vfsp);
672 #endif
673 atomic_dec_32(&li->li_refct);
674 vfsp = vp->v_vfsp;
675 vn_invalid(vp);
676 if (vfsp != li->li_mountvfs) {
677 mutex_enter(&li->li_lfslock);
678 /*
679 * Check for unused lfs
680 */
681 lfs = li->li_lfs;
682 while (lfs != NULL) {
683 nextlfs = lfs->lfs_next;
684 if (vfsp == &lfs->lfs_vfs) {
685 lfs_rele(lfs, li);
686 break;
687 }
688 if (lfs->lfs_vfs.vfs_count == 1) {
689 /*
690 * Lfs is idle
691 */
692 freelfsnode(lfs, li);
693 }
|