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

Reply via email to