xfs
[Top] [All Lists]

[PATCH 4/5] xfs: log file size updates at I/O completion time

To: xfs@xxxxxxxxxxx
Subject: [PATCH 4/5] xfs: log file size updates at I/O completion time
From: Christoph Hellwig <hch@xxxxxxxxxxxxx>
Date: Tue, 07 Feb 2012 13:10:41 -0500
References: <20120207181037.745771452@xxxxxxxxxxxxxxxxxxxxxx>
User-agent: quilt/0.48-1
Do not use unlogged metadata updates and the VFS dirty bit for updating
the file size after writeback.  In addition to causing various problems
with updates getting delayed for far too log this also drags in the
unscalable VFS dirty tracking, and is one of the few remaining unlogged
metadata updates.

Signed-off-by: Christoph Hellwig <hch@xxxxxx>

---
 fs/xfs/xfs_aops.c |  124 ++++++++++++++++++++++++++++++++++++++++--------------
 fs/xfs/xfs_aops.h |    2 
 2 files changed, 95 insertions(+), 31 deletions(-)

Index: xfs/fs/xfs/xfs_aops.c
===================================================================
--- xfs.orig/fs/xfs/xfs_aops.c  2012-01-30 11:56:54.716005146 +0100
+++ xfs/fs/xfs/xfs_aops.c       2012-01-30 11:56:55.469338493 +0100
@@ -26,6 +26,7 @@
 #include "xfs_bmap_btree.h"
 #include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_inode_item.h"
 #include "xfs_alloc.h"
 #include "xfs_error.h"
 #include "xfs_rw.h"
@@ -107,25 +108,65 @@ static inline bool xfs_ioend_is_append(s
                XFS_I(ioend->io_inode)->i_d.di_size;
 }
 
+STATIC int
+xfs_setfilesize_trans_alloc(
+       struct xfs_ioend        *ioend)
+{
+       struct xfs_mount        *mp = XFS_I(ioend->io_inode)->i_mount;
+       struct xfs_trans        *tp;
+       int                     error;
+
+       tp = xfs_trans_alloc(mp, XFS_TRANS_FSYNC_TS);
+
+       error = xfs_trans_reserve(tp, 0, XFS_FSYNC_TS_LOG_RES(mp), 0, 0, 0);
+       if (error) {
+               xfs_trans_cancel(tp, 0);
+               return error;
+       }
+
+       ioend->io_append_trans = tp;
+
+       /*
+        * We hand off the transaction to the completion thread now, so
+        * clear the flag here.
+        */
+       current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
+       return 0;
+}
+
 /*
  * Update on-disk file size now that data has been written to disk.
  */
