Commit 4eb559dd authored by Darrick J. Wong's avatar Darrick J. Wong

Merge tag 'refcount-cow-domain-6.1_2022-10-31' of...

Merge tag 'refcount-cow-domain-6.1_2022-10-31' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.1-fixesA

xfs: improve runtime refcountbt corruption detection

Fuzz testing of the refcount btree demonstrated a weakness in validation
of refcount btree records during normal runtime.  The idea of using the
upper bit of the rc_startblock field to separate the refcount records
into one group for shared space and another for CoW staging extents was
added at the last minute.  The incore struct left this bit encoded in
the upper bit of the startblock field, which makes it all too easy for
arithmetic operations to overflow if we don't detect the cowflag
properly.

When I ran a norepair fuzz tester, I was able to crash the kernel on one
of these accidental overflows by fuzzing a key record in a node block,
which broke lookups.  To fix the problem, make the domain (shared/cow) a
separate field in the incore record.

Unfortunately, a customer also hit this once in production.  Due to bugs
in the kernel running on the VM host, writes to the disk image would
occasionally be lost.  Given sufficient memory pressure on the VM guest,
a refcountbt xfs_buf could be reclaimed and later reloaded from the
stale copy on the virtual disk.  The stale disk contents were a refcount
btree leaf block full of records for the wrong domain, and this caused
an infinite loop in the guest VM.

v2: actually include the refcount adjust loop invariant checking patch;
    move the deferred refcount continuation checks earlier in the series;
    break up the megapatch into smaller pieces; fix an uninitialized list
    error.
v3: in the continuation check patch, verify the per-ag extent before
    converting it to a fsblock
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>

* tag 'refcount-cow-domain-6.1_2022-10-31' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux:
  xfs: rename XFS_REFC_COW_START to _COWFLAG
  xfs: fix uninitialized list head in struct xfs_refcount_recovery
  xfs: fix agblocks check in the cow leftover recovery function
  xfs: check record domain when accessing refcount records
  xfs: remove XFS_FIND_RCEXT_SHARED and _COW
  xfs: refactor domain and refcount checking
  xfs: report refcount domain in tracepoints
  xfs: track cow/shared record domains explicitly in xfs_refcount_irec
  xfs: refactor refcount record usage in xchk_refcountbt_rec
  xfs: move _irec structs to xfs_types.h
  xfs: check deferred refcount op continuation parameters
  xfs: create a predicate to verify per-AG extents
  xfs: make sure aglen never goes negative in xfs_refcount_adjust_extents
