2
* Copyright (C) 2010 Red Hat, Inc.
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public License as
7
* published by the Free Software Foundation.
9
* This program is distributed in the hope that it would be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write the Free Software Foundation,
16
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
#include "xfs_mount.h"
24
#include "xfs_quota.h"
25
#include "xfs_trans.h"
26
#include "xfs_alloc_btree.h"
27
#include "xfs_bmap_btree.h"
28
#include "xfs_ialloc_btree.h"
29
#include "xfs_btree.h"
30
#include "xfs_inode.h"
31
#include "xfs_alloc.h"
32
#include "xfs_error.h"
33
#include "xfs_discard.h"
34
#include "xfs_trace.h"
43
__uint64_t *blocks_trimmed)
45
struct block_device *bdev = mp->m_ddev_targp->bt_bdev;
46
struct xfs_btree_cur *cur;
48
struct xfs_perag *pag;
52
pag = xfs_perag_get(mp, agno);
54
error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
58
cur = xfs_allocbt_init_cursor(mp, NULL, agbp, agno, XFS_BTNUM_CNT);
61
* Force out the log. This means any transactions that might have freed
62
* space before we took the AGF buffer lock are now on disk, and the
63
* volatile disk cache is flushed.
65
xfs_log_force(mp, XFS_LOG_SYNC);
68
* Look up the longest btree in the AGF and start with it.
70
error = xfs_alloc_lookup_le(cur, 0,
71
XFS_BUF_TO_AGF(agbp)->agf_longest, &i);
76
* Loop until we are done with all extents that are large
77
* enough to be worth discarding.
83
error = xfs_alloc_get_rec(cur, &fbno, &flen, &i);
86
XFS_WANT_CORRUPTED_GOTO(i == 1, out_del_cursor);
87
ASSERT(flen <= XFS_BUF_TO_AGF(agbp)->agf_longest);
93
trace_xfs_discard_toosmall(mp, agno, fbno, flen);
98
* If the extent is entirely outside of the range we are
99
* supposed to discard skip it. Do not bother to trim
100
* down partially overlapping ranges for now.
102
if (XFS_AGB_TO_FSB(mp, agno, fbno) + flen < start ||
103
XFS_AGB_TO_FSB(mp, agno, fbno) > end) {
104
trace_xfs_discard_exclude(mp, agno, fbno, flen);
109
* If any blocks in the range are still busy, skip the
110
* discard and try again the next time.
112
if (xfs_alloc_busy_search(mp, agno, fbno, flen)) {
113
trace_xfs_discard_busy(mp, agno, fbno, flen);
117
trace_xfs_discard_extent(mp, agno, fbno, flen);
118
error = -blkdev_issue_discard(bdev,
119
XFS_AGB_TO_DADDR(mp, agno, fbno),
120
XFS_FSB_TO_BB(mp, flen),
124
*blocks_trimmed += flen;
127
error = xfs_btree_decrement(cur, 0, &i);
133
xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
142
struct xfs_mount *mp,
143
struct fstrim_range __user *urange)
145
struct request_queue *q = mp->m_ddev_targp->bt_bdev->bd_disk->queue;
146
unsigned int granularity = q->limits.discard_granularity;
147
struct fstrim_range range;
148
xfs_fsblock_t start, end, minlen;
149
xfs_agnumber_t start_agno, end_agno, agno;
150
__uint64_t blocks_trimmed = 0;
151
int error, last_error = 0;
153
if (!capable(CAP_SYS_ADMIN))
154
return -XFS_ERROR(EPERM);
155
if (!blk_queue_discard(q))
156
return -XFS_ERROR(EOPNOTSUPP);
157
if (copy_from_user(&range, urange, sizeof(range)))
158
return -XFS_ERROR(EFAULT);
161
* Truncating down the len isn't actually quite correct, but using
162
* XFS_B_TO_FSB would mean we trivially get overflows for values
163
* of ULLONG_MAX or slightly lower. And ULLONG_MAX is the default
164
* used by the fstrim application. In the end it really doesn't
165
* matter as trimming blocks is an advisory interface.
167
start = XFS_B_TO_FSBT(mp, range.start);
168
end = start + XFS_B_TO_FSBT(mp, range.len) - 1;
169
minlen = XFS_B_TO_FSB(mp, max_t(u64, granularity, range.minlen));
171
if (start >= mp->m_sb.sb_dblocks)
172
return -XFS_ERROR(EINVAL);
173
if (end > mp->m_sb.sb_dblocks - 1)
174
end = mp->m_sb.sb_dblocks - 1;
176
start_agno = XFS_FSB_TO_AGNO(mp, start);
177
end_agno = XFS_FSB_TO_AGNO(mp, end);
179
for (agno = start_agno; agno <= end_agno; agno++) {
180
error = -xfs_trim_extents(mp, agno, start, end, minlen,
189
range.len = XFS_FSB_TO_B(mp, blocks_trimmed);
190
if (copy_to_user(urange, &range, sizeof(range)))
191
return -XFS_ERROR(EFAULT);
197
struct xfs_mount *mp,
198
struct list_head *list)
200
struct xfs_busy_extent *busyp;
203
list_for_each_entry(busyp, list, list) {
204
trace_xfs_discard_extent(mp, busyp->agno, busyp->bno,
207
error = -blkdev_issue_discard(mp->m_ddev_targp->bt_bdev,
208
XFS_AGB_TO_DADDR(mp, busyp->agno, busyp->bno),
209
XFS_FSB_TO_BB(mp, busyp->length),
211
if (error && error != EOPNOTSUPP) {
213
"discard failed for extent [0x%llu,%u], error %d",
214
(unsigned long long)busyp->bno,