[XFS] Fix race in xfs_write() b/w dmapi callout and direct I/O checks.
authorLachlan McIlroy <lachlan@sgi.com>
Tue, 8 May 2007 03:50:12 +0000 (13:50 +1000)
committerTim Shimmin <tes@sgi.com>
Tue, 8 May 2007 03:50:12 +0000 (13:50 +1000)
In xfs_write() the iolock is dropped and reacquired in XFS_SEND_DATA()
which means that the file could change from not-cached to cached and we
need to redo the direct I/O checks. We should also redo the direct I/O
checks when the file size changes regardless if O_APPEND is set or not.

SGI-PV: 963483
SGI-Modid: xfs-linux-melb:xfs-kern:28440a

Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Signed-off-by: David Chinner <dgc@sgi.com>
Signed-off-by: Tim Shimmin <tes@sgi.com>
fs/xfs/linux-2.6/xfs_lrw.c

index 82ab792..b2a1beb 100644 (file)
@@ -724,34 +724,8 @@ start:
                goto out_unlock_mutex;
        }
 
-       if (ioflags & IO_ISDIRECT) {
-               xfs_buftarg_t   *target =
-                       (xip->i_d.di_flags & XFS_DIFLAG_REALTIME) ?
-                               mp->m_rtdev_targp : mp->m_ddev_targp;
-
-               if ((pos & target->bt_smask) || (count & target->bt_smask)) {
-                       xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
-                       return XFS_ERROR(-EINVAL);
-               }
-
-               if (!need_i_mutex && (VN_CACHED(vp) || pos > xip->i_size)) {
-                       xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
-                       iolock = XFS_IOLOCK_EXCL;
-                       locktype = VRWLOCK_WRITE;
-                       need_i_mutex = 1;
-                       mutex_lock(&inode->i_mutex);
-                       xfs_ilock(xip, XFS_ILOCK_EXCL|iolock);
-                       goto start;
-               }
-       }
-
-       new_size = pos + count;
-       if (new_size > xip->i_size)
-               io->io_new_size = new_size;
-
        if ((DM_EVENT_ENABLED(vp->v_vfsp, xip, DM_EVENT_WRITE) &&
            !(ioflags & IO_INVIS) && !eventsent)) {
-               loff_t          savedsize = pos;
                int             dmflags = FILP_DELAY_FLAG(file);
 
                if (need_i_mutex)
@@ -774,10 +748,35 @@ start:
                 * event prevents another call to XFS_SEND_DATA, which is
                 * what allows the size to change in the first place.
                 */
-               if ((file->f_flags & O_APPEND) && savedsize != xip->i_size)
+               if ((file->f_flags & O_APPEND) && pos != xip->i_size)
                        goto start;
        }
 
+       if (ioflags & IO_ISDIRECT) {
+               xfs_buftarg_t   *target =
+                       (xip->i_d.di_flags & XFS_DIFLAG_REALTIME) ?
+                               mp->m_rtdev_targp : mp->m_ddev_targp;
+
+               if ((pos & target->bt_smask) || (count & target->bt_smask)) {
+                       xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
+                       return XFS_ERROR(-EINVAL);
+               }
+
+               if (!need_i_mutex && (VN_CACHED(vp) || pos > xip->i_size)) {
+                       xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
+                       iolock = XFS_IOLOCK_EXCL;
+                       locktype = VRWLOCK_WRITE;
+                       need_i_mutex = 1;
+                       mutex_lock(&inode->i_mutex);
+                       xfs_ilock(xip, XFS_ILOCK_EXCL|iolock);
+                       goto start;
+               }
+       }
+
+       new_size = pos + count;
+       if (new_size > xip->i_size)
+               io->io_new_size = new_size;
+
        if (likely(!(ioflags & IO_INVIS))) {
                file_update_time(file);
                xfs_ichgtime_fast(xip, inode,