summaryrefslogtreecommitdiffstats
path: root/sys/kern/vfs_subr.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2012-09-09 19:17:15 +0000
committerkib <kib@FreeBSD.org>2012-09-09 19:17:15 +0000
commit5a86a6849adc026d814651fd6afc864b2377b9c8 (patch)
tree23fb14fcdb2f9274e6cc5294ce7f1bf0634f2755 /sys/kern/vfs_subr.c
parent01b848f0eebd0e92a90c06e0708aa3d48942ed79 (diff)
downloadFreeBSD-src-5a86a6849adc026d814651fd6afc864b2377b9c8.zip
FreeBSD-src-5a86a6849adc026d814651fd6afc864b2377b9c8.tar.gz
Add a facility for vgone() to inform the set of subscribed mounts
about vnode reclamation. Typical use is for the bypass mounts like nullfs to get a notification about lower vnode going away. Now, vgone() calls new VFS op vfs_reclaim_lowervp() with an argument lowervp which is reclaimed. It is possible to register several reclamation event listeners, to correctly handle the case of several nullfs mounts over the same directory. For the filesystem not having nullfs mounts over it, the overhead added is a single mount interlock lock/unlock in the vnode reclamation path. In collaboration with: pho MFC after: 3 weeks
Diffstat (limited to 'sys/kern/vfs_subr.c')
-rw-r--r--sys/kern/vfs_subr.c55
1 files changed, 55 insertions, 0 deletions
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index 631d3f2..06d16c0 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -2688,6 +2688,58 @@ vgone(struct vnode *vp)
VI_UNLOCK(vp);
}
+static void
+vgonel_reclaim_lowervp_vfs(struct mount *mp __unused,
+ struct vnode *lowervp __unused)
+{
+}
+
+/*
+ * Notify upper mounts about reclaimed vnode.
+ */
+static void
+vgonel_reclaim_lowervp(struct vnode *vp)
+{
+ static struct vfsops vgonel_vfsops = {
+ .vfs_reclaim_lowervp = vgonel_reclaim_lowervp_vfs
+ };
+ struct mount *mp, *ump, *mmp;
+
+ mp = vp->v_mount;
+ if (mp == NULL)
+ return;
+
+ MNT_ILOCK(mp);
+ if (TAILQ_EMPTY(&mp->mnt_uppers))
+ goto unlock;
+ MNT_IUNLOCK(mp);
+ mmp = malloc(sizeof(struct mount), M_TEMP, M_WAITOK | M_ZERO);
+ mmp->mnt_op = &vgonel_vfsops;
+ mmp->mnt_kern_flag |= MNTK_MARKER;
+ MNT_ILOCK(mp);
+ mp->mnt_kern_flag |= MNTK_VGONE_UPPER;
+ for (ump = TAILQ_FIRST(&mp->mnt_uppers); ump != NULL;) {
+ if ((ump->mnt_kern_flag & MNTK_MARKER) != 0) {
+ ump = TAILQ_NEXT(ump, mnt_upper_link);
+ continue;
+ }
+ TAILQ_INSERT_AFTER(&mp->mnt_uppers, ump, mmp, mnt_upper_link);
+ MNT_IUNLOCK(mp);
+ VFS_RECLAIM_LOWERVP(ump, vp);
+ MNT_ILOCK(mp);
+ ump = TAILQ_NEXT(mmp, mnt_upper_link);
+ TAILQ_REMOVE(&mp->mnt_uppers, mmp, mnt_upper_link);
+ }
+ free(mmp, M_TEMP);
+ mp->mnt_kern_flag &= ~MNTK_VGONE_UPPER;
+ if ((mp->mnt_kern_flag & MNTK_VGONE_WAITER) != 0) {
+ mp->mnt_kern_flag &= ~MNTK_VGONE_WAITER;
+ wakeup(&mp->mnt_uppers);
+ }
+unlock:
+ MNT_IUNLOCK(mp);
+}
+
/*
* vgone, with the vp interlock held.
*/
@@ -2712,6 +2764,7 @@ vgonel(struct vnode *vp)
if (vp->v_iflag & VI_DOOMED)
return;
vp->v_iflag |= VI_DOOMED;
+
/*
* Check to see if the vnode is in use. If so, we have to call
* VOP_CLOSE() and VOP_INACTIVE().
@@ -2719,6 +2772,8 @@ vgonel(struct vnode *vp)
active = vp->v_usecount;
oweinact = (vp->v_iflag & VI_OWEINACT);
VI_UNLOCK(vp);
+ vgonel_reclaim_lowervp(vp);
+
/*
* Clean out any buffers associated with the vnode.
* If the flush fails, just toss the buffers.
OpenPOWER on IntegriCloud