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( ...@@ -173,7 +173,6 @@ __xfs_free_perag(
struct xfs_perag *pag = container_of(head, struct xfs_perag, rcu_head); struct xfs_perag *pag = container_of(head, struct xfs_perag, rcu_head);
ASSERT(!delayed_work_pending(&pag->pag_blockgc_work)); ASSERT(!delayed_work_pending(&pag->pag_blockgc_work));
ASSERT(atomic_read(&pag->pag_ref) == 0);
kmem_free(pag); kmem_free(pag);
} }
...@@ -192,7 +191,7 @@ xfs_free_perag( ...@@ -192,7 +191,7 @@ xfs_free_perag(
pag = radix_tree_delete(&mp->m_perag_tree, agno); pag = radix_tree_delete(&mp->m_perag_tree, agno);
spin_unlock(&mp->m_perag_lock); spin_unlock(&mp->m_perag_lock);
ASSERT(pag); 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); cancel_delayed_work_sync(&pag->pag_blockgc_work);
xfs_iunlink_destroy(pag); xfs_iunlink_destroy(pag);
......
...@@ -25,10 +25,9 @@ ...@@ -25,10 +25,9 @@
#include "xfs_trans_space.h" #include "xfs_trans_space.h"
#include "xfs_trace.h" #include "xfs_trace.h"
#include "xfs_attr_item.h" #include "xfs_attr_item.h"
#include "xfs_log.h" #include "xfs_xattr.h"
struct kmem_cache *xfs_attri_cache; struct kmem_cache *xfs_attr_intent_cache;
struct kmem_cache *xfs_attrd_cache;
/* /*
* xfs_attr.c * xfs_attr.c
...@@ -58,11 +57,11 @@ STATIC int xfs_attr_leaf_try_add(struct xfs_da_args *args, struct xfs_buf *bp); ...@@ -58,11 +57,11 @@ STATIC int xfs_attr_leaf_try_add(struct xfs_da_args *args, struct xfs_buf *bp);
*/ */
STATIC int xfs_attr_node_get(xfs_da_args_t *args); STATIC int xfs_attr_node_get(xfs_da_args_t *args);
STATIC void xfs_attr_restore_rmt_blk(struct xfs_da_args *args); STATIC void xfs_attr_restore_rmt_blk(struct xfs_da_args *args);
static int xfs_attr_node_try_addname(struct xfs_attr_item *attr); static int xfs_attr_node_try_addname(struct xfs_attr_intent *attr);
STATIC int xfs_attr_node_addname_find_attr(struct xfs_attr_item *attr); STATIC int xfs_attr_node_addname_find_attr(struct xfs_attr_intent *attr);
STATIC int xfs_attr_node_remove_attr(struct xfs_attr_item *attr); STATIC int xfs_attr_node_remove_attr(struct xfs_attr_intent *attr);
STATIC int xfs_attr_node_hasname(xfs_da_args_t *args, STATIC int xfs_attr_node_lookup(struct xfs_da_args *args,
struct xfs_da_state **state); struct xfs_da_state *state);
int int
xfs_inode_hasattr( xfs_inode_hasattr(
...@@ -377,7 +376,7 @@ xfs_attr_try_sf_addname( ...@@ -377,7 +376,7 @@ xfs_attr_try_sf_addname(
static int static int
xfs_attr_sf_addname( xfs_attr_sf_addname(
struct xfs_attr_item *attr) struct xfs_attr_intent *attr)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
struct xfs_inode *dp = args->dp; struct xfs_inode *dp = args->dp;
...@@ -423,7 +422,7 @@ xfs_attr_sf_addname( ...@@ -423,7 +422,7 @@ xfs_attr_sf_addname(
*/ */
static enum xfs_delattr_state static enum xfs_delattr_state
xfs_attr_complete_op( xfs_attr_complete_op(
struct xfs_attr_item *attr, struct xfs_attr_intent *attr,
enum xfs_delattr_state replace_state) enum xfs_delattr_state replace_state)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
...@@ -439,7 +438,7 @@ xfs_attr_complete_op( ...@@ -439,7 +438,7 @@ xfs_attr_complete_op(
static int static int
xfs_attr_leaf_addname( xfs_attr_leaf_addname(
struct xfs_attr_item *attr) struct xfs_attr_intent *attr)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
int error; int error;
...@@ -493,7 +492,7 @@ xfs_attr_leaf_addname( ...@@ -493,7 +492,7 @@ xfs_attr_leaf_addname(
*/ */
static int static int
xfs_attr_node_addname( xfs_attr_node_addname(
struct xfs_attr_item *attr) struct xfs_attr_intent *attr)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
int error; int error;
...@@ -530,7 +529,7 @@ xfs_attr_node_addname( ...@@ -530,7 +529,7 @@ xfs_attr_node_addname(
static int static int
xfs_attr_rmtval_alloc( xfs_attr_rmtval_alloc(
struct xfs_attr_item *attr) struct xfs_attr_intent *attr)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
int error = 0; int error = 0;
...@@ -594,6 +593,19 @@ xfs_attr_leaf_mark_incomplete( ...@@ -594,6 +593,19 @@ xfs_attr_leaf_mark_incomplete(
return xfs_attr3_leaf_setflag(args); return xfs_attr3_leaf_setflag(args);
} }
/* Ensure the da state of an xattr deferred work item is ready to go. */
static inline void
xfs_attr_item_init_da_state(
struct xfs_attr_intent *attr)
{
struct xfs_da_args *args = attr->xattri_da_args;
if (!attr->xattri_da_state)
attr->xattri_da_state = xfs_da_state_alloc(args);
else
xfs_da_state_reset(attr->xattri_da_state, args);
}
/* /*
* Initial setup for xfs_attr_node_removename. Make sure the attr is there and * Initial setup for xfs_attr_node_removename. Make sure the attr is there and
* the blocks are valid. Attr keys with remote blocks will be marked * the blocks are valid. Attr keys with remote blocks will be marked
...@@ -601,29 +613,33 @@ xfs_attr_leaf_mark_incomplete( ...@@ -601,29 +613,33 @@ xfs_attr_leaf_mark_incomplete(
*/ */
static static
int xfs_attr_node_removename_setup( int xfs_attr_node_removename_setup(
struct xfs_attr_item *attr) struct xfs_attr_intent *attr)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
struct xfs_da_state **state = &attr->xattri_da_state; struct xfs_da_state *state;
int error; int error;
error = xfs_attr_node_hasname(args, state); xfs_attr_item_init_da_state(attr);
error = xfs_attr_node_lookup(args, attr->xattri_da_state);
if (error != -EEXIST) if (error != -EEXIST)
goto out; goto out;
error = 0; error = 0;
ASSERT((*state)->path.blk[(*state)->path.active - 1].bp != NULL); state = attr->xattri_da_state;
ASSERT((*state)->path.blk[(*state)->path.active - 1].magic == ASSERT(state->path.blk[state->path.active - 1].bp != NULL);
ASSERT(state->path.blk[state->path.active - 1].magic ==
XFS_ATTR_LEAF_MAGIC); XFS_ATTR_LEAF_MAGIC);
error = xfs_attr_leaf_mark_incomplete(args, *state); error = xfs_attr_leaf_mark_incomplete(args, state);
if (error) if (error)
goto out; goto out;
if (args->rmtblkno > 0) if (args->rmtblkno > 0)
error = xfs_attr_rmtval_invalidate(args); error = xfs_attr_rmtval_invalidate(args);
out: out:
if (error) if (error) {
xfs_da_state_free(*state); xfs_da_state_free(attr->xattri_da_state);
attr->xattri_da_state = NULL;
}
return error; return error;
} }
...@@ -635,7 +651,7 @@ int xfs_attr_node_removename_setup( ...@@ -635,7 +651,7 @@ int xfs_attr_node_removename_setup(
*/ */
static int static int
xfs_attr_leaf_remove_attr( xfs_attr_leaf_remove_attr(
struct xfs_attr_item *attr) struct xfs_attr_intent *attr)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
struct xfs_inode *dp = args->dp; struct xfs_inode *dp = args->dp;
...@@ -700,7 +716,7 @@ xfs_attr_leaf_shrink( ...@@ -700,7 +716,7 @@ xfs_attr_leaf_shrink(
*/ */
int int
xfs_attr_set_iter( xfs_attr_set_iter(
struct xfs_attr_item *attr) struct xfs_attr_intent *attr)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
int error = 0; int error = 0;
...@@ -852,6 +868,7 @@ xfs_attr_lookup( ...@@ -852,6 +868,7 @@ xfs_attr_lookup(
{ {
struct xfs_inode *dp = args->dp; struct xfs_inode *dp = args->dp;
struct xfs_buf *bp = NULL; struct xfs_buf *bp = NULL;
struct xfs_da_state *state;
int error; int error;
if (!xfs_inode_hasattr(dp)) if (!xfs_inode_hasattr(dp))
...@@ -869,19 +886,22 @@ xfs_attr_lookup( ...@@ -869,19 +886,22 @@ xfs_attr_lookup(
return error; return error;
} }
return xfs_attr_node_hasname(args, NULL); state = xfs_da_state_alloc(args);
error = xfs_attr_node_lookup(args, state);
xfs_da_state_free(state);
return error;
} }
static int static int
xfs_attr_item_init( xfs_attr_intent_init(
struct xfs_da_args *args, struct xfs_da_args *args,
unsigned int op_flags, /* op flag (set or remove) */ unsigned int op_flags, /* op flag (set or remove) */
struct xfs_attr_item **attr) /* new xfs_attr_item */ struct xfs_attr_intent **attr) /* new xfs_attr_intent */
{ {
struct xfs_attr_item *new; struct xfs_attr_intent *new;
new = kmem_zalloc(sizeof(struct xfs_attr_item), KM_NOFS); new = kmem_cache_zalloc(xfs_attr_intent_cache, GFP_NOFS | __GFP_NOFAIL);
new->xattri_op_flags = op_flags; new->xattri_op_flags = op_flags;
new->xattri_da_args = args; new->xattri_da_args = args;
...@@ -894,10 +914,10 @@ static int ...@@ -894,10 +914,10 @@ static int
xfs_attr_defer_add( xfs_attr_defer_add(
struct xfs_da_args *args) struct xfs_da_args *args)
{ {
struct xfs_attr_item *new; struct xfs_attr_intent *new;
int error = 0; int error = 0;
error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_SET, &new); error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_SET, &new);
if (error) if (error)
return error; return error;
...@@ -913,10 +933,10 @@ static int ...@@ -913,10 +933,10 @@ static int
xfs_attr_defer_replace( xfs_attr_defer_replace(
struct xfs_da_args *args) struct xfs_da_args *args)
{ {
struct xfs_attr_item *new; struct xfs_attr_intent *new;
int error = 0; int error = 0;
error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_REPLACE, &new); error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_REPLACE, &new);
if (error) if (error)
return error; return error;
...@@ -933,10 +953,10 @@ xfs_attr_defer_remove( ...@@ -933,10 +953,10 @@ xfs_attr_defer_remove(
struct xfs_da_args *args) struct xfs_da_args *args)
{ {
struct xfs_attr_item *new; struct xfs_attr_intent *new;
int error; int error;
error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_REMOVE, &new); error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_REMOVE, &new);
if (error) if (error)
return error; return error;
...@@ -962,7 +982,6 @@ xfs_attr_set( ...@@ -962,7 +982,6 @@ xfs_attr_set(
int error, local; int error, local;
int rmt_blks = 0; int rmt_blks = 0;
unsigned int total; unsigned int total;
int delayed = xfs_has_larp(mp);
if (xfs_is_shutdown(dp->i_mount)) if (xfs_is_shutdown(dp->i_mount))
return -EIO; return -EIO;
...@@ -1007,12 +1026,6 @@ xfs_attr_set( ...@@ -1007,12 +1026,6 @@ xfs_attr_set(
rmt_blks = xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX); rmt_blks = xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX);
} }
if (delayed) {
error = xfs_attr_use_log_assist(mp);
if (error)
return error;
}
/* /*
* Root fork attributes can use reserved data blocks for this * Root fork attributes can use reserved data blocks for this
* operation if necessary * operation if necessary
...@@ -1020,7 +1033,7 @@ xfs_attr_set( ...@@ -1020,7 +1033,7 @@ xfs_attr_set(
xfs_init_attr_trans(args, &tres, &total); xfs_init_attr_trans(args, &tres, &total);
error = xfs_trans_alloc_inode(dp, &tres, total, 0, rsvd, &args->trans); error = xfs_trans_alloc_inode(dp, &tres, total, 0, rsvd, &args->trans);
if (error) if (error)
goto drop_incompat; return error;
if (args->value || xfs_inode_hasattr(dp)) { if (args->value || xfs_inode_hasattr(dp)) {
error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK, error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK,
...@@ -1080,9 +1093,6 @@ xfs_attr_set( ...@@ -1080,9 +1093,6 @@ xfs_attr_set(
error = xfs_trans_commit(args->trans); error = xfs_trans_commit(args->trans);
out_unlock: out_unlock:
xfs_iunlock(dp, XFS_ILOCK_EXCL); xfs_iunlock(dp, XFS_ILOCK_EXCL);
drop_incompat:
if (delayed)
xlog_drop_incompat_feat(mp->m_log);
return error; return error;
out_trans_cancel: out_trans_cancel:
...@@ -1091,40 +1101,6 @@ xfs_attr_set( ...@@ -1091,40 +1101,6 @@ xfs_attr_set(
goto out_unlock; goto out_unlock;
} }
int __init
xfs_attri_init_cache(void)
{
xfs_attri_cache = kmem_cache_create("xfs_attri",
sizeof(struct xfs_attri_log_item),
0, 0, NULL);
return xfs_attri_cache != NULL ? 0 : -ENOMEM;
}
void
xfs_attri_destroy_cache(void)
{
kmem_cache_destroy(xfs_attri_cache);
xfs_attri_cache = NULL;
}
int __init
xfs_attrd_init_cache(void)
{
xfs_attrd_cache = kmem_cache_create("xfs_attrd",
sizeof(struct xfs_attrd_log_item),
0, 0, NULL);
return xfs_attrd_cache != NULL ? 0 : -ENOMEM;
}
void
xfs_attrd_destroy_cache(void)
{
kmem_cache_destroy(xfs_attrd_cache);
xfs_attrd_cache = NULL;
}
/*======================================================================== /*========================================================================
* External routines when attribute list is inside the inode * External routines when attribute list is inside the inode
*========================================================================*/ *========================================================================*/
...@@ -1384,32 +1360,20 @@ xfs_attr_leaf_get(xfs_da_args_t *args) ...@@ -1384,32 +1360,20 @@ xfs_attr_leaf_get(xfs_da_args_t *args)
return error; return error;
} }
/* /* Return EEXIST if attr is found, or ENOATTR if not. */
* Return EEXIST if attr is found, or ENOATTR if not
* statep: If not null is set to point at the found state. Caller will
* be responsible for freeing the state in this case.
*/
STATIC int STATIC int
xfs_attr_node_hasname( xfs_attr_node_lookup(
struct xfs_da_args *args, struct xfs_da_args *args,
struct xfs_da_state **statep) struct xfs_da_state *state)
{ {
struct xfs_da_state *state;
int retval, error; int retval, error;
state = xfs_da_state_alloc(args);
if (statep != NULL)
*statep = state;
/* /*
* Search to see if name exists, and get back a pointer to it. * Search to see if name exists, and get back a pointer to it.
*/ */
error = xfs_da3_node_lookup_int(state, &retval); error = xfs_da3_node_lookup_int(state, &retval);
if (error) if (error)
retval = error; return error;
if (!statep)
xfs_da_state_free(state);
return retval; return retval;
} }
...@@ -1420,7 +1384,7 @@ xfs_attr_node_hasname( ...@@ -1420,7 +1384,7 @@ xfs_attr_node_hasname(
STATIC int STATIC int
xfs_attr_node_addname_find_attr( xfs_attr_node_addname_find_attr(
struct xfs_attr_item *attr) struct xfs_attr_intent *attr)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
int error; int error;
...@@ -1429,7 +1393,8 @@ xfs_attr_node_addname_find_attr( ...@@ -1429,7 +1393,8 @@ xfs_attr_node_addname_find_attr(
* Search to see if name already exists, and get back a pointer * Search to see if name already exists, and get back a pointer
* to where it should go. * to where it should go.
*/ */
error = xfs_attr_node_hasname(args, &attr->xattri_da_state); xfs_attr_item_init_da_state(attr);
error = xfs_attr_node_lookup(args, attr->xattri_da_state);
switch (error) { switch (error) {
case -ENOATTR: case -ENOATTR:
if (args->op_flags & XFS_DA_OP_REPLACE) if (args->op_flags & XFS_DA_OP_REPLACE)
...@@ -1456,8 +1421,10 @@ xfs_attr_node_addname_find_attr( ...@@ -1456,8 +1421,10 @@ xfs_attr_node_addname_find_attr(
return 0; return 0;
error: error:
if (attr->xattri_da_state) if (attr->xattri_da_state) {
xfs_da_state_free(attr->xattri_da_state); xfs_da_state_free(attr->xattri_da_state);
attr->xattri_da_state = NULL;
}
return error; return error;
} }
...@@ -1470,7 +1437,7 @@ xfs_attr_node_addname_find_attr( ...@@ -1470,7 +1437,7 @@ xfs_attr_node_addname_find_attr(
*/ */
static int static int
xfs_attr_node_try_addname( xfs_attr_node_try_addname(
struct xfs_attr_item *attr) struct xfs_attr_intent *attr)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
struct xfs_da_state *state = attr->xattri_da_state; struct xfs_da_state *state = attr->xattri_da_state;
...@@ -1511,6 +1478,7 @@ xfs_attr_node_try_addname( ...@@ -1511,6 +1478,7 @@ xfs_attr_node_try_addname(
out: out:
xfs_da_state_free(state); xfs_da_state_free(state);
attr->xattri_da_state = NULL;
return error; return error;
} }
...@@ -1535,10 +1503,10 @@ xfs_attr_node_removename( ...@@ -1535,10 +1503,10 @@ xfs_attr_node_removename(
static int static int
xfs_attr_node_remove_attr( xfs_attr_node_remove_attr(
struct xfs_attr_item *attr) struct xfs_attr_intent *attr)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
struct xfs_da_state *state = NULL; struct xfs_da_state *state = xfs_da_state_alloc(args);
int retval = 0; int retval = 0;
int error = 0; int error = 0;
...@@ -1548,8 +1516,6 @@ xfs_attr_node_remove_attr( ...@@ -1548,8 +1516,6 @@ xfs_attr_node_remove_attr(
* attribute entry after any split ops. * attribute entry after any split ops.
*/ */
args->attr_filter |= XFS_ATTR_INCOMPLETE; args->attr_filter |= XFS_ATTR_INCOMPLETE;
state = xfs_da_state_alloc(args);
state->inleaf = 0;
error = xfs_da3_node_lookup_int(state, &retval); error = xfs_da3_node_lookup_int(state, &retval);
if (error) if (error)
goto out; goto out;
...@@ -1567,7 +1533,6 @@ xfs_attr_node_remove_attr( ...@@ -1567,7 +1533,6 @@ xfs_attr_node_remove_attr(
retval = error = 0; retval = error = 0;
out: out:
if (state)
xfs_da_state_free(state); xfs_da_state_free(state);
if (error) if (error)
return error; return error;
...@@ -1597,7 +1562,8 @@ xfs_attr_node_get( ...@@ -1597,7 +1562,8 @@ xfs_attr_node_get(
/* /*
* Search to see if name exists, and get back a pointer to it. * Search to see if name exists, and get back a pointer to it.
*/ */
error = xfs_attr_node_hasname(args, &state); state = xfs_da_state_alloc(args);
error = xfs_attr_node_lookup(args, state);
if (error != -EEXIST) if (error != -EEXIST)
goto out_release; goto out_release;
...@@ -1616,7 +1582,6 @@ xfs_attr_node_get( ...@@ -1616,7 +1582,6 @@ xfs_attr_node_get(
state->path.blk[i].bp = NULL; state->path.blk[i].bp = NULL;
} }
if (state)
xfs_da_state_free(state); xfs_da_state_free(state);
return error; return error;
} }
...@@ -1637,3 +1602,20 @@ xfs_attr_namecheck( ...@@ -1637,3 +1602,20 @@ xfs_attr_namecheck(
/* There shouldn't be any nulls here */ /* There shouldn't be any nulls here */
return !memchr(name, 0, length); return !memchr(name, 0, length);
} }
int __init
xfs_attr_intent_init_cache(void)
{
xfs_attr_intent_cache = kmem_cache_create("xfs_attr_intent",
sizeof(struct xfs_attr_intent),
0, 0, NULL);
return xfs_attr_intent_cache != NULL ? 0 : -ENOMEM;
}
void
xfs_attr_intent_destroy_cache(void)
{
kmem_cache_destroy(xfs_attr_intent_cache);
xfs_attr_intent_cache = NULL;
}
...@@ -31,7 +31,8 @@ struct xfs_attr_list_context; ...@@ -31,7 +31,8 @@ struct xfs_attr_list_context;
static inline bool xfs_has_larp(struct xfs_mount *mp) static inline bool xfs_has_larp(struct xfs_mount *mp)
{ {
#ifdef DEBUG #ifdef DEBUG
return xfs_globals.larp; /* Logged xattrs require a V5 super for log_incompat */
return xfs_has_crc(mp) && xfs_globals.larp;
#else #else
return false; return false;
#endif #endif
...@@ -434,7 +435,7 @@ struct xfs_attr_list_context { ...@@ -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 * 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 * they were before they returned -EAGAIN. A return code of -EAGAIN signals the
...@@ -501,44 +502,46 @@ enum xfs_delattr_state { ...@@ -501,44 +502,46 @@ enum xfs_delattr_state {
{ XFS_DAS_NODE_REMOVE_ATTR, "XFS_DAS_NODE_REMOVE_ATTR" }, \ { XFS_DAS_NODE_REMOVE_ATTR, "XFS_DAS_NODE_REMOVE_ATTR" }, \
{ XFS_DAS_DONE, "XFS_DAS_DONE" } { XFS_DAS_DONE, "XFS_DAS_DONE" }
/* struct xfs_attri_log_nameval;
* Defines for xfs_attr_item.xattri_flags
*/
#define XFS_DAC_LEAF_ADDNAME_INIT 0x01 /* xfs_attr_leaf_addname init*/
/* /*
* Context used for keeping track of delayed attribute operations * 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; 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 * Used by xfs_attr_set to hold a leaf buffer across a transaction roll
*/ */
struct xfs_buf *xattri_leaf_bp; 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 */ /* Used to keep track of current state of delayed operation */
unsigned int xattri_flags;
enum xfs_delattr_state xattri_dela_state; 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; unsigned int xattri_op_flags;
/* /* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
* used to log this item to an intent containing a list of attrs to xfs_dablk_t xattri_lblkno;
* commit later int xattri_blkcnt;
*/ struct xfs_bmbt_irec xattri_map;
struct list_head xattri_list;
}; };
...@@ -557,21 +560,13 @@ bool xfs_attr_is_leaf(struct xfs_inode *ip); ...@@ -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_ilocked(struct xfs_da_args *args);
int xfs_attr_get(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(struct xfs_da_args *args);
int xfs_attr_set_iter(struct xfs_attr_item *attr); int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_item *attr); int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
bool xfs_attr_namecheck(const void *name, size_t length); bool xfs_attr_namecheck(const void *name, size_t length);
int xfs_attr_calc_size(struct xfs_da_args *args, int *local); 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, void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
unsigned int *total); 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 * Check to see if the attr should be upgraded from non-existent or shortform to
* single-leaf-block attribute list. * single-leaf-block attribute list.
...@@ -634,4 +629,8 @@ xfs_attr_init_replace_state(struct xfs_da_args *args) ...@@ -634,4 +629,8 @@ xfs_attr_init_replace_state(struct xfs_da_args *args)
return xfs_attr_init_add_state(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__ */ #endif /* __XFS_ATTR_H__ */
...@@ -568,7 +568,7 @@ xfs_attr_rmtval_stale( ...@@ -568,7 +568,7 @@ xfs_attr_rmtval_stale(
*/ */
int int
xfs_attr_rmtval_find_space( 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_da_args *args = attr->xattri_da_args;
struct xfs_bmbt_irec *map = &attr->xattri_map; struct xfs_bmbt_irec *map = &attr->xattri_map;
...@@ -598,7 +598,7 @@ xfs_attr_rmtval_find_space( ...@@ -598,7 +598,7 @@ xfs_attr_rmtval_find_space(
*/ */
int int
xfs_attr_rmtval_set_blk( 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_da_args *args = attr->xattri_da_args;
struct xfs_inode *dp = args->dp; struct xfs_inode *dp = args->dp;
...@@ -674,7 +674,7 @@ xfs_attr_rmtval_invalidate( ...@@ -674,7 +674,7 @@ xfs_attr_rmtval_invalidate(
*/ */
int int
xfs_attr_rmtval_remove( xfs_attr_rmtval_remove(
struct xfs_attr_item *attr) struct xfs_attr_intent *attr)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
int error, done; int error, done;
......
...@@ -12,9 +12,9 @@ int xfs_attr_rmtval_get(struct xfs_da_args *args); ...@@ -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, int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
xfs_buf_flags_t incore_flags); xfs_buf_flags_t incore_flags);
int xfs_attr_rmtval_invalidate(struct xfs_da_args *args); 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_rmt_find_hole(struct xfs_da_args *args);
int xfs_attr_rmtval_set_value(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_set_blk(struct xfs_attr_intent *attr);
int xfs_attr_rmtval_find_space(struct xfs_attr_item *attr); int xfs_attr_rmtval_find_space(struct xfs_attr_intent *attr);
#endif /* __XFS_ATTR_REMOTE_H__ */ #endif /* __XFS_ATTR_REMOTE_H__ */
...@@ -51,16 +51,31 @@ xfs_btree_magic( ...@@ -51,16 +51,31 @@ xfs_btree_magic(
return 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( xfs_btree_check_lblock_siblings(
struct xfs_mount *mp, struct xfs_mount *mp,
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
int level, int level,
xfs_fsblock_t fsb, 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; return NULL;
sibling = be64_to_cpu(dsibling);
if (sibling == fsb) if (sibling == fsb)
return __this_address; return __this_address;
if (level >= 0) { if (level >= 0) {
...@@ -74,17 +89,21 @@ xfs_btree_check_lblock_siblings( ...@@ -74,17 +89,21 @@ xfs_btree_check_lblock_siblings(
return NULL; return NULL;
} }
static xfs_failaddr_t static inline xfs_failaddr_t
xfs_btree_check_sblock_siblings( xfs_btree_check_sblock_siblings(
struct xfs_mount *mp, struct xfs_mount *mp,
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
int level, int level,
xfs_agnumber_t agno, xfs_agnumber_t agno,
xfs_agblock_t agbno, 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; return NULL;
sibling = be32_to_cpu(dsibling);
if (sibling == agbno) if (sibling == agbno)
return __this_address; return __this_address;
if (level >= 0) { if (level >= 0) {
...@@ -136,10 +155,10 @@ __xfs_btree_check_lblock( ...@@ -136,10 +155,10 @@ __xfs_btree_check_lblock(
fsb = XFS_DADDR_TO_FSB(mp, xfs_buf_daddr(bp)); fsb = XFS_DADDR_TO_FSB(mp, xfs_buf_daddr(bp));
fa = xfs_btree_check_lblock_siblings(mp, cur, level, fsb, 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) if (!fa)
fa = xfs_btree_check_lblock_siblings(mp, cur, level, fsb, 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; return fa;
} }
...@@ -204,10 +223,10 @@ __xfs_btree_check_sblock( ...@@ -204,10 +223,10 @@ __xfs_btree_check_sblock(
} }
fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno, agbno, 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) if (!fa)
fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno, 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; return fa;
} }
...@@ -426,8 +445,14 @@ xfs_btree_del_cursor( ...@@ -426,8 +445,14 @@ xfs_btree_del_cursor(
break; 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 || 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)) if (unlikely(cur->bc_flags & XFS_BTREE_STAGING))
kmem_free(cur->bc_ops); kmem_free(cur->bc_ops);
if (!(cur->bc_flags & XFS_BTREE_LONG_PTRS) && cur->bc_ag.pag) if (!(cur->bc_flags & XFS_BTREE_LONG_PTRS) && cur->bc_ag.pag)
...@@ -3247,7 +3272,7 @@ xfs_btree_insrec( ...@@ -3247,7 +3272,7 @@ xfs_btree_insrec(
struct xfs_btree_block *block; /* btree block */ struct xfs_btree_block *block; /* btree block */
struct xfs_buf *bp; /* buffer for block */ struct xfs_buf *bp; /* buffer for block */
union xfs_btree_ptr nptr; /* new block ptr */ 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 nkey; /* new block key */
union xfs_btree_key *lkey; union xfs_btree_key *lkey;
int optr; /* old key/record index */ int optr; /* old key/record index */
...@@ -3327,7 +3352,7 @@ xfs_btree_insrec( ...@@ -3327,7 +3352,7 @@ xfs_btree_insrec(
#ifdef DEBUG #ifdef DEBUG
error = xfs_btree_check_block(cur, block, level, bp); error = xfs_btree_check_block(cur, block, level, bp);
if (error) if (error)
return error; goto error0;
#endif #endif
/* /*
...@@ -3347,7 +3372,7 @@ xfs_btree_insrec( ...@@ -3347,7 +3372,7 @@ xfs_btree_insrec(
for (i = numrecs - ptr; i >= 0; i--) { for (i = numrecs - ptr; i >= 0; i--) {
error = xfs_btree_debug_check_ptr(cur, pp, i, level); error = xfs_btree_debug_check_ptr(cur, pp, i, level);
if (error) if (error)
return error; goto error0;
} }
xfs_btree_shift_keys(cur, kp, 1, numrecs - ptr + 1); xfs_btree_shift_keys(cur, kp, 1, numrecs - ptr + 1);
...@@ -3432,6 +3457,8 @@ xfs_btree_insrec( ...@@ -3432,6 +3457,8 @@ xfs_btree_insrec(
return 0; return 0;
error0: error0:
if (ncur)
xfs_btree_del_cursor(ncur, error);
return error; return error;
} }
...@@ -4523,10 +4550,10 @@ xfs_btree_lblock_verify( ...@@ -4523,10 +4550,10 @@ xfs_btree_lblock_verify(
/* sibling pointer verification */ /* sibling pointer verification */
fsb = XFS_DADDR_TO_FSB(mp, xfs_buf_daddr(bp)); fsb = XFS_DADDR_TO_FSB(mp, xfs_buf_daddr(bp));
fa = xfs_btree_check_lblock_siblings(mp, NULL, -1, fsb, 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) if (!fa)
fa = xfs_btree_check_lblock_siblings(mp, NULL, -1, fsb, 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; return fa;
} }
...@@ -4580,10 +4607,10 @@ xfs_btree_sblock_verify( ...@@ -4580,10 +4607,10 @@ xfs_btree_sblock_verify(
agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp)); agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp));
agbno = xfs_daddr_to_agbno(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, 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) if (!fa)
fa = xfs_btree_check_sblock_siblings(mp, NULL, -1, agno, agbno, 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; return fa;
} }
......
...@@ -117,6 +117,17 @@ xfs_da_state_free(xfs_da_state_t *state) ...@@ -117,6 +117,17 @@ xfs_da_state_free(xfs_da_state_t *state)
kmem_cache_free(xfs_da_state_cache, 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) static inline int xfs_dabuf_nfsb(struct xfs_mount *mp, int whichfork)
{ {
if (whichfork == XFS_DATA_FORK) if (whichfork == XFS_DATA_FORK)
......
...@@ -225,6 +225,7 @@ enum xfs_dacmp xfs_da_compname(struct xfs_da_args *args, ...@@ -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); 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_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, void xfs_da3_node_hdr_from_disk(struct xfs_mount *mp,
struct xfs_da3_icnode_hdr *to, struct xfs_da_intnode *from); 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[] = { ...@@ -191,35 +191,56 @@ static const struct xfs_defer_op_type *defer_op_types[] = {
[XFS_DEFER_OPS_TYPE_ATTR] = &xfs_attr_defer_type, [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( xfs_defer_create_intent(
struct xfs_trans *tp, struct xfs_trans *tp,
struct xfs_defer_pending *dfp, struct xfs_defer_pending *dfp,
bool sort) bool sort)
{ {
const struct xfs_defer_op_type *ops = defer_op_types[dfp->dfp_type]; 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) lip = ops->create_intent(tp, &dfp->dfp_work, dfp->dfp_count, sort);
dfp->dfp_intent = ops->create_intent(tp, &dfp->dfp_work, if (!lip)
dfp->dfp_count, sort); return 0;
return dfp->dfp_intent != NULL; 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 * 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 * associated extents, then add the entire intake list to the end of
* the pending list. * 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( xfs_defer_create_intents(
struct xfs_trans *tp) struct xfs_trans *tp)
{ {
struct xfs_defer_pending *dfp; struct xfs_defer_pending *dfp;
bool ret = false; int ret = 0;
list_for_each_entry(dfp, &tp->t_dfops, dfp_list) { list_for_each_entry(dfp, &tp->t_dfops, dfp_list) {
int ret2;
trace_xfs_defer_create_intent(tp->t_mountp, dfp); 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; return ret;
} }
...@@ -457,6 +478,8 @@ xfs_defer_finish_one( ...@@ -457,6 +478,8 @@ xfs_defer_finish_one(
dfp->dfp_count--; dfp->dfp_count--;
error = ops->finish_item(tp, dfp->dfp_done, li, &state); error = ops->finish_item(tp, dfp->dfp_done, li, &state);
if (error == -EAGAIN) { if (error == -EAGAIN) {
int ret;
/* /*
* Caller wants a fresh transaction; put the work item * Caller wants a fresh transaction; put the work item
* back on the list and log a new log intent item to * back on the list and log a new log intent item to
...@@ -467,7 +490,9 @@ xfs_defer_finish_one( ...@@ -467,7 +490,9 @@ xfs_defer_finish_one(
dfp->dfp_count++; dfp->dfp_count++;
dfp->dfp_done = NULL; dfp->dfp_done = NULL;
dfp->dfp_intent = 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) if (error)
...@@ -514,10 +539,14 @@ xfs_defer_finish_noroll( ...@@ -514,10 +539,14 @@ xfs_defer_finish_noroll(
* of time that any one intent item can stick around in memory, * of time that any one intent item can stick around in memory,
* pinning the log tail. * 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); list_splice_init(&(*tp)->t_dfops, &dop_pending);
if (has_intents < 0) {
error = has_intents;
goto out_shutdown;
}
if (has_intents || dfp) { if (has_intents || dfp) {
error = xfs_defer_trans_roll(tp); error = xfs_defer_trans_roll(tp);
if (error) if (error)
...@@ -676,13 +705,15 @@ xfs_defer_ops_capture( ...@@ -676,13 +705,15 @@ xfs_defer_ops_capture(
if (list_empty(&tp->t_dfops)) if (list_empty(&tp->t_dfops))
return NULL; return NULL;
error = xfs_defer_create_intents(tp);
if (error < 0)
return ERR_PTR(error);
/* Create an object to capture the defer ops. */ /* Create an object to capture the defer ops. */
dfc = kmem_zalloc(sizeof(*dfc), KM_NOFS); dfc = kmem_zalloc(sizeof(*dfc), KM_NOFS);
INIT_LIST_HEAD(&dfc->dfc_list); INIT_LIST_HEAD(&dfc->dfc_list);
INIT_LIST_HEAD(&dfc->dfc_dfops); INIT_LIST_HEAD(&dfc->dfc_dfops);
xfs_defer_create_intents(tp);
/* Move the dfops chain and transaction state to the capture struct. */ /* Move the dfops chain and transaction state to the capture struct. */
list_splice_init(&tp->t_dfops, &dfc->dfc_dfops); list_splice_init(&tp->t_dfops, &dfc->dfc_dfops);
dfc->dfc_tpflags = tp->t_flags & XFS_TRANS_LOWMODE; dfc->dfc_tpflags = tp->t_flags & XFS_TRANS_LOWMODE;
...@@ -759,6 +790,10 @@ xfs_defer_ops_capture_and_commit( ...@@ -759,6 +790,10 @@ xfs_defer_ops_capture_and_commit(
/* If we don't capture anything, commit transaction and exit. */ /* If we don't capture anything, commit transaction and exit. */
dfc = xfs_defer_ops_capture(tp); dfc = xfs_defer_ops_capture(tp);
if (IS_ERR(dfc)) {
xfs_trans_cancel(tp);
return PTR_ERR(dfc);
}
if (!dfc) if (!dfc)
return xfs_trans_commit(tp); return xfs_trans_commit(tp);
...@@ -873,10 +908,7 @@ xfs_defer_init_item_caches(void) ...@@ -873,10 +908,7 @@ xfs_defer_init_item_caches(void)
error = xfs_extfree_intent_init_cache(); error = xfs_extfree_intent_init_cache();
if (error) if (error)
goto err; goto err;
error = xfs_attri_init_cache(); error = xfs_attr_intent_init_cache();
if (error)
goto err;
error = xfs_attrd_init_cache();
if (error) if (error)
goto err; goto err;
return 0; return 0;
...@@ -889,8 +921,7 @@ xfs_defer_init_item_caches(void) ...@@ -889,8 +921,7 @@ xfs_defer_init_item_caches(void)
void void
xfs_defer_destroy_item_caches(void) xfs_defer_destroy_item_caches(void)
{ {
xfs_attri_destroy_cache(); xfs_attr_intent_destroy_cache();
xfs_attrd_destroy_cache();
xfs_extfree_intent_destroy_cache(); xfs_extfree_intent_destroy_cache();
xfs_bmap_intent_destroy_cache(); xfs_bmap_intent_destroy_cache();
xfs_refcount_intent_destroy_cache(); xfs_refcount_intent_destroy_cache();
......
...@@ -906,10 +906,18 @@ struct xfs_icreate_log { ...@@ -906,10 +906,18 @@ struct xfs_icreate_log {
* Flags for deferred attribute operations. * Flags for deferred attribute operations.
* Upper bits are flags, lower byte is type code * Upper bits are flags, lower byte is type code
*/ */
#define XFS_ATTR_OP_FLAGS_SET 1 /* Set the attribute */ #define XFS_ATTRI_OP_FLAGS_SET 1 /* Set the attribute */
#define XFS_ATTR_OP_FLAGS_REMOVE 2 /* Remove the attribute */ #define XFS_ATTRI_OP_FLAGS_REMOVE 2 /* Remove the attribute */
#define XFS_ATTR_OP_FLAGS_REPLACE 3 /* Replace the attribute */ #define XFS_ATTRI_OP_FLAGS_REPLACE 3 /* Replace the attribute */
#define XFS_ATTR_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */ #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 * This is the structure used to lay out an attr log item in the
...@@ -924,7 +932,7 @@ struct xfs_attri_log_format { ...@@ -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_op_flags; /* marks the op as a set or remove */
uint32_t alfi_name_len; /* attr name length */ uint32_t alfi_name_len; /* attr name length */
uint32_t alfi_value_len; /* attr value 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 { struct xfs_attrd_log_format {
......
...@@ -110,12 +110,6 @@ struct xlog_recover { ...@@ -110,12 +110,6 @@ struct xlog_recover {
#define ITEM_TYPE(i) (*(unsigned short *)(i)->ri_buf[0].i_addr) #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_CRCPASS 0
#define XLOG_RECOVER_PASS1 1 #define XLOG_RECOVER_PASS1 1
#define XLOG_RECOVER_PASS2 2 #define XLOG_RECOVER_PASS2 2
...@@ -128,5 +122,13 @@ int xlog_recover_iget(struct xfs_mount *mp, xfs_ino_t ino, ...@@ -128,5 +122,13 @@ int xlog_recover_iget(struct xfs_mount *mp, xfs_ino_t ino,
struct xfs_inode **ipp); struct xfs_inode **ipp);
void xlog_recover_release_intent(struct xlog *log, unsigned short intent_type, void xlog_recover_release_intent(struct xlog *log, unsigned short intent_type,
uint64_t intent_id); 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__ */ #endif /* __XFS_LOG_RECOVER_H__ */
...@@ -213,7 +213,7 @@ xfs_symlink_shortform_verify( ...@@ -213,7 +213,7 @@ xfs_symlink_shortform_verify(
/* /*
* Zero length symlinks should never occur in memory as they are * 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) if (!size)
return __this_address; return __this_address;
......
...@@ -340,20 +340,6 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { ...@@ -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 static int
xchk_validate_inputs( xchk_validate_inputs(
struct xfs_mount *mp, struct xfs_mount *mp,
...@@ -478,7 +464,8 @@ xfs_scrub_metadata( ...@@ -478,7 +464,8 @@ xfs_scrub_metadata(
if (error) if (error)
goto out; 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); sc = kmem_zalloc(sizeof(struct xfs_scrub), KM_NOFS | KM_MAYFAIL);
if (!sc) { if (!sc) {
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "xfs_error.h" #include "xfs_error.h"
#include "xfs_acl.h" #include "xfs_acl.h"
#include "xfs_trans.h" #include "xfs_trans.h"
#include "xfs_xattr.h"
#include <linux/posix_acl_xattr.h> #include <linux/posix_acl_xattr.h>
...@@ -202,7 +203,7 @@ __xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type) ...@@ -202,7 +203,7 @@ __xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
xfs_acl_to_disk(args.value, acl); xfs_acl_to_disk(args.value, acl);
} }
error = xfs_attr_set(&args); error = xfs_attr_change(&args);
kmem_free(args.value); kmem_free(args.value);
/* /*
......
...@@ -22,13 +22,15 @@ ...@@ -22,13 +22,15 @@
#include "xfs_attr.h" #include "xfs_attr.h"
#include "xfs_attr_item.h" #include "xfs_attr_item.h"
#include "xfs_trace.h" #include "xfs_trace.h"
#include "xfs_inode.h"
#include "xfs_trans_space.h" #include "xfs_trans_space.h"
#include "xfs_errortag.h" #include "xfs_errortag.h"
#include "xfs_error.h" #include "xfs_error.h"
#include "xfs_log_priv.h" #include "xfs_log_priv.h"
#include "xfs_log_recover.h" #include "xfs_log_recover.h"
struct kmem_cache *xfs_attri_cache;
struct kmem_cache *xfs_attrd_cache;
static const struct xfs_item_ops xfs_attri_item_ops; static const struct xfs_item_ops xfs_attri_item_ops;
static const struct xfs_item_ops xfs_attrd_item_ops; static const struct xfs_item_ops xfs_attrd_item_ops;
static struct xfs_attrd_log_item *xfs_trans_get_attrd(struct xfs_trans *tp, static struct xfs_attrd_log_item *xfs_trans_get_attrd(struct xfs_trans *tp,
...@@ -39,12 +41,80 @@ static inline struct xfs_attri_log_item *ATTRI_ITEM(struct xfs_log_item *lip) ...@@ -39,12 +41,80 @@ static inline struct xfs_attri_log_item *ATTRI_ITEM(struct xfs_log_item *lip)
return container_of(lip, struct xfs_attri_log_item, attri_item); return container_of(lip, struct xfs_attri_log_item, attri_item);
} }
/*
* Shared xattr name/value buffers for logged extended attribute operations
*
* When logging updates to extended attributes, we can create quite a few
* attribute log intent items for a single xattr update. To avoid cycling the
* memory allocator and memcpy overhead, the name (and value, for setxattr)
* are kept in a refcounted object that is shared across all related log items
* and the upper-level deferred work state structure. The shared buffer has
* a control structure, followed by the name, and then the value.
*/
static inline struct xfs_attri_log_nameval *
xfs_attri_log_nameval_get(
struct xfs_attri_log_nameval *nv)
{
if (!refcount_inc_not_zero(&nv->refcount))
return NULL;
return nv;
}
static inline void
xfs_attri_log_nameval_put(
struct xfs_attri_log_nameval *nv)
{
if (!nv)
return;
if (refcount_dec_and_test(&nv->refcount))
kvfree(nv);
}
static inline struct xfs_attri_log_nameval *
xfs_attri_log_nameval_alloc(
const void *name,
unsigned int name_len,
const void *value,
unsigned int value_len)
{
struct xfs_attri_log_nameval *nv;
/*
* This could be over 64kB in length, so we have to use kvmalloc() for
* this. But kvmalloc() utterly sucks, so we use our own version.
*/
nv = xlog_kvmalloc(sizeof(struct xfs_attri_log_nameval) +
name_len + value_len);
if (!nv)
return nv;
nv->name.i_addr = nv + 1;
nv->name.i_len = name_len;
nv->name.i_type = XLOG_REG_TYPE_ATTR_NAME;
memcpy(nv->name.i_addr, name, name_len);
if (value_len) {
nv->value.i_addr = nv->name.i_addr + name_len;
nv->value.i_len = value_len;
memcpy(nv->value.i_addr, value, value_len);
} else {
nv->value.i_addr = NULL;
nv->value.i_len = 0;
}
nv->value.i_type = XLOG_REG_TYPE_ATTR_VALUE;
refcount_set(&nv->refcount, 1);
return nv;
}
STATIC void STATIC void
xfs_attri_item_free( xfs_attri_item_free(
struct xfs_attri_log_item *attrip) struct xfs_attri_log_item *attrip)
{ {
kmem_free(attrip->attri_item.li_lv_shadow); kmem_free(attrip->attri_item.li_lv_shadow);
kvfree(attrip); xfs_attri_log_nameval_put(attrip->attri_nameval);
kmem_cache_free(xfs_attri_cache, attrip);
} }
/* /*
...@@ -73,16 +143,17 @@ xfs_attri_item_size( ...@@ -73,16 +143,17 @@ xfs_attri_item_size(
int *nbytes) int *nbytes)
{ {
struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip);
struct xfs_attri_log_nameval *nv = attrip->attri_nameval;
*nvecs += 2; *nvecs += 2;
*nbytes += sizeof(struct xfs_attri_log_format) + *nbytes += sizeof(struct xfs_attri_log_format) +
xlog_calc_iovec_len(attrip->attri_name_len); xlog_calc_iovec_len(nv->name.i_len);
if (!attrip->attri_value_len) if (!nv->value.i_len)
return; return;
*nvecs += 1; *nvecs += 1;
*nbytes += xlog_calc_iovec_len(attrip->attri_value_len); *nbytes += xlog_calc_iovec_len(nv->value.i_len);
} }
/* /*
...@@ -97,6 +168,7 @@ xfs_attri_item_format( ...@@ -97,6 +168,7 @@ xfs_attri_item_format(
{ {
struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip);
struct xfs_log_iovec *vecp = NULL; struct xfs_log_iovec *vecp = NULL;
struct xfs_attri_log_nameval *nv = attrip->attri_nameval;
attrip->attri_format.alfi_type = XFS_LI_ATTRI; attrip->attri_format.alfi_type = XFS_LI_ATTRI;
attrip->attri_format.alfi_size = 1; attrip->attri_format.alfi_size = 1;
...@@ -108,22 +180,18 @@ xfs_attri_item_format( ...@@ -108,22 +180,18 @@ xfs_attri_item_format(
* the log recovery. * the log recovery.
*/ */
ASSERT(attrip->attri_name_len > 0); ASSERT(nv->name.i_len > 0);
attrip->attri_format.alfi_size++; attrip->attri_format.alfi_size++;
if (attrip->attri_value_len > 0) if (nv->value.i_len > 0)
attrip->attri_format.alfi_size++; attrip->attri_format.alfi_size++;
xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRI_FORMAT, xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRI_FORMAT,
&attrip->attri_format, &attrip->attri_format,
sizeof(struct xfs_attri_log_format)); sizeof(struct xfs_attri_log_format));
xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_NAME, xlog_copy_from_iovec(lv, &vecp, &nv->name);
attrip->attri_name, if (nv->value.i_len > 0)
attrip->attri_name_len); xlog_copy_from_iovec(lv, &vecp, &nv->value);
if (attrip->attri_value_len > 0)
xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_VALUE,
attrip->attri_value,
attrip->attri_value_len);
} }
/* /*
...@@ -158,41 +226,18 @@ xfs_attri_item_release( ...@@ -158,41 +226,18 @@ xfs_attri_item_release(
STATIC struct xfs_attri_log_item * STATIC struct xfs_attri_log_item *
xfs_attri_init( xfs_attri_init(
struct xfs_mount *mp, struct xfs_mount *mp,
uint32_t name_len, struct xfs_attri_log_nameval *nv)
uint32_t value_len)
{ {
struct xfs_attri_log_item *attrip; struct xfs_attri_log_item *attrip;
uint32_t buffer_size = name_len + value_len;
if (buffer_size) { attrip = kmem_cache_zalloc(xfs_attri_cache, GFP_NOFS | __GFP_NOFAIL);
/* /*
* This could be over 64kB in length, so we have to use * Grab an extra reference to the name/value buffer for this log item.
* kvmalloc() for this. But kvmalloc() utterly sucks, so we * The caller retains its own reference!
* use own version.
*/ */
attrip = xlog_kvmalloc(sizeof(struct xfs_attri_log_item) + attrip->attri_nameval = xfs_attri_log_nameval_get(nv);
buffer_size); ASSERT(attrip->attri_nameval);
} else {
attrip = kmem_cache_alloc(xfs_attri_cache,
GFP_NOFS | __GFP_NOFAIL);
}
memset(attrip, 0, sizeof(struct xfs_attri_log_item));
attrip->attri_name_len = name_len;
if (name_len)
attrip->attri_name = ((char *)attrip) +
sizeof(struct xfs_attri_log_item);
else
attrip->attri_name = NULL;
attrip->attri_value_len = value_len;
if (value_len)
attrip->attri_value = ((char *)attrip) +
sizeof(struct xfs_attri_log_item) +
name_len;
else
attrip->attri_value = NULL;
xfs_log_item_init(mp, &attrip->attri_item, XFS_LI_ATTRI, xfs_log_item_init(mp, &attrip->attri_item, XFS_LI_ATTRI,
&xfs_attri_item_ops); &xfs_attri_item_ops);
...@@ -233,7 +278,7 @@ STATIC void ...@@ -233,7 +278,7 @@ STATIC void
xfs_attrd_item_free(struct xfs_attrd_log_item *attrdp) xfs_attrd_item_free(struct xfs_attrd_log_item *attrdp)
{ {
kmem_free(attrdp->attrd_item.li_lv_shadow); kmem_free(attrdp->attrd_item.li_lv_shadow);
kmem_free(attrdp); kmem_cache_free(xfs_attrd_cache, attrdp);
} }
STATIC void STATIC void
...@@ -297,7 +342,7 @@ xfs_attrd_item_intent( ...@@ -297,7 +342,7 @@ xfs_attrd_item_intent(
*/ */
STATIC int STATIC int
xfs_xattri_finish_update( xfs_xattri_finish_update(
struct xfs_attr_item *attr, struct xfs_attr_intent *attr,
struct xfs_attrd_log_item *attrdp) struct xfs_attrd_log_item *attrdp)
{ {
struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_args *args = attr->xattri_da_args;
...@@ -335,7 +380,7 @@ STATIC void ...@@ -335,7 +380,7 @@ STATIC void
xfs_attr_log_item( xfs_attr_log_item(
struct xfs_trans *tp, struct xfs_trans *tp,
struct xfs_attri_log_item *attrip, struct xfs_attri_log_item *attrip,
struct xfs_attr_item *attr) const struct xfs_attr_intent *attr)
{ {
struct xfs_attri_log_format *attrp; struct xfs_attri_log_format *attrp;
...@@ -343,23 +388,18 @@ xfs_attr_log_item( ...@@ -343,23 +388,18 @@ xfs_attr_log_item(
set_bit(XFS_LI_DIRTY, &attrip->attri_item.li_flags); set_bit(XFS_LI_DIRTY, &attrip->attri_item.li_flags);
/* /*
* At this point the xfs_attr_item has been constructed, and we've * At this point the xfs_attr_intent has been constructed, and we've
* created the log intent. Fill in the attri log item and log format * created the log intent. Fill in the attri log item and log format
* structure with fields from this xfs_attr_item * structure with fields from this xfs_attr_intent
*/ */
attrp = &attrip->attri_format; attrp = &attrip->attri_format;
attrp->alfi_ino = attr->xattri_da_args->dp->i_ino; attrp->alfi_ino = attr->xattri_da_args->dp->i_ino;
ASSERT(!(attr->xattri_op_flags & ~XFS_ATTRI_OP_FLAGS_TYPE_MASK));
attrp->alfi_op_flags = attr->xattri_op_flags; attrp->alfi_op_flags = attr->xattri_op_flags;
attrp->alfi_value_len = attr->xattri_da_args->valuelen; attrp->alfi_value_len = attr->xattri_nameval->value.i_len;
attrp->alfi_name_len = attr->xattri_da_args->namelen; attrp->alfi_name_len = attr->xattri_nameval->name.i_len;
attrp->alfi_attr_flags = attr->xattri_da_args->attr_filter; ASSERT(!(attr->xattri_da_args->attr_filter & ~XFS_ATTRI_FILTER_MASK));
attrp->alfi_attr_filter = attr->xattri_da_args->attr_filter;
memcpy(attrip->attri_name, attr->xattri_da_args->name,
attr->xattri_da_args->namelen);
memcpy(attrip->attri_value, attr->xattri_da_args->value,
attr->xattri_da_args->valuelen);
attrip->attri_name_len = attr->xattri_da_args->namelen;
attrip->attri_value_len = attr->xattri_da_args->valuelen;
} }
/* Get an ATTRI. */ /* Get an ATTRI. */
...@@ -372,7 +412,7 @@ xfs_attr_create_intent( ...@@ -372,7 +412,7 @@ xfs_attr_create_intent(
{ {
struct xfs_mount *mp = tp->t_mountp; struct xfs_mount *mp = tp->t_mountp;
struct xfs_attri_log_item *attrip; struct xfs_attri_log_item *attrip;
struct xfs_attr_item *attr; struct xfs_attr_intent *attr;
ASSERT(count == 1); ASSERT(count == 1);
...@@ -383,19 +423,47 @@ xfs_attr_create_intent( ...@@ -383,19 +423,47 @@ xfs_attr_create_intent(
* Each attr item only performs one attribute operation at a time, so * Each attr item only performs one attribute operation at a time, so
* this is a list of one * this is a list of one
*/ */
list_for_each_entry(attr, items, xattri_list) { attr = list_first_entry_or_null(items, struct xfs_attr_intent,
attrip = xfs_attri_init(mp, attr->xattri_da_args->namelen, xattri_list);
attr->xattri_da_args->valuelen);
if (attrip == NULL) /*
return NULL; * Create a buffer to store the attribute name and value. This buffer
* will be shared between the higher level deferred xattr work state
* and the lower level xattr log items.
*/
if (!attr->xattri_nameval) {
struct xfs_da_args *args = attr->xattri_da_args;
/*
* Transfer our reference to the name/value buffer to the
* deferred work state structure.
*/
attr->xattri_nameval = xfs_attri_log_nameval_alloc(args->name,
args->namelen, args->value, args->valuelen);
}
if (!attr->xattri_nameval)
return ERR_PTR(-ENOMEM);
attrip = xfs_attri_init(mp, attr->xattri_nameval);
xfs_trans_add_item(tp, &attrip->attri_item); xfs_trans_add_item(tp, &attrip->attri_item);
xfs_attr_log_item(tp, attrip, attr); xfs_attr_log_item(tp, attrip, attr);
}
return &attrip->attri_item; return &attrip->attri_item;
} }
static inline void
xfs_attr_free_item(
struct xfs_attr_intent *attr)
{
if (attr->xattri_da_state)
xfs_da_state_free(attr->xattri_da_state);
xfs_attri_log_nameval_put(attr->xattri_nameval);
if (attr->xattri_da_args->op_flags & XFS_DA_OP_RECOVERY)
kmem_free(attr);
else
kmem_cache_free(xfs_attr_intent_cache, attr);
}
/* Process an attr. */ /* Process an attr. */
STATIC int STATIC int
xfs_attr_finish_item( xfs_attr_finish_item(
...@@ -404,11 +472,11 @@ xfs_attr_finish_item( ...@@ -404,11 +472,11 @@ xfs_attr_finish_item(
struct list_head *item, struct list_head *item,
struct xfs_btree_cur **state) struct xfs_btree_cur **state)
{ {
struct xfs_attr_item *attr; struct xfs_attr_intent *attr;
struct xfs_attrd_log_item *done_item = NULL; struct xfs_attrd_log_item *done_item = NULL;
int error; int error;
attr = container_of(item, struct xfs_attr_item, xattri_list); attr = container_of(item, struct xfs_attr_intent, xattri_list);
if (done) if (done)
done_item = ATTRD_ITEM(done); done_item = ATTRD_ITEM(done);
...@@ -420,7 +488,7 @@ xfs_attr_finish_item( ...@@ -420,7 +488,7 @@ xfs_attr_finish_item(
error = xfs_xattri_finish_update(attr, done_item); error = xfs_xattri_finish_update(attr, done_item);
if (error != -EAGAIN) if (error != -EAGAIN)
kmem_free(attr); xfs_attr_free_item(attr);
return error; return error;
} }
...@@ -438,33 +506,10 @@ STATIC void ...@@ -438,33 +506,10 @@ STATIC void
xfs_attr_cancel_item( xfs_attr_cancel_item(
struct list_head *item) struct list_head *item)
{ {
struct xfs_attr_item *attr; struct xfs_attr_intent *attr;
attr = container_of(item, struct xfs_attr_item, xattri_list); attr = container_of(item, struct xfs_attr_intent, xattri_list);
kmem_free(attr); xfs_attr_free_item(attr);
}
STATIC xfs_lsn_t
xfs_attri_item_committed(
struct xfs_log_item *lip,
xfs_lsn_t lsn)
{
struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip);
/*
* The attrip refers to xfs_attr_item memory to log the name and value
* with the intent item. This already occurred when the intent was
* committed so these fields are no longer accessed. Clear them out of
* caution since we're about to free the xfs_attr_item.
*/
attrip->attri_name = NULL;
attrip->attri_value = NULL;
/*
* The ATTRI is logged only once and cannot be moved in the log, so
* simply return the lsn at which it's been logged.
*/
return lsn;
} }
STATIC bool STATIC bool
...@@ -482,16 +527,22 @@ xfs_attri_validate( ...@@ -482,16 +527,22 @@ xfs_attri_validate(
struct xfs_attri_log_format *attrp) struct xfs_attri_log_format *attrp)
{ {
unsigned int op = attrp->alfi_op_flags & unsigned int op = attrp->alfi_op_flags &
XFS_ATTR_OP_FLAGS_TYPE_MASK; XFS_ATTRI_OP_FLAGS_TYPE_MASK;
if (attrp->__pad != 0) if (attrp->__pad != 0)
return false; return false;
if (attrp->alfi_op_flags & ~XFS_ATTRI_OP_FLAGS_TYPE_MASK)
return false;
if (attrp->alfi_attr_filter & ~XFS_ATTRI_FILTER_MASK)
return false;
/* alfi_op_flags should be either a set or remove */ /* alfi_op_flags should be either a set or remove */
switch (op) { switch (op) {
case XFS_ATTR_OP_FLAGS_SET: case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTR_OP_FLAGS_REPLACE: case XFS_ATTRI_OP_FLAGS_REPLACE:
case XFS_ATTR_OP_FLAGS_REMOVE: case XFS_ATTRI_OP_FLAGS_REMOVE:
break; break;
default: default:
return false; return false;
...@@ -517,13 +568,14 @@ xfs_attri_item_recover( ...@@ -517,13 +568,14 @@ xfs_attri_item_recover(
struct list_head *capture_list) struct list_head *capture_list)
{ {
struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip);
struct xfs_attr_item *attr; struct xfs_attr_intent *attr;
struct xfs_mount *mp = lip->li_log->l_mp; struct xfs_mount *mp = lip->li_log->l_mp;
struct xfs_inode *ip; struct xfs_inode *ip;
struct xfs_da_args *args; struct xfs_da_args *args;
struct xfs_trans *tp; struct xfs_trans *tp;
struct xfs_trans_res tres; struct xfs_trans_res tres;
struct xfs_attri_log_format *attrp; struct xfs_attri_log_format *attrp;
struct xfs_attri_log_nameval *nv = attrip->attri_nameval;
int error, ret = 0; int error, ret = 0;
int total; int total;
int local; int local;
...@@ -535,41 +587,50 @@ xfs_attri_item_recover( ...@@ -535,41 +587,50 @@ xfs_attri_item_recover(
*/ */
attrp = &attrip->attri_format; attrp = &attrip->attri_format;
if (!xfs_attri_validate(mp, attrp) || if (!xfs_attri_validate(mp, attrp) ||
!xfs_attr_namecheck(attrip->attri_name, attrip->attri_name_len)) !xfs_attr_namecheck(nv->name.i_addr, nv->name.i_len))
return -EFSCORRUPTED; return -EFSCORRUPTED;
error = xlog_recover_iget(mp, attrp->alfi_ino, &ip); error = xlog_recover_iget(mp, attrp->alfi_ino, &ip);
if (error) if (error)
return error; return error;
attr = kmem_zalloc(sizeof(struct xfs_attr_item) + attr = kmem_zalloc(sizeof(struct xfs_attr_intent) +
sizeof(struct xfs_da_args), KM_NOFS); sizeof(struct xfs_da_args), KM_NOFS);
args = (struct xfs_da_args *)(attr + 1); args = (struct xfs_da_args *)(attr + 1);
attr->xattri_da_args = args; attr->xattri_da_args = args;
attr->xattri_op_flags = attrp->alfi_op_flags; attr->xattri_op_flags = attrp->alfi_op_flags &
XFS_ATTRI_OP_FLAGS_TYPE_MASK;
/*
* We're reconstructing the deferred work state structure from the
* recovered log item. Grab a reference to the name/value buffer and
* attach it to the new work state.
*/
attr->xattri_nameval = xfs_attri_log_nameval_get(nv);
ASSERT(attr->xattri_nameval);
args->dp = ip; args->dp = ip;
args->geo = mp->m_attr_geo; args->geo = mp->m_attr_geo;
args->whichfork = XFS_ATTR_FORK; args->whichfork = XFS_ATTR_FORK;
args->name = attrip->attri_name; args->name = nv->name.i_addr;
args->namelen = attrp->alfi_name_len; args->namelen = nv->name.i_len;
args->hashval = xfs_da_hashname(args->name, args->namelen); args->hashval = xfs_da_hashname(args->name, args->namelen);
args->attr_filter = attrp->alfi_attr_flags; args->attr_filter = attrp->alfi_attr_filter & XFS_ATTRI_FILTER_MASK;
args->op_flags = XFS_DA_OP_RECOVERY | XFS_DA_OP_OKNOENT; args->op_flags = XFS_DA_OP_RECOVERY | XFS_DA_OP_OKNOENT;
switch (attrp->alfi_op_flags & XFS_ATTR_OP_FLAGS_TYPE_MASK) { switch (attr->xattri_op_flags) {
case XFS_ATTR_OP_FLAGS_SET: case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTR_OP_FLAGS_REPLACE: case XFS_ATTRI_OP_FLAGS_REPLACE:
args->value = attrip->attri_value; args->value = nv->value.i_addr;
args->valuelen = attrp->alfi_value_len; args->valuelen = nv->value.i_len;
args->total = xfs_attr_calc_size(args, &local); args->total = xfs_attr_calc_size(args, &local);
if (xfs_inode_hasattr(args->dp)) if (xfs_inode_hasattr(args->dp))
attr->xattri_dela_state = xfs_attr_init_replace_state(args); attr->xattri_dela_state = xfs_attr_init_replace_state(args);
else else
attr->xattri_dela_state = xfs_attr_init_add_state(args); attr->xattri_dela_state = xfs_attr_init_add_state(args);
break; break;
case XFS_ATTR_OP_FLAGS_REMOVE: case XFS_ATTRI_OP_FLAGS_REMOVE:
if (!xfs_inode_hasattr(args->dp)) if (!xfs_inode_hasattr(args->dp))
goto out; goto out;
attr->xattri_dela_state = xfs_attr_init_remove_state(args); attr->xattri_dela_state = xfs_attr_init_remove_state(args);
...@@ -613,7 +674,7 @@ xfs_attri_item_recover( ...@@ -613,7 +674,7 @@ xfs_attri_item_recover(
xfs_irele(ip); xfs_irele(ip);
out: out:
if (ret != -EAGAIN) if (ret != -EAGAIN)
kmem_free(attr); xfs_attr_free_item(attr);
return error; return error;
} }
...@@ -636,22 +697,18 @@ xfs_attri_item_relog( ...@@ -636,22 +697,18 @@ xfs_attri_item_relog(
attrdp = xfs_trans_get_attrd(tp, old_attrip); attrdp = xfs_trans_get_attrd(tp, old_attrip);
set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags); set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags);
new_attrip = xfs_attri_init(tp->t_mountp, old_attrp->alfi_name_len, /*
old_attrp->alfi_value_len); * Create a new log item that shares the same name/value buffer as the
* old log item.
*/
new_attrip = xfs_attri_init(tp->t_mountp, old_attrip->attri_nameval);
new_attrp = &new_attrip->attri_format; new_attrp = &new_attrip->attri_format;
new_attrp->alfi_ino = old_attrp->alfi_ino; new_attrp->alfi_ino = old_attrp->alfi_ino;
new_attrp->alfi_op_flags = old_attrp->alfi_op_flags; new_attrp->alfi_op_flags = old_attrp->alfi_op_flags;
new_attrp->alfi_value_len = old_attrp->alfi_value_len; new_attrp->alfi_value_len = old_attrp->alfi_value_len;
new_attrp->alfi_name_len = old_attrp->alfi_name_len; new_attrp->alfi_name_len = old_attrp->alfi_name_len;
new_attrp->alfi_attr_flags = old_attrp->alfi_attr_flags; new_attrp->alfi_attr_filter = old_attrp->alfi_attr_filter;
memcpy(new_attrip->attri_name, old_attrip->attri_name,
new_attrip->attri_name_len);
if (new_attrip->attri_value_len > 0)
memcpy(new_attrip->attri_value, old_attrip->attri_value,
new_attrip->attri_value_len);
xfs_trans_add_item(tp, &new_attrip->attri_item); xfs_trans_add_item(tp, &new_attrip->attri_item);
set_bit(XFS_LI_DIRTY, &new_attrip->attri_item.li_flags); set_bit(XFS_LI_DIRTY, &new_attrip->attri_item.li_flags);
...@@ -666,47 +723,47 @@ xlog_recover_attri_commit_pass2( ...@@ -666,47 +723,47 @@ xlog_recover_attri_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_attri_log_item *attrip; struct xfs_attri_log_item *attrip;
struct xfs_attri_log_format *attri_formatp; struct xfs_attri_log_format *attri_formatp;
int region = 0; struct xfs_attri_log_nameval *nv;
const void *attr_value = NULL;
const void *attr_name;
int error;
attri_formatp = item->ri_buf[region].i_addr; attri_formatp = item->ri_buf[0].i_addr;
attr_name = item->ri_buf[1].i_addr;
/* Validate xfs_attri_log_format */ /* Validate xfs_attri_log_format before the large memory allocation */
if (!xfs_attri_validate(mp, attri_formatp)) { if (!xfs_attri_validate(mp, attri_formatp)) {
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
/* memory alloc failure will cause replay to abort */ if (!xfs_attr_namecheck(attr_name, attri_formatp->alfi_name_len)) {
attrip = xfs_attri_init(mp, attri_formatp->alfi_name_len, XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
return -EFSCORRUPTED;
}
if (attri_formatp->alfi_value_len)
attr_value = item->ri_buf[2].i_addr;
/*
* Memory alloc failure will cause replay to abort. We attach the
* name/value buffer to the recovered incore log item and drop our
* reference.
*/
nv = xfs_attri_log_nameval_alloc(attr_name,
attri_formatp->alfi_name_len, attr_value,
attri_formatp->alfi_value_len); attri_formatp->alfi_value_len);
if (attrip == NULL) if (!nv)
return -ENOMEM; return -ENOMEM;
error = xfs_attri_copy_format(&item->ri_buf[region], attrip = xfs_attri_init(mp, nv);
&attrip->attri_format); error = xfs_attri_copy_format(&item->ri_buf[0], &attrip->attri_format);
if (error) if (error)
goto out; goto out;
region++;
memcpy(attrip->attri_name, item->ri_buf[region].i_addr,
attrip->attri_name_len);
if (!xfs_attr_namecheck(attrip->attri_name, attrip->attri_name_len)) {
XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
error = -EFSCORRUPTED;
goto out;
}
if (attrip->attri_value_len > 0) {
region++;
memcpy(attrip->attri_value, item->ri_buf[region].i_addr,
attrip->attri_value_len);
}
/* /*
* 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
* ensure it makes it into the AIL. Insert the ATTRI into the AIL * ensure it makes it into the AIL. Insert the ATTRI into the AIL
...@@ -715,9 +772,11 @@ xlog_recover_attri_commit_pass2( ...@@ -715,9 +772,11 @@ xlog_recover_attri_commit_pass2(
*/ */
xfs_trans_ail_insert(log->l_ailp, &attrip->attri_item, lsn); xfs_trans_ail_insert(log->l_ailp, &attrip->attri_item, lsn);
xfs_attri_release(attrip); xfs_attri_release(attrip);
xfs_attri_log_nameval_put(nv);
return 0; return 0;
out: out:
xfs_attri_item_free(attrip); xfs_attri_item_free(attrip);
xfs_attri_log_nameval_put(nv);
return error; return error;
} }
...@@ -797,7 +856,6 @@ static const struct xfs_item_ops xfs_attri_item_ops = { ...@@ -797,7 +856,6 @@ static const struct xfs_item_ops xfs_attri_item_ops = {
.iop_size = xfs_attri_item_size, .iop_size = xfs_attri_item_size,
.iop_format = xfs_attri_item_format, .iop_format = xfs_attri_item_format,
.iop_unpin = xfs_attri_item_unpin, .iop_unpin = xfs_attri_item_unpin,
.iop_committed = xfs_attri_item_committed,
.iop_release = xfs_attri_item_release, .iop_release = xfs_attri_item_release,
.iop_recover = xfs_attri_item_recover, .iop_recover = xfs_attri_item_recover,
.iop_match = xfs_attri_item_match, .iop_match = xfs_attri_item_match,
......
...@@ -11,25 +11,30 @@ ...@@ -11,25 +11,30 @@
struct xfs_mount; struct xfs_mount;
struct kmem_zone; 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 * 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 * extended attribute operations need to be processed. An operation is
* currently either a set or remove. Set or remove operations are described by * 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 * 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 * 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 * 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 * attached to the xfs_attr_intent until they are committed. They are freed
* the xfs_attr_item itself is freed when the work is done. * when the xfs_attr_intent itself is freed when the work is done.
*/ */
struct xfs_attri_log_item { struct xfs_attri_log_item {
struct xfs_log_item attri_item; struct xfs_log_item attri_item;
atomic_t attri_refcount; atomic_t attri_refcount;
int attri_name_len; struct xfs_attri_log_nameval *attri_nameval;
int attri_value_len;
void *attri_name;
void *attri_value;
struct xfs_attri_log_format attri_format; struct xfs_attri_log_format attri_format;
}; };
...@@ -43,4 +48,7 @@ struct xfs_attrd_log_item { ...@@ -43,4 +48,7 @@ struct xfs_attrd_log_item {
struct xfs_attrd_log_format attrd_format; 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__ */ #endif /* __XFS_ATTR_ITEM_H__ */
...@@ -23,6 +23,15 @@ ...@@ -23,6 +23,15 @@
#include "xfs_dir2.h" #include "xfs_dir2.h"
#include "xfs_quota.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 * This structure is used during recovery to record the buf log items which
* have been canceled and should not be replayed. * have been canceled and should not be replayed.
...@@ -993,3 +1002,60 @@ const struct xlog_recover_item_ops xlog_buf_item_ops = { ...@@ -993,3 +1002,60 @@ const struct xlog_recover_item_ops xlog_buf_item_ops = {
.commit_pass1 = xlog_recover_buf_commit_pass1, .commit_pass1 = xlog_recover_buf_commit_pass1,
.commit_pass2 = xlog_recover_buf_commit_pass2, .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( ...@@ -576,9 +576,9 @@ xfs_file_dio_write_unaligned(
* don't even bother trying the fast path in this case. * don't even bother trying the fast path in this case.
*/ */
if (iocb->ki_pos > isize || iocb->ki_pos + count >= isize) { if (iocb->ki_pos > isize || iocb->ki_pos + count >= isize) {
retry_exclusive:
if (iocb->ki_flags & IOCB_NOWAIT) if (iocb->ki_flags & IOCB_NOWAIT)
return -EAGAIN; return -EAGAIN;
retry_exclusive:
iolock = XFS_IOLOCK_EXCL; iolock = XFS_IOLOCK_EXCL;
flags = IOMAP_DIO_FORCE_WAIT; flags = IOMAP_DIO_FORCE_WAIT;
} }
......
...@@ -149,12 +149,7 @@ xfs_growfs_data_private( ...@@ -149,12 +149,7 @@ xfs_growfs_data_private(
error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount, error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
delta, &lastag_extended); delta, &lastag_extended);
} else { } else {
static struct ratelimit_state shrink_warning = \ xfs_warn_mount(mp, XFS_OPSTATE_WARNED_SHRINK,
RATELIMIT_STATE_INIT("shrink_warning", 86400 * HZ, 1);
ratelimit_set_flags(&shrink_warning, RATELIMIT_MSG_ON_RELEASE);
if (__ratelimit(&shrink_warning))
xfs_alert(mp,
"EXPERIMENTAL online shrink feature in use. Use at your own risk!"); "EXPERIMENTAL online shrink feature in use. Use at your own risk!");
error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta); error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta);
......
...@@ -2622,7 +2622,7 @@ xfs_ifree( ...@@ -2622,7 +2622,7 @@ xfs_ifree(
*/ */
error = xfs_difree(tp, pag, ip->i_ino, &xic); error = xfs_difree(tp, pag, ip->i_ino, &xic);
if (error) if (error)
return error; goto out;
error = xfs_iunlink_remove(tp, pag, ip); error = xfs_iunlink_remove(tp, pag, ip);
if (error) if (error)
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "xfs_health.h" #include "xfs_health.h"
#include "xfs_reflink.h" #include "xfs_reflink.h"
#include "xfs_ioctl.h" #include "xfs_ioctl.h"
#include "xfs_xattr.h"
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/namei.h> #include <linux/namei.h>
...@@ -524,7 +525,7 @@ xfs_attrmulti_attr_set( ...@@ -524,7 +525,7 @@ xfs_attrmulti_attr_set(
args.valuelen = len; args.valuelen = len;
} }
error = xfs_attr_set(&args); error = xfs_attr_change(&args);
if (!error && (flags & XFS_IOC_ATTR_ROOT)) if (!error && (flags & XFS_IOC_ATTR_ROOT))
xfs_forget_acl(inode, name); xfs_forget_acl(inode, name);
kfree(args.value); kfree(args.value);
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "xfs_iomap.h" #include "xfs_iomap.h"
#include "xfs_error.h" #include "xfs_error.h"
#include "xfs_ioctl.h" #include "xfs_ioctl.h"
#include "xfs_xattr.h"
#include <linux/posix_acl.h> #include <linux/posix_acl.h>
#include <linux/security.h> #include <linux/security.h>
...@@ -61,7 +62,7 @@ xfs_initxattrs( ...@@ -61,7 +62,7 @@ xfs_initxattrs(
.value = xattr->value, .value = xattr->value,
.valuelen = xattr->value_len, .valuelen = xattr->value_len,
}; };
error = xfs_attr_set(&args); error = xfs_attr_change(&args);
if (error < 0) if (error < 0)
break; break;
} }
......
...@@ -3877,44 +3877,3 @@ xlog_drop_incompat_feat( ...@@ -3877,44 +3877,3 @@ xlog_drop_incompat_feat(
{ {
up_read(&log->l_incompat_users); 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, ...@@ -86,6 +86,13 @@ xlog_copy_iovec(struct xfs_log_vec *lv, struct xfs_log_iovec **vecp,
return buf; 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 * 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 * endian issues in treating two 32 bit numbers as one 64 bit number
......
...@@ -428,9 +428,6 @@ struct xlog { ...@@ -428,9 +428,6 @@ struct xlog {
struct rw_semaphore l_incompat_users; 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 * Bits for operational state
*/ */
......
...@@ -39,13 +39,6 @@ STATIC int ...@@ -39,13 +39,6 @@ STATIC int
xlog_clear_stale_blocks( xlog_clear_stale_blocks(
struct xlog *, struct xlog *,
xfs_lsn_t); xfs_lsn_t);
#if defined(DEBUG)
STATIC void
xlog_recover_check_summary(
struct xlog *);
#else
#define xlog_recover_check_summary(log)
#endif
STATIC int STATIC int
xlog_do_recovery_pass( xlog_do_recovery_pass(
struct xlog *, xfs_daddr_t, xfs_daddr_t, int, xfs_daddr_t *); struct xlog *, xfs_daddr_t, xfs_daddr_t, int, xfs_daddr_t *);
...@@ -3230,7 +3223,7 @@ xlog_do_log_recovery( ...@@ -3230,7 +3223,7 @@ xlog_do_log_recovery(
xfs_daddr_t head_blk, xfs_daddr_t head_blk,
xfs_daddr_t tail_blk) xfs_daddr_t tail_blk)
{ {
int error, i; int error;
ASSERT(head_blk != tail_blk); ASSERT(head_blk != tail_blk);
...@@ -3238,37 +3231,25 @@ xlog_do_log_recovery( ...@@ -3238,37 +3231,25 @@ xlog_do_log_recovery(
* First do a pass to find all of the cancelled buf log items. * 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. * Store them in the buf_cancel_table for use in the second pass.
*/ */
log->l_buf_cancel_table = kmem_zalloc(XLOG_BC_TABLE_SIZE * error = xlog_alloc_buf_cancel_table(log);
sizeof(struct list_head), if (error)
0); return error;
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++)
INIT_LIST_HEAD(&log->l_buf_cancel_table[i]);
error = xlog_do_recovery_pass(log, head_blk, tail_blk, error = xlog_do_recovery_pass(log, head_blk, tail_blk,
XLOG_RECOVER_PASS1, NULL); XLOG_RECOVER_PASS1, NULL);
if (error != 0) { if (error != 0)
kmem_free(log->l_buf_cancel_table); goto out_cancel;
log->l_buf_cancel_table = NULL;
return error;
}
/* /*
* Then do a second pass to actually recover the items in the log. * Then do a second pass to actually recover the items in the log.
* When it is complete free the table of buf cancel items. * When it is complete free the table of buf cancel items.
*/ */
error = xlog_do_recovery_pass(log, head_blk, tail_blk, error = xlog_do_recovery_pass(log, head_blk, tail_blk,
XLOG_RECOVER_PASS2, NULL); XLOG_RECOVER_PASS2, NULL);
#ifdef DEBUG if (!error)
if (!error) { xlog_check_buf_cancel_table(log);
int i; out_cancel:
xlog_free_buf_cancel_table(log);
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;
return error; return error;
} }
...@@ -3339,8 +3320,6 @@ xlog_do_recover( ...@@ -3339,8 +3320,6 @@ xlog_do_recover(
} }
mp->m_alloc_set_aside = xfs_alloc_set_aside(mp); mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
xlog_recover_check_summary(log);
/* Normal transactions can now occur */ /* Normal transactions can now occur */
clear_bit(XLOG_ACTIVE_RECOVERY, &log->l_opstate); clear_bit(XLOG_ACTIVE_RECOVERY, &log->l_opstate);
return 0; return 0;
...@@ -3483,7 +3462,6 @@ xlog_recover_finish( ...@@ -3483,7 +3462,6 @@ xlog_recover_finish(
} }
xlog_recover_process_iunlinks(log); xlog_recover_process_iunlinks(log);
xlog_recover_check_summary(log);
/* /*
* Recover any CoW staging blocks that are still referenced by the * Recover any CoW staging blocks that are still referenced by the
...@@ -3517,52 +3495,3 @@ xlog_recover_cancel( ...@@ -3517,52 +3495,3 @@ xlog_recover_cancel(
xlog_recover_cancel_intents(log); 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 { \ ...@@ -75,6 +75,12 @@ do { \
#define xfs_debug_ratelimited(dev, fmt, ...) \ #define xfs_debug_ratelimited(dev, fmt, ...) \
xfs_printk_ratelimited(xfs_debug, dev, fmt, ##__VA_ARGS__) 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, ...) \ #define xfs_warn_once(dev, fmt, ...) \
xfs_printk_once(xfs_warn, dev, fmt, ##__VA_ARGS__) xfs_printk_once(xfs_warn, dev, fmt, ##__VA_ARGS__)
#define xfs_notice_once(dev, fmt, ...) \ #define xfs_notice_once(dev, fmt, ...) \
......
...@@ -1356,7 +1356,6 @@ xfs_clear_incompat_log_features( ...@@ -1356,7 +1356,6 @@ xfs_clear_incompat_log_features(
if (xfs_sb_has_incompat_log_feature(&mp->m_sb, if (xfs_sb_has_incompat_log_feature(&mp->m_sb,
XFS_SB_FEAT_INCOMPAT_LOG_ALL)) { XFS_SB_FEAT_INCOMPAT_LOG_ALL)) {
xfs_info(mp, "Clearing log incompat feature flags.");
xfs_sb_remove_incompat_log_features(&mp->m_sb); xfs_sb_remove_incompat_log_features(&mp->m_sb);
ret = true; ret = true;
} }
......
...@@ -391,6 +391,13 @@ __XFS_HAS_FEAT(nouuid, NOUUID) ...@@ -391,6 +391,13 @@ __XFS_HAS_FEAT(nouuid, NOUUID)
*/ */
#define XFS_OPSTATE_BLOCKGC_ENABLED 6 #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) \ #define __XFS_IS_OPSTATE(name, NAME) \
static inline bool xfs_is_ ## name (struct xfs_mount *mp) \ static inline bool xfs_is_ ## name (struct xfs_mount *mp) \
{ \ { \
...@@ -413,6 +420,12 @@ __XFS_IS_OPSTATE(readonly, READONLY) ...@@ -413,6 +420,12 @@ __XFS_IS_OPSTATE(readonly, READONLY)
__XFS_IS_OPSTATE(inodegc_enabled, INODEGC_ENABLED) __XFS_IS_OPSTATE(inodegc_enabled, INODEGC_ENABLED)
__XFS_IS_OPSTATE(blockgc_enabled, BLOCKGC_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 \ #define XFS_OPSTATE_STRINGS \
{ (1UL << XFS_OPSTATE_UNMOUNTING), "unmounting" }, \ { (1UL << XFS_OPSTATE_UNMOUNTING), "unmounting" }, \
{ (1UL << XFS_OPSTATE_CLEAN), "clean" }, \ { (1UL << XFS_OPSTATE_CLEAN), "clean" }, \
...@@ -420,7 +433,10 @@ __XFS_IS_OPSTATE(blockgc_enabled, BLOCKGC_ENABLED) ...@@ -420,7 +433,10 @@ __XFS_IS_OPSTATE(blockgc_enabled, BLOCKGC_ENABLED)
{ (1UL << XFS_OPSTATE_INODE32), "inode32" }, \ { (1UL << XFS_OPSTATE_INODE32), "inode32" }, \
{ (1UL << XFS_OPSTATE_READONLY), "read_only" }, \ { (1UL << XFS_OPSTATE_READONLY), "read_only" }, \
{ (1UL << XFS_OPSTATE_INODEGC_ENABLED), "inodegc" }, \ { (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 * Max and min values for mount-option defined I/O
......
...@@ -1308,8 +1308,15 @@ xfs_qm_quotacheck( ...@@ -1308,8 +1308,15 @@ xfs_qm_quotacheck(
error = xfs_iwalk_threaded(mp, 0, 0, xfs_qm_dqusage_adjust, 0, true, error = xfs_iwalk_threaded(mp, 0, 0, xfs_qm_dqusage_adjust, 0, true,
NULL); 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; goto error_return;
}
/* /*
* We've made all the changes that we need to make incore. Flush them * We've made all the changes that we need to make incore. Flush them
......
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
#include "xfs_pwork.h" #include "xfs_pwork.h"
#include "xfs_ag.h" #include "xfs_ag.h"
#include "xfs_defer.h" #include "xfs_defer.h"
#include "xfs_attr_item.h"
#include "xfs_xattr.h"
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/fs_context.h> #include <linux/fs_context.h>
...@@ -2079,8 +2081,24 @@ xfs_init_caches(void) ...@@ -2079,8 +2081,24 @@ xfs_init_caches(void)
if (!xfs_bui_cache) if (!xfs_bui_cache)
goto out_destroy_bud_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; 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: out_destroy_bud_cache:
kmem_cache_destroy(xfs_bud_cache); kmem_cache_destroy(xfs_bud_cache);
out_destroy_cui_cache: out_destroy_cui_cache:
...@@ -2127,6 +2145,8 @@ xfs_destroy_caches(void) ...@@ -2127,6 +2145,8 @@ xfs_destroy_caches(void)
* destroy caches. * destroy caches.
*/ */
rcu_barrier(); rcu_barrier();
kmem_cache_destroy(xfs_attri_cache);
kmem_cache_destroy(xfs_attrd_cache);
kmem_cache_destroy(xfs_bui_cache); kmem_cache_destroy(xfs_bui_cache);
kmem_cache_destroy(xfs_bud_cache); kmem_cache_destroy(xfs_bud_cache);
kmem_cache_destroy(xfs_cui_cache); kmem_cache_destroy(xfs_cui_cache);
......
...@@ -91,7 +91,6 @@ extern xfs_agnumber_t xfs_set_inode_alloc(struct xfs_mount *, ...@@ -91,7 +91,6 @@ extern xfs_agnumber_t xfs_set_inode_alloc(struct xfs_mount *,
xfs_agnumber_t agcount); xfs_agnumber_t agcount);
extern const struct export_operations xfs_export_operations; 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 const struct quotactl_ops xfs_quotactl_operations;
extern void xfs_reinit_percpu_counters(struct xfs_mount *mp); extern void xfs_reinit_percpu_counters(struct xfs_mount *mp);
......
...@@ -15,9 +15,86 @@ ...@@ -15,9 +15,86 @@
#include "xfs_da_btree.h" #include "xfs_da_btree.h"
#include "xfs_attr.h" #include "xfs_attr.h"
#include "xfs_acl.h" #include "xfs_acl.h"
#include "xfs_log.h"
#include "xfs_xattr.h"
#include <linux/posix_acl_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 static int
xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused, xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused,
...@@ -56,7 +133,7 @@ xfs_xattr_set(const struct xattr_handler *handler, ...@@ -56,7 +133,7 @@ xfs_xattr_set(const struct xattr_handler *handler,
}; };
int error; int error;
error = xfs_attr_set(&args); error = xfs_attr_change(&args);
if (!error && (handler->flags & XFS_ATTR_ROOT)) if (!error && (handler->flags & XFS_ATTR_ROOT))
xfs_forget_acl(inode, name); xfs_forget_acl(inode, name);
return error; 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