summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/libxfs/xfs_attr.c99
-rw-r--r--fs/xfs/libxfs/xfs_attr.h6
-rw-r--r--fs/xfs/libxfs/xfs_parent.c14
3 files changed, 109 insertions, 10 deletions
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 9a5402d1e9bf..54be75edb2eb 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1029,6 +1029,91 @@ trans_cancel:
}
/*
+ * Decide if it is theoretically possible to try to bypass the attr intent
+ * mechanism for better performance. Other constraints (e.g. available space
+ * in the existing structure) are not considered here.
+ */
+static inline bool
+xfs_attr_can_shortcut(
+ const struct xfs_inode *ip)
+{
+ return xfs_inode_has_attr_fork(ip) && xfs_attr_is_shortform(ip);
+}
+
+/* Try to set an attr in one transaction or fall back to attr intents. */
+int
+xfs_attr_setname(
+ struct xfs_da_args *args,
+ int rmt_blks)
+{
+ int error;
+
+ if (!rmt_blks && xfs_attr_can_shortcut(args->dp)) {
+ args->op_flags |= XFS_DA_OP_ADDNAME;
+
+ error = xfs_attr_try_sf_addname(args);
+ if (error != -ENOSPC)
+ return error;
+ }
+
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
+ return 0;
+}
+
+/* Try to remove an attr in one transaction or fall back to attr intents. */
+int
+xfs_attr_removename(
+ struct xfs_da_args *args)
+{
+ if (xfs_attr_can_shortcut(args->dp))
+ return xfs_attr_sf_removename(args);
+
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE);
+ return 0;
+}
+
+/* Try to replace an attr in one transaction or fall back to attr intents. */
+int
+xfs_attr_replacename(
+ struct xfs_da_args *args,
+ int rmt_blks)
+{
+ int error;
+
+ if (rmt_blks || !xfs_attr_can_shortcut(args->dp)) {
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE);
+ return 0;
+ }
+
+ args->op_flags |= XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE;
+
+ error = xfs_attr_sf_removename(args);
+ if (error)
+ return error;
+
+ if (args->attr_filter & XFS_ATTR_PARENT) {
+ /*
+ * Move the new name/value to the regular name/value slots and
+ * zero out the new name/value slots because we don't need to
+ * log them for a PPTR_SET operation.
+ */
+ xfs_attr_update_pptr_replace_args(args);
+ args->new_name = NULL;
+ args->new_namelen = 0;
+ args->new_value = NULL;
+ args->new_valuelen = 0;
+ }
+ args->op_flags &= ~XFS_DA_OP_REPLACE;
+
+ error = xfs_attr_try_sf_addname(args);
+ if (error != -ENOSPC)
+ return error;
+
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
+ return 0;
+}
+
+/*
* Make a change to the xattr structure.
*
* The caller must have initialized @args, attached dquots, and must not hold
@@ -1108,14 +1193,19 @@ xfs_attr_set(
case -EEXIST:
if (op == XFS_ATTRUPDATE_REMOVE) {
/* if no value, we are performing a remove operation */
- xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE);
+ error = xfs_attr_removename(args);
+ if (error)
+ goto out_trans_cancel;
break;
}
/* Pure create fails if the attr already exists */
if (op == XFS_ATTRUPDATE_CREATE)
goto out_trans_cancel;
- xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE);
+
+ error = xfs_attr_replacename(args, rmt_blks);
+ if (error)
+ goto out_trans_cancel;
break;
case -ENOATTR:
/* Can't remove what isn't there. */
@@ -1125,7 +1215,10 @@ xfs_attr_set(
/* Pure replace fails if no existing attr to replace. */
if (op == XFS_ATTRUPDATE_REPLACE)
goto out_trans_cancel;
- xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
+
+ error = xfs_attr_setname(args, rmt_blks);
+ if (error)
+ goto out_trans_cancel;
break;
default:
goto out_trans_cancel;
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 0e51d0723f9a..8244305949de 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -573,7 +573,7 @@ struct xfs_trans_res xfs_attr_set_resv(const struct xfs_da_args *args);
*/
static inline bool
xfs_attr_is_shortform(
- struct xfs_inode *ip)
+ const struct xfs_inode *ip)
{
return ip->i_af.if_format == XFS_DINODE_FMT_LOCAL ||
(ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS &&
@@ -649,4 +649,8 @@ void xfs_attr_intent_destroy_cache(void);
int xfs_attr_sf_totsize(struct xfs_inode *dp);
int xfs_attr_add_fork(struct xfs_inode *ip, int size, int rsvd);
+int xfs_attr_setname(struct xfs_da_args *args, int rmt_blks);
+int xfs_attr_removename(struct xfs_da_args *args);
+int xfs_attr_replacename(struct xfs_da_args *args, int rmt_blks);
+
#endif /* __XFS_ATTR_H__ */
diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
index 6539f5adae2d..3509cc4b2175 100644
--- a/fs/xfs/libxfs/xfs_parent.c
+++ b/fs/xfs/libxfs/xfs_parent.c
@@ -29,6 +29,7 @@
#include "xfs_trans_space.h"
#include "xfs_attr_item.h"
#include "xfs_health.h"
+#include "xfs_attr_leaf.h"
struct kmem_cache *xfs_parent_args_cache;
@@ -202,8 +203,8 @@ xfs_parent_addname(
xfs_inode_to_parent_rec(&ppargs->rec, dp);
xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
child->i_ino, parent_name);
- xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_SET);
- return 0;
+
+ return xfs_attr_setname(&ppargs->args, 0);
}
/* Remove a parent pointer to reflect a dirent removal. */
@@ -224,8 +225,8 @@ xfs_parent_removename(
xfs_inode_to_parent_rec(&ppargs->rec, dp);
xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
child->i_ino, parent_name);
- xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REMOVE);
- return 0;
+
+ return xfs_attr_removename(&ppargs->args);
}
/* Replace one parent pointer with another to reflect a rename. */
@@ -250,12 +251,13 @@ xfs_parent_replacename(
child->i_ino, old_name);
xfs_inode_to_parent_rec(&ppargs->new_rec, new_dp);
+
ppargs->args.new_name = new_name->name;
ppargs->args.new_namelen = new_name->len;
ppargs->args.new_value = &ppargs->new_rec;
ppargs->args.new_valuelen = sizeof(struct xfs_parent_rec);
- xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REPLACE);
- return 0;
+
+ return xfs_attr_replacename(&ppargs->args, 0);
}
/*