From: Darrick J. Wong <djwong@xxxxxxxxxx> Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- fs/xfs/xfs_discard.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_trace.h | 20 +++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_discard.c b/fs/xfs/xfs_discard.c index 6d3400771e21..f6ee2b8e5e9c 100644 --- a/fs/xfs/xfs_discard.c +++ b/fs/xfs/xfs_discard.c @@ -19,6 +19,7 @@ #include "xfs_log.h" #include "xfs_ag.h" #include "xfs_health.h" +#include "xfs_rtbitmap.h" STATIC int xfs_trim_perag_extents( @@ -168,6 +169,86 @@ xfs_trim_ddev_extents( return last_error; } +#ifdef CONFIG_XFS_RT +static int +xfs_trim_rtdev_extent( + struct xfs_mount *mp, + struct xfs_trans *tp, + const struct xfs_rtalloc_rec *rec, + void *priv) +{ + struct block_device *bdev = xfs_buftarg_bdev(mp->m_rtdev_targp); + uint64_t *blocks_trimmed = priv; + xfs_rtblock_t rbno, rlen; + xfs_daddr_t dbno, dlen; + int error; + + if (fatal_signal_pending(current)) + return -ERESTARTSYS; + + rbno = xfs_rtx_to_rtb(mp, rec->ar_startext); + rlen = xfs_rtx_to_rtb(mp, rec->ar_extcount); + + trace_xfs_discard_rtextent(mp, rbno, rlen); + + dbno = XFS_FSB_TO_BB(mp, rbno); + dlen = XFS_FSB_TO_BB(mp, rlen); + + error = blkdev_issue_discard(bdev, dbno, dlen, GFP_NOFS); + if (error) + return error; + + *blocks_trimmed += rlen; + return 0; +} + +static int +xfs_trim_rtdev_extents( + struct xfs_mount *mp, + xfs_daddr_t start, + xfs_daddr_t end, + xfs_daddr_t minlen, + uint64_t *blocks_trimmed) +{ + struct xfs_rtalloc_rec low = { }, high = { }; + xfs_daddr_t rtdev_daddr; + xfs_extlen_t mod; + int error; + + /* Shift the start and end downwards to match the rt device. */ + rtdev_daddr = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks); + if (start > rtdev_daddr) + start -= rtdev_daddr; + else + start = 0; + + if (end <= rtdev_daddr) + return 0; + end -= rtdev_daddr; + + if (end > XFS_FSB_TO_BB(mp, mp->m_sb.sb_rblocks) - 1) + end = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rblocks) - 1; + + /* Convert the rt blocks to rt extents */ + low.ar_startext = xfs_rtb_to_rtx(mp, XFS_BB_TO_FSB(mp, start), &mod); + if (mod) + low.ar_startext++; + high.ar_startext = xfs_rtb_to_rtx(mp, XFS_BB_TO_FSBT(mp, end), &mod); + + /* + * Walk the free ranges between low and high. The query_range function + * trims the extents returned. + */ + xfs_rtbitmap_lock_shared(mp, XFS_RBMLOCK_BITMAP); + error = xfs_rtalloc_query_range(mp, NULL, &low, &high, + xfs_trim_rtdev_extent, blocks_trimmed); + xfs_rtbitmap_unlock_shared(mp, XFS_RBMLOCK_BITMAP); + return error; +} +#else +# define xfs_trim_rtdev_extents(m,s,e,n,b) (-EOPNOTSUPP) +#endif /* CONFIG_XFS_RT */ + /* * trim a range of the filesystem. * @@ -176,6 +257,9 @@ xfs_trim_ddev_extents( * addressing. FSB addressing is sparse (AGNO|AGBNO), while the incoming format * is a linear address range. Hence we need to use DADDR based conversions and * comparisons for determining the correct offset and regions to trim. + * + * The realtime device is mapped into the FITRIM "address space" immediately + * after the data device. */ int xfs_ioc_trim( @@ -183,8 +267,10 @@ xfs_ioc_trim( struct fstrim_range __user *urange) { struct block_device *bdev = xfs_buftarg_bdev(mp->m_ddev_targp); + struct block_device *rt_bdev = NULL; unsigned int granularity = bdev_discard_granularity(bdev); struct fstrim_range range; + xfs_rfsblock_t max_blocks; xfs_daddr_t start, end, minlen; uint64_t blocks_trimmed = 0; int error, last_error = 0; @@ -194,6 +280,14 @@ xfs_ioc_trim( if (!bdev_max_discard_sectors(bdev)) return -EOPNOTSUPP; + if (mp->m_rtdev_targp) { + rt_bdev = xfs_buftarg_bdev(mp->m_rtdev_targp); + if (!bdev_max_discard_sectors(rt_bdev)) + return -EOPNOTSUPP; + granularity = max(granularity, + bdev_discard_granularity(rt_bdev)); + } + /* * We haven't recovered the log, so we cannot use our bnobt-guided * storage zapping commands. @@ -213,7 +307,8 @@ xfs_ioc_trim( * used by the fstrim application. In the end it really doesn't * matter as trimming blocks is an advisory interface. */ - if (range.start >= XFS_FSB_TO_B(mp, mp->m_sb.sb_dblocks) || + max_blocks = mp->m_sb.sb_dblocks + mp->m_sb.sb_rblocks; + if (range.start >= XFS_FSB_TO_B(mp, max_blocks) || range.minlen > XFS_FSB_TO_B(mp, mp->m_ag_max_usable) || range.len < mp->m_sb.sb_blocksize) return -EINVAL; @@ -227,6 +322,15 @@ xfs_ioc_trim( if (error) last_error = error; + if (rt_bdev) { + error = xfs_trim_rtdev_extents(mp, start, end, minlen, + &blocks_trimmed); + if (error == -ERESTARTSYS) + return error; + if (error) + last_error = error; + } + if (last_error) return last_error; diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index ec9aa1914a93..cfb26288394a 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -2520,6 +2520,26 @@ DEFINE_DISCARD_EVENT(xfs_discard_toosmall); DEFINE_DISCARD_EVENT(xfs_discard_exclude); DEFINE_DISCARD_EVENT(xfs_discard_busy); +TRACE_EVENT(xfs_discard_rtextent, + TP_PROTO(struct xfs_mount *mp, + xfs_rtblock_t rtbno, xfs_rtblock_t len), + TP_ARGS(mp, rtbno, len), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_rtblock_t, rtbno) + __field(xfs_rtblock_t, len) + ), + TP_fast_assign( + __entry->dev = mp->m_rtdev_targp->bt_dev; + __entry->rtbno = rtbno; + __entry->len = len; + ), + TP_printk("dev %d:%d rtbno 0x%llx rtbcount 0x%llx", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->rtbno, + __entry->len) +); + /* btree cursor events */ TRACE_DEFINE_ENUM(XFS_BTNUM_BNOi); TRACE_DEFINE_ENUM(XFS_BTNUM_CNTi);