Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

[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>

authored by

Trond Myklebust and committed by
Linus Torvalds
717d44e8 bde8f00c

+72 -32
+1 -1
fs/nfs/dir.c
··· 532 532 533 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 536 if (res < 0) { 537 537 unlock_kernel(); 538 538 return res;
+67 -30
fs/nfs/inode.c
··· 665 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 + 705 + /** 706 + * nfs_revalidate_mapping_nolock - Revalidate the pagecache 707 + * @inode - pointer to host inode 708 + * @mapping - pointer to mapping 709 + */ 710 + int nfs_revalidate_mapping_nolock(struct inode *inode, struct address_space *mapping) 711 + { 712 + struct nfs_inode *nfsi = NFS_I(inode); 713 + int ret = 0; 714 + 715 + if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) 716 + || nfs_attribute_timeout(inode) || NFS_STALE(inode)) { 717 + ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); 718 + if (ret < 0) 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 + } 726 + 668 727 /** 669 728 * nfs_revalidate_mapping - Revalidate the pagecache 670 729 * @inode - pointer to host inode 671 730 * @mapping - pointer to mapping 731 + * 732 + * This version of the function will take the inode->i_mutex and attempt to 733 + * flush out all dirty data if it needs to invalidate the page cache. 672 734 */ 673 735 int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) 674 736 { 675 737 struct nfs_inode *nfsi = NFS_I(inode); 676 738 int ret = 0; 677 739 678 - if (NFS_STALE(inode)) 679 - ret = -ESTALE; 680 740 if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) 681 - || nfs_attribute_timeout(inode)) 741 + || nfs_attribute_timeout(inode) || NFS_STALE(inode)) { 682 742 ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); 683 - if (ret < 0) 684 - goto out; 685 - 686 - if (nfsi->cache_validity & NFS_INO_INVALID_DATA) { 687 - if (mapping->nrpages != 0) { 688 - if (S_ISREG(inode->i_mode)) { 689 - ret = nfs_sync_mapping(mapping); 690 - if (ret < 0) 691 - goto out; 692 - } 693 - ret = invalidate_inode_pages2(mapping); 694 - if (ret < 0) 695 - goto out; 696 - } 697 - spin_lock(&inode->i_lock); 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 - 706 - nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); 707 - dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", 708 - inode->i_sb->s_id, 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 748 out: 712 749 return ret; 713 750 }
+3 -1
fs/nfs/symlink.c
··· 50 50 { 51 51 struct inode *inode = dentry->d_inode; 52 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 56 if (err) 55 57 goto read_failed; 56 58 page = read_cache_page(&inode->i_data, 0,
+1
include/linux/nfs_fs.h
··· 308 308 extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode); 309 309 extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); 310 310 extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping); 311 + extern int nfs_revalidate_mapping_nolock(struct inode *inode, struct address_space *mapping); 311 312 extern int nfs_setattr(struct dentry *, struct iattr *); 312 313 extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr); 313 314 extern void nfs_begin_attr_update(struct inode *);