Skip to content

Commit d693534

Browse files
author
Darrick J. Wong
committed
xfs: speed up parent pointer operations when possible
After a recent fsmark benchmarking run, I observed that the overhead of parent pointers on file creation and deletion can be a bit high. On a machine with 20 CPUs, 128G of memory, and an NVME SSD capable of pushing 750000iops, I see the following results: $ mkfs.xfs -f -l logdev=/dev/nvme1n1,size=1g /dev/nvme0n1 -n parent=0 meta-data=/dev/nvme0n1 isize=512 agcount=40, agsize=9767586 blks = sectsz=4096 attr=2, projid32bit=1 = crc=1 finobt=1, sparse=1, rmapbt=1 = reflink=1 bigtime=1 inobtcount=1 nrext64=1 = exchange=0 metadir=0 data = bsize=4096 blocks=390703440, imaxpct=5 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=0 log =/dev/nvme1n1 bsize=4096 blocks=262144, version=2 = sectsz=4096 sunit=1 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 = rgcount=0 rgsize=0 extents = zoned=0 start=0 reserved=0 So we created 40 AGs, one per CPU. Now we create 40 directories and run fsmark: $ time fs_mark -D 10000 -S 0 -n 100000 -s 0 -L 8 -d ... # Version 3.3, 40 thread(s) starting at Wed Dec 10 14:22:07 2025 # Sync method: NO SYNC: Test does not issue sync() or fsync() calls. # Directories: Time based hash between directories across 10000 subdirectories with 180 seconds per subdirectory. # File names: 40 bytes long, (16 initial bytes of time stamp with 24 random bytes at end of name) # Files info: size 0 bytes, written with an IO size of 16384 bytes per write # App overhead is time in microseconds spent in the test not doing file writing related system calls. parent=0 parent=1 ================== ================== real 0m57.573s real 1m2.934s user 3m53.578s user 3m53.508s sys 19m44.440s sys 25m14.810s $ time rm -rf ... parent=0 parent=1 ================== ================== real 0m59.649s real 1m12.505s user 0m41.196s user 0m47.489s sys 13m9.566s sys 20m33.844s Parent pointers increase the system time by 28% overhead to create 32 million files that are totally empty. Removing them incurs a system time increase of 56%. Wall time increases by 9% and 22%. For most filesystems, each file tends to have a single owner and not that many xattrs. If the xattr structure is shortform, then all xattr changes are logged with the inode and do not require the the xattr intent mechanism to persist the parent pointer. Therefore, we can speed up parent pointer operations by calling the shortform xattr functions directly if the child's xattr is in short format. Now the overhead looks like: $ time fs_mark -D 10000 -S 0 -n 100000 -s 0 -L 8 -d ... parent=0 parent=1 ================== ================== real 0m58.030s real 1m0.983s user 3m54.141s user 3m53.758s sys 19m57.003s sys 21m30.605s $ time rm -rf ... parent=0 parent=1 ================== ================== real 0m58.911s real 1m4.420s user 0m41.329s user 0m45.169s sys 13m27.857s sys 15m58.564s Now parent pointers only increase the system time by 8% for creation and 19% for deletion. Wall time increases by 5% and 9% now. Close the performance gap by creating helpers for the attr set, remove, and replace operations that will try to make direct shortform updates, and fall back to the attr intent machinery if that doesn't work. This works for regular xattrs and for parent pointers. Signed-off-by: "Darrick J. Wong" <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
1 parent 1ef7729 commit d693534

3 files changed

Lines changed: 109 additions & 10 deletions

File tree

fs/xfs/libxfs/xfs_attr.c

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,91 @@ xfs_attr_add_fork(
10281028
return error;
10291029
}
10301030

1031+
/*
1032+
* Decide if it is theoretically possible to try to bypass the attr intent
1033+
* mechanism for better performance. Other constraints (e.g. available space
1034+
* in the existing structure) are not considered here.
1035+
*/
1036+
static inline bool
1037+
xfs_attr_can_shortcut(
1038+
const struct xfs_inode *ip)
1039+
{
1040+
return xfs_inode_has_attr_fork(ip) && xfs_attr_is_shortform(ip);
1041+
}
1042+
1043+
/* Try to set an attr in one transaction or fall back to attr intents. */
1044+
int
1045+
xfs_attr_setname(
1046+
struct xfs_da_args *args,
1047+
int rmt_blks)
1048+
{
1049+
int error;
1050+
1051+
if (!rmt_blks && xfs_attr_can_shortcut(args->dp)) {
1052+
args->op_flags |= XFS_DA_OP_ADDNAME;
1053+
1054+
error = xfs_attr_try_sf_addname(args);
1055+
if (error != -ENOSPC)
1056+
return error;
1057+
}
1058+
1059+
xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
1060+
return 0;
1061+
}
1062+
1063+
/* Try to remove an attr in one transaction or fall back to attr intents. */
1064+
int
1065+
xfs_attr_removename(
1066+
struct xfs_da_args *args)
1067+
{
1068+
if (xfs_attr_can_shortcut(args->dp))
1069+
return xfs_attr_sf_removename(args);
1070+
1071+
xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE);
1072+
return 0;
1073+
}
1074+
1075+
/* Try to replace an attr in one transaction or fall back to attr intents. */
1076+
int
1077+
xfs_attr_replacename(
1078+
struct xfs_da_args *args,
1079+
int rmt_blks)
1080+
{
1081+
int error;
1082+
1083+
if (rmt_blks || !xfs_attr_can_shortcut(args->dp)) {
1084+
xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE);
1085+
return 0;
1086+
}
1087+
1088+
args->op_flags |= XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE;
1089+
1090+
error = xfs_attr_sf_removename(args);
1091+
if (error)
1092+
return error;
1093+
1094+
if (args->attr_filter & XFS_ATTR_PARENT) {
1095+
/*
1096+
* Move the new name/value to the regular name/value slots and
1097+
* zero out the new name/value slots because we don't need to
1098+
* log them for a PPTR_SET operation.
1099+
*/
1100+
xfs_attr_update_pptr_replace_args(args);
1101+
args->new_name = NULL;
1102+
args->new_namelen = 0;
1103+
args->new_value = NULL;
1104+
args->new_valuelen = 0;
1105+
}
1106+
args->op_flags &= ~XFS_DA_OP_REPLACE;
1107+
1108+
error = xfs_attr_try_sf_addname(args);
1109+
if (error != -ENOSPC)
1110+
return error;
1111+
1112+
xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
1113+
return 0;
1114+
}
1115+
10311116
/*
10321117
* Make a change to the xattr structure.
10331118
*
@@ -1108,14 +1193,19 @@ xfs_attr_set(
11081193
case -EEXIST:
11091194
if (op == XFS_ATTRUPDATE_REMOVE) {
11101195
/* if no value, we are performing a remove operation */
1111-
xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE);
1196+
error = xfs_attr_removename(args);
1197+
if (error)
1198+
goto out_trans_cancel;
11121199
break;
11131200
}
11141201

