Commit 98e63b91 authored by Chandan Babu R's avatar Chandan Babu R

Merge tag 'repair-file-mappings-6.8_2023-12-15' of...

Merge tag 'repair-file-mappings-6.8_2023-12-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.8-mergeB

xfs: online repair of file fork mappings

In this series, online repair gains the ability to rebuild data and attr
fork mappings from the reverse mapping information.  It is at this point
where we reintroduce the ability to reap file extents.

Repair of CoW forks is a little different -- on disk, CoW staging
extents are owned by the refcount btree and cannot be mapped back to
individual files.  Hence we can only detect staging extents that don't
quite look right (missing reverse mappings, shared staging extents) and
replace them with fresh allocations.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Signed-off-by: default avatarChandan Babu R <chandanbabu@kernel.org>

* tag 'repair-file-mappings-6.8_2023-12-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux:
  xfs: repair problems in CoW forks
  xfs: create a ranged query function for refcount btrees
  xfs: refactor repair forcing tests into a repair.c helper
  xfs: repair inode fork block mapping data structures
  xfs: reintroduce reaping of file metadata blocks to xrep_reap_extents
parents 7b63ce86 dbbdbd00
...@@ -183,6 +183,8 @@ ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y) ...@@ -183,6 +183,8 @@ ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y)
xfs-y += $(addprefix scrub/, \ xfs-y += $(addprefix scrub/, \
agheader_repair.o \ agheader_repair.o \
alloc_repair.o \ alloc_repair.o \
bmap_repair.o \
cow_repair.o \
ialloc_repair.o \ ialloc_repair.o \
inode_repair.o \ inode_repair.o \
newbt.o \ newbt.o \
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "xfs_trans.h" #include "xfs_trans.h"
#include "xfs_alloc.h" #include "xfs_alloc.h"
#include "xfs_btree.h" #include "xfs_btree.h"
#include "xfs_btree_staging.h"
#include "xfs_bmap_btree.h" #include "xfs_bmap_btree.h"
#include "xfs_bmap.h" #include "xfs_bmap.h"
#include "xfs_error.h" #include "xfs_error.h"
...@@ -288,10 +289,7 @@ xfs_bmbt_get_minrecs( ...@@ -288,10 +289,7 @@ xfs_bmbt_get_minrecs(
int level) int level)
{ {
if (level == cur->bc_nlevels - 1) { if (level == cur->bc_nlevels - 1) {
struct xfs_ifork *ifp; struct xfs_ifork *ifp = xfs_btree_ifork_ptr(cur);
ifp = xfs_ifork_ptr(cur->bc_ino.ip,
cur->bc_ino.whichfork);
return xfs_bmbt_maxrecs(cur->bc_mp, return xfs_bmbt_maxrecs(cur->bc_mp,
ifp->if_broot_bytes, level == 0) / 2; ifp->if_broot_bytes, level == 0) / 2;
...@@ -306,10 +304,7 @@ xfs_bmbt_get_maxrecs( ...@@ -306,10 +304,7 @@ xfs_bmbt_get_maxrecs(
int level) int level)
{ {
if (level == cur->bc_nlevels - 1) { if (level == cur->bc_nlevels - 1) {
struct xfs_ifork *ifp; struct xfs_ifork *ifp = xfs_btree_ifork_ptr(cur);
ifp = xfs_ifork_ptr(cur->bc_ino.ip,
cur->bc_ino.whichfork);
return xfs_bmbt_maxrecs(cur->bc_mp, return xfs_bmbt_maxrecs(cur->bc_mp,
ifp->if_broot_bytes, level == 0); ifp->if_broot_bytes, level == 0);
...@@ -543,23 +538,19 @@ static const struct xfs_btree_ops xfs_bmbt_ops = { ...@@ -543,23 +538,19 @@ static const struct xfs_btree_ops xfs_bmbt_ops = {
.keys_contiguous = xfs_bmbt_keys_contiguous, .keys_contiguous = xfs_bmbt_keys_contiguous,
}; };
/* static struct xfs_btree_cur *
* Allocate a new bmap btree cursor. xfs_bmbt_init_common(
*/ struct xfs_mount *mp,
struct xfs_btree_cur * /* new bmap btree cursor */ struct xfs_trans *tp,
xfs_bmbt_init_cursor( struct xfs_inode *ip,
struct xfs_mount *mp, /* file system mount point */ int whichfork)
struct xfs_trans *tp, /* transaction pointer */
struct xfs_inode *ip, /* inode owning the btree */
int whichfork) /* data or attr fork */
{ {
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
struct xfs_btree_cur *cur; struct xfs_btree_cur *cur;
ASSERT(whichfork != XFS_COW_FORK); ASSERT(whichfork != XFS_COW_FORK);
cur = xfs_btree_alloc_cursor(mp, tp, XFS_BTNUM_BMAP, cur = xfs_btree_alloc_cursor(mp, tp, XFS_BTNUM_BMAP,
mp->m_bm_maxlevels[whichfork], xfs_bmbt_cur_cache); mp->m_bm_maxlevels[whichfork], xfs_bmbt_cur_cache);
cur->bc_nlevels = be16_to_cpu(ifp->if_broot->bb_level) + 1;
cur->bc_statoff = XFS_STATS_CALC_INDEX(xs_bmbt_2); cur->bc_statoff = XFS_STATS_CALC_INDEX(xs_bmbt_2);
cur->bc_ops = &xfs_bmbt_ops; cur->bc_ops = &xfs_bmbt_ops;
...@@ -567,10 +558,30 @@ xfs_bmbt_init_cursor( ...@@ -567,10 +558,30 @@ xfs_bmbt_init_cursor(
if (xfs_has_crc(mp)) if (xfs_has_crc(mp))
cur->bc_flags |= XFS_BTREE_CRC_BLOCKS; cur->bc_flags |= XFS_BTREE_CRC_BLOCKS;
cur->bc_ino.forksize = xfs_inode_fork_size(ip, whichfork);
cur->bc_ino.ip = ip; cur->bc_ino.ip = ip;
cur->bc_ino.allocated = 0; cur->bc_ino.allocated = 0;
cur->bc_ino.flags = 0; cur->bc_ino.flags = 0;
return cur;
}
/*
* Allocate a new bmap btree cursor.
*/
struct xfs_btree_cur *
xfs_bmbt_init_cursor(
struct xfs_mount *mp,
struct xfs_trans *tp,
struct xfs_inode *ip,
int whichfork)
{
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
struct xfs_btree_cur *cur;
cur = xfs_bmbt_init_common(mp, tp, ip, whichfork);
cur->bc_nlevels = be16_to_cpu(ifp->if_broot->bb_level) + 1;
cur->bc_ino.forksize = xfs_inode_fork_size(ip, whichfork);
cur->bc_ino.whichfork = whichfork; cur->bc_ino.whichfork = whichfork;
return cur; return cur;
...@@ -587,6 +598,76 @@ xfs_bmbt_block_maxrecs( ...@@ -587,6 +598,76 @@ xfs_bmbt_block_maxrecs(
return blocklen / (sizeof(xfs_bmbt_key_t) + sizeof(xfs_bmbt_ptr_t)); return blocklen / (sizeof(xfs_bmbt_key_t) + sizeof(xfs_bmbt_ptr_t));
} }
/*
* Allocate a new bmap btree cursor for reloading an inode block mapping data
* structure. Note that callers can use the staged cursor to reload extents
* format inode forks if they rebuild the iext tree and commit the staged
* cursor immediately.
*/
struct xfs_btree_cur *
xfs_bmbt_stage_cursor(
struct xfs_mount *mp,
struct xfs_inode *ip,
struct xbtree_ifakeroot *ifake)
{
struct xfs_btree_cur *cur;
struct xfs_btree_ops *ops;
/* data fork always has larger maxheight */
cur = xfs_bmbt_init_common(mp, NULL, ip, XFS_DATA_FORK);
cur->bc_nlevels = ifake->if_levels;
cur->bc_ino.forksize = ifake->if_fork_size;
/* Don't let anyone think we're attached to the real fork yet. */
cur->bc_ino.whichfork = -1;
xfs_btree_stage_ifakeroot(cur, ifake, &ops);
ops->update_cursor = NULL;
return cur;
}
/*
* Swap in the new inode fork root. Once we pass this point the newly rebuilt
* mappings are in place and we have to kill off any old btree blocks.
*/
void
xfs_bmbt_commit_staged_btree(
struct xfs_btree_cur *cur,
struct xfs_trans *tp,
int whichfork)
{
struct xbtree_ifakeroot *ifake = cur->bc_ino.ifake;
struct xfs_ifork *ifp;
static const short brootflag[2] = {XFS_ILOG_DBROOT, XFS_ILOG_ABROOT};
static const short extflag[2] = {XFS_ILOG_DEXT, XFS_ILOG_AEXT};
int flags = XFS_ILOG_CORE;
ASSERT(cur->bc_flags & XFS_BTREE_STAGING);
ASSERT(whichfork != XFS_COW_FORK);
/*
* Free any resources hanging off the real fork, then shallow-copy the
* staging fork's contents into the real fork to transfer everything
* we just built.
*/
ifp = xfs_ifork_ptr(cur->bc_ino.ip, whichfork);
xfs_idestroy_fork(ifp);
memcpy(ifp, ifake->if_fork, sizeof(struct xfs_ifork));
switch (ifp->if_format) {
case XFS_DINODE_FMT_EXTENTS:
flags |= extflag[whichfork];
break;
case XFS_DINODE_FMT_BTREE:
flags |= brootflag[whichfork];
break;
default:
ASSERT(0);
break;
}
xfs_trans_log_inode(tp, cur->bc_ino.ip, flags);
xfs_btree_commit_ifakeroot(cur, tp, whichfork, &xfs_bmbt_ops);
}
/* /*
* Calculate number of records in a bmap btree block. * Calculate number of records in a bmap btree block.
*/ */
......
...@@ -11,6 +11,7 @@ struct xfs_btree_block; ...@@ -11,6 +11,7 @@ struct xfs_btree_block;
struct xfs_mount; struct xfs_mount;
struct xfs_inode; struct xfs_inode;
struct xfs_trans; struct xfs_trans;
struct xbtree_ifakeroot;
/* /*
* Btree block header size depends on a superblock flag. * Btree block header size depends on a superblock flag.
...@@ -106,6 +107,10 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip, ...@@ -106,6 +107,10 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip,
extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *, extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
struct xfs_trans *, struct xfs_inode *, int); struct xfs_trans *, struct xfs_inode *, int);
struct xfs_btree_cur *xfs_bmbt_stage_cursor(struct xfs_mount *mp,
struct xfs_inode *ip, struct xbtree_ifakeroot *ifake);
void xfs_bmbt_commit_staged_btree(struct xfs_btree_cur *cur,
struct xfs_trans *tp, int whichfork);
extern unsigned long long xfs_bmbt_calc_size(struct xfs_mount *mp, extern unsigned long long xfs_bmbt_calc_size(struct xfs_mount *mp,
unsigned long long len); unsigned long long len);
......
...@@ -405,7 +405,7 @@ xfs_btree_bload_prep_block( ...@@ -405,7 +405,7 @@ xfs_btree_bload_prep_block(
ASSERT(*bpp == NULL); ASSERT(*bpp == NULL);
/* Allocate a new incore btree root block. */ /* Allocate a new incore btree root block. */
new_size = bbl->iroot_size(cur, nr_this_block, priv); new_size = bbl->iroot_size(cur, level, nr_this_block, priv);
ifp->if_broot = kmem_zalloc(new_size, 0); ifp->if_broot = kmem_zalloc(new_size, 0);
ifp->if_broot_bytes = (int)new_size; ifp->if_broot_bytes = (int)new_size;
...@@ -596,6 +596,13 @@ xfs_btree_bload_level_geometry( ...@@ -596,6 +596,13 @@ xfs_btree_bload_level_geometry(
unsigned int desired_npb; unsigned int desired_npb;
unsigned int maxnr; unsigned int maxnr;
/*
* Compute the absolute maximum number of records that we can store in
* the ondisk block or inode root.
*/
if (cur->bc_ops->get_dmaxrecs)
maxnr = cur->bc_ops->get_dmaxrecs(cur, level);
else
maxnr = cur->bc_ops->get_maxrecs(cur, level); maxnr = cur->bc_ops->get_maxrecs(cur, level);
/* /*
......
...@@ -53,7 +53,7 @@ typedef int (*xfs_btree_bload_get_records_fn)(struct xfs_btree_cur *cur, ...@@ -53,7 +53,7 @@ typedef int (*xfs_btree_bload_get_records_fn)(struct xfs_btree_cur *cur,
typedef int (*xfs_btree_bload_claim_block_fn)(struct xfs_btree_cur *cur, typedef int (*xfs_btree_bload_claim_block_fn)(struct xfs_btree_cur *cur,
union xfs_btree_ptr *ptr, void *priv); union xfs_btree_ptr *ptr, void *priv);
typedef size_t (*xfs_btree_bload_iroot_size_fn)(struct xfs_btree_cur *cur, typedef size_t (*xfs_btree_bload_iroot_size_fn)(struct xfs_btree_cur *cur,
unsigned int nr_this_level, void *priv); unsigned int level, unsigned int nr_this_level, void *priv);
struct xfs_btree_bload { struct xfs_btree_bload {
/* /*
......
...@@ -622,13 +622,11 @@ static inline void xfs_iext_inc_seq(struct xfs_ifork *ifp) ...@@ -622,13 +622,11 @@ static inline void xfs_iext_inc_seq(struct xfs_ifork *ifp)
} }
void void
xfs_iext_insert( xfs_iext_insert_raw(
struct xfs_inode *ip, struct xfs_ifork *ifp,
struct xfs_iext_cursor *cur, struct xfs_iext_cursor *cur,
struct xfs_bmbt_irec *irec, struct xfs_bmbt_irec *irec)
int state)
{ {
struct xfs_ifork *ifp = xfs_iext_state_to_fork(ip, state);
xfs_fileoff_t offset = irec->br_startoff; xfs_fileoff_t offset = irec->br_startoff;
struct xfs_iext_leaf *new = NULL; struct xfs_iext_leaf *new = NULL;
int nr_entries, i; int nr_entries, i;
...@@ -662,12 +660,23 @@ xfs_iext_insert( ...@@ -662,12 +660,23 @@ xfs_iext_insert(
xfs_iext_set(cur_rec(cur), irec); xfs_iext_set(cur_rec(cur), irec);
ifp->if_bytes += sizeof(struct xfs_iext_rec); ifp->if_bytes += sizeof(struct xfs_iext_rec);
trace_xfs_iext_insert(ip, cur, state, _RET_IP_);
if (new) if (new)
xfs_iext_insert_node(ifp, xfs_iext_leaf_key(new, 0), new, 2); xfs_iext_insert_node(ifp, xfs_iext_leaf_key(new, 0), new, 2);
} }
void
xfs_iext_insert(
struct xfs_inode *ip,
struct xfs_iext_cursor *cur,
struct xfs_bmbt_irec *irec,
int state)
{
struct xfs_ifork *ifp = xfs_iext_state_to_fork(ip, state);
xfs_iext_insert_raw(ifp, cur, irec);
trace_xfs_iext_insert(ip, cur, state, _RET_IP_);
}
static struct xfs_iext_node * static struct xfs_iext_node *
xfs_iext_rebalance_node( xfs_iext_rebalance_node(
struct xfs_iext_node *parent, struct xfs_iext_node *parent,
......
...@@ -520,6 +520,7 @@ xfs_idata_realloc( ...@@ -520,6 +520,7 @@ xfs_idata_realloc(
ifp->if_bytes = new_size; ifp->if_bytes = new_size;
} }
/* Free all memory and reset a fork back to its initial state. */
void void
xfs_idestroy_fork( xfs_idestroy_fork(
struct xfs_ifork *ifp) struct xfs_ifork *ifp)
......
...@@ -180,6 +180,9 @@ void xfs_init_local_fork(struct xfs_inode *ip, int whichfork, ...@@ -180,6 +180,9 @@ void xfs_init_local_fork(struct xfs_inode *ip, int whichfork,
const void *data, int64_t size); const void *data, int64_t size);
xfs_extnum_t xfs_iext_count(struct xfs_ifork *ifp); xfs_extnum_t xfs_iext_count(struct xfs_ifork *ifp);
void xfs_iext_insert_raw(struct xfs_ifork *ifp,
struct xfs_iext_cursor *cur,
struct xfs_bmbt_irec *irec);
void xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur, void xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur,
struct xfs_bmbt_irec *, int); struct xfs_bmbt_irec *, int);
void xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *, void xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
......
...@@ -2031,6 +2031,47 @@ xfs_refcount_has_records( ...@@ -2031,6 +2031,47 @@ xfs_refcount_has_records(
return xfs_btree_has_records(cur, &low, &high, NULL, outcome); return xfs_btree_has_records(cur, &low, &high, NULL, outcome);
} }
struct xfs_refcount_query_range_info {
xfs_refcount_query_range_fn fn;
void *priv;
};
/* Format btree record and pass to our callback. */
STATIC int
xfs_refcount_query_range_helper(
struct xfs_btree_cur *cur,
const union xfs_btree_rec *rec,
void *priv)
{
struct xfs_refcount_query_range_info *query = priv;
struct xfs_refcount_irec irec;
xfs_failaddr_t fa;
xfs_refcount_btrec_to_irec(rec, &irec);
fa = xfs_refcount_check_irec(cur->bc_ag.pag, &irec);
if (fa)
return xfs_refcount_complain_bad_rec(cur, fa, &irec);
return query->fn(cur, &irec, query->priv);
}
/* Find all refcount records between two keys. */
int
xfs_refcount_query_range(
struct xfs_btree_cur *cur,
const struct xfs_refcount_irec *low_rec,
const struct xfs_refcount_irec *high_rec,
xfs_refcount_query_range_fn fn,
void *priv)
{
union xfs_btree_irec low_brec = { .rc = *low_rec };
union xfs_btree_irec high_brec = { .rc = *high_rec };
struct xfs_refcount_query_range_info query = { .priv = priv, .fn = fn };
return xfs_btree_query_range(cur, &low_brec, &high_brec,
xfs_refcount_query_range_helper, &query);
}
int __init int __init
xfs_refcount_intent_init_cache(void) xfs_refcount_intent_init_cache(void)
{ {
......
...@@ -127,4 +127,14 @@ extern struct kmem_cache *xfs_refcount_intent_cache; ...@@ -127,4 +127,14 @@ extern struct kmem_cache *xfs_refcount_intent_cache;
int __init xfs_refcount_intent_init_cache(void); int __init xfs_refcount_intent_init_cache(void);
void xfs_refcount_intent_destroy_cache(void); void xfs_refcount_intent_destroy_cache(void);
typedef int (*xfs_refcount_query_range_fn)(
struct xfs_btree_cur *cur,
const struct xfs_refcount_irec *rec,
void *priv);
int xfs_refcount_query_range(struct xfs_btree_cur *cur,
const struct xfs_refcount_irec *low_rec,
const struct xfs_refcount_irec *high_rec,
xfs_refcount_query_range_fn fn, void *priv);
#endif /* __XFS_REFCOUNT_H__ */ #endif /* __XFS_REFCOUNT_H__ */
...@@ -50,9 +50,18 @@ xchk_setup_inode_bmap( ...@@ -50,9 +50,18 @@ xchk_setup_inode_bmap(
if (S_ISREG(VFS_I(sc->ip)->i_mode) && if (S_ISREG(VFS_I(sc->ip)->i_mode) &&
sc->sm->sm_type != XFS_SCRUB_TYPE_BMBTA) { sc->sm->sm_type != XFS_SCRUB_TYPE_BMBTA) {
struct address_space *mapping = VFS_I(sc->ip)->i_mapping; struct address_space *mapping = VFS_I(sc->ip)->i_mapping;
bool is_repair = xchk_could_repair(sc);
xchk_ilock(sc, XFS_MMAPLOCK_EXCL); xchk_ilock(sc, XFS_MMAPLOCK_EXCL);
/* Break all our leases, we're going to mess with things. */
if (is_repair) {
error = xfs_break_layouts(VFS_I(sc->ip),
&sc->ilock_flags, BREAK_WRITE);
if (error)
goto out;
}
inode_dio_wait(VFS_I(sc->ip)); inode_dio_wait(VFS_I(sc->ip));
/* /*
...@@ -73,6 +82,15 @@ xchk_setup_inode_bmap( ...@@ -73,6 +82,15 @@ xchk_setup_inode_bmap(
error = filemap_fdatawait_keep_errors(mapping); error = filemap_fdatawait_keep_errors(mapping);
if (error && (error != -ENOSPC && error != -EIO)) if (error && (error != -ENOSPC && error != -EIO))
goto out; goto out;
/* Drop the page cache if we're repairing block mappings. */
if (is_repair) {
error = invalidate_inode_pages2(
VFS_I(sc->ip)->i_mapping);
if (error)
goto out;
}
} }
/* Got the inode, lock it and we're ready to go. */ /* Got the inode, lock it and we're ready to go. */
......
This diff is collapsed.
...@@ -239,7 +239,11 @@ int xchk_metadata_inode_forks(struct xfs_scrub *sc); ...@@ -239,7 +239,11 @@ int xchk_metadata_inode_forks(struct xfs_scrub *sc);
(sc)->mp->m_super->s_id, \ (sc)->mp->m_super->s_id, \
(sc)->sa.pag ? (sc)->sa.pag->pag_agno : (sc)->sm->sm_agno, \ (sc)->sa.pag ? (sc)->sa.pag->pag_agno : (sc)->sm->sm_agno, \
##__VA_ARGS__) ##__VA_ARGS__)
#define xchk_xfile_ino_descr(sc, fmt, ...) \
kasprintf(XCHK_GFP_FLAGS, "XFS (%s): inode 0x%llx " fmt, \
(sc)->mp->m_super->s_id, \
(sc)->ip ? (sc)->ip->i_ino : (sc)->sm->sm_ino, \
##__VA_ARGS__)
/* /*
* Setting up a hook to wait for intents to drain is costly -- we have to take * Setting up a hook to wait for intents to drain is costly -- we have to take
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018-2023 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#ifndef __XFS_SCRUB_FSB_BITMAP_H__
#define __XFS_SCRUB_FSB_BITMAP_H__
/* Bitmaps, but for type-checked for xfs_fsblock_t */
struct xfsb_bitmap {
struct xbitmap64 fsbitmap;
};
static inline void xfsb_bitmap_init(struct xfsb_bitmap *bitmap)
{
xbitmap64_init(&bitmap->fsbitmap);
}
static inline void xfsb_bitmap_destroy(struct xfsb_bitmap *bitmap)
{
xbitmap64_destroy(&bitmap->fsbitmap);
}
static inline int xfsb_bitmap_set(struct xfsb_bitmap *bitmap,
xfs_fsblock_t start, xfs_filblks_t len)
{
return xbitmap64_set(&bitmap->fsbitmap, start, len);
}
static inline int xfsb_bitmap_walk(struct xfsb_bitmap *bitmap,
xbitmap64_walk_fn fn, void *priv)
{
return xbitmap64_walk(&bitmap->fsbitmap, fn, priv);
}
#endif /* __XFS_SCRUB_FSB_BITMAP_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022-2023 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#ifndef __XFS_SCRUB_OFF_BITMAP_H__
#define __XFS_SCRUB_OFF_BITMAP_H__
/* Bitmaps, but for type-checked for xfs_fileoff_t */
struct xoff_bitmap {
struct xbitmap64 offbitmap;
};
static inline void xoff_bitmap_init(struct xoff_bitmap *bitmap)
{
xbitmap64_init(&bitmap->offbitmap);
}
static inline void xoff_bitmap_destroy(struct xoff_bitmap *bitmap)
{
xbitmap64_destroy(&bitmap->offbitmap);
}
static inline int xoff_bitmap_set(struct xoff_bitmap *bitmap,
xfs_fileoff_t off, xfs_filblks_t len)
{
return xbitmap64_set(&bitmap->offbitmap, off, len);
}
static inline int xoff_bitmap_walk(struct xoff_bitmap *bitmap,
xbitmap64_walk_fn fn, void *priv)
{
return xbitmap64_walk(&bitmap->offbitmap, fn, priv);
}
#endif /* __XFS_SCRUB_OFF_BITMAP_H__ */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "xfs_ialloc_btree.h" #include "xfs_ialloc_btree.h"
#include "xfs_rmap.h" #include "xfs_rmap.h"
#include "xfs_rmap_btree.h" #include "xfs_rmap_btree.h"
#include "xfs_refcount.h"
#include "xfs_refcount_btree.h" #include "xfs_refcount_btree.h"
#include "xfs_extent_busy.h" #include "xfs_extent_busy.h"
#include "xfs_ag.h" #include "xfs_ag.h"
...@@ -38,6 +39,7 @@ ...@@ -38,6 +39,7 @@
#include "scrub/repair.h" #include "scrub/repair.h"
#include "scrub/bitmap.h" #include "scrub/bitmap.h"
#include "scrub/agb_bitmap.h" #include "scrub/agb_bitmap.h"
#include "scrub/fsb_bitmap.h"
#include "scrub/reap.h" #include "scrub/reap.h"
/* /*
...@@ -75,10 +77,10 @@ ...@@ -75,10 +77,10 @@
* with only the same rmap owner but the block is not owned by something with * with only the same rmap owner but the block is not owned by something with
* the same rmap owner, the block will be freed. * the same rmap owner, the block will be freed.
* *
* The caller is responsible for locking the AG headers for the entire rebuild * The caller is responsible for locking the AG headers/inode for the entire
* operation so that nothing else can sneak in and change the AG state while * rebuild operation so that nothing else can sneak in and change the incore
* we're not looking. We must also invalidate any buffers associated with * state while we're not looking. We must also invalidate any buffers
* @bitmap. * associated with @bitmap.
*/ */
/* Information about reaping extents after a repair. */ /* Information about reaping extents after a repair. */
...@@ -379,6 +381,17 @@ xreap_agextent_iter( ...@@ -379,6 +381,17 @@ xreap_agextent_iter(
trace_xreap_dispose_unmap_extent(sc->sa.pag, agbno, *aglenp); trace_xreap_dispose_unmap_extent(sc->sa.pag, agbno, *aglenp);
rs->force_roll = true; rs->force_roll = true;
if (rs->oinfo == &XFS_RMAP_OINFO_COW) {
/*
* If we're unmapping CoW staging extents, remove the
* records from the refcountbt, which will remove the
* rmap record as well.
*/
xfs_refcount_free_cow_extent(sc->tp, fsbno, *aglenp);
return 0;
}
return xfs_rmap_free(sc->tp, sc->sa.agf_bp, sc->sa.pag, agbno, return xfs_rmap_free(sc->tp, sc->sa.agf_bp, sc->sa.pag, agbno,
*aglenp, rs->oinfo); *aglenp, rs->oinfo);
} }
...@@ -397,6 +410,26 @@ xreap_agextent_iter( ...@@ -397,6 +410,26 @@ xreap_agextent_iter(
return 0; return 0;
} }
/*
* If we're getting rid of CoW staging extents, use deferred work items
* to remove the refcountbt records (which removes the rmap records)
* and free the extent. We're not worried about the system going down
* here because log recovery walks the refcount btree to clean out the
* CoW staging extents.
*/
if (rs->oinfo == &XFS_RMAP_OINFO_COW) {
ASSERT(rs->resv == XFS_AG_RESV_NONE);
xfs_refcount_free_cow_extent(sc->tp, fsbno, *aglenp);
error = xfs_free_extent_later(sc->tp, fsbno, *aglenp, NULL,
rs->resv, true);
if (error)
return error;
rs->force_roll = true;
return 0;
}
/* Put blocks back on the AGFL one at a time. */ /* Put blocks back on the AGFL one at a time. */
if (rs->resv == XFS_AG_RESV_AGFL) { if (rs->resv == XFS_AG_RESV_AGFL) {
ASSERT(*aglenp == 1); ASSERT(*aglenp == 1);
...@@ -501,3 +534,115 @@ xrep_reap_agblocks( ...@@ -501,3 +534,115 @@ xrep_reap_agblocks(
return 0; return 0;
} }
/*
* Break a file metadata extent into sub-extents by fate (crosslinked, not
* crosslinked), and dispose of each sub-extent separately. The extent must
* not cross an AG boundary.
*/
STATIC int
xreap_fsmeta_extent(
uint64_t fsbno,
uint64_t len,
void *priv)
{
struct xreap_state *rs = priv;
struct xfs_scrub *sc = rs->sc;
xfs_agnumber_t agno = XFS_FSB_TO_AGNO(sc->mp, fsbno);
xfs_agblock_t agbno = XFS_FSB_TO_AGBNO(sc->mp, fsbno);
xfs_agblock_t agbno_next = agbno + len;
int error = 0;
ASSERT(len <= XFS_MAX_BMBT_EXTLEN);
ASSERT(sc->ip != NULL);
ASSERT(!sc->sa.pag);
/*
* We're reaping blocks after repairing file metadata, which means that
* we have to init the xchk_ag structure ourselves.
*/
sc->sa.pag = xfs_perag_get(sc->mp, agno);
if (!sc->sa.pag)
return -EFSCORRUPTED;
error = xfs_alloc_read_agf(sc->sa.pag, sc->tp, 0, &sc->sa.agf_bp);
if (error)
goto out_pag;
while (agbno < agbno_next) {
xfs_extlen_t aglen;
bool crosslinked;
error = xreap_agextent_select(rs, agbno, agbno_next,
&crosslinked, &aglen);
if (error)
goto out_agf;
error = xreap_agextent_iter(rs, agbno, &aglen, crosslinked);
if (error)
goto out_agf;
if (xreap_want_defer_finish(rs)) {
/*
* Holds the AGF buffer across the deferred chain
* processing.
*/
error = xrep_defer_finish(sc);
if (error)
goto out_agf;
xreap_defer_finish_reset(rs);
} else if (xreap_want_roll(rs)) {
/*
* Hold the AGF buffer across the transaction roll so
* that we don't have to reattach it to the scrub
* context.
*/
xfs_trans_bhold(sc->tp, sc->sa.agf_bp);
error = xfs_trans_roll_inode(&sc->tp, sc->ip);
xfs_trans_bjoin(sc->tp, sc->sa.agf_bp);
if (error)
goto out_agf;
xreap_reset(rs);
}
agbno += aglen;
}
out_agf:
xfs_trans_brelse(sc->tp, sc->sa.agf_bp);
sc->sa.agf_bp = NULL;
out_pag:
xfs_perag_put(sc->sa.pag);
sc->sa.pag = NULL;
return error;
}
/*
* Dispose of every block of every fs metadata extent in the bitmap.
* Do not use this to dispose of the mappings in an ondisk inode fork.
*/
int
xrep_reap_fsblocks(
struct xfs_scrub *sc,
struct xfsb_bitmap *bitmap,
const struct xfs_owner_info *oinfo)
{
struct xreap_state rs = {
.sc = sc,
.oinfo = oinfo,
.resv = XFS_AG_RESV_NONE,
};
int error;
ASSERT(xfs_has_rmapbt(sc->mp));
ASSERT(sc->ip != NULL);
error = xfsb_bitmap_walk(bitmap, xreap_fsmeta_extent, &rs);
if (error)
return error;
if (xreap_dirty(&rs))
return xrep_defer_finish(sc);
return 0;
}
...@@ -6,7 +6,12 @@ ...@@ -6,7 +6,12 @@
#ifndef __XFS_SCRUB_REAP_H__ #ifndef __XFS_SCRUB_REAP_H__
#define __XFS_SCRUB_REAP_H__ #define __XFS_SCRUB_REAP_H__
struct xagb_bitmap;
struct xfsb_bitmap;
int xrep_reap_agblocks(struct xfs_scrub *sc, struct xagb_bitmap *bitmap, int xrep_reap_agblocks(struct xfs_scrub *sc, struct xagb_bitmap *bitmap,
const struct xfs_owner_info *oinfo, enum xfs_ag_resv_type type); const struct xfs_owner_info *oinfo, enum xfs_ag_resv_type type);
int xrep_reap_fsblocks(struct xfs_scrub *sc, struct xfsb_bitmap *bitmap,
const struct xfs_owner_info *oinfo);
#endif /* __XFS_SCRUB_REAP_H__ */ #endif /* __XFS_SCRUB_REAP_H__ */
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
#include "xfs_quota.h" #include "xfs_quota.h"
#include "xfs_qm.h" #include "xfs_qm.h"
#include "xfs_defer.h" #include "xfs_defer.h"
#include "xfs_errortag.h"
#include "xfs_error.h"
#include "scrub/scrub.h" #include "scrub/scrub.h"
#include "scrub/common.h" #include "scrub/common.h"
#include "scrub/trace.h" #include "scrub/trace.h"
...@@ -883,6 +885,34 @@ xrep_reinit_pagi( ...@@ -883,6 +885,34 @@ xrep_reinit_pagi(
return 0; return 0;
} }
/*
* Given an active reference to a perag structure, load AG headers and cursors.
* This should only be called to scan an AG while repairing file-based metadata.
*/
int
xrep_ag_init(
struct xfs_scrub *sc,
struct xfs_perag *pag,
struct xchk_ag *sa)
{
int error;
ASSERT(!sa->pag);
error = xfs_ialloc_read_agi(pag, sc->tp, &sa->agi_bp);
if (error)
return error;
error = xfs_alloc_read_agf(pag, sc->tp, 0, &sa->agf_bp);
if (error)
return error;
/* Grab our own passive reference from the caller's ref. */
sa->pag = xfs_perag_hold(pag);
xrep_ag_btcur_init(sc, sa);
return 0;
}
/* Reinitialize the per-AG block reservation for the AG we just fixed. */ /* Reinitialize the per-AG block reservation for the AG we just fixed. */
int int
xrep_reset_perag_resv( xrep_reset_perag_resv(
...@@ -912,3 +942,23 @@ xrep_reset_perag_resv( ...@@ -912,3 +942,23 @@ xrep_reset_perag_resv(
out: out:
return error; return error;
} }
/* Decide if we are going to call the repair function for a scrub type. */
bool
xrep_will_attempt(
struct xfs_scrub *sc)
{
/* Userspace asked us to rebuild the structure regardless. */
if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD)
return true;
/* Let debug users force us into the repair routines. */
if (XFS_TEST_ERROR(false, sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
return true;
/* Metadata is corrupt or failed cross-referencing. */
if (xchk_needs_repair(sc->sm))
return true;
return false;
}
...@@ -28,6 +28,7 @@ static inline int xrep_notsupported(struct xfs_scrub *sc) ...@@ -28,6 +28,7 @@ static inline int xrep_notsupported(struct xfs_scrub *sc)
/* Repair helpers */ /* Repair helpers */
int xrep_attempt(struct xfs_scrub *sc, struct xchk_stats_run *run); int xrep_attempt(struct xfs_scrub *sc, struct xchk_stats_run *run);
bool xrep_will_attempt(struct xfs_scrub *sc);
void xrep_failure(struct xfs_mount *mp); void xrep_failure(struct xfs_mount *mp);
int xrep_roll_ag_trans(struct xfs_scrub *sc); int xrep_roll_ag_trans(struct xfs_scrub *sc);
int xrep_roll_trans(struct xfs_scrub *sc); int xrep_roll_trans(struct xfs_scrub *sc);
...@@ -48,6 +49,7 @@ xrep_trans_commit( ...@@ -48,6 +49,7 @@ xrep_trans_commit(
struct xbitmap; struct xbitmap;
struct xagb_bitmap; struct xagb_bitmap;
struct xfsb_bitmap;
int xrep_fix_freelist(struct xfs_scrub *sc, bool can_shrink); int xrep_fix_freelist(struct xfs_scrub *sc, bool can_shrink);
...@@ -88,6 +90,8 @@ struct xfs_imap; ...@@ -88,6 +90,8 @@ struct xfs_imap;
int xrep_setup_inode(struct xfs_scrub *sc, const struct xfs_imap *imap); int xrep_setup_inode(struct xfs_scrub *sc, const struct xfs_imap *imap);
void xrep_ag_btcur_init(struct xfs_scrub *sc, struct xchk_ag *sa); void xrep_ag_btcur_init(struct xfs_scrub *sc, struct xchk_ag *sa);
int xrep_ag_init(struct xfs_scrub *sc, struct xfs_perag *pag,
struct xchk_ag *sa);
/* Metadata revalidators */ /* Metadata revalidators */
...@@ -105,6 +109,9 @@ int xrep_allocbt(struct xfs_scrub *sc); ...@@ -105,6 +109,9 @@ int xrep_allocbt(struct xfs_scrub *sc);
int xrep_iallocbt(struct xfs_scrub *sc); int xrep_iallocbt(struct xfs_scrub *sc);
int xrep_refcountbt(struct xfs_scrub *sc); int xrep_refcountbt(struct xfs_scrub *sc);
int xrep_inode(struct xfs_scrub *sc); int xrep_inode(struct xfs_scrub *sc);
int xrep_bmap_data(struct xfs_scrub *sc);
int xrep_bmap_attr(struct xfs_scrub *sc);
int xrep_bmap_cow(struct xfs_scrub *sc);
int xrep_reinit_pagf(struct xfs_scrub *sc); int xrep_reinit_pagf(struct xfs_scrub *sc);
int xrep_reinit_pagi(struct xfs_scrub *sc); int xrep_reinit_pagi(struct xfs_scrub *sc);
...@@ -112,6 +119,7 @@ int xrep_reinit_pagi(struct xfs_scrub *sc); ...@@ -112,6 +119,7 @@ int xrep_reinit_pagi(struct xfs_scrub *sc);
#else #else
#define xrep_ino_dqattach(sc) (0) #define xrep_ino_dqattach(sc) (0)
#define xrep_will_attempt(sc) (false)
static inline int static inline int
xrep_attempt( xrep_attempt(
...@@ -164,6 +172,9 @@ xrep_setup_nothing( ...@@ -164,6 +172,9 @@ xrep_setup_nothing(
#define xrep_iallocbt xrep_notsupported #define xrep_iallocbt xrep_notsupported
#define xrep_refcountbt xrep_notsupported #define xrep_refcountbt xrep_notsupported
#define xrep_inode xrep_notsupported #define xrep_inode xrep_notsupported
#define xrep_bmap_data xrep_notsupported
#define xrep_bmap_attr xrep_notsupported
#define xrep_bmap_cow xrep_notsupported
#endif /* CONFIG_XFS_ONLINE_REPAIR */ #endif /* CONFIG_XFS_ONLINE_REPAIR */
......
...@@ -14,8 +14,6 @@ ...@@ -14,8 +14,6 @@
#include "xfs_inode.h" #include "xfs_inode.h"
#include "xfs_quota.h" #include "xfs_quota.h"
#include "xfs_qm.h" #include "xfs_qm.h"
#include "xfs_errortag.h"
#include "xfs_error.h"
#include "xfs_scrub.h" #include "xfs_scrub.h"
#include "scrub/scrub.h" #include "scrub/scrub.h"
#include "scrub/common.h" #include "scrub/common.h"
...@@ -288,19 +286,19 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { ...@@ -288,19 +286,19 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.type = ST_INODE, .type = ST_INODE,
.setup = xchk_setup_inode_bmap, .setup = xchk_setup_inode_bmap,
.scrub = xchk_bmap_data, .scrub = xchk_bmap_data,
.repair = xrep_notsupported, .repair = xrep_bmap_data,
}, },
[XFS_SCRUB_TYPE_BMBTA] = { /* inode attr fork */ [XFS_SCRUB_TYPE_BMBTA] = { /* inode attr fork */
.type = ST_INODE, .type = ST_INODE,
.setup = xchk_setup_inode_bmap, .setup = xchk_setup_inode_bmap,
.scrub = xchk_bmap_attr, .scrub = xchk_bmap_attr,
.repair = xrep_notsupported, .repair = xrep_bmap_attr,
}, },
[XFS_SCRUB_TYPE_BMBTC] = { /* inode CoW fork */ [XFS_SCRUB_TYPE_BMBTC] = { /* inode CoW fork */
.type = ST_INODE, .type = ST_INODE,
.setup = xchk_setup_inode_bmap, .setup = xchk_setup_inode_bmap,
.scrub = xchk_bmap_cow, .scrub = xchk_bmap_cow,
.repair = xrep_notsupported, .repair = xrep_bmap_cow,
}, },
[XFS_SCRUB_TYPE_DIR] = { /* directory */ [XFS_SCRUB_TYPE_DIR] = { /* directory */
.type = ST_INODE, .type = ST_INODE,
...@@ -550,21 +548,11 @@ xfs_scrub_metadata( ...@@ -550,21 +548,11 @@ xfs_scrub_metadata(
xchk_update_health(sc); xchk_update_health(sc);
if (xchk_could_repair(sc)) { if (xchk_could_repair(sc)) {
bool needs_fix = xchk_needs_repair(sc->sm);
/* Userspace asked us to rebuild the structure regardless. */
if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD)
needs_fix = true;
/* Let debug users force us into the repair routines. */
if (XFS_TEST_ERROR(needs_fix, mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
needs_fix = true;
/* /*
* If userspace asked for a repair but it wasn't necessary, * If userspace asked for a repair but it wasn't necessary,
* report that back to userspace. * report that back to userspace.
*/ */
if (!needs_fix) { if (!xrep_will_attempt(sc)) {
sc->sm->sm_flags |= XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED; sc->sm->sm_flags |= XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED;
goto out_nofix; goto out_nofix;
} }
......
...@@ -1175,7 +1175,7 @@ DEFINE_EVENT(xrep_rmap_class, name, \ ...@@ -1175,7 +1175,7 @@ DEFINE_EVENT(xrep_rmap_class, name, \
TP_ARGS(mp, agno, agbno, len, owner, offset, flags)) TP_ARGS(mp, agno, agbno, len, owner, offset, flags))
DEFINE_REPAIR_RMAP_EVENT(xrep_ibt_walk_rmap); DEFINE_REPAIR_RMAP_EVENT(xrep_ibt_walk_rmap);
DEFINE_REPAIR_RMAP_EVENT(xrep_rmap_extent_fn); DEFINE_REPAIR_RMAP_EVENT(xrep_rmap_extent_fn);
DEFINE_REPAIR_RMAP_EVENT(xrep_bmap_extent_fn); DEFINE_REPAIR_RMAP_EVENT(xrep_bmap_walk_rmap);
TRACE_EVENT(xrep_abt_found, TRACE_EVENT(xrep_abt_found,
TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
...@@ -1260,6 +1260,38 @@ TRACE_EVENT(xrep_refc_found, ...@@ -1260,6 +1260,38 @@ TRACE_EVENT(xrep_refc_found,
__entry->refcount) __entry->refcount)
) )
TRACE_EVENT(xrep_bmap_found,
TP_PROTO(struct xfs_inode *ip, int whichfork,
struct xfs_bmbt_irec *irec),
TP_ARGS(ip, whichfork, irec),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(int, whichfork)
__field(xfs_fileoff_t, lblk)
__field(xfs_filblks_t, len)
__field(xfs_fsblock_t, pblk)
__field(int, state)
),
TP_fast_assign(
__entry->dev = VFS_I(ip)->i_sb->s_dev;
__entry->ino = ip->i_ino;
__entry->whichfork = whichfork;
__entry->lblk = irec->br_startoff;
__entry->len = irec->br_blockcount;
__entry->pblk = irec->br_startblock;
__entry->state = irec->br_state;
),
TP_printk("dev %d:%d ino 0x%llx whichfork %s fileoff 0x%llx fsbcount 0x%llx startblock 0x%llx state %d",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__print_symbolic(__entry->whichfork, XFS_WHICHFORK_STRINGS),
__entry->lblk,
__entry->len,
__entry->pblk,
__entry->state)
);
TRACE_EVENT(xrep_findroot_block, TRACE_EVENT(xrep_findroot_block,
TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agblock_t agbno, TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agblock_t agbno,
uint32_t magic, uint16_t level), uint32_t magic, uint16_t level),
...@@ -1564,6 +1596,90 @@ TRACE_EVENT(xrep_dinode_count_rmaps, ...@@ -1564,6 +1596,90 @@ TRACE_EVENT(xrep_dinode_count_rmaps,
__entry->attr_extents) __entry->attr_extents)
); );
TRACE_EVENT(xrep_cow_mark_file_range,
TP_PROTO(struct xfs_inode *ip, xfs_fsblock_t startblock,
xfs_fileoff_t startoff, xfs_filblks_t blockcount),
TP_ARGS(ip, startblock, startoff, blockcount),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_fsblock_t, startblock)
__field(xfs_fileoff_t, startoff)
__field(xfs_filblks_t, blockcount)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
__entry->startoff = startoff;
__entry->startblock = startblock;
__entry->blockcount = blockcount;
),
TP_printk("dev %d:%d ino 0x%llx fileoff 0x%llx startblock 0x%llx fsbcount 0x%llx",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->startoff,
__entry->startblock,
__entry->blockcount)
);
TRACE_EVENT(xrep_cow_replace_mapping,
TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *irec,
xfs_fsblock_t new_startblock, xfs_extlen_t new_blockcount),
TP_ARGS(ip, irec, new_startblock, new_blockcount),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_fsblock_t, startblock)
__field(xfs_fileoff_t, startoff)
__field(xfs_filblks_t, blockcount)
__field(xfs_exntst_t, state)
__field(xfs_fsblock_t, new_startblock)
__field(xfs_extlen_t, new_blockcount)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
__entry->startoff = irec->br_startoff;
__entry->startblock = irec->br_startblock;
__entry->blockcount = irec->br_blockcount;
__entry->state = irec->br_state;
__entry->new_startblock = new_startblock;
__entry->new_blockcount = new_blockcount;
),
TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startblock 0x%llx new_fsbcount 0x%x",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->startoff,
__entry->startblock,
__entry->blockcount,
__entry->state,
__entry->new_startblock,
__entry->new_blockcount)
);
TRACE_EVENT(xrep_cow_free_staging,
TP_PROTO(struct xfs_perag *pag, xfs_agblock_t agbno,
xfs_extlen_t blockcount),
TP_ARGS(pag, agbno, blockcount),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_agnumber_t, agno)
__field(xfs_agblock_t, agbno)
__field(xfs_extlen_t, blockcount)
),
TP_fast_assign(
__entry->dev = pag->pag_mount->m_super->s_dev;
__entry->agno = pag->pag_agno;
__entry->agbno = agbno;
__entry->blockcount = blockcount;
),
TP_printk("dev %d:%d agno 0x%x agbno 0x%x fsbcount 0x%x",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno,
__entry->agbno,
__entry->blockcount)
);
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */ #endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
#endif /* _TRACE_XFS_SCRUB_TRACE_H */ #endif /* _TRACE_XFS_SCRUB_TRACE_H */
......
...@@ -1236,6 +1236,68 @@ xfs_trans_alloc_inode( ...@@ -1236,6 +1236,68 @@ xfs_trans_alloc_inode(
return error; return error;
} }
/*
* Try to reserve more blocks for a transaction.
*
* This is for callers that need to attach resources to a transaction, scan
* those resources to determine the space reservation requirements, and then
* modify the attached resources. In other words, online repair. This can
* fail due to ENOSPC, so the caller must be able to cancel the transaction
* without shutting down the fs.
*/
int
xfs_trans_reserve_more(
struct xfs_trans *tp,
unsigned int blocks,
unsigned int rtextents)
{
struct xfs_trans_res resv = { };
return xfs_trans_reserve(tp, &resv, blocks, rtextents);
}
/*
* Try to reserve more blocks and file quota for a transaction. Same
* conditions of usage as xfs_trans_reserve_more.
*/
int
xfs_trans_reserve_more_inode(
struct xfs_trans *tp,
struct xfs_inode *ip,
unsigned int dblocks,
unsigned int rblocks,
bool force_quota)
{
struct xfs_trans_res resv = { };
struct xfs_mount *mp = ip->i_mount;
unsigned int rtx = xfs_extlen_to_rtxlen(mp, rblocks);
int error;
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
error = xfs_trans_reserve(tp, &resv, dblocks, rtx);
if (error)
return error;
if (!XFS_IS_QUOTA_ON(mp) || xfs_is_quota_inode(&mp->m_sb, ip->i_ino))
return 0;
if (tp->t_flags & XFS_TRANS_RESERVE)
force_quota = true;
error = xfs_trans_reserve_quota_nblks(tp, ip, dblocks, rblocks,
force_quota);
if (!error)
return 0;
/* Quota failed, give back the new reservation. */
xfs_mod_fdblocks(mp, dblocks, tp->t_flags & XFS_TRANS_RESERVE);
tp->t_blk_res -= dblocks;
xfs_mod_frextents(mp, rtx);
tp->t_rtx_res -= rtx;
return error;
}
/* /*
* Allocate an transaction in preparation for inode creation by reserving quota * Allocate an transaction in preparation for inode creation by reserving quota
* against the given dquots. Callers are not required to hold any inode locks. * against the given dquots. Callers are not required to hold any inode locks.
......
...@@ -164,6 +164,8 @@ typedef struct xfs_trans { ...@@ -164,6 +164,8 @@ typedef struct xfs_trans {
int xfs_trans_alloc(struct xfs_mount *mp, struct xfs_trans_res *resp, int xfs_trans_alloc(struct xfs_mount *mp, struct xfs_trans_res *resp,
uint blocks, uint rtextents, uint flags, uint blocks, uint rtextents, uint flags,
struct xfs_trans **tpp); struct xfs_trans **tpp);
int xfs_trans_reserve_more(struct xfs_trans *tp,
unsigned int blocks, unsigned int rtextents);
int xfs_trans_alloc_empty(struct xfs_mount *mp, int xfs_trans_alloc_empty(struct xfs_mount *mp,
struct xfs_trans **tpp); struct xfs_trans **tpp);
void xfs_trans_mod_sb(xfs_trans_t *, uint, int64_t); void xfs_trans_mod_sb(xfs_trans_t *, uint, int64_t);
...@@ -248,6 +250,8 @@ struct xfs_dquot; ...@@ -248,6 +250,8 @@ struct xfs_dquot;
int xfs_trans_alloc_inode(struct xfs_inode *ip, struct xfs_trans_res *resv, int xfs_trans_alloc_inode(struct xfs_inode *ip, struct xfs_trans_res *resv,
unsigned int dblocks, unsigned int rblocks, bool force, unsigned int dblocks, unsigned int rblocks, bool force,
struct xfs_trans **tpp); struct xfs_trans **tpp);
int xfs_trans_reserve_more_inode(struct xfs_trans *tp, struct xfs_inode *ip,
unsigned int dblocks, unsigned int rblocks, bool force_quota);
int xfs_trans_alloc_icreate(struct xfs_mount *mp, struct xfs_trans_res *resv, int xfs_trans_alloc_icreate(struct xfs_mount *mp, struct xfs_trans_res *resv,
struct xfs_dquot *udqp, struct xfs_dquot *gdqp, struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
struct xfs_dquot *pdqp, unsigned int dblocks, struct xfs_dquot *pdqp, unsigned int dblocks,
......
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