diff options
author | Jeff Layton <jeff.layton@primarydata.com> | 2019-08-18 14:18:57 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2019-08-19 11:09:10 -0400 |
commit | 7775ec57f4c71309109f9e98cf8854a2ca8c9df1 (patch) | |
tree | a5c20ba739bdc4692bb7b2ae399d249eea9d1c5e /fs/nfsd/vfs.c | |
parent | 501cb1849f865960501d19d54e6a5af306f9b6fd (diff) |
nfsd: close cached files prior to a REMOVE or RENAME that would replace target
It's not uncommon for some workloads to do a bunch of I/O to a file and
delete it just afterward. If knfsd has a cached open file however, then
the file may still be open when the dentry is unlinked. If the
underlying filesystem is nfs, then that could trigger it to do a
sillyrename.
On a REMOVE or RENAME scan the nfsd_file cache for open files that
correspond to the inode, and proactively unhash and put their
references. This should prevent any delete-on-last-close activity from
occurring, solely due to knfsd's open file cache.
This must be done synchronously though so we use the variants that call
flush_delayed_fput. There are deadlock possibilities if you call
flush_delayed_fput while holding locks, however. In the case of
nfsd_rename, we don't even do the lookups of the dentries to be renamed
until we've locked for rename.
Once we've figured out what the target dentry is for a rename, check to
see whether there are cached open files associated with it. If there
are, then unwind all of the locking, close them all, and then reattempt
the rename.
Signed-off-by: Jeff Layton <jeff.layton@primarydata.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd/vfs.c')
-rw-r--r-- | fs/nfsd/vfs.c | 62 |
1 files changed, 53 insertions, 9 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 8e2c8f36eba3..84e87772c2b8 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -1590,6 +1590,26 @@ out_nfserr: | |||
1590 | goto out_unlock; | 1590 | goto out_unlock; |
1591 | } | 1591 | } |
1592 | 1592 | ||
1593 | static void | ||
1594 | nfsd_close_cached_files(struct dentry *dentry) | ||
1595 | { | ||
1596 | struct inode *inode = d_inode(dentry); | ||
1597 | |||
1598 | if (inode && S_ISREG(inode->i_mode)) | ||
1599 | nfsd_file_close_inode_sync(inode); | ||
1600 | } | ||
1601 | |||
1602 | static bool | ||
1603 | nfsd_has_cached_files(struct dentry *dentry) | ||
1604 | { | ||
1605 | bool ret = false; | ||
1606 | struct inode *inode = d_inode(dentry); | ||
1607 | |||
1608 | if (inode && S_ISREG(inode->i_mode)) | ||
1609 | ret = nfsd_file_is_cached(inode); | ||
1610 | return ret; | ||
1611 | } | ||
1612 | |||
1593 | /* | 1613 | /* |
1594 | * Rename a file | 1614 | * Rename a file |
1595 | * N.B. After this call _both_ ffhp and tfhp need an fh_put | 1615 | * N.B. After this call _both_ ffhp and tfhp need an fh_put |
@@ -1602,6 +1622,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, | |||
1602 | struct inode *fdir, *tdir; | 1622 | struct inode *fdir, *tdir; |
1603 | __be32 err; | 1623 | __be32 err; |
1604 | int host_err; | 1624 | int host_err; |
1625 | bool has_cached = false; | ||
1605 | 1626 | ||
1606 | err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_REMOVE); | 1627 | err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_REMOVE); |
1607 | if (err) | 1628 | if (err) |
@@ -1620,6 +1641,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, | |||
1620 | if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen)) | 1641 | if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen)) |
1621 | goto out; | 1642 | goto out; |
1622 | 1643 | ||
1644 | retry: | ||
1623 | host_err = fh_want_write(ffhp); | 1645 | host_err = fh_want_write(ffhp); |
1624 | if (host_err) { | 1646 | if (host_err) { |
1625 | err = nfserrno(host_err); | 1647 | err = nfserrno(host_err); |
@@ -1659,11 +1681,16 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, | |||
1659 | if (ffhp->fh_export->ex_path.dentry != tfhp->fh_export->ex_path.dentry) | 1681 | if (ffhp->fh_export->ex_path.dentry != tfhp->fh_export->ex_path.dentry) |
1660 | goto out_dput_new; | 1682 | goto out_dput_new; |
1661 | 1683 | ||
1662 | host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL, 0); | 1684 | if (nfsd_has_cached_files(ndentry)) { |
1663 | if (!host_err) { | 1685 | has_cached = true; |
1664 | host_err = commit_metadata(tfhp); | 1686 | goto out_dput_old; |
1665 | if (!host_err) | 1687 | } else { |
1666 | host_err = commit_metadata(ffhp); | 1688 | host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL, 0); |
1689 | if (!host_err) { | ||
1690 | host_err = commit_metadata(tfhp); | ||
1691 | if (!host_err) | ||
1692 | host_err = commit_metadata(ffhp); | ||
1693 | } | ||
1667 | } | 1694 | } |
1668 | out_dput_new: | 1695 | out_dput_new: |
1669 | dput(ndentry); | 1696 | dput(ndentry); |
@@ -1676,12 +1703,26 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, | |||
1676 | * as that would do the wrong thing if the two directories | 1703 | * as that would do the wrong thing if the two directories |
1677 | * were the same, so again we do it by hand. | 1704 | * were the same, so again we do it by hand. |
1678 | */ | 1705 | */ |
1679 | fill_post_wcc(ffhp); | 1706 | if (!has_cached) { |
1680 | fill_post_wcc(tfhp); | 1707 | fill_post_wcc(ffhp); |
1708 | fill_post_wcc(tfhp); | ||
1709 | } | ||
1681 | unlock_rename(tdentry, fdentry); | 1710 | unlock_rename(tdentry, fdentry); |
1682 | ffhp->fh_locked = tfhp->fh_locked = false; | 1711 | ffhp->fh_locked = tfhp->fh_locked = false; |
1683 | fh_drop_write(ffhp); | 1712 | fh_drop_write(ffhp); |
1684 | 1713 | ||
1714 | /* | ||
1715 | * If the target dentry has cached open files, then we need to try to | ||
1716 | * close them prior to doing the rename. Flushing delayed fput | ||
1717 | * shouldn't be done with locks held however, so we delay it until this | ||
1718 | * point and then reattempt the whole shebang. | ||
1719 | */ | ||
1720 | if (has_cached) { | ||
1721 | has_cached = false; | ||
1722 | nfsd_close_cached_files(ndentry); | ||
1723 | dput(ndentry); | ||
1724 | goto retry; | ||
1725 | } | ||
1685 | out: | 1726 | out: |
1686 | return err; | 1727 | return err; |
1687 | } | 1728 | } |
@@ -1728,10 +1769,13 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, | |||
1728 | if (!type) | 1769 | if (!type) |
1729 | type = d_inode(rdentry)->i_mode & S_IFMT; | 1770 | type = d_inode(rdentry)->i_mode & S_IFMT; |
1730 | 1771 | ||
1731 | if (type != S_IFDIR) | 1772 | if (type != S_IFDIR) { |
1773 | nfsd_close_cached_files(rdentry); | ||
1732 | host_err = vfs_unlink(dirp, rdentry, NULL); | 1774 | host_err = vfs_unlink(dirp, rdentry, NULL); |
1733 | else | 1775 | } else { |
1734 | host_err = vfs_rmdir(dirp, rdentry); | 1776 | host_err = vfs_rmdir(dirp, rdentry); |
1777 | } | ||
1778 | |||
1735 | if (!host_err) | 1779 | if (!host_err) |
1736 | host_err = commit_metadata(fhp); | 1780 | host_err = commit_metadata(fhp); |
1737 | dput(rdentry); | 1781 | dput(rdentry); |