diff options
author | Marcel Telka <marcel@telka.sk> | 2018-04-25 00:28:01 +0200 |
---|---|---|
committer | Dan McDonald <danmcd@joyent.com> | 2018-07-18 20:10:13 -0400 |
commit | e010bda94b034e413b6fe35fd45bca0afaf1a0df (patch) | |
tree | 252bdfc74d19e9790fb82af170be1a0d9ac0db65 /usr/src/uts/common/fs/nfs/nfs_subr.c | |
parent | 5882b622b7e2afa5385d4601dd82f81066f62d67 (diff) | |
download | illumos-joyent-e010bda94b034e413b6fe35fd45bca0afaf1a0df.tar.gz |
9447 NFS unmount is slow
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Arne Jansen <arne@die-jansens.de>
Reviewed by: Ken Mays <kmays2000@gmail.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Approved by: Dan McDonald <danmcd@joyent.com>
Diffstat (limited to 'usr/src/uts/common/fs/nfs/nfs_subr.c')
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs_subr.c | 169 |
1 files changed, 113 insertions, 56 deletions
diff --git a/usr/src/uts/common/fs/nfs/nfs_subr.c b/usr/src/uts/common/fs/nfs/nfs_subr.c index cda666c27e..68cd0df081 100644 --- a/usr/src/uts/common/fs/nfs/nfs_subr.c +++ b/usr/src/uts/common/fs/nfs/nfs_subr.c @@ -2821,6 +2821,7 @@ rp_rmfree(rnode_t *rp) static void rp_addhash(rnode_t *rp) { + mntinfo_t *mi; ASSERT(RW_WRITE_HELD(&rp->r_hashq->r_lock)); ASSERT(!(rp->r_flags & RHASHED)); @@ -2833,6 +2834,11 @@ rp_addhash(rnode_t *rp) mutex_enter(&rp->r_statelock); rp->r_flags |= RHASHED; mutex_exit(&rp->r_statelock); + + mi = VTOMI(RTOV(rp)); + mutex_enter(&mi->mi_rnodes_lock); + list_insert_tail(&mi->mi_rnodes, rp); + mutex_exit(&mi->mi_rnodes_lock); } /* @@ -2843,6 +2849,7 @@ rp_addhash(rnode_t *rp) static void rp_rmhash_locked(rnode_t *rp) { + mntinfo_t *mi; ASSERT(RW_WRITE_HELD(&rp->r_hashq->r_lock)); ASSERT(rp->r_flags & RHASHED); @@ -2853,6 +2860,12 @@ rp_rmhash_locked(rnode_t *rp) mutex_enter(&rp->r_statelock); rp->r_flags &= ~RHASHED; mutex_exit(&rp->r_statelock); + + mi = VTOMI(RTOV(rp)); + mutex_enter(&mi->mi_rnodes_lock); + if (list_link_active(&rp->r_mi_link)) + list_remove(&mi->mi_rnodes, rp); + mutex_exit(&mi->mi_rnodes_lock); } /* @@ -2914,7 +2927,7 @@ rfind(rhashq_t *rhtp, nfs_fhandle *fh, struct vfs *vfsp) } /* - * Return 1 if there is a active vnode belonging to this vfs in the + * Return 1 if there is an active vnode belonging to this vfs in the * rtable cache. * * Several of these checks are done without holding the usual @@ -2925,28 +2938,27 @@ rfind(rhashq_t *rhtp, nfs_fhandle *fh, struct vfs *vfsp) int check_rtable(struct vfs *vfsp) { - int index; rnode_t *rp; vnode_t *vp; + mntinfo_t *mi; - for (index = 0; index < rtablesize; index++) { - rw_enter(&rtable[index].r_lock, RW_READER); - for (rp = rtable[index].r_hashf; - rp != (rnode_t *)(&rtable[index]); - rp = rp->r_hashf) { - vp = RTOV(rp); - if (vp->v_vfsp == vfsp) { - if (rp->r_freef == NULL || - (vn_has_cached_data(vp) && - (rp->r_flags & RDIRTY)) || - rp->r_count > 0) { - rw_exit(&rtable[index].r_lock); - return (1); - } - } + ASSERT(vfsp != NULL); + mi = VFTOMI(vfsp); + + mutex_enter(&mi->mi_rnodes_lock); + for (rp = list_head(&mi->mi_rnodes); rp != NULL; + rp = list_next(&mi->mi_rnodes, rp)) { + vp = RTOV(rp); + + if (rp->r_freef == NULL || + (vn_has_cached_data(vp) && (rp->r_flags & RDIRTY)) || + rp->r_count > 0) { + mutex_exit(&mi->mi_rnodes_lock); + return (1); } - rw_exit(&rtable[index].r_lock); } + mutex_exit(&mi->mi_rnodes_lock); + return (0); } @@ -2958,47 +2970,42 @@ check_rtable(struct vfs *vfsp) void destroy_rtable(struct vfs *vfsp, cred_t *cr) { - int index; rnode_t *rp; - rnode_t *rlist; - rnode_t *r_hashf; - vnode_t *vp; + mntinfo_t *mi; - rlist = NULL; + ASSERT(vfsp != NULL); - for (index = 0; index < rtablesize; index++) { - rw_enter(&rtable[index].r_lock, RW_WRITER); - for (rp = rtable[index].r_hashf; - rp != (rnode_t *)(&rtable[index]); - rp = r_hashf) { - /* save the hash pointer before destroying */ - r_hashf = rp->r_hashf; - vp = RTOV(rp); - if (vp->v_vfsp == vfsp) { - mutex_enter(&rpfreelist_lock); - if (rp->r_freef != NULL) { - rp_rmfree(rp); - mutex_exit(&rpfreelist_lock); - rp_rmhash_locked(rp); - rp->r_hashf = rlist; - rlist = rp; - } else - mutex_exit(&rpfreelist_lock); - } - } - rw_exit(&rtable[index].r_lock); - } + mi = VFTOMI(vfsp); + + mutex_enter(&rpfreelist_lock); + mutex_enter(&mi->mi_rnodes_lock); + while ((rp = list_remove_head(&mi->mi_rnodes)) != NULL) { + /* + * If the rnode is no longer on the freelist it is not + * ours and it will be handled by some other thread, so + * skip it. + */ + if (rp->r_freef == NULL) + continue; + mutex_exit(&mi->mi_rnodes_lock); + + rp_rmfree(rp); + mutex_exit(&rpfreelist_lock); + + rp_rmhash(rp); - for (rp = rlist; rp != NULL; rp = rlist) { - rlist = rp->r_hashf; /* * This call to rp_addfree will end up destroying the * rnode, but in a safe way with the appropriate set * of checks done. */ rp_addfree(rp, cr); - } + mutex_enter(&rpfreelist_lock); + mutex_enter(&mi->mi_rnodes_lock); + } + mutex_exit(&mi->mi_rnodes_lock); + mutex_exit(&rpfreelist_lock); } /* @@ -3066,6 +3073,53 @@ rflush(struct vfs *vfsp, cred_t *cr) cnt = 0; /* + * If the vfs is known we can do fast path by iterating all rnodes that + * belongs to this vfs. This is much faster than the traditional way + * of iterating rtable (below) in a case there is a lot of rnodes that + * does not belong to our vfs. + */ + if (vfsp != NULL) { + mntinfo_t *mi = VFTOMI(vfsp); + + mutex_enter(&mi->mi_rnodes_lock); + for (rp = list_head(&mi->mi_rnodes); rp != NULL; + rp = list_next(&mi->mi_rnodes, rp)) { + vp = RTOV(rp); + /* + * Don't bother sync'ing a vp if it + * is part of virtual swap device or + * if VFS is read-only + */ + if (IS_SWAPVP(vp) || vn_is_readonly(vp)) + continue; + /* + * If the vnode has pages and is marked as either dirty + * or mmap'd, hold and add this vnode to the list of + * vnodes to flush. + */ + ASSERT(vp->v_vfsp == vfsp); + if (vn_has_cached_data(vp) && + ((rp->r_flags & RDIRTY) || rp->r_mapcnt > 0)) { + VN_HOLD(vp); + vplist[cnt++] = vp; + if (cnt == num) { + /* + * The vplist is full because there is + * too many rnodes. We are done for + * now. + */ + break; + } + } + } + mutex_exit(&mi->mi_rnodes_lock); + + goto done; + } + + ASSERT(vfsp == NULL); + + /* * Walk the hash queues looking for rnodes with page * lists associated with them. Make a list of these * files. @@ -3084,26 +3138,29 @@ rflush(struct vfs *vfsp, cred_t *cr) if (IS_SWAPVP(vp) || vn_is_readonly(vp)) continue; /* - * If flushing all mounted file systems or - * the vnode belongs to this vfs, has pages - * and is marked as either dirty or mmap'd, - * hold and add this vnode to the list of + * If the vnode has pages and is marked as either dirty + * or mmap'd, hold and add this vnode to the list of * vnodes to flush. */ - if ((vfsp == NULL || vp->v_vfsp == vfsp) && - vn_has_cached_data(vp) && + if (vn_has_cached_data(vp) && ((rp->r_flags & RDIRTY) || rp->r_mapcnt > 0)) { VN_HOLD(vp); vplist[cnt++] = vp; if (cnt == num) { rw_exit(&rtable[index].r_lock); - goto toomany; + /* + * The vplist is full because there is + * too many rnodes. We are done for + * now. + */ + goto done; } } } rw_exit(&rtable[index].r_lock); } -toomany: + +done: /* * Flush and release all of the files on the list. |