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 shared IOLOCK keeps fallocate and truncate away from the balloon meanwhile, so none of its blocks can be freed and reused under the discard. An unwritten extent reads as zeroes whatever the device returns, so the discard changes nothing visible, and repeating it is harmless. 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 | 106 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/fs/xfs/xfs_discard.c b/fs/xfs/xfs_discard.c index 93943e698eea..a7f1491dc35a 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,102 @@ xfs_trim_rtdev_extents( # define xfs_trim_rtdev_extents(...) (-EOPNOTSUPP) #endif /* CONFIG_XFS_RT */ +static int +xfs_trim_balloon_run( + struct xfs_mount *mp, + xfs_daddr_t daddr, + xfs_daddr_t len, + xfs_daddr_t start, + xfs_daddr_t end, + xfs_extlen_t minlen) +{ + xfs_daddr_t last = daddr + len - 1; + + 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; + + return blkdev_issue_discard(mp->m_ddev_targp->bt_bdev, daddr, + last - daddr + 1, GFP_NOFS); +} + +/* + * 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, run_len = 0; + struct xfs_bmbt_irec map; + struct xfs_inode *ip; + xfs_fileoff_t off = 0, end_fsb; + unsigned int lock_mode; + bool unwritten; + int nmaps, error = 0; + + if (!balloon_ino || xfs_is_readonly(mp)) + return 0; + ip = xfs_balloon_get(mp, balloon_ino, 0); + if (IS_ERR(ip)) + return 0; + if (XFS_IS_REALTIME_INODE(ip)) + goto out_rele; + + xfs_ilock(ip, XFS_IOLOCK_SHARED); + end_fsb = XFS_B_TO_FSB(mp, i_size_read(VFS_I(ip))); + while (off < end_fsb) { + if (xfs_trim_should_stop()) + break; + cond_resched(); + + nmaps = 1; + lock_mode = xfs_ilock_data_map_shared(ip); + error = xfs_bmapi_read(ip, off, end_fsb - 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); + xfs_iunlock(ip, XFS_IOLOCK_SHARED); +out_rele: + xfs_irele(ip); + return error; +} + /* * trim a range of the filesystem. * @@ -905,6 +1005,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
