xfs: remove the fork fields in the writepage_ctx and ioend
[linux-2.6-block.git] / fs / xfs / xfs_aops.c
index f16d5f196c6b15cfb7cd84807984a76cf10b52dd..00fe40b35f72bc20b939f8640c4ece14adb68bfb 100644 (file)
@@ -22,8 +22,7 @@
  * structure owned by writepages passed to individual writepage calls
  */
 struct xfs_writepage_ctx {
-       struct xfs_bmbt_irec    imap;
-       int                     fork;
+       struct iomap            iomap;
        unsigned int            data_seq;
        unsigned int            cow_seq;
        struct xfs_ioend        *ioend;
@@ -116,6 +115,22 @@ xfs_destroy_ioend(
        }
 }
 
+static void
+xfs_destroy_ioends(
+       struct xfs_ioend        *ioend,
+       int                     error)
+{
+       struct list_head        tmp;
+
+       list_replace_init(&ioend->io_list, &tmp);
+       xfs_destroy_ioend(ioend, error);
+       while ((ioend = list_first_entry_or_null(&tmp, struct xfs_ioend,
+                       io_list))) {
+               list_del_init(&ioend->io_list);
+               xfs_destroy_ioend(ioend, error);
+       }
+}
+
 /*
  * Fast and loose check if this write could update the on-disk inode size.
  */
