0001
0002
0003
0004
0005
0006 #include "xfs.h"
0007 #include "xfs_shared.h"
0008 #include "xfs_format.h"
0009 #include "xfs_log_format.h"
0010 #include "xfs_trans_resv.h"
0011 #include "xfs_mount.h"
0012 #include "xfs_btree.h"
0013 #include "xfs_alloc_btree.h"
0014 #include "xfs_alloc.h"
0015 #include "xfs_discard.h"
0016 #include "xfs_error.h"
0017 #include "xfs_extent_busy.h"
0018 #include "xfs_trace.h"
0019 #include "xfs_log.h"
0020 #include "xfs_ag.h"
0021
0022 STATIC int
0023 xfs_trim_extents(
0024 struct xfs_mount *mp,
0025 xfs_agnumber_t agno,
0026 xfs_daddr_t start,
0027 xfs_daddr_t end,
0028 xfs_daddr_t minlen,
0029 uint64_t *blocks_trimmed)
0030 {
0031 struct block_device *bdev = mp->m_ddev_targp->bt_bdev;
0032 struct xfs_btree_cur *cur;
0033 struct xfs_buf *agbp;
0034 struct xfs_agf *agf;
0035 struct xfs_perag *pag;
0036 int error;
0037 int i;
0038
0039 pag = xfs_perag_get(mp, agno);
0040
0041
0042
0043
0044
0045
0046 xfs_log_force(mp, XFS_LOG_SYNC);
0047
0048 error = xfs_alloc_read_agf(pag, NULL, 0, &agbp);
0049 if (error)
0050 goto out_put_perag;
0051 agf = agbp->b_addr;
0052
0053 cur = xfs_allocbt_init_cursor(mp, NULL, agbp, pag, XFS_BTNUM_CNT);
0054
0055
0056
0057
0058 error = xfs_alloc_lookup_ge(cur, 0, be32_to_cpu(agf->agf_longest), &i);
0059 if (error)
0060 goto out_del_cursor;
0061
0062
0063
0064
0065
0066 while (i) {
0067 xfs_agblock_t fbno;
0068 xfs_extlen_t flen;
0069 xfs_daddr_t dbno;
0070 xfs_extlen_t dlen;
0071
0072 error = xfs_alloc_get_rec(cur, &fbno, &flen, &i);
0073 if (error)
0074 goto out_del_cursor;
0075 if (XFS_IS_CORRUPT(mp, i != 1)) {
0076 error = -EFSCORRUPTED;
0077 goto out_del_cursor;
0078 }
0079 ASSERT(flen <= be32_to_cpu(agf->agf_longest));
0080
0081
0082
0083
0084
0085
0086 dbno = XFS_AGB_TO_DADDR(mp, agno, fbno);
0087 dlen = XFS_FSB_TO_BB(mp, flen);
0088
0089
0090
0091
0092 if (dlen < minlen) {
0093 trace_xfs_discard_toosmall(mp, agno, fbno, flen);
0094 goto out_del_cursor;
0095 }
0096
0097
0098
0099
0100
0101
0102 if (dbno + dlen < start || dbno > end) {
0103 trace_xfs_discard_exclude(mp, agno, fbno, flen);
0104 goto next_extent;
0105 }
0106
0107
0108
0109
0110
0111 if (xfs_extent_busy_search(mp, pag, fbno, flen)) {
0112 trace_xfs_discard_busy(mp, agno, fbno, flen);
0113 goto next_extent;
0114 }
0115
0116 trace_xfs_discard_extent(mp, agno, fbno, flen);
0117 error = blkdev_issue_discard(bdev, dbno, dlen, GFP_NOFS);
0118 if (error)
0119 goto out_del_cursor;
0120 *blocks_trimmed += flen;
0121
0122 next_extent:
0123 error = xfs_btree_decrement(cur, 0, &i);
0124 if (error)
0125 goto out_del_cursor;
0126
0127 if (fatal_signal_pending(current)) {
0128 error = -ERESTARTSYS;
0129 goto out_del_cursor;
0130 }
0131 }
0132
0133 out_del_cursor:
0134 xfs_btree_del_cursor(cur, error);
0135 xfs_buf_relse(agbp);
0136 out_put_perag:
0137 xfs_perag_put(pag);
0138 return error;
0139 }
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150 int
0151 xfs_ioc_trim(
0152 struct xfs_mount *mp,
0153 struct fstrim_range __user *urange)
0154 {
0155 unsigned int granularity =
0156 bdev_discard_granularity(mp->m_ddev_targp->bt_bdev);
0157 struct fstrim_range range;
0158 xfs_daddr_t start, end, minlen;
0159 xfs_agnumber_t start_agno, end_agno, agno;
0160 uint64_t blocks_trimmed = 0;
0161 int error, last_error = 0;
0162
0163 if (!capable(CAP_SYS_ADMIN))
0164 return -EPERM;
0165 if (!bdev_max_discard_sectors(mp->m_ddev_targp->bt_bdev))
0166 return -EOPNOTSUPP;
0167
0168
0169
0170
0171
0172 if (xfs_has_norecovery(mp))
0173 return -EROFS;
0174
0175 if (copy_from_user(&range, urange, sizeof(range)))
0176 return -EFAULT;
0177
0178 range.minlen = max_t(u64, granularity, range.minlen);
0179 minlen = BTOBB(range.minlen);
0180
0181
0182
0183
0184
0185
0186
0187 if (range.start >= XFS_FSB_TO_B(mp, mp->m_sb.sb_dblocks) ||
0188 range.minlen > XFS_FSB_TO_B(mp, mp->m_ag_max_usable) ||
0189 range.len < mp->m_sb.sb_blocksize)
0190 return -EINVAL;
0191
0192 start = BTOBB(range.start);
0193 end = start + BTOBBT(range.len) - 1;
0194
0195 if (end > XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks) - 1)
0196 end = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)- 1;
0197
0198 start_agno = xfs_daddr_to_agno(mp, start);
0199 end_agno = xfs_daddr_to_agno(mp, end);
0200
0201 for (agno = start_agno; agno <= end_agno; agno++) {
0202 error = xfs_trim_extents(mp, agno, start, end, minlen,
0203 &blocks_trimmed);
0204 if (error) {
0205 last_error = error;
0206 if (error == -ERESTARTSYS)
0207 break;
0208 }
0209 }
0210
0211 if (last_error)
0212 return last_error;
0213
0214 range.len = XFS_FSB_TO_B(mp, blocks_trimmed);
0215 if (copy_to_user(urange, &range, sizeof(range)))
0216 return -EFAULT;
0217 return 0;
0218 }