diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-01-24 14:54:55 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-01-24 15:31:06 -0500 |
commit | 717d44e849219781ced028a40fcc59d3e1f49e4c (patch) | |
tree | aa34a9b84377d18ff58901cc342b84c7e8b81dca /fs/nfs | |
parent | bde8f00ce64d9824a4f227c8594e335a1a10d044 (diff) |
[PATCH] NFS: Fix races in nfs_revalidate_mapping()
Prevent the call to invalidate_inode_pages2() from racing with file writes
by taking the inode->i_mutex across the page cache flush and invalidate.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/dir.c | 2 | ||||
-rw-r--r-- | fs/nfs/inode.c | 97 | ||||
-rw-r--r-- | fs/nfs/symlink.c | 4 |
3 files changed, 71 insertions, 32 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index dee3d6c0f194..d9ba8cb0ee75 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -532,7 +532,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
532 | 532 | ||
533 | lock_kernel(); | 533 | lock_kernel(); |
534 | 534 | ||
535 | res = nfs_revalidate_mapping(inode, filp->f_mapping); | 535 | res = nfs_revalidate_mapping_nolock(inode, filp->f_mapping); |
536 | if (res < 0) { | 536 | if (res < 0) { |
537 | unlock_kernel(); | 537 | unlock_kernel(); |
538 | return res; | 538 | return res; |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 63e470279309..d83498282837 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -665,49 +665,86 @@ int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) | |||
665 | return __nfs_revalidate_inode(server, inode); | 665 | return __nfs_revalidate_inode(server, inode); |
666 | } | 666 | } |
667 | 667 | ||
668 | static int nfs_invalidate_mapping_nolock(struct inode *inode, struct address_space *mapping) | ||
669 | { | ||
670 | struct nfs_inode *nfsi = NFS_I(inode); | ||
671 | |||
672 | if (mapping->nrpages != 0) { | ||
673 | int ret = invalidate_inode_pages2(mapping); | ||
674 | if (ret < 0) | ||
675 | return ret; | ||
676 | } | ||
677 | spin_lock(&inode->i_lock); | ||
678 | nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; | ||
679 | if (S_ISDIR(inode->i_mode)) { | ||
680 | memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); | ||
681 | /* This ensures we revalidate child dentries */ | ||
682 | nfsi->cache_change_attribute = jiffies; | ||
683 | } | ||
684 | spin_unlock(&inode->i_lock); | ||
685 | nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); | ||
686 | dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", | ||
687 | inode->i_sb->s_id, (long long)NFS_FILEID(inode)); | ||
688 | return 0; | ||
689 | } | ||
690 | |||
691 | static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping) | ||
692 | { | ||
693 | int ret = 0; | ||
694 | |||
695 | mutex_lock(&inode->i_mutex); | ||
696 | if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_DATA) { | ||
697 | ret = nfs_sync_mapping(mapping); | ||
698 | if (ret == 0) | ||
699 | ret = nfs_invalidate_mapping_nolock(inode, mapping); | ||
700 | } | ||
701 | mutex_unlock(&inode->i_mutex); | ||
702 | return ret; | ||
703 | } | ||
704 | |||
668 | /** | 705 | /** |
669 | * nfs_revalidate_mapping - Revalidate the pagecache | 706 | * nfs_revalidate_mapping_nolock - Revalidate the pagecache |
670 | * @inode - pointer to host inode | 707 | * @inode - pointer to host inode |
671 | * @mapping - pointer to mapping | 708 | * @mapping - pointer to mapping |
672 | */ | 709 | */ |
673 | int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) | 710 | int nfs_revalidate_mapping_nolock(struct inode *inode, struct address_space *mapping) |
674 | { | 711 | { |
675 | struct nfs_inode *nfsi = NFS_I(inode); | 712 | struct nfs_inode *nfsi = NFS_I(inode); |
676 | int ret = 0; | 713 | int ret = 0; |
677 | 714 | ||
678 | if (NFS_STALE(inode)) | ||
679 | ret = -ESTALE; | ||
680 | if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) | 715 | if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) |
681 | || nfs_attribute_timeout(inode)) | 716 | || nfs_attribute_timeout(inode) || NFS_STALE(inode)) { |
682 | ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); | 717 | ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); |
683 | if (ret < 0) | 718 | if (ret < 0) |
684 | goto out; | 719 | goto out; |
720 | } | ||
721 | if (nfsi->cache_validity & NFS_INO_INVALID_DATA) | ||
722 | ret = nfs_invalidate_mapping_nolock(inode, mapping); | ||
723 | out: | ||
724 | return ret; | ||
725 | } | ||
685 | 726 | ||
686 | if (nfsi->cache_validity & NFS_INO_INVALID_DATA) { | 727 | /** |
687 | if (mapping->nrpages != 0) { | 728 | * nfs_revalidate_mapping - Revalidate the pagecache |
688 | if (S_ISREG(inode->i_mode)) { | 729 | * @inode - pointer to host inode |
689 | ret = nfs_sync_mapping(mapping); | 730 | * @mapping - pointer to mapping |
690 | if (ret < 0) | 731 | * |
691 | goto out; | 732 | * This version of the function will take the inode->i_mutex and attempt to |
692 | } | 733 | * flush out all dirty data if it needs to invalidate the page cache. |
693 | ret = invalidate_inode_pages2(mapping); | 734 | */ |
694 | if (ret < 0) | 735 | int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) |
695 | goto out; | 736 | { |
696 | } | 737 | struct nfs_inode *nfsi = NFS_I(inode); |
697 | spin_lock(&inode->i_lock); | 738 | int ret = 0; |
698 | nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; | ||
699 | if (S_ISDIR(inode->i_mode)) { | ||
700 | memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); | ||
701 | /* This ensures we revalidate child dentries */ | ||
702 | nfsi->cache_change_attribute = jiffies; | ||
703 | } | ||
704 | spin_unlock(&inode->i_lock); | ||
705 | 739 | ||
706 | nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); | 740 | if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) |
707 | dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", | 741 | || nfs_attribute_timeout(inode) || NFS_STALE(inode)) { |
708 | inode->i_sb->s_id, | 742 | ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); |
709 | (long long)NFS_FILEID(inode)); | 743 | if (ret < 0) |
744 | goto out; | ||
710 | } | 745 | } |
746 | if (nfsi->cache_validity & NFS_INO_INVALID_DATA) | ||
747 | ret = nfs_invalidate_mapping(inode, mapping); | ||
711 | out: | 748 | out: |
712 | return ret; | 749 | return ret; |
713 | } | 750 | } |
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 6c686112cc03..525c136c7d8c 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c | |||
@@ -50,7 +50,9 @@ static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
50 | { | 50 | { |
51 | struct inode *inode = dentry->d_inode; | 51 | struct inode *inode = dentry->d_inode; |
52 | struct page *page; | 52 | struct page *page; |
53 | void *err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping)); | 53 | void *err; |
54 | |||
55 | err = ERR_PTR(nfs_revalidate_mapping_nolock(inode, inode->i_mapping)); | ||
54 | if (err) | 56 | if (err) |
55 | goto read_failed; | 57 | goto read_failed; |
56 | page = read_cache_page(&inode->i_data, 0, | 58 | page = read_cache_page(&inode->i_data, 0, |