Commit 64c3dd0b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'xfs-6.1-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Darrick Wong:
 "Dave and I had thought that this would be a very quiet cycle, but we
  thought wrong.

  At first there were the usual trickle of minor bugfixes, but then
  Zorro pulled -rc1 and noticed complaints about the stronger memcpy
  checks w.r.t. flex arrays.

  Analyzing how to fix that revealed a bunch of validation gaps in
  validating ondisk log items during recovery, and then a customer hit
  an infinite loop in the refcounting code on a corrupt filesystem.

  So. This largeish batch of fixes addresses all those problems, I hope.

  Summary:

   - Fix a UAF bug during log recovery

   - Fix memory leaks when mount fails

   - Detect corrupt bestfree information in a directory block

   - Fix incorrect return value type for the dax page fault handlers

   - Fix fortify complaints about memcpy of xfs log item objects

   - Strengthen inadequate validation of recovered log items

   - Fix incorrectly declared flex array in EFI log item structs

   - Log corrupt log items for debugging purposes

   - Fix infinite loop problems in the refcount code if the refcount
     btree node block keys are corrupt

   - Fix infinite loop problems in the refcount code if the refcount
     btree records suffer MSB bitflips

   - Add more sanity checking to continued defer ops to prevent
     overflows from one AG to the next or off EOFS"

* tag 'xfs-6.1-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: (28 commits)
  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: dump corrupt recovered log intent items to dmesg consistently
  xfs: move _irec structs to xfs_types.h
  xfs: actually abort log recovery on corrupt intent-done log items
  xfs: check deferred refcount op continuation parameters
  xfs: refactor all the EFI/EFD log item sizeof logic
  xfs: create a predicate to verify per-AG extents
  xfs: fix memcpy fortify errors in EFI log format copying
  xfs: make sure aglen never goes negative in xfs_refcount_adjust_extents
  xfs: fix memcpy fortify errors in RUI log format copying
  xfs: fix memcpy fortify errors in CUI log format copying
  xfs: fix memcpy fortify errors in BUI log format copying
  ...