11151202
/* Pure create fails if the attr already exists */
11161203
if (op == XFS_ATTRUPDATE_CREATE)
11171204
goto out_trans_cancel;
1118-
xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE);
1205+
1206+
error = xfs_attr_replacename(args, rmt_blks);
1207+
if (error)
1208+
goto out_trans_cancel;
11191209
break;
11201210
case -ENOATTR:
11211211
/* Can't remove what isn't there. */
@@ -1125,7 +1215,10 @@ xfs_attr_set(
11251215
/* Pure replace fails if no existing attr to replace. */
11261216
if (op == XFS_ATTRUPDATE_REPLACE)
11271217
goto out_trans_cancel;
1128-
xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
1218+
1219+
error = xfs_attr_setname(args, rmt_blks);
1220+
if (error)
1221+
goto out_trans_cancel;
11291222
break;
11301223
default:
11311224
goto out_trans_cancel;

fs/xfs/libxfs/xfs_attr.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ struct xfs_trans_res xfs_attr_set_resv(const struct xfs_da_args *args);
573573
*/
574574
static inline bool
575575
xfs_attr_is_shortform(
576-
struct xfs_inode *ip)
576+
const struct xfs_inode *ip)
577577
{
578578
return ip->i_af.if_format == XFS_DINODE_FMT_LOCAL ||
579579
(ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS &&
@@ -649,4 +649,8 @@ void xfs_attr_intent_destroy_cache(void);
649649
int xfs_attr_sf_totsize(struct xfs_inode *dp);
650650
int xfs_attr_add_fork(struct xfs_inode *ip, int size, int rsvd);
651651

652+
int xfs_attr_setname(struct xfs_da_args *args, int rmt_blks);
653+
int xfs_attr_removename(struct xfs_da_args *args);
654+
int xfs_attr_replacename(struct xfs_da_args *args, int rmt_blks);
655+
652656
#endif /* __XFS_ATTR_H__ */

fs/xfs/libxfs/xfs_parent.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "xfs_trans_space.h"
3030
#include "xfs_attr_item.h"
3131
#include "xfs_health.h"
32+
#include "xfs_attr_leaf.h"
3233

3334
struct kmem_cache *xfs_parent_args_cache;
3435

@@ -202,8 +203,8 @@ xfs_parent_addname(
202203
xfs_inode_to_parent_rec(&ppargs->rec, dp);
203204
xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
204205
child->i_ino, parent_name);
205-
xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_SET);
206-
return 0;
206+
207+
return xfs_attr_setname(&ppargs->args, 0);
207208
}
208209

209210
/* Remove a parent pointer to reflect a dirent removal. */
@@ -224,8 +225,8 @@ xfs_parent_removename(
224225
xfs_inode_to_parent_rec(&ppargs->rec, dp);
225226
xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
226227
child->i_ino, parent_name);
227-
xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REMOVE);
228-
return 0;
228+
229+
return xfs_attr_removename(&ppargs->args);
229230
}
230231

231232
/* Replace one parent pointer with another to reflect a rename. */
@@ -250,12 +251,13 @@ xfs_parent_replacename(
250251
child->i_ino, old_name);
251252

252253
xfs_inode_to_parent_rec(&ppargs->new_rec, new_dp);
254+
253255
ppargs->args.new_name = new_name->name;
254256
ppargs->args.new_namelen = new_name->len;
255257
ppargs->args.new_value = &ppargs->new_rec;
256258
ppargs->args.new_valuelen = sizeof(struct xfs_parent_rec);
257-
xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REPLACE);
258-
return 0;
259+
260+
return xfs_attr_replacename(&ppargs->args, 0);
259261
}
260262

261263
/*

0 commit comments

Comments
 (0)