diff --git a/sys/vfs/dirfs/dirfs.h b/sys/vfs/dirfs/dirfs.h index 4af2986..6f60ec0 100644 --- a/sys/vfs/dirfs/dirfs.h +++ b/sys/vfs/dirfs/dirfs.h @@ -256,6 +256,7 @@ int dirfs_node_free(dirfs_mount_t, dirfs_node_t); void dirfs_node_drop(dirfs_mount_t dmp, dirfs_node_t dnp); void dirfs_alloc_vp(struct mount *, struct vnode **, int, dirfs_node_t); void dirfs_free_vp(dirfs_node_t); +int dirfs_node_tryclose(dirfs_node_t); int dirfs_alloc_file(dirfs_mount_t, dirfs_node_t *, dirfs_node_t, struct namecache *, struct vnode **, struct vattr *, int); dirfs_node_t dirfs_findfd(dirfs_mount_t dmp, dirfs_node_t cur, diff --git a/sys/vfs/dirfs/dirfs_subr.c b/sys/vfs/dirfs/dirfs_subr.c index 7a92af3..d93d15c 100644 --- a/sys/vfs/dirfs/dirfs_subr.c +++ b/sys/vfs/dirfs/dirfs_subr.c @@ -88,10 +88,59 @@ dirfs_node_alloc(struct mount *mp) void dirfs_node_drop(dirfs_mount_t dmp, dirfs_node_t dnp) { - if (dirfs_node_unref(dnp)) - dirfs_node_free(dmp, dnp); + if (dirfs_node_unref(dnp)) { + if (dirfs_node_tryclose(dnp)) + dirfs_node_free(dmp, dnp); + else + kprintf("Could not close %s\n", dnp->dn_name); + } } +int +dirfs_node_tryclose(dirfs_node_t dnp) +{ + struct vnode *vp; + + KKASSERT(dnp->dn_vnode); + + /* + * Attempt to close the descriptor. We can only do this + * if the related vnode is inactive and has exactly two + * refs (representing the vp<->dnp and PASVFD). Otherwise + * someone might have ref'd the node in order to use the + * dn_fd. + * + * Also, if the vnode is in any way dirty we leave the fd + * open for the buffer cache code. The syncer will eventually + * come along and fsync the vnode, and the next inactive + * transition will deal with the descriptor. + * + * The descriptor for the root node is NEVER closed by + * this function. + */ + vp = dnp->dn_vnode; + if (dirfs_node_refcnt(dnp) == 2 && vp && + dnp->dn_fd != DIRFS_NOFD && + !dirfs_node_isroot(dnp) && + (vp->v_flag & (VINACTIVE|VOBJDIRTY)) == VINACTIVE && + RB_EMPTY(&vp->v_rbdirty_tree)) { + dbg(5, "passive cache: closing %d\n", dnp->dn_fd); + close(dnp->dn_fd); + dnp->dn_fd = DIRFS_NOFD; + } else { + if (dirfs_node_refcnt(dnp) == 1 && dnp->dn_vnode == NULL && + dnp->dn_fd != DIRFS_NOFD && + !dirfs_node_isroot(dnp)) { + dbg(5, "passive cache: closing %d\n", dnp->dn_fd); + close(dnp->dn_fd); + dnp->dn_fd = DIRFS_NOFD; + } + } + + return (dnp->dn_fd == DIRFS_NOFD); +} + + /* * Removes the association with its parent. Before freeing up its resources * the node will be removed from the per-mount passive fd cache and its fd @@ -109,8 +158,7 @@ dirfs_node_free(dirfs_mount_t dmp, dirfs_node_t dnp) vp = NODE_TO_VP(dnp); /* - * Remove the inode from the passive fds list - * as we are tearing down the node. + * Decrease parent's refcnt as dnp is being released. * Root inode will be removed on VOP_UNMOUNT() */ dirfs_mount_gettoken(dmp); @@ -123,7 +171,6 @@ dirfs_node_free(dirfs_mount_t dmp, dirfs_node_t dnp) kfree(dnp->dn_name, M_DIRFS_MISC); dnp->dn_name = NULL; } - close(dnp->dn_fd); /* @@ -140,6 +187,7 @@ dirfs_node_free(dirfs_mount_t dmp, dirfs_node_t dnp) lockuninit(&dnp->dn_lock); kfree(dnp, M_DIRFS_NODE); + atomic_set_int(&dnp->dn_vnode->v_refcnt, VREF_FINALIZE); dnp = NULL; dirfs_mount_reltoken(dmp); diff --git a/sys/vfs/dirfs/dirfs_vnops.c b/sys/vfs/dirfs/dirfs_vnops.c index d2696b8..d917017 100644 --- a/sys/vfs/dirfs/dirfs_vnops.c +++ b/sys/vfs/dirfs/dirfs_vnops.c @@ -1367,7 +1367,12 @@ dirfs_inactive(struct vop_inactive_args *ap) return 0; } - if (dirfs_node_refcnt(dnp) == 0) + /* + * If no references, try to close the dnp and + * release its resources. + */ + if (dirfs_node_refcnt(dnp) == 0 && + dirfs_node_tryclose(dnp)) dirfs_node_free(dmp,dnp); /*