parents 9f187ba0 8b972158
...@@ -133,6 +133,21 @@ xfs_verify_agbno(struct xfs_perag *pag, xfs_agblock_t agbno) ...@@ -133,6 +133,21 @@ xfs_verify_agbno(struct xfs_perag *pag, xfs_agblock_t agbno)
return true; return true;
} }
static inline bool
xfs_verify_agbext(
struct xfs_perag *pag,
xfs_agblock_t agbno,
xfs_agblock_t len)
{
if (agbno + len <= agbno)
return false;
if (!xfs_verify_agbno(pag, agbno))
return false;
return xfs_verify_agbno(pag, agbno + len - 1);
}
/* /*
* Verify that an AG inode number pointer neither points outside the AG * Verify that an AG inode number pointer neither points outside the AG
* nor points at static metadata. * nor points at static metadata.
......
...@@ -263,11 +263,7 @@ xfs_alloc_get_rec( ...@@ -263,11 +263,7 @@ xfs_alloc_get_rec(
goto out_bad_rec; goto out_bad_rec;
/* check for valid extent range, including overflow */ /* check for valid extent range, including overflow */
if (!xfs_verify_agbno(pag, *bno)) if (!xfs_verify_agbext(pag, *bno, *len))
goto out_bad_rec;
if (*bno > *bno + *len)
goto out_bad_rec;
if (!xfs_verify_agbno(pag, *bno + *len - 1))
goto out_bad_rec; goto out_bad_rec;
return 0; return 0;
......
...@@ -1564,20 +1564,6 @@ struct xfs_rmap_rec { ...@@ -1564,20 +1564,6 @@ struct xfs_rmap_rec {
#define RMAPBT_UNUSED_OFFSET_BITLEN 7 #define RMAPBT_UNUSED_OFFSET_BITLEN 7
#define RMAPBT_OFFSET_BITLEN 54 #define RMAPBT_OFFSET_BITLEN 54
#define XFS_RMAP_ATTR_FORK (1 << 0)
#define XFS_RMAP_BMBT_BLOCK (1 << 1)
#define XFS_RMAP_UNWRITTEN (1 << 2)
#define XFS_RMAP_KEY_FLAGS (XFS_RMAP_ATTR_FORK | \
XFS_RMAP_BMBT_BLOCK)
#define XFS_RMAP_REC_FLAGS (XFS_RMAP_UNWRITTEN)
struct xfs_rmap_irec {
xfs_agblock_t rm_startblock; /* extent start block */
xfs_extlen_t rm_blockcount; /* extent length */
uint64_t rm_owner; /* extent owner */
uint64_t rm_offset; /* offset within the owner */
unsigned int rm_flags; /* state flags */
};
/* /*
* Key structure * Key structure
* *
...@@ -1626,7 +1612,7 @@ unsigned int xfs_refc_block(struct xfs_mount *mp); ...@@ -1626,7 +1612,7 @@ unsigned int xfs_refc_block(struct xfs_mount *mp);
* on the startblock. This speeds up mount time deletion of stale * on the startblock. This speeds up mount time deletion of stale
* staging extents because they're all at the right side of the tree. * staging extents because they're all at the right side of the tree.
*/ */
#define XFS_REFC_COW_START ((xfs_agblock_t)(1U << 31)) #define XFS_REFC_COWFLAG (1U << 31)
#define REFCNTBT_COWFLAG_BITLEN 1 #define REFCNTBT_COWFLAG_BITLEN 1
#define REFCNTBT_AGBLOCK_BITLEN 31 #define REFCNTBT_AGBLOCK_BITLEN 31
...@@ -1640,12 +1626,6 @@ struct xfs_refcount_key { ...@@ -1640,12 +1626,6 @@ struct xfs_refcount_key {
__be32 rc_startblock; /* starting block number */ __be32 rc_startblock; /* starting block number */
}; };
struct xfs_refcount_irec {
xfs_agblock_t rc_startblock; /* starting block number */
xfs_extlen_t rc_blockcount; /* count of free blocks */
xfs_nlink_t rc_refcount; /* number of inodes linked here */
};
#define MAXREFCOUNT ((xfs_nlink_t)~0U) #define MAXREFCOUNT ((xfs_nlink_t)~0U)
#define MAXREFCEXTLEN ((xfs_extlen_t)~0U) #define MAXREFCEXTLEN ((xfs_extlen_t)~0U)
......
...@@ -46,13 +46,16 @@ STATIC int __xfs_refcount_cow_free(struct xfs_btree_cur *rcur, ...@@ -46,13 +46,16 @@ STATIC int __xfs_refcount_cow_free(struct xfs_btree_cur *rcur,
int int
xfs_refcount_lookup_le( xfs_refcount_lookup_le(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
enum xfs_refc_domain domain,
xfs_agblock_t bno, xfs_agblock_t bno,
int *stat) int *stat)
{ {
trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno, bno, trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno,
xfs_refcount_encode_startblock(bno, domain),
XFS_LOOKUP_LE); XFS_LOOKUP_LE);
cur->bc_rec.rc.rc_startblock = bno; cur->bc_rec.rc.rc_startblock = bno;
cur->bc_rec.rc.rc_blockcount = 0; cur->bc_rec.rc.rc_blockcount = 0;
cur->bc_rec.rc.rc_domain = domain;
return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat); return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat);
} }
...@@ -63,13 +66,16 @@ xfs_refcount_lookup_le( ...@@ -63,13 +66,16 @@ xfs_refcount_lookup_le(
int int
xfs_refcount_lookup_ge( xfs_refcount_lookup_ge(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
enum xfs_refc_domain domain,
xfs_agblock_t bno, xfs_agblock_t bno,
int *stat) int *stat)
{ {
trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno, bno, trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno,
xfs_refcount_encode_startblock(bno, domain),
XFS_LOOKUP_GE); XFS_LOOKUP_GE);
cur->bc_rec.rc.rc_startblock = bno; cur->bc_rec.rc.rc_startblock = bno;
cur->bc_rec.rc.rc_blockcount = 0; cur->bc_rec.rc.rc_blockcount = 0;
cur->bc_rec.rc.rc_domain = domain;
return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat); return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat);
} }
...@@ -80,13 +86,16 @@ xfs_refcount_lookup_ge( ...@@ -80,13 +86,16 @@ xfs_refcount_lookup_ge(
int int
xfs_refcount_lookup_eq( xfs_refcount_lookup_eq(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
enum xfs_refc_domain domain,
xfs_agblock_t bno, xfs_agblock_t bno,
int *stat) int *stat)
{ {
trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno, bno, trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno,
xfs_refcount_encode_startblock(bno, domain),
XFS_LOOKUP_LE); XFS_LOOKUP_LE);
cur->bc_rec.rc.rc_startblock = bno; cur->bc_rec.rc.rc_startblock = bno;
cur->bc_rec.rc.rc_blockcount = 0; cur->bc_rec.rc.rc_blockcount = 0;
cur->bc_rec.rc.rc_domain = domain;
return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat); return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat);
} }
...@@ -96,7 +105,17 @@ xfs_refcount_btrec_to_irec( ...@@ -96,7 +105,17 @@ xfs_refcount_btrec_to_irec(
const union xfs_btree_rec *rec, const union xfs_btree_rec *rec,
struct xfs_refcount_irec *irec) struct xfs_refcount_irec *irec)
{ {
irec->rc_startblock = be32_to_cpu(rec->refc.rc_startblock); uint32_t start;
start = be32_to_cpu(rec->refc.rc_startblock);
if (start & XFS_REFC_COWFLAG) {
start &= ~XFS_REFC_COWFLAG;
irec->rc_domain = XFS_REFC_DOMAIN_COW;
} else {
irec->rc_domain = XFS_REFC_DOMAIN_SHARED;
}
irec->rc_startblock = start;
irec->rc_blockcount = be32_to_cpu(rec->refc.rc_blockcount); irec->rc_blockcount = be32_to_cpu(rec->refc.rc_blockcount);
irec->rc_refcount = be32_to_cpu(rec->refc.rc_refcount); irec->rc_refcount = be32_to_cpu(rec->refc.rc_refcount);
} }
...@@ -114,7 +133,6 @@ xfs_refcount_get_rec( ...@@ -114,7 +133,6 @@ xfs_refcount_get_rec(
struct xfs_perag *pag = cur->bc_ag.pag; struct xfs_perag *pag = cur->bc_ag.pag;
union xfs_btree_rec *rec; union xfs_btree_rec *rec;
int error; int error;
xfs_agblock_t realstart;
error = xfs_btree_get_rec(cur, &rec, stat); error = xfs_btree_get_rec(cur, &rec, stat);
if (error || !*stat) if (error || !*stat)
...@@ -124,22 +142,11 @@ xfs_refcount_get_rec( ...@@ -124,22 +142,11 @@ xfs_refcount_get_rec(
if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN) if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN)
goto out_bad_rec; goto out_bad_rec;
/* handle special COW-staging state */ if (!xfs_refcount_check_domain(irec))
realstart = irec->rc_startblock;
if (realstart & XFS_REFC_COW_START) {
if (irec->rc_refcount != 1)
goto out_bad_rec;
realstart &= ~XFS_REFC_COW_START;
} else if (irec->rc_refcount < 2) {
goto out_bad_rec; goto out_bad_rec;
}
/* check for valid extent range, including overflow */ /* check for valid extent range, including overflow */
if (!xfs_verify_agbno(pag, realstart)) if (!xfs_verify_agbext(pag, irec->rc_startblock, irec->rc_blockcount))
goto out_bad_rec;
if (realstart > realstart + irec->rc_blockcount)
goto out_bad_rec;
if (!xfs_verify_agbno(pag, realstart + irec->rc_blockcount - 1))
goto out_bad_rec; goto out_bad_rec;
if (irec->rc_refcount == 0 || irec->rc_refcount > MAXREFCOUNT) if (irec->rc_refcount == 0 || irec->rc_refcount > MAXREFCOUNT)
...@@ -169,12 +176,17 @@ xfs_refcount_update( ...@@ -169,12 +176,17 @@ xfs_refcount_update(
struct xfs_refcount_irec *irec) struct xfs_refcount_irec *irec)
{ {
union xfs_btree_rec rec; union xfs_btree_rec rec;
uint32_t start;
int error; int error;
trace_xfs_refcount_update(cur->bc_mp, cur->bc_ag.pag->pag_agno, irec); trace_xfs_refcount_update(cur->bc_mp, cur->bc_ag.pag->pag_agno, irec);
rec.refc.rc_startblock = cpu_to_be32(irec->rc_startblock);
start = xfs_refcount_encode_startblock(irec->rc_startblock,
irec->rc_domain);
rec.refc.rc_startblock = cpu_to_be32(start);
rec.refc.rc_blockcount = cpu_to_be32(irec->rc_blockcount); rec.refc.rc_blockcount = cpu_to_be32(irec->rc_blockcount);
rec.refc.rc_refcount = cpu_to_be32(irec->rc_refcount); rec.refc.rc_refcount = cpu_to_be32(irec->rc_refcount);
error = xfs_btree_update(cur, &rec); error = xfs_btree_update(cur, &rec);
if (error) if (error)
trace_xfs_refcount_update_error(cur->bc_mp, trace_xfs_refcount_update_error(cur->bc_mp,
...@@ -196,9 +208,12 @@ xfs_refcount_insert( ...@@ -196,9 +208,12 @@ xfs_refcount_insert(
int error; int error;
trace_xfs_refcount_insert(cur->bc_mp, cur->bc_ag.pag->pag_agno, irec); trace_xfs_refcount_insert(cur->bc_mp, cur->bc_ag.pag->pag_agno, irec);
cur->bc_rec.rc.rc_startblock = irec->rc_startblock; cur->bc_rec.rc.rc_startblock = irec->rc_startblock;
cur->bc_rec.rc.rc_blockcount = irec->rc_blockcount; cur->bc_rec.rc.rc_blockcount = irec->rc_blockcount;
cur->bc_rec.rc.rc_refcount = irec->rc_refcount; cur->bc_rec.rc.rc_refcount = irec->rc_refcount;
cur->bc_rec.rc.rc_domain = irec->rc_domain;
error = xfs_btree_insert(cur, i); error = xfs_btree_insert(cur, i);
if (error) if (error)
goto out_error; goto out_error;
...@@ -244,7 +259,8 @@ xfs_refcount_delete( ...@@ -244,7 +259,8 @@ xfs_refcount_delete(
} }
if (error) if (error)
goto out_error; goto out_error;
error = xfs_refcount_lookup_ge(cur, irec.rc_startblock, &found_rec); error = xfs_refcount_lookup_ge(cur, irec.rc_domain, irec.rc_startblock,
&found_rec);
out_error: out_error:
if (error) if (error)
trace_xfs_refcount_delete_error(cur->bc_mp, trace_xfs_refcount_delete_error(cur->bc_mp,
...@@ -343,6 +359,7 @@ xfs_refc_next( ...@@ -343,6 +359,7 @@ xfs_refc_next(
STATIC int STATIC int
xfs_refcount_split_extent( xfs_refcount_split_extent(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
enum xfs_refc_domain domain,
xfs_agblock_t agbno, xfs_agblock_t agbno,
bool *shape_changed) bool *shape_changed)
{ {
...@@ -351,7 +368,7 @@ xfs_refcount_split_extent( ...@@ -351,7 +368,7 @@ xfs_refcount_split_extent(
int error; int error;
*shape_changed = false; *shape_changed = false;
error = xfs_refcount_lookup_le(cur, agbno, &found_rec); error = xfs_refcount_lookup_le(cur, domain, agbno, &found_rec);
if (error) if (error)
goto out_error; goto out_error;
if (!found_rec) if (!found_rec)
...@@ -364,6 +381,8 @@ xfs_refcount_split_extent( ...@@ -364,6 +381,8 @@ xfs_refcount_split_extent(
error = -EFSCORRUPTED; error = -EFSCORRUPTED;
goto out_error; goto out_error;
} }
if (rcext.rc_domain != domain)
return 0;
if (rcext.rc_startblock == agbno || xfs_refc_next(&rcext) <= agbno) if (rcext.rc_startblock == agbno || xfs_refc_next(&rcext) <= agbno)
return 0; return 0;
...@@ -415,6 +434,9 @@ xfs_refcount_merge_center_extents( ...@@ -415,6 +434,9 @@ xfs_refcount_merge_center_extents(
trace_xfs_refcount_merge_center_extents(cur->bc_mp, trace_xfs_refcount_merge_center_extents(cur->bc_mp,
cur->bc_ag.pag->pag_agno, left, center, right); cur->bc_ag.pag->pag_agno, left, center, right);
ASSERT(left->rc_domain == center->rc_domain);
ASSERT(right->rc_domain == center->rc_domain);
/* /*
* Make sure the center and right extents are not in the btree. * Make sure the center and right extents are not in the btree.
* If the center extent was synthesized, the first delete call * If the center extent was synthesized, the first delete call
...@@ -423,8 +445,8 @@ xfs_refcount_merge_center_extents( ...@@ -423,8 +445,8 @@ xfs_refcount_merge_center_extents(
* call removes the center and the second one removes the right * call removes the center and the second one removes the right
* extent. * extent.
*/ */
error = xfs_refcount_lookup_ge(cur, center->rc_startblock, error = xfs_refcount_lookup_ge(cur, center->rc_domain,
&found_rec); center->rc_startblock, &found_rec);
if (error) if (error)
goto out_error; goto out_error;
if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) {
...@@ -451,8 +473,8 @@ xfs_refcount_merge_center_extents( ...@@ -451,8 +473,8 @@ xfs_refcount_merge_center_extents(
} }
/* Enlarge the left extent. */ /* Enlarge the left extent. */
error = xfs_refcount_lookup_le(cur, left->rc_startblock, error = xfs_refcount_lookup_le(cur, left->rc_domain,
&found_rec); left->rc_startblock, &found_rec);
if (error) if (error)
goto out_error; goto out_error;
if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) {
...@@ -491,10 +513,12 @@ xfs_refcount_merge_left_extent( ...@@ -491,10 +513,12 @@ xfs_refcount_merge_left_extent(
trace_xfs_refcount_merge_left_extent(cur->bc_mp, trace_xfs_refcount_merge_left_extent(cur->bc_mp,
cur->bc_ag.pag->pag_agno, left, cleft); cur->bc_ag.pag->pag_agno, left, cleft);
ASSERT(left->rc_domain == cleft->rc_domain);
/* If the extent at agbno (cleft) wasn't synthesized, remove it. */ /* If the extent at agbno (cleft) wasn't synthesized, remove it. */
if (cleft->rc_refcount > 1) { if (cleft->rc_refcount > 1) {
error = xfs_refcount_lookup_le(cur, cleft->rc_startblock, error = xfs_refcount_lookup_le(cur, cleft->rc_domain,
&found_rec); cleft->rc_startblock, &found_rec);
if (error) if (error)
goto out_error; goto out_error;
if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) {
...@@ -512,8 +536,8 @@ xfs_refcount_merge_left_extent( ...@@ -512,8 +536,8 @@ xfs_refcount_merge_left_extent(
} }
/* Enlarge the left extent. */ /* Enlarge the left extent. */
error = xfs_refcount_lookup_le(cur, left->rc_startblock, error = xfs_refcount_lookup_le(cur, left->rc_domain,
&found_rec); left->rc_startblock, &found_rec);
if (error) if (error)
goto out_error; goto out_error;
if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) {
...@@ -552,13 +576,15 @@ xfs_refcount_merge_right_extent( ...@@ -552,13 +576,15 @@ xfs_refcount_merge_right_extent(
trace_xfs_refcount_merge_right_extent(cur->bc_mp, trace_xfs_refcount_merge_right_extent(cur->bc_mp,
cur->bc_ag.pag->pag_agno, cright, right); cur->bc_ag.pag->pag_agno, cright, right);
ASSERT(right->rc_domain == cright->rc_domain);
/* /*
* If the extent ending at agbno+aglen (cright) wasn't synthesized, * If the extent ending at agbno+aglen (cright) wasn't synthesized,
* remove it. * remove it.
*/ */
if (cright->rc_refcount > 1) { if (cright->rc_refcount > 1) {
error = xfs_refcount_lookup_le(cur, cright->rc_startblock, error = xfs_refcount_lookup_le(cur, cright->rc_domain,
&found_rec); cright->rc_startblock, &found_rec);
if (error) if (error)
goto out_error; goto out_error;
if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) {
...@@ -576,8 +602,8 @@ xfs_refcount_merge_right_extent( ...@@ -576,8 +602,8 @@ xfs_refcount_merge_right_extent(
} }
/* Enlarge the right extent. */ /* Enlarge the right extent. */
error = xfs_refcount_lookup_le(cur, right->rc_startblock, error = xfs_refcount_lookup_le(cur, right->rc_domain,
&found_rec); right->rc_startblock, &found_rec);
if (error) if (error)
goto out_error; goto out_error;
if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) {
...@@ -600,8 +626,6 @@ xfs_refcount_merge_right_extent( ...@@ -600,8 +626,6 @@ xfs_refcount_merge_right_extent(
return error; return error;
} }
#define XFS_FIND_RCEXT_SHARED 1
#define XFS_FIND_RCEXT_COW 2
/* /*
* Find the left extent and the one after it (cleft). This function assumes * Find the left extent and the one after it (cleft). This function assumes
* that we've already split any extent crossing agbno. * that we've already split any extent crossing agbno.
...@@ -611,16 +635,16 @@ xfs_refcount_find_left_extents( ...@@ -611,16 +635,16 @@ xfs_refcount_find_left_extents(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
struct xfs_refcount_irec *left, struct xfs_refcount_irec *left,
struct xfs_refcount_irec *cleft, struct xfs_refcount_irec *cleft,
enum xfs_refc_domain domain,
xfs_agblock_t agbno, xfs_agblock_t agbno,
xfs_extlen_t aglen, xfs_extlen_t aglen)
int flags)
{ {
struct xfs_refcount_irec tmp; struct xfs_refcount_irec tmp;
int error; int error;
int found_rec; int found_rec;
left->rc_startblock = cleft->rc_startblock = NULLAGBLOCK; left->rc_startblock = cleft->rc_startblock = NULLAGBLOCK;
error = xfs_refcount_lookup_le(cur, agbno - 1, &found_rec); error = xfs_refcount_lookup_le(cur, domain, agbno - 1, &found_rec);
if (error) if (error)
goto out_error; goto out_error;
if (!found_rec) if (!found_rec)
...@@ -634,11 +658,9 @@ xfs_refcount_find_left_extents( ...@@ -634,11 +658,9 @@ xfs_refcount_find_left_extents(
goto out_error; goto out_error;
} }
if (xfs_refc_next(&tmp) != agbno) if (tmp.rc_domain != domain)
return 0;
if ((flags & XFS_FIND_RCEXT_SHARED) && tmp.rc_refcount < 2)
return 0; return 0;
if ((flags & XFS_FIND_RCEXT_COW) && tmp.rc_refcount > 1) if (xfs_refc_next(&tmp) != agbno)
return 0; return 0;
/* We have a left extent; retrieve (or invent) the next right one */ /* We have a left extent; retrieve (or invent) the next right one */
*left = tmp; *left = tmp;
...@@ -655,6 +677,9 @@ xfs_refcount_find_left_extents( ...@@ -655,6 +677,9 @@ xfs_refcount_find_left_extents(
goto out_error; goto out_error;
} }
if (tmp.rc_domain != domain)
goto not_found;
/* if tmp starts at the end of our range, just use that */ /* if tmp starts at the end of our range, just use that */
if (tmp.rc_startblock == agbno) if (tmp.rc_startblock == agbno)
*cleft = tmp; *cleft = tmp;
...@@ -671,8 +696,10 @@ xfs_refcount_find_left_extents( ...@@ -671,8 +696,10 @@ xfs_refcount_find_left_extents(
cleft->rc_blockcount = min(aglen, cleft->rc_blockcount = min(aglen,
tmp.rc_startblock - agbno); tmp.rc_startblock - agbno);
cleft->rc_refcount = 1; cleft->rc_refcount = 1;
cleft->rc_domain = domain;
} }
} else { } else {
not_found:
/* /*
* No extents, so pretend that there's one covering the whole * No extents, so pretend that there's one covering the whole
* range. * range.
...@@ -680,6 +707,7 @@ xfs_refcount_find_left_extents( ...@@ -680,6 +707,7 @@ xfs_refcount_find_left_extents(
cleft->rc_startblock = agbno; cleft->rc_startblock = agbno;
cleft->rc_blockcount = aglen; cleft->rc_blockcount = aglen;
cleft->rc_refcount = 1; cleft->rc_refcount = 1;
cleft->rc_domain = domain;
} }
trace_xfs_refcount_find_left_extent(cur->bc_mp, cur->bc_ag.pag->pag_agno, trace_xfs_refcount_find_left_extent(cur->bc_mp, cur->bc_ag.pag->pag_agno,
left, cleft, agbno); left, cleft, agbno);
...@@ -700,16 +728,16 @@ xfs_refcount_find_right_extents( ...@@ -700,16 +728,16 @@ xfs_refcount_find_right_extents(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
struct xfs_refcount_irec *right, struct xfs_refcount_irec *right,
struct xfs_refcount_irec *cright, struct xfs_refcount_irec *cright,
enum xfs_refc_domain domain,
xfs_agblock_t agbno, xfs_agblock_t agbno,
xfs_extlen_t aglen, xfs_extlen_t aglen)
int flags)
{ {
struct xfs_refcount_irec tmp; struct xfs_refcount_irec tmp;
int error; int error;
int found_rec; int found_rec;
right->rc_startblock = cright->rc_startblock = NULLAGBLOCK; right->rc_startblock = cright->rc_startblock = NULLAGBLOCK;
error = xfs_refcount_lookup_ge(cur, agbno + aglen, &found_rec); error = xfs_refcount_lookup_ge(cur, domain, agbno + aglen, &found_rec);
if (error) if (error)
goto out_error; goto out_error;
if (!found_rec) if (!found_rec)
...@@ -723,11 +751,9 @@ xfs_refcount_find_right_extents( ...@@ -723,11 +751,9 @@ xfs_refcount_find_right_extents(
goto out_error; goto out_error;
} }
if (tmp.rc_startblock != agbno + aglen) if (tmp.rc_domain != domain)
return 0;
if ((flags & XFS_FIND_RCEXT_SHARED) && tmp.rc_refcount < 2)
return 0; return 0;
if ((flags & XFS_FIND_RCEXT_COW) && tmp.rc_refcount > 1) if (tmp.rc_startblock != agbno + aglen)
return 0; return 0;
/* We have a right extent; retrieve (or invent) the next left one */ /* We have a right extent; retrieve (or invent) the next left one */
*right = tmp; *right = tmp;
...@@ -744,6 +770,9 @@ xfs_refcount_find_right_extents( ...@@ -744,6 +770,9 @@ xfs_refcount_find_right_extents(
goto out_error; goto out_error;
} }
if (tmp.rc_domain != domain)
goto not_found;
/* if tmp ends at the end of our range, just use that */ /* if tmp ends at the end of our range, just use that */
if (xfs_refc_next(&tmp) == agbno + aglen) if (xfs_refc_next(&tmp) == agbno + aglen)
*cright = tmp; *cright = tmp;
...@@ -760,8 +789,10 @@ xfs_refcount_find_right_extents( ...@@ -760,8 +789,10 @@ xfs_refcount_find_right_extents(
cright->rc_blockcount = right->rc_startblock - cright->rc_blockcount = right->rc_startblock -
cright->rc_startblock; cright->rc_startblock;
cright->rc_refcount = 1; cright->rc_refcount = 1;
cright->rc_domain = domain;
} }
} else { } else {
not_found:
/* /*
* No extents, so pretend that there's one covering the whole * No extents, so pretend that there's one covering the whole
* range. * range.
...@@ -769,6 +800,7 @@ xfs_refcount_find_right_extents( ...@@ -769,6 +800,7 @@ xfs_refcount_find_right_extents(
cright->rc_startblock = agbno; cright->rc_startblock = agbno;
cright->rc_blockcount = aglen; cright->rc_blockcount = aglen;
cright->rc_refcount = 1; cright->rc_refcount = 1;
cright->rc_domain = domain;
} }
trace_xfs_refcount_find_right_extent(cur->bc_mp, cur->bc_ag.pag->pag_agno, trace_xfs_refcount_find_right_extent(cur->bc_mp, cur->bc_ag.pag->pag_agno,
cright, right, agbno + aglen); cright, right, agbno + aglen);
...@@ -794,10 +826,10 @@ xfs_refc_valid( ...@@ -794,10 +826,10 @@ xfs_refc_valid(
STATIC int STATIC int
xfs_refcount_merge_extents( xfs_refcount_merge_extents(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
enum xfs_refc_domain domain,
xfs_agblock_t *agbno, xfs_agblock_t *agbno,
xfs_extlen_t *aglen, xfs_extlen_t *aglen,
enum xfs_refc_adjust_op adjust, enum xfs_refc_adjust_op adjust,
int flags,
bool *shape_changed) bool *shape_changed)
{ {
struct xfs_refcount_irec left = {0}, cleft = {0}; struct xfs_refcount_irec left = {0}, cleft = {0};
...@@ -812,12 +844,12 @@ xfs_refcount_merge_extents( ...@@ -812,12 +844,12 @@ xfs_refcount_merge_extents(
* just below (agbno + aglen) [cright], and just above (agbno + aglen) * just below (agbno + aglen) [cright], and just above (agbno + aglen)
* [right]. * [right].
*/ */
error = xfs_refcount_find_left_extents(cur, &left, &cleft, *agbno, error = xfs_refcount_find_left_extents(cur, &left, &cleft, domain,
*aglen, flags); *agbno, *aglen);
if (error) if (error)
return error; return error;
error = xfs_refcount_find_right_extents(cur, &right, &cright, *agbno, error = xfs_refcount_find_right_extents(cur, &right, &cright, domain,
*aglen, flags); *agbno, *aglen);
if (error) if (error)
return error; return error;
...@@ -870,7 +902,7 @@ xfs_refcount_merge_extents( ...@@ -870,7 +902,7 @@ xfs_refcount_merge_extents(
aglen); aglen);
} }
return error; return 0;
} }
/* /*
...@@ -933,7 +965,8 @@ xfs_refcount_adjust_extents( ...@@ -933,7 +965,8 @@ xfs_refcount_adjust_extents(
if (*aglen == 0) if (*aglen == 0)
return 0; return 0;
error = xfs_refcount_lookup_ge(cur, *agbno, &found_rec); error = xfs_refcount_lookup_ge(cur, XFS_REFC_DOMAIN_SHARED, *agbno,
&found_rec);
if (error) if (error)
goto out_error; goto out_error;
...@@ -941,10 +974,11 @@ xfs_refcount_adjust_extents( ...@@ -941,10 +974,11 @@ xfs_refcount_adjust_extents(
error = xfs_refcount_get_rec(cur, &ext, &found_rec); error = xfs_refcount_get_rec(cur, &ext, &found_rec);
if (error) if (error)
goto out_error; goto out_error;
if (!found_rec) { if (!found_rec || ext.rc_domain != XFS_REFC_DOMAIN_SHARED) {
ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks; ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks;
ext.rc_blockcount = 0; ext.rc_blockcount = 0;
ext.rc_refcount = 0; ext.rc_refcount = 0;
ext.rc_domain = XFS_REFC_DOMAIN_SHARED;
} }
/* /*
...@@ -957,6 +991,8 @@ xfs_refcount_adjust_extents( ...@@ -957,6 +991,8 @@ xfs_refcount_adjust_extents(
tmp.rc_blockcount = min(*aglen, tmp.rc_blockcount = min(*aglen,
ext.rc_startblock - *agbno); ext.rc_startblock - *agbno);
tmp.rc_refcount = 1 + adj; tmp.rc_refcount = 1 + adj;
tmp.rc_domain = XFS_REFC_DOMAIN_SHARED;
trace_xfs_refcount_modify_extent(cur->bc_mp, trace_xfs_refcount_modify_extent(cur->bc_mp,
cur->bc_ag.pag->pag_agno, &tmp); cur->bc_ag.pag->pag_agno, &tmp);
...@@ -986,15 +1022,30 @@ xfs_refcount_adjust_extents( ...@@ -986,15 +1022,30 @@ xfs_refcount_adjust_extents(
(*agbno) += tmp.rc_blockcount; (*agbno) += tmp.rc_blockcount;
(*aglen) -= tmp.rc_blockcount; (*aglen) -= tmp.rc_blockcount;
error = xfs_refcount_lookup_ge(cur, *agbno, /* Stop if there's nothing left to modify */
if (*aglen == 0 || !xfs_refcount_still_have_space(cur))
break;
/* Move the cursor to the start of ext. */
error = xfs_refcount_lookup_ge(cur,
XFS_REFC_DOMAIN_SHARED, *agbno,
&found_rec); &found_rec);
if (error) if (error)
goto out_error; goto out_error;
} }
/* Stop if there's nothing left to modify */ /*
if (*aglen == 0 || !xfs_refcount_still_have_space(cur)) * A previous step trimmed agbno/aglen such that the end of the
break; * range would not be in the middle of the record. If this is
* no longer the case, something is seriously wrong with the
* btree. Make sure we never feed the synthesized record into
* the processing loop below.
*/
if (XFS_IS_CORRUPT(cur->bc_mp, ext.rc_blockcount == 0) ||
XFS_IS_CORRUPT(cur->bc_mp, ext.rc_blockcount > *aglen)) {
error = -EFSCORRUPTED;
goto out_error;
}
/* /*
* Adjust the reference count and either update the tree * Adjust the reference count and either update the tree
...@@ -1070,13 +1121,15 @@ xfs_refcount_adjust( ...@@ -1070,13 +1121,15 @@ xfs_refcount_adjust(
/* /*
* Ensure that no rcextents cross the boundary of the adjustment range. * Ensure that no rcextents cross the boundary of the adjustment range.
*/ */
error = xfs_refcount_split_extent(cur, agbno, &shape_changed); error = xfs_refcount_split_extent(cur, XFS_REFC_DOMAIN_SHARED,
agbno, &shape_changed);
if (error) if (error)
goto out_error; goto out_error;
if (shape_changed) if (shape_changed)
shape_changes++; shape_changes++;
error = xfs_refcount_split_extent(cur, agbno + aglen, &shape_changed); error = xfs_refcount_split_extent(cur, XFS_REFC_DOMAIN_SHARED,
agbno + aglen, &shape_changed);
if (error) if (error)
goto out_error; goto out_error;
if (shape_changed) if (shape_changed)
...@@ -1085,8 +1138,8 @@ xfs_refcount_adjust( ...@@ -1085,8 +1138,8 @@ xfs_refcount_adjust(
/* /*
* Try to merge with the left or right extents of the range. * Try to merge with the left or right extents of the range.
*/ */
error = xfs_refcount_merge_extents(cur, new_agbno, new_aglen, adj, error = xfs_refcount_merge_extents(cur, XFS_REFC_DOMAIN_SHARED,
XFS_FIND_RCEXT_SHARED, &shape_changed); new_agbno, new_aglen, adj, &shape_changed);
if (error) if (error)
goto out_error; goto out_error;
if (shape_changed) if (shape_changed)
...@@ -1124,6 +1177,32 @@ xfs_refcount_finish_one_cleanup( ...@@ -1124,6 +1177,32 @@ xfs_refcount_finish_one_cleanup(
xfs_trans_brelse(tp, agbp); xfs_trans_brelse(tp, agbp);
} }
/*
* Set up a continuation a deferred refcount operation by updating the intent.
* Checks to make sure we're not going to run off the end of the AG.
*/
static inline int
xfs_refcount_continue_op(
struct xfs_btree_cur *cur,
xfs_fsblock_t startblock,
xfs_agblock_t new_agbno,
xfs_extlen_t new_len,
xfs_fsblock_t *new_fsbno)
{
struct xfs_mount *mp = cur->bc_mp;
struct xfs_perag *pag = cur->bc_ag.pag;
if (XFS_IS_CORRUPT(mp, !xfs_verify_agbext(pag, new_agbno, new_len)))
return -EFSCORRUPTED;
*new_fsbno = XFS_AGB_TO_FSB(mp, pag->pag_agno, new_agbno);
ASSERT(xfs_verify_fsbext(mp, *new_fsbno, new_len));
ASSERT(pag->pag_agno == XFS_FSB_TO_AGNO(mp, *new_fsbno));
return 0;
}
/* /*
* Process one of the deferred refcount operations. We pass back the * Process one of the deferred refcount operations. We pass back the
* btree cursor to maintain our lock on the btree between calls. * btree cursor to maintain our lock on the btree between calls.
...@@ -1191,12 +1270,20 @@ xfs_refcount_finish_one( ...@@ -1191,12 +1270,20 @@ xfs_refcount_finish_one(
case XFS_REFCOUNT_INCREASE: case XFS_REFCOUNT_INCREASE:
error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno, error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno,
new_len, XFS_REFCOUNT_ADJUST_INCREASE); new_len, XFS_REFCOUNT_ADJUST_INCREASE);
*new_fsb = XFS_AGB_TO_FSB(mp, pag->pag_agno, new_agbno); if (error)
goto out_drop;
if (*new_len > 0)
error = xfs_refcount_continue_op(rcur, startblock,
new_agbno, *new_len, new_fsb);
break; break;
case XFS_REFCOUNT_DECREASE: case XFS_REFCOUNT_DECREASE:
error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno, error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno,
new_len, XFS_REFCOUNT_ADJUST_DECREASE); new_len, XFS_REFCOUNT_ADJUST_DECREASE);
*new_fsb = XFS_AGB_TO_FSB(mp, pag->pag_agno, new_agbno); if (error)
goto out_drop;
if (*new_len > 0)
error = xfs_refcount_continue_op(rcur, startblock,
new_agbno, *new_len, new_fsb);
break; break;
case XFS_REFCOUNT_ALLOC_COW: case XFS_REFCOUNT_ALLOC_COW:
*new_fsb = startblock + blockcount; *new_fsb = startblock + blockcount;
...@@ -1307,7 +1394,8 @@ xfs_refcount_find_shared( ...@@ -1307,7 +1394,8 @@ xfs_refcount_find_shared(
*flen = 0; *flen = 0;
/* Try to find a refcount extent that crosses the start */ /* Try to find a refcount extent that crosses the start */
error = xfs_refcount_lookup_le(cur, agbno, &have); error = xfs_refcount_lookup_le(cur, XFS_REFC_DOMAIN_SHARED, agbno,
&have);
if (error) if (error)
goto out_error; goto out_error;
if (!have) { if (!have) {
...@@ -1325,6 +1413,8 @@ xfs_refcount_find_shared( ...@@ -1325,6 +1413,8 @@ xfs_refcount_find_shared(
error = -EFSCORRUPTED; error = -EFSCORRUPTED;
goto out_error; goto out_error;
} }
if (tmp.rc_domain != XFS_REFC_DOMAIN_SHARED)
goto done;
/* If the extent ends before the start, look at the next one */ /* If the extent ends before the start, look at the next one */
if (tmp.rc_startblock + tmp.rc_blockcount <= agbno) { if (tmp.rc_startblock + tmp.rc_blockcount <= agbno) {
...@@ -1340,6 +1430,8 @@ xfs_refcount_find_shared( ...@@ -1340,6 +1430,8 @@ xfs_refcount_find_shared(
error = -EFSCORRUPTED; error = -EFSCORRUPTED;
goto out_error; goto out_error;
} }
if (tmp.rc_domain != XFS_REFC_DOMAIN_SHARED)
goto done;
} }
/* If the extent starts after the range we want, bail out */ /* If the extent starts after the range we want, bail out */
...@@ -1371,7 +1463,8 @@ xfs_refcount_find_shared( ...@@ -1371,7 +1463,8 @@ xfs_refcount_find_shared(
error = -EFSCORRUPTED; error = -EFSCORRUPTED;
goto out_error; goto out_error;
} }
if (tmp.rc_startblock >= agbno + aglen || if (tmp.rc_domain != XFS_REFC_DOMAIN_SHARED ||
tmp.rc_startblock >= agbno + aglen ||
tmp.rc_startblock != *fbno + *flen) tmp.rc_startblock != *fbno + *flen)
break; break;
*flen = min(*flen + tmp.rc_blockcount, agbno + aglen - *fbno); *flen = min(*flen + tmp.rc_blockcount, agbno + aglen - *fbno);
...@@ -1455,17 +1548,23 @@ xfs_refcount_adjust_cow_extents( ...@@ -1455,17 +1548,23 @@ xfs_refcount_adjust_cow_extents(
return 0; return 0;
/* Find any overlapping refcount records */ /* Find any overlapping refcount records */
error = xfs_refcount_lookup_ge(cur, agbno, &found_rec); error = xfs_refcount_lookup_ge(cur, XFS_REFC_DOMAIN_COW, agbno,
&found_rec);
if (error) if (error)
goto out_error; goto out_error;
error = xfs_refcount_get_rec(cur, &ext, &found_rec); error = xfs_refcount_get_rec(cur, &ext, &found_rec);
if (error) if (error)
goto out_error; goto out_error;
if (XFS_IS_CORRUPT(cur->bc_mp, found_rec &&
ext.rc_domain != XFS_REFC_DOMAIN_COW)) {
error = -EFSCORRUPTED;
goto out_error;
}
if (!found_rec) { if (!found_rec) {
ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks + ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks;
XFS_REFC_COW_START;
ext.rc_blockcount = 0; ext.rc_blockcount = 0;
ext.rc_refcount = 0; ext.rc_refcount = 0;
ext.rc_domain = XFS_REFC_DOMAIN_COW;
} }
switch (adj) { switch (adj) {
...@@ -1480,6 +1579,8 @@ xfs_refcount_adjust_cow_extents( ...@@ -1480,6 +1579,8 @@ xfs_refcount_adjust_cow_extents(
tmp.rc_startblock = agbno; tmp.rc_startblock = agbno;
tmp.rc_blockcount = aglen; tmp.rc_blockcount = aglen;
tmp.rc_refcount = 1; tmp.rc_refcount = 1;
tmp.rc_domain = XFS_REFC_DOMAIN_COW;
trace_xfs_refcount_modify_extent(cur->bc_mp, trace_xfs_refcount_modify_extent(cur->bc_mp,
cur->bc_ag.pag->pag_agno, &tmp); cur->bc_ag.pag->pag_agno, &tmp);
...@@ -1542,24 +1643,24 @@ xfs_refcount_adjust_cow( ...@@ -1542,24 +1643,24 @@ xfs_refcount_adjust_cow(
bool shape_changed; bool shape_changed;
int error; int error;
agbno += XFS_REFC_COW_START;
/* /*
* Ensure that no rcextents cross the boundary of the adjustment range. * Ensure that no rcextents cross the boundary of the adjustment range.
*/ */
error = xfs_refcount_split_extent(cur, agbno, &shape_changed); error = xfs_refcount_split_extent(cur, XFS_REFC_DOMAIN_COW,
agbno, &shape_changed);
if (error) if (error)
goto out_error; goto out_error;
error = xfs_refcount_split_extent(cur, agbno + aglen, &shape_changed); error = xfs_refcount_split_extent(cur, XFS_REFC_DOMAIN_COW,
agbno + aglen, &shape_changed);
if (error) if (error)
goto out_error; goto out_error;
/* /*
* Try to merge with the left or right extents of the range. * Try to merge with the left or right extents of the range.
*/ */
error = xfs_refcount_merge_extents(cur, &agbno, &aglen, adj, error = xfs_refcount_merge_extents(cur, XFS_REFC_DOMAIN_COW, &agbno,
XFS_FIND_RCEXT_COW, &shape_changed); &aglen, adj, &shape_changed);
if (error) if (error)
goto out_error; goto out_error;
...@@ -1666,10 +1767,18 @@ xfs_refcount_recover_extent( ...@@ -1666,10 +1767,18 @@ xfs_refcount_recover_extent(
be32_to_cpu(rec->refc.rc_refcount) != 1)) be32_to_cpu(rec->refc.rc_refcount) != 1))
return -EFSCORRUPTED; return -EFSCORRUPTED;
rr = kmem_alloc(sizeof(struct xfs_refcount_recovery), 0); rr = kmalloc(sizeof(struct xfs_refcount_recovery),
GFP_KERNEL | __GFP_NOFAIL);
INIT_LIST_HEAD(&rr->rr_list);
xfs_refcount_btrec_to_irec(rec, &rr->rr_rrec); xfs_refcount_btrec_to_irec(rec, &rr->rr_rrec);
list_add_tail(&rr->rr_list, debris);
if (XFS_IS_CORRUPT(cur->bc_mp,
rr->rr_rrec.rc_domain != XFS_REFC_DOMAIN_COW)) {
kfree(rr);
return -EFSCORRUPTED;
}
list_add_tail(&rr->rr_list, debris);
return 0; return 0;
} }
...@@ -1687,10 +1796,11 @@ xfs_refcount_recover_cow_leftovers( ...@@ -1687,10 +1796,11 @@ xfs_refcount_recover_cow_leftovers(
union xfs_btree_irec low; union xfs_btree_irec low;
union xfs_btree_irec high; union xfs_btree_irec high;
xfs_fsblock_t fsb; xfs_fsblock_t fsb;
xfs_agblock_t agbno;
int error; int error;
if (mp->m_sb.sb_agblocks >= XFS_REFC_COW_START) /* reflink filesystems mustn't have AGs larger than 2^31-1 blocks */
BUILD_BUG_ON(XFS_MAX_CRC_AG_BLOCKS >= XFS_REFC_COWFLAG);
if (mp->m_sb.sb_agblocks > XFS_MAX_CRC_AG_BLOCKS)
return -EOPNOTSUPP; return -EOPNOTSUPP;
INIT_LIST_HEAD(&debris); INIT_LIST_HEAD(&debris);
...@@ -1717,7 +1827,7 @@ xfs_refcount_recover_cow_leftovers( ...@@ -1717,7 +1827,7 @@ xfs_refcount_recover_cow_leftovers(
/* Find all the leftover CoW staging extents. */ /* Find all the leftover CoW staging extents. */
memset(&low, 0, sizeof(low)); memset(&low, 0, sizeof(low));
memset(&high, 0, sizeof(high)); memset(&high, 0, sizeof(high));
low.rc.rc_startblock = XFS_REFC_COW_START; low.rc.rc_domain = high.rc.rc_domain = XFS_REFC_DOMAIN_COW;
high.rc.rc_startblock = -1U; high.rc.rc_startblock = -1U;
error = xfs_btree_query_range(cur, &low, &high, error = xfs_btree_query_range(cur, &low, &high,
xfs_refcount_recover_extent, &debris); xfs_refcount_recover_extent, &debris);
...@@ -1738,8 +1848,8 @@ xfs_refcount_recover_cow_leftovers( ...@@ -1738,8 +1848,8 @@ xfs_refcount_recover_cow_leftovers(
&rr->rr_rrec); &rr->rr_rrec);
/* Free the orphan record */ /* Free the orphan record */
agbno = rr->rr_rrec.rc_startblock - XFS_REFC_COW_START; fsb = XFS_AGB_TO_FSB(mp, pag->pag_agno,
fsb = XFS_AGB_TO_FSB(mp, pag->pag_agno, agbno); rr->rr_rrec.rc_startblock);
xfs_refcount_free_cow_extent(tp, fsb, xfs_refcount_free_cow_extent(tp, fsb,
rr->rr_rrec.rc_blockcount); rr->rr_rrec.rc_blockcount);
...@@ -1751,7 +1861,7 @@ xfs_refcount_recover_cow_leftovers( ...@@ -1751,7 +1861,7 @@ xfs_refcount_recover_cow_leftovers(
goto out_free; goto out_free;
list_del(&rr->rr_list); list_del(&rr->rr_list);
kmem_free(rr); kfree(rr);
} }
return error; return error;
...@@ -1761,7 +1871,7 @@ xfs_refcount_recover_cow_leftovers( ...@@ -1761,7 +1871,7 @@ xfs_refcount_recover_cow_leftovers(
/* Free the leftover list */ /* Free the leftover list */
list_for_each_entry_safe(rr, n, &debris, rr_list) { list_for_each_entry_safe(rr, n, &debris, rr_list) {
list_del(&rr->rr_list); list_del(&rr->rr_list);
kmem_free(rr); kfree(rr);
} }
return error; return error;
} }
...@@ -1770,6 +1880,7 @@ xfs_refcount_recover_cow_leftovers( ...@@ -1770,6 +1880,7 @@ xfs_refcount_recover_cow_leftovers(
int int
xfs_refcount_has_record( xfs_refcount_has_record(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
enum xfs_refc_domain domain,
xfs_agblock_t bno, xfs_agblock_t bno,
xfs_extlen_t len, xfs_extlen_t len,
bool *exists) bool *exists)
...@@ -1781,6 +1892,7 @@ xfs_refcount_has_record( ...@@ -1781,6 +1892,7 @@ xfs_refcount_has_record(
low.rc.rc_startblock = bno; low.rc.rc_startblock = bno;
memset(&high, 0xFF, sizeof(high)); memset(&high, 0xFF, sizeof(high));
high.rc.rc_startblock = bno + len - 1; high.rc.rc_startblock = bno + len - 1;
low.rc.rc_domain = high.rc.rc_domain = domain;
return xfs_btree_has_record(cur, &low, &high, exists); return xfs_btree_has_record(cur, &low, &high, exists);
} }
......
...@@ -14,14 +14,33 @@ struct xfs_bmbt_irec; ...@@ -14,14 +14,33 @@ struct xfs_bmbt_irec;
struct xfs_refcount_irec; struct xfs_refcount_irec;
extern int xfs_refcount_lookup_le(struct xfs_btree_cur *cur, extern int xfs_refcount_lookup_le(struct xfs_btree_cur *cur,
xfs_agblock_t bno, int *stat); enum xfs_refc_domain domain, xfs_agblock_t bno, int *stat);
extern int xfs_refcount_lookup_ge(struct xfs_btree_cur *cur, extern int xfs_refcount_lookup_ge(struct xfs_btree_cur *cur,
xfs_agblock_t bno, int *stat); enum xfs_refc_domain domain, xfs_agblock_t bno, int *stat);
extern int xfs_refcount_lookup_eq(struct xfs_btree_cur *cur, extern int xfs_refcount_lookup_eq(struct xfs_btree_cur *cur,
xfs_agblock_t bno, int *stat); enum xfs_refc_domain domain, xfs_agblock_t bno, int *stat);
extern int xfs_refcount_get_rec(struct xfs_btree_cur *cur, extern int xfs_refcount_get_rec(struct xfs_btree_cur *cur,
struct xfs_refcount_irec *irec, int *stat); struct xfs_refcount_irec *irec, int *stat);
static inline uint32_t
xfs_refcount_encode_startblock(
xfs_agblock_t startblock,
enum xfs_refc_domain domain)
{
uint32_t start;
/*
* low level btree operations need to handle the generic btree range
* query functions (which set rc_domain == -1U), so we check that the
* domain is /not/ shared.
*/
start = startblock & ~XFS_REFC_COWFLAG;
if (domain != XFS_REFC_DOMAIN_SHARED)
start |= XFS_REFC_COWFLAG;
return start;
}
enum xfs_refcount_intent_type { enum xfs_refcount_intent_type {
XFS_REFCOUNT_INCREASE = 1, XFS_REFCOUNT_INCREASE = 1,
XFS_REFCOUNT_DECREASE, XFS_REFCOUNT_DECREASE,
...@@ -36,6 +55,18 @@ struct xfs_refcount_intent { ...@@ -36,6 +55,18 @@ struct xfs_refcount_intent {
xfs_fsblock_t ri_startblock; xfs_fsblock_t ri_startblock;
}; };
/* Check that the refcount is appropriate for the record domain. */
static inline bool
xfs_refcount_check_domain(
const struct xfs_refcount_irec *irec)
{
if (irec->rc_domain == XFS_REFC_DOMAIN_COW && irec->rc_refcount != 1)
return false;
if (irec->rc_domain == XFS_REFC_DOMAIN_SHARED && irec->rc_refcount < 2)
return false;
return true;
}
void xfs_refcount_increase_extent(struct xfs_trans *tp, void xfs_refcount_increase_extent(struct xfs_trans *tp,
struct xfs_bmbt_irec *irec); struct xfs_bmbt_irec *irec);
void xfs_refcount_decrease_extent(struct xfs_trans *tp, void xfs_refcount_decrease_extent(struct xfs_trans *tp,
...@@ -79,7 +110,8 @@ extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp, ...@@ -79,7 +110,8 @@ extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
#define XFS_REFCOUNT_ITEM_OVERHEAD 32 #define XFS_REFCOUNT_ITEM_OVERHEAD 32
extern int xfs_refcount_has_record(struct xfs_btree_cur *cur, extern int xfs_refcount_has_record(struct xfs_btree_cur *cur,
xfs_agblock_t bno, xfs_extlen_t len, bool *exists); enum xfs_refc_domain domain, xfs_agblock_t bno,
xfs_extlen_t len, bool *exists);
union xfs_btree_rec; union xfs_btree_rec;
extern void xfs_refcount_btrec_to_irec(const union xfs_btree_rec *rec, extern void xfs_refcount_btrec_to_irec(const union xfs_btree_rec *rec,
struct xfs_refcount_irec *irec); struct xfs_refcount_irec *irec);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "xfs_btree.h" #include "xfs_btree.h"
#include "xfs_btree_staging.h" #include "xfs_btree_staging.h"
#include "xfs_refcount_btree.h" #include "xfs_refcount_btree.h"
#include "xfs_refcount.h"
#include "xfs_alloc.h" #include "xfs_alloc.h"
#include "xfs_error.h" #include "xfs_error.h"
#include "xfs_trace.h" #include "xfs_trace.h"
...@@ -160,7 +161,12 @@ xfs_refcountbt_init_rec_from_cur( ...@@ -160,7 +161,12 @@ xfs_refcountbt_init_rec_from_cur(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
union xfs_btree_rec *rec) union xfs_btree_rec *rec)
{ {
rec->refc.rc_startblock = cpu_to_be32(cur->bc_rec.rc.rc_startblock); const struct xfs_refcount_irec *irec = &cur->bc_rec.rc;
uint32_t start;
start = xfs_refcount_encode_startblock(irec->rc_startblock,
irec->rc_domain);
rec->refc.rc_startblock = cpu_to_be32(start);
rec->refc.rc_blockcount = cpu_to_be32(cur->bc_rec.rc.rc_blockcount); rec->refc.rc_blockcount = cpu_to_be32(cur->bc_rec.rc.rc_blockcount);
rec->refc.rc_refcount = cpu_to_be32(cur->bc_rec.rc.rc_refcount); rec->refc.rc_refcount = cpu_to_be32(cur->bc_rec.rc.rc_refcount);
} }
...@@ -182,10 +188,13 @@ xfs_refcountbt_key_diff( ...@@ -182,10 +188,13 @@ xfs_refcountbt_key_diff(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
const union xfs_btree_key *key) const union xfs_btree_key *key)
{ {
struct xfs_refcount_irec *rec = &cur->bc_rec.rc;
const struct xfs_refcount_key *kp = &key->refc; const struct xfs_refcount_key *kp = &key->refc;
const struct xfs_refcount_irec *irec = &cur->bc_rec.rc;
uint32_t start;
return (int64_t)be32_to_cpu(kp->rc_startblock) - rec->rc_startblock; start = xfs_refcount_encode_startblock(irec->rc_startblock,
irec->rc_domain);
return (int64_t)be32_to_cpu(kp->rc_startblock) - start;
} }
STATIC int64_t STATIC int64_t
......
...@@ -235,13 +235,8 @@ xfs_rmap_get_rec( ...@@ -235,13 +235,8 @@ xfs_rmap_get_rec(
goto out_bad_rec; goto out_bad_rec;
} else { } else {
/* check for valid extent range, including overflow */ /* check for valid extent range, including overflow */
if (!xfs_verify_agbno(pag, irec->rm_startblock)) if (!xfs_verify_agbext(pag, irec->rm_startblock,
goto out_bad_rec; irec->rm_blockcount))
if (irec->rm_startblock >
irec->rm_startblock + irec->rm_blockcount)
goto out_bad_rec;
if (!xfs_verify_agbno(pag,
irec->rm_startblock + irec->rm_blockcount - 1))
goto out_bad_rec; goto out_bad_rec;
} }
......
...@@ -166,6 +166,36 @@ typedef struct xfs_bmbt_irec ...@@ -166,6 +166,36 @@ typedef struct xfs_bmbt_irec
xfs_exntst_t br_state; /* extent state */ xfs_exntst_t br_state; /* extent state */
} xfs_bmbt_irec_t; } xfs_bmbt_irec_t;
enum xfs_refc_domain {
XFS_REFC_DOMAIN_SHARED = 0,
XFS_REFC_DOMAIN_COW,
};
#define XFS_REFC_DOMAIN_STRINGS \
{ XFS_REFC_DOMAIN_SHARED, "shared" }, \
{ XFS_REFC_DOMAIN_COW, "cow" }
struct xfs_refcount_irec {
xfs_agblock_t rc_startblock; /* starting block number */
xfs_extlen_t rc_blockcount; /* count of free blocks */
xfs_nlink_t rc_refcount; /* number of inodes linked here */
enum xfs_refc_domain rc_domain; /* shared or cow staging extent? */
};
#define XFS_RMAP_ATTR_FORK (1 << 0)
#define XFS_RMAP_BMBT_BLOCK (1 << 1)
#define XFS_RMAP_UNWRITTEN (1 << 2)
#define XFS_RMAP_KEY_FLAGS (XFS_RMAP_ATTR_FORK | \
XFS_RMAP_BMBT_BLOCK)
#define XFS_RMAP_REC_FLAGS (XFS_RMAP_UNWRITTEN)
struct xfs_rmap_irec {
xfs_agblock_t rm_startblock; /* extent start block */
xfs_extlen_t rm_blockcount; /* extent length */
uint64_t rm_owner; /* extent owner */
uint64_t rm_offset; /* offset within the owner */
unsigned int rm_flags; /* state flags */
};
/* per-AG block reservation types */ /* per-AG block reservation types */
enum xfs_ag_resv_type { enum xfs_ag_resv_type {
XFS_AG_RESV_NONE = 0, XFS_AG_RESV_NONE = 0,
......
...@@ -100,9 +100,7 @@ xchk_allocbt_rec( ...@@ -100,9 +100,7 @@ xchk_allocbt_rec(
bno = be32_to_cpu(rec->alloc.ar_startblock); bno = be32_to_cpu(rec->alloc.ar_startblock);
len = be32_to_cpu(rec->alloc.ar_blockcount); len = be32_to_cpu(rec->alloc.ar_blockcount);
if (bno + len <= bno || if (!xfs_verify_agbext(pag, bno, len))
!xfs_verify_agbno(pag, bno) ||
!xfs_verify_agbno(pag, bno + len - 1))
xchk_btree_set_corrupt(bs->sc, bs->cur, 0); xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
xchk_allocbt_xref(bs->sc, bno, len); xchk_allocbt_xref(bs->sc, bno, len);
......
...@@ -108,9 +108,8 @@ xchk_iallocbt_chunk( ...@@ -108,9 +108,8 @@ xchk_iallocbt_chunk(
xfs_agblock_t bno; xfs_agblock_t bno;
bno = XFS_AGINO_TO_AGBNO(mp, agino); bno = XFS_AGINO_TO_AGBNO(mp, agino);
if (bno + len <= bno ||
!xfs_verify_agbno(pag, bno) || if (!xfs_verify_agbext(pag, bno, len))
!xfs_verify_agbno(pag, bno + len - 1))
xchk_btree_set_corrupt(bs->sc, bs->cur, 0); xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
xchk_iallocbt_chunk_xref(bs->sc, irec, agino, bno, len); xchk_iallocbt_chunk_xref(bs->sc, irec, agino, bno, len);
......
...@@ -269,15 +269,13 @@ xchk_refcountbt_process_rmap_fragments( ...@@ -269,15 +269,13 @@ xchk_refcountbt_process_rmap_fragments(
STATIC void STATIC void
xchk_refcountbt_xref_rmap( xchk_refcountbt_xref_rmap(
struct xfs_scrub *sc, struct xfs_scrub *sc,
xfs_agblock_t bno, const struct xfs_refcount_irec *irec)
xfs_extlen_t len,
xfs_nlink_t refcount)
{ {
struct xchk_refcnt_check refchk = { struct xchk_refcnt_check refchk = {
.sc = sc, .sc = sc,
.bno = bno, .bno = irec->rc_startblock,
.len = len, .len = irec->rc_blockcount,
.refcount = refcount, .refcount = irec->rc_refcount,
.seen = 0, .seen = 0,
}; };
struct xfs_rmap_irec low; struct xfs_rmap_irec low;
...@@ -291,9 +289,9 @@ xchk_refcountbt_xref_rmap( ...@@ -291,9 +289,9 @@ xchk_refcountbt_xref_rmap(
/* Cross-reference with the rmapbt to confirm the refcount. */ /* Cross-reference with the rmapbt to confirm the refcount. */
memset(&low, 0, sizeof(low)); memset(&low, 0, sizeof(low));
low.rm_startblock = bno; low.rm_startblock = irec->rc_startblock;
memset(&high, 0xFF, sizeof(high)); memset(&high, 0xFF, sizeof(high));
high.rm_startblock = bno + len - 1; high.rm_startblock = irec->rc_startblock + irec->rc_blockcount - 1;
INIT_LIST_HEAD(&refchk.fragments); INIT_LIST_HEAD(&refchk.fragments);
error = xfs_rmap_query_range(sc->sa.rmap_cur, &low, &high, error = xfs_rmap_query_range(sc->sa.rmap_cur, &low, &high,
...@@ -302,7 +300,7 @@ xchk_refcountbt_xref_rmap( ...@@ -302,7 +300,7 @@ xchk_refcountbt_xref_rmap(
goto out_free; goto out_free;
xchk_refcountbt_process_rmap_fragments(&refchk); xchk_refcountbt_process_rmap_fragments(&refchk);
if (refcount != refchk.seen) if (irec->rc_refcount != refchk.seen)
xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0); xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
out_free: out_free:
...@@ -315,17 +313,16 @@ xchk_refcountbt_xref_rmap( ...@@ -315,17 +313,16 @@ xchk_refcountbt_xref_rmap(
/* Cross-reference with the other btrees. */ /* Cross-reference with the other btrees. */
STATIC void STATIC void
xchk_refcountbt_xref( xchk_refcountbt_xref(
struct xfs_scrub *sc, struct xfs_scrub *sc,
xfs_agblock_t agbno, const struct xfs_refcount_irec *irec)
xfs_extlen_t len,
xfs_nlink_t refcount)
{ {
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
return; return;
xchk_xref_is_used_space(sc, agbno, len); xchk_xref_is_used_space(sc, irec->rc_startblock, irec->rc_blockcount);
xchk_xref_is_not_inode_chunk(sc, agbno, len); xchk_xref_is_not_inode_chunk(sc, irec->rc_startblock,
xchk_refcountbt_xref_rmap(sc, agbno, len, refcount); irec->rc_blockcount);
xchk_refcountbt_xref_rmap(sc, irec);
} }
/* Scrub a refcountbt record. */ /* Scrub a refcountbt record. */
...@@ -334,35 +331,27 @@ xchk_refcountbt_rec( ...@@ -334,35 +331,27 @@ xchk_refcountbt_rec(
struct xchk_btree *bs, struct xchk_btree *bs,
const union xfs_btree_rec *rec) const union xfs_btree_rec *rec)
{ {
struct xfs_refcount_irec irec;
xfs_agblock_t *cow_blocks = bs->private; xfs_agblock_t *cow_blocks = bs->private;
struct xfs_perag *pag = bs->cur->bc_ag.pag; struct xfs_perag *pag = bs->cur->bc_ag.pag;
xfs_agblock_t bno;
xfs_extlen_t len;
xfs_nlink_t refcount;
bool has_cowflag;
bno = be32_to_cpu(rec->refc.rc_startblock); xfs_refcount_btrec_to_irec(rec, &irec);
len = be32_to_cpu(rec->refc.rc_blockcount);
refcount = be32_to_cpu(rec->refc.rc_refcount);
/* Only CoW records can have refcount == 1. */ /* Check the domain and refcount are not incompatible. */
has_cowflag = (bno & XFS_REFC_COW_START); if (!xfs_refcount_check_domain(&irec))
if ((refcount == 1 && !has_cowflag) || (refcount != 1 && has_cowflag))
xchk_btree_set_corrupt(bs->sc, bs->cur, 0); xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
if (has_cowflag)
(*cow_blocks) += len; if (irec.rc_domain == XFS_REFC_DOMAIN_COW)
(*cow_blocks) += irec.rc_blockcount;
/* Check the extent. */ /* Check the extent. */
bno &= ~XFS_REFC_COW_START; if (!xfs_verify_agbext(pag, irec.rc_startblock, irec.rc_blockcount))
if (bno + len <= bno ||
!xfs_verify_agbno(pag, bno) ||
!xfs_verify_agbno(pag, bno + len - 1))
xchk_btree_set_corrupt(bs->sc, bs->cur, 0); xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
if (refcount == 0) if (irec.rc_refcount == 0)
xchk_btree_set_corrupt(bs->sc, bs->cur, 0); xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
xchk_refcountbt_xref(bs->sc, bno, len, refcount); xchk_refcountbt_xref(bs->sc, &irec);
return 0; return 0;
} }
...@@ -426,7 +415,6 @@ xchk_xref_is_cow_staging( ...@@ -426,7 +415,6 @@ xchk_xref_is_cow_staging(
xfs_extlen_t len) xfs_extlen_t len)
{ {
struct xfs_refcount_irec rc; struct xfs_refcount_irec rc;
bool has_cowflag;
int has_refcount; int has_refcount;
int error; int error;
...@@ -434,8 +422,8 @@ xchk_xref_is_cow_staging( ...@@ -434,8 +422,8 @@ xchk_xref_is_cow_staging(
return; return;
/* Find the CoW staging extent. */ /* Find the CoW staging extent. */
error = xfs_refcount_lookup_le(sc->sa.refc_cur, error = xfs_refcount_lookup_le(sc->sa.refc_cur, XFS_REFC_DOMAIN_COW,
agbno + XFS_REFC_COW_START, &has_refcount); agbno, &has_refcount);
if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur)) if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur))
return; return;
if (!has_refcount) { if (!has_refcount) {
...@@ -451,9 +439,8 @@ xchk_xref_is_cow_staging( ...@@ -451,9 +439,8 @@ xchk_xref_is_cow_staging(
return; return;
} }
/* CoW flag must be set, refcount must be 1. */ /* CoW lookup returned a shared extent record? */
has_cowflag = (rc.rc_startblock & XFS_REFC_COW_START); if (rc.rc_domain != XFS_REFC_DOMAIN_COW)
if (!has_cowflag || rc.rc_refcount != 1)
xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0); xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
/* Must be at least as long as what was passed in */ /* Must be at least as long as what was passed in */
...@@ -477,7 +464,8 @@ xchk_xref_is_not_shared( ...@@ -477,7 +464,8 @@ xchk_xref_is_not_shared(
if (!sc->sa.refc_cur || xchk_skip_xref(sc->sm)) if (!sc->sa.refc_cur || xchk_skip_xref(sc->sm))
return; return;
error = xfs_refcount_has_record(sc->sa.refc_cur, agbno, len, &shared); error = xfs_refcount_has_record(sc->sa.refc_cur, XFS_REFC_DOMAIN_SHARED,
agbno, len, &shared);
if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur)) if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur))
return; return;
if (shared) if (shared)
......
...@@ -799,6 +799,9 @@ TRACE_DEFINE_ENUM(PE_SIZE_PTE); ...@@ -799,6 +799,9 @@ TRACE_DEFINE_ENUM(PE_SIZE_PTE);
TRACE_DEFINE_ENUM(PE_SIZE_PMD); TRACE_DEFINE_ENUM(PE_SIZE_PMD);
TRACE_DEFINE_ENUM(PE_SIZE_PUD); TRACE_DEFINE_ENUM(PE_SIZE_PUD);
TRACE_DEFINE_ENUM(XFS_REFC_DOMAIN_SHARED);
TRACE_DEFINE_ENUM(XFS_REFC_DOMAIN_COW);
TRACE_EVENT(xfs_filemap_fault, TRACE_EVENT(xfs_filemap_fault,
TP_PROTO(struct xfs_inode *ip, enum page_entry_size pe_size, TP_PROTO(struct xfs_inode *ip, enum page_entry_size pe_size,
bool write_fault), bool write_fault),
...@@ -2925,6 +2928,7 @@ DECLARE_EVENT_CLASS(xfs_refcount_extent_class, ...@@ -2925,6 +2928,7 @@ DECLARE_EVENT_CLASS(xfs_refcount_extent_class,
TP_STRUCT__entry( TP_STRUCT__entry(
__field(dev_t, dev) __field(dev_t, dev)
__field(xfs_agnumber_t, agno) __field(xfs_agnumber_t, agno)
__field(enum xfs_refc_domain, domain)
__field(xfs_agblock_t, startblock) __field(xfs_agblock_t, startblock)
__field(xfs_extlen_t, blockcount) __field(xfs_extlen_t, blockcount)
__field(xfs_nlink_t, refcount) __field(xfs_nlink_t, refcount)
...@@ -2932,13 +2936,15 @@ DECLARE_EVENT_CLASS(xfs_refcount_extent_class, ...@@ -2932,13 +2936,15 @@ DECLARE_EVENT_CLASS(xfs_refcount_extent_class,
TP_fast_assign( TP_fast_assign(
__entry->dev = mp->m_super->s_dev; __entry->dev = mp->m_super->s_dev;
__entry->agno = agno; __entry->agno = agno;
__entry->domain = irec->rc_domain;
__entry->startblock = irec->rc_startblock; __entry->startblock = irec->rc_startblock;
__entry->blockcount = irec->rc_blockcount; __entry->blockcount = irec->rc_blockcount;
__entry->refcount = irec->rc_refcount; __entry->refcount = irec->rc_refcount;
), ),
TP_printk("dev %d:%d agno 0x%x agbno 0x%x fsbcount 0x%x refcount %u", TP_printk("dev %d:%d agno 0x%x dom %s agbno 0x%x fsbcount 0x%x refcount %u",
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno, __entry->agno,
__print_symbolic(__entry->domain, XFS_REFC_DOMAIN_STRINGS),
__entry->startblock, __entry->startblock,
__entry->blockcount, __entry->blockcount,
__entry->refcount) __entry->refcount)
...@@ -2958,6 +2964,7 @@ DECLARE_EVENT_CLASS(xfs_refcount_extent_at_class, ...@@ -2958,6 +2964,7 @@ DECLARE_EVENT_CLASS(xfs_refcount_extent_at_class,
TP_STRUCT__entry( TP_STRUCT__entry(
__field(dev_t, dev) __field(dev_t, dev)
__field(xfs_agnumber_t, agno) __field(xfs_agnumber_t, agno)
__field(enum xfs_refc_domain, domain)
__field(xfs_agblock_t, startblock) __field(xfs_agblock_t, startblock)
__field(xfs_extlen_t, blockcount) __field(xfs_extlen_t, blockcount)
__field(xfs_nlink_t, refcount) __field(xfs_nlink_t, refcount)
...@@ -2966,14 +2973,16 @@ DECLARE_EVENT_CLASS(xfs_refcount_extent_at_class, ...@@ -2966,14 +2973,16 @@ DECLARE_EVENT_CLASS(xfs_refcount_extent_at_class,
TP_fast_assign( TP_fast_assign(
__entry->dev = mp->m_super->s_dev; __entry->dev = mp->m_super->s_dev;
__entry->agno = agno; __entry->agno = agno;
__entry->domain = irec->rc_domain;
__entry->startblock = irec->rc_startblock; __entry->startblock = irec->rc_startblock;
__entry->blockcount = irec->rc_blockcount; __entry->blockcount = irec->rc_blockcount;
__entry->refcount = irec->rc_refcount; __entry->refcount = irec->rc_refcount;
__entry->agbno = agbno; __entry->agbno = agbno;
), ),
TP_printk("dev %d:%d agno 0x%x agbno 0x%x fsbcount 0x%x refcount %u @ agbno 0x%x", TP_printk("dev %d:%d agno 0x%x dom %s agbno 0x%x fsbcount 0x%x refcount %u @ agbno 0x%x",
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno, __entry->agno,
__print_symbolic(__entry->domain, XFS_REFC_DOMAIN_STRINGS),
__entry->startblock, __entry->startblock,
__entry->blockcount, __entry->blockcount,
__entry->refcount, __entry->refcount,
...@@ -2994,9 +3003,11 @@ DECLARE_EVENT_CLASS(xfs_refcount_double_extent_class, ...@@ -2994,9 +3003,11 @@ DECLARE_EVENT_CLASS(xfs_refcount_double_extent_class,
TP_STRUCT__entry( TP_STRUCT__entry(
__field(dev_t, dev) __field(dev_t, dev)
__field(xfs_agnumber_t, agno) __field(xfs_agnumber_t, agno)
__field(enum xfs_refc_domain, i1_domain)
__field(xfs_agblock_t, i1_startblock) __field(xfs_agblock_t, i1_startblock)
__field(xfs_extlen_t, i1_blockcount) __field(xfs_extlen_t, i1_blockcount)
__field(xfs_nlink_t, i1_refcount) __field(xfs_nlink_t, i1_refcount)
__field(enum xfs_refc_domain, i2_domain)
__field(xfs_agblock_t, i2_startblock) __field(xfs_agblock_t, i2_startblock)
__field(xfs_extlen_t, i2_blockcount) __field(xfs_extlen_t, i2_blockcount)
__field(xfs_nlink_t, i2_refcount) __field(xfs_nlink_t, i2_refcount)
...@@ -3004,20 +3015,24 @@ DECLARE_EVENT_CLASS(xfs_refcount_double_extent_class, ...@@ -3004,20 +3015,24 @@ DECLARE_EVENT_CLASS(xfs_refcount_double_extent_class,
TP_fast_assign( TP_fast_assign(
__entry->dev = mp->m_super->s_dev; __entry->dev = mp->m_super->s_dev;
__entry->agno = agno; __entry->agno = agno;
__entry->i1_domain = i1->rc_domain;
__entry->i1_startblock = i1->rc_startblock; __entry->i1_startblock = i1->rc_startblock;
__entry->i1_blockcount = i1->rc_blockcount; __entry->i1_blockcount = i1->rc_blockcount;
__entry->i1_refcount = i1->rc_refcount; __entry->i1_refcount = i1->rc_refcount;
__entry->i2_domain = i2->rc_domain;
__entry->i2_startblock = i2->rc_startblock; __entry->i2_startblock = i2->rc_startblock;
__entry->i2_blockcount = i2->rc_blockcount; __entry->i2_blockcount = i2->rc_blockcount;
__entry->i2_refcount = i2->rc_refcount; __entry->i2_refcount = i2->rc_refcount;
), ),
TP_printk("dev %d:%d agno 0x%x agbno 0x%x fsbcount 0x%x refcount %u -- " TP_printk("dev %d:%d agno 0x%x dom %s agbno 0x%x fsbcount 0x%x refcount %u -- "
"agbno 0x%x fsbcount 0x%x refcount %u", "dom %s agbno 0x%x fsbcount 0x%x refcount %u",
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno, __entry->agno,
__print_symbolic(__entry->i1_domain, XFS_REFC_DOMAIN_STRINGS),
__entry->i1_startblock, __entry->i1_startblock,
__entry->i1_blockcount, __entry->i1_blockcount,
__entry->i1_refcount, __entry->i1_refcount,
__print_symbolic(__entry->i2_domain, XFS_REFC_DOMAIN_STRINGS),
__entry->i2_startblock, __entry->i2_startblock,
__entry->i2_blockcount, __entry->i2_blockcount,
__entry->i2_refcount) __entry->i2_refcount)
...@@ -3038,9 +3053,11 @@ DECLARE_EVENT_CLASS(xfs_refcount_double_extent_at_class, ...@@ -3038,9 +3053,11 @@ DECLARE_EVENT_CLASS(xfs_refcount_double_extent_at_class,
TP_STRUCT__entry( TP_STRUCT__entry(
__field(dev_t, dev) __field(dev_t, dev)
__field(xfs_agnumber_t, agno) __field(xfs_agnumber_t, agno)
__field(enum xfs_refc_domain, i1_domain)
__field(xfs_agblock_t, i1_startblock) __field(xfs_agblock_t, i1_startblock)
__field(xfs_extlen_t, i1_blockcount) __field(xfs_extlen_t, i1_blockcount)
__field(xfs_nlink_t, i1_refcount) __field(xfs_nlink_t, i1_refcount)
__field(enum xfs_refc_domain, i2_domain)
__field(xfs_agblock_t, i2_startblock) __field(xfs_agblock_t, i2_startblock)
__field(xfs_extlen_t, i2_blockcount) __field(xfs_extlen_t, i2_blockcount)
__field(xfs_nlink_t, i2_refcount) __field(xfs_nlink_t, i2_refcount)
...@@ -3049,21 +3066,25 @@ DECLARE_EVENT_CLASS(xfs_refcount_double_extent_at_class, ...@@ -3049,21 +3066,25 @@ DECLARE_EVENT_CLASS(xfs_refcount_double_extent_at_class,
TP_fast_assign( TP_fast_assign(
__entry->dev = mp->m_super->s_dev; __entry->dev = mp->m_super->s_dev;
__entry->agno = agno; __entry->agno = agno;
__entry->i1_domain = i1->rc_domain;
__entry->i1_startblock = i1->rc_startblock; __entry->i1_startblock = i1->rc_startblock;
__entry->i1_blockcount = i1->rc_blockcount; __entry->i1_blockcount = i1->rc_blockcount;
__entry->i1_refcount = i1->rc_refcount; __entry->i1_refcount = i1->rc_refcount;
__entry->i2_domain = i2->rc_domain;
__entry->i2_startblock = i2->rc_startblock; __entry->i2_startblock = i2->rc_startblock;
__entry->i2_blockcount = i2->rc_blockcount; __entry->i2_blockcount = i2->rc_blockcount;
__entry->i2_refcount = i2->rc_refcount; __entry->i2_refcount = i2->rc_refcount;
__entry->agbno = agbno; __entry->agbno = agbno;
), ),
TP_printk("dev %d:%d agno 0x%x agbno 0x%x fsbcount 0x%x refcount %u -- " TP_printk("dev %d:%d agno 0x%x dom %s agbno 0x%x fsbcount 0x%x refcount %u -- "
"agbno 0x%x fsbcount 0x%x refcount %u @ agbno 0x%x", "dom %s agbno 0x%x fsbcount 0x%x refcount %u @ agbno 0x%x",
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno, __entry->agno,
__print_symbolic(__entry->i1_domain, XFS_REFC_DOMAIN_STRINGS),
__entry->i1_startblock, __entry->i1_startblock,
__entry->i1_blockcount, __entry->i1_blockcount,
__entry->i1_refcount, __entry->i1_refcount,
__print_symbolic(__entry->i2_domain, XFS_REFC_DOMAIN_STRINGS),
__entry->i2_startblock, __entry->i2_startblock,
__entry->i2_blockcount, __entry->i2_blockcount,
__entry->i2_refcount, __entry->i2_refcount,
...@@ -3086,12 +3107,15 @@ DECLARE_EVENT_CLASS(xfs_refcount_triple_extent_class, ...@@ -3086,12 +3107,15 @@ DECLARE_EVENT_CLASS(xfs_refcount_triple_extent_class,
TP_STRUCT__entry( TP_STRUCT__entry(
__field(dev_t, dev) __field(dev_t, dev)
__field(xfs_agnumber_t, agno) __field(xfs_agnumber_t, agno)
__field(enum xfs_refc_domain, i1_domain)
__field(xfs_agblock_t, i1_startblock) __field(xfs_agblock_t, i1_startblock)
__field(xfs_extlen_t, i1_blockcount) __field(xfs_extlen_t, i1_blockcount)
__field(xfs_nlink_t, i1_refcount) __field(xfs_nlink_t, i1_refcount)
__field(enum xfs_refc_domain, i2_domain)
__field(xfs_agblock_t, i2_startblock) __field(xfs_agblock_t, i2_startblock)
__field(xfs_extlen_t, i2_blockcount) __field(xfs_extlen_t, i2_blockcount)
__field(xfs_nlink_t, i2_refcount) __field(xfs_nlink_t, i2_refcount)
__field(enum xfs_refc_domain, i3_domain)
__field(xfs_agblock_t, i3_startblock) __field(xfs_agblock_t, i3_startblock)
__field(xfs_extlen_t, i3_blockcount) __field(xfs_extlen_t, i3_blockcount)
__field(xfs_nlink_t, i3_refcount) __field(xfs_nlink_t, i3_refcount)
...@@ -3099,27 +3123,33 @@ DECLARE_EVENT_CLASS(xfs_refcount_triple_extent_class, ...@@ -3099,27 +3123,33 @@ DECLARE_EVENT_CLASS(xfs_refcount_triple_extent_class,
TP_fast_assign( TP_fast_assign(
__entry->dev = mp->m_super->s_dev; __entry->dev = mp->m_super->s_dev;
__entry->agno = agno; __entry->agno = agno;
__entry->i1_domain = i1->rc_domain;
__entry->i1_startblock = i1->rc_startblock; __entry->i1_startblock = i1->rc_startblock;
__entry->i1_blockcount = i1->rc_blockcount; __entry->i1_blockcount = i1->rc_blockcount;
__entry->i1_refcount = i1->rc_refcount; __entry->i1_refcount = i1->rc_refcount;
__entry->i2_domain = i2->rc_domain;
__entry->i2_startblock = i2->rc_startblock; __entry->i2_startblock = i2->rc_startblock;
__entry->i2_blockcount = i2->rc_blockcount; __entry->i2_blockcount = i2->rc_blockcount;
__entry->i2_refcount = i2->rc_refcount; __entry->i2_refcount = i2->rc_refcount;
__entry->i3_domain = i3->rc_domain;
__entry->i3_startblock = i3->rc_startblock; __entry->i3_startblock = i3->rc_startblock;
__entry->i3_blockcount = i3->rc_blockcount; __entry->i3_blockcount = i3->rc_blockcount;
__entry->i3_refcount = i3->rc_refcount; __entry->i3_refcount = i3->rc_refcount;
), ),
TP_printk("dev %d:%d agno 0x%x agbno 0x%x fsbcount 0x%x refcount %u -- " TP_printk("dev %d:%d agno 0x%x dom %s agbno 0x%x fsbcount 0x%x refcount %u -- "
"agbno 0x%x fsbcount 0x%x refcount %u -- " "dom %s agbno 0x%x fsbcount 0x%x refcount %u -- "
"agbno 0x%x fsbcount 0x%x refcount %u", "dom %s agbno 0x%x fsbcount 0x%x refcount %u",
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno, __entry->agno,
__print_symbolic(__entry->i1_domain, XFS_REFC_DOMAIN_STRINGS),
__entry->i1_startblock, __entry->i1_startblock,
__entry->i1_blockcount, __entry->i1_blockcount,
__entry->i1_refcount, __entry->i1_refcount,
__print_symbolic(__entry->i2_domain, XFS_REFC_DOMAIN_STRINGS),
__entry->i2_startblock, __entry->i2_startblock,
__entry->i2_blockcount, __entry->i2_blockcount,
__entry->i2_refcount, __entry->i2_refcount,
__print_symbolic(__entry->i3_domain, XFS_REFC_DOMAIN_STRINGS),
__entry->i3_startblock, __entry->i3_startblock,
__entry->i3_blockcount, __entry->i3_blockcount,
__entry->i3_refcount) __entry->i3_refcount)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment