From: Denis V. Lunev <[email protected]> The balloon holds file system space that never carries data, but its blocks are allocated, so the free space walk of xfs_ioc_trim() never discards them, and inflating the balloon is a plain fallocate() that does not discard either. Once the balloon is inflated over blocks that held data, e.g. on an online shrink after files were removed, the device below keeps them allocated: a ploop or qcow2 image keeps host disk space the container can never use, and compaction, which is FITRIM, does not give it back.
Walk the balloon after the free space of the data device and discard its unwritten extents that fall into the requested range, merging physically adjacent ones into one discard. The walk goes to the end of the file offset space, as fallocate() with FALLOC_FL_KEEP_SIZE can leave extents past i_size. A merged run is discarded in chunks, with a check for fatal signals and freezing between them. The walk holds the IOLOCK and the MMAPLOCK exclusively, after waiting for direct I/O and flushing the page cache of the balloon. No block of the balloon can be freed and reused under the discard, and nothing is written to it meanwhile, so an extent that is still unwritten reads as zeroes before and after the discard. The balloon is hidden from lookup and opened only through XFS_IOC_OPEN_BALLOON, which takes CAP_SYS_ADMIN, so the locks only wait for ploop inflating or shrinking it. Every FITRIM discards the balloon again, which costs little: dm-ploop completes the discard of an unmapped cluster at once. A discard error or a failure to read the balloon inode fails FITRIM; a balloon that is no longer a regular mutable file is skipped. A balloon inflated before this change is released by the next FITRIM. https://virtuozzo.atlassian.net/browse/VSTOR-153590 Feature: fs/xfs: fast online shrink support Signed-off-by: Denis V. Lunev <[email protected]> --- fs/xfs/xfs_discard.c | 144 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/fs/xfs/xfs_discard.c b/fs/xfs/xfs_discard.c index 93943e698eea..b9b5a28b0d96 100644 --- a/fs/xfs/xfs_discard.c +++ b/fs/xfs/xfs_discard.c @@ -22,6 +22,10 @@ #include "xfs_health.h" #include "xfs_rtbitmap.h" #include "xfs_rtgroup.h" +#include "xfs_bit.h" +#include "xfs_inode.h" +#include "xfs_bmap.h" +#include "xfs_super.h" /* * Notes on an efficient, low latency fstrim algorithm @@ -833,6 +837,140 @@ xfs_trim_rtdev_extents( # define xfs_trim_rtdev_extents(...) (-EOPNOTSUPP) #endif /* CONFIG_XFS_RT */ +#define XFS_BALLOON_DISCARD_CHUNK_BB ((128U << 20) >> BBSHIFT) + +static int +xfs_trim_balloon_run( + struct xfs_mount *mp, + xfs_daddr_t daddr, + uint64_t len, + xfs_daddr_t start, + xfs_daddr_t end, + xfs_extlen_t minlen) +{ + struct block_device *bdev = mp->m_ddev_targp->bt_bdev; + unsigned int gran = max(1U, + bdev_discard_granularity(bdev) >> BBSHIFT); + unsigned int chunk = roundup(XFS_BALLOON_DISCARD_CHUNK_BB, gran); + xfs_daddr_t last = daddr + len - 1, next; + int error; + + if (!len) + return 0; + if (daddr < start) + daddr = start; + if (last > end) + last = end; + if (last < daddr || last - daddr + 1 < XFS_FSB_TO_BB(mp, minlen)) + return 0; + + /* + * Chunks keep a long run interruptible. They end on multiples of the + * discard granularity, so no cluster below is split between two. + */ + while (daddr <= last) { + u64 rem = daddr; + + next = daddr + chunk - do_div(rem, chunk); + if (next > last + 1) + next = last + 1; + error = blkdev_issue_discard(bdev, daddr, next - daddr, + GFP_KERNEL); + if (error) + return error; + daddr = next; + if (xfs_trim_should_stop()) + break; + cond_resched(); + } + + return 0; +} + +/* + * Blocks of the balloon never hold data, but they are allocated, so the + * free space btree walk never discards them. Discard its unwritten + * extents, merging physically adjacent ones, so that the device below + * can release the space as it does for free blocks. + */ +static int +xfs_trim_balloon( + struct xfs_mount *mp, + xfs_daddr_t start, + xfs_daddr_t end, + xfs_extlen_t minlen) +{ + u64 balloon_ino = READ_ONCE(mp->m_balloon_ino); + xfs_daddr_t run_daddr = 0; + uint64_t run_len = 0; + struct xfs_bmbt_irec map; + struct xfs_inode *ip; + xfs_fileoff_t off = 0; + unsigned int lock_mode; + bool unwritten; + int nmaps, error; + + if (!balloon_ino) + return 0; + ip = xfs_balloon_get(mp, balloon_ino, 0); + if (IS_ERR(ip)) + return PTR_ERR(ip) == -EINVAL ? 0 : PTR_ERR(ip); + error = 0; + if (XFS_IS_REALTIME_INODE(ip)) + goto out_rele; + + /* + * Nobody may write the balloon meanwhile: data written into an + * unwritten extent would be discarded with it. + */ + xfs_ilock(ip, XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL); + inode_dio_wait(VFS_I(ip)); + error = filemap_write_and_wait(VFS_I(ip)->i_mapping); + if (error) + goto out_unlock; + + while (off < XFS_MAX_FILEOFF) { + if (xfs_trim_should_stop()) + break; + cond_resched(); + + nmaps = 1; + lock_mode = xfs_ilock_data_map_shared(ip); + error = xfs_bmapi_read(ip, off, XFS_MAX_FILEOFF - off, &map, + &nmaps, 0); + xfs_iunlock(ip, lock_mode); + if (error || !nmaps || !map.br_blockcount) + break; + off = map.br_startoff + map.br_blockcount; + + unwritten = xfs_bmap_is_real_extent(&map) && + map.br_state == XFS_EXT_UNWRITTEN; + if (unwritten && run_len && run_daddr + run_len == + XFS_FSB_TO_DADDR(mp, map.br_startblock)) { + run_len += XFS_FSB_TO_BB(mp, map.br_blockcount); + continue; + } + + error = xfs_trim_balloon_run(mp, run_daddr, run_len, start, + end, minlen); + if (error) + break; + run_len = 0; + if (unwritten) { + run_daddr = XFS_FSB_TO_DADDR(mp, map.br_startblock); + run_len = XFS_FSB_TO_BB(mp, map.br_blockcount); + } + } + if (!error && !xfs_trim_should_stop()) + error = xfs_trim_balloon_run(mp, run_daddr, run_len, start, + end, minlen); +out_unlock: + xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL); +out_rele: + xfs_irele(ip); + return error; +} + /* * trim a range of the filesystem. * @@ -905,6 +1043,12 @@ xfs_ioc_trim( error = xfs_trim_datadev_extents(mp, start, end, minlen); if (error) last_error = error; + + if (!xfs_trim_should_stop()) { + error = xfs_trim_balloon(mp, start, end, minlen); + if (error) + last_error = error; + } } if (rt_bdev && !xfs_trim_should_stop()) { -- 2.53.0 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
