xfs: rework deferred attribute operation setup

Logged attribute intents only have set and remove types - there is
no separate intent type for a replace operation. We should have a
separate type for a replace operation, as it needs to perform
operations that neither SET or REMOVE can perform.

Add this type to the intent items and rearrange the deferred
operation setup to reflect the different operations we are
performing.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Allison Henderson<allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
Dave Chinner 2022-05-11 17:05:23 +10:00 committed by Dave Chinner
parent e22b88de5b
commit 709c863259
5 changed files with 110 additions and 71 deletions

View File

@ -670,6 +670,81 @@ xfs_attr_lookup(
return xfs_attr_node_hasname(args, NULL);
}
static int
xfs_attr_item_init(
struct xfs_da_args *args,
unsigned int op_flags, /* op flag (set or remove) */
struct xfs_attr_item **attr) /* new xfs_attr_item */
{
struct xfs_attr_item *new;
new = kmem_zalloc(sizeof(struct xfs_attr_item), KM_NOFS);
new->xattri_op_flags = op_flags;
new->xattri_da_args = args;
*attr = new;
return 0;
}
/* Sets an attribute for an inode as a deferred operation */
static int
xfs_attr_defer_add(
struct xfs_da_args *args)
{
struct xfs_attr_item *new;
int error = 0;
error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_SET, &new);
if (error)
return error;
new->xattri_dela_state = XFS_DAS_UNINIT;
xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
trace_xfs_attr_defer_add(new->xattri_dela_state, args->dp);
return 0;
}
/* Sets an attribute for an inode as a deferred operation */
static int
xfs_attr_defer_replace(
struct xfs_da_args *args)
{
struct xfs_attr_item *new;
int error = 0;
error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_REPLACE, &new);
if (error)
return error;
new->xattri_dela_state = XFS_DAS_UNINIT;
xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
trace_xfs_attr_defer_replace(new->xattri_dela_state, args->dp);
return 0;
}
/* Removes an attribute for an inode as a deferred operation */
static int
xfs_attr_defer_remove(
struct xfs_da_args *args)
{
struct xfs_attr_item *new;
int error;
error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_REMOVE, &new);
if (error)
return error;
new->xattri_dela_state = XFS_DAS_UNINIT;
xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
trace_xfs_attr_defer_remove(new->xattri_dela_state, args->dp);
return 0;
}
/*
* Note: If args->value is NULL the attribute will be removed, just like the
* Linux ->setattr API.
@ -758,29 +833,35 @@ xfs_attr_set(
}
error = xfs_attr_lookup(args);
if (args->value) {
if (error == -EEXIST && (args->attr_flags & XATTR_CREATE))
goto out_trans_cancel;
if (error == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
goto out_trans_cancel;
if (error != -ENOATTR && error != -EEXIST)
switch (error) {
case -EEXIST:
/* if no value, we are performing a remove operation */
if (!args->value) {
error = xfs_attr_defer_remove(args);
break;
}
/* Pure create fails if the attr already exists */
if (args->attr_flags & XATTR_CREATE)
goto out_trans_cancel;
error = xfs_attr_set_deferred(args);
if (error)
error = xfs_attr_defer_replace(args);
break;
case -ENOATTR:
/* Can't remove what isn't there. */
if (!args->value)
goto out_trans_cancel;
/* shortform attribute has already been committed */
if (!args->trans)
goto out_unlock;
} else {
if (error != -EEXIST)
/* Pure replace fails if no existing attr to replace. */
if (args->attr_flags & XATTR_REPLACE)
goto out_trans_cancel;
error = xfs_attr_remove_deferred(args);
if (error)
error = xfs_attr_defer_add(args);
break;
default:
goto out_trans_cancel;
}
if (error)
goto out_trans_cancel;
/*
* If this is a synchronous mount, make sure that the
@ -844,58 +925,6 @@ xfs_attrd_destroy_cache(void)
xfs_attrd_cache = NULL;
}
STATIC int
xfs_attr_item_init(
struct xfs_da_args *args,
unsigned int op_flags, /* op flag (set or remove) */
struct xfs_attr_item **attr) /* new xfs_attr_item */
{
struct xfs_attr_item *new;
new = kmem_zalloc(sizeof(struct xfs_attr_item), KM_NOFS);
new->xattri_op_flags = op_flags;
new->xattri_da_args = args;
*attr = new;
return 0;
}
/* Sets an attribute for an inode as a deferred operation */
int
xfs_attr_set_deferred(
struct xfs_da_args *args)
{
struct xfs_attr_item *new;
int error = 0;
error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_SET, &new);
if (error)
return error;
xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
return 0;
}
/* Removes an attribute for an inode as a deferred operation */
int
xfs_attr_remove_deferred(
struct xfs_da_args *args)
{
struct xfs_attr_item *new;
int error;
error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_REMOVE, &new);
if (error)
return error;
xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
return 0;
}
/*========================================================================
* External routines when attribute list is inside the inode
*========================================================================*/

View File

@ -521,8 +521,6 @@ bool xfs_attr_namecheck(const void *name, size_t length);
int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
unsigned int *total);
int xfs_attr_set_deferred(struct xfs_da_args *args);
int xfs_attr_remove_deferred(struct xfs_da_args *args);
extern struct kmem_cache *xfs_attri_cache;
extern struct kmem_cache *xfs_attrd_cache;

View File

@ -908,6 +908,7 @@ struct xfs_icreate_log {
*/
#define XFS_ATTR_OP_FLAGS_SET 1 /* Set the attribute */
#define XFS_ATTR_OP_FLAGS_REMOVE 2 /* Remove the attribute */
#define XFS_ATTR_OP_FLAGS_REPLACE 3 /* Replace the attribute */
#define XFS_ATTR_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
/*

View File

@ -318,6 +318,7 @@ xfs_xattri_finish_update(
switch (op) {
case XFS_ATTR_OP_FLAGS_SET:
case XFS_ATTR_OP_FLAGS_REPLACE:
error = xfs_attr_set_iter(attr);
break;
case XFS_ATTR_OP_FLAGS_REMOVE:
@ -507,8 +508,14 @@ xfs_attri_validate(
return false;
/* alfi_op_flags should be either a set or remove */
if (op != XFS_ATTR_OP_FLAGS_SET && op != XFS_ATTR_OP_FLAGS_REMOVE)
switch (op) {
case XFS_ATTR_OP_FLAGS_SET:
case XFS_ATTR_OP_FLAGS_REPLACE:
case XFS_ATTR_OP_FLAGS_REMOVE:
break;
default:
return false;
}
if (attrp->alfi_value_len > XATTR_SIZE_MAX)
return false;

View File

@ -4154,6 +4154,10 @@ DEFINE_DAS_STATE_EVENT(xfs_attr_leaf_addname_return);
DEFINE_DAS_STATE_EVENT(xfs_attr_node_addname_return);
DEFINE_DAS_STATE_EVENT(xfs_attr_remove_iter_return);
DEFINE_DAS_STATE_EVENT(xfs_attr_rmtval_remove_return);
DEFINE_DAS_STATE_EVENT(xfs_attr_defer_add);
DEFINE_DAS_STATE_EVENT(xfs_attr_defer_replace);
DEFINE_DAS_STATE_EVENT(xfs_attr_defer_remove);
TRACE_EVENT(xfs_force_shutdown,
TP_PROTO(struct xfs_mount *mp, int ptag, int flags, const char *fname,