66ae56a53f
Add a mode where XFS never overwrites existing blocks in place. This is to aid debugging our COW code, and also put infatructure in place for things like possible future support for zoned block devices, which can't support overwrites. This mode is enabled globally by doing a: echo 1 > /sys/fs/xfs/debug/always_cow Note that the parameter is global to allow running all tests in xfstests easily in this mode, which would not easily be possible with a per-fs sysfs file. In always_cow mode persistent preallocations are disabled, and fallocate will fail when called with a 0 mode (with our without FALLOC_FL_KEEP_SIZE), and not create unwritten extent for zeroed space when called with FALLOC_FL_ZERO_RANGE or FALLOC_FL_UNSHARE_RANGE. There are a few interesting xfstests failures when run in always_cow mode: - generic/392 fails because the bytes used in the file used to test hole punch recovery are less after the log replay. This is because the blocks written and then punched out are only freed with a delay due to the logging mechanism. - xfs/170 will fail as the already fragile file streams mechanism doesn't seem to interact well with the COW allocator - xfs/180 xfs/182 xfs/192 xfs/198 xfs/204 and xfs/208 will claim the file system is badly fragmented, but there is not much we can do to avoid that when always writing out of place - xfs/205 fails because overwriting a file in always_cow mode will require new space allocation and the assumption in the test thus don't work anymore. - xfs/326 fails to modify the file at all in always_cow mode after injecting the refcount error, leading to an unexpected md5sum after the remount, but that again is expected Signed-off-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>
63 lines
2.5 KiB
C
63 lines
2.5 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2016 Oracle. All Rights Reserved.
|
|
* Author: Darrick J. Wong <darrick.wong@oracle.com>
|
|
*/
|
|
#ifndef __XFS_REFLINK_H
|
|
#define __XFS_REFLINK_H 1
|
|
|
|
static inline bool xfs_is_always_cow_inode(struct xfs_inode *ip)
|
|
{
|
|
return ip->i_mount->m_always_cow &&
|
|
xfs_sb_version_hasreflink(&ip->i_mount->m_sb);
|
|
}
|
|
|
|
static inline bool xfs_is_cow_inode(struct xfs_inode *ip)
|
|
{
|
|
return xfs_is_reflink_inode(ip) || xfs_is_always_cow_inode(ip);
|
|
}
|
|
|
|
extern int xfs_reflink_find_shared(struct xfs_mount *mp, struct xfs_trans *tp,
|
|
xfs_agnumber_t agno, xfs_agblock_t agbno, xfs_extlen_t aglen,
|
|
xfs_agblock_t *fbno, xfs_extlen_t *flen, bool find_maximal);
|
|
extern int xfs_reflink_trim_around_shared(struct xfs_inode *ip,
|
|
struct xfs_bmbt_irec *irec, bool *shared);
|
|
bool xfs_inode_need_cow(struct xfs_inode *ip, struct xfs_bmbt_irec *imap,
|
|
bool *shared);
|
|
|
|
extern int xfs_reflink_allocate_cow(struct xfs_inode *ip,
|
|
struct xfs_bmbt_irec *imap, bool *shared, uint *lockmode,
|
|
unsigned iomap_flags);
|
|
extern int xfs_reflink_convert_cow(struct xfs_inode *ip, xfs_off_t offset,
|
|
xfs_off_t count);
|
|
|
|
extern int xfs_reflink_cancel_cow_blocks(struct xfs_inode *ip,
|
|
struct xfs_trans **tpp, xfs_fileoff_t offset_fsb,
|
|
xfs_fileoff_t end_fsb, bool cancel_real);
|
|
extern int xfs_reflink_cancel_cow_range(struct xfs_inode *ip, xfs_off_t offset,
|
|
xfs_off_t count, bool cancel_real);
|
|
extern int xfs_reflink_end_cow(struct xfs_inode *ip, xfs_off_t offset,
|
|
xfs_off_t count);
|
|
extern int xfs_reflink_recover_cow(struct xfs_mount *mp);
|
|
extern loff_t xfs_reflink_remap_range(struct file *file_in, loff_t pos_in,
|
|
struct file *file_out, loff_t pos_out, loff_t len,
|
|
unsigned int remap_flags);
|
|
extern int xfs_reflink_inode_has_shared_extents(struct xfs_trans *tp,
|
|
struct xfs_inode *ip, bool *has_shared);
|
|
extern int xfs_reflink_clear_inode_flag(struct xfs_inode *ip,
|
|
struct xfs_trans **tpp);
|
|
extern int xfs_reflink_unshare(struct xfs_inode *ip, xfs_off_t offset,
|
|
xfs_off_t len);
|
|
extern int xfs_reflink_remap_prep(struct file *file_in, loff_t pos_in,
|
|
struct file *file_out, loff_t pos_out, loff_t *len,
|
|
unsigned int remap_flags);
|
|
extern int xfs_reflink_remap_blocks(struct xfs_inode *src, loff_t pos_in,
|
|
struct xfs_inode *dest, loff_t pos_out, loff_t remap_len,
|
|
loff_t *remapped);
|
|
extern int xfs_reflink_update_dest(struct xfs_inode *dest, xfs_off_t newlen,
|
|
xfs_extlen_t cowextsize, unsigned int remap_flags);
|
|
extern void xfs_reflink_remap_unlock(struct file *file_in,
|
|
struct file *file_out);
|
|
|
|
#endif /* __XFS_REFLINK_H */
|