blob: 1c0e2a8943a4b6a031ff2dc464ae354066f98ad8 [file] [log] [blame]
Darrick J. Wong673930c2016-08-03 11:33:43 +10001/*
2 * Copyright (c) 2014 Red Hat, Inc.
3 * All Rights Reserved.
4 *
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.
8 *
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.
13 *
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
17 */
18#include "xfs.h"
19#include "xfs_fs.h"
20#include "xfs_shared.h"
21#include "xfs_format.h"
22#include "xfs_log_format.h"
23#include "xfs_trans_resv.h"
24#include "xfs_bit.h"
25#include "xfs_sb.h"
26#include "xfs_mount.h"
27#include "xfs_defer.h"
28#include "xfs_da_format.h"
29#include "xfs_da_btree.h"
30#include "xfs_btree.h"
31#include "xfs_trans.h"
32#include "xfs_alloc.h"
33#include "xfs_rmap.h"
Darrick J. Wong0a1b0b32016-08-03 11:44:21 +100034#include "xfs_rmap_btree.h"
Darrick J. Wong673930c2016-08-03 11:33:43 +100035#include "xfs_trans_space.h"
36#include "xfs_trace.h"
37#include "xfs_error.h"
38#include "xfs_extent_busy.h"
39
Darrick J. Wong4b8ed672016-08-03 11:39:05 +100040/*
41 * Lookup the first record less than or equal to [bno, len, owner, offset]
42 * in the btree given by cur.
43 */
44int
45xfs_rmap_lookup_le(
46 struct xfs_btree_cur *cur,
47 xfs_agblock_t bno,
48 xfs_extlen_t len,
49 uint64_t owner,
50 uint64_t offset,
51 unsigned int flags,
52 int *stat)
53{
54 cur->bc_rec.r.rm_startblock = bno;
55 cur->bc_rec.r.rm_blockcount = len;
56 cur->bc_rec.r.rm_owner = owner;
57 cur->bc_rec.r.rm_offset = offset;
58 cur->bc_rec.r.rm_flags = flags;
59 return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat);
60}
61
62/*
63 * Lookup the record exactly matching [bno, len, owner, offset]
64 * in the btree given by cur.
65 */
66int
67xfs_rmap_lookup_eq(
68 struct xfs_btree_cur *cur,
69 xfs_agblock_t bno,
70 xfs_extlen_t len,
71 uint64_t owner,
72 uint64_t offset,
73 unsigned int flags,
74 int *stat)
75{
76 cur->bc_rec.r.rm_startblock = bno;
77 cur->bc_rec.r.rm_blockcount = len;
78 cur->bc_rec.r.rm_owner = owner;
79 cur->bc_rec.r.rm_offset = offset;
80 cur->bc_rec.r.rm_flags = flags;
81 return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat);
82}
83
84/*
85 * Update the record referred to by cur to the value given
86 * by [bno, len, owner, offset].
87 * This either works (return 0) or gets an EFSCORRUPTED error.
88 */
89STATIC int
90xfs_rmap_update(
91 struct xfs_btree_cur *cur,
92 struct xfs_rmap_irec *irec)
93{
94 union xfs_btree_rec rec;
95
96 rec.rmap.rm_startblock = cpu_to_be32(irec->rm_startblock);
97 rec.rmap.rm_blockcount = cpu_to_be32(irec->rm_blockcount);
98 rec.rmap.rm_owner = cpu_to_be64(irec->rm_owner);
99 rec.rmap.rm_offset = cpu_to_be64(
100 xfs_rmap_irec_offset_pack(irec));
101 return xfs_btree_update(cur, &rec);
102}
103
104static int
105xfs_rmap_btrec_to_irec(
106 union xfs_btree_rec *rec,
107 struct xfs_rmap_irec *irec)
108{
109 irec->rm_flags = 0;
110 irec->rm_startblock = be32_to_cpu(rec->rmap.rm_startblock);
111 irec->rm_blockcount = be32_to_cpu(rec->rmap.rm_blockcount);
112 irec->rm_owner = be64_to_cpu(rec->rmap.rm_owner);
113 return xfs_rmap_irec_offset_unpack(be64_to_cpu(rec->rmap.rm_offset),
114 irec);
115}
116
117/*
118 * Get the data from the pointed-to record.
119 */
120int
121xfs_rmap_get_rec(
122 struct xfs_btree_cur *cur,
123 struct xfs_rmap_irec *irec,
124 int *stat)
125{
126 union xfs_btree_rec *rec;
127 int error;
128
129 error = xfs_btree_get_rec(cur, &rec, stat);
130 if (error || !*stat)
131 return error;
132
133 return xfs_rmap_btrec_to_irec(rec, irec);
134}
135
Darrick J. Wong673930c2016-08-03 11:33:43 +1000136int
137xfs_rmap_free(
138 struct xfs_trans *tp,
139 struct xfs_buf *agbp,
140 xfs_agnumber_t agno,
141 xfs_agblock_t bno,
142 xfs_extlen_t len,
143 struct xfs_owner_info *oinfo)
144{
145 struct xfs_mount *mp = tp->t_mountp;
146 int error = 0;
147
148 if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
149 return 0;
150
151 trace_xfs_rmap_unmap(mp, agno, bno, len, false, oinfo);
152 if (1)
153 goto out_error;
154 trace_xfs_rmap_unmap_done(mp, agno, bno, len, false, oinfo);
155 return 0;
156
157out_error:
158 trace_xfs_rmap_unmap_error(mp, agno, error, _RET_IP_);
159 return error;
160}
161
Darrick J. Wong0a1b0b32016-08-03 11:44:21 +1000162/*
163 * A mergeable rmap must have the same owner and the same values for
164 * the unwritten, attr_fork, and bmbt flags. The startblock and
165 * offset are checked separately.
166 */
167static bool
168xfs_rmap_is_mergeable(
169 struct xfs_rmap_irec *irec,
170 uint64_t owner,
171 unsigned int flags)
172{
173 if (irec->rm_owner == XFS_RMAP_OWN_NULL)
174 return false;
175 if (irec->rm_owner != owner)
176 return false;
177 if ((flags & XFS_RMAP_UNWRITTEN) ^
178 (irec->rm_flags & XFS_RMAP_UNWRITTEN))
179 return false;
180 if ((flags & XFS_RMAP_ATTR_FORK) ^
181 (irec->rm_flags & XFS_RMAP_ATTR_FORK))
182 return false;
183 if ((flags & XFS_RMAP_BMBT_BLOCK) ^
184 (irec->rm_flags & XFS_RMAP_BMBT_BLOCK))
185 return false;
186 return true;
187}
188
189/*
190 * When we allocate a new block, the first thing we do is add a reference to
191 * the extent in the rmap btree. This takes the form of a [agbno, length,
192 * owner, offset] record. Flags are encoded in the high bits of the offset
193 * field.
194 */
195STATIC int
196xfs_rmap_map(
197 struct xfs_btree_cur *cur,
198 xfs_agblock_t bno,
199 xfs_extlen_t len,
200 bool unwritten,
201 struct xfs_owner_info *oinfo)
202{
203 struct xfs_mount *mp = cur->bc_mp;
204 struct xfs_rmap_irec ltrec;
205 struct xfs_rmap_irec gtrec;
206 int have_gt;
207 int have_lt;
208 int error = 0;
209 int i;
210 uint64_t owner;
211 uint64_t offset;
212 unsigned int flags = 0;
213 bool ignore_off;
214
215 xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
216 ASSERT(owner != 0);
217 ignore_off = XFS_RMAP_NON_INODE_OWNER(owner) ||
218 (flags & XFS_RMAP_BMBT_BLOCK);
219 if (unwritten)
220 flags |= XFS_RMAP_UNWRITTEN;
221 trace_xfs_rmap_map(mp, cur->bc_private.a.agno, bno, len,
222 unwritten, oinfo);
223
224 /*
225 * For the initial lookup, look for an exact match or the left-adjacent
226 * record for our insertion point. This will also give us the record for
227 * start block contiguity tests.
228 */
229 error = xfs_rmap_lookup_le(cur, bno, len, owner, offset, flags,
230 &have_lt);
231 if (error)
232 goto out_error;
233 XFS_WANT_CORRUPTED_GOTO(mp, have_lt == 1, out_error);
234
235 error = xfs_rmap_get_rec(cur, &ltrec, &have_lt);
236 if (error)
237 goto out_error;
238 XFS_WANT_CORRUPTED_GOTO(mp, have_lt == 1, out_error);
239 trace_xfs_rmap_lookup_le_range_result(cur->bc_mp,
240 cur->bc_private.a.agno, ltrec.rm_startblock,
241 ltrec.rm_blockcount, ltrec.rm_owner,
242 ltrec.rm_offset, ltrec.rm_flags);
243
244 if (!xfs_rmap_is_mergeable(&ltrec, owner, flags))
245 have_lt = 0;
246
247 XFS_WANT_CORRUPTED_GOTO(mp,
248 have_lt == 0 ||
249 ltrec.rm_startblock + ltrec.rm_blockcount <= bno, out_error);
250
251 /*
252 * Increment the cursor to see if we have a right-adjacent record to our
253 * insertion point. This will give us the record for end block
254 * contiguity tests.
255 */
256 error = xfs_btree_increment(cur, 0, &have_gt);
257 if (error)
258 goto out_error;
259 if (have_gt) {
260 error = xfs_rmap_get_rec(cur, &gtrec, &have_gt);
261 if (error)
262 goto out_error;
263 XFS_WANT_CORRUPTED_GOTO(mp, have_gt == 1, out_error);
264 XFS_WANT_CORRUPTED_GOTO(mp, bno + len <= gtrec.rm_startblock,
265 out_error);
266 trace_xfs_rmap_find_right_neighbor_result(cur->bc_mp,
267 cur->bc_private.a.agno, gtrec.rm_startblock,
268 gtrec.rm_blockcount, gtrec.rm_owner,
269 gtrec.rm_offset, gtrec.rm_flags);
270 if (!xfs_rmap_is_mergeable(&gtrec, owner, flags))
271 have_gt = 0;
272 }
273
274 /*
275 * Note: cursor currently points one record to the right of ltrec, even
276 * if there is no record in the tree to the right.
277 */
278 if (have_lt &&
279 ltrec.rm_startblock + ltrec.rm_blockcount == bno &&
280 (ignore_off || ltrec.rm_offset + ltrec.rm_blockcount == offset)) {
281 /*
282 * left edge contiguous, merge into left record.
283 *
284 * ltbno ltlen
285 * orig: |ooooooooo|
286 * adding: |aaaaaaaaa|
287 * result: |rrrrrrrrrrrrrrrrrrr|
288 * bno len
289 */
290 ltrec.rm_blockcount += len;
291 if (have_gt &&
292 bno + len == gtrec.rm_startblock &&
293 (ignore_off || offset + len == gtrec.rm_offset) &&
294 (unsigned long)ltrec.rm_blockcount + len +
295 gtrec.rm_blockcount <= XFS_RMAP_LEN_MAX) {
296 /*
297 * right edge also contiguous, delete right record
298 * and merge into left record.
299 *
300 * ltbno ltlen gtbno gtlen
301 * orig: |ooooooooo| |ooooooooo|
302 * adding: |aaaaaaaaa|
303 * result: |rrrrrrrrrrrrrrrrrrrrrrrrrrrrr|
304 */
305 ltrec.rm_blockcount += gtrec.rm_blockcount;
306 trace_xfs_rmap_delete(mp, cur->bc_private.a.agno,
307 gtrec.rm_startblock,
308 gtrec.rm_blockcount,
309 gtrec.rm_owner,
310 gtrec.rm_offset,
311 gtrec.rm_flags);
312 error = xfs_btree_delete(cur, &i);
313 if (error)
314 goto out_error;
315 XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
316 }
317
318 /* point the cursor back to the left record and update */
319 error = xfs_btree_decrement(cur, 0, &have_gt);
320 if (error)
321 goto out_error;
322 error = xfs_rmap_update(cur, &ltrec);
323 if (error)
324 goto out_error;
325 } else if (have_gt &&
326 bno + len == gtrec.rm_startblock &&
327 (ignore_off || offset + len == gtrec.rm_offset)) {
328 /*
329 * right edge contiguous, merge into right record.
330 *
331 * gtbno gtlen
332 * Orig: |ooooooooo|
333 * adding: |aaaaaaaaa|
334 * Result: |rrrrrrrrrrrrrrrrrrr|
335 * bno len
336 */
337 gtrec.rm_startblock = bno;
338 gtrec.rm_blockcount += len;
339 if (!ignore_off)
340 gtrec.rm_offset = offset;
341 error = xfs_rmap_update(cur, &gtrec);
342 if (error)
343 goto out_error;
344 } else {
345 /*
346 * no contiguous edge with identical owner, insert
347 * new record at current cursor position.
348 */
349 cur->bc_rec.r.rm_startblock = bno;
350 cur->bc_rec.r.rm_blockcount = len;
351 cur->bc_rec.r.rm_owner = owner;
352 cur->bc_rec.r.rm_offset = offset;
353 cur->bc_rec.r.rm_flags = flags;
354 trace_xfs_rmap_insert(mp, cur->bc_private.a.agno, bno, len,
355 owner, offset, flags);
356 error = xfs_btree_insert(cur, &i);
357 if (error)
358 goto out_error;
359 XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
360 }
361
362 trace_xfs_rmap_map_done(mp, cur->bc_private.a.agno, bno, len,
363 unwritten, oinfo);
364out_error:
365 if (error)
366 trace_xfs_rmap_map_error(mp, cur->bc_private.a.agno,
367 error, _RET_IP_);
368 return error;
369}
370
371/*
372 * Add a reference to an extent in the rmap btree.
373 */
Darrick J. Wong673930c2016-08-03 11:33:43 +1000374int
375xfs_rmap_alloc(
376 struct xfs_trans *tp,
377 struct xfs_buf *agbp,
378 xfs_agnumber_t agno,
379 xfs_agblock_t bno,
380 xfs_extlen_t len,
381 struct xfs_owner_info *oinfo)
382{
383 struct xfs_mount *mp = tp->t_mountp;
Darrick J. Wong0a1b0b32016-08-03 11:44:21 +1000384 struct xfs_btree_cur *cur;
385 int error;
Darrick J. Wong673930c2016-08-03 11:33:43 +1000386
387 if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
388 return 0;
389
Darrick J. Wong0a1b0b32016-08-03 11:44:21 +1000390 cur = xfs_rmapbt_init_cursor(mp, tp, agbp, agno);
391 error = xfs_rmap_map(cur, bno, len, false, oinfo);
392 if (error)
Darrick J. Wong673930c2016-08-03 11:33:43 +1000393 goto out_error;
Darrick J. Wong0a1b0b32016-08-03 11:44:21 +1000394
395 xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
Darrick J. Wong673930c2016-08-03 11:33:43 +1000396 return 0;
397
398out_error:
Darrick J. Wong0a1b0b32016-08-03 11:44:21 +1000399 xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
Darrick J. Wong673930c2016-08-03 11:33:43 +1000400 return error;
401}
Darrick J. Wongc5438382016-08-03 11:42:39 +1000402
403struct xfs_rmap_query_range_info {
404 xfs_rmap_query_range_fn fn;
405 void *priv;
406};
407
408/* Format btree record and pass to our callback. */
409STATIC int
410xfs_rmap_query_range_helper(
411 struct xfs_btree_cur *cur,
412 union xfs_btree_rec *rec,
413 void *priv)
414{
415 struct xfs_rmap_query_range_info *query = priv;
416 struct xfs_rmap_irec irec;
417 int error;
418
419 error = xfs_rmap_btrec_to_irec(rec, &irec);
420 if (error)
421 return error;
422 return query->fn(cur, &irec, query->priv);
423}
424
425/* Find all rmaps between two keys. */
426int
427xfs_rmap_query_range(
428 struct xfs_btree_cur *cur,
429 struct xfs_rmap_irec *low_rec,
430 struct xfs_rmap_irec *high_rec,
431 xfs_rmap_query_range_fn fn,
432 void *priv)
433{
434 union xfs_btree_irec low_brec;
435 union xfs_btree_irec high_brec;
436 struct xfs_rmap_query_range_info query;
437
438 low_brec.r = *low_rec;
439 high_brec.r = *high_rec;
440 query.priv = priv;
441 query.fn = fn;
442 return xfs_btree_query_range(cur, &low_brec, &high_brec,
443 xfs_rmap_query_range_helper, &query);
444}