parents 5d8401be 4eb559dd
...@@ -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;
......
...@@ -146,6 +146,8 @@ xfs_dir3_leaf_check_int( ...@@ -146,6 +146,8 @@ xfs_dir3_leaf_check_int(
xfs_dir2_leaf_tail_t *ltp; xfs_dir2_leaf_tail_t *ltp;
int stale; int stale;
int i; int i;
bool isleaf1 = (hdr->magic == XFS_DIR2_LEAF1_MAGIC ||
hdr->magic == XFS_DIR3_LEAF1_MAGIC);
ltp = xfs_dir2_leaf_tail_p(geo, leaf); ltp = xfs_dir2_leaf_tail_p(geo, leaf);
...@@ -158,8 +160,7 @@ xfs_dir3_leaf_check_int( ...@@ -158,8 +160,7 @@ xfs_dir3_leaf_check_int(
return __this_address; return __this_address;
/* Leaves and bests don't overlap in leaf format. */ /* Leaves and bests don't overlap in leaf format. */
if ((hdr->magic == XFS_DIR2_LEAF1_MAGIC || if (isleaf1 &&
hdr->magic == XFS_DIR3_LEAF1_MAGIC) &&
(char *)&hdr->ents[hdr->count] > (char *)xfs_dir2_leaf_bests_p(ltp)) (char *)&hdr->ents[hdr->count] > (char *)xfs_dir2_leaf_bests_p(ltp))
return __this_address; return __this_address;
...@@ -175,6 +176,10 @@ xfs_dir3_leaf_check_int( ...@@ -175,6 +176,10 @@ xfs_dir3_leaf_check_int(
} }
if (hdr->ents[i].address == cpu_to_be32(XFS_DIR2_NULL_DATAPTR)) if (hdr->ents[i].address == cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
stale++; stale++;
if (isleaf1 && xfs_dir2_dataptr_to_db(geo,
be32_to_cpu(hdr->ents[i].address)) >=
be32_to_cpu(ltp->bestcount))
return __this_address;
} }
if (hdr->stale != stale) if (hdr->stale != stale)
return __this_address; return __this_address;
......
...@@ -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)
......
...@@ -613,25 +613,49 @@ typedef struct xfs_efi_log_format { ...@@ -613,25 +613,49 @@ typedef struct xfs_efi_log_format {
uint16_t efi_size; /* size of this item */ uint16_t efi_size; /* size of this item */
uint32_t efi_nextents; /* # extents to free */ uint32_t efi_nextents; /* # extents to free */
uint64_t efi_id; /* efi identifier */ uint64_t efi_id; /* efi identifier */
xfs_extent_t efi_extents[1]; /* array of extents to free */ xfs_extent_t efi_extents[]; /* array of extents to free */
} xfs_efi_log_format_t; } xfs_efi_log_format_t;
static inline size_t
xfs_efi_log_format_sizeof(
unsigned int nr)
{
return sizeof(struct xfs_efi_log_format) +
nr * sizeof(struct xfs_extent);
}
typedef struct xfs_efi_log_format_32 { typedef struct xfs_efi_log_format_32 {
uint16_t efi_type; /* efi log item type */ uint16_t efi_type; /* efi log item type */
uint16_t efi_size; /* size of this item */ uint16_t efi_size; /* size of this item */
uint32_t efi_nextents; /* # extents to free */ uint32_t efi_nextents; /* # extents to free */
uint64_t efi_id; /* efi identifier */ uint64_t efi_id; /* efi identifier */
xfs_extent_32_t efi_extents[1]; /* array of extents to free */ xfs_extent_32_t efi_extents[]; /* array of extents to free */
} __attribute__((packed)) xfs_efi_log_format_32_t; } __attribute__((packed)) xfs_efi_log_format_32_t;
static inline size_t
xfs_efi_log_format32_sizeof(
unsigned int nr)
{
return sizeof(struct xfs_efi_log_format_32) +
nr * sizeof(struct xfs_extent_32);
}
typedef struct xfs_efi_log_format_64 { typedef struct xfs_efi_log_format_64 {
uint16_t efi_type; /* efi log item type */ uint16_t efi_type; /* efi log item type */
uint16_t efi_size; /* size of this item */ uint16_t efi_size; /* size of this item */
uint32_t efi_nextents; /* # extents to free */ uint32_t efi_nextents; /* # extents to free */
uint64_t efi_id; /* efi identifier */ uint64_t efi_id; /* efi identifier */
xfs_extent_64_t efi_extents[1]; /* array of extents to free */ xfs_extent_64_t efi_extents[]; /* array of extents to free */
} xfs_efi_log_format_64_t; } xfs_efi_log_format_64_t;
static inline size_t
xfs_efi_log_format64_sizeof(
unsigned int nr)
{
return sizeof(struct xfs_efi_log_format_64) +
nr * sizeof(struct xfs_extent_64);
}
/* /*
* This is the structure used to lay out an efd log item in the * This is the structure used to lay out an efd log item in the
* log. The efd_extents array is a variable size array whose * log. The efd_extents array is a variable size array whose
...@@ -642,25 +666,49 @@ typedef struct xfs_efd_log_format { ...@@ -642,25 +666,49 @@ typedef struct xfs_efd_log_format {
uint16_t efd_size; /* size of this item */ uint16_t efd_size; /* size of this item */
uint32_t efd_nextents; /* # of extents freed */ uint32_t efd_nextents; /* # of extents freed */
uint64_t efd_efi_id; /* id of corresponding efi */ uint64_t efd_efi_id; /* id of corresponding efi */
xfs_extent_t efd_extents[1]; /* array of extents freed */ xfs_extent_t efd_extents[]; /* array of extents freed */
} xfs_efd_log_format_t; } xfs_efd_log_format_t;
static inline size_t
xfs_efd_log_format_sizeof(
unsigned int nr)
{
return sizeof(struct xfs_efd_log_format) +
nr * sizeof(struct xfs_extent);
}
typedef struct xfs_efd_log_format_32 { typedef struct xfs_efd_log_format_32 {
uint16_t efd_type; /* efd log item type */ uint16_t efd_type; /* efd log item type */
uint16_t efd_size; /* size of this item */ uint16_t efd_size; /* size of this item */
uint32_t efd_nextents; /* # of extents freed */ uint32_t efd_nextents; /* # of extents freed */
uint64_t efd_efi_id; /* id of corresponding efi */ uint64_t efd_efi_id; /* id of corresponding efi */
xfs_extent_32_t efd_extents[1]; /* array of extents freed */ xfs_extent_32_t efd_extents[]; /* array of extents freed */
} __attribute__((packed)) xfs_efd_log_format_32_t; } __attribute__((packed)) xfs_efd_log_format_32_t;
static inline size_t
xfs_efd_log_format32_sizeof(
unsigned int nr)
{
return sizeof(struct xfs_efd_log_format_32) +
nr * sizeof(struct xfs_extent_32);
}
typedef struct xfs_efd_log_format_64 { typedef struct xfs_efd_log_format_64 {
uint16_t efd_type; /* efd log item type */ uint16_t efd_type; /* efd log item type */
uint16_t efd_size; /* size of this item */ uint16_t efd_size; /* size of this item */
uint32_t efd_nextents; /* # of extents freed */ uint32_t efd_nextents; /* # of extents freed */
uint64_t efd_efi_id; /* id of corresponding efi */ uint64_t efd_efi_id; /* id of corresponding efi */
xfs_extent_64_t efd_extents[1]; /* array of extents freed */ xfs_extent_64_t efd_extents[]; /* array of extents freed */
} xfs_efd_log_format_64_t; } xfs_efd_log_format_64_t;
static inline size_t
xfs_efd_log_format64_sizeof(
unsigned int nr)
{
return sizeof(struct xfs_efd_log_format_64) +
nr * sizeof(struct xfs_extent_64);
}
/* /*
* RUI/RUD (reverse mapping) log format definitions * RUI/RUD (reverse mapping) log format definitions
*/ */
......
...@@ -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;
} }
......
...@@ -422,7 +422,7 @@ xfs_calc_itruncate_reservation_minlogsize( ...@@ -422,7 +422,7 @@ xfs_calc_itruncate_reservation_minlogsize(
/* /*
* In renaming a files we can modify: * In renaming a files we can modify:
* the four inodes involved: 4 * inode size * the five inodes involved: 5 * inode size
* the two directory btrees: 2 * (max depth + v2) * dir block size * the two directory btrees: 2 * (max depth + v2) * dir block size
* the two directory bmap btrees: 2 * max depth * block size * the two directory bmap btrees: 2 * max depth * block size
* And the bmap_finish transaction can free dir and bmap blocks (two sets * And the bmap_finish transaction can free dir and bmap blocks (two sets
...@@ -437,7 +437,7 @@ xfs_calc_rename_reservation( ...@@ -437,7 +437,7 @@ xfs_calc_rename_reservation(
struct xfs_mount *mp) struct xfs_mount *mp)
{ {
return XFS_DQUOT_LOGRES(mp) + return XFS_DQUOT_LOGRES(mp) +
max((xfs_calc_inode_res(mp, 4) + max((xfs_calc_inode_res(mp, 5) +
xfs_calc_buf_res(2 * XFS_DIROP_LOG_COUNT(mp), xfs_calc_buf_res(2 * XFS_DIROP_LOG_COUNT(mp),
XFS_FSB_TO_B(mp, 1))), XFS_FSB_TO_B(mp, 1))),
(xfs_calc_buf_res(7, mp->m_sb.sb_sectsize) + (xfs_calc_buf_res(7, mp->m_sb.sb_sectsize) +
......
...@@ -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)
......
...@@ -245,28 +245,6 @@ xfs_attri_init( ...@@ -245,28 +245,6 @@ xfs_attri_init(
return attrip; return attrip;
} }
/*
* Copy an attr format buffer from the given buf, and into the destination attr
* format structure.
*/
STATIC int
xfs_attri_copy_format(
struct xfs_log_iovec *buf,
struct xfs_attri_log_format *dst_attr_fmt)
{
struct xfs_attri_log_format *src_attr_fmt = buf->i_addr;
size_t len;
len = sizeof(struct xfs_attri_log_format);
if (buf->i_len != len) {
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL);
return -EFSCORRUPTED;
}
memcpy((char *)dst_attr_fmt, (char *)src_attr_fmt, len);
return 0;
}
static inline struct xfs_attrd_log_item *ATTRD_ITEM(struct xfs_log_item *lip) static inline struct xfs_attrd_log_item *ATTRD_ITEM(struct xfs_log_item *lip)
{ {
return container_of(lip, struct xfs_attrd_log_item, attrd_item); return container_of(lip, struct xfs_attrd_log_item, attrd_item);
...@@ -731,24 +709,50 @@ xlog_recover_attri_commit_pass2( ...@@ -731,24 +709,50 @@ xlog_recover_attri_commit_pass2(
struct xfs_attri_log_nameval *nv; struct xfs_attri_log_nameval *nv;
const void *attr_value = NULL; const void *attr_value = NULL;
const void *attr_name; const void *attr_name;
int error; size_t len;
attri_formatp = item->ri_buf[0].i_addr; attri_formatp = item->ri_buf[0].i_addr;
attr_name = item->ri_buf[1].i_addr; attr_name = item->ri_buf[1].i_addr;
/* Validate xfs_attri_log_format before the large memory allocation */ /* Validate xfs_attri_log_format before the large memory allocation */
len = sizeof(struct xfs_attri_log_format);
if (item->ri_buf[0].i_len != len) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED;
}
if (!xfs_attri_validate(mp, attri_formatp)) { if (!xfs_attri_validate(mp, attri_formatp)) {
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED;
}
/* Validate the attr name */
if (item->ri_buf[1].i_len !=
xlog_calc_iovec_len(attri_formatp->alfi_name_len)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
if (!xfs_attr_namecheck(attr_name, attri_formatp->alfi_name_len)) { if (!xfs_attr_namecheck(attr_name, attri_formatp->alfi_name_len)) {
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[1].i_addr, item->ri_buf[1].i_len);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
if (attri_formatp->alfi_value_len) /* Validate the attr value, if present */
if (attri_formatp->alfi_value_len != 0) {
if (item->ri_buf[2].i_len != xlog_calc_iovec_len(attri_formatp->alfi_value_len)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr,
item->ri_buf[0].i_len);
return -EFSCORRUPTED;
}
attr_value = item->ri_buf[2].i_addr; attr_value = item->ri_buf[2].i_addr;
}
/* /*
* Memory alloc failure will cause replay to abort. We attach the * Memory alloc failure will cause replay to abort. We attach the
...@@ -760,9 +764,7 @@ xlog_recover_attri_commit_pass2( ...@@ -760,9 +764,7 @@ xlog_recover_attri_commit_pass2(
attri_formatp->alfi_value_len); attri_formatp->alfi_value_len);
attrip = xfs_attri_init(mp, nv); attrip = xfs_attri_init(mp, nv);
error = xfs_attri_copy_format(&item->ri_buf[0], &attrip->attri_format); memcpy(&attrip->attri_format, attri_formatp, len);
if (error)
goto out;
/* /*
* The ATTRI has two references. One for the ATTRD and one for ATTRI to * The ATTRI has two references. One for the ATTRD and one for ATTRI to
...@@ -774,10 +776,6 @@ xlog_recover_attri_commit_pass2( ...@@ -774,10 +776,6 @@ xlog_recover_attri_commit_pass2(
xfs_attri_release(attrip); xfs_attri_release(attrip);
xfs_attri_log_nameval_put(nv); xfs_attri_log_nameval_put(nv);
return 0; return 0;
out:
xfs_attri_item_free(attrip);
xfs_attri_log_nameval_put(nv);
return error;
} }
/* /*
...@@ -842,7 +840,8 @@ xlog_recover_attrd_commit_pass2( ...@@ -842,7 +840,8 @@ xlog_recover_attrd_commit_pass2(
attrd_formatp = item->ri_buf[0].i_addr; attrd_formatp = item->ri_buf[0].i_addr;
if (item->ri_buf[0].i_len != sizeof(struct xfs_attrd_log_format)) { if (item->ri_buf[0].i_len != sizeof(struct xfs_attrd_log_format)) {
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, log->l_mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
......
...@@ -608,28 +608,18 @@ static const struct xfs_item_ops xfs_bui_item_ops = { ...@@ -608,28 +608,18 @@ static const struct xfs_item_ops xfs_bui_item_ops = {
.iop_relog = xfs_bui_item_relog, .iop_relog = xfs_bui_item_relog,
}; };
/* static inline void
* Copy an BUI format buffer from the given buf, and into the destination
* BUI format structure. The BUI/BUD items were designed not to need any
* special alignment handling.
*/
static int
xfs_bui_copy_format( xfs_bui_copy_format(
struct xfs_log_iovec *buf, struct xfs_bui_log_format *dst,
struct xfs_bui_log_format *dst_bui_fmt) const struct xfs_bui_log_format *src)
{ {
struct xfs_bui_log_format *src_bui_fmt; unsigned int i;
uint len;
src_bui_fmt = buf->i_addr; memcpy(dst, src, offsetof(struct xfs_bui_log_format, bui_extents));
len = xfs_bui_log_format_sizeof(src_bui_fmt->bui_nextents);
if (buf->i_len == len) { for (i = 0; i < src->bui_nextents; i++)
memcpy(dst_bui_fmt, src_bui_fmt, len); memcpy(&dst->bui_extents[i], &src->bui_extents[i],
return 0; sizeof(struct xfs_map_extent));
}
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL);
return -EFSCORRUPTED;
} }
/* /*
...@@ -646,23 +636,34 @@ xlog_recover_bui_commit_pass2( ...@@ -646,23 +636,34 @@ xlog_recover_bui_commit_pass2(
struct xlog_recover_item *item, struct xlog_recover_item *item,
xfs_lsn_t lsn) xfs_lsn_t lsn)
{ {
int error;
struct xfs_mount *mp = log->l_mp; struct xfs_mount *mp = log->l_mp;
struct xfs_bui_log_item *buip; struct xfs_bui_log_item *buip;
struct xfs_bui_log_format *bui_formatp; struct xfs_bui_log_format *bui_formatp;
size_t len;
bui_formatp = item->ri_buf[0].i_addr; bui_formatp = item->ri_buf[0].i_addr;
if (item->ri_buf[0].i_len < xfs_bui_log_format_sizeof(0)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED;
}
if (bui_formatp->bui_nextents != XFS_BUI_MAX_FAST_EXTENTS) { if (bui_formatp->bui_nextents != XFS_BUI_MAX_FAST_EXTENTS) {
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, log->l_mp); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
buip = xfs_bui_init(mp);
error = xfs_bui_copy_format(&item->ri_buf[0], &buip->bui_format); len = xfs_bui_log_format_sizeof(bui_formatp->bui_nextents);
if (error) { if (item->ri_buf[0].i_len != len) {
xfs_bui_item_free(buip); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
return error; item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED;
} }
buip = xfs_bui_init(mp);
xfs_bui_copy_format(&buip->bui_format, bui_formatp);
atomic_set(&buip->bui_next_extent, bui_formatp->bui_nextents); atomic_set(&buip->bui_next_extent, bui_formatp->bui_nextents);
/* /*
* Insert the intent into the AIL directly and drop one reference so * Insert the intent into the AIL directly and drop one reference so
...@@ -696,7 +697,8 @@ xlog_recover_bud_commit_pass2( ...@@ -696,7 +697,8 @@ xlog_recover_bud_commit_pass2(
bud_formatp = item->ri_buf[0].i_addr; bud_formatp = item->ri_buf[0].i_addr;
if (item->ri_buf[0].i_len != sizeof(struct xfs_bud_log_format)) { if (item->ri_buf[0].i_len != sizeof(struct xfs_bud_log_format)) {
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, log->l_mp); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, log->l_mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
......
...@@ -234,13 +234,18 @@ int ...@@ -234,13 +234,18 @@ int
xfs_errortag_init( xfs_errortag_init(
struct xfs_mount *mp) struct xfs_mount *mp)
{ {
int ret;
mp->m_errortag = kmem_zalloc(sizeof(unsigned int) * XFS_ERRTAG_MAX, mp->m_errortag = kmem_zalloc(sizeof(unsigned int) * XFS_ERRTAG_MAX,
KM_MAYFAIL); KM_MAYFAIL);
if (!mp->m_errortag) if (!mp->m_errortag)
return -ENOMEM; return -ENOMEM;
return xfs_sysfs_init(&mp->m_errortag_kobj, &xfs_errortag_ktype, ret = xfs_sysfs_init(&mp->m_errortag_kobj, &xfs_errortag_ktype,
&mp->m_kobj, "errortag"); &mp->m_kobj, "errortag");
if (ret)
kmem_free(mp->m_errortag);
return ret;
} }
void void
......
...@@ -66,27 +66,16 @@ xfs_efi_release( ...@@ -66,27 +66,16 @@ xfs_efi_release(
xfs_efi_item_free(efip); xfs_efi_item_free(efip);
} }
/*
* This returns the number of iovecs needed to log the given efi item.
* We only need 1 iovec for an efi item. It just logs the efi_log_format
* structure.
*/
static inline int
xfs_efi_item_sizeof(
struct xfs_efi_log_item *efip)
{
return sizeof(struct xfs_efi_log_format) +
(efip->efi_format.efi_nextents - 1) * sizeof(xfs_extent_t);
}
STATIC void STATIC void
xfs_efi_item_size( xfs_efi_item_size(
struct xfs_log_item *lip, struct xfs_log_item *lip,
int *nvecs, int *nvecs,
int *nbytes) int *nbytes)
{ {
struct xfs_efi_log_item *efip = EFI_ITEM(lip);
*nvecs += 1; *nvecs += 1;
*nbytes += xfs_efi_item_sizeof(EFI_ITEM(lip)); *nbytes += xfs_efi_log_format_sizeof(efip->efi_format.efi_nextents);
} }
/* /*
...@@ -112,7 +101,7 @@ xfs_efi_item_format( ...@@ -112,7 +101,7 @@ xfs_efi_item_format(
xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_EFI_FORMAT, xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_EFI_FORMAT,
&efip->efi_format, &efip->efi_format,
xfs_efi_item_sizeof(efip)); xfs_efi_log_format_sizeof(efip->efi_format.efi_nextents));
} }
...@@ -155,13 +144,11 @@ xfs_efi_init( ...@@ -155,13 +144,11 @@ xfs_efi_init(
{ {
struct xfs_efi_log_item *efip; struct xfs_efi_log_item *efip;
uint size;
ASSERT(nextents > 0); ASSERT(nextents > 0);
if (nextents > XFS_EFI_MAX_FAST_EXTENTS) { if (nextents > XFS_EFI_MAX_FAST_EXTENTS) {
size = (uint)(sizeof(struct xfs_efi_log_item) + efip = kzalloc(xfs_efi_log_item_sizeof(nextents),
((nextents - 1) * sizeof(xfs_extent_t))); GFP_KERNEL | __GFP_NOFAIL);
efip = kmem_zalloc(size, 0);
} else { } else {
efip = kmem_cache_zalloc(xfs_efi_cache, efip = kmem_cache_zalloc(xfs_efi_cache,
GFP_KERNEL | __GFP_NOFAIL); GFP_KERNEL | __GFP_NOFAIL);
...@@ -188,15 +175,17 @@ xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt) ...@@ -188,15 +175,17 @@ xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt)
{ {
xfs_efi_log_format_t *src_efi_fmt = buf->i_addr; xfs_efi_log_format_t *src_efi_fmt = buf->i_addr;
uint i; uint i;
uint len = sizeof(xfs_efi_log_format_t) + uint len = xfs_efi_log_format_sizeof(src_efi_fmt->efi_nextents);
(src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_t); uint len32 = xfs_efi_log_format32_sizeof(src_efi_fmt->efi_nextents);
uint len32 = sizeof(xfs_efi_log_format_32_t) + uint len64 = xfs_efi_log_format64_sizeof(src_efi_fmt->efi_nextents);
(src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_32_t);
uint len64 = sizeof(xfs_efi_log_format_64_t) +
(src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_64_t);
if (buf->i_len == len) { if (buf->i_len == len) {
memcpy((char *)dst_efi_fmt, (char*)src_efi_fmt, len); memcpy(dst_efi_fmt, src_efi_fmt,
offsetof(struct xfs_efi_log_format, efi_extents));
for (i = 0; i < src_efi_fmt->efi_nextents; i++)
memcpy(&dst_efi_fmt->efi_extents[i],
&src_efi_fmt->efi_extents[i],
sizeof(struct xfs_extent));
return 0; return 0;
} else if (buf->i_len == len32) { } else if (buf->i_len == len32) {
xfs_efi_log_format_32_t *src_efi_fmt_32 = buf->i_addr; xfs_efi_log_format_32_t *src_efi_fmt_32 = buf->i_addr;
...@@ -227,7 +216,8 @@ xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt) ...@@ -227,7 +216,8 @@ xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt)
} }
return 0; return 0;
} }
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, NULL, buf->i_addr,
buf->i_len);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
...@@ -246,27 +236,16 @@ xfs_efd_item_free(struct xfs_efd_log_item *efdp) ...@@ -246,27 +236,16 @@ xfs_efd_item_free(struct xfs_efd_log_item *efdp)
kmem_cache_free(xfs_efd_cache, efdp); kmem_cache_free(xfs_efd_cache, efdp);
} }
/*
* This returns the number of iovecs needed to log the given efd item.
* We only need 1 iovec for an efd item. It just logs the efd_log_format
* structure.
*/
static inline int
xfs_efd_item_sizeof(
struct xfs_efd_log_item *efdp)
{
return sizeof(xfs_efd_log_format_t) +
(efdp->efd_format.efd_nextents - 1) * sizeof(xfs_extent_t);
}
STATIC void STATIC void
xfs_efd_item_size( xfs_efd_item_size(
struct xfs_log_item *lip, struct xfs_log_item *lip,
int *nvecs, int *nvecs,
int *nbytes) int *nbytes)
{ {
struct xfs_efd_log_item *efdp = EFD_ITEM(lip);
*nvecs += 1; *nvecs += 1;
*nbytes += xfs_efd_item_sizeof(EFD_ITEM(lip)); *nbytes += xfs_efd_log_format_sizeof(efdp->efd_format.efd_nextents);
} }
/* /*
...@@ -291,7 +270,7 @@ xfs_efd_item_format( ...@@ -291,7 +270,7 @@ xfs_efd_item_format(
xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_EFD_FORMAT, xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_EFD_FORMAT,
&efdp->efd_format, &efdp->efd_format,
xfs_efd_item_sizeof(efdp)); xfs_efd_log_format_sizeof(efdp->efd_format.efd_nextents));
} }
/* /*
...@@ -340,9 +319,8 @@ xfs_trans_get_efd( ...@@ -340,9 +319,8 @@ xfs_trans_get_efd(
ASSERT(nextents > 0); ASSERT(nextents > 0);
if (nextents > XFS_EFD_MAX_FAST_EXTENTS) { if (nextents > XFS_EFD_MAX_FAST_EXTENTS) {
efdp = kmem_zalloc(sizeof(struct xfs_efd_log_item) + efdp = kzalloc(xfs_efd_log_item_sizeof(nextents),
(nextents - 1) * sizeof(struct xfs_extent), GFP_KERNEL | __GFP_NOFAIL);
0);
} else { } else {
efdp = kmem_cache_zalloc(xfs_efd_cache, efdp = kmem_cache_zalloc(xfs_efd_cache,
GFP_KERNEL | __GFP_NOFAIL); GFP_KERNEL | __GFP_NOFAIL);
...@@ -733,6 +711,12 @@ xlog_recover_efi_commit_pass2( ...@@ -733,6 +711,12 @@ xlog_recover_efi_commit_pass2(
efi_formatp = item->ri_buf[0].i_addr; efi_formatp = item->ri_buf[0].i_addr;
if (item->ri_buf[0].i_len < xfs_efi_log_format_sizeof(0)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED;
}
efip = xfs_efi_init(mp, efi_formatp->efi_nextents); efip = xfs_efi_init(mp, efi_formatp->efi_nextents);
error = xfs_efi_copy_format(&item->ri_buf[0], &efip->efi_format); error = xfs_efi_copy_format(&item->ri_buf[0], &efip->efi_format);
if (error) { if (error) {
...@@ -769,12 +753,24 @@ xlog_recover_efd_commit_pass2( ...@@ -769,12 +753,24 @@ xlog_recover_efd_commit_pass2(
xfs_lsn_t lsn) xfs_lsn_t lsn)
{ {
struct xfs_efd_log_format *efd_formatp; struct xfs_efd_log_format *efd_formatp;
int buflen = item->ri_buf[0].i_len;
efd_formatp = item->ri_buf[0].i_addr; efd_formatp = item->ri_buf[0].i_addr;
ASSERT((item->ri_buf[0].i_len == (sizeof(xfs_efd_log_format_32_t) +
((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_32_t)))) || if (buflen < sizeof(struct xfs_efd_log_format)) {
(item->ri_buf[0].i_len == (sizeof(xfs_efd_log_format_64_t) + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, log->l_mp,
((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_64_t))))); efd_formatp, buflen);
return -EFSCORRUPTED;
}
if (item->ri_buf[0].i_len != xfs_efd_log_format32_sizeof(
efd_formatp->efd_nextents) &&
item->ri_buf[0].i_len != xfs_efd_log_format64_sizeof(
efd_formatp->efd_nextents)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, log->l_mp,
efd_formatp, buflen);
return -EFSCORRUPTED;
}
xlog_recover_release_intent(log, XFS_LI_EFI, efd_formatp->efd_efi_id); xlog_recover_release_intent(log, XFS_LI_EFI, efd_formatp->efd_efi_id);
return 0; return 0;
......
...@@ -52,6 +52,14 @@ struct xfs_efi_log_item { ...@@ -52,6 +52,14 @@ struct xfs_efi_log_item {
xfs_efi_log_format_t efi_format; xfs_efi_log_format_t efi_format;
}; };
static inline size_t
xfs_efi_log_item_sizeof(
unsigned int nr)
{
return offsetof(struct xfs_efi_log_item, efi_format) +
xfs_efi_log_format_sizeof(nr);
}
/* /*
* This is the "extent free done" log item. It is used to log * This is the "extent free done" log item. It is used to log
* the fact that some extents earlier mentioned in an efi item * the fact that some extents earlier mentioned in an efi item
...@@ -64,6 +72,14 @@ struct xfs_efd_log_item { ...@@ -64,6 +72,14 @@ struct xfs_efd_log_item {
xfs_efd_log_format_t efd_format; xfs_efd_log_format_t efd_format;
}; };
static inline size_t
xfs_efd_log_item_sizeof(
unsigned int nr)
{
return offsetof(struct xfs_efd_log_item, efd_format) +
xfs_efd_log_format_sizeof(nr);
}
/* /*
* Max number of extents in fast allocation path. * Max number of extents in fast allocation path.
*/ */
......
...@@ -1261,7 +1261,7 @@ xfs_file_llseek( ...@@ -1261,7 +1261,7 @@ xfs_file_llseek(
} }
#ifdef CONFIG_FS_DAX #ifdef CONFIG_FS_DAX
static int static inline vm_fault_t
xfs_dax_fault( xfs_dax_fault(
struct vm_fault *vmf, struct vm_fault *vmf,
enum page_entry_size pe_size, enum page_entry_size pe_size,
...@@ -1274,14 +1274,15 @@ xfs_dax_fault( ...@@ -1274,14 +1274,15 @@ xfs_dax_fault(
&xfs_read_iomap_ops); &xfs_read_iomap_ops);
} }
#else #else
static int static inline vm_fault_t
xfs_dax_fault( xfs_dax_fault(
struct vm_fault *vmf, struct vm_fault *vmf,
enum page_entry_size pe_size, enum page_entry_size pe_size,
bool write_fault, bool write_fault,
pfn_t *pfn) pfn_t *pfn)
{ {
return 0; ASSERT(0);
return VM_FAULT_SIGBUS;
} }
#endif #endif
......
...@@ -2818,7 +2818,7 @@ xfs_rename( ...@@ -2818,7 +2818,7 @@ xfs_rename(
* Lock all the participating inodes. Depending upon whether * Lock all the participating inodes. Depending upon whether
* the target_name exists in the target directory, and * the target_name exists in the target directory, and
* whether the target directory is the same as the source * whether the target directory is the same as the source
* directory, we can lock from 2 to 4 inodes. * directory, we can lock from 2 to 5 inodes.
*/ */
xfs_lock_inodes(inodes, num_inodes, XFS_ILOCK_EXCL); xfs_lock_inodes(inodes, num_inodes, XFS_ILOCK_EXCL);
......
...@@ -2552,6 +2552,8 @@ xlog_recover_process_intents( ...@@ -2552,6 +2552,8 @@ xlog_recover_process_intents(
for (lip = xfs_trans_ail_cursor_first(ailp, &cur, 0); for (lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
lip != NULL; lip != NULL;
lip = xfs_trans_ail_cursor_next(ailp, &cur)) { lip = xfs_trans_ail_cursor_next(ailp, &cur)) {
const struct xfs_item_ops *ops;
if (!xlog_item_is_intent(lip)) if (!xlog_item_is_intent(lip))
break; break;
...@@ -2567,13 +2569,17 @@ xlog_recover_process_intents( ...@@ -2567,13 +2569,17 @@ xlog_recover_process_intents(
* deferred ops, you /must/ attach them to the capture list in * deferred ops, you /must/ attach them to the capture list in
* the recover routine or else those subsequent intents will be * the recover routine or else those subsequent intents will be
* replayed in the wrong order! * replayed in the wrong order!
*
* The recovery function can free the log item, so we must not
* access lip after it returns.
*/ */
spin_unlock(&ailp->ail_lock); spin_unlock(&ailp->ail_lock);
error = lip->li_ops->iop_recover(lip, &capture_list); ops = lip->li_ops;
error = ops->iop_recover(lip, &capture_list);
spin_lock(&ailp->ail_lock); spin_lock(&ailp->ail_lock);
if (error) { if (error) {
trace_xlog_intent_recovery_failed(log->l_mp, error, trace_xlog_intent_recovery_failed(log->l_mp, error,
lip->li_ops->iop_recover); ops->iop_recover);
break; break;
} }
} }
......
...@@ -118,10 +118,10 @@ xfs_check_ondisk_structs(void) ...@@ -118,10 +118,10 @@ xfs_check_ondisk_structs(void)
/* log structures */ /* log structures */
XFS_CHECK_STRUCT_SIZE(struct xfs_buf_log_format, 88); XFS_CHECK_STRUCT_SIZE(struct xfs_buf_log_format, 88);
XFS_CHECK_STRUCT_SIZE(struct xfs_dq_logformat, 24); XFS_CHECK_STRUCT_SIZE(struct xfs_dq_logformat, 24);
XFS_CHECK_STRUCT_SIZE(struct xfs_efd_log_format_32, 28); XFS_CHECK_STRUCT_SIZE(struct xfs_efd_log_format_32, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_efd_log_format_64, 32); XFS_CHECK_STRUCT_SIZE(struct xfs_efd_log_format_64, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_efi_log_format_32, 28); XFS_CHECK_STRUCT_SIZE(struct xfs_efi_log_format_32, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_efi_log_format_64, 32); XFS_CHECK_STRUCT_SIZE(struct xfs_efi_log_format_64, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_extent_32, 12); XFS_CHECK_STRUCT_SIZE(struct xfs_extent_32, 12);
XFS_CHECK_STRUCT_SIZE(struct xfs_extent_64, 16); XFS_CHECK_STRUCT_SIZE(struct xfs_extent_64, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_log_dinode, 176); XFS_CHECK_STRUCT_SIZE(struct xfs_log_dinode, 176);
...@@ -134,6 +134,21 @@ xfs_check_ondisk_structs(void) ...@@ -134,6 +134,21 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_STRUCT_SIZE(struct xfs_trans_header, 16); XFS_CHECK_STRUCT_SIZE(struct xfs_trans_header, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format, 40); XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format, 40);
XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format, 16); XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_bui_log_format, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_bud_log_format, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_cui_log_format, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_cud_log_format, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_rui_log_format, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_rud_log_format, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_map_extent, 32);
XFS_CHECK_STRUCT_SIZE(struct xfs_phys_extent, 16);
XFS_CHECK_OFFSET(struct xfs_bui_log_format, bui_extents, 16);
XFS_CHECK_OFFSET(struct xfs_cui_log_format, cui_extents, 16);
XFS_CHECK_OFFSET(struct xfs_rui_log_format, rui_extents, 16);
XFS_CHECK_OFFSET(struct xfs_efi_log_format, efi_extents, 16);
XFS_CHECK_OFFSET(struct xfs_efi_log_format_32, efi_extents, 16);
XFS_CHECK_OFFSET(struct xfs_efi_log_format_64, efi_extents, 16);
/* /*
* The v5 superblock format extended several v4 header structures with * The v5 superblock format extended several v4 header structures with
......
...@@ -523,7 +523,9 @@ xfs_cui_item_recover( ...@@ -523,7 +523,9 @@ xfs_cui_item_recover(
type = refc_type; type = refc_type;
break; break;
default: default:
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
&cuip->cui_format,
sizeof(cuip->cui_format));
error = -EFSCORRUPTED; error = -EFSCORRUPTED;
goto abort_error; goto abort_error;
} }
...@@ -536,7 +538,8 @@ xfs_cui_item_recover( ...@@ -536,7 +538,8 @@ xfs_cui_item_recover(
&new_fsb, &new_len, &rcur); &new_fsb, &new_len, &rcur);
if (error == -EFSCORRUPTED) if (error == -EFSCORRUPTED)
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
refc, sizeof(*refc)); &cuip->cui_format,
sizeof(cuip->cui_format));
if (error) if (error)
goto abort_error; goto abort_error;
...@@ -622,28 +625,18 @@ static const struct xfs_item_ops xfs_cui_item_ops = { ...@@ -622,28 +625,18 @@ static const struct xfs_item_ops xfs_cui_item_ops = {
.iop_relog = xfs_cui_item_relog, .iop_relog = xfs_cui_item_relog,
}; };
/* static inline void
* Copy an CUI format buffer from the given buf, and into the destination
* CUI format structure. The CUI/CUD items were designed not to need any
* special alignment handling.
*/
static int
xfs_cui_copy_format( xfs_cui_copy_format(
struct xfs_log_iovec *buf, struct xfs_cui_log_format *dst,
struct xfs_cui_log_format *dst_cui_fmt) const struct xfs_cui_log_format *src)
{ {
struct xfs_cui_log_format *src_cui_fmt; unsigned int i;
uint len;
src_cui_fmt = buf->i_addr; memcpy(dst, src, offsetof(struct xfs_cui_log_format, cui_extents));
len = xfs_cui_log_format_sizeof(src_cui_fmt->cui_nextents);
if (buf->i_len == len) { for (i = 0; i < src->cui_nextents; i++)
memcpy(dst_cui_fmt, src_cui_fmt, len); memcpy(&dst->cui_extents[i], &src->cui_extents[i],
return 0; sizeof(struct xfs_phys_extent));
}
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL);
return -EFSCORRUPTED;
} }
/* /*
...@@ -660,19 +653,28 @@ xlog_recover_cui_commit_pass2( ...@@ -660,19 +653,28 @@ xlog_recover_cui_commit_pass2(
struct xlog_recover_item *item, struct xlog_recover_item *item,
xfs_lsn_t lsn) xfs_lsn_t lsn)
{ {
int error;
struct xfs_mount *mp = log->l_mp; struct xfs_mount *mp = log->l_mp;
struct xfs_cui_log_item *cuip; struct xfs_cui_log_item *cuip;
struct xfs_cui_log_format *cui_formatp; struct xfs_cui_log_format *cui_formatp;
size_t len;
cui_formatp = item->ri_buf[0].i_addr; cui_formatp = item->ri_buf[0].i_addr;
cuip = xfs_cui_init(mp, cui_formatp->cui_nextents); if (item->ri_buf[0].i_len < xfs_cui_log_format_sizeof(0)) {
error = xfs_cui_copy_format(&item->ri_buf[0], &cuip->cui_format); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
if (error) { item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
xfs_cui_item_free(cuip); return -EFSCORRUPTED;
return error;
} }
len = xfs_cui_log_format_sizeof(cui_formatp->cui_nextents);
if (item->ri_buf[0].i_len != len) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED;
}
cuip = xfs_cui_init(mp, cui_formatp->cui_nextents);
xfs_cui_copy_format(&cuip->cui_format, cui_formatp);
atomic_set(&cuip->cui_next_extent, cui_formatp->cui_nextents); atomic_set(&cuip->cui_next_extent, cui_formatp->cui_nextents);
/* /*
* Insert the intent into the AIL directly and drop one reference so * Insert the intent into the AIL directly and drop one reference so
...@@ -706,7 +708,8 @@ xlog_recover_cud_commit_pass2( ...@@ -706,7 +708,8 @@ xlog_recover_cud_commit_pass2(
cud_formatp = item->ri_buf[0].i_addr; cud_formatp = item->ri_buf[0].i_addr;
if (item->ri_buf[0].i_len != sizeof(struct xfs_cud_log_format)) { if (item->ri_buf[0].i_len != sizeof(struct xfs_cud_log_format)) {
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, log->l_mp); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, log->l_mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
......
...@@ -155,31 +155,6 @@ xfs_rui_init( ...@@ -155,31 +155,6 @@ xfs_rui_init(
return ruip; return ruip;
} }
/*
* Copy an RUI format buffer from the given buf, and into the destination
* RUI format structure. The RUI/RUD items were designed not to need any
* special alignment handling.
*/
STATIC int
xfs_rui_copy_format(
struct xfs_log_iovec *buf,
struct xfs_rui_log_format *dst_rui_fmt)
{
struct xfs_rui_log_format *src_rui_fmt;
uint len;
src_rui_fmt = buf->i_addr;
len = xfs_rui_log_format_sizeof(src_rui_fmt->rui_nextents);
if (buf->i_len != len) {
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL);
return -EFSCORRUPTED;
}
memcpy(dst_rui_fmt, src_rui_fmt, len);
return 0;
}
static inline struct xfs_rud_log_item *RUD_ITEM(struct xfs_log_item *lip) static inline struct xfs_rud_log_item *RUD_ITEM(struct xfs_log_item *lip)
{ {
return container_of(lip, struct xfs_rud_log_item, rud_item); return container_of(lip, struct xfs_rud_log_item, rud_item);
...@@ -582,7 +557,9 @@ xfs_rui_item_recover( ...@@ -582,7 +557,9 @@ xfs_rui_item_recover(
type = XFS_RMAP_FREE; type = XFS_RMAP_FREE;
break; break;
default: default:
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
&ruip->rui_format,
sizeof(ruip->rui_format));
error = -EFSCORRUPTED; error = -EFSCORRUPTED;
goto abort_error; goto abort_error;
} }
...@@ -652,6 +629,20 @@ static const struct xfs_item_ops xfs_rui_item_ops = { ...@@ -652,6 +629,20 @@ static const struct xfs_item_ops xfs_rui_item_ops = {
.iop_relog = xfs_rui_item_relog, .iop_relog = xfs_rui_item_relog,
}; };
static inline void
xfs_rui_copy_format(
struct xfs_rui_log_format *dst,
const struct xfs_rui_log_format *src)
{
unsigned int i;
memcpy(dst, src, offsetof(struct xfs_rui_log_format, rui_extents));
for (i = 0; i < src->rui_nextents; i++)
memcpy(&dst->rui_extents[i], &src->rui_extents[i],
sizeof(struct xfs_map_extent));
}
/* /*
* This routine is called to create an in-core extent rmap update * This routine is called to create an in-core extent rmap update
* item from the rui format structure which was logged on disk. * item from the rui format structure which was logged on disk.
...@@ -666,19 +657,28 @@ xlog_recover_rui_commit_pass2( ...@@ -666,19 +657,28 @@ xlog_recover_rui_commit_pass2(
struct xlog_recover_item *item, struct xlog_recover_item *item,
xfs_lsn_t lsn) xfs_lsn_t lsn)
{ {
int error;
struct xfs_mount *mp = log->l_mp; struct xfs_mount *mp = log->l_mp;
struct xfs_rui_log_item *ruip; struct xfs_rui_log_item *ruip;
struct xfs_rui_log_format *rui_formatp; struct xfs_rui_log_format *rui_formatp;
size_t len;
rui_formatp = item->ri_buf[0].i_addr; rui_formatp = item->ri_buf[0].i_addr;
ruip = xfs_rui_init(mp, rui_formatp->rui_nextents); if (item->ri_buf[0].i_len < xfs_rui_log_format_sizeof(0)) {
error = xfs_rui_copy_format(&item->ri_buf[0], &ruip->rui_format); XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
if (error) { item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
xfs_rui_item_free(ruip); return -EFSCORRUPTED;
return error; }
len = xfs_rui_log_format_sizeof(rui_formatp->rui_nextents);
if (item->ri_buf[0].i_len != len) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED;
} }
ruip = xfs_rui_init(mp, rui_formatp->rui_nextents);
xfs_rui_copy_format(&ruip->rui_format, rui_formatp);
atomic_set(&ruip->rui_next_extent, rui_formatp->rui_nextents); atomic_set(&ruip->rui_next_extent, rui_formatp->rui_nextents);
/* /*
* Insert the intent into the AIL directly and drop one reference so * Insert the intent into the AIL directly and drop one reference so
...@@ -711,7 +711,11 @@ xlog_recover_rud_commit_pass2( ...@@ -711,7 +711,11 @@ xlog_recover_rud_commit_pass2(
struct xfs_rud_log_format *rud_formatp; struct xfs_rud_log_format *rud_formatp;
rud_formatp = item->ri_buf[0].i_addr; rud_formatp = item->ri_buf[0].i_addr;
ASSERT(item->ri_buf[0].i_len == sizeof(struct xfs_rud_log_format)); if (item->ri_buf[0].i_len != sizeof(struct xfs_rud_log_format)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, log->l_mp,
rud_formatp, item->ri_buf[0].i_len);
return -EFSCORRUPTED;
}
xlog_recover_release_intent(log, XFS_LI_RUI, rud_formatp->rud_rui_id); xlog_recover_release_intent(log, XFS_LI_RUI, rud_formatp->rud_rui_id);
return 0; return 0;
......
...@@ -2028,18 +2028,14 @@ xfs_init_caches(void) ...@@ -2028,18 +2028,14 @@ xfs_init_caches(void)
goto out_destroy_trans_cache; goto out_destroy_trans_cache;
xfs_efd_cache = kmem_cache_create("xfs_efd_item", xfs_efd_cache = kmem_cache_create("xfs_efd_item",
(sizeof(struct xfs_efd_log_item) + xfs_efd_log_item_sizeof(XFS_EFD_MAX_FAST_EXTENTS),
(XFS_EFD_MAX_FAST_EXTENTS - 1) * 0, 0, NULL);
sizeof(struct xfs_extent)),
0, 0, NULL);
if (!xfs_efd_cache) if (!xfs_efd_cache)
goto out_destroy_buf_item_cache; goto out_destroy_buf_item_cache;
xfs_efi_cache = kmem_cache_create("xfs_efi_item", xfs_efi_cache = kmem_cache_create("xfs_efi_item",
(sizeof(struct xfs_efi_log_item) + xfs_efi_log_item_sizeof(XFS_EFI_MAX_FAST_EXTENTS),
(XFS_EFI_MAX_FAST_EXTENTS - 1) * 0, 0, NULL);
sizeof(struct xfs_extent)),
0, 0, NULL);
if (!xfs_efi_cache) if (!xfs_efi_cache)
goto out_destroy_efd_cache; goto out_destroy_efd_cache;
......
...@@ -33,10 +33,15 @@ xfs_sysfs_init( ...@@ -33,10 +33,15 @@ xfs_sysfs_init(
const char *name) const char *name)
{ {
struct kobject *parent; struct kobject *parent;
int err;
parent = parent_kobj ? &parent_kobj->kobject : NULL; parent = parent_kobj ? &parent_kobj->kobject : NULL;
init_completion(&kobj->complete); init_completion(&kobj->complete);
return kobject_init_and_add(&kobj->kobject, ktype, parent, "%s", name); err = kobject_init_and_add(&kobj->kobject, ktype, parent, "%s", name);
if (err)
kobject_put(&kobj->kobject);
return err;
} }
static inline void static inline void
......
...@@ -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)
......
...@@ -730,11 +730,10 @@ void ...@@ -730,11 +730,10 @@ void
xfs_ail_push_all_sync( xfs_ail_push_all_sync(
struct xfs_ail *ailp) struct xfs_ail *ailp)
{ {
struct xfs_log_item *lip;
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
spin_lock(&ailp->ail_lock); spin_lock(&ailp->ail_lock);
while ((lip = xfs_ail_max(ailp)) != NULL) { while (xfs_ail_max(ailp) != NULL) {
prepare_to_wait(&ailp->ail_empty, &wait, TASK_UNINTERRUPTIBLE); prepare_to_wait(&ailp->ail_empty, &wait, TASK_UNINTERRUPTIBLE);
wake_up_process(ailp->ail_task); wake_up_process(ailp->ail_task);
spin_unlock(&ailp->ail_lock); spin_unlock(&ailp->ail_lock);
......
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