Commit 0e5ab8dd authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'xfs-5.19-for-linus-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull more xfs updates from Dave Chinner:
 "This update is largely bug fixes and cleanups for all the code merged
  in the first pull request. The majority of them are to the new logged
  attribute code, but there are also a couple of fixes for other log
  recovery and memory leaks that have recently been found.

  Summary:

   - fix refcount leak in xfs_ifree()

   - fix xfs_buf_cancel structure leaks in log recovery

   - fix dquot leak after failed quota check

   - fix a couple of problematic ASSERTS

   - fix small aim7 perf regression in from new btree sibling validation

   - clean up log incompat feature marking for new logged attribute
     feature

   - disallow logged attributes on legacy V4 filesystem formats.

   - fix da state leak when freeing attr intents

   - improve validation of the attr log items in recovery

   - use slab caches for commonly used attr structures

   - fix leaks of attr name/value buffer and reduce copying overhead
     during intent logging

   - remove some dead debug code from log recovery"

* tag 'xfs-5.19-for-linus-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: (33 commits)
  xfs: fix xfs_ifree() error handling to not leak perag ref
  xfs: move xfs_attr_use_log_assist usage out of libxfs
  xfs: move xfs_attr_use_log_assist out of xfs_log.c
  xfs: warn about LARP once per mount
  xfs: implement per-mount warnings for scrub and shrink usage
  xfs: don't log every time we clear the log incompat flags
  xfs: convert buf_cancel_table allocation to kmalloc_array
  xfs: don't leak xfs_buf_cancel structures when recovery fails
  xfs: refactor buffer cancellation table allocation
  xfs: don't leak btree cursor when insrec fails after a split
  xfs: purge dquots after inode walk fails during quotacheck
  xfs: assert in xfs_btree_del_cursor should take into account error
  xfs: don't assert fail on perag references on teardown
  xfs: avoid unnecessary runtime sibling pointer endian conversions
  xfs: share xattr name and value buffers when logging xattr updates
  xfs: do not use logged xattr updates on V4 filesystems
  xfs: Remove duplicate include
  xfs: reduce IOCB_NOWAIT judgment for retry exclusive unaligned DIO
  xfs: Remove dead code
  xfs: fix typo in comment
  ...