-STATIC void
+STATIC int
 xfs_setfilesize(
        struct xfs_ioend        *ioend)
 {
        struct xfs_inode        *ip = XFS_I(ioend->io_inode);
+       struct xfs_trans        *tp = ioend->io_append_trans;
        xfs_fsize_t             isize;
 
+       /*
+        * The transaction was allocated in the I/O submission thread,
+        * thus we need to mark ourselves as beeing in a transaction
+        * manually.
+        */
+       current_set_flags_nested(&tp->t_pflags, PF_FSTRANS);
+
        xfs_ilock(ip, XFS_ILOCK_EXCL);
        isize = xfs_new_eof(ip, ioend->io_offset + ioend->io_size);
-       if (isize) {
-               trace_xfs_setfilesize(ip, ioend->io_offset, ioend->io_size);
-               ip->i_d.di_size = isize;
-               xfs_mark_inode_dirty(ip);
+       if (!isize) {
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+               xfs_trans_cancel(tp, 0);
+               return 0;
        }
 
-       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       trace_xfs_setfilesize(ip, ioend->io_offset, ioend->io_size);
+
+       ip->i_d.di_size = isize;
+       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+
+       return xfs_trans_commit(tp, 0);
 }
 
 /*
@@ -143,7 +184,7 @@ xfs_finish_ioend(
 
                if (ioend->io_type == IO_UNWRITTEN)
                        queue_work(mp->m_unwritten_workqueue, &ioend->io_work);
-               else if (xfs_ioend_is_append(ioend))
+               else if (ioend->io_append_trans)
                        queue_work(mp->m_data_workqueue, &ioend->io_work);
                else
                        xfs_destroy_ioend(ioend);
@@ -173,18 +214,26 @@ xfs_end_io(
         * range to normal written extens after the data I/O has finished.
         */
        if (ioend->io_type == IO_UNWRITTEN) {
+               if (ioend->io_append_trans) {
+                       ASSERT(ioend->io_isdirect);
+
+                       current_set_flags_nested(
+                               &ioend->io_append_trans->t_pflags, PF_FSTRANS);
+                       xfs_trans_cancel(ioend->io_append_trans, 0);
+               }
+
                error = xfs_iomap_write_unwritten(ip, ioend->io_offset,
                                                 ioend->io_size);
                if (error) {
                        ioend->io_error = -error;
                        goto done;
                }
+       } else if (ioend->io_append_trans) {
+               error = xfs_setfilesize(ioend);
+               if (error)
+                       ioend->io_error = error;
        } else {
-               /*
-                * We might have to update the on-disk file size after
-                * extending writes.
-                */
-               xfs_setfilesize(ioend);
+               ASSERT(!xfs_ioend_is_append(ioend));
        }
 
 done:
@@ -224,6 +273,7 @@ xfs_alloc_ioend(
         */
        atomic_set(&ioend->io_remaining, 1);
        ioend->io_isasync = 0;
+       ioend->io_isdirect = 0;
        ioend->io_error = 0;
        ioend->io_list = NULL;
        ioend->io_type = type;
@@ -234,6 +284,7 @@ xfs_alloc_ioend(
        ioend->io_size = 0;
        ioend->io_iocb = NULL;
        ioend->io_result = 0;
+       ioend->io_append_trans = NULL;
 
        INIT_WORK(&ioend->io_work, xfs_end_io);
        return ioend;
@@ -341,18 +392,9 @@ xfs_submit_ioend_bio(
        xfs_ioend_t             *ioend,
        struct bio              *bio)
 {
-       struct xfs_inode        *ip = XFS_I(ioend->io_inode);
        atomic_inc(&ioend->io_remaining);
        bio->bi_private = ioend;
        bio->bi_end_io = xfs_end_bio;
-
-       /*
-        * If the I/O is beyond EOF we mark the inode dirty immediately
-        * but don't update the inode size until I/O completion.
-        */
-       if (xfs_new_eof(ip, ioend->io_offset + ioend->io_size))
-               xfs_mark_inode_dirty(ip);
-
        submit_bio(wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC : WRITE, bio);
 }
 
@@ -1014,8 +1056,20 @@ xfs_vm_writepage(
                                  wbc, end_index);
        }
 
-       if (iohead)
+       if (iohead) {
+               /*
+                * Reserve log space if we might write beyond the on-disk
+                * inode size.
+                */
+               if (ioend->io_type != IO_UNWRITTEN &&
+                   xfs_ioend_is_append(ioend)) {
+                       err = xfs_setfilesize_trans_alloc(ioend);
+                       if (err)
+                               goto error;
+               }
+
                xfs_submit_ioend(wbc, iohead);
+       }
 
        return 0;
 
@@ -1295,17 +1349,26 @@ xfs_vm_direct_IO(
 {
        struct inode            *inode = iocb->ki_filp->f_mapping->host;
        struct block_device     *bdev = xfs_find_bdev_for_inode(inode);
+       struct xfs_ioend        *ioend = NULL;
        ssize_t                 ret;
 
        if (rw & WRITE) {
-               iocb->private = xfs_alloc_ioend(inode, IO_DIRECT);
+               size_t size = iov_length(iov, nr_segs);
+
+               iocb->private = ioend = xfs_alloc_ioend(inode, IO_DIRECT);
+               if (offset + size > XFS_I(inode)->i_d.di_size) {
+                       ret = xfs_setfilesize_trans_alloc(ioend);
+                       if (ret)
+                               goto destroy_ioend;
+                       ioend->io_isdirect = 1;
+               }
 
                ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iov,
                                            offset, nr_segs,
                                            xfs_get_blocks_direct,
                                            xfs_end_io_direct_write, NULL, 0);
                if (ret != -EIOCBQUEUED && iocb->private)
-                       xfs_destroy_ioend(iocb->private);
+                       goto destroy_ioend;
        } else {
                ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iov,
                                            offset, nr_segs,
@@ -1314,6 +1377,12 @@ xfs_vm_direct_IO(
        }
 
        return ret;
+
+destroy_ioend:
+       if (ioend->io_append_trans)
+               xfs_trans_cancel(ioend->io_append_trans, 0);
+       xfs_destroy_ioend(ioend);
+       return ret;
 }
 
 STATIC void
Index: xfs/fs/xfs/xfs_aops.h
===================================================================
--- xfs.orig/fs/xfs/xfs_aops.h  2012-01-30 11:56:50.096005059 +0100
+++ xfs/fs/xfs/xfs_aops.h       2012-01-30 11:56:55.469338493 +0100
@@ -46,12 +46,14 @@ typedef struct xfs_ioend {
        int                     io_error;       /* I/O error code */
        atomic_t                io_remaining;   /* hold count */
        unsigned int            io_isasync : 1; /* needs aio_complete */
+       unsigned int            io_isdirect : 1;/* direct I/O */
        struct inode            *io_inode;      /* file being written to */
        struct buffer_head      *io_buffer_head;/* buffer linked list head */
        struct buffer_head      *io_buffer_tail;/* buffer linked list tail */
        size_t                  io_size;        /* size of the extent */
        xfs_off_t               io_offset;      /* offset in the file */
        struct work_struct      io_work;        /* xfsdatad work queue */
+       struct xfs_trans        *io_append_trans;/* xact. for size update */
        struct kiocb            *io_iocb;
        int                     io_result;
 } xfs_ioend_t;

<Prev in Thread] Current Thread [Next in Thread>