mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 09:19:51 +00:00
![Dave Chinner](/assets/img/avatar_default.png)
On a sub-page block size filesystem, fsx is failing with a data corruption after a series of operations involving copying a file with the destination offset beyond EOF of the destination of the file: 8093(157 mod 256): TRUNCATE DOWN from 0x7a120 to 0x50000 ******WWWW 8094(158 mod 256): INSERT 0x25000 thru 0x25fff (0x1000 bytes) 8095(159 mod 256): COPY 0x18000 thru 0x1afff (0x3000 bytes) to 0x2f400 8096(160 mod 256): WRITE 0x5da00 thru 0x651ff (0x7800 bytes) HOLE 8097(161 mod 256): COPY 0x2000 thru 0x5fff (0x4000 bytes) to 0x6fc00 The second copy here is beyond EOF, and it is to sub-page (4k) but block aligned (1k) offset. The clone runs the EOF zeroing, landing in a pre-existing post-eof delalloc extent. This zeroes the post-eof extents in the page cache just fine, dirtying the pages correctly. The problem is that xfs_reflink_remap_prep() now truncates the page cache over the range that it is copying it to, and rounds that down to cover the entire start page. This removes the dirty page over the delalloc extent from the page cache without having written it back. Hence later, when the page cache is flushed, the page at offset 0x6f000 has not been written back and hence exposes stale data, which fsx trips over less than 10 operations later. Fix this by changing xfs_reflink_remap_prep() to use xfs_flush_unmap_range(). Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
86 lines
2.8 KiB
C
86 lines
2.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2000-2006 Silicon Graphics, Inc.
|
|
* All Rights Reserved.
|
|
*/
|
|
#ifndef __XFS_BMAP_UTIL_H__
|
|
#define __XFS_BMAP_UTIL_H__
|
|
|
|
/* Kernel only BMAP related definitions and functions */
|
|
|
|
struct xfs_bmbt_irec;
|
|
struct xfs_extent_free_item;
|
|
struct xfs_ifork;
|
|
struct xfs_inode;
|
|
struct xfs_mount;
|
|
struct xfs_trans;
|
|
struct xfs_bmalloca;
|
|
|
|
#ifdef CONFIG_XFS_RT
|
|
int xfs_bmap_rtalloc(struct xfs_bmalloca *ap);
|
|
#else /* !CONFIG_XFS_RT */
|
|
/*
|
|
* Attempts to allocate RT extents when RT is disable indicates corruption and
|
|
* should trigger a shutdown.
|
|
*/
|
|
static inline int
|
|
xfs_bmap_rtalloc(struct xfs_bmalloca *ap)
|
|
{
|
|
return -EFSCORRUPTED;
|
|
}
|
|
#endif /* CONFIG_XFS_RT */
|
|
|
|
int xfs_bmap_eof(struct xfs_inode *ip, xfs_fileoff_t endoff,
|
|
int whichfork, int *eof);
|
|
int xfs_bmap_punch_delalloc_range(struct xfs_inode *ip,
|
|
xfs_fileoff_t start_fsb, xfs_fileoff_t length);
|
|
|
|
struct kgetbmap {
|
|
__s64 bmv_offset; /* file offset of segment in blocks */
|
|
__s64 bmv_block; /* starting block (64-bit daddr_t) */
|
|
__s64 bmv_length; /* length of segment, blocks */
|
|
__s32 bmv_oflags; /* output flags */
|
|
};
|
|
int xfs_getbmap(struct xfs_inode *ip, struct getbmapx *bmv,
|
|
struct kgetbmap *out);
|
|
|
|
/* functions in xfs_bmap.c that are only needed by xfs_bmap_util.c */
|
|
int xfs_bmap_extsize_align(struct xfs_mount *mp, struct xfs_bmbt_irec *gotp,
|
|
struct xfs_bmbt_irec *prevp, xfs_extlen_t extsz,
|
|
int rt, int eof, int delay, int convert,
|
|
xfs_fileoff_t *offp, xfs_extlen_t *lenp);
|
|
void xfs_bmap_adjacent(struct xfs_bmalloca *ap);
|
|
int xfs_bmap_last_extent(struct xfs_trans *tp, struct xfs_inode *ip,
|
|
int whichfork, struct xfs_bmbt_irec *rec,
|
|
int *is_empty);
|
|
|
|
/* preallocation and hole punch interface */
|
|
int xfs_alloc_file_space(struct xfs_inode *ip, xfs_off_t offset,
|
|
xfs_off_t len, int alloc_type);
|
|
int xfs_free_file_space(struct xfs_inode *ip, xfs_off_t offset,
|
|
xfs_off_t len);
|
|
int xfs_zero_file_space(struct xfs_inode *ip, xfs_off_t offset,
|
|
xfs_off_t len);
|
|
int xfs_collapse_file_space(struct xfs_inode *, xfs_off_t offset,
|
|
xfs_off_t len);
|
|
int xfs_insert_file_space(struct xfs_inode *, xfs_off_t offset,
|
|
xfs_off_t len);
|
|
|
|
/* EOF block manipulation functions */
|
|
bool xfs_can_free_eofblocks(struct xfs_inode *ip, bool force);
|
|
int xfs_free_eofblocks(struct xfs_inode *ip);
|
|
|
|
int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip,
|
|
struct xfs_swapext *sx);
|
|
|
|
xfs_daddr_t xfs_fsb_to_db(struct xfs_inode *ip, xfs_fsblock_t fsb);
|
|
|
|
xfs_extnum_t xfs_bmap_count_leaves(struct xfs_ifork *ifp, xfs_filblks_t *count);
|
|
int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip,
|
|
int whichfork, xfs_extnum_t *nextents,
|
|
xfs_filblks_t *count);
|
|
|
|
int xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset,
|
|
xfs_off_t len);
|
|
|
|
#endif /* __XFS_BMAP_UTIL_H__ */
|