parents 54eb8462 7146bda7
......@@ -173,7 +173,6 @@ __xfs_free_perag(
struct xfs_perag *pag = container_of(head, struct xfs_perag, rcu_head);
ASSERT(!delayed_work_pending(&pag->pag_blockgc_work));
ASSERT(atomic_read(&pag->pag_ref) == 0);
kmem_free(pag);
}
......@@ -192,7 +191,7 @@ xfs_free_perag(
pag = radix_tree_delete(&mp->m_perag_tree, agno);
spin_unlock(&mp->m_perag_lock);
ASSERT(pag);
ASSERT(atomic_read(&pag->pag_ref) == 0);
XFS_IS_CORRUPT(pag->pag_mount, atomic_read(&pag->pag_ref) != 0);
cancel_delayed_work_sync(&pag->pag_blockgc_work);
xfs_iunlink_destroy(pag);
......
This diff is collapsed.
......@@ -31,7 +31,8 @@ struct xfs_attr_list_context;
static inline bool xfs_has_larp(struct xfs_mount *mp)
{
#ifdef DEBUG
return xfs_globals.larp;
/* Logged xattrs require a V5 super for log_incompat */
return xfs_has_crc(mp) && xfs_globals.larp;
#else
return false;
#endif
......@@ -434,7 +435,7 @@ struct xfs_attr_list_context {
*/
/*
* Enum values for xfs_attr_item.xattri_da_state
* Enum values for xfs_attr_intent.xattri_da_state
*
* These values are used by delayed attribute operations to keep track of where
* they were before they returned -EAGAIN. A return code of -EAGAIN signals the
......@@ -501,44 +502,46 @@ enum xfs_delattr_state {
{ XFS_DAS_NODE_REMOVE_ATTR, "XFS_DAS_NODE_REMOVE_ATTR" }, \
{ XFS_DAS_DONE, "XFS_DAS_DONE" }
/*
* Defines for xfs_attr_item.xattri_flags
*/
#define XFS_DAC_LEAF_ADDNAME_INIT 0x01 /* xfs_attr_leaf_addname init*/
struct xfs_attri_log_nameval;
/*
* Context used for keeping track of delayed attribute operations
*/
struct xfs_attr_item {
struct xfs_attr_intent {
/*
* used to log this item to an intent containing a list of attrs to
* commit later
*/
struct list_head xattri_list;
/* Used in xfs_attr_node_removename to roll through removing blocks */
struct xfs_da_state *xattri_da_state;
struct xfs_da_args *xattri_da_args;
/*
* Shared buffer containing the attr name and value so that the logging
* code can share large memory buffers between log items.
*/
struct xfs_attri_log_nameval *xattri_nameval;
/*
* Used by xfs_attr_set to hold a leaf buffer across a transaction roll
*/
struct xfs_buf *xattri_leaf_bp;
/* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
struct xfs_bmbt_irec xattri_map;
xfs_dablk_t xattri_lblkno;
int xattri_blkcnt;
/* Used in xfs_attr_node_removename to roll through removing blocks */
struct xfs_da_state *xattri_da_state;
/* Used to keep track of current state of delayed operation */
unsigned int xattri_flags;
enum xfs_delattr_state xattri_dela_state;
/*
* Attr operation being performed - XFS_ATTR_OP_FLAGS_*
* Attr operation being performed - XFS_ATTRI_OP_FLAGS_*
*/
unsigned int xattri_op_flags;
/*
* used to log this item to an intent containing a list of attrs to
* commit later
*/
struct list_head xattri_list;
/* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
xfs_dablk_t xattri_lblkno;
int xattri_blkcnt;
struct xfs_bmbt_irec xattri_map;
};
......@@ -557,21 +560,13 @@ bool xfs_attr_is_leaf(struct xfs_inode *ip);
int xfs_attr_get_ilocked(struct xfs_da_args *args);
int xfs_attr_get(struct xfs_da_args *args);
int xfs_attr_set(struct xfs_da_args *args);
int xfs_attr_set_iter(struct xfs_attr_item *attr);
int xfs_attr_remove_iter(struct xfs_attr_item *attr);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
bool xfs_attr_namecheck(const void *name, size_t length);
int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
unsigned int *total);
extern struct kmem_cache *xfs_attri_cache;
extern struct kmem_cache *xfs_attrd_cache;
int __init xfs_attri_init_cache(void);
void xfs_attri_destroy_cache(void);
int __init xfs_attrd_init_cache(void);
void xfs_attrd_destroy_cache(void);
/*
* Check to see if the attr should be upgraded from non-existent or shortform to
* single-leaf-block attribute list.
......@@ -634,4 +629,8 @@ xfs_attr_init_replace_state(struct xfs_da_args *args)
return xfs_attr_init_add_state(args);
}
extern struct kmem_cache *xfs_attr_intent_cache;
int __init xfs_attr_intent_init_cache(void);
void xfs_attr_intent_destroy_cache(void);
#endif /* __XFS_ATTR_H__ */
......@@ -568,7 +568,7 @@ xfs_attr_rmtval_stale(
*/
int
xfs_attr_rmtval_find_space(
struct xfs_attr_item *attr)
struct xfs_attr_intent *attr)
{
struct xfs_da_args *args = attr->xattri_da_args;
struct xfs_bmbt_irec *map = &attr->xattri_map;
......@@ -598,7 +598,7 @@ xfs_attr_rmtval_find_space(
*/
int
xfs_attr_rmtval_set_blk(
struct xfs_attr_item *attr)
struct xfs_attr_intent *attr)
{
struct xfs_da_args *args = attr->xattri_da_args;
struct xfs_inode *dp = args->dp;
......@@ -674,7 +674,7 @@ xfs_attr_rmtval_invalidate(
*/
int
xfs_attr_rmtval_remove(
struct xfs_attr_item *attr)
struct xfs_attr_intent *attr)
{
struct xfs_da_args *args = attr->xattri_da_args;
int error, done;
......
......@@ -12,9 +12,9 @@ int xfs_attr_rmtval_get(struct xfs_da_args *args);
int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
xfs_buf_flags_t incore_flags);
int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
int xfs_attr_rmtval_remove(struct xfs_attr_item *attr);
int xfs_attr_rmtval_remove(struct xfs_attr_intent *attr);
int xfs_attr_rmt_find_hole(struct xfs_da_args *args);
int xfs_attr_rmtval_set_value(struct xfs_da_args *args);
int xfs_attr_rmtval_set_blk(struct xfs_attr_item *attr);
int xfs_attr_rmtval_find_space(struct xfs_attr_item *attr);
int xfs_attr_rmtval_set_blk(struct xfs_attr_intent *attr);
int xfs_attr_rmtval_find_space(struct xfs_attr_intent *attr);
#endif /* __XFS_ATTR_REMOTE_H__ */
......@@ -51,16 +51,31 @@ xfs_btree_magic(
return magic;
}
static xfs_failaddr_t
/*
* These sibling pointer checks are optimised for null sibling pointers. This
* happens a lot, and we don't need to byte swap at runtime if the sibling
* pointer is NULL.
*
* These are explicitly marked at inline because the cost of calling them as
* functions instead of inlining them is about 36 bytes extra code per call site
* on x86-64. Yes, gcc-11 fails to inline them, and explicit inlining of these
* two sibling check functions reduces the compiled code size by over 300
* bytes.
*/
static inline xfs_failaddr_t
xfs_btree_check_lblock_siblings(
struct xfs_mount *mp,
struct xfs_btree_cur *cur,
int level,
xfs_fsblock_t fsb,
xfs_fsblock_t sibling)
__be64 dsibling)
{
if (sibling == NULLFSBLOCK)
xfs_fsblock_t sibling;
if (dsibling == cpu_to_be64(NULLFSBLOCK))
return NULL;
sibling = be64_to_cpu(dsibling);
if (sibling == fsb)
return __this_address;
if (level >= 0) {
......@@ -74,17 +89,21 @@ xfs_btree_check_lblock_siblings(
return NULL;
}
static xfs_failaddr_t
static inline xfs_failaddr_t
xfs_btree_check_sblock_siblings(
struct xfs_mount *mp,
struct xfs_btree_cur *cur,
int level,
xfs_agnumber_t agno,
xfs_agblock_t agbno,
xfs_agblock_t sibling)
__be32 dsibling)
{
if (sibling == NULLAGBLOCK)
xfs_agblock_t sibling;
if (dsibling == cpu_to_be32(NULLAGBLOCK))
return NULL;
sibling = be32_to_cpu(dsibling);
if (sibling == agbno)
return __this_address;
if (level >= 0) {
......@@ -136,10 +155,10 @@ __xfs_btree_check_lblock(
fsb = XFS_DADDR_TO_FSB(mp, xfs_buf_daddr(bp));
fa = xfs_btree_check_lblock_siblings(mp, cur, level, fsb,
be64_to_cpu(block->bb_u.l.bb_leftsib));
block->bb_u.l.bb_leftsib);
if (!fa)
fa = xfs_btree_check_lblock_siblings(mp, cur, level, fsb,
be64_to_cpu(block->bb_u.l.bb_rightsib));
block->bb_u.l.bb_rightsib);
return fa;
}
......@@ -204,10 +223,10 @@ __xfs_btree_check_sblock(
}
fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno, agbno,
be32_to_cpu(block->bb_u.s.bb_leftsib));
block->bb_u.s.bb_leftsib);
if (!fa)
fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno,
agbno, be32_to_cpu(block->bb_u.s.bb_rightsib));
agbno, block->bb_u.s.bb_rightsib);
return fa;
}
......@@ -426,8 +445,14 @@ xfs_btree_del_cursor(
break;
}
/*
* If we are doing a BMBT update, the number of unaccounted blocks
* allocated during this cursor life time should be zero. If it's not
* zero, then we should be shut down or on our way to shutdown due to
* cancelling a dirty transaction on error.
*/
ASSERT(cur->bc_btnum != XFS_BTNUM_BMAP || cur->bc_ino.allocated == 0 ||
xfs_is_shutdown(cur->bc_mp));
xfs_is_shutdown(cur->bc_mp) || error != 0);
if (unlikely(cur->bc_flags & XFS_BTREE_STAGING))
kmem_free(cur->bc_ops);
if (!(cur->bc_flags & XFS_BTREE_LONG_PTRS) && cur->bc_ag.pag)
......@@ -3247,7 +3272,7 @@ xfs_btree_insrec(
struct xfs_btree_block *block; /* btree block */
struct xfs_buf *bp; /* buffer for block */
union xfs_btree_ptr nptr; /* new block ptr */
struct xfs_btree_cur *ncur; /* new btree cursor */
struct xfs_btree_cur *ncur = NULL; /* new btree cursor */
union xfs_btree_key nkey; /* new block key */
union xfs_btree_key *lkey;
int optr; /* old key/record index */
......@@ -3327,7 +3352,7 @@ xfs_btree_insrec(
#ifdef DEBUG
error = xfs_btree_check_block(cur, block, level, bp);
if (error)
return error;
goto error0;
#endif
/*
......@@ -3347,7 +3372,7 @@ xfs_btree_insrec(
for (i = numrecs - ptr; i >= 0; i--) {
error = xfs_btree_debug_check_ptr(cur, pp, i, level);
if (error)
return error;
goto error0;
}
xfs_btree_shift_keys(cur, kp, 1, numrecs - ptr + 1);
......@@ -3432,6 +3457,8 @@ xfs_btree_insrec(
return 0;
error0:
if (ncur)
xfs_btree_del_cursor(ncur, error);
return error;
}
......@@ -4523,10 +4550,10 @@ xfs_btree_lblock_verify(
/* sibling pointer verification */
fsb = XFS_DADDR_TO_FSB(mp, xfs_buf_daddr(bp));
fa = xfs_btree_check_lblock_siblings(mp, NULL, -1, fsb,
be64_to_cpu(block->bb_u.l.bb_leftsib));
block->bb_u.l.bb_leftsib);
if (!fa)
fa = xfs_btree_check_lblock_siblings(mp, NULL, -1, fsb,
be64_to_cpu(block->bb_u.l.bb_rightsib));
block->bb_u.l.bb_rightsib);
return fa;
}
......@@ -4580,10 +4607,10 @@ xfs_btree_sblock_verify(
agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp));
agbno = xfs_daddr_to_agbno(mp, xfs_buf_daddr(bp));
fa = xfs_btree_check_sblock_siblings(mp, NULL, -1, agno, agbno,
be32_to_cpu(block->bb_u.s.bb_leftsib));
block->bb_u.s.bb_leftsib);
if (!fa)
fa = xfs_btree_check_sblock_siblings(mp, NULL, -1, agno, agbno,
be32_to_cpu(block->bb_u.s.bb_rightsib));
block->bb_u.s.bb_rightsib);
return fa;
}
......
......@@ -117,6 +117,17 @@ xfs_da_state_free(xfs_da_state_t *state)
kmem_cache_free(xfs_da_state_cache, state);
}
void
xfs_da_state_reset(
struct xfs_da_state *state,
struct xfs_da_args *args)
{
xfs_da_state_kill_altpath(state);
memset(state, 0, sizeof(struct xfs_da_state));
state->args = args;
state->mp = state->args->dp->i_mount;
}
static inline int xfs_dabuf_nfsb(struct xfs_mount *mp, int whichfork)
{
if (whichfork == XFS_DATA_FORK)
......
......@@ -225,6 +225,7 @@ enum xfs_dacmp xfs_da_compname(struct xfs_da_args *args,
struct xfs_da_state *xfs_da_state_alloc(struct xfs_da_args *args);
void xfs_da_state_free(xfs_da_state_t *state);
void xfs_da_state_reset(struct xfs_da_state *state, struct xfs_da_args *args);
void xfs_da3_node_hdr_from_disk(struct xfs_mount *mp,
struct xfs_da3_icnode_hdr *to, struct xfs_da_intnode *from);
......
......@@ -191,35 +191,56 @@ static const struct xfs_defer_op_type *defer_op_types[] = {
[XFS_DEFER_OPS_TYPE_ATTR] = &xfs_attr_defer_type,
};
static bool
/*
* Ensure there's a log intent item associated with this deferred work item if
* the operation must be restarted on crash. Returns 1 if there's a log item;
* 0 if there isn't; or a negative errno.
*/
static int
xfs_defer_create_intent(
struct xfs_trans *tp,
struct xfs_defer_pending *dfp,
bool sort)
{
const struct xfs_defer_op_type *ops = defer_op_types[dfp->dfp_type];
struct xfs_log_item *lip;
if (dfp->dfp_intent)
return 1;
if (!dfp->dfp_intent)
dfp->dfp_intent = ops->create_intent(tp, &dfp->dfp_work,
dfp->dfp_count, sort);
return dfp->dfp_intent != NULL;
lip = ops->create_intent(tp, &dfp->dfp_work, dfp->dfp_count, sort);
if (!lip)
return 0;
if (IS_ERR(lip))
return PTR_ERR(lip);
dfp->dfp_intent = lip;
return 1;
}
/*
* For each pending item in the intake list, log its intent item and the
* associated extents, then add the entire intake list to the end of
* the pending list.
*
* Returns 1 if at least one log item was associated with the deferred work;
* 0 if there are no log items; or a negative errno.
*/
static bool
static int
xfs_defer_create_intents(
struct xfs_trans *tp)
{
struct xfs_defer_pending *dfp;
bool ret = false;
int ret = 0;
list_for_each_entry(dfp, &tp->t_dfops, dfp_list) {
int ret2;
trace_xfs_defer_create_intent(tp->t_mountp, dfp);
ret |= xfs_defer_create_intent(tp, dfp, true);
ret2 = xfs_defer_create_intent(tp, dfp, true);
if (ret2 < 0)
return ret2;
ret |= ret2;
}
return ret;
}
......@@ -457,6 +478,8 @@ xfs_defer_finish_one(
dfp->dfp_count--;
error = ops->finish_item(tp, dfp->dfp_done, li, &state);
if (error == -EAGAIN) {
int ret;
/*
* Caller wants a fresh transaction; put the work item
* back on the list and log a new log intent item to
......@@ -467,7 +490,9 @@ xfs_defer_finish_one(
dfp->dfp_count++;
dfp->dfp_done = NULL;
dfp->dfp_intent = NULL;
xfs_defer_create_intent(tp, dfp, false);
ret = xfs_defer_create_intent(tp, dfp, false);
if (ret < 0)
error = ret;
}
if (error)
......@@ -514,10 +539,14 @@ xfs_defer_finish_noroll(
* of time that any one intent item can stick around in memory,
* pinning the log tail.
*/
bool has_intents = xfs_defer_create_intents(*tp);
int has_intents = xfs_defer_create_intents(*tp);
list_splice_init(&(*tp)->t_dfops, &dop_pending);
if (has_intents < 0) {
error = has_intents;
goto out_shutdown;
}
if (has_intents || dfp) {
error = xfs_defer_trans_roll(tp);
if (error)
......@@ -676,13 +705,15 @@ xfs_defer_ops_capture(
if (list_empty(&tp->t_dfops))
return NULL;
error = xfs_defer_create_intents(tp);
if (error < 0)
return ERR_PTR(error);
/* Create an object to capture the defer ops. */
dfc = kmem_zalloc(sizeof(*dfc), KM_NOFS);
INIT_LIST_HEAD(&dfc->dfc_list);
INIT_LIST_HEAD(&dfc->dfc_dfops);
xfs_defer_create_intents(tp);
/* Move the dfops chain and transaction state to the capture struct. */
list_splice_init(&tp->t_dfops, &dfc->dfc_dfops);
dfc->dfc_tpflags = tp->t_flags & XFS_TRANS_LOWMODE;
......@@ -759,6 +790,10 @@ xfs_defer_ops_capture_and_commit(
/* If we don't capture anything, commit transaction and exit. */
dfc = xfs_defer_ops_capture(tp);
if (IS_ERR(dfc)) {
xfs_trans_cancel(tp);
return PTR_ERR(dfc);
}
if (!dfc)
return xfs_trans_commit(tp);
......@@ -873,10 +908,7 @@ xfs_defer_init_item_caches(void)
error = xfs_extfree_intent_init_cache();
if (error)
goto err;
error = xfs_attri_init_cache();
if (error)
goto err;
error = xfs_attrd_init_cache();
error = xfs_attr_intent_init_cache();
if (error)
goto err;
return 0;
......@@ -889,8 +921,7 @@ xfs_defer_init_item_caches(void)
void
xfs_defer_destroy_item_caches(void)
{
xfs_attri_destroy_cache();
xfs_attrd_destroy_cache();
xfs_attr_intent_destroy_cache();
xfs_extfree_intent_destroy_cache();
xfs_bmap_intent_destroy_cache();
xfs_refcount_intent_destroy_cache();
......
......@@ -906,10 +906,18 @@ struct xfs_icreate_log {
* Flags for deferred attribute operations.
* Upper bits are flags, lower byte is type code
*/
#define XFS_ATTR_OP_FLAGS_SET 1 /* Set the attribute */
#define XFS_ATTR_OP_FLAGS_REMOVE 2 /* Remove the attribute */
#define XFS_ATTR_OP_FLAGS_REPLACE 3 /* Replace the attribute */
#define XFS_ATTR_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
#define XFS_ATTRI_OP_FLAGS_SET 1 /* Set the attribute */
#define XFS_ATTRI_OP_FLAGS_REMOVE 2 /* Remove the attribute */
#define XFS_ATTRI_OP_FLAGS_REPLACE 3 /* Replace the attribute */
#define XFS_ATTRI_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
/*
* alfi_attr_filter captures the state of xfs_da_args.attr_filter, so it should
* never have any other bits set.
*/
#define XFS_ATTRI_FILTER_MASK (XFS_ATTR_ROOT | \
XFS_ATTR_SECURE | \
XFS_ATTR_INCOMPLETE)
/*
* This is the structure used to lay out an attr log item in the
......@@ -924,7 +932,7 @@ struct xfs_attri_log_format {
uint32_t alfi_op_flags; /* marks the op as a set or remove */
uint32_t alfi_name_len; /* attr name length */
uint32_t alfi_value_len; /* attr value length */
uint32_t alfi_attr_flags;/* attr flags */
uint32_t alfi_attr_filter;/* attr filter flags */
};
struct xfs_attrd_log_format {
......
......@@ -110,12 +110,6 @@ struct xlog_recover {
#define ITEM_TYPE(i) (*(unsigned short *)(i)->ri_buf[0].i_addr)
/*
* This is the number of entries in the l_buf_cancel_table used during
* recovery.
*/
#define XLOG_BC_TABLE_SIZE 64
#define XLOG_RECOVER_CRCPASS 0
#define XLOG_RECOVER_PASS1 1
#define XLOG_RECOVER_PASS2 2
......@@ -128,5 +122,13 @@ int xlog_recover_iget(struct xfs_mount *mp, xfs_ino_t ino,
struct xfs_inode **ipp);
void xlog_recover_release_intent(struct xlog *log, unsigned short intent_type,
uint64_t intent_id);
int xlog_alloc_buf_cancel_table(struct xlog *log);
void xlog_free_buf_cancel_table(struct xlog *log);
#ifdef DEBUG
void xlog_check_buf_cancel_table(struct xlog *log);
#else
#define xlog_check_buf_cancel_table(log) do { } while (0)
#endif
#endif /* __XFS_LOG_RECOVER_H__ */
......@@ -213,7 +213,7 @@ xfs_symlink_shortform_verify(
/*
* Zero length symlinks should never occur in memory as they are
* never alllowed to exist on disk.
* never allowed to exist on disk.
*/
if (!size)
return __this_address;
......
......@@ -340,20 +340,6 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
},
};
/* This isn't a stable feature, warn once per day. */
static inline void
xchk_experimental_warning(
struct xfs_mount *mp)
{
static struct ratelimit_state scrub_warning = RATELIMIT_STATE_INIT(
"xchk_warning", 86400 * HZ, 1);
ratelimit_set_flags(&scrub_warning, RATELIMIT_MSG_ON_RELEASE);
if (__ratelimit(&scrub_warning))
xfs_alert(mp,
"EXPERIMENTAL online scrub feature in use. Use at your own risk!");
}
static int
xchk_validate_inputs(
struct xfs_mount *mp,
......@@ -478,7 +464,8 @@ xfs_scrub_metadata(
if (error)
goto out;
xchk_experimental_warning(mp);
xfs_warn_mount(mp, XFS_OPSTATE_WARNED_SCRUB,
"EXPERIMENTAL online scrub feature in use. Use at your own risk!");
sc = kmem_zalloc(sizeof(struct xfs_scrub), KM_NOFS | KM_MAYFAIL);
if (!sc) {
......
......@@ -17,6 +17,7 @@
#include "xfs_error.h"
#include "xfs_acl.h"
#include "xfs_trans.h"
#include "xfs_xattr.h"
#include <linux/posix_acl_xattr.h>
......@@ -202,7 +203,7 @@ __xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
xfs_acl_to_disk(args.value, acl);
}
error = xfs_attr_set(&args);
error = xfs_attr_change(&args);
kmem_free(args.value);
/*
......
This diff is collapsed.
......@@ -11,25 +11,30 @@
struct xfs_mount;
struct kmem_zone;
struct xfs_attri_log_nameval {
struct xfs_log_iovec name;
struct xfs_log_iovec value;
refcount_t refcount;
/* name and value follow the end of this struct */
};
/*
* This is the "attr intention" log item. It is used to log the fact that some
* extended attribute operations need to be processed. An operation is
* currently either a set or remove. Set or remove operations are described by
* the xfs_attr_item which may be logged to this intent.
* the xfs_attr_intent which may be logged to this intent.
*
* During a normal attr operation, name and value point to the name and value
* fields of the caller's xfs_da_args structure. During a recovery, the name
* and value buffers are copied from the log, and stored in a trailing buffer
* attached to the xfs_attr_item until they are committed. They are freed when
* the xfs_attr_item itself is freed when the work is done.
* attached to the xfs_attr_intent until they are committed. They are freed
* when the xfs_attr_intent itself is freed when the work is done.
*/
struct xfs_attri_log_item {
struct xfs_log_item attri_item;
atomic_t attri_refcount;
int attri_name_len;
int attri_value_len;
void *attri_name;
void *attri_value;
struct xfs_attri_log_nameval *attri_nameval;
struct xfs_attri_log_format attri_format;
};
......@@ -43,4 +48,7 @@ struct xfs_attrd_log_item {
struct xfs_attrd_log_format attrd_format;
};
extern struct kmem_cache *xfs_attri_cache;
extern struct kmem_cache *xfs_attrd_cache;
#endif /* __XFS_ATTR_ITEM_H__ */
......@@ -23,6 +23,15 @@
#include "xfs_dir2.h"
#include "xfs_quota.h"
/*
* This is the number of entries in the l_buf_cancel_table used during
* recovery.
*/
#define XLOG_BC_TABLE_SIZE 64
#define XLOG_BUF_CANCEL_BUCKET(log, blkno) \
((log)->l_buf_cancel_table + ((uint64_t)blkno % XLOG_BC_TABLE_SIZE))
/*
* This structure is used during recovery to record the buf log items which
* have been canceled and should not be replayed.
......@@ -993,3 +1002,60 @@ const struct xlog_recover_item_ops xlog_buf_item_ops = {
.commit_pass1 = xlog_recover_buf_commit_pass1,
.commit_pass2 = xlog_recover_buf_commit_pass2,
};
#ifdef DEBUG
void
xlog_check_buf_cancel_table(
struct xlog *log)
{
int i;
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++)
ASSERT(list_empty(&log->l_buf_cancel_table[i]));
}
#endif
int
xlog_alloc_buf_cancel_table(
struct xlog *log)
{
void *p;
int i;
ASSERT(log->l_buf_cancel_table == NULL);
p = kmalloc_array(XLOG_BC_TABLE_SIZE, sizeof(struct list_head),
GFP_KERNEL);
if (!p)
return -ENOMEM;
log->l_buf_cancel_table = p;
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++)
INIT_LIST_HEAD(&log->l_buf_cancel_table[i]);
return 0;
}
void
xlog_free_buf_cancel_table(
struct xlog *log)
{
int i;
if (!log->l_buf_cancel_table)
return;
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++) {
struct xfs_buf_cancel *bc;
while ((bc = list_first_entry_or_null(
&log->l_buf_cancel_table[i],
struct xfs_buf_cancel, bc_list))) {
list_del(&bc->bc_list);
kmem_free(bc);
}
}
kmem_free(log->l_buf_cancel_table);
log->l_buf_cancel_table = NULL;
}
......@@ -576,9 +576,9 @@ xfs_file_dio_write_unaligned(
* don't even bother trying the fast path in this case.
*/
if (iocb->ki_pos > isize || iocb->ki_pos + count >= isize) {
retry_exclusive:
if (iocb->ki_flags & IOCB_NOWAIT)
return -EAGAIN;
retry_exclusive:
iolock = XFS_IOLOCK_EXCL;
flags = IOMAP_DIO_FORCE_WAIT;
}
......
......@@ -149,12 +149,7 @@ xfs_growfs_data_private(
error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
delta, &lastag_extended);
} else {
static struct ratelimit_state shrink_warning = \
RATELIMIT_STATE_INIT("shrink_warning", 86400 * HZ, 1);
ratelimit_set_flags(&shrink_warning, RATELIMIT_MSG_ON_RELEASE);
if (__ratelimit(&shrink_warning))
xfs_alert(mp,
xfs_warn_mount(mp, XFS_OPSTATE_WARNED_SHRINK,
"EXPERIMENTAL online shrink feature in use. Use at your own risk!");
error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta);
......
......@@ -2622,7 +2622,7 @@ xfs_ifree(
*/
error = xfs_difree(tp, pag, ip->i_ino, &xic);
if (error)
return error;
goto out;
error = xfs_iunlink_remove(tp, pag, ip);
if (error)
......
......@@ -37,6 +37,7 @@
#include "xfs_health.h"
#include "xfs_reflink.h"
#include "xfs_ioctl.h"
#include "xfs_xattr.h"
#include <linux/mount.h>
#include <linux/namei.h>
......@@ -524,7 +525,7 @@ xfs_attrmulti_attr_set(
args.valuelen = len;
}
error = xfs_attr_set(&args);
error = xfs_attr_change(&args);
if (!error && (flags & XFS_IOC_ATTR_ROOT))
xfs_forget_acl(inode, name);
kfree(args.value);
......
......@@ -24,6 +24,7 @@
#include "xfs_iomap.h"
#include "xfs_error.h"
#include "xfs_ioctl.h"
#include "xfs_xattr.h"
#include <linux/posix_acl.h>
#include <linux/security.h>
......@@ -61,7 +62,7 @@ xfs_initxattrs(
.value = xattr->value,
.valuelen = xattr->value_len,
};
error = xfs_attr_set(&args);
error = xfs_attr_change(&args);
if (error < 0)
break;
}
......
......@@ -3877,44 +3877,3 @@ xlog_drop_incompat_feat(
{
up_read(&log->l_incompat_users);
}
/*
* Get permission to use log-assisted atomic exchange of file extents.
*
* Callers must not be running any transactions or hold any inode locks, and
* they must release the permission by calling xlog_drop_incompat_feat
* when they're done.
*/
int
xfs_attr_use_log_assist(
struct xfs_mount *mp)
{
int error = 0;
/*
* Protect ourselves from an idle log clearing the logged xattrs log
* incompat feature bit.
*/
xlog_use_incompat_feat(mp->m_log);
/*
* If log-assisted xattrs are already enabled, the caller can use the
* log assisted swap functions with the log-incompat reference we got.
*/
if (xfs_sb_version_haslogxattrs(&mp->m_sb))
return 0;
/* Enable log-assisted xattrs. */
error = xfs_add_incompat_log_feature(mp,
XFS_SB_FEAT_INCOMPAT_LOG_XATTRS);
if (error)
goto drop_incompat;
xfs_warn_once(mp,
"EXPERIMENTAL logged extended attributes feature added. Use at your own risk!");
return 0;
drop_incompat:
xlog_drop_incompat_feat(mp->m_log);
return error;
}
......@@ -86,6 +86,13 @@ xlog_copy_iovec(struct xfs_log_vec *lv, struct xfs_log_iovec **vecp,
return buf;
}
static inline void *
xlog_copy_from_iovec(struct xfs_log_vec *lv, struct xfs_log_iovec **vecp,
const struct xfs_log_iovec *src)
{
return xlog_copy_iovec(lv, vecp, src->i_type, src->i_addr, src->i_len);
}
/*
* By comparing each component, we don't have to worry about extra
* endian issues in treating two 32 bit numbers as one 64 bit number
......
......@@ -428,9 +428,6 @@ struct xlog {
struct rw_semaphore l_incompat_users;
};
#define XLOG_BUF_CANCEL_BUCKET(log, blkno) \
((log)->l_buf_cancel_table + ((uint64_t)blkno % XLOG_BC_TABLE_SIZE))
/*
* Bits for operational state
*/
......
......@@ -39,13 +39,6 @@ STATIC int
xlog_clear_stale_blocks(
struct xlog *,
xfs_lsn_t);
#if defined(DEBUG)
STATIC void
xlog_recover_check_summary(
struct xlog *);
#else
#define xlog_recover_check_summary(log)
#endif
STATIC int
xlog_do_recovery_pass(
struct xlog *, xfs_daddr_t, xfs_daddr_t, int, xfs_daddr_t *);
......@@ -3230,7 +3223,7 @@ xlog_do_log_recovery(
xfs_daddr_t head_blk,
xfs_daddr_t tail_blk)
{
int error, i;
int error;
ASSERT(head_blk != tail_blk);
......@@ -3238,37 +3231,25 @@ xlog_do_log_recovery(
* First do a pass to find all of the cancelled buf log items.
* Store them in the buf_cancel_table for use in the second pass.
*/
log->l_buf_cancel_table = kmem_zalloc(XLOG_BC_TABLE_SIZE *
sizeof(struct list_head),
0);
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++)
INIT_LIST_HEAD(&log->l_buf_cancel_table[i]);
error = xlog_alloc_buf_cancel_table(log);
if (error)
return error;
error = xlog_do_recovery_pass(log, head_blk, tail_blk,
XLOG_RECOVER_PASS1, NULL);
if (error != 0) {
kmem_free(log->l_buf_cancel_table);
log->l_buf_cancel_table = NULL;
return error;
}
if (error != 0)
goto out_cancel;
/*
* Then do a second pass to actually recover the items in the log.
* When it is complete free the table of buf cancel items.
*/
error = xlog_do_recovery_pass(log, head_blk, tail_blk,
XLOG_RECOVER_PASS2, NULL);
#ifdef DEBUG
if (!error) {
int i;
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++)
ASSERT(list_empty(&log->l_buf_cancel_table[i]));
}
#endif /* DEBUG */
kmem_free(log->l_buf_cancel_table);
log->l_buf_cancel_table = NULL;
if (!error)
xlog_check_buf_cancel_table(log);
out_cancel:
xlog_free_buf_cancel_table(log);
return error;
}
......@@ -3339,8 +3320,6 @@ xlog_do_recover(
}
mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
xlog_recover_check_summary(log);
/* Normal transactions can now occur */
clear_bit(XLOG_ACTIVE_RECOVERY, &log->l_opstate);
return 0;
......@@ -3483,7 +3462,6 @@ xlog_recover_finish(
}
xlog_recover_process_iunlinks(log);
xlog_recover_check_summary(log);
/*
* Recover any CoW staging blocks that are still referenced by the
......@@ -3517,52 +3495,3 @@ xlog_recover_cancel(
xlog_recover_cancel_intents(log);
}
#if defined(DEBUG)
/*
* Read all of the agf and agi counters and check that they
* are consistent with the superblock counters.
*/
STATIC void
xlog_recover_check_summary(
struct xlog *log)
{
struct xfs_mount *mp = log->l_mp;
struct xfs_perag *pag;
struct xfs_buf *agfbp;
struct xfs_buf *agibp;
xfs_agnumber_t agno;
uint64_t freeblks;
uint64_t itotal;
uint64_t ifree;
int error;
freeblks = 0LL;
itotal = 0LL;
ifree = 0LL;
for_each_perag(mp, agno, pag) {
error = xfs_read_agf(mp, NULL, pag->pag_agno, 0, &agfbp);
if (error) {
xfs_alert(mp, "%s agf read failed agno %d error %d",
__func__, pag->pag_agno, error);
} else {
struct xfs_agf *agfp = agfbp->b_addr;
freeblks += be32_to_cpu(agfp->agf_freeblks) +
be32_to_cpu(agfp->agf_flcount);
xfs_buf_relse(agfbp);
}
error = xfs_read_agi(mp, NULL, pag->pag_agno, &agibp);
if (error) {
xfs_alert(mp, "%s agi read failed agno %d error %d",
__func__, pag->pag_agno, error);
} else {
struct xfs_agi *agi = agibp->b_addr;
itotal += be32_to_cpu(agi->agi_count);
ifree += be32_to_cpu(agi->agi_freecount);
xfs_buf_relse(agibp);
}
}
}
#endif /* DEBUG */
......@@ -75,6 +75,12 @@ do { \
#define xfs_debug_ratelimited(dev, fmt, ...) \
xfs_printk_ratelimited(xfs_debug, dev, fmt, ##__VA_ARGS__)
#define xfs_warn_mount(mp, warntag, fmt, ...) \
do { \
if (xfs_should_warn((mp), (warntag))) \
xfs_warn((mp), (fmt), ##__VA_ARGS__); \
} while (0)
#define xfs_warn_once(dev, fmt, ...) \
xfs_printk_once(xfs_warn, dev, fmt, ##__VA_ARGS__)
#define xfs_notice_once(dev, fmt, ...) \
......
......@@ -1356,7 +1356,6 @@ xfs_clear_incompat_log_features(
if (xfs_sb_has_incompat_log_feature(&mp->m_sb,
XFS_SB_FEAT_INCOMPAT_LOG_ALL)) {
xfs_info(mp, "Clearing log incompat feature flags.");
xfs_sb_remove_incompat_log_features(&mp->m_sb);
ret = true;
}
......
......@@ -391,6 +391,13 @@ __XFS_HAS_FEAT(nouuid, NOUUID)
*/
#define XFS_OPSTATE_BLOCKGC_ENABLED 6
/* Kernel has logged a warning about online fsck being used on this fs. */
#define XFS_OPSTATE_WARNED_SCRUB 7
/* Kernel has logged a warning about shrink being used on this fs. */
#define XFS_OPSTATE_WARNED_SHRINK 8
/* Kernel has logged a warning about logged xattr updates being used. */
#define XFS_OPSTATE_WARNED_LARP 9
#define __XFS_IS_OPSTATE(name, NAME) \
static inline bool xfs_is_ ## name (struct xfs_mount *mp) \
{ \
......@@ -413,6 +420,12 @@ __XFS_IS_OPSTATE(readonly, READONLY)
__XFS_IS_OPSTATE(inodegc_enabled, INODEGC_ENABLED)
__XFS_IS_OPSTATE(blockgc_enabled, BLOCKGC_ENABLED)
static inline bool
xfs_should_warn(struct xfs_mount *mp, long nr)
{
return !test_and_set_bit(nr, &mp->m_opstate);
}
#define XFS_OPSTATE_STRINGS \
{ (1UL << XFS_OPSTATE_UNMOUNTING), "unmounting" }, \
{ (1UL << XFS_OPSTATE_CLEAN), "clean" }, \
......@@ -420,7 +433,10 @@ __XFS_IS_OPSTATE(blockgc_enabled, BLOCKGC_ENABLED)
{ (1UL << XFS_OPSTATE_INODE32), "inode32" }, \
{ (1UL << XFS_OPSTATE_READONLY), "read_only" }, \
{ (1UL << XFS_OPSTATE_INODEGC_ENABLED), "inodegc" }, \
{ (1UL << XFS_OPSTATE_BLOCKGC_ENABLED), "blockgc" }
{ (1UL << XFS_OPSTATE_BLOCKGC_ENABLED), "blockgc" }, \
{ (1UL << XFS_OPSTATE_WARNED_SCRUB), "wscrub" }, \
{ (1UL << XFS_OPSTATE_WARNED_SHRINK), "wshrink" }, \
{ (1UL << XFS_OPSTATE_WARNED_LARP), "wlarp" }
/*
* Max and min values for mount-option defined I/O
......
......@@ -1308,8 +1308,15 @@ xfs_qm_quotacheck(
error = xfs_iwalk_threaded(mp, 0, 0, xfs_qm_dqusage_adjust, 0, true,
NULL);
if (error)
if (error) {
/*
* The inode walk may have partially populated the dquot
* caches. We must purge them before disabling quota and
* tearing down the quotainfo, or else the dquots will leak.
*/
xfs_qm_dqpurge_all(mp);
goto error_return;
}
/*
* We've made all the changes that we need to make incore. Flush them
......
......@@ -38,6 +38,8 @@
#include "xfs_pwork.h"
#include "xfs_ag.h"
#include "xfs_defer.h"
#include "xfs_attr_item.h"
#include "xfs_xattr.h"
#include <linux/magic.h>
#include <linux/fs_context.h>
......@@ -2079,8 +2081,24 @@ xfs_init_caches(void)
if (!xfs_bui_cache)
goto out_destroy_bud_cache;
xfs_attrd_cache = kmem_cache_create("xfs_attrd_item",
sizeof(struct xfs_attrd_log_item),
0, 0, NULL);
if (!xfs_attrd_cache)
goto out_destroy_bui_cache;
xfs_attri_cache = kmem_cache_create("xfs_attri_item",
sizeof(struct xfs_attri_log_item),
0, 0, NULL);
if (!xfs_attri_cache)
goto out_destroy_attrd_cache;
return 0;
out_destroy_attrd_cache:
kmem_cache_destroy(xfs_attrd_cache);
out_destroy_bui_cache:
kmem_cache_destroy(xfs_bui_cache);
out_destroy_bud_cache:
kmem_cache_destroy(xfs_bud_cache);
out_destroy_cui_cache:
......@@ -2127,6 +2145,8 @@ xfs_destroy_caches(void)
* destroy caches.
*/
rcu_barrier();
kmem_cache_destroy(xfs_attri_cache);
kmem_cache_destroy(xfs_attrd_cache);
kmem_cache_destroy(xfs_bui_cache);
kmem_cache_destroy(xfs_bud_cache);
kmem_cache_destroy(xfs_cui_cache);
......
......@@ -91,7 +91,6 @@ extern xfs_agnumber_t xfs_set_inode_alloc(struct xfs_mount *,
xfs_agnumber_t agcount);
extern const struct export_operations xfs_export_operations;
extern const struct xattr_handler *xfs_xattr_handlers[];
extern const struct quotactl_ops xfs_quotactl_operations;
extern void xfs_reinit_percpu_counters(struct xfs_mount *mp);
......
......@@ -15,9 +15,86 @@
#include "xfs_da_btree.h"
#include "xfs_attr.h"
#include "xfs_acl.h"
#include "xfs_log.h"
#include "xfs_xattr.h"
#include <linux/posix_acl_xattr.h>
/*
* Get permission to use log-assisted atomic exchange of file extents.
*
* Callers must not be running any transactions or hold any inode locks, and
* they must release the permission by calling xlog_drop_incompat_feat
* when they're done.
*/
static inline int
xfs_attr_grab_log_assist(
struct xfs_mount *mp)
{
int error = 0;
/*
* Protect ourselves from an idle log clearing the logged xattrs log
* incompat feature bit.
*/
xlog_use_incompat_feat(mp->m_log);
/*
* If log-assisted xattrs are already enabled, the caller can use the
* log assisted swap functions with the log-incompat reference we got.
*/
if (xfs_sb_version_haslogxattrs(&mp->m_sb))
return 0;
/* Enable log-assisted xattrs. */
error = xfs_add_incompat_log_feature(mp,
XFS_SB_FEAT_INCOMPAT_LOG_XATTRS);
if (error)
goto drop_incompat;
xfs_warn_mount(mp, XFS_OPSTATE_WARNED_LARP,
"EXPERIMENTAL logged extended attributes feature in use. Use at your own risk!");
return 0;
drop_incompat:
xlog_drop_incompat_feat(mp->m_log);
return error;
}
static inline void
xfs_attr_rele_log_assist(
struct xfs_mount *mp)
{
xlog_drop_incompat_feat(mp->m_log);
}
/*
* Set or remove an xattr, having grabbed the appropriate logging resources
* prior to calling libxfs.
*/
int
xfs_attr_change(
struct xfs_da_args *args)
{
struct xfs_mount *mp = args->dp->i_mount;
bool use_logging = false;
int error;
if (xfs_has_larp(mp)) {
error = xfs_attr_grab_log_assist(mp);
if (error)
return error;
use_logging = true;
}
error = xfs_attr_set(args);
if (use_logging)
xfs_attr_rele_log_assist(mp);
return error;
}
static int
xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused,
......@@ -56,7 +133,7 @@ xfs_xattr_set(const struct xattr_handler *handler,
};
int error;
error = xfs_attr_set(&args);
error = xfs_attr_change(&args);
if (!error && (handler->flags & XFS_ATTR_ROOT))
xfs_forget_acl(inode, name);
return error;
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#ifndef __XFS_XATTR_H__
#define __XFS_XATTR_H__
int xfs_attr_change(struct xfs_da_args *args);
extern const struct xattr_handler *xfs_xattr_handlers[];
#endif /* __XFS_XATTR_H__ */
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