| From david@fromorbit.com Fri Apr 2 11:12:53 2010 |
| From: Christoph Hellwig <hch@infradead.org> |
| Date: Fri, 12 Mar 2010 09:42:17 +1100 |
| Subject: xfs: fix locking for inode cache radix tree tag updates |
| To: stable@kernel.org |
| Cc: xfs@oss.sgi.com |
| Message-ID: <1268347337-7160-20-git-send-email-david@fromorbit.com> |
| |
| |
| From: Christoph Hellwig <hch@infradead.org> |
| |
| commit f1f724e4b523d444c5a598d74505aefa3d6844d2 upstream |
| |
| The radix-tree code requires it's users to serialize tag updates |
| against other updates to the tree. While XFS protects tag updates |
| against each other it does not serialize them against updates of the |
| tree contents, which can lead to tag corruption. Fix the inode |
| cache to always take pag_ici_lock in exclusive mode when updating |
| radix tree tags. |
| |
| Signed-off-by: Christoph Hellwig <hch@lst.de> |
| Reported-by: Patrick Schreurs <patrick@news-service.com> |
| Tested-by: Patrick Schreurs <patrick@news-service.com> |
| Signed-off-by: Alex Elder <aelder@sgi.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| --- |
| fs/xfs/linux-2.6/xfs_sync.c | 4 ++-- |
| fs/xfs/xfs_iget.c | 19 +++++++++++++------ |
| 2 files changed, 15 insertions(+), 8 deletions(-) |
| |
| --- a/fs/xfs/linux-2.6/xfs_sync.c |
| +++ b/fs/xfs/linux-2.6/xfs_sync.c |
| @@ -692,12 +692,12 @@ xfs_inode_set_reclaim_tag( |
| xfs_mount_t *mp = ip->i_mount; |
| xfs_perag_t *pag = xfs_get_perag(mp, ip->i_ino); |
| |
| - read_lock(&pag->pag_ici_lock); |
| + write_lock(&pag->pag_ici_lock); |
| spin_lock(&ip->i_flags_lock); |
| __xfs_inode_set_reclaim_tag(pag, ip); |
| __xfs_iflags_set(ip, XFS_IRECLAIMABLE); |
| spin_unlock(&ip->i_flags_lock); |
| - read_unlock(&pag->pag_ici_lock); |
| + write_unlock(&pag->pag_ici_lock); |
| xfs_put_perag(mp, pag); |
| } |
| |
| --- a/fs/xfs/xfs_iget.c |
| +++ b/fs/xfs/xfs_iget.c |
| @@ -228,13 +228,12 @@ xfs_iget_cache_hit( |
| xfs_itrace_exit_tag(ip, "xfs_iget.alloc"); |
| |
| /* |
| - * We need to set XFS_INEW atomically with clearing the |
| - * reclaimable tag so that we do have an indicator of the |
| - * inode still being initialized. |
| + * We need to set XFS_IRECLAIM to prevent xfs_reclaim_inode |
| + * from stomping over us while we recycle the inode. We can't |
| + * clear the radix tree reclaimable tag yet as it requires |
| + * pag_ici_lock to be held exclusive. |
| */ |
| - ip->i_flags |= XFS_INEW; |
| - ip->i_flags &= ~XFS_IRECLAIMABLE; |
| - __xfs_inode_clear_reclaim_tag(mp, pag, ip); |
| + ip->i_flags |= XFS_IRECLAIM; |
| |
| spin_unlock(&ip->i_flags_lock); |
| read_unlock(&pag->pag_ici_lock); |
| @@ -253,7 +252,15 @@ xfs_iget_cache_hit( |
| __xfs_inode_set_reclaim_tag(pag, ip); |
| goto out_error; |
| } |
| + |
| + write_lock(&pag->pag_ici_lock); |
| + spin_lock(&ip->i_flags_lock); |
| + ip->i_flags &= ~(XFS_IRECLAIMABLE | XFS_IRECLAIM); |
| + ip->i_flags |= XFS_INEW; |
| + __xfs_inode_clear_reclaim_tag(mp, pag, ip); |
| inode->i_state = I_LOCK|I_NEW; |
| + spin_unlock(&ip->i_flags_lock); |
| + write_unlock(&pag->pag_ici_lock); |
| } else { |
| /* If the VFS inode is being torn down, pause and try again. */ |
| if (!igrab(inode)) { |