@@ -137,7 +152,7 @@ xfs_setfilesize_trans_alloc(
        if (error)
                return error;
 
-       ioend->io_append_trans = tp;
+       ioend->io_private = tp;
 
        /*
         * We may pass freeze protection with a transaction.  So tell lockdep
@@ -204,7 +219,7 @@ xfs_setfilesize_ioend(
        int                     error)
 {
        struct xfs_inode        *ip = XFS_I(ioend->io_inode);
-       struct xfs_trans        *tp = ioend->io_append_trans;
+       struct xfs_trans        *tp = ioend->io_private;
 
        /*
         * The transaction may have been allocated in the I/O submission thread,
@@ -230,7 +245,6 @@ STATIC void
 xfs_end_ioend(
        struct xfs_ioend        *ioend)
 {
-       struct list_head        ioend_list;
        struct xfs_inode        *ip = XFS_I(ioend->io_inode);
        xfs_off_t               offset = ioend->io_offset;
        size_t                  size = ioend->io_size;
@@ -257,7 +271,7 @@ xfs_end_ioend(
         */
        error = blk_status_to_errno(ioend->io_bio->bi_status);
        if (unlikely(error)) {
-               if (ioend->io_fork == XFS_COW_FORK)
+               if (ioend->io_flags & IOMAP_F_SHARED)
                        xfs_reflink_cancel_cow_range(ip, offset, size, true);
                goto done;
        }
@@ -265,26 +279,17 @@ xfs_end_ioend(
        /*
         * Success: commit the COW or unwritten blocks if needed.
         */
-       if (ioend->io_fork == XFS_COW_FORK)
+       if (ioend->io_flags & IOMAP_F_SHARED)
                error = xfs_reflink_end_cow(ip, offset, size);
-       else if (ioend->io_state == XFS_EXT_UNWRITTEN)
+       else if (ioend->io_type == IOMAP_UNWRITTEN)
                error = xfs_iomap_write_unwritten(ip, offset, size, false);
        else
-               ASSERT(!xfs_ioend_is_append(ioend) || ioend->io_append_trans);
+               ASSERT(!xfs_ioend_is_append(ioend) || ioend->io_private);
 
 done:
-       if (ioend->io_append_trans)
+       if (ioend->io_private)
                error = xfs_setfilesize_ioend(ioend, error);
-       list_replace_init(&ioend->io_list, &ioend_list);
-       xfs_destroy_ioend(ioend, error);
-
-       while (!list_empty(&ioend_list)) {
-               ioend = list_first_entry(&ioend_list, struct xfs_ioend,
-                               io_list);
-               list_del_init(&ioend->io_list);
-               xfs_destroy_ioend(ioend, error);
-       }
-
+       xfs_destroy_ioends(ioend, error);
        memalloc_nofs_restore(nofs_flag);
 }
 
@@ -298,10 +303,11 @@ xfs_ioend_can_merge(
 {
        if (ioend->io_bio->bi_status != next->io_bio->bi_status)
                return false;
-       if ((ioend->io_fork == XFS_COW_FORK) ^ (next->io_fork == XFS_COW_FORK))
+       if ((ioend->io_flags & IOMAP_F_SHARED) ^
+           (next->io_flags & IOMAP_F_SHARED))
                return false;
-       if ((ioend->io_state == XFS_EXT_UNWRITTEN) ^
-           (next->io_state == XFS_EXT_UNWRITTEN))
+       if ((ioend->io_type == IOMAP_UNWRITTEN) ^
+           (next->io_type == IOMAP_UNWRITTEN))
                return false;
        if (ioend->io_offset + ioend->io_size != next->io_offset)
                return false;
@@ -315,13 +321,13 @@ xfs_ioend_can_merge(
  * as it is guaranteed to be clean.
  */
 static void
-xfs_ioend_merge_append_transactions(
+xfs_ioend_merge_private(
        struct xfs_ioend        *ioend,
        struct xfs_ioend        *next)
 {
-       if (!ioend->io_append_trans) {
-               ioend->io_append_trans = next->io_append_trans;
-               next->io_append_trans = NULL;
+       if (!ioend->io_private) {
+               ioend->io_private = next->io_private;
+               next->io_private = NULL;
        } else {
                xfs_setfilesize_ioend(next, -ECANCELED);
        }
@@ -333,17 +339,18 @@ xfs_ioend_try_merge(
        struct xfs_ioend        *ioend,
        struct list_head        *more_ioends)
 {
-       struct xfs_ioend        *next_ioend;
+       struct xfs_ioend        *next;
+
+       INIT_LIST_HEAD(&ioend->io_list);
 
-       while (!list_empty(more_ioends)) {
-               next_ioend = list_first_entry(more_ioends, struct xfs_ioend,
-                               io_list);
-               if (!xfs_ioend_can_merge(ioend, next_ioend))
+       while ((next = list_first_entry_or_null(more_ioends, struct xfs_ioend,
+                       io_list))) {
+               if (!xfs_ioend_can_merge(ioend, next))
                        break;
-               list_move_tail(&next_ioend->io_list, &ioend->io_list);
-               ioend->io_size += next_ioend->io_size;
-               if (next_ioend->io_append_trans)
-                       xfs_ioend_merge_append_transactions(ioend, next_ioend);
+               list_move_tail(&next->io_list, &ioend->io_list);
+               ioend->io_size += next->io_size;
+               if (next->io_private)
+                       xfs_ioend_merge_private(ioend, next);
        }
 }
 
@@ -366,33 +373,44 @@ xfs_ioend_compare(
        return 0;
 }
 
+static void
+xfs_sort_ioends(
+       struct list_head        *ioend_list)
+{
+       list_sort(NULL, ioend_list, xfs_ioend_compare);
+}
+
 /* Finish all pending io completions. */
 void
 xfs_end_io(
        struct work_struct      *work)
 {
-       struct xfs_inode        *ip;
+       struct xfs_inode        *ip =
+               container_of(work, struct xfs_inode, i_ioend_work);
        struct xfs_ioend        *ioend;
-       struct list_head        completion_list;
+       struct list_head        tmp;
        unsigned long           flags;
 
-       ip = container_of(work, struct xfs_inode, i_ioend_work);
-
        spin_lock_irqsave(&ip->i_ioend_lock, flags);
-       list_replace_init(&ip->i_ioend_list, &completion_list);
+       list_replace_init(&ip->i_ioend_list, &tmp);
        spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
 
-       list_sort(NULL, &completion_list, xfs_ioend_compare);
-
-       while (!list_empty(&completion_list)) {
-               ioend = list_first_entry(&completion_list, struct xfs_ioend,
-                               io_list);
+       xfs_sort_ioends(&tmp);
+       while ((ioend = list_first_entry_or_null(&tmp, struct xfs_ioend,
+                       io_list))) {
                list_del_init(&ioend->io_list);
-               xfs_ioend_try_merge(ioend, &completion_list);
+               xfs_ioend_try_merge(ioend, &tmp);
                xfs_end_ioend(ioend);
        }
 }
 
+static inline bool xfs_ioend_needs_workqueue(struct xfs_ioend *ioend)
+{
+       return ioend->io_private ||
+               ioend->io_type == IOMAP_UNWRITTEN ||
+               (ioend->io_flags & IOMAP_F_SHARED);
+}
+
 STATIC void
 xfs_end_bio(
        struct bio              *bio)
@@ -402,9 +420,7 @@ xfs_end_bio(
        struct xfs_mount        *mp = ip->i_mount;
        unsigned long           flags;
 
-       if (ioend->io_fork == XFS_COW_FORK ||
-           ioend->io_state == XFS_EXT_UNWRITTEN ||
-           ioend->io_append_trans != NULL) {
+       if (xfs_ioend_needs_workqueue(ioend)) {
                spin_lock_irqsave(&ip->i_ioend_lock, flags);
                if (list_empty(&ip->i_ioend_list))
                        WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue,
@@ -423,17 +439,17 @@ static bool
 xfs_imap_valid(
        struct xfs_writepage_ctx        *wpc,
        struct xfs_inode                *ip,
-       xfs_fileoff_t                   offset_fsb)
+       loff_t                          offset)
 {
-       if (offset_fsb < wpc->imap.br_startoff ||
-           offset_fsb >= wpc->imap.br_startoff + wpc->imap.br_blockcount)
+       if (offset < wpc->iomap.offset ||
+           offset >= wpc->iomap.offset + wpc->iomap.length)
                return false;
        /*
         * If this is a COW mapping, it is sufficient to check that the mapping
         * covers the offset. Be careful to check this first because the caller
         * can revalidate a COW mapping without updating the data seqno.
         */
-       if (wpc->fork == XFS_COW_FORK)
+       if (wpc->iomap.flags & IOMAP_F_SHARED)
                return true;
 
        /*
@@ -453,7 +469,7 @@ xfs_imap_valid(
 
 /*
  * Pass in a dellalloc extent and convert it to real extents, return the real
- * extent that maps offset_fsb in wpc->imap.
+ * extent that maps offset_fsb in wpc->iomap.
  *
  * The current page is held locked so nothing could have removed the block
  * backing offset_fsb, although it could have moved from the COW to the data
@@ -463,23 +479,24 @@ static int
 xfs_convert_blocks(
        struct xfs_writepage_ctx *wpc,
        struct xfs_inode        *ip,
-       xfs_fileoff_t           offset_fsb)
+       int                     whichfork,
+       loff_t                  offset)
 {
        int                     error;
 
        /*
-        * Attempt to allocate whatever delalloc extent currently backs
-        * offset_fsb and put the result into wpc->imap.  Allocate in a loop
-        * because it may take several attempts to allocate real blocks for a
-        * contiguous delalloc extent if free space is sufficiently fragmented.
+        * Attempt to allocate whatever delalloc extent currently backs offset
+        * and put the result into wpc->iomap.  Allocate in a loop because it
+        * may take several attempts to allocate real blocks for a contiguous
+        * delalloc extent if free space is sufficiently fragmented.
         */
        do {
-               error = xfs_bmapi_convert_delalloc(ip, wpc->fork, offset_fsb,
-                               &wpc->imap, wpc->fork == XFS_COW_FORK ?
+               error = xfs_bmapi_convert_delalloc(ip, whichfork, offset,
+                               &wpc->iomap, whichfork == XFS_COW_FORK ?
                                        &wpc->cow_seq : &wpc->data_seq);
                if (error)
                        return error;
-       } while (wpc->imap.br_startoff + wpc->imap.br_blockcount <= offset_fsb);
+       } while (wpc->iomap.offset + wpc->iomap.length <= offset);
 
        return 0;
 }
@@ -496,6 +513,7 @@ xfs_map_blocks(
        xfs_fileoff_t           offset_fsb = XFS_B_TO_FSBT(mp, offset);
        xfs_fileoff_t           end_fsb = XFS_B_TO_FSB(mp, offset + count);
        xfs_fileoff_t           cow_fsb = NULLFILEOFF;
+       int                     whichfork = XFS_DATA_FORK;
        struct xfs_bmbt_irec    imap;
        struct xfs_iext_cursor  icur;
        int                     retries = 0;
@@ -519,7 +537,7 @@ xfs_map_blocks(
         * against concurrent updates and provides a memory barrier on the way
         * out that ensures that we always see the current value.
         */
-       if (xfs_imap_valid(wpc, ip, offset_fsb))
+       if (xfs_imap_valid(wpc, ip, offset))
                return 0;
 
        /*
@@ -544,7 +562,7 @@ retry:
                wpc->cow_seq = READ_ONCE(ip->i_cowfp->if_seq);
                xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
-               wpc->fork = XFS_COW_FORK;
+               whichfork = XFS_COW_FORK;
                goto allocate_blocks;
        }
 
@@ -552,7 +570,7 @@ retry:
         * No COW extent overlap. Revalidate now that we may have updated
         * ->cow_seq. If the data mapping is still valid, we're done.
         */
-       if (xfs_imap_valid(wpc, ip, offset_fsb)) {
+       if (xfs_imap_valid(wpc, ip, offset)) {
                xfs_iunlock(ip, XFS_ILOCK_SHARED);
                return 0;
        }
@@ -567,8 +585,6 @@ retry:
        wpc->data_seq = READ_ONCE(ip->i_df.if_seq);
        xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
-       wpc->fork = XFS_DATA_FORK;
-
        /* landed in a hole or beyond EOF? */
        if (imap.br_startoff > offset_fsb) {
                imap.br_blockcount = imap.br_startoff - offset_fsb;
@@ -592,11 +608,11 @@ retry:
            isnullstartblock(imap.br_startblock))
                goto allocate_blocks;
 
-       wpc->imap = imap;
-       trace_xfs_map_blocks_found(ip, offset, count, wpc->fork, &imap);
+       xfs_bmbt_to_iomap(ip, &wpc->iomap, &imap, 0);
+       trace_xfs_map_blocks_found(ip, offset, count, whichfork, &imap);
        return 0;
 allocate_blocks:
-       error = xfs_convert_blocks(wpc, ip, offset_fsb);
+       error = xfs_convert_blocks(wpc, ip, whichfork, offset);
        if (error) {
                /*
                 * If we failed to find the extent in the COW fork we might have
@@ -605,7 +621,7 @@ allocate_blocks:
                 * the former case, but prevent additional retries to avoid
                 * looping forever for the latter case.
                 */
-               if (error == -EAGAIN && wpc->fork == XFS_COW_FORK && !retries++)
+               if (error == -EAGAIN && whichfork == XFS_COW_FORK && !retries++)
                        goto retry;
                ASSERT(error != -EAGAIN);
                return error;
@@ -616,13 +632,16 @@ allocate_blocks:
         * original delalloc one.  Trim the return extent to the next COW
         * boundary again to force a re-lookup.
         */
-       if (wpc->fork != XFS_COW_FORK && cow_fsb != NULLFILEOFF &&
-           cow_fsb < wpc->imap.br_startoff + wpc->imap.br_blockcount)
-               wpc->imap.br_blockcount = cow_fsb - wpc->imap.br_startoff;
+       if (whichfork != XFS_COW_FORK && cow_fsb != NULLFILEOFF) {
+               loff_t          cow_offset = XFS_FSB_TO_B(mp, cow_fsb);
 
-       ASSERT(wpc->imap.br_startoff <= offset_fsb);
-       ASSERT(wpc->imap.br_startoff + wpc->imap.br_blockcount > offset_fsb);
-       trace_xfs_map_blocks_alloc(ip, offset, count, wpc->fork, &imap);
+               if (cow_offset < wpc->iomap.offset + wpc->iomap.length)
+                       wpc->iomap.length = cow_offset - wpc->iomap.offset;
+       }
+
+       ASSERT(wpc->iomap.offset <= offset);
+       ASSERT(wpc->iomap.offset + wpc->iomap.length > offset);
+       trace_xfs_map_blocks_alloc(ip, offset, count, whichfork, &imap);
        return 0;
 }
 
@@ -656,17 +675,17 @@ xfs_submit_ioend(
        nofs_flag = memalloc_nofs_save();
 
        /* Convert CoW extents to regular */
-       if (!status && ioend->io_fork == XFS_COW_FORK) {
+       if (!status && (ioend->io_flags & IOMAP_F_SHARED)) {
                status = xfs_reflink_convert_cow(XFS_I(ioend->io_inode),
                                ioend->io_offset, ioend->io_size);
        }
 
        /* Reserve log space if we might write beyond the on-disk inode size. */
        if (!status &&
-           (ioend->io_fork == XFS_COW_FORK ||
-            ioend->io_state != XFS_EXT_UNWRITTEN) &&
+           ((ioend->io_flags & IOMAP_F_SHARED) ||
+            ioend->io_type != IOMAP_UNWRITTEN) &&
            xfs_ioend_is_append(ioend) &&
-           !ioend->io_append_trans)
+           !ioend->io_private)
                status = xfs_setfilesize_trans_alloc(ioend);
 
        memalloc_nofs_restore(nofs_flag);
@@ -693,10 +712,8 @@ xfs_submit_ioend(
 static struct xfs_ioend *
 xfs_alloc_ioend(
        struct inode            *inode,
-       int                     fork,
-       xfs_exntst_t            state,
+       struct xfs_writepage_ctx *wpc,
        xfs_off_t               offset,
-       struct block_device     *bdev,
        sector_t                sector,
        struct writeback_control *wbc)
 {
@@ -704,7 +721,7 @@ xfs_alloc_ioend(
        struct bio              *bio;
 
        bio = bio_alloc_bioset(GFP_NOFS, BIO_MAX_PAGES, &xfs_ioend_bioset);
-       bio_set_dev(bio, bdev);
+       bio_set_dev(bio, wpc->iomap.bdev);
        bio->bi_iter.bi_sector = sector;
        bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
        bio->bi_write_hint = inode->i_write_hint;
@@ -712,12 +729,12 @@ xfs_alloc_ioend(
 
        ioend = container_of(bio, struct xfs_ioend, io_inline_bio);
        INIT_LIST_HEAD(&ioend->io_list);
-       ioend->io_fork = fork;
-       ioend->io_state = state;
+       ioend->io_type = wpc->iomap.type;
+       ioend->io_flags = wpc->iomap.flags;
        ioend->io_inode = inode;
        ioend->io_size = 0;
        ioend->io_offset = offset;
-       ioend->io_append_trans = NULL;
+       ioend->io_private = NULL;
        ioend->io_bio = bio;
        return ioend;
 }
@@ -747,6 +764,24 @@ xfs_chain_bio(
        return new;
 }
 
+static bool
+xfs_can_add_to_ioend(
+       struct xfs_writepage_ctx *wpc,
+       xfs_off_t               offset,
+       sector_t                sector)
+{
+       if ((wpc->iomap.flags & IOMAP_F_SHARED) !=
+           (wpc->ioend->io_flags & IOMAP_F_SHARED))
+               return false;
+       if (wpc->iomap.type != wpc->ioend->io_type)
+               return false;
+       if (offset != wpc->ioend->io_offset + wpc->ioend->io_size)
+               return false;
+       if (sector != bio_end_sector(wpc->ioend->io_bio))
+               return false;
+       return true;
+}
+
 /*
  * Test to see if we have an existing ioend structure that we could append to
  * first, otherwise finish off the current ioend and start another.
@@ -761,26 +796,15 @@ xfs_add_to_ioend(
        struct writeback_control *wbc,
        struct list_head        *iolist)
 {
-       struct xfs_inode        *ip = XFS_I(inode);
-       struct xfs_mount        *mp = ip->i_mount;
-       struct block_device     *bdev = xfs_find_bdev_for_inode(inode);
+       sector_t                sector = iomap_sector(&wpc->iomap, offset);
        unsigned                len = i_blocksize(inode);
        unsigned                poff = offset & (PAGE_SIZE - 1);
        bool                    merged, same_page = false;
-       sector_t                sector;
-
-       sector = xfs_fsb_to_db(ip, wpc->imap.br_startblock) +
-               ((offset - XFS_FSB_TO_B(mp, wpc->imap.br_startoff)) >> 9);
 
-       if (!wpc->ioend ||
-           wpc->fork != wpc->ioend->io_fork ||
-           wpc->imap.br_state != wpc->ioend->io_state ||
-           sector != bio_end_sector(wpc->ioend->io_bio) ||
-           offset != wpc->ioend->io_offset + wpc->ioend->io_size) {
+       if (!wpc->ioend || !xfs_can_add_to_ioend(wpc, offset, sector)) {
                if (wpc->ioend)
                        list_add(&wpc->ioend->io_list, iolist);
-               wpc->ioend = xfs_alloc_ioend(inode, wpc->fork,
-                               wpc->imap.br_state, offset, bdev, sector, wbc);
+               wpc->ioend = xfs_alloc_ioend(inode, wpc, offset, sector, wbc);
        }
 
        merged = __bio_try_merge_page(wpc->ioend->io_bio, page, len, poff,
@@ -894,7 +918,7 @@ xfs_writepage_map(
                error = xfs_map_blocks(wpc, inode, file_offset);
                if (error)
                        break;
-               if (wpc->imap.br_startblock == HOLESTARTBLOCK)
+               if (wpc->iomap.type == IOMAP_HOLE)
                        continue;
                xfs_add_to_ioend(inode, file_offset, page, iop, wpc, wbc,
                                 &submit_list);