aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLachlan McIlroy <lachlan@redback.melbourne.sgi.com>2008-12-23 22:07:32 -0500
committerLachlan McIlroy <lachlan@redback.melbourne.sgi.com>2008-12-23 22:07:32 -0500
commit25051158bbed127e8672b43396c71c5eb610e5f1 (patch)
treeee93995d39c50956334157ca723f980f5d9fa7d2
parentad1ad968f4e7b06c75741575ea077e25a87da49a (diff)
[XFS] Fix race in xfs_write() between direct and buffered I/O with DMAPI
The iolock is dropped and re-acquired around the call to XFS_SEND_NAMESP(). While the iolock is released the file can become cached. We then 'goto retry' and - if we are doing direct I/O - mapping->nrpages may now be non zero but need_i_mutex will be zero and we will hit the WARN_ON(). Since we have dropped the I/O lock then the file size may have also changed so what we need to do here is 'goto start' like we do for the XFS_SEND_DATA() DMAPI event. We also need to update the filesize before releasing the iolock so that needs to be done before the XFS_SEND_NAMESP event. If we drop the iolock before setting the filesize we could race with a truncate. Reviewed-by: Christoph Hellwig <hch@infradead.org> Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
-rw-r--r--fs/xfs/linux-2.6/xfs_lrw.c27
1 files changed, 12 insertions, 15 deletions
diff --git a/fs/xfs/linux-2.6/xfs_lrw.c b/fs/xfs/linux-2.6/xfs_lrw.c
index 92ce34b3787a..7e90daa0d1d1 100644
--- a/fs/xfs/linux-2.6/xfs_lrw.c
+++ b/fs/xfs/linux-2.6/xfs_lrw.c
@@ -707,7 +707,6 @@ start:
707 } 707 }
708 } 708 }
709 709
710retry:
711 /* We can write back this queue in page reclaim */ 710 /* We can write back this queue in page reclaim */
712 current->backing_dev_info = mapping->backing_dev_info; 711 current->backing_dev_info = mapping->backing_dev_info;
713 712
@@ -763,6 +762,17 @@ retry:
763 if (ret == -EIOCBQUEUED && !(ioflags & IO_ISAIO)) 762 if (ret == -EIOCBQUEUED && !(ioflags & IO_ISAIO))
764 ret = wait_on_sync_kiocb(iocb); 763 ret = wait_on_sync_kiocb(iocb);
765 764
765 isize = i_size_read(inode);
766 if (unlikely(ret < 0 && ret != -EFAULT && *offset > isize))
767 *offset = isize;
768
769 if (*offset > xip->i_size) {
770 xfs_ilock(xip, XFS_ILOCK_EXCL);
771 if (*offset > xip->i_size)
772 xip->i_size = *offset;
773 xfs_iunlock(xip, XFS_ILOCK_EXCL);
774 }
775
766 if (ret == -ENOSPC && 776 if (ret == -ENOSPC &&
767 DM_EVENT_ENABLED(xip, DM_EVENT_NOSPACE) && !(ioflags & IO_INVIS)) { 777 DM_EVENT_ENABLED(xip, DM_EVENT_NOSPACE) && !(ioflags & IO_INVIS)) {
768 xfs_iunlock(xip, iolock); 778 xfs_iunlock(xip, iolock);
@@ -776,20 +786,7 @@ retry:
776 xfs_ilock(xip, iolock); 786 xfs_ilock(xip, iolock);
777 if (error) 787 if (error)
778 goto out_unlock_internal; 788 goto out_unlock_internal;
779 pos = xip->i_size; 789 goto start;
780 ret = 0;
781 goto retry;
782 }
783
784 isize = i_size_read(inode);
785 if (unlikely(ret < 0 && ret != -EFAULT && *offset > isize))
786 *offset = isize;
787
788 if (*offset > xip->i_size) {
789 xfs_ilock(xip, XFS_ILOCK_EXCL);
790 if (*offset > xip->i_size)
791 xip->i_size = *offset;
792 xfs_iunlock(xip, XFS_ILOCK_EXCL);
793 } 790 }
794 791
795 error = -ret; 792 error = -ret;