mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
xfs: improve shortform attr performance [2/3]
Improve performance of the xattr (and parent pointer) code when the attr structure is in short format and we can therefore perform all updates in a single transaction. Avoiding the attr intent code brings a very nice speedup in those operations. With a bit of luck, this should all go splendidly. Signed-off-by: "Darrick J. Wong" <djwong@kernel.org> -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQQ2qTKExjcn+O1o2YRKO3ySh0YRpgUCaXbguQAKCRBKO3ySh0YR pkGhAP4q0606NWz+XcF+5f3KlehLBOnpmnozVvudVMCd1rCmpgD9HecarQThh0VI ZHo7LrKQpl+jrg0fhuKcbocQzxGNpgI= =2NAV -----END PGP SIGNATURE----- Merge tag 'attr-pptr-speedup-7.0_2026-01-25' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-7.0-merge xfs: improve shortform attr performance [2/3] Improve performance of the xattr (and parent pointer) code when the attr structure is in short format and we can therefore perform all updates in a single transaction. Avoiding the attr intent code brings a very nice speedup in those operations. With a bit of luck, this should all go splendidly. Signed-off-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
commit
c04ed39d85
6 changed files with 157 additions and 17 deletions
|
|
@ -350,16 +350,14 @@ xfs_attr_set_resv(
|
|||
*/
|
||||
STATIC int
|
||||
xfs_attr_try_sf_addname(
|
||||
struct xfs_inode *dp,
|
||||
struct xfs_da_args *args)
|
||||
{
|
||||
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Build initial attribute list (if required).
|
||||
*/
|
||||
if (dp->i_af.if_format == XFS_DINODE_FMT_EXTENTS)
|
||||
if (args->dp->i_af.if_format == XFS_DINODE_FMT_EXTENTS)
|
||||
xfs_attr_shortform_create(args);
|
||||
|
||||
error = xfs_attr_shortform_addname(args);
|
||||
|
|
@ -371,9 +369,9 @@ xfs_attr_try_sf_addname(
|
|||
* NOTE: this is also the error path (EEXIST, etc).
|
||||
*/
|
||||
if (!error)
|
||||
xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
|
||||
xfs_trans_ichgtime(args->trans, args->dp, XFS_ICHGTIME_CHG);
|
||||
|
||||
if (xfs_has_wsync(dp->i_mount))
|
||||
if (xfs_has_wsync(args->dp->i_mount))
|
||||
xfs_trans_set_sync(args->trans);
|
||||
|
||||
return error;
|
||||
|
|
@ -384,10 +382,9 @@ xfs_attr_sf_addname(
|
|||
struct xfs_attr_intent *attr)
|
||||
{
|
||||
struct xfs_da_args *args = attr->xattri_da_args;
|
||||
struct xfs_inode *dp = args->dp;
|
||||
int error = 0;
|
||||
|
||||
error = xfs_attr_try_sf_addname(dp, args);
|
||||
error = xfs_attr_try_sf_addname(args);
|
||||
if (error != -ENOSPC) {
|
||||
ASSERT(!error || error == -EEXIST);
|
||||
attr->xattri_dela_state = XFS_DAS_DONE;
|
||||
|
|
@ -1031,6 +1028,95 @@ trans_cancel:
|
|||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
error = xfs_attr_shortform_replace(args);
|
||||
if (error != -ENOSPC)
|
||||
return error;
|
||||
|
||||
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.
|
||||
*
|
||||
|
|
@ -1111,14 +1197,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. */
|
||||
|
|
@ -1128,7 +1219,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;
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -842,6 +842,44 @@ xfs_attr_sf_findname(
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace a shortform xattr if it's the right length. Returns 0 on success,
|
||||
* -ENOSPC if the length is wrong, or -ENOATTR if the attr was not found.
|
||||
*/
|
||||
int
|
||||
xfs_attr_shortform_replace(
|
||||
struct xfs_da_args *args)
|
||||
{
|
||||
struct xfs_attr_sf_entry *sfe;
|
||||
|
||||
ASSERT(args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL);
|
||||
|
||||
trace_xfs_attr_sf_replace(args);
|
||||
|
||||
sfe = xfs_attr_sf_findname(args);
|
||||
if (!sfe)
|
||||
return -ENOATTR;
|
||||
|
||||
if (args->attr_filter & XFS_ATTR_PARENT) {
|
||||
if (sfe->namelen != args->new_namelen ||
|
||||
sfe->valuelen != args->new_valuelen)
|
||||
return -ENOSPC;
|
||||
|
||||
memcpy(sfe->nameval, args->new_name, sfe->namelen);
|
||||
memcpy(&sfe->nameval[sfe->namelen], args->new_value,
|
||||
sfe->valuelen);
|
||||
} else {
|
||||
if (sfe->valuelen != args->valuelen)
|
||||
return -ENOSPC;
|
||||
memcpy(&sfe->nameval[sfe->namelen], args->value,
|
||||
sfe->valuelen);
|
||||
}
|
||||
|
||||
xfs_trans_log_inode(args->trans, args->dp,
|
||||
XFS_ILOG_CORE | XFS_ILOG_ADATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a name/value pair to the shortform attribute list.
|
||||
* Overflow from the inode has already been checked for.
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ struct xfs_attr3_icleaf_hdr {
|
|||
* Internal routines when attribute fork size < XFS_LITINO(mp).
|
||||
*/
|
||||
void xfs_attr_shortform_create(struct xfs_da_args *args);
|
||||
int xfs_attr_shortform_replace(struct xfs_da_args *args);
|
||||
void xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff);
|
||||
int xfs_attr_shortform_getvalue(struct xfs_da_args *args);
|
||||
int xfs_attr_shortform_to_leaf(struct xfs_da_args *args);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -2413,6 +2413,7 @@ DEFINE_ATTR_EVENT(xfs_attr_sf_addname);
|
|||
DEFINE_ATTR_EVENT(xfs_attr_sf_create);
|
||||
DEFINE_ATTR_EVENT(xfs_attr_sf_lookup);
|
||||
DEFINE_ATTR_EVENT(xfs_attr_sf_remove);
|
||||
DEFINE_ATTR_EVENT(xfs_attr_sf_replace);
|
||||
DEFINE_ATTR_EVENT(xfs_attr_sf_to_leaf);
|
||||
|
||||
DEFINE_ATTR_EVENT(xfs_attr_leaf_add);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue