Patch "xfs: mark speculative prealloc CoW fork extents unwritten" has been added to the 4.10-stable tree
Subsystems (2)
This is a note to let you know that I've just added the patch titled
xfs: mark speculative prealloc CoW fork extents unwritten
to the 4.10-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
xfs-mark-speculative-prealloc-cow-fork-extents-unwritten.patch
and it can be found in the queue-4.10 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
5eda43000064a69a39fb7869cc63c9571535ad29Mon Sep 17 00:00:00 2001
From: "Darrick J. Wong" <[email protected]>
Date: Thu, 2 Feb 2017 15:14:02 -0800
Subject: xfs: mark speculative prealloc CoW fork extents unwritten
From: Darrick J. Wong <[email protected]>
commit 5eda43000064a69a39fb7869cc63c9571535ad29 upstream.
Christoph Hellwig pointed out that there's a potentially nasty race when performing simultaneous nearby directio cow writes:
"Thread 1 writes a range from B to c
" B --------- C
p
"a little later thread 2 writes from A to B
" A --------- B
p
[editor's note: the 'p' denote cowextsize boundaries, which I added to make this more clear]
"but the code preallocates beyond B into the range where thread "1 has just written, but ->end_io hasn't been called yet. "But once ->end_io is called thread 2 has already allocated "up to the extent size hint into the write range of thread 1, "so the end_io handler will splice the unintialized blocks from "that preallocation back into the file right after B."
We can avoid this race by ensuring that thread 1 cannot accidentally remap the blocks that thread 2 allocated (as part of speculative preallocation) as part of t2's write preparation in t1's end_io handler. The way we make this happen is by taking advantage of the unwritten extent flag as an intermediate step.
Recall that when we begin the process of writing data to shared blocks, we create a delayed allocation extent in the CoW fork:
D: --RRRRRRSSSRRRRRRRR---
C: ------DDDDDDD---------
When a thread prepares to CoW some dirty data out to disk, it will now convert the delalloc reservation into an /unwritten/ allocated extent in the cow fork. The da conversion code tries to opportunistically allocate as much of a (speculatively prealloc'd) extent as possible, so we may end up allocating a larger extent than we're actually writing out:
D: --RRRRRRSSSRRRRRRRR---
U: ------UUUUUUU---------
Next, we convert only the part of the extent that we're actively planning to write to normal (i.e. not unwritten) status:
D: --RRRRRRSSSRRRRRRRR---
U: ------UURRUUU---------
If the write succeeds, the end_cow function will now scan the relevant range of the CoW fork for real extents and remap only the real extents into the data fork:
D: --RRRRRRRRSRRRRRRRR---
U: ------UU--UUU---------
This ensures that we never obliterate valid data fork extents with unwritten blocks from the CoW fork.
--- fs/xfs/xfs_aops.c | 6 ++ fs/xfs/xfs_iomap.c | 2 fs/xfs/xfs_reflink.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++---- fs/xfs/xfs_reflink.h | 2 fs/xfs/xfs_trace.h | 8 ++- 5 files changed, 123 insertions(+), 11 deletions(-)
fs/xfs/xfs_aops.c +6 -0
@@ -481,6 +481,12 @@ xfs_submit_ioend( struct xfs_ioend *ioend, int status) { +/* Convert CoW extents to regular */ +if (!status && ioend->io_type ==XFS_IO_COW ) { + 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_type != XFS_IO_UNWRITTEN &&
fs/xfs/xfs_iomap.c +1 -1
@@ -685,7 +685,7 @@ xfs_iomap_write_allocate(int nres;if (whichfork ==XFS_COW_FORK ) - flags |=XFS_BMAPI_COWFORK ; + flags |=XFS_BMAPI_COWFORK |XFS_BMAPI_PREALLOC ;/* * Make sure that the dquots are there.
fs/xfs/xfs_reflink.c +108 -8
175 lines not on this page · raw message
fs/xfs/xfs_reflink.h +2 -0
@@ -30,6 +30,8 @@ extern int xfs_reflink_reserve_cow(struc struct xfs_bmbt_irec *imap, bool *shared);extern int xfs_reflink_allocate_cow_range (struct xfs_inode *ip,xfs_off_t offset,xfs_off_t count); +extern int xfs_reflink_convert_cow (struct xfs_inode *ip,xfs_off_t offset, +xfs_off_t count);extern bool xfs_reflink_find_cow_mapping (struct xfs_inode *ip,xfs_off_t offset,struct xfs_bmbt_irec *imap); extern void xfs_reflink_trim_irec_to_next_cow(struct xfs_inode *ip,
fs/xfs/xfs_trace.h +6 -2
@@ -3089,6 +3089,7 @@ DECLARE_EVENT_CLASS(xfs_inode_irec_class __field(xfs_fileoff_t, lblk) __field(xfs_extlen_t, len) __field(xfs_fsblock_t, pblk) +__field (int, state) ),TP_fast_assign ( __entry->dev = VFS_I(ip)->i_sb->s_dev;
@@ -3096,13 +3097,15 @@ DECLARE_EVENT_CLASS(xfs_inode_irec_class __entry->lblk = irec->br_startoff; __entry->len = irec->br_blockcount; __entry->pblk = irec->br_startblock; + __entry->state = irec->br_state; ), -TP_printk ("dev %d:%d ino 0x%llx lblk 0x%llx len 0x%x pblk %llu" , +TP_printk ("dev %d:%d ino 0x%llx lblk 0x%llx len 0x%x pblk %llu st %d" ,MAJOR (__entry->dev),MINOR (__entry->dev), __entry->ino, __entry->lblk, __entry->len, - __entry->pblk) + __entry->pblk, + __entry->state) );#define DEFINE_INODE_IREC_EVENT (name) \ DEFINE_EVENT(xfs_inode_irec_class, name, \
@@ -3242,6 +3245,7 @@ DEFINE_INODE_IREC_EVENT(xfs_reflink_trimDEFINE_INODE_IREC_EVENT (xfs_reflink_cow_alloc);DEFINE_INODE_IREC_EVENT (xfs_reflink_cow_found);DEFINE_INODE_IREC_EVENT (xfs_reflink_cow_enospc); +DEFINE_INODE_IREC_EVENT (xfs_reflink_convert_cow);DEFINE_RW_EVENT (xfs_reflink_reserve_cow);DEFINE_RW_EVENT (xfs_reflink_allocate_cow_range);
Patches currently in stable-queue which might be from [email protected] are
queue-4.10/xfs-correct-null-checks-and-error-processing-in-xfs_initialize_perag.patch queue-4.10/xfs-mark-speculative-prealloc-cow-fork-extents-unwritten.patch queue-4.10/xfs-fix-toctou-race-when-locking-an-inode-to-access-the-data-map.patch queue-4.10/xfs-use-iomap-new-flag-for-newly-allocated-delalloc-blocks.patch queue-4.10/xfs-handle-indlen-shortage-on-delalloc-extent-merge.patch queue-4.10/xfs-reject-all-unaligned-direct-writes-to-reflinked-files.patch queue-4.10/xfs-allow-unwritten-extents-in-the-cow-fork.patch queue-4.10/xfs-tune-down-agno-asserts-in-the-bmap-code.patch queue-4.10/xfs-verify-free-block-header-fields.patch queue-4.10/xfs-check-for-obviously-bad-level-values-in-the-bmbt-root.patch queue-4.10/xfs-don-t-fail-xfs_extent_busy-allocation.patch queue-4.10/xfs-sync-eofblocks-scans-under-iolock-are-livelock-prone.patch queue-4.10/xfs-pull-up-iolock-from-xfs_free_eofblocks.patch queue-4.10/xfs-fail-_dir_open-when-readahead-fails.patch queue-4.10/xfs-reset-b_first_retry_time-when-clear-the-retry-status-of-xfs_buf_t.patch queue-4.10/xfs-update-ctime-and-mtime-on-clone-destinatation-inodes.patch queue-4.10/xfs-split-indlen-reservations-fairly-when-under-reserved.patch queue-4.10/xfs-filter-out-obviously-bad-btree-pointers.patch queue-4.10/xfs-use-xfs_icluster_size_fsb-to-calculate-inode-chunk-alignment.patch queue-4.10/xfs-only-reclaim-unwritten-cow-extents-periodically.patch queue-4.10/xfs-try-any-ag-when-allocating-the-first-btree-block-when-reflinking.patch queue-4.10/xfs-fix-and-streamline-error-handling-in-xfs_end_io.patch queue-4.10/xfs-fix-eofblocks-race-with-file-extending-async-dio-writes.patch queue-4.10/xfs-fix-uninitialized-variable-in-_reflink_convert_cow.patch queue-4.10/xfs-don-t-reserve-blocks-for-right-shift-transactions.patch queue-4.10/xfs-use-xfs_icluster_size_fsb-to-calculate-inode-alignment-mask.patch