* [PATCH 01/24] libxfs: create attr log item opcodes and formats for parent pointers
2024-07-02 0:52 [PATCHSET v13.7 11/16] xfsprogs: Parent Pointers Darrick J. Wong
@ 2024-07-02 1:10 ` Darrick J. Wong
2024-07-02 6:24 ` Christoph Hellwig
0 siblings, 1 reply; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-02 1:10 UTC (permalink / raw)
To: djwong, cem; +Cc: catherine.hoang, linux-xfs, allison.henderson, hch
From: Darrick J. Wong <djwong@kernel.org>
Update xfs_attr_defer_add to use the pptr-specific opcodes if it's
reading or writing parent pointers.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/defer_item.c | 52 +++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 46 insertions(+), 6 deletions(-)
diff --git a/libxfs/defer_item.c b/libxfs/defer_item.c
index 9955e189dfe1..8cdf57eac900 100644
--- a/libxfs/defer_item.c
+++ b/libxfs/defer_item.c
@@ -676,21 +676,61 @@ xfs_attr_defer_add(
enum xfs_attr_defer_op op)
{
struct xfs_attr_intent *new;
+ unsigned int log_op = 0;
+ bool is_pptr = args->attr_filter & XFS_ATTR_PARENT;
- new = kmem_cache_zalloc(xfs_attr_intent_cache, GFP_NOFS | __GFP_NOFAIL);
+ if (is_pptr) {
+ ASSERT(xfs_has_parent(args->dp->i_mount));
+ ASSERT((args->attr_filter & ~XFS_ATTR_PARENT) != 0);
+ ASSERT(args->op_flags & XFS_DA_OP_LOGGED);
+ ASSERT(args->valuelen == sizeof(struct xfs_parent_rec));
+ }
+
+ new = kmem_cache_zalloc(xfs_attr_intent_cache,
+ GFP_NOFS | __GFP_NOFAIL);
new->xattri_da_args = args;
+ /* Compute log operation from the higher level op and namespace. */
switch (op) {
case XFS_ATTR_DEFER_SET:
- new->xattri_op_flags = XFS_ATTRI_OP_FLAGS_SET;
- new->xattri_dela_state = xfs_attr_init_add_state(args);
+ if (is_pptr)
+ log_op = XFS_ATTRI_OP_FLAGS_PPTR_SET;
+ else
+ log_op = XFS_ATTRI_OP_FLAGS_SET;
break;
case XFS_ATTR_DEFER_REPLACE:
- new->xattri_op_flags = XFS_ATTRI_OP_FLAGS_REPLACE;
- new->xattri_dela_state = xfs_attr_init_replace_state(args);
+ if (is_pptr)
+ log_op = XFS_ATTRI_OP_FLAGS_PPTR_REPLACE;
+ else
+ log_op = XFS_ATTRI_OP_FLAGS_REPLACE;
break;
case XFS_ATTR_DEFER_REMOVE:
- new->xattri_op_flags = XFS_ATTRI_OP_FLAGS_REMOVE;
+ if (is_pptr)
+ log_op = XFS_ATTRI_OP_FLAGS_PPTR_REMOVE;
+ else
+ log_op = XFS_ATTRI_OP_FLAGS_REMOVE;
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ new->xattri_op_flags = log_op;
+
+ /* Set up initial attr operation state. */
+ switch (log_op) {
+ case XFS_ATTRI_OP_FLAGS_PPTR_SET:
+ case XFS_ATTRI_OP_FLAGS_SET:
+ new->xattri_dela_state = xfs_attr_init_add_state(args);
+ break;
+ case XFS_ATTRI_OP_FLAGS_PPTR_REPLACE:
+ ASSERT(args->new_valuelen == args->valuelen);
+ new->xattri_dela_state = xfs_attr_init_replace_state(args);
+ break;
+ case XFS_ATTRI_OP_FLAGS_REPLACE:
+ new->xattri_dela_state = xfs_attr_init_replace_state(args);
+ break;
+ case XFS_ATTRI_OP_FLAGS_PPTR_REMOVE:
+ case XFS_ATTRI_OP_FLAGS_REMOVE:
new->xattri_dela_state = xfs_attr_init_remove_state(args);
break;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* Re: [PATCH 01/24] libxfs: create attr log item opcodes and formats for parent pointers
2024-07-02 1:10 ` [PATCH 01/24] libxfs: create attr log item opcodes and formats for parent pointers Darrick J. Wong
@ 2024-07-02 6:24 ` Christoph Hellwig
0 siblings, 0 replies; 296+ messages in thread
From: Christoph Hellwig @ 2024-07-02 6:24 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: cem, catherine.hoang, linux-xfs, allison.henderson, hch
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHBOMB v2] xfsprogs: catch us up to 6.10
@ 2024-07-30 0:10 Darrick J. Wong
2024-07-30 0:16 ` [PATCHSET 01/23] libxfs: fixes for 6.9 Darrick J. Wong
` (22 more replies)
0 siblings, 23 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:10 UTC (permalink / raw)
To: Carlos Maiolino; +Cc: xfs
Hi Carlos,
For archival purposes, this is a resend of every patch that I have
queued (+ reviewed) for xfsprogs 6.10. This will be followed by a pile
of pull requests for all that work.
v2: apply reviewed by tags, accumulate bugfixes
--Darrick
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET 01/23] libxfs: fixes for 6.9
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
@ 2024-07-30 0:16 ` Darrick J. Wong
2024-07-30 0:22 ` [PATCH 1/5] [PATCH v3] Remove support for split-/usr installs Darrick J. Wong
` (4 more replies)
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (21 subsequent siblings)
22 siblings, 5 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:16 UTC (permalink / raw)
To: djwong, cem
Cc: Carlos Maiolino, Christoph Hellwig, Chris Hofstaedtler,
Santiago Kraus, linux-xfs
Hi all,
A couple more last minute fixes for 6.9.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=libxfs-6.9-fixes
---
Commits in this patchset:
* [PATCH v3] Remove support for split-/usr installs
* repair: btree blocks are never on the RT subvolume
* xfile: fix missing error unlock in xfile_fcb_find
* xfs_repair: don't leak the rootdir inode when orphanage already exists
* xfs_repair: don't crash on -vv
---
configure.ac | 21 ---------------------
debian/Makefile | 4 ++--
debian/local/initramfs.hook | 2 +-
debian/rules | 5 ++---
fsck/Makefile | 4 ++--
include/builddefs.in | 2 --
include/buildmacros | 20 ++++++++++----------
libxfs/xfile.c | 6 +++---
mkfs/Makefile | 4 ++--
repair/Makefile | 4 ++--
repair/phase6.c | 7 +++++--
repair/progress.c | 2 +-
repair/scan.c | 21 +++++----------------
repair/xfs_repair.c | 2 +-
14 files changed, 36 insertions(+), 68 deletions(-)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET 02/23] libxfs: sync with 6.10
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
2024-07-30 0:16 ` [PATCHSET 01/23] libxfs: fixes for 6.9 Darrick J. Wong
@ 2024-07-30 0:16 ` Darrick J. Wong
2024-07-30 0:24 ` [PATCH 001/115] xfs: pass xfs_buf lookup flags to xfs_*read_agi Darrick J. Wong
` (114 more replies)
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
` (20 subsequent siblings)
22 siblings, 115 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:16 UTC (permalink / raw)
To: djwong, cem
Cc: Allison Henderson, Darrick J. Wong, Zhang Yi, 刘通,
Ojaswin Mujoo, Ritesh Harjani (IBM), Dave Chinner,
Catherine Hoang, John Garry, Andrey Albershteyn, Disha Goel,
Chandan Babu R, Christoph Hellwig, Mark Tinguely, Wengang Wang,
linux-xfs
Hi all,
Synchronize libxfs with the kernel.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=libxfs-sync-6.10
---
Commits in this patchset:
* xfs: pass xfs_buf lookup flags to xfs_*read_agi
* xfs: constify xfs_bmap_is_written_extent
* xfs: introduce new file range exchange ioctl
* xfs: create a incompat flag for atomic file mapping exchanges
* xfs: introduce a file mapping exchange log intent item
* xfs: create deferred log items for file mapping exchanges
* xfs: add error injection to test file mapping exchange recovery
* xfs: condense extended attributes after a mapping exchange operation
* xfs: condense directories after a mapping exchange operation
* xfs: condense symbolic links after a mapping exchange operation
* xfs: make file range exchange support realtime files
* xfs: capture inode generation numbers in the ondisk exchmaps log item
* xfs: enable logged file mapping exchange feature
* xfs: add an explicit owner field to xfs_da_args
* xfs: use the xfs_da_args owner field to set new dir/attr block owner
* xfs: validate attr leaf buffer owners
* xfs: validate attr remote value buffer owners
* xfs: validate dabtree node buffer owners
* xfs: validate directory leaf buffer owners
* xfs: validate explicit directory data buffer owners
* xfs: validate explicit directory block buffer owners
* xfs: validate explicit directory free block owners
* xfs: use atomic extent swapping to fix user file fork data
* xfs: repair extended attributes
* xfs: expose xfs_bmap_local_to_extents for online repair
* xfs: pass the owner to xfs_symlink_write_target
* xfs: check unused nlink fields in the ondisk inode
* xfs: try to avoid allocating from sick inode clusters
* xfs: pin inodes that would otherwise overflow link count
* xfs: Increase XFS_DEFER_OPS_NR_INODES to 5
* xfs: make XFS_TRANS_LOWMODE match the other XFS_TRANS_ definitions
* xfs: refactor realtime inode locking
* xfs: free RT extents after updating the bmap btree
* xfs: move RT inode locking out of __xfs_bunmapi
* xfs: split xfs_mod_freecounter
* xfs: reinstate RT support in xfs_bmapi_reserve_delalloc
* xfs: cleanup fdblock/frextent accounting in xfs_bmap_del_extent_delay
* xfs: support RT inodes in xfs_mod_delalloc
* xfs: rework splitting of indirect block reservations
* xfs: stop the steal (of data blocks for RT indirect blocks)
* xfs: remove XFS_DA_OP_REMOVE
* xfs: remove XFS_DA_OP_NOTIME
* xfs: remove xfs_da_args.attr_flags
* xfs: make attr removal an explicit operation
* xfs: rearrange xfs_da_args a bit to use less space
* xfs: attr fork iext must be loaded before calling xfs_attr_is_leaf
* xfs: fix missing check for invalid attr flags
* xfs: restructure xfs_attr_complete_op a bit
* xfs: use helpers to extract xattr op from opflags
* xfs: enforce one namespace per attribute
* xfs: rearrange xfs_attr_match parameters
* xfs: check the flags earlier in xfs_attr_match
* xfs: move xfs_attr_defer_add to xfs_attr_item.c
* xfs: create a separate hashname function for extended attributes
* xfs: add parent pointer support to attribute code
* xfs: define parent pointer ondisk extended attribute format
* xfs: allow xattr matching on name and value for parent pointers
* xfs: create attr log item opcodes and formats for parent pointers
* xfs: record inode generation in xattr update log intent items
* xfs: add parent pointer validator functions
* xfs: extend transaction reservations for parent attributes
* xfs: create a hashname function for parent pointers
* xfs: parent pointer attribute creation
* xfs: add parent attributes to link
* xfs: add parent attributes to symlink
* xfs: remove parent pointers in unlink
* xfs: Add parent pointers to rename
* xfs: don't return XFS_ATTR_PARENT attributes via listxattr
* xfs: pass the attr value to put_listent when possible
* xfs: split out handle management helpers a bit
* xfs: add parent pointer ioctls
* xfs: don't remove the attr fork when parent pointers are enabled
* xfs: add a incompat feature bit for parent pointers
* xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res
* xfs: drop compatibility minimum log size computations for reflink
* xfs: enable parent pointers
* xfs: check dirents have parent pointers
* xfs: remove some boilerplate from xfs_attr_set
* xfs: make the reserved block permission flag explicit in xfs_attr_set
* xfs: add raw parent pointer apis to support repair
* xfs: remove pointless unlocked assertion
* xfs: split xfs_bmap_add_attrfork into two pieces
* xfs: actually rebuild the parent pointer xattrs
* xfs: teach online scrub to find directory tree structure problems
* xfs: report directory tree corruption in the health information
* xfs: introduce vectored scrub mode
* xfs: factor out a xfs_dir_lookup_args helper
* xfs: factor out a xfs_dir_createname_args helper
* xfs: factor out a xfs_dir_removename_args helper
* xfs: factor out a xfs_dir_replace_args helper
* xfs: refactor dir format helpers
* xfs: make the seq argument to xfs_bmapi_convert_delalloc() optional
* xfs: make xfs_bmapi_convert_delalloc() to allocate the target offset
* xfs: fix error returns from xfs_bmapi_write
* xfs: remove the unusued tmp_logflags variable in xfs_bmapi_allocate
* xfs: lift a xfs_valid_startblock into xfs_bmapi_allocate
* xfs: don't open code XFS_FILBLKS_MIN in xfs_bmapi_write
* xfs: pass the actual offset and len to allocate to xfs_bmapi_allocate
* xfs: remove the xfs_iext_peek_prev_extent call in xfs_bmapi_allocate
* xfs: fix xfs_bmap_add_extent_delay_real for partial conversions
* xfs: do not allocate the entire delalloc extent in xfs_bmapi_write
* xfs: use unsigned ints for non-negative quantities in xfs_attr_remote.c
* xfs: turn XFS_ATTR3_RMT_BUF_SPACE into a function
* xfs: create a helper to compute the blockcount of a max sized remote value
* xfs: minor cleanups of xfs_attr3_rmt_blocks
* xfs: xfs_quota_unreserve_blkres can't fail
* xfs: simplify iext overflow checking and upgrade
* xfs: Stop using __maybe_unused in xfs_alloc.c
* xfs: fix xfs_init_attr_trans not handling explicit operation codes
* xfs: allow symlinks with short remote targets
* xfs: Add cond_resched to block unmap range and reflink remap path
* xfs: make sure sb_fdblocks is non-negative
* xfs: restrict when we try to align cow fork delalloc to cowextsz hints
* xfs: allow unlinked symlinks and dirs with zero size
* xfs: fix direction in XFS_IOC_EXCHANGE_RANGE
---
db/attr.c | 2
db/attrset.c | 11
db/iunlink.c | 4
db/metadump.c | 8
db/namei.c | 23 -
include/libxfs.h | 1
include/xfs_inode.h | 16 +
include/xfs_mount.h | 2
include/xfs_trace.h | 15 +
io/inject.c | 1
libxfs/Makefile | 5
libxfs/defer_item.c | 116 ++++
libxfs/defer_item.h | 13
libxfs/init.c | 3
libxfs/libxfs_api_defs.h | 6
libxfs/libxfs_priv.h | 75 ++-
libxfs/util.c | 6
libxfs/xfs_ag.c | 12
libxfs/xfs_ag_resv.c | 24 -
libxfs/xfs_ag_resv.h | 2
libxfs/xfs_alloc.c | 10
libxfs/xfs_attr.c | 272 ++++++---
libxfs/xfs_attr.h | 49 +-
libxfs/xfs_attr_leaf.c | 154 ++++-
libxfs/xfs_attr_leaf.h | 4
libxfs/xfs_attr_remote.c | 102 ++--
libxfs/xfs_attr_remote.h | 8
libxfs/xfs_attr_sf.h | 1
libxfs/xfs_bmap.c | 409 ++++++++------
libxfs/xfs_bmap.h | 13
libxfs/xfs_da_btree.c | 189 ++++++-
libxfs/xfs_da_btree.h | 34 +
libxfs/xfs_da_format.h | 37 +
libxfs/xfs_defer.c | 12
libxfs/xfs_defer.h | 10
libxfs/xfs_dir2.c | 283 +++++-----
libxfs/xfs_dir2.h | 23 +
libxfs/xfs_dir2_block.c | 42 +
libxfs/xfs_dir2_data.c | 18 -
libxfs/xfs_dir2_leaf.c | 100 +++
libxfs/xfs_dir2_node.c | 44 +-
libxfs/xfs_dir2_priv.h | 15 -
libxfs/xfs_errortag.h | 4
libxfs/xfs_exchmaps.c | 1232 +++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_exchmaps.h | 124 ++++
libxfs/xfs_format.h | 34 +
libxfs/xfs_fs.h | 158 +++++-
libxfs/xfs_health.h | 4
libxfs/xfs_ialloc.c | 56 ++
libxfs/xfs_ialloc.h | 5
libxfs/xfs_ialloc_btree.c | 4
libxfs/xfs_inode_buf.c | 55 ++
libxfs/xfs_inode_fork.c | 63 +-
libxfs/xfs_inode_fork.h | 6
libxfs/xfs_log_format.h | 89 +++
libxfs/xfs_log_rlimit.c | 46 ++
libxfs/xfs_ondisk.h | 6
libxfs/xfs_parent.c | 376 +++++++++++++
libxfs/xfs_parent.h | 110 ++++
libxfs/xfs_rtbitmap.c | 57 ++
libxfs/xfs_rtbitmap.h | 17 +
libxfs/xfs_sb.c | 15 -
libxfs/xfs_shared.h | 6
libxfs/xfs_symlink_remote.c | 54 ++
libxfs/xfs_symlink_remote.h | 8
libxfs/xfs_trans_resv.c | 324 +++++++++--
libxfs/xfs_trans_space.c | 121 ++++
libxfs/xfs_trans_space.h | 29 +
mkfs/proto.c | 3
repair/attr_repair.c | 7
repair/phase6.c | 35 +
71 files changed, 4349 insertions(+), 873 deletions(-)
create mode 100644 libxfs/xfs_exchmaps.c
create mode 100644 libxfs/xfs_exchmaps.h
create mode 100644 libxfs/xfs_parent.c
create mode 100644 libxfs/xfs_parent.h
create mode 100644 libxfs/xfs_trans_space.c
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 03/23] xfsprogs: atomic file updates
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
2024-07-30 0:16 ` [PATCHSET 01/23] libxfs: fixes for 6.9 Darrick J. Wong
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
@ 2024-07-30 0:17 ` Darrick J. Wong
2024-07-30 0:54 ` [PATCH 01/12] man: document the exchange-range ioctl Darrick J. Wong
` (11 more replies)
2024-07-30 0:17 ` [PATCHSET v30.9 04/23] xfsprogs: set and validate dir/attr block owners Darrick J. Wong
` (19 subsequent siblings)
22 siblings, 12 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:17 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
This series creates a new XFS_IOC_EXCHANGE_RANGE ioctl to exchange
ranges of bytes between two files atomically.
This new functionality enables data storage programs to stage and commit
file updates such that reader programs will see either the old contents
or the new contents in their entirety, with no chance of torn writes. A
successful call completion guarantees that the new contents will be seen
even if the system fails.
The ability to exchange file fork mappings between files in this manner
is critical to supporting online filesystem repair, which is built upon
the strategy of constructing a clean copy of a damaged structure and
committing the new structure into the metadata file atomically. The
ioctls exist to facilitate testing of the new functionality and to
enable future application program designs.
User programs will be able to update files atomically by opening an
O_TMPFILE, reflinking the source file to it, making whatever updates
they want to make, and exchange the relevant ranges of the temp file
with the original file. If the updates are aligned with the file block
size, a new (since v2) flag provides for exchanging only the written
areas. Note that application software must quiesce writes to the file
while it stages an atomic update. This will be addressed by a
subsequent series.
This mechanism solves the clunkiness of two existing atomic file update
mechanisms: for O_TRUNC + rewrite, this eliminates the brief period
where other programs can see an empty file. For create tempfile +
rename, the need to copy file attributes and extended attributes for
each file update is eliminated.
However, this method introduces its own awkwardness -- any program
initiating an exchange now needs to have a way to signal to other
programs that the file contents have changed. For file access mediated
via read and write, fanotify or inotify are probably sufficient. For
mmaped files, that may not be fast enough.
The reference implementation in XFS creates a new log incompat feature
and log intent items to track high level progress of swapping ranges of
two files and finish interrupted work if the system goes down. Sample
code can be found in the corresponding changes to xfs_io to exercise the
use case mentioned above.
Note that this function is /not/ the O_DIRECT atomic untorn file writes
concept that has also been floating around for years. It is also not
the RWF_ATOMIC patchset that has been shared. This RFC is constructed
entirely in software, which means that there are no limitations other
than the general filesystem limits.
As a side note, the original motivation behind the kernel functionality
is online repair of file-based metadata. The atomic file content
exchange is implemented as an atomic exchange of file fork mappings,
which means that we can implement online reconstruction of extended
attributes and directories by building a new one in another inode and
exchanging the contents.
Subsequent patchsets adapt the online filesystem repair code to use
atomic file exchanges. This enables repair functions to construct a
clean copy of a directory, xattr information, symbolic links, realtime
bitmaps, and realtime summary information in a temporary inode. If this
completes successfully, the new contents can be committed atomically
into the inode being repaired. This is essential to avoid making
corruption problems worse if the system goes down in the middle of
running repair.
For userspace, this series also includes the userspace pieces needed to
test the new functionality, and a sample implementation of atomic file
updates.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=atomic-file-updates-6.10
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=atomic-file-updates-6.10
---
Commits in this patchset:
* man: document the exchange-range ioctl
* man: document XFS_FSOP_GEOM_FLAGS_EXCHRANGE
* libhandle: add support for bulkstat v5
* libfrog: add support for exchange range ioctl family
* xfs_db: advertise exchange-range in the version command
* xfs_logprint: support dumping exchmaps log items
* xfs_fsr: convert to bulkstat v5 ioctls
* xfs_fsr: skip the xattr/forkoff levering with the newer swapext implementations
* xfs_io: create exchangerange command to test file range exchange ioctl
* libfrog: advertise exchange-range support
* xfs_repair: add exchange-range to file systems
* mkfs: add a formatting option for exchange-range
---
db/sb.c | 2
fsr/xfs_fsr.c | 167 +++++++++++++--------
include/jdm.h | 23 +++
io/Makefile | 48 +++++-
io/exchrange.c | 156 ++++++++++++++++++++
io/init.c | 1
io/io.h | 1
libfrog/Makefile | 2
libfrog/file_exchange.c | 52 +++++++
libfrog/file_exchange.h | 15 ++
libfrog/fsgeom.c | 49 +++++-
libfrog/fsgeom.h | 1
libhandle/jdm.c | 117 +++++++++++++++
logprint/log_misc.c | 11 +
logprint/log_print_all.c | 12 ++
logprint/log_redo.c | 128 ++++++++++++++++
logprint/logprint.h | 6 +
man/man2/ioctl_xfs_exchange_range.2 | 278 +++++++++++++++++++++++++++++++++++
man/man2/ioctl_xfs_fsgeometry.2 | 3
man/man8/mkfs.xfs.8.in | 7 +
man/man8/xfs_admin.8 | 7 +
man/man8/xfs_io.8 | 40 +++++
mkfs/lts_4.19.conf | 1
mkfs/lts_5.10.conf | 1
mkfs/lts_5.15.conf | 1
mkfs/lts_5.4.conf | 1
mkfs/lts_6.1.conf | 1
mkfs/lts_6.6.conf | 1
mkfs/xfs_mkfs.c | 26 +++
repair/globals.c | 1
repair/globals.h | 1
repair/phase2.c | 30 ++++
repair/xfs_repair.c | 11 +
33 files changed, 1114 insertions(+), 87 deletions(-)
create mode 100644 io/exchrange.c
create mode 100644 libfrog/file_exchange.c
create mode 100644 libfrog/file_exchange.h
create mode 100644 man/man2/ioctl_xfs_exchange_range.2
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 04/23] xfsprogs: set and validate dir/attr block owners
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
@ 2024-07-30 0:17 ` Darrick J. Wong
2024-07-30 0:57 ` [PATCH 1/1] xfs_{db,repair}: add an explicit owner field to xfs_da_args Darrick J. Wong
2024-07-30 0:17 ` [PATCHSET v30.9 05/23] xfsprogs: inode-related repair fixes Darrick J. Wong
` (18 subsequent siblings)
22 siblings, 1 reply; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:17 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
There are a couple of significatn changes that need to be made to the
directory and xattr code before we can support online repairs of those
data structures.
The first change is because online repair is designed to use libxfs to
create a replacement dir/xattr structure in a temporary file, and use
atomic extent swapping to commit the corrected structure. To avoid the
performance hit of walking every block of the new structure to rewrite
the owner number, we instead change libxfs to allow callers of the dir
and xattr code the ability to set an explicit owner number to be written
into the header fields of any new blocks that are created.
The second change is to update the dir/xattr code to actually *check*
the owner number in each block that is read off the disk, since we don't
currently do that.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=dirattr-validate-owners-6.10
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=dirattr-validate-owners-6.10
---
Commits in this patchset:
* xfs_{db,repair}: add an explicit owner field to xfs_da_args
---
db/namei.c | 1 +
repair/phase6.c | 3 +++
2 files changed, 4 insertions(+)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 05/23] xfsprogs: inode-related repair fixes
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 0:17 ` [PATCHSET v30.9 04/23] xfsprogs: set and validate dir/attr block owners Darrick J. Wong
@ 2024-07-30 0:17 ` Darrick J. Wong
2024-07-30 0:57 ` [PATCH 1/2] libxfs: port the bumplink function from the kernel Darrick J. Wong
2024-07-30 0:57 ` [PATCH 2/2] mkfs/repair: pin inodes that would otherwise overflow link count Darrick J. Wong
2024-07-30 0:18 ` [PATCHSET v30.9 06/23] xfs_scrub: fixes to the repair code Darrick J. Wong
` (17 subsequent siblings)
22 siblings, 2 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:17 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
While doing QA of the online fsck code, I made a few observations:
First, nobody was checking that the di_onlink field is actually zero;
Second, that allocating a temporary file for repairs can fail (and
thus bring down the entire fs) if the inode cluster is corrupt; and
Third, that file link counts do not pin at ~0U to prevent integer
overflows.
This scattered patchset fixes those three problems.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=inode-repair-improvements-6.10
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=inode-repair-improvements-6.10
---
Commits in this patchset:
* libxfs: port the bumplink function from the kernel
* mkfs/repair: pin inodes that would otherwise overflow link count
---
include/xfs_inode.h | 2 ++
libxfs/util.c | 18 ++++++++++++++++++
mkfs/proto.c | 4 ++--
repair/incore_ino.c | 14 +++++++++-----
repair/phase6.c | 10 +++++-----
5 files changed, 36 insertions(+), 12 deletions(-)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 06/23] xfs_scrub: fixes to the repair code
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 0:17 ` [PATCHSET v30.9 05/23] xfsprogs: inode-related repair fixes Darrick J. Wong
@ 2024-07-30 0:18 ` Darrick J. Wong
2024-07-30 0:57 ` [PATCH 1/5] xfs_scrub: remove ALP_* flags namespace Darrick J. Wong
` (4 more replies)
2024-07-30 0:18 ` [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs Darrick J. Wong
` (16 subsequent siblings)
22 siblings, 5 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:18 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
Now that we've landed the new kernel code, it's time to reorganize the
xfs_scrub code that handles repairs. Clean up various naming warts and
misleading error messages. Move the repair code to scrub/repair.c as
the first step. Then, fix various issues in the repair code before we
start reorganizing things.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-repair-fixes-6.10
---
Commits in this patchset:
* xfs_scrub: remove ALP_* flags namespace
* xfs_scrub: move repair functions to repair.c
* xfs_scrub: log when a repair was unnecessary
* xfs_scrub: require primary superblock repairs to complete before proceeding
* xfs_scrub: actually try to fix summary counters ahead of repairs
---
scrub/phase1.c | 2
scrub/phase2.c | 3 -
scrub/phase3.c | 2
scrub/phase4.c | 22 ++++-
scrub/phase5.c | 2
scrub/phase7.c | 2
scrub/repair.c | 177 ++++++++++++++++++++++++++++++++++++++++++-
scrub/repair.h | 16 +++-
scrub/scrub.c | 204 +------------------------------------------------
scrub/scrub.h | 16 ----
scrub/scrub_private.h | 55 +++++++++++++
11 files changed, 269 insertions(+), 232 deletions(-)
create mode 100644 scrub/scrub_private.h
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (5 preceding siblings ...)
2024-07-30 0:18 ` [PATCHSET v30.9 06/23] xfs_scrub: fixes to the repair code Darrick J. Wong
@ 2024-07-30 0:18 ` Darrick J. Wong
2024-07-30 0:59 ` [PATCH 1/8] xfs_scrub: fix missing scrub coverage for broken inodes Darrick J. Wong
` (7 more replies)
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
` (15 subsequent siblings)
22 siblings, 8 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:18 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
While I was poking through the QA results for xfs_scrub, I noticed that
it doesn't warn the user when the primary and secondary realtime
metadata are so out of whack that the chances of a successful repair are
not so high. I decided that it was worth refactoring the scrub code a
bit so that we could warn the user about these types of things, and
ended up refactoring unnecessary helpers out of existence and fixing
other reporting gaps.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-better-repair-warnings-6.10
---
Commits in this patchset:
* xfs_scrub: fix missing scrub coverage for broken inodes
* xfs_scrub: collapse trivial superblock scrub helpers
* xfs_scrub: get rid of trivial fs metadata scanner helpers
* xfs_scrub: split up the mustfix repairs and difficulty assessment functions
* xfs_scrub: add missing repair types to the mustfix and difficulty assessment
* xfs_scrub: any inconsistency in metadata should trigger difficulty warnings
* xfs_scrub: warn about difficult repairs to rt and quota metadata
* xfs_scrub: enable users to bump information messages to warnings
---
man/man8/xfs_scrub.8 | 19 ++++++++++++++++++
scrub/common.c | 2 ++
scrub/phase1.c | 2 +-
scrub/phase2.c | 53 +++++++++++++++++++++++++++++++-------------------
scrub/phase3.c | 21 ++++++++++++++++----
scrub/phase4.c | 9 +++++---
scrub/phase5.c | 15 +++++++-------
scrub/repair.c | 47 ++++++++++++++++++++++++++++++++++----------
scrub/repair.h | 10 +++++++--
scrub/scrub.c | 52 +------------------------------------------------
scrub/scrub.h | 7 ++-----
scrub/xfs_scrub.c | 45 ++++++++++++++++++++++++++++++++++++++++++
scrub/xfs_scrub.h | 1 +
13 files changed, 175 insertions(+), 108 deletions(-)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (6 preceding siblings ...)
2024-07-30 0:18 ` [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs Darrick J. Wong
@ 2024-07-30 0:18 ` Darrick J. Wong
2024-07-30 1:01 ` [PATCH 1/9] xfs_scrub: track repair items by principal, not by individual repairs Darrick J. Wong
` (8 more replies)
2024-07-30 0:18 ` [PATCHSET v30.9 09/23] xfs_scrub: use scrub_item to track check progress Darrick J. Wong
` (14 subsequent siblings)
22 siblings, 9 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:18 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
Certain kinds of XFS metadata depend on the correctness of lower level
metadata. For example, directory indexes depends on the directory data
fork, which in turn depend on the directory inode to be correct. The
current scrub code does not strictly preserve these dependencies if it
has to defer a repair until phase 4, because phase 4 prioritizes repairs
by type (corruption, then cross referencing, and then preening) and
loses the ordering of in the previous phases. This leads to absurd
things like trying to repair a directory before repairing its corrupted
fork, which is absurd.
To solve this problem, introduce a repair ticket structure to track all
the repairs pending for a principal object (inode, AG, etc). This
reduces memory requirements if an object requires more than one type of
repair and makes it very easy to track the data dependencies between
sub-objects of a principal object. Repair dependencies between object
types (e.g. bnobt before inodes) must still be encoded statically into
phase 4.
A secondary benefit of this new ticket structure is that we can decide
to attempt a repair of an object A that was flagged for a cross
referencing error during the scan if a different object B depends on A
but only B showed definitive signs of corruption.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-repair-data-deps-6.10
---
Commits in this patchset:
* xfs_scrub: track repair items by principal, not by individual repairs
* xfs_scrub: use repair_item to direct repair activities
* xfs_scrub: remove action lists from phaseX code
* xfs_scrub: remove scrub_metadata_file
* xfs_scrub: boost the repair priority of dependencies of damaged items
* xfs_scrub: clean up repair_item_difficulty a little
* xfs_scrub: check dependencies of a scrub type before repairing
* xfs_scrub: retry incomplete repairs
* xfs_scrub: remove unused action_list fields
---
libfrog/scrub.c | 1
scrub/phase1.c | 9 -
scrub/phase2.c | 46 ++--
scrub/phase3.c | 77 ++++---
scrub/phase4.c | 17 +-
scrub/phase5.c | 9 -
scrub/phase7.c | 9 -
scrub/repair.c | 530 +++++++++++++++++++++++++++++++++----------------
scrub/repair.h | 47 +++-
scrub/scrub.c | 136 ++++++-------
scrub/scrub.h | 108 ++++++++--
scrub/scrub_private.h | 37 +++
12 files changed, 664 insertions(+), 362 deletions(-)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 09/23] xfs_scrub: use scrub_item to track check progress
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (7 preceding siblings ...)
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
@ 2024-07-30 0:18 ` Darrick J. Wong
2024-07-30 1:03 ` [PATCH 1/5] xfs_scrub: start tracking scrub state in scrub_item Darrick J. Wong
` (4 more replies)
2024-07-30 0:19 ` [PATCHSET v30.9 10/23] xfs_scrub: improve scheduling of repair items Darrick J. Wong
` (13 subsequent siblings)
22 siblings, 5 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:18 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
Now that we've introduced tickets to track the status of repairs to a
specific principal XFS object (fs, ag, file), use them to track the
scrub state of those same objects. Ultimately, we want to make it easy
to introduce vectorized repair, where we send a batch of repair requests
to the kernel instead of making millions of ioctl calls. For now,
however, we'll settle for easier bookkeepping.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-object-tracking-6.10
---
Commits in this patchset:
* xfs_scrub: start tracking scrub state in scrub_item
* xfs_scrub: remove enum check_outcome
* xfs_scrub: refactor scrub_meta_type out of existence
* xfs_scrub: hoist repair retry loop to repair_item_class
* xfs_scrub: hoist scrub retry loop to scrub_item_check_file
---
scrub/phase1.c | 3
scrub/phase2.c | 12 +-
scrub/phase3.c | 41 ++----
scrub/phase4.c | 16 +-
scrub/phase5.c | 5 -
scrub/phase7.c | 5 +
scrub/repair.c | 71 +++++------
scrub/scrub.c | 321 ++++++++++++++++++++++---------------------------
scrub/scrub.h | 40 ++++--
scrub/scrub_private.h | 14 ++
10 files changed, 257 insertions(+), 271 deletions(-)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 10/23] xfs_scrub: improve scheduling of repair items
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (8 preceding siblings ...)
2024-07-30 0:18 ` [PATCHSET v30.9 09/23] xfs_scrub: use scrub_item to track check progress Darrick J. Wong
@ 2024-07-30 0:19 ` Darrick J. Wong
2024-07-30 1:05 ` [PATCH 1/4] libfrog: enhance ptvar to support initializer functions Darrick J. Wong
` (3 more replies)
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
` (12 subsequent siblings)
22 siblings, 4 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:19 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
Currently, phase 4 of xfs_scrub uses per-AG repair item lists to
schedule repair work across a thread pool. This scheme is suboptimal
when most of the repairs involve a single AG because all the work gets
dumped on a single pool thread.
Instead, we should create a thread pool with the same number of workers
as CPUs, and dispatch individual repair tickets as separate work items
to maximize parallelization.
However, we also need to ensure that repairs to space metadata and file
metadata are kept in separate queues because file repairs generally
depend on correctness of space metadata.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-repair-scheduling-6.10
---
Commits in this patchset:
* libfrog: enhance ptvar to support initializer functions
* xfs_scrub: improve thread scheduling repair items during phase 4
* xfs_scrub: recheck entire metadata objects after corruption repairs
* xfs_scrub: try to repair space metadata before file metadata
---
libfrog/ptvar.c | 9 ++
libfrog/ptvar.h | 4 +
scrub/counter.c | 2
scrub/descr.c | 2
scrub/phase1.c | 15 ++-
scrub/phase2.c | 23 ++++-
scrub/phase3.c | 106 ++++++++++++++-------
scrub/phase4.c | 244 ++++++++++++++++++++++++++++++++++++-------------
scrub/phase7.c | 2
scrub/read_verify.c | 2
scrub/repair.c | 172 ++++++++++++++++++++++-------------
scrub/repair.h | 37 ++++++-
scrub/scrub.c | 5 +
scrub/scrub.h | 10 ++
scrub/scrub_private.h | 2
scrub/xfs_scrub.h | 3 -
16 files changed, 455 insertions(+), 183 deletions(-)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (9 preceding siblings ...)
2024-07-30 0:19 ` [PATCHSET v30.9 10/23] xfs_scrub: improve scheduling of repair items Darrick J. Wong
@ 2024-07-30 0:19 ` Darrick J. Wong
2024-07-30 1:06 ` [PATCH 01/13] xfs_scrub: use proper UChar string iterators Darrick J. Wong
` (12 more replies)
2024-07-30 0:19 ` [PATCHSET v30.9 12/23] xfs_scrub: move fstrim to a separate phase Darrick J. Wong
` (11 subsequent siblings)
22 siblings, 13 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:19 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
In early 2023, malware researchers disclosed a phishing attack that was
targeted at people running Linux workstations. The attack vector
involved the use of filenames containing what looked like a file
extension but instead contained a lookalike for the full stop (".")
and a common extension ("pdf"). Enhance xfs_scrub phase 5 to detect
these types of attacks and warn the system administrator.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-detect-deceptive-extensions-6.10
---
Commits in this patchset:
* xfs_scrub: use proper UChar string iterators
* xfs_scrub: hoist code that removes ignorable characters
* xfs_scrub: add a couple of omitted invisible code points
* xfs_scrub: avoid potential UAF after freeing a duplicate name entry
* xfs_scrub: guard against libicu returning negative buffer lengths
* xfs_scrub: hoist non-rendering character predicate
* xfs_scrub: store bad flags with the name entry
* xfs_scrub: rename UNICRASH_ZERO_WIDTH to UNICRASH_INVISIBLE
* xfs_scrub: type-coerce the UNICRASH_* flags
* xfs_scrub: reduce size of struct name_entry
* xfs_scrub: rename struct unicrash.normalizer
* xfs_scrub: report deceptive file extensions
* xfs_scrub: dump unicode points
---
scrub/unicrash.c | 532 +++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 426 insertions(+), 106 deletions(-)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 12/23] xfs_scrub: move fstrim to a separate phase
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (10 preceding siblings ...)
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
@ 2024-07-30 0:19 ` Darrick J. Wong
2024-07-30 1:09 ` [PATCH 1/7] xfs_scrub: move FITRIM to phase 8 Darrick J. Wong
` (6 more replies)
2024-07-30 0:19 ` [PATCHSET v30.9 13/23] xfs_scrub: use free space histograms to reduce fstrim runtime Darrick J. Wong
` (10 subsequent siblings)
22 siblings, 7 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:19 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
Back when I originally designed xfs_scrub, all filesystem metadata
checks were complete by the end of phase 3, and phase 4 was where all
the metadata repairs occurred. On the grounds that the filesystem
should be fully consistent by then, I made a call to FITRIM at the end
of phase 4 to discard empty space in the filesystem.
Unfortunately, that's no longer the case -- summary counters, link
counts, and quota counters are not checked until phase 7. It's not safe
to instruct the storage to unmap "empty" areas if we don't know where
those empty areas are, so we need to create a phase 8 to trim the fs.
While we're at it, make it more obvious that fstrim only gets to run if
there are no unfixed corruptions and no other runtime errors have
occurred.
Finally, reduce the latency impacts on the rest of the system by
breaking up the fstrim work into a loop that targets only 16GB per call.
This enables better progress reporting for interactive runs and cgroup
based resource constraints for background runs.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-fstrim-phase-6.10
---
Commits in this patchset:
* xfs_scrub: move FITRIM to phase 8
* xfs_scrub: ignore phase 8 if the user disabled fstrim
* xfs_scrub: collapse trim_filesystem
* xfs_scrub: fix the work estimation for phase 8
* xfs_scrub: report FITRIM errors properly
* xfs_scrub: don't call FITRIM after runtime errors
* xfs_scrub: improve responsiveness while trimming the filesystem
---
scrub/Makefile | 1
scrub/phase4.c | 30 +----------
scrub/phase8.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++
scrub/vfs.c | 22 +++++---
scrub/vfs.h | 2 -
scrub/xfs_scrub.c | 11 ++++
scrub/xfs_scrub.h | 3 +
7 files changed, 183 insertions(+), 37 deletions(-)
create mode 100644 scrub/phase8.c
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 13/23] xfs_scrub: use free space histograms to reduce fstrim runtime
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (11 preceding siblings ...)
2024-07-30 0:19 ` [PATCHSET v30.9 12/23] xfs_scrub: move fstrim to a separate phase Darrick J. Wong
@ 2024-07-30 0:19 ` Darrick J. Wong
2024-07-30 1:11 ` [PATCH 1/7] libfrog: hoist free space histogram code Darrick J. Wong
` (6 more replies)
2024-07-30 0:20 ` [PATCHSET v30.9 14/23] xfs_scrub: tighten security of systemd services Darrick J. Wong
` (9 subsequent siblings)
22 siblings, 7 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:19 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
This patchset dramatically reduces the runtime of the FITRIM calls made
during phase 8 of xfs_scrub. It turns out that phase 8 can really get
bogged down if the free space contains a large number of very small
extents. In these cases, the runtime can increase by an order of
magnitude to free less than 1% of the free space. This is not worth the
time, since we're spending a lot of time to do very little work. The
FITRIM ioctl allows us to specify a minimum extent length, so we can use
statistical methods to compute a minlen parameter.
It turns out xfs_db/spaceman already have the code needed to create
histograms of free space extent lengths. We add the ability to compute
a CDF of the extent lengths, which make it easy to pick a minimum length
corresponding to 99% of the free space. In most cases, this results in
dramatic reductions in phase 8 runtime. Hence, move the histogram code
to libfrog, and wire up xfs_scrub, since phase 7 already walks the
fsmap.
We also add a new -o suboption to xfs_scrub so that people who /do/ want
to examine every free extent can do so.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-fstrim-minlen-freesp-histogram-6.10
---
Commits in this patchset:
* libfrog: hoist free space histogram code
* libfrog: print wider columns for free space histogram
* libfrog: print cdf of free space buckets
* xfs_scrub: don't close stdout when closing the progress bar
* xfs_scrub: remove pointless spacemap.c arguments
* xfs_scrub: collect free space histograms during phase 7
* xfs_scrub: tune fstrim minlen parameter based on free space histograms
---
db/freesp.c | 89 ++++------------
libfrog/Makefile | 2
libfrog/histogram.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++
libfrog/histogram.h | 77 ++++++++++++++
man/man8/xfs_scrub.8 | 16 +++
scrub/phase7.c | 47 ++++++++-
scrub/phase8.c | 91 ++++++++++++++++-
scrub/spacemap.c | 11 +-
scrub/vfs.c | 4 +
scrub/vfs.h | 2
scrub/xfs_scrub.c | 45 ++++++++
scrub/xfs_scrub.h | 16 +++
spaceman/freesp.c | 99 ++++++------------
13 files changed, 619 insertions(+), 150 deletions(-)
create mode 100644 libfrog/histogram.c
create mode 100644 libfrog/histogram.h
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 14/23] xfs_scrub: tighten security of systemd services
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (12 preceding siblings ...)
2024-07-30 0:19 ` [PATCHSET v30.9 13/23] xfs_scrub: use free space histograms to reduce fstrim runtime Darrick J. Wong
@ 2024-07-30 0:20 ` Darrick J. Wong
2024-07-30 1:13 ` [PATCH 1/6] xfs_scrub: allow auxiliary pathnames for sandboxing Darrick J. Wong
` (5 more replies)
2024-07-30 0:20 ` [PATCHSET v30.9 15/23] xfs_scrub_all: automatic media scan service Darrick J. Wong
` (8 subsequent siblings)
22 siblings, 6 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:20 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Helle Vaanzinn, linux-xfs
Hi all,
To reduce the risk of the online fsck service suffering some sort of
catastrophic breach that results in attackers reconfiguring the running
system, I embarked on a security audit of the systemd service files.
The result should be that all elements of the background service
(individual scrub jobs, the scrub_all initiator, and the failure
reporting) run with as few privileges and within as strong of a sandbox
as possible.
Granted, this does nothing about the potential for the /kernel/ screwing
up, but at least we could prevent obvious container escapes.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-service-security-6.10
---
Commits in this patchset:
* xfs_scrub: allow auxiliary pathnames for sandboxing
* xfs_scrub.service: reduce background CPU usage to less than one core if possible
* xfs_scrub: use dynamic users when running as a systemd service
* xfs_scrub: tighten up the security on the background systemd service
* xfs_scrub_fail: tighten up the security on the background systemd service
* xfs_scrub_all: tighten up the security on the background systemd service
---
man/man8/xfs_scrub.8 | 9 +++-
scrub/Makefile | 7 ++-
scrub/phase1.c | 4 +-
scrub/system-xfs_scrub.slice | 30 ++++++++++++
scrub/vfs.c | 2 -
scrub/xfs_scrub.c | 11 +++-
scrub/xfs_scrub.h | 5 ++
scrub/xfs_scrub@.service.in | 97 ++++++++++++++++++++++++++++++++++----
scrub/xfs_scrub_all.service.in | 66 ++++++++++++++++++++++++++
scrub/xfs_scrub_fail@.service.in | 59 +++++++++++++++++++++++
10 files changed, 270 insertions(+), 20 deletions(-)
create mode 100644 scrub/system-xfs_scrub.slice
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 15/23] xfs_scrub_all: automatic media scan service
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (13 preceding siblings ...)
2024-07-30 0:20 ` [PATCHSET v30.9 14/23] xfs_scrub: tighten security of systemd services Darrick J. Wong
@ 2024-07-30 0:20 ` Darrick J. Wong
2024-07-30 1:14 ` [PATCH 1/6] xfs_scrub_all: only use the xfs_scrub@ systemd services in service mode Darrick J. Wong
` (5 more replies)
2024-07-30 0:20 ` [PATCHSET v30.9 16/23] xfs_scrub_all: improve systemd handling Darrick J. Wong
` (7 subsequent siblings)
22 siblings, 6 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:20 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
Now that we've completed the online fsck functionality, there are a few
things that could be improved in the automatic service. Specifically,
we would like to perform a more intensive metadata + media scan once per
month, to give the user confidence that the filesystem isn't losing data
silently. To accomplish this, enhance xfs_scrub_all to be able to
trigger media scans. Next, add a duplicate set of system services that
start the media scans automatically.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-media-scan-service-6.10
---
Commits in this patchset:
* xfs_scrub_all: only use the xfs_scrub@ systemd services in service mode
* xfs_scrub_all: remove journalctl background process
* xfs_scrub_all: support metadata+media scans of all filesystems
* xfs_scrub_all: enable periodic file data scrubs automatically
* xfs_scrub_all: trigger automatic media scans once per month
* xfs_scrub_all: failure reporting for the xfs_scrub_all job
---
debian/rules | 3 +
include/builddefs.in | 3 +
man/man8/Makefile | 7 ++
man/man8/xfs_scrub_all.8.in | 20 +++++
scrub/Makefile | 21 +++++
scrub/xfs_scrub@.service.in | 2 -
scrub/xfs_scrub_all.cron.in | 2 -
scrub/xfs_scrub_all.in | 126 ++++++++++++++++++++++++++------
scrub/xfs_scrub_all.service.in | 9 ++
scrub/xfs_scrub_all_fail.service.in | 71 ++++++++++++++++++
scrub/xfs_scrub_fail.in | 46 +++++++++---
scrub/xfs_scrub_fail@.service.in | 2 -
scrub/xfs_scrub_media@.service.in | 100 +++++++++++++++++++++++++
scrub/xfs_scrub_media_fail@.service.in | 76 +++++++++++++++++++
14 files changed, 443 insertions(+), 45 deletions(-)
rename man/man8/{xfs_scrub_all.8 => xfs_scrub_all.8.in} (59%)
create mode 100644 scrub/xfs_scrub_all_fail.service.in
create mode 100644 scrub/xfs_scrub_media@.service.in
create mode 100644 scrub/xfs_scrub_media_fail@.service.in
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 16/23] xfs_scrub_all: improve systemd handling
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (14 preceding siblings ...)
2024-07-30 0:20 ` [PATCHSET v30.9 15/23] xfs_scrub_all: automatic media scan service Darrick J. Wong
@ 2024-07-30 0:20 ` Darrick J. Wong
2024-07-30 1:16 ` [PATCH 1/5] xfs_scrub_all: encapsulate all the subprocess code in an object Darrick J. Wong
` (4 more replies)
2024-07-30 0:20 ` [PATCHSET v13.8 17/23] xfsprogs: improve extended attribute validation Darrick J. Wong
` (6 subsequent siblings)
22 siblings, 5 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:20 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-all-improve-systemd-handling-6.10
---
Commits in this patchset:
* xfs_scrub_all: encapsulate all the subprocess code in an object
* xfs_scrub_all: encapsulate all the systemctl code in an object
* xfs_scrub_all: add CLI option for easier debugging
* xfs_scrub_all: convert systemctl calls to dbus
* xfs_scrub_all: implement retry and backoff for dbus calls
---
debian/control | 2
scrub/xfs_scrub_all.in | 284 ++++++++++++++++++++++++++++++++++++------------
2 files changed, 213 insertions(+), 73 deletions(-)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v13.8 17/23] xfsprogs: improve extended attribute validation
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (15 preceding siblings ...)
2024-07-30 0:20 ` [PATCHSET v30.9 16/23] xfs_scrub_all: improve systemd handling Darrick J. Wong
@ 2024-07-30 0:20 ` Darrick J. Wong
2024-07-30 1:17 ` [PATCH 1/6] xfs_scrub_all: fail fast on masked units Darrick J. Wong
` (5 more replies)
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (5 subsequent siblings)
22 siblings, 6 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:20 UTC (permalink / raw)
To: djwong, cem
Cc: Dave Chinner, Chandan Babu R, Christoph Hellwig, hch, linux-xfs
Hi all,
Prior to introducing parent pointer extended attributes, let's spend
some time cleaning up the attr code and strengthening the validation
that it performs on attrs coming in from the disk.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=improve-attr-validation-6.10
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=improve-attr-validation-6.10
---
Commits in this patchset:
* xfs_scrub_all: fail fast on masked units
* xfs_scrub: automatic downgrades to dry-run mode in service mode
* xfs_scrub: add an optimization-only mode
* xfs_repair: check free space requirements before allowing upgrades
* xfs_repair: enforce one namespace bit per extended attribute
* xfs_repair: check for unknown flags in attr entries
---
include/libxfs.h | 1
libxfs/libxfs_api_defs.h | 1
man/man8/xfs_scrub.8 | 6 ++
repair/attr_repair.c | 30 ++++++++++
repair/phase2.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++
scrub/Makefile | 2 -
scrub/phase1.c | 13 ++++
scrub/phase4.c | 6 ++
scrub/repair.c | 37 ++++++++++++-
scrub/repair.h | 2 +
scrub/scrub.c | 4 +
scrub/xfs_scrub.c | 21 +++++++
scrub/xfs_scrub.h | 1
scrub/xfs_scrub_all.in | 21 +++++++
14 files changed, 272 insertions(+), 7 deletions(-)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (16 preceding siblings ...)
2024-07-30 0:20 ` [PATCHSET v13.8 17/23] xfsprogs: improve extended attribute validation Darrick J. Wong
@ 2024-07-30 0:21 ` Darrick J. Wong
2024-07-30 1:19 ` [PATCH 01/24] libxfs: create attr log item opcodes and formats for parent pointers Darrick J. Wong
` (23 more replies)
2024-07-30 0:21 ` [PATCHSET v13.8 19/23] xfsprogs: scrubbing for " Darrick J. Wong
` (4 subsequent siblings)
22 siblings, 24 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:21 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, Allison Henderson, linux-xfs, catherine.hoang,
allison.henderson
Hi all,
This is the latest parent pointer attributes for xfs. The goal of this
patch set is to add a parent pointer attribute to each inode. The
attribute name containing the parent inode, generation, and directory
offset, while the attribute value contains the file name. This feature
will enable future optimizations for online scrub, shrink, nfs handles,
verity, or any other feature that could make use of quickly deriving an
inodes path from the mount point.
Directory parent pointers are stored as namespaced extended attributes
of a file. Because parent pointers are an indivisible tuple of
(dirent_name, parent_ino, parent_gen) we cannot use the usual attr name
lookup functions to find a parent pointer. This is solvable by
introducing a new lookup mode that checks both the name and the value of
the xattr.
Therefore, introduce this new name-value lookup mode that's gated on the
XFS_ATTR_PARENT namespace. This requires the introduction of new
opcodes for the extended attribute update log intent items, which
actually means that parent pointers (itself an INCOMPAT feature) does
not depend on the LOGGED_XATTRS log incompat feature bit.
To reduce collisions on the dirent names of parent pointers, introduce a
new attr hash mode that is the dir2 namehash of the dirent name xor'd
with the parent inode number.
At this point, Allison has moved on to other things, so I've merged her
patchset into djwong-dev for merging.
Updates since v12 [djwong]:
Rebase on 6.9-rc and update the online fsck design document.
Redesign the ondisk format to use the name-value lookups to get us back
to the point where the attr is (dirent_name -> parent_ino/gen).
Updates since v11 [djwong]:
Rebase on 6.4-rc and make some tweaks and bugfixes to enable the repair
prototypes. Merge with djwong-dev and make online repair actually work.
Updates since v10 [djwong]:
Merge in the ondisk format changes to get rid of the diroffset conflicts
with the parent pointer repair code, rebase the entire series with the
attr vlookup changes first, and merge all the other random fixes.
Updates since v9:
Reordered patches 2 and 3 to be 6 and 7
xfs: Add xfs_verify_pptr
moved parent pointer validators to xfs_parent
xfs: Add parent pointer ioctl
Extra validation checks for fs id
added missing release for the inode
use GFP_KERNEL flags for malloc/realloc
reworked ioctl to use pptr listenty and flex array
NEW
xfs: don't remove the attr fork when parent pointers are enabled
NEW
directory lookups should return diroffsets too
NEW
xfs: move/add parent pointer validators to xfs_parent
Updates since v8:
xfs: parent pointer attribute creation
Fix xfs_parent_init to release log assist on alloc fail
Add slab cache for xfs_parent_defer
Fix xfs_create to release after unlock
Add xfs_parent_start and xfs_parent_finish wrappers
removed unused xfs_parent_name_irec and xfs_init_parent_name_irec
xfs: add parent attributes to link
Start/finish wrapper updates
Fix xfs_link to disallow reservationless quotas
xfs: add parent attributes to symlink
Fix xfs_symlink to release after unlock
Start/finish wrapper updates
xfs: remove parent pointers in unlink
Start/finish wrapper updates
Add missing parent free
xfs: Add parent pointers to rename
Start/finish wrapper updates
Fix rename to only grab logged xattr once
Fix xfs_rename to disallow reservationless quotas
Fix double unlock on dqattach fail
Move parent frees to out_release_wip
xfs: Add parent pointers to xfs_cross_rename
Hoist parent pointers into rename
Questions comments and feedback appreciated!
Thanks all!
Allison
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs-6.10
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs-6.10
---
Commits in this patchset:
* libxfs: create attr log item opcodes and formats for parent pointers
* xfs_{db,repair}: implement new attr hash value function
* xfs_logprint: dump new attr log item fields
* man: document the XFS_IOC_GETPARENTS ioctl
* libfrog: report parent pointers to userspace
* libfrog: add parent pointer support code
* xfs_io: adapt parent command to new parent pointer ioctls
* xfs_io: Add i, n and f flags to parent command
* xfs_logprint: decode parent pointers in ATTRI items fully
* xfs_spaceman: report file paths
* xfs_scrub: use parent pointers when possible to report file operations
* xfs_scrub: use parent pointers to report lost file data
* xfs_db: report parent pointers in version command
* xfs_db: report parent bit on xattrs
* xfs_db: report parent pointers embedded in xattrs
* xfs_db: obfuscate dirent and parent pointer names consistently
* libxfs: export attr3_leaf_hdr_from_disk via libxfs_api_defs.h
* xfs_db: add a parents command to list the parents of a file
* xfs_db: make attr_set and attr_remove handle parent pointers
* xfs_db: add link and unlink expert commands
* xfs_db: compute hashes of parent pointers
* libxfs: create new files with attr forks if necessary
* mkfs: Add parent pointers during protofile creation
* mkfs: enable formatting with parent pointers
---
db/attr.c | 33 ++
db/attrset.c | 202 +++++++++--
db/attrshort.c | 27 ++
db/field.c | 10 +
db/field.h | 3
db/hash.c | 44 ++
db/metadump.c | 322 +++++++++++++++++-
db/namei.c | 701 +++++++++++++++++++++++++++++++++++++++
db/sb.c | 2
include/handle.h | 1
include/xfs_inode.h | 4
io/parent.c | 541 +++++++++++-------------------
libfrog/Makefile | 2
libfrog/fsgeom.c | 6
libfrog/getparents.c | 355 ++++++++++++++++++++
libfrog/getparents.h | 42 ++
libfrog/paths.c | 168 +++++++++
libfrog/paths.h | 25 +
libhandle/handle.c | 7
libxfs/defer_item.c | 52 +++
libxfs/init.c | 4
libxfs/libxfs_api_defs.h | 19 +
libxfs/util.c | 19 +
logprint/log_redo.c | 217 +++++++++++-
logprint/logprint.h | 5
man/man2/ioctl_xfs_getparents.2 | 212 ++++++++++++
man/man8/xfs_db.8 | 59 +++
man/man8/xfs_io.8 | 32 +-
man/man8/xfs_spaceman.8 | 7
mkfs/lts_4.19.conf | 3
mkfs/lts_5.10.conf | 3
mkfs/lts_5.15.conf | 3
mkfs/lts_5.4.conf | 3
mkfs/lts_6.1.conf | 3
mkfs/lts_6.6.conf | 3
mkfs/proto.c | 62 +++
mkfs/xfs_mkfs.c | 45 ++-
repair/attr_repair.c | 24 +
scrub/common.c | 41 ++
scrub/phase6.c | 75 ++++
spaceman/Makefile | 16 +
spaceman/file.c | 7
spaceman/health.c | 53 ++-
spaceman/space.h | 3
44 files changed, 2941 insertions(+), 524 deletions(-)
create mode 100644 libfrog/getparents.c
create mode 100644 libfrog/getparents.h
create mode 100644 man/man2/ioctl_xfs_getparents.2
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v13.8 19/23] xfsprogs: scrubbing for parent pointers
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (17 preceding siblings ...)
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
@ 2024-07-30 0:21 ` Darrick J. Wong
2024-07-30 1:25 ` [PATCH 1/2] xfs: create a blob array data structure Darrick J. Wong
2024-07-30 1:25 ` [PATCH 2/2] man2: update ioctl_xfs_scrub_metadata.2 for parent pointers Darrick J. Wong
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
` (3 subsequent siblings)
22 siblings, 2 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:21 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
Hi all,
Teach online fsck to use parent pointers to assist in checking
directories, parent pointers, extended attributes, and link counts.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-pptrs-6.10
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-pptrs-6.10
---
Commits in this patchset:
* xfs: create a blob array data structure
* man2: update ioctl_xfs_scrub_metadata.2 for parent pointers
---
libxfs/Makefile | 2
libxfs/xfblob.c | 147 +++++++++++++++++++++++++++++++++++
libxfs/xfblob.h | 24 ++++++
libxfs/xfile.c | 11 +++
libxfs/xfile.h | 1
man/man2/ioctl_xfs_scrub_metadata.2 | 20 ++++-
6 files changed, 201 insertions(+), 4 deletions(-)
create mode 100644 libxfs/xfblob.c
create mode 100644 libxfs/xfblob.h
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v13.8 20/23] xfsprogs: offline repair for parent pointers
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (18 preceding siblings ...)
2024-07-30 0:21 ` [PATCHSET v13.8 19/23] xfsprogs: scrubbing for " Darrick J. Wong
@ 2024-07-30 0:21 ` Darrick J. Wong
2024-07-30 1:25 ` [PATCH 01/12] xfs_db: remove some boilerplate from xfs_attr_set Darrick J. Wong
` (11 more replies)
2024-07-30 0:21 ` [PATCHSET v13.8 21/23] xfsprogs: detect and correct directory tree problems Darrick J. Wong
` (2 subsequent siblings)
22 siblings, 12 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:21 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
Hi all,
This series implements online checking and repair for directory parent
pointer metadata. The checking half is fairly straightforward -- for
each outgoing directory link (forward or backwards), grab the inode at
the other end, and confirm that there's a corresponding link. If we
can't grab an inode or lock it, we'll save that link for a slower loop
that cycles all the locks, confirms the continued existence of the link,
and rechecks the link if it's actually still there.
Repairs are a bit more involved -- for directories, we walk the entire
filesystem to rebuild the dirents from parent pointer information.
Parent pointer repairs do the same walk but rebuild the pptrs from the
dirent information, but with the added twist that it duplicates all the
xattrs so that it can use the atomic extent swapping code to commit the
repairs atomically.
This introduces an added twist to the xattr repair code -- we use dirent
hooks to detect a colliding update to the pptr data while we're not
holding the ILOCKs; if one is detected, we restart the xattr salvaging
process but this time hold all the ILOCKs until the end of the scan.
For offline repair, the phase6 directory connectivity scan generates an
index of all the expected parent pointers in the filesystem. Then it
walks each file and compares the parent pointers attached to that file
against the index generated, and resyncs the results as necessary.
The last patch teaches xfs_scrub to report pathnames of files that are
being repaired, when possible.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-pptrs-6.10
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=repair-pptrs-6.10
---
Commits in this patchset:
* xfs_db: remove some boilerplate from xfs_attr_set
* xfs_db: actually report errors from libxfs_attr_set
* xfs_repair: junk parent pointer attributes when filesystem doesn't support them
* xfs_repair: add parent pointers when messing with /lost+found
* xfs_repair: junk duplicate hashtab entries when processing sf dirents
* xfs_repair: build a parent pointer index
* xfs_repair: move the global dirent name store to a separate object
* xfs_repair: deduplicate strings stored in string blob
* xfs_repair: check parent pointers
* xfs_repair: dump garbage parent pointer attributes
* xfs_repair: update ondisk parent pointer records
* xfs_repair: wipe ondisk parent pointers when there are none
---
db/attrset.c | 36 +
libxfs/libxfs_api_defs.h | 6
libxfs/xfblob.c | 9
libxfs/xfblob.h | 2
repair/Makefile | 6
repair/attr_repair.c | 30 +
repair/listxattr.c | 271 +++++++++
repair/listxattr.h | 15 +
repair/phase6.c | 121 ++++
repair/pptr.c | 1331 ++++++++++++++++++++++++++++++++++++++++++++++
repair/pptr.h | 17 +
repair/strblobs.c | 211 +++++++
repair/strblobs.h | 24 +
13 files changed, 2069 insertions(+), 10 deletions(-)
create mode 100644 repair/listxattr.c
create mode 100644 repair/listxattr.h
create mode 100644 repair/pptr.c
create mode 100644 repair/pptr.h
create mode 100644 repair/strblobs.c
create mode 100644 repair/strblobs.h
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v13.8 21/23] xfsprogs: detect and correct directory tree problems
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (19 preceding siblings ...)
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
@ 2024-07-30 0:21 ` Darrick J. Wong
2024-07-30 1:29 ` [PATCH 1/5] libfrog: add directory tree structure scrubber to scrub library Darrick J. Wong
` (4 more replies)
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
2024-07-30 0:22 ` [PATCHSET v30.9 23/23] xfs_repair: fixes for kernel 6.10 Darrick J. Wong
22 siblings, 5 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:21 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
Historically, checking the tree-ness of the directory tree structure has
not been complete. Cycles of subdirectories break the tree properties,
as do subdirectories with multiple parents. It's easy enough for DFS to
detect problems as long as one of the participants is reachable from the
root, but this technique cannot find unconnected cycles.
Directory parent pointers change that, because we can discover all of
these problems from a simple walk from a subdirectory towards the root.
For each child we start with, if the walk terminates without reaching
the root, we know the path is disconnected and ought to be attached to
the lost and found. If we find ourselves, we know this is a cycle and
can delete an incoming edge. If we find multiple paths to the root, we
know to delete an incoming edge.
Even better, once we've finished walking paths, we've identified the
good ones and know which other path(s) to remove.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-directory-tree-6.10
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-directory-tree-6.10
---
Commits in this patchset:
* libfrog: add directory tree structure scrubber to scrub library
* xfs_spaceman: report directory tree corruption in the health information
* xfs_scrub: fix erroring out of check_inode_names
* xfs_scrub: detect and repair directory tree corruptions
* xfs_scrub: defer phase5 file scans if dirloop fails
---
libfrog/scrub.c | 5 +
man/man2/ioctl_xfs_bulkstat.2 | 3
man/man2/ioctl_xfs_fsbulkstat.2 | 3
man/man2/ioctl_xfs_scrub_metadata.2 | 14 ++
scrub/phase5.c | 271 +++++++++++++++++++++++++++++++++--
scrub/repair.c | 13 ++
scrub/repair.h | 2
spaceman/health.c | 4 +
8 files changed, 301 insertions(+), 14 deletions(-)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (20 preceding siblings ...)
2024-07-30 0:21 ` [PATCHSET v13.8 21/23] xfsprogs: detect and correct directory tree problems Darrick J. Wong
@ 2024-07-30 0:22 ` Darrick J. Wong
2024-07-30 1:30 ` [PATCH 01/10] man: document vectored scrub mode Darrick J. Wong
` (9 more replies)
2024-07-30 0:22 ` [PATCHSET v30.9 23/23] xfs_repair: fixes for kernel 6.10 Darrick J. Wong
22 siblings, 10 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:22 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
Create a vectorized version of the metadata scrub and repair ioctl, and
adapt xfs_scrub to use that. This is an experiment to measure overhead
and to try refactoring xfs_scrub.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=vectorized-scrub-6.10
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=vectorized-scrub-6.10
---
Commits in this patchset:
* man: document vectored scrub mode
* libfrog: support vectored scrub
* xfs_io: support vectored scrub
* xfs_scrub: split the scrub epilogue code into a separate function
* xfs_scrub: split the repair epilogue code into a separate function
* xfs_scrub: convert scrub and repair epilogues to use xfs_scrub_vec
* xfs_scrub: vectorize scrub calls
* xfs_scrub: vectorize repair calls
* xfs_scrub: use scrub barriers to reduce kernel calls
* xfs_scrub: try spot repairs of metadata items to make scrub progress
---
io/scrub.c | 368 ++++++++++++++++++++++++++++++----
libfrog/fsgeom.h | 6 +
libfrog/scrub.c | 137 +++++++++++++
libfrog/scrub.h | 35 +++
man/man2/ioctl_xfs_scrubv_metadata.2 | 171 ++++++++++++++++
man/man8/xfs_io.8 | 51 +++++
scrub/phase1.c | 2
scrub/phase2.c | 93 +++++++--
scrub/phase3.c | 84 ++++++--
scrub/repair.c | 355 +++++++++++++++++++++------------
scrub/scrub.c | 360 +++++++++++++++++++++++++--------
scrub/scrub.h | 19 ++
scrub/scrub_private.h | 55 +++--
scrub/xfs_scrub.c | 1
14 files changed, 1431 insertions(+), 306 deletions(-)
create mode 100644 man/man2/ioctl_xfs_scrubv_metadata.2
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCHSET v30.9 23/23] xfs_repair: fixes for kernel 6.10
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
` (21 preceding siblings ...)
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
@ 2024-07-30 0:22 ` Darrick J. Wong
2024-07-30 1:32 ` [PATCH 1/1] xfs_repair: allow symlinks with short remote targets Darrick J. Wong
22 siblings, 1 reply; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:22 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
Hi all,
Fix some incorrect validation problems in xfs_repair.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-fixes-6.10
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=repair-fixes-6.10
---
Commits in this patchset:
* xfs_repair: allow symlinks with short remote targets
---
repair/dinode.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
^ permalink raw reply [flat|nested] 296+ messages in thread
* [PATCH 1/5] [PATCH v3] Remove support for split-/usr installs
2024-07-30 0:16 ` [PATCHSET 01/23] libxfs: fixes for 6.9 Darrick J. Wong
@ 2024-07-30 0:22 ` Darrick J. Wong
2024-07-30 0:22 ` [PATCH 2/5] repair: btree blocks are never on the RT subvolume Darrick J. Wong
` (3 subsequent siblings)
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:22 UTC (permalink / raw)
To: djwong, cem; +Cc: Chris Hofstaedtler, linux-xfs
From: Chris Hofstaedtler <zeha@debian.org>
Always install binaries and other files under /usr, not /. This will
break any distribution that hasn't yet merged the two, which are
vanishingly small these days. This breaks the usecase of needing to
repair the /usr partition when there is no initramfs or livecd
available and / is the only option.
Signed-off-by: Chris Hofstaedtler <zeha@debian.org>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
configure.ac | 21 ---------------------
debian/Makefile | 4 ++--
debian/local/initramfs.hook | 2 +-
debian/rules | 5 ++---
fsck/Makefile | 4 ++--
include/builddefs.in | 2 --
include/buildmacros | 20 ++++++++++----------
mkfs/Makefile | 4 ++--
repair/Makefile | 4 ++--
9 files changed, 21 insertions(+), 45 deletions(-)
diff --git a/configure.ac b/configure.ac
index b692b4b42..b84234b50 100644
--- a/configure.ac
+++ b/configure.ac
@@ -113,27 +113,6 @@ esac
#
test -n "$multiarch" && enable_lib64=no
-#
-# Some important tools should be installed into the root partitions.
-#
-# Check whether exec_prefix=/usr: and install them to /sbin in that
-# case. If the user chooses a different prefix assume they just want
-# a local install for testing and not a system install.
-#
-case $exec_prefix:$prefix in
-NONE:NONE | NONE:/usr | /usr:*)
- root_sbindir='/sbin'
- root_libdir="/${base_libdir}"
- ;;
-*)
- root_sbindir="${sbindir}"
- root_libdir="${libdir}"
- ;;
-esac
-
-AC_SUBST([root_sbindir])
-AC_SUBST([root_libdir])
-
# Find localized files. Don't descend into any "dot directories"
# (like .git or .pc from quilt). Strangely, the "-print" argument
# to "find" is required, to avoid including such directories in the
diff --git a/debian/Makefile b/debian/Makefile
index cafe8bbb3..2f9cd38c2 100644
--- a/debian/Makefile
+++ b/debian/Makefile
@@ -31,6 +31,6 @@ endif
install-d-i: default
ifeq ($(PKG_DISTRIBUTION), debian)
- $(INSTALL) -m 755 -d $(PKG_ROOT_SBIN_DIR)
- $(INSTALL) -m 755 $(BOOT_MKFS_BIN) $(PKG_ROOT_SBIN_DIR)/mkfs.xfs
+ $(INSTALL) -m 755 -d $(PKG_SBIN_DIR)
+ $(INSTALL) -m 755 $(BOOT_MKFS_BIN) $(PKG_SBIN_DIR)/mkfs.xfs
endif
diff --git a/debian/local/initramfs.hook b/debian/local/initramfs.hook
index 5b24eaece..eac7e79ea 100644
--- a/debian/local/initramfs.hook
+++ b/debian/local/initramfs.hook
@@ -45,7 +45,7 @@ rootfs_type() {
. /usr/share/initramfs-tools/hook-functions
if [ "$(rootfs_type)" = "xfs" ]; then
- copy_exec /sbin/xfs_repair
+ copy_exec /usr/sbin/xfs_repair
copy_exec /usr/sbin/xfs_db
copy_exec /usr/sbin/xfs_metadump
fi
diff --git a/debian/rules b/debian/rules
index 0c1cef92d..0db0ed8e7 100755
--- a/debian/rules
+++ b/debian/rules
@@ -105,9 +105,8 @@ binary-arch: checkroot built
$(pkgme) $(MAKE) dist
install -D -m 0755 debian/local/initramfs.hook debian/xfsprogs/usr/share/initramfs-tools/hooks/xfs
rmdir debian/xfslibs-dev/usr/share/doc/xfsprogs
- rm -f debian/xfslibs-dev/lib/$(DEB_HOST_MULTIARCH)/libhandle.la
- rm -f debian/xfslibs-dev/lib/$(DEB_HOST_MULTIARCH)/libhandle.a
- rm -fr debian/xfslibs-dev/usr/lib
+ rm -f debian/xfslibs-dev/usr/lib/$(DEB_HOST_MULTIARCH)/libhandle.la
+ rm -f debian/xfslibs-dev/usr/lib/$(DEB_HOST_MULTIARCH)/libhandle.a
dh_installdocs -XCHANGES
dh_installchangelogs
dh_strip
diff --git a/fsck/Makefile b/fsck/Makefile
index da9b6ded8..5ca529f53 100644
--- a/fsck/Makefile
+++ b/fsck/Makefile
@@ -12,6 +12,6 @@ default: $(LTCOMMAND)
include $(BUILDRULES)
install: default
- $(INSTALL) -m 755 -d $(PKG_ROOT_SBIN_DIR)
- $(INSTALL) -m 755 xfs_fsck.sh $(PKG_ROOT_SBIN_DIR)/fsck.xfs
+ $(INSTALL) -m 755 -d $(PKG_SBIN_DIR)
+ $(INSTALL) -m 755 xfs_fsck.sh $(PKG_SBIN_DIR)/fsck.xfs
install-dev:
diff --git a/include/builddefs.in b/include/builddefs.in
index 644ed1cb1..6ac36c149 100644
--- a/include/builddefs.in
+++ b/include/builddefs.in
@@ -48,8 +48,6 @@ datarootdir = @datarootdir@
top_builddir = @top_builddir@
PKG_SBIN_DIR = @sbindir@
-PKG_ROOT_SBIN_DIR = @root_sbindir@
-PKG_ROOT_LIB_DIR= @root_libdir@@libdirsuffix@
PKG_LIB_DIR = @libdir@@libdirsuffix@
PKG_LIBEXEC_DIR = @libexecdir@/@pkg_name@
PKG_INC_DIR = @includedir@/xfs
diff --git a/include/buildmacros b/include/buildmacros
index 6f34d7c52..9183e5bc7 100644
--- a/include/buildmacros
+++ b/include/buildmacros
@@ -50,16 +50,16 @@ LTINSTALL = $(LIBTOOL) --quiet --mode=install $(INSTALL)
LTCOMPILE = $(LIBTOOL) --quiet --tag=CC --mode=compile $(CCF)
ifeq ($(ENABLE_SHARED),yes)
-LTLDFLAGS += -rpath $(PKG_ROOT_LIB_DIR)
+LTLDFLAGS += -rpath $(PKG_LIB_DIR)
LTLDFLAGS += -version-info $(LTVERSION)
endif
ifeq ($(ENABLE_SHARED),yes)
INSTALL_LTLIB = \
cd $(TOPDIR)/$(LIBNAME)/.libs; \
- ../$(INSTALL) -m 755 -d $(PKG_ROOT_LIB_DIR); \
- ../$(INSTALL) -m 755 -T so_dot_version $(LIBNAME).lai $(PKG_ROOT_LIB_DIR); \
- ../$(INSTALL) -T so_dot_current $(LIBNAME).lai $(PKG_ROOT_LIB_DIR)
+ ../$(INSTALL) -m 755 -d $(PKG_LIB_DIR); \
+ ../$(INSTALL) -m 755 -T so_dot_version $(LIBNAME).lai $(PKG_LIB_DIR); \
+ ../$(INSTALL) -T so_dot_current $(LIBNAME).lai $(PKG_LIB_DIR)
endif
# Libtool thinks the static and shared libs should be in the same dir, so
@@ -74,13 +74,13 @@ INSTALL_LTLIB_DEV = \
../$(INSTALL) -m 755 -d $(PKG_LIB_DIR); \
../$(INSTALL) -m 644 -T old_lib $(LIBNAME).lai $(PKG_LIB_DIR); \
../$(INSTALL) -m 644 $(LIBNAME).lai $(PKG_LIB_DIR)/$(LIBNAME).la ; \
- ../$(INSTALL) -m 755 -d $(PKG_ROOT_LIB_DIR); \
- ../$(INSTALL) -T so_base $(LIBNAME).lai $(PKG_ROOT_LIB_DIR); \
+ ../$(INSTALL) -m 755 -d $(PKG_LIB_DIR); \
+ ../$(INSTALL) -T so_base $(LIBNAME).lai $(PKG_LIB_DIR); \
if [ "x$(shell readlink -f $(PKG_LIB_DIR))" != \
- "x$(shell readlink -f $(PKG_ROOT_LIB_DIR))" ]; then \
- ../$(INSTALL) -S $(PKG_LIB_DIR)/$(LIBNAME).a $(PKG_ROOT_LIB_DIR)/$(LIBNAME).a; \
- ../$(INSTALL) -S $(PKG_LIB_DIR)/$(LIBNAME).la $(PKG_ROOT_LIB_DIR)/$(LIBNAME).la; \
- ../$(INSTALL) -S $(PKG_ROOT_LIB_DIR)/$(LIBNAME).so $(PKG_LIB_DIR)/$(LIBNAME).so; \
+ "x$(shell readlink -f $(PKG_LIB_DIR))" ]; then \
+ ../$(INSTALL) -S $(PKG_LIB_DIR)/$(LIBNAME).a $(PKG_LIB_DIR)/$(LIBNAME).a; \
+ ../$(INSTALL) -S $(PKG_LIB_DIR)/$(LIBNAME).la $(PKG_LIB_DIR)/$(LIBNAME).la; \
+ ../$(INSTALL) -S $(PKG_LIB_DIR)/$(LIBNAME).so $(PKG_LIB_DIR)/$(LIBNAME).so; \
fi
else
INSTALL_LTLIB_DEV = $(INSTALL_LTLIB_STATIC)
diff --git a/mkfs/Makefile b/mkfs/Makefile
index a0c168e38..a6173083e 100644
--- a/mkfs/Makefile
+++ b/mkfs/Makefile
@@ -28,8 +28,8 @@ default: depend $(LTCOMMAND) $(CFGFILES)
include $(BUILDRULES)
install: default
- $(INSTALL) -m 755 -d $(PKG_ROOT_SBIN_DIR)
- $(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_ROOT_SBIN_DIR)
+ $(INSTALL) -m 755 -d $(PKG_SBIN_DIR)
+ $(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_SBIN_DIR)
$(INSTALL) -m 755 -d $(MKFS_CFG_DIR)
$(INSTALL) -m 644 $(CFGFILES) $(MKFS_CFG_DIR)
diff --git a/repair/Makefile b/repair/Makefile
index 250c86cca..d94858878 100644
--- a/repair/Makefile
+++ b/repair/Makefile
@@ -103,8 +103,8 @@ include $(BUILDRULES)
#CFLAGS += ...
install: default
- $(INSTALL) -m 755 -d $(PKG_ROOT_SBIN_DIR)
- $(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_ROOT_SBIN_DIR)
+ $(INSTALL) -m 755 -d $(PKG_SBIN_DIR)
+ $(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_SBIN_DIR)
install-dev:
-include .dep
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/5] repair: btree blocks are never on the RT subvolume
2024-07-30 0:16 ` [PATCHSET 01/23] libxfs: fixes for 6.9 Darrick J. Wong
2024-07-30 0:22 ` [PATCH 1/5] [PATCH v3] Remove support for split-/usr installs Darrick J. Wong
@ 2024-07-30 0:22 ` Darrick J. Wong
2024-07-30 0:23 ` [PATCH 3/5] xfile: fix missing error unlock in xfile_fcb_find Darrick J. Wong
` (2 subsequent siblings)
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:22 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Christoph Hellwig <hch@lst.de>
scan_bmapbt tries to track btree blocks in the RT duplicate extent
AVL tree if the inode has the realtime flag set. Given that the
RT subvolume is only ever used for file data this is incorrect.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
repair/scan.c | 21 +++++----------------
1 file changed, 5 insertions(+), 16 deletions(-)
diff --git a/repair/scan.c b/repair/scan.c
index 338308ef8..8352b3ccf 100644
--- a/repair/scan.c
+++ b/repair/scan.c
@@ -390,22 +390,11 @@ _("bad state %d, inode %" PRIu64 " bmap block 0x%" PRIx64 "\n"),
break;
}
pthread_mutex_unlock(&ag_locks[agno].lock);
- } else {
- /*
- * attribute fork for realtime files is in the regular
- * filesystem
- */
- if (type != XR_INO_RTDATA || whichfork != XFS_DATA_FORK) {
- if (search_dup_extent(XFS_FSB_TO_AGNO(mp, bno),
- XFS_FSB_TO_AGBNO(mp, bno),
- XFS_FSB_TO_AGBNO(mp, bno) + 1))
- return(1);
- } else {
- xfs_rtxnum_t ext = xfs_rtb_to_rtx(mp, bno);
-
- if (search_rt_dup_extent(mp, ext))
- return 1;
- }
+ } else {
+ if (search_dup_extent(XFS_FSB_TO_AGNO(mp, bno),
+ XFS_FSB_TO_AGBNO(mp, bno),
+ XFS_FSB_TO_AGBNO(mp, bno) + 1))
+ return 1;
}
(*tot)++;
numrecs = be16_to_cpu(block->bb_numrecs);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/5] xfile: fix missing error unlock in xfile_fcb_find
2024-07-30 0:16 ` [PATCHSET 01/23] libxfs: fixes for 6.9 Darrick J. Wong
2024-07-30 0:22 ` [PATCH 1/5] [PATCH v3] Remove support for split-/usr installs Darrick J. Wong
2024-07-30 0:22 ` [PATCH 2/5] repair: btree blocks are never on the RT subvolume Darrick J. Wong
@ 2024-07-30 0:23 ` Darrick J. Wong
2024-07-30 0:23 ` [PATCH 4/5] xfs_repair: don't leak the rootdir inode when orphanage already exists Darrick J. Wong
2024-07-30 0:23 ` [PATCH 5/5] xfs_repair: don't crash on -vv Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:23 UTC (permalink / raw)
To: djwong, cem; +Cc: Carlos Maiolino, Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Fix a missing mutex pthread_mutex_unlock and uninitialized return value
in xfile_fcb_find.
Coverity-id: 1604113
Coverity-id: 1604099
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfile.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libxfs/xfile.c b/libxfs/xfile.c
index fdb76f406..6e0fa809a 100644
--- a/libxfs/xfile.c
+++ b/libxfs/xfile.c
@@ -179,7 +179,7 @@ xfile_fcb_find(
{
struct xfile_fcb *fcb;
int ret;
- int error;
+ int error = 0;
/* No maximum range means that the caller gets a private memfd. */
if (maxbytes == 0) {
@@ -222,13 +222,13 @@ xfile_fcb_find(
/* Otherwise, open a new memfd and add it to our list. */
error = xfile_fcb_create(description, &fcb);
if (error)
- return error;
+ goto out_unlock;
ret = ftruncate(fcb->fd, maxbytes);
if (ret) {
error = -errno;
xfile_fcb_irele(fcb, 0, maxbytes);
- return error;
+ goto out_unlock;
}
list_add_tail(&fcb->fcb_list, &fcb_list);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/5] xfs_repair: don't leak the rootdir inode when orphanage already exists
2024-07-30 0:16 ` [PATCHSET 01/23] libxfs: fixes for 6.9 Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 0:23 ` [PATCH 3/5] xfile: fix missing error unlock in xfile_fcb_find Darrick J. Wong
@ 2024-07-30 0:23 ` Darrick J. Wong
2024-07-30 0:23 ` [PATCH 5/5] xfs_repair: don't crash on -vv Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:23 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If repair calls mk_orphanage and the /lost+found directory already
exists, we need to irele the root directory before exiting the function.
Fixes: 6c39a3cbda32 ("Don't trash lost+found in phase 4 Merge of master-melb:xfs-cmds:29144a by kenmcd.")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
repair/phase6.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/repair/phase6.c b/repair/phase6.c
index ae8935a26..e6103f768 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -919,8 +919,10 @@ mk_orphanage(xfs_mount_t *mp)
xname.len = strlen(ORPHANAGE);
xname.type = XFS_DIR3_FT_DIR;
- if (libxfs_dir_lookup(NULL, pip, &xname, &ino, NULL) == 0)
- return ino;
+ /* If the lookup of /lost+found succeeds, return the inumber. */
+ error = -libxfs_dir_lookup(NULL, pip, &xname, &ino, NULL);
+ if (error == 0)
+ goto out_pip;
/*
* could not be found, create it
@@ -1012,6 +1014,7 @@ mk_orphanage(xfs_mount_t *mp)
ORPHANAGE, error);
}
libxfs_irele(ip);
+out_pip:
libxfs_irele(pip);
return(ino);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/5] xfs_repair: don't crash on -vv
2024-07-30 0:16 ` [PATCHSET 01/23] libxfs: fixes for 6.9 Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 0:23 ` [PATCH 4/5] xfs_repair: don't leak the rootdir inode when orphanage already exists Darrick J. Wong
@ 2024-07-30 0:23 ` Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:23 UTC (permalink / raw)
To: djwong, cem; +Cc: Santiago Kraus, Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
A user reported a crash in xfs_repair when they run it with -vv
specified on the command line. Ultimately this harks back to xfs_m in
main() containing uninitialized stack contents, and inadequate null
checks. Fix both problems in one go.
Reported-by: Santiago Kraus <santiago_kraus@yahoo.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
repair/progress.c | 2 +-
repair/xfs_repair.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/repair/progress.c b/repair/progress.c
index 07cf4e4f2..74e7a6719 100644
--- a/repair/progress.c
+++ b/repair/progress.c
@@ -394,7 +394,7 @@ timestamp(
time_t now;
struct tm *tmp;
- if (verbose > 1 && mp && mp->m_ddev_targp)
+ if (verbose > 1 && mp && mp->m_ddev_targp && mp->m_ddev_targp->bcache)
cache_report(stderr, "libxfs_bcache", mp->m_ddev_targp->bcache);
now = time(NULL);
diff --git a/repair/xfs_repair.c b/repair/xfs_repair.c
index cf7749643..88aa75542 100644
--- a/repair/xfs_repair.c
+++ b/repair/xfs_repair.c
@@ -1018,7 +1018,7 @@ main(int argc, char **argv)
xfs_mount_t *temp_mp;
xfs_mount_t *mp;
struct xfs_buf *sbp;
- xfs_mount_t xfs_m;
+ struct xfs_mount xfs_m = { };
struct xlog log = {0};
char *msgbuf;
struct xfs_sb psb;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 001/115] xfs: pass xfs_buf lookup flags to xfs_*read_agi
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
@ 2024-07-30 0:24 ` Darrick J. Wong
2024-07-30 0:24 ` [PATCH 002/115] xfs: constify xfs_bmap_is_written_extent Darrick J. Wong
` (113 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:24 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 549d3c9a29921f388ef3bcfd1d4f669b7dd4eed2
Allow callers to pass buffer lookup flags to xfs_read_agi and
xfs_ialloc_read_agi. This will be used in the next patch to fix a
deadlock in the online fsck inode scanner.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/iunlink.c | 4 ++--
libxfs/xfs_ag.c | 8 ++++----
libxfs/xfs_ialloc.c | 16 ++++++++++------
libxfs/xfs_ialloc.h | 5 +++--
libxfs/xfs_ialloc_btree.c | 4 ++--
5 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/db/iunlink.c b/db/iunlink.c
index d87562e3b..256c85560 100644
--- a/db/iunlink.c
+++ b/db/iunlink.c
@@ -117,7 +117,7 @@ dump_unlinked(
xfs_agnumber_t agno = pag->pag_agno;
int error;
- error = -libxfs_ialloc_read_agi(pag, NULL, &agi_bp);
+ error = -libxfs_ialloc_read_agi(pag, NULL, 0, &agi_bp);
if (error) {
dbprintf(_("AGI %u: %s\n"), agno, strerror(errno));
return;
@@ -295,7 +295,7 @@ iunlink(
pag = libxfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
/* Get the agi buffer first. It ensures lock ordering on the list. */
- error = -libxfs_read_agi(pag, tp, &agibp);
+ error = -libxfs_read_agi(pag, tp, 0, &agibp);
if (error)
goto out;
diff --git a/libxfs/xfs_ag.c b/libxfs/xfs_ag.c
index a9aae0990..ad721c192 100644
--- a/libxfs/xfs_ag.c
+++ b/libxfs/xfs_ag.c
@@ -192,7 +192,7 @@ xfs_initialize_perag_data(
pag = xfs_perag_get(mp, index);
error = xfs_alloc_read_agf(pag, NULL, 0, NULL);
if (!error)
- error = xfs_ialloc_read_agi(pag, NULL, NULL);
+ error = xfs_ialloc_read_agi(pag, NULL, 0, NULL);
if (error) {
xfs_perag_put(pag);
return error;
@@ -929,7 +929,7 @@ xfs_ag_shrink_space(
int error, err2;
ASSERT(pag->pag_agno == mp->m_sb.sb_agcount - 1);
- error = xfs_ialloc_read_agi(pag, *tpp, &agibp);
+ error = xfs_ialloc_read_agi(pag, *tpp, 0, &agibp);
if (error)
return error;
@@ -1060,7 +1060,7 @@ xfs_ag_extend_space(
ASSERT(pag->pag_agno == pag->pag_mount->m_sb.sb_agcount - 1);
- error = xfs_ialloc_read_agi(pag, tp, &bp);
+ error = xfs_ialloc_read_agi(pag, tp, 0, &bp);
if (error)
return error;
@@ -1117,7 +1117,7 @@ xfs_ag_get_geometry(
int error;
/* Lock the AG headers. */
- error = xfs_ialloc_read_agi(pag, NULL, &agi_bp);
+ error = xfs_ialloc_read_agi(pag, NULL, 0, &agi_bp);
if (error)
return error;
error = xfs_alloc_read_agf(pag, NULL, 0, &agf_bp);
diff --git a/libxfs/xfs_ialloc.c b/libxfs/xfs_ialloc.c
index c30e76830..992b8348a 100644
--- a/libxfs/xfs_ialloc.c
+++ b/libxfs/xfs_ialloc.c
@@ -1694,7 +1694,7 @@ xfs_dialloc_good_ag(
return false;
if (!xfs_perag_initialised_agi(pag)) {
- error = xfs_ialloc_read_agi(pag, tp, NULL);
+ error = xfs_ialloc_read_agi(pag, tp, 0, NULL);
if (error)
return false;
}
@@ -1763,7 +1763,7 @@ xfs_dialloc_try_ag(
* Then read in the AGI buffer and recheck with the AGI buffer
* lock held.
*/
- error = xfs_ialloc_read_agi(pag, *tpp, &agbp);
+ error = xfs_ialloc_read_agi(pag, *tpp, 0, &agbp);
if (error)
return error;
@@ -2281,7 +2281,7 @@ xfs_difree(
/*
* Get the allocation group header.
*/
- error = xfs_ialloc_read_agi(pag, tp, &agbp);
+ error = xfs_ialloc_read_agi(pag, tp, 0, &agbp);
if (error) {
xfs_warn(mp, "%s: xfs_ialloc_read_agi() returned error %d.",
__func__, error);
@@ -2327,7 +2327,7 @@ xfs_imap_lookup(
int error;
int i;
- error = xfs_ialloc_read_agi(pag, tp, &agbp);
+ error = xfs_ialloc_read_agi(pag, tp, 0, &agbp);
if (error) {
xfs_alert(mp,
"%s: xfs_ialloc_read_agi() returned error %d, agno %d",
@@ -2670,6 +2670,7 @@ int
xfs_read_agi(
struct xfs_perag *pag,
struct xfs_trans *tp,
+ xfs_buf_flags_t flags,
struct xfs_buf **agibpp)
{
struct xfs_mount *mp = pag->pag_mount;
@@ -2679,7 +2680,7 @@ xfs_read_agi(
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGI_DADDR(mp)),
- XFS_FSS_TO_BB(mp, 1), 0, agibpp, &xfs_agi_buf_ops);
+ XFS_FSS_TO_BB(mp, 1), flags, agibpp, &xfs_agi_buf_ops);
if (xfs_metadata_is_sick(error))
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);
if (error)
@@ -2699,6 +2700,7 @@ int
xfs_ialloc_read_agi(
struct xfs_perag *pag,
struct xfs_trans *tp,
+ int flags,
struct xfs_buf **agibpp)
{
struct xfs_buf *agibp;
@@ -2707,7 +2709,9 @@ xfs_ialloc_read_agi(
trace_xfs_ialloc_read_agi(pag->pag_mount, pag->pag_agno);
- error = xfs_read_agi(pag, tp, &agibp);
+ error = xfs_read_agi(pag, tp,
+ (flags & XFS_IALLOC_FLAG_TRYLOCK) ? XBF_TRYLOCK : 0,
+ &agibp);
if (error)
return error;
diff --git a/libxfs/xfs_ialloc.h b/libxfs/xfs_ialloc.h
index f1412183b..b549627e3 100644
--- a/libxfs/xfs_ialloc.h
+++ b/libxfs/xfs_ialloc.h
@@ -63,10 +63,11 @@ xfs_ialloc_log_agi(
struct xfs_buf *bp, /* allocation group header buffer */
uint32_t fields); /* bitmask of fields to log */
-int xfs_read_agi(struct xfs_perag *pag, struct xfs_trans *tp,
+int xfs_read_agi(struct xfs_perag *pag, struct xfs_trans *tp, xfs_buf_flags_t flags,
struct xfs_buf **agibpp);
int xfs_ialloc_read_agi(struct xfs_perag *pag, struct xfs_trans *tp,
- struct xfs_buf **agibpp);
+ int flags, struct xfs_buf **agibpp);
+#define XFS_IALLOC_FLAG_TRYLOCK (1U << 0) /* use trylock for buffer locking */
/*
* Lookup a record by ino in the btree given by cur.
diff --git a/libxfs/xfs_ialloc_btree.c b/libxfs/xfs_ialloc_btree.c
index 58c520ecb..5db9d0b33 100644
--- a/libxfs/xfs_ialloc_btree.c
+++ b/libxfs/xfs_ialloc_btree.c
@@ -744,7 +744,7 @@ xfs_finobt_count_blocks(
struct xfs_btree_cur *cur;
int error;
- error = xfs_ialloc_read_agi(pag, tp, &agbp);
+ error = xfs_ialloc_read_agi(pag, tp, 0, &agbp);
if (error)
return error;
@@ -767,7 +767,7 @@ xfs_finobt_read_blocks(
struct xfs_agi *agi;
int error;
- error = xfs_ialloc_read_agi(pag, tp, &agbp);
+ error = xfs_ialloc_read_agi(pag, tp, 0, &agbp);
if (error)
return error;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 002/115] xfs: constify xfs_bmap_is_written_extent
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
2024-07-30 0:24 ` [PATCH 001/115] xfs: pass xfs_buf lookup flags to xfs_*read_agi Darrick J. Wong
@ 2024-07-30 0:24 ` Darrick J. Wong
2024-07-30 0:24 ` [PATCH 003/115] xfs: introduce new file range exchange ioctl Darrick J. Wong
` (112 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:24 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 15f78aa3eb07645e7bef15a53b4ae1c757907d2c
This predicate doesn't modify the structure that's being passed in, so
we can mark it const.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_bmap.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libxfs/xfs_bmap.h b/libxfs/xfs_bmap.h
index f76625953..b8bdbf156 100644
--- a/libxfs/xfs_bmap.h
+++ b/libxfs/xfs_bmap.h
@@ -158,7 +158,7 @@ static inline bool xfs_bmap_is_real_extent(const struct xfs_bmbt_irec *irec)
* Return true if the extent is a real, allocated extent, or false if it is a
* delayed allocation, and unwritten extent or a hole.
*/
-static inline bool xfs_bmap_is_written_extent(struct xfs_bmbt_irec *irec)
+static inline bool xfs_bmap_is_written_extent(const struct xfs_bmbt_irec *irec)
{
return xfs_bmap_is_real_extent(irec) &&
irec->br_state != XFS_EXT_UNWRITTEN;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 003/115] xfs: introduce new file range exchange ioctl
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
2024-07-30 0:24 ` [PATCH 001/115] xfs: pass xfs_buf lookup flags to xfs_*read_agi Darrick J. Wong
2024-07-30 0:24 ` [PATCH 002/115] xfs: constify xfs_bmap_is_written_extent Darrick J. Wong
@ 2024-07-30 0:24 ` Darrick J. Wong
2024-07-30 0:24 ` [PATCH 004/115] xfs: create a incompat flag for atomic file mapping exchanges Darrick J. Wong
` (111 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:24 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 9a64d9b3109d01cca0b83c1d36538b7a37c5284e
Introduce a new ioctl to handle exchanging ranges of bytes
between files. The goal here is to perform the exchange atomically with
respect to applications -- either they see the file contents before the
exchange or they see that A-B is now B-A, even if the kernel crashes.
My original goal with all this code was to make it so that online repair
can build a replacement directory or xattr structure in a temporary file
and commit the repair by atomically exchanging all the data blocks
between the two files. However, I needed a way to test this mechanism
thoroughly, so I've been evolving an ioctl interface since then.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_fs.h | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index ca1b17d01..8a1e30cf4 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -772,6 +772,46 @@ struct xfs_scrub_metadata {
# define XFS_XATTR_LIST_MAX 65536
#endif
+/*
+ * Exchange part of file1 with part of the file that this ioctl that is being
+ * called against (which we'll call file2). Filesystems must be able to
+ * restart and complete the operation even after the system goes down.
+ */
+struct xfs_exchange_range {
+ __s32 file1_fd;
+ __u32 pad; /* must be zeroes */
+ __u64 file1_offset; /* file1 offset, bytes */
+ __u64 file2_offset; /* file2 offset, bytes */
+ __u64 length; /* bytes to exchange */
+
+ __u64 flags; /* see XFS_EXCHANGE_RANGE_* below */
+};
+
+/*
+ * Exchange file data all the way to the ends of both files, and then exchange
+ * the file sizes. This flag can be used to replace a file's contents with a
+ * different amount of data. length will be ignored.
+ */
+#define XFS_EXCHANGE_RANGE_TO_EOF (1ULL << 0)
+
+/* Flush all changes in file data and file metadata to disk before returning. */
+#define XFS_EXCHANGE_RANGE_DSYNC (1ULL << 1)
+
+/* Dry run; do all the parameter verification but do not change anything. */
+#define XFS_EXCHANGE_RANGE_DRY_RUN (1ULL << 2)
+
+/*
+ * Exchange only the parts of the two files where the file allocation units
+ * mapped to file1's range have been written to. This can accelerate
+ * scatter-gather atomic writes with a temp file if all writes are aligned to
+ * the file allocation unit.
+ */
+#define XFS_EXCHANGE_RANGE_FILE1_WRITTEN (1ULL << 3)
+
+#define XFS_EXCHANGE_RANGE_ALL_FLAGS (XFS_EXCHANGE_RANGE_TO_EOF | \
+ XFS_EXCHANGE_RANGE_DSYNC | \
+ XFS_EXCHANGE_RANGE_DRY_RUN | \
+ XFS_EXCHANGE_RANGE_FILE1_WRITTEN)
/*
* ioctl commands that are used by Linux filesystems
@@ -843,6 +883,7 @@ struct xfs_scrub_metadata {
#define XFS_IOC_FSGEOMETRY _IOR ('X', 126, struct xfs_fsop_geom)
#define XFS_IOC_BULKSTAT _IOR ('X', 127, struct xfs_bulkstat_req)
#define XFS_IOC_INUMBERS _IOR ('X', 128, struct xfs_inumbers_req)
+#define XFS_IOC_EXCHANGE_RANGE _IOWR('X', 129, struct xfs_exchange_range)
/* XFS_IOC_GETFSUUID ---------- deprecated 140 */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 004/115] xfs: create a incompat flag for atomic file mapping exchanges
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 0:24 ` [PATCH 003/115] xfs: introduce new file range exchange ioctl Darrick J. Wong
@ 2024-07-30 0:24 ` Darrick J. Wong
2024-07-30 0:25 ` [PATCH 005/115] xfs: introduce a file mapping exchange log intent item Darrick J. Wong
` (110 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:24 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 1518646eef26c220e9256906260ecaaa64503522
Create a incompat flag so that we only attempt to process file mapping
exchange log items if the filesystem supports it, and a geometry flag to
advertise support if it's present.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
include/xfs_mount.h | 2 ++
libxfs/xfs_format.h | 23 ++++++++++++-----------
libxfs/xfs_fs.h | 1 +
libxfs/xfs_sb.c | 4 ++++
4 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/include/xfs_mount.h b/include/xfs_mount.h
index 9c492b8f5..a9525e4e0 100644
--- a/include/xfs_mount.h
+++ b/include/xfs_mount.h
@@ -169,6 +169,7 @@ typedef struct xfs_mount {
#define XFS_FEAT_BIGTIME (1ULL << 24) /* large timestamps */
#define XFS_FEAT_NEEDSREPAIR (1ULL << 25) /* needs xfs_repair */
#define XFS_FEAT_NREXT64 (1ULL << 26) /* large extent counters */
+#define XFS_FEAT_EXCHANGE_RANGE (1ULL << 27) /* exchange range */
#define __XFS_HAS_FEAT(name, NAME) \
static inline bool xfs_has_ ## name (struct xfs_mount *mp) \
@@ -213,6 +214,7 @@ __XFS_HAS_FEAT(inobtcounts, INOBTCNT)
__XFS_HAS_FEAT(bigtime, BIGTIME)
__XFS_HAS_FEAT(needsrepair, NEEDSREPAIR)
__XFS_HAS_FEAT(large_extent_counts, NREXT64)
+__XFS_HAS_FEAT(exchange_range, EXCHANGE_RANGE)
/* Kernel mount features that we don't support */
#define __XFS_UNSUPP_FEAT(name) \
diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h
index 2b2f9050f..ff1e28316 100644
--- a/libxfs/xfs_format.h
+++ b/libxfs/xfs_format.h
@@ -367,18 +367,19 @@ xfs_sb_has_ro_compat_feature(
return (sbp->sb_features_ro_compat & feature) != 0;
}
-#define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */
-#define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1) /* sparse inode chunks */
-#define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */
-#define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */
-#define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */
-#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */
+#define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */
+#define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1) /* sparse inode chunks */
+#define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */
+#define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */
+#define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */
+#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */
+#define XFS_SB_FEAT_INCOMPAT_EXCHRANGE (1 << 6) /* exchangerange supported */
#define XFS_SB_FEAT_INCOMPAT_ALL \
- (XFS_SB_FEAT_INCOMPAT_FTYPE| \
- XFS_SB_FEAT_INCOMPAT_SPINODES| \
- XFS_SB_FEAT_INCOMPAT_META_UUID| \
- XFS_SB_FEAT_INCOMPAT_BIGTIME| \
- XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR| \
+ (XFS_SB_FEAT_INCOMPAT_FTYPE | \
+ XFS_SB_FEAT_INCOMPAT_SPINODES | \
+ XFS_SB_FEAT_INCOMPAT_META_UUID | \
+ XFS_SB_FEAT_INCOMPAT_BIGTIME | \
+ XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \
XFS_SB_FEAT_INCOMPAT_NREXT64)
#define XFS_SB_FEAT_INCOMPAT_UNKNOWN ~XFS_SB_FEAT_INCOMPAT_ALL
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index 8a1e30cf4..53526fca7 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -239,6 +239,7 @@ typedef struct xfs_fsop_resblks {
#define XFS_FSOP_GEOM_FLAGS_BIGTIME (1 << 21) /* 64-bit nsec timestamps */
#define XFS_FSOP_GEOM_FLAGS_INOBTCNT (1 << 22) /* inobt btree counter */
#define XFS_FSOP_GEOM_FLAGS_NREXT64 (1 << 23) /* large extent counters */
+#define XFS_FSOP_GEOM_FLAGS_EXCHANGE_RANGE (1 << 24) /* exchange range */
/*
* Minimum and maximum sizes need for growth checks.
diff --git a/libxfs/xfs_sb.c b/libxfs/xfs_sb.c
index 895d646bb..2db43b805 100644
--- a/libxfs/xfs_sb.c
+++ b/libxfs/xfs_sb.c
@@ -173,6 +173,8 @@ xfs_sb_version_to_features(
features |= XFS_FEAT_NEEDSREPAIR;
if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_NREXT64)
features |= XFS_FEAT_NREXT64;
+ if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_EXCHRANGE)
+ features |= XFS_FEAT_EXCHANGE_RANGE;
return features;
}
@@ -1257,6 +1259,8 @@ xfs_fs_geometry(
}
if (xfs_has_large_extent_counts(mp))
geo->flags |= XFS_FSOP_GEOM_FLAGS_NREXT64;
+ if (xfs_has_exchange_range(mp))
+ geo->flags |= XFS_FSOP_GEOM_FLAGS_EXCHANGE_RANGE;
geo->rtsectsize = sbp->sb_blocksize;
geo->dirblocksize = xfs_dir2_dirblock_bytes(sbp);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 005/115] xfs: introduce a file mapping exchange log intent item
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 0:24 ` [PATCH 004/115] xfs: create a incompat flag for atomic file mapping exchanges Darrick J. Wong
@ 2024-07-30 0:25 ` Darrick J. Wong
2024-07-30 0:25 ` [PATCH 006/115] xfs: create deferred log items for file mapping exchanges Darrick J. Wong
` (109 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:25 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 6c08f434bd33f88cf169e9e43c7a5e42fb3f2118
Introduce a new intent log item to handle exchanging mappings between
the forks of two files.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_log_format.h | 42 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 39 insertions(+), 3 deletions(-)
diff --git a/libxfs/xfs_log_format.h b/libxfs/xfs_log_format.h
index 16872972e..09024431c 100644
--- a/libxfs/xfs_log_format.h
+++ b/libxfs/xfs_log_format.h
@@ -117,8 +117,9 @@ struct xfs_unmount_log_format {
#define XLOG_REG_TYPE_ATTRD_FORMAT 28
#define XLOG_REG_TYPE_ATTR_NAME 29
#define XLOG_REG_TYPE_ATTR_VALUE 30
-#define XLOG_REG_TYPE_MAX 30
-
+#define XLOG_REG_TYPE_XMI_FORMAT 31
+#define XLOG_REG_TYPE_XMD_FORMAT 32
+#define XLOG_REG_TYPE_MAX 32
/*
* Flags to log operation header
@@ -243,6 +244,8 @@ typedef struct xfs_trans_header {
#define XFS_LI_BUD 0x1245
#define XFS_LI_ATTRI 0x1246 /* attr set/remove intent*/
#define XFS_LI_ATTRD 0x1247 /* attr set/remove done */
+#define XFS_LI_XMI 0x1248 /* mapping exchange intent */
+#define XFS_LI_XMD 0x1249 /* mapping exchange done */
#define XFS_LI_TYPE_DESC \
{ XFS_LI_EFI, "XFS_LI_EFI" }, \
@@ -260,7 +263,9 @@ typedef struct xfs_trans_header {
{ XFS_LI_BUI, "XFS_LI_BUI" }, \
{ XFS_LI_BUD, "XFS_LI_BUD" }, \
{ XFS_LI_ATTRI, "XFS_LI_ATTRI" }, \
- { XFS_LI_ATTRD, "XFS_LI_ATTRD" }
+ { XFS_LI_ATTRD, "XFS_LI_ATTRD" }, \
+ { XFS_LI_XMI, "XFS_LI_XMI" }, \
+ { XFS_LI_XMD, "XFS_LI_XMD" }
/*
* Inode Log Item Format definitions.
@@ -878,6 +883,37 @@ struct xfs_bud_log_format {
uint64_t bud_bui_id; /* id of corresponding bui */
};
+/*
+ * XMI/XMD (file mapping exchange) log format definitions
+ */
+
+/* This is the structure used to lay out an mapping exchange log item. */
+struct xfs_xmi_log_format {
+ uint16_t xmi_type; /* xmi log item type */
+ uint16_t xmi_size; /* size of this item */
+ uint32_t __pad; /* must be zero */
+ uint64_t xmi_id; /* xmi identifier */
+
+ uint64_t xmi_inode1; /* inumber of first file */
+ uint64_t xmi_inode2; /* inumber of second file */
+ uint64_t xmi_startoff1; /* block offset into file1 */
+ uint64_t xmi_startoff2; /* block offset into file2 */
+ uint64_t xmi_blockcount; /* number of blocks */
+ uint64_t xmi_flags; /* XFS_EXCHMAPS_* */
+ uint64_t xmi_isize1; /* intended file1 size */
+ uint64_t xmi_isize2; /* intended file2 size */
+};
+
+#define XFS_EXCHMAPS_LOGGED_FLAGS (0)
+
+/* This is the structure used to lay out an mapping exchange done log item. */
+struct xfs_xmd_log_format {
+ uint16_t xmd_type; /* xmd log item type */
+ uint16_t xmd_size; /* size of this item */
+ uint32_t __pad;
+ uint64_t xmd_xmi_id; /* id of corresponding xmi */
+};
+
/*
* Dquot Log format definitions.
*
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 006/115] xfs: create deferred log items for file mapping exchanges
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 0:25 ` [PATCH 005/115] xfs: introduce a file mapping exchange log intent item Darrick J. Wong
@ 2024-07-30 0:25 ` Darrick J. Wong
2024-07-30 0:25 ` [PATCH 007/115] xfs: add error injection to test file mapping exchange recovery Darrick J. Wong
` (108 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:25 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 966ceafc7a437105ecfe1cadb3747b2965a260ca
Now that we've created the skeleton of a log intent item to track and
restart file mapping exchange operations, add the upper level logic to
log. This builds on the existing bmap update intent items that have
been around for a while now.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
include/xfs_trace.h | 14 +
libxfs/Makefile | 2
libxfs/defer_item.c | 88 ++++
libxfs/defer_item.h | 5
libxfs/libxfs_priv.h | 30 +
libxfs/xfs_defer.c | 6
libxfs/xfs_defer.h | 2
libxfs/xfs_exchmaps.c | 1042 ++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_exchmaps.h | 118 +++++
libxfs/xfs_log_format.h | 24 +
libxfs/xfs_trans_space.h | 4
11 files changed, 1333 insertions(+), 2 deletions(-)
create mode 100644 libxfs/xfs_exchmaps.c
create mode 100644 libxfs/xfs_exchmaps.h
diff --git a/include/xfs_trace.h b/include/xfs_trace.h
index 6b9d3358a..2e5e89d65 100644
--- a/include/xfs_trace.h
+++ b/include/xfs_trace.h
@@ -329,6 +329,9 @@
#define trace_xfs_refcount_cow_decrease(...) ((void) 0)
#define trace_xfs_refcount_recover_extent(...) ((void) 0)
+#define trace_xfs_reflink_set_inode_flag(...) ((void) 0)
+#define trace_xfs_reflink_unset_inode_flag(...) ((void) 0)
+
#define trace_xfs_rmap_find_left_neighbor_candidate(...) ((void) 0)
#define trace_xfs_rmap_find_left_neighbor_query(...) ((void) 0)
#define trace_xfs_rmap_find_left_neighbor_result(...) ((void) 0)
@@ -342,6 +345,17 @@
#define trace_xfs_rmap_map_error(...) ((void) 0)
#define trace_xfs_rmap_delete_error(...) ((void) 0)
+#define trace_xfs_exchmaps_defer(...) ((void) 0)
+#define trace_xfs_exchmaps_delta_nextents(...) ((void) 0)
+#define trace_xfs_exchmaps_delta_nextents_step(...) ((void) 0)
+#define trace_xfs_exchmaps_mapping1_skip(...) ((void) 0)
+#define trace_xfs_exchmaps_mapping1(...) ((void) 0)
+#define trace_xfs_exchmaps_mapping2(...) ((void) 0)
+#define trace_xfs_exchmaps_final_estimate(...) ((void) 0)
+#define trace_xfs_exchmaps_initial_estimate(...) ((void) 0)
+#define trace_xfs_exchmaps_overhead(...) ((void) 0)
+#define trace_xfs_exchmaps_update_inode_size(...) ((void) 0)
+
#define trace_xfs_fs_mark_healthy(a,b) ((void) 0)
#define trace_xlog_intent_recovery_failed(...) ((void) 0)
diff --git a/libxfs/Makefile b/libxfs/Makefile
index 1c88dbc2a..e3fa18fee 100644
--- a/libxfs/Makefile
+++ b/libxfs/Makefile
@@ -45,6 +45,7 @@ HFILES = \
xfs_da_btree.h \
xfs_dir2.h \
xfs_errortag.h \
+ xfs_exchmaps.h \
xfs_ialloc.h \
xfs_ialloc_btree.h \
xfs_inode_buf.h \
@@ -94,6 +95,7 @@ CFILES = buf_mem.c \
xfs_dir2_node.c \
xfs_dir2_sf.c \
xfs_dquot_buf.c \
+ xfs_exchmaps.c \
xfs_ialloc.c \
xfs_iext_tree.c \
xfs_inode_buf.c \
diff --git a/libxfs/defer_item.c b/libxfs/defer_item.c
index 21dd1d0f4..fd329e77c 100644
--- a/libxfs/defer_item.c
+++ b/libxfs/defer_item.c
@@ -25,6 +25,8 @@
#include "xfs_attr.h"
#include "libxfs.h"
#include "defer_item.h"
+#include "xfs_ag.h"
+#include "xfs_exchmaps.h"
/* Dummy defer item ops, since we don't do logging. */
@@ -677,3 +679,89 @@ const struct xfs_defer_op_type xfs_attr_defer_type = {
.finish_item = xfs_attr_finish_item,
.cancel_item = xfs_attr_cancel_item,
};
+
+/* File Mapping Exchanges */
+
+STATIC struct xfs_log_item *
+xfs_exchmaps_create_intent(
+ struct xfs_trans *tp,
+ struct list_head *items,
+ unsigned int count,
+ bool sort)
+{
+ return NULL;
+}
+STATIC struct xfs_log_item *
+xfs_exchmaps_create_done(
+ struct xfs_trans *tp,
+ struct xfs_log_item *intent,
+ unsigned int count)
+{
+ return NULL;
+}
+
+/* Add this deferred XMI to the transaction. */
+void
+xfs_exchmaps_defer_add(
+ struct xfs_trans *tp,
+ struct xfs_exchmaps_intent *xmi)
+{
+ trace_xfs_exchmaps_defer(tp->t_mountp, xmi);
+
+ xfs_defer_add(tp, &xmi->xmi_list, &xfs_exchmaps_defer_type);
+}
+
+static inline struct xfs_exchmaps_intent *xmi_entry(const struct list_head *e)
+{
+ return list_entry(e, struct xfs_exchmaps_intent, xmi_list);
+}
+
+/* Process a deferred swapext update. */
+STATIC int
+xfs_exchmaps_finish_item(
+ struct xfs_trans *tp,
+ struct xfs_log_item *done,
+ struct list_head *item,
+ struct xfs_btree_cur **state)
+{
+ struct xfs_exchmaps_intent *xmi = xmi_entry(item);
+ int error;
+
+ /*
+ * Exchange one more extent between the two files. If there's still
+ * more work to do, we want to requeue ourselves after all other
+ * pending deferred operations have finished. This includes all of the
+ * dfops that we queued directly as well as any new ones created in the
+ * process of finishing the others.
+ */
+ error = xfs_exchmaps_finish_one(tp, xmi);
+ if (error != -EAGAIN)
+ kmem_cache_free(xfs_exchmaps_intent_cache, xmi);
+ return error;
+}
+
+/* Abort all pending XMIs. */
+STATIC void
+xfs_exchmaps_abort_intent(
+ struct xfs_log_item *intent)
+{
+}
+
+/* Cancel a deferred swapext update. */
+STATIC void
+xfs_exchmaps_cancel_item(
+ struct list_head *item)
+{
+ struct xfs_exchmaps_intent *xmi = xmi_entry(item);
+
+ kmem_cache_free(xfs_exchmaps_intent_cache, xmi);
+}
+
+const struct xfs_defer_op_type xfs_exchmaps_defer_type = {
+ .name = "exchmaps",
+ .create_intent = xfs_exchmaps_create_intent,
+ .abort_intent = xfs_exchmaps_abort_intent,
+ .create_done = xfs_exchmaps_create_done,
+ .finish_item = xfs_exchmaps_finish_item,
+ .cancel_item = xfs_exchmaps_cancel_item,
+};
diff --git a/libxfs/defer_item.h b/libxfs/defer_item.h
index 6d3abf158..a5a07867c 100644
--- a/libxfs/defer_item.h
+++ b/libxfs/defer_item.h
@@ -10,4 +10,9 @@ struct xfs_bmap_intent;
void xfs_bmap_defer_add(struct xfs_trans *tp, struct xfs_bmap_intent *bi);
+struct xfs_exchmaps_intent;
+
+void xfs_exchmaps_defer_add(struct xfs_trans *tp,
+ struct xfs_exchmaps_intent *xmi);
+
#endif /* __LIBXFS_DEFER_ITEM_H_ */
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index 81f641f32..9ddba767b 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -219,6 +219,35 @@ static inline bool WARN_ON(bool expr) {
(inode)->i_version = (version); \
} while (0)
+#define __must_check __attribute__((__warn_unused_result__))
+
+/*
+ * Allows for effectively applying __must_check to a macro so we can have
+ * both the type-agnostic benefits of the macros while also being able to
+ * enforce that the return value is, in fact, checked.
+ */
+static inline bool __must_check __must_check_overflow(bool overflow)
+{
+ return unlikely(overflow);
+}
+
+/*
+ * For simplicity and code hygiene, the fallback code below insists on
+ * a, b and *d having the same type (similar to the min() and max()
+ * macros), whereas gcc's type-generic overflow checkers accept
+ * different types. Hence we don't just make check_add_overflow an
+ * alias for __builtin_add_overflow, but add type checks similar to
+ * below.
+ */
+#define check_add_overflow(a, b, d) __must_check_overflow(({ \
+ typeof(a) __a = (a); \
+ typeof(b) __b = (b); \
+ typeof(d) __d = (d); \
+ (void) (&__a == &__b); \
+ (void) (&__a == __d); \
+ __builtin_add_overflow(__a, __b, __d); \
+}))
+
#define min_t(type,x,y) \
({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
#define max_t(type,x,y) \
@@ -541,6 +570,7 @@ void xfs_log_item_init(struct xfs_mount *mp, struct xfs_log_item *lip, int type,
#define xfs_log_in_recovery(mp) (false)
/* xfs_icache.c */
+#define xfs_inode_clear_cowblocks_tag(ip) do { } while (0)
#define xfs_inode_set_cowblocks_tag(ip) do { } while (0)
#define xfs_inode_set_eofblocks_tag(ip) do { } while (0)
diff --git a/libxfs/xfs_defer.c b/libxfs/xfs_defer.c
index b80ac04ab..3a91bb3a5 100644
--- a/libxfs/xfs_defer.c
+++ b/libxfs/xfs_defer.c
@@ -21,6 +21,7 @@
#include "xfs_da_format.h"
#include "xfs_da_btree.h"
#include "xfs_attr.h"
+#include "xfs_exchmaps.h"
static struct kmem_cache *xfs_defer_pending_cache;
@@ -1170,6 +1171,10 @@ xfs_defer_init_item_caches(void)
error = xfs_attr_intent_init_cache();
if (error)
goto err;
+ error = xfs_exchmaps_intent_init_cache();
+ if (error)
+ goto err;
+
return 0;
err:
xfs_defer_destroy_item_caches();
@@ -1180,6 +1185,7 @@ xfs_defer_init_item_caches(void)
void
xfs_defer_destroy_item_caches(void)
{
+ xfs_exchmaps_intent_destroy_cache();
xfs_attr_intent_destroy_cache();
xfs_extfree_intent_destroy_cache();
xfs_bmap_intent_destroy_cache();
diff --git a/libxfs/xfs_defer.h b/libxfs/xfs_defer.h
index 18a9fb92d..81cca60d7 100644
--- a/libxfs/xfs_defer.h
+++ b/libxfs/xfs_defer.h
@@ -72,7 +72,7 @@ extern const struct xfs_defer_op_type xfs_rmap_update_defer_type;
extern const struct xfs_defer_op_type xfs_extent_free_defer_type;
extern const struct xfs_defer_op_type xfs_agfl_free_defer_type;
extern const struct xfs_defer_op_type xfs_attr_defer_type;
-
+extern const struct xfs_defer_op_type xfs_exchmaps_defer_type;
/*
* Deferred operation item relogging limits.
diff --git a/libxfs/xfs_exchmaps.c b/libxfs/xfs_exchmaps.c
new file mode 100644
index 000000000..b2c35032d
--- /dev/null
+++ b/libxfs/xfs_exchmaps.c
@@ -0,0 +1,1042 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "libxfs_priv.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_inode.h"
+#include "xfs_trans.h"
+#include "xfs_bmap.h"
+#include "xfs_exchmaps.h"
+#include "xfs_trace.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_trans_space.h"
+#include "xfs_errortag.h"
+#include "xfs_health.h"
+#include "defer_item.h"
+
+struct kmem_cache *xfs_exchmaps_intent_cache;
+
+/* bmbt mappings adjacent to a pair of records. */
+struct xfs_exchmaps_adjacent {
+ struct xfs_bmbt_irec left1;
+ struct xfs_bmbt_irec right1;
+ struct xfs_bmbt_irec left2;
+ struct xfs_bmbt_irec right2;
+};
+
+#define ADJACENT_INIT { \
+ .left1 = { .br_startblock = HOLESTARTBLOCK }, \
+ .right1 = { .br_startblock = HOLESTARTBLOCK }, \
+ .left2 = { .br_startblock = HOLESTARTBLOCK }, \
+ .right2 = { .br_startblock = HOLESTARTBLOCK }, \
+}
+
+/* Information to reset reflink flag / CoW fork state after an exchange. */
+
+/*
+ * If the reflink flag is set on either inode, make sure it has an incore CoW
+ * fork, since all reflink inodes must have them. If there's a CoW fork and it
+ * has mappings in it, make sure the inodes are tagged appropriately so that
+ * speculative preallocations can be GC'd if we run low of space.
+ */
+static inline void
+xfs_exchmaps_ensure_cowfork(
+ struct xfs_inode *ip)
+{
+ struct xfs_ifork *cfork;
+
+ if (xfs_is_reflink_inode(ip))
+ xfs_ifork_init_cow(ip);
+
+ cfork = xfs_ifork_ptr(ip, XFS_COW_FORK);
+ if (!cfork)
+ return;
+ if (cfork->if_bytes > 0)
+ xfs_inode_set_cowblocks_tag(ip);
+ else
+ xfs_inode_clear_cowblocks_tag(ip);
+}
+
+/*
+ * Adjust the on-disk inode size upwards if needed so that we never add
+ * mappings into the file past EOF. This is crucial so that log recovery won't
+ * get confused by the sudden appearance of post-eof mappings.
+ */
+STATIC void
+xfs_exchmaps_update_size(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ struct xfs_bmbt_irec *imap,
+ xfs_fsize_t new_isize)
+{
+ struct xfs_mount *mp = tp->t_mountp;
+ xfs_fsize_t len;
+
+ if (new_isize < 0)
+ return;
+
+ len = min(XFS_FSB_TO_B(mp, imap->br_startoff + imap->br_blockcount),
+ new_isize);
+
+ if (len <= ip->i_disk_size)
+ return;
+
+ trace_xfs_exchmaps_update_inode_size(ip, len);
+
+ ip->i_disk_size = len;
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+}
+
+/* Advance the incore state tracking after exchanging a mapping. */
+static inline void
+xmi_advance(
+ struct xfs_exchmaps_intent *xmi,
+ const struct xfs_bmbt_irec *irec)
+{
+ xmi->xmi_startoff1 += irec->br_blockcount;
+ xmi->xmi_startoff2 += irec->br_blockcount;
+ xmi->xmi_blockcount -= irec->br_blockcount;
+}
+
+/* Do we still have more mappings to exchange? */
+static inline bool
+xmi_has_more_exchange_work(const struct xfs_exchmaps_intent *xmi)
+{
+ return xmi->xmi_blockcount > 0;
+}
+
+/* Do we have post-operation cleanups to perform? */
+static inline bool
+xmi_has_postop_work(const struct xfs_exchmaps_intent *xmi)
+{
+ return xmi->xmi_flags & (XFS_EXCHMAPS_CLEAR_INO1_REFLINK |
+ XFS_EXCHMAPS_CLEAR_INO2_REFLINK);
+}
+
+/* Check all mappings to make sure we can actually exchange them. */
+int
+xfs_exchmaps_check_forks(
+ struct xfs_mount *mp,
+ const struct xfs_exchmaps_req *req)
+{
+ struct xfs_ifork *ifp1, *ifp2;
+ int whichfork = xfs_exchmaps_reqfork(req);
+
+ /* No fork? */
+ ifp1 = xfs_ifork_ptr(req->ip1, whichfork);
+ ifp2 = xfs_ifork_ptr(req->ip2, whichfork);
+ if (!ifp1 || !ifp2)
+ return -EINVAL;
+
+ /* We don't know how to exchange local format forks. */
+ if (ifp1->if_format == XFS_DINODE_FMT_LOCAL ||
+ ifp2->if_format == XFS_DINODE_FMT_LOCAL)
+ return -EINVAL;
+
+ /* We don't support realtime data forks yet. */
+ if (!XFS_IS_REALTIME_INODE(req->ip1))
+ return 0;
+ if (whichfork == XFS_ATTR_FORK)
+ return 0;
+ return -EINVAL;
+}
+
+#ifdef CONFIG_XFS_QUOTA
+/* Log the actual updates to the quota accounting. */
+static inline void
+xfs_exchmaps_update_quota(
+ struct xfs_trans *tp,
+ struct xfs_exchmaps_intent *xmi,
+ struct xfs_bmbt_irec *irec1,
+ struct xfs_bmbt_irec *irec2)
+{
+ int64_t ip1_delta = 0, ip2_delta = 0;
+ unsigned int qflag;
+
+ qflag = XFS_IS_REALTIME_INODE(xmi->xmi_ip1) ? XFS_TRANS_DQ_RTBCOUNT :
+ XFS_TRANS_DQ_BCOUNT;
+
+ if (xfs_bmap_is_real_extent(irec1)) {
+ ip1_delta -= irec1->br_blockcount;
+ ip2_delta += irec1->br_blockcount;
+ }
+
+ if (xfs_bmap_is_real_extent(irec2)) {
+ ip1_delta += irec2->br_blockcount;
+ ip2_delta -= irec2->br_blockcount;
+ }
+
+ xfs_trans_mod_dquot_byino(tp, xmi->xmi_ip1, qflag, ip1_delta);
+ xfs_trans_mod_dquot_byino(tp, xmi->xmi_ip2, qflag, ip2_delta);
+}
+#else
+# define xfs_exchmaps_update_quota(tp, xmi, irec1, irec2) ((void)0)
+#endif
+
+/* Decide if we want to skip this mapping from file1. */
+static inline bool
+xfs_exchmaps_can_skip_mapping(
+ struct xfs_exchmaps_intent *xmi,
+ struct xfs_bmbt_irec *irec)
+{
+ /* Do not skip this mapping if the caller did not tell us to. */
+ if (!(xmi->xmi_flags & XFS_EXCHMAPS_INO1_WRITTEN))
+ return false;
+
+ /* Do not skip mapped, written mappings. */
+ if (xfs_bmap_is_written_extent(irec))
+ return false;
+
+ /*
+ * The mapping is unwritten or a hole. It cannot be a delalloc
+ * reservation because we already excluded those. It cannot be an
+ * unwritten mapping with dirty page cache because we flushed the page
+ * cache. We don't support realtime files yet, so we needn't (yet)
+ * deal with them.
+ */
+ return true;
+}
+
+/*
+ * Walk forward through the file ranges in @xmi until we find two different
+ * mappings to exchange. If there is work to do, return the mappings;
+ * otherwise we've reached the end of the range and xmi_blockcount will be
+ * zero.
+ *
+ * If the walk skips over a pair of mappings to the same storage, save them as
+ * the left records in @adj (if provided) so that the simulation phase can
+ * avoid an extra lookup.
+ */
+static int
+xfs_exchmaps_find_mappings(
+ struct xfs_exchmaps_intent *xmi,
+ struct xfs_bmbt_irec *irec1,
+ struct xfs_bmbt_irec *irec2,
+ struct xfs_exchmaps_adjacent *adj)
+{
+ int nimaps;
+ int bmap_flags;
+ int error;
+
+ bmap_flags = xfs_bmapi_aflag(xfs_exchmaps_whichfork(xmi));
+
+ for (; xmi_has_more_exchange_work(xmi); xmi_advance(xmi, irec1)) {
+ /* Read mapping from the first file */
+ nimaps = 1;
+ error = xfs_bmapi_read(xmi->xmi_ip1, xmi->xmi_startoff1,
+ xmi->xmi_blockcount, irec1, &nimaps,
+ bmap_flags);
+ if (error)
+ return error;
+ if (nimaps != 1 ||
+ irec1->br_startblock == DELAYSTARTBLOCK ||
+ irec1->br_startoff != xmi->xmi_startoff1) {
+ /*
+ * We should never get no mapping or a delalloc mapping
+ * or something that doesn't match what we asked for,
+ * since the caller flushed both inodes and we hold the
+ * ILOCKs for both inodes.
+ */
+ ASSERT(0);
+ return -EINVAL;
+ }
+
+ if (xfs_exchmaps_can_skip_mapping(xmi, irec1)) {
+ trace_xfs_exchmaps_mapping1_skip(xmi->xmi_ip1, irec1);
+ continue;
+ }
+
+ /* Read mapping from the second file */
+ nimaps = 1;
+ error = xfs_bmapi_read(xmi->xmi_ip2, xmi->xmi_startoff2,
+ irec1->br_blockcount, irec2, &nimaps,
+ bmap_flags);
+ if (error)
+ return error;
+ if (nimaps != 1 ||
+ irec2->br_startblock == DELAYSTARTBLOCK ||
+ irec2->br_startoff != xmi->xmi_startoff2) {
+ /*
+ * We should never get no mapping or a delalloc mapping
+ * or something that doesn't match what we asked for,
+ * since the caller flushed both inodes and we hold the
+ * ILOCKs for both inodes.
+ */
+ ASSERT(0);
+ return -EINVAL;
+ }
+
+ /*
+ * We can only exchange as many blocks as the smaller of the
+ * two mapping maps.
+ */
+ irec1->br_blockcount = min(irec1->br_blockcount,
+ irec2->br_blockcount);
+
+ trace_xfs_exchmaps_mapping1(xmi->xmi_ip1, irec1);
+ trace_xfs_exchmaps_mapping2(xmi->xmi_ip2, irec2);
+
+ /* We found something to exchange, so return it. */
+ if (irec1->br_startblock != irec2->br_startblock)
+ return 0;
+
+ /*
+ * Two mappings pointing to the same physical block must not
+ * have different states; that's filesystem corruption. Move
+ * on to the next mapping if they're both holes or both point
+ * to the same physical space extent.
+ */
+ if (irec1->br_state != irec2->br_state) {
+ xfs_bmap_mark_sick(xmi->xmi_ip1,
+ xfs_exchmaps_whichfork(xmi));
+ xfs_bmap_mark_sick(xmi->xmi_ip2,
+ xfs_exchmaps_whichfork(xmi));
+ return -EFSCORRUPTED;
+ }
+
+ /*
+ * Save the mappings if we're estimating work and skipping
+ * these identical mappings.
+ */
+ if (adj) {
+ memcpy(&adj->left1, irec1, sizeof(*irec1));
+ memcpy(&adj->left2, irec2, sizeof(*irec2));
+ }
+ }
+
+ return 0;
+}
+
+/* Exchange these two mappings. */
+static void
+xfs_exchmaps_one_step(
+ struct xfs_trans *tp,
+ struct xfs_exchmaps_intent *xmi,
+ struct xfs_bmbt_irec *irec1,
+ struct xfs_bmbt_irec *irec2)
+{
+ int whichfork = xfs_exchmaps_whichfork(xmi);
+
+ xfs_exchmaps_update_quota(tp, xmi, irec1, irec2);
+
+ /* Remove both mappings. */
+ xfs_bmap_unmap_extent(tp, xmi->xmi_ip1, whichfork, irec1);
+ xfs_bmap_unmap_extent(tp, xmi->xmi_ip2, whichfork, irec2);
+
+ /*
+ * Re-add both mappings. We exchange the file offsets between the two
+ * maps and add the opposite map, which has the effect of filling the
+ * logical offsets we just unmapped, but with with the physical mapping
+ * information exchanged.
+ */
+ swap(irec1->br_startoff, irec2->br_startoff);
+ xfs_bmap_map_extent(tp, xmi->xmi_ip1, whichfork, irec2);
+ xfs_bmap_map_extent(tp, xmi->xmi_ip2, whichfork, irec1);
+
+ /* Make sure we're not adding mappings past EOF. */
+ if (whichfork == XFS_DATA_FORK) {
+ xfs_exchmaps_update_size(tp, xmi->xmi_ip1, irec2,
+ xmi->xmi_isize1);
+ xfs_exchmaps_update_size(tp, xmi->xmi_ip2, irec1,
+ xmi->xmi_isize2);
+ }
+
+ /*
+ * Advance our cursor and exit. The caller (either defer ops or log
+ * recovery) will log the XMD item, and if *blockcount is nonzero, it
+ * will log a new XMI item for the remainder and call us back.
+ */
+ xmi_advance(xmi, irec1);
+}
+
+/* Clear the reflink flag after an exchange. */
+static inline void
+xfs_exchmaps_clear_reflink(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip)
+{
+ trace_xfs_reflink_unset_inode_flag(ip);
+
+ ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK;
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+}
+
+/* Finish whatever work might come after an exchange operation. */
+static int
+xfs_exchmaps_do_postop_work(
+ struct xfs_trans *tp,
+ struct xfs_exchmaps_intent *xmi)
+{
+ if (xmi->xmi_flags & XFS_EXCHMAPS_CLEAR_INO1_REFLINK) {
+ xfs_exchmaps_clear_reflink(tp, xmi->xmi_ip1);
+ xmi->xmi_flags &= ~XFS_EXCHMAPS_CLEAR_INO1_REFLINK;
+ }
+
+ if (xmi->xmi_flags & XFS_EXCHMAPS_CLEAR_INO2_REFLINK) {
+ xfs_exchmaps_clear_reflink(tp, xmi->xmi_ip2);
+ xmi->xmi_flags &= ~XFS_EXCHMAPS_CLEAR_INO2_REFLINK;
+ }
+
+ return 0;
+}
+
+/* Finish one step in a mapping exchange operation, possibly relogging. */
+int
+xfs_exchmaps_finish_one(
+ struct xfs_trans *tp,
+ struct xfs_exchmaps_intent *xmi)
+{
+ struct xfs_bmbt_irec irec1, irec2;
+ int error;
+
+ if (xmi_has_more_exchange_work(xmi)) {
+ /*
+ * If the operation state says that some range of the files
+ * have not yet been exchanged, look for mappings in that range
+ * to exchange. If we find some mappings, exchange them.
+ */
+ error = xfs_exchmaps_find_mappings(xmi, &irec1, &irec2, NULL);
+ if (error)
+ return error;
+
+ if (xmi_has_more_exchange_work(xmi))
+ xfs_exchmaps_one_step(tp, xmi, &irec1, &irec2);
+
+ /*
+ * If the caller asked us to exchange the file sizes after the
+ * exchange and either we just exchanged the last mappings in
+ * the range or we didn't find anything to exchange, update the
+ * ondisk file sizes.
+ */
+ if ((xmi->xmi_flags & XFS_EXCHMAPS_SET_SIZES) &&
+ !xmi_has_more_exchange_work(xmi)) {
+ xmi->xmi_ip1->i_disk_size = xmi->xmi_isize1;
+ xmi->xmi_ip2->i_disk_size = xmi->xmi_isize2;
+
+ xfs_trans_log_inode(tp, xmi->xmi_ip1, XFS_ILOG_CORE);
+ xfs_trans_log_inode(tp, xmi->xmi_ip2, XFS_ILOG_CORE);
+ }
+ } else if (xmi_has_postop_work(xmi)) {
+ /*
+ * Now that we're finished with the exchange operation,
+ * complete the post-op cleanup work.
+ */
+ error = xfs_exchmaps_do_postop_work(tp, xmi);
+ if (error)
+ return error;
+ }
+
+ /* If we still have work to do, ask for a new transaction. */
+ if (xmi_has_more_exchange_work(xmi) || xmi_has_postop_work(xmi)) {
+ trace_xfs_exchmaps_defer(tp->t_mountp, xmi);
+ return -EAGAIN;
+ }
+
+ /*
+ * If we reach here, we've finished all the exchange work and the post
+ * operation work. The last thing we need to do before returning to
+ * the caller is to make sure that COW forks are set up correctly.
+ */
+ if (!(xmi->xmi_flags & XFS_EXCHMAPS_ATTR_FORK)) {
+ xfs_exchmaps_ensure_cowfork(xmi->xmi_ip1);
+ xfs_exchmaps_ensure_cowfork(xmi->xmi_ip2);
+ }
+
+ return 0;
+}
+
+/*
+ * Compute the amount of bmbt blocks we should reserve for each file. In the
+ * worst case, each exchange will fill a hole with a new mapping, which could
+ * result in a btree split every time we add a new leaf block.
+ */
+static inline uint64_t
+xfs_exchmaps_bmbt_blocks(
+ struct xfs_mount *mp,
+ const struct xfs_exchmaps_req *req)
+{
+ return howmany_64(req->nr_exchanges,
+ XFS_MAX_CONTIG_BMAPS_PER_BLOCK(mp)) *
+ XFS_EXTENTADD_SPACE_RES(mp, xfs_exchmaps_reqfork(req));
+}
+
+/* Compute the space we should reserve for the rmap btree expansions. */
+static inline uint64_t
+xfs_exchmaps_rmapbt_blocks(
+ struct xfs_mount *mp,
+ const struct xfs_exchmaps_req *req)
+{
+ if (!xfs_has_rmapbt(mp))
+ return 0;
+ if (XFS_IS_REALTIME_INODE(req->ip1))
+ return 0;
+
+ return howmany_64(req->nr_exchanges,
+ XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp)) *
+ XFS_RMAPADD_SPACE_RES(mp);
+}
+
+/* Estimate the bmbt and rmapbt overhead required to exchange mappings. */
+static int
+xfs_exchmaps_estimate_overhead(
+ struct xfs_exchmaps_req *req)
+{
+ struct xfs_mount *mp = req->ip1->i_mount;
+ xfs_filblks_t bmbt_blocks;
+ xfs_filblks_t rmapbt_blocks;
+ xfs_filblks_t resblks = req->resblks;
+
+ /*
+ * Compute the number of bmbt and rmapbt blocks we might need to handle
+ * the estimated number of exchanges.
+ */
+ bmbt_blocks = xfs_exchmaps_bmbt_blocks(mp, req);
+ rmapbt_blocks = xfs_exchmaps_rmapbt_blocks(mp, req);
+
+ trace_xfs_exchmaps_overhead(mp, bmbt_blocks, rmapbt_blocks);
+
+ /* Make sure the change in file block count doesn't overflow. */
+ if (check_add_overflow(req->ip1_bcount, bmbt_blocks, &req->ip1_bcount))
+ return -EFBIG;
+ if (check_add_overflow(req->ip2_bcount, bmbt_blocks, &req->ip2_bcount))
+ return -EFBIG;
+
+ /*
+ * Add together the number of blocks we need to handle btree growth,
+ * then add it to the number of blocks we need to reserve to this
+ * transaction.
+ */
+ if (check_add_overflow(resblks, bmbt_blocks, &resblks))
+ return -ENOSPC;
+ if (check_add_overflow(resblks, bmbt_blocks, &resblks))
+ return -ENOSPC;
+ if (check_add_overflow(resblks, rmapbt_blocks, &resblks))
+ return -ENOSPC;
+ if (check_add_overflow(resblks, rmapbt_blocks, &resblks))
+ return -ENOSPC;
+
+ /* Can't actually reserve more than UINT_MAX blocks. */
+ if (req->resblks > UINT_MAX)
+ return -ENOSPC;
+
+ req->resblks = resblks;
+ trace_xfs_exchmaps_final_estimate(req);
+ return 0;
+}
+
+/* Decide if we can merge two real mappings. */
+static inline bool
+xmi_can_merge(
+ const struct xfs_bmbt_irec *b1,
+ const struct xfs_bmbt_irec *b2)
+{
+ /* Don't merge holes. */
+ if (b1->br_startblock == HOLESTARTBLOCK ||
+ b2->br_startblock == HOLESTARTBLOCK)
+ return false;
+
+ /* We don't merge holes. */
+ if (!xfs_bmap_is_real_extent(b1) || !xfs_bmap_is_real_extent(b2))
+ return false;
+
+ if (b1->br_startoff + b1->br_blockcount == b2->br_startoff &&
+ b1->br_startblock + b1->br_blockcount == b2->br_startblock &&
+ b1->br_state == b2->br_state &&
+ b1->br_blockcount + b2->br_blockcount <= XFS_MAX_BMBT_EXTLEN)
+ return true;
+
+ return false;
+}
+
+/*
+ * Decide if we can merge three mappings. Caller must ensure all three
+ * mappings must not be holes or delalloc reservations.
+ */
+static inline bool
+xmi_can_merge_all(
+ const struct xfs_bmbt_irec *l,
+ const struct xfs_bmbt_irec *m,
+ const struct xfs_bmbt_irec *r)
+{
+ xfs_filblks_t new_len;
+
+ new_len = l->br_blockcount + m->br_blockcount + r->br_blockcount;
+ return new_len <= XFS_MAX_BMBT_EXTLEN;
+}
+
+#define CLEFT_CONTIG 0x01
+#define CRIGHT_CONTIG 0x02
+#define CHOLE 0x04
+#define CBOTH_CONTIG (CLEFT_CONTIG | CRIGHT_CONTIG)
+
+#define NLEFT_CONTIG 0x10
+#define NRIGHT_CONTIG 0x20
+#define NHOLE 0x40
+#define NBOTH_CONTIG (NLEFT_CONTIG | NRIGHT_CONTIG)
+
+/* Estimate the effect of a single exchange on mapping count. */
+static inline int
+xmi_delta_nextents_step(
+ struct xfs_mount *mp,
+ const struct xfs_bmbt_irec *left,
+ const struct xfs_bmbt_irec *curr,
+ const struct xfs_bmbt_irec *new,
+ const struct xfs_bmbt_irec *right)
+{
+ bool lhole, rhole, chole, nhole;
+ unsigned int state = 0;
+ int ret = 0;
+
+ lhole = left->br_startblock == HOLESTARTBLOCK;
+ rhole = right->br_startblock == HOLESTARTBLOCK;
+ chole = curr->br_startblock == HOLESTARTBLOCK;
+ nhole = new->br_startblock == HOLESTARTBLOCK;
+
+ if (chole)
+ state |= CHOLE;
+ if (!lhole && !chole && xmi_can_merge(left, curr))
+ state |= CLEFT_CONTIG;
+ if (!rhole && !chole && xmi_can_merge(curr, right))
+ state |= CRIGHT_CONTIG;
+ if ((state & CBOTH_CONTIG) == CBOTH_CONTIG &&
+ !xmi_can_merge_all(left, curr, right))
+ state &= ~CRIGHT_CONTIG;
+
+ if (nhole)
+ state |= NHOLE;
+ if (!lhole && !nhole && xmi_can_merge(left, new))
+ state |= NLEFT_CONTIG;
+ if (!rhole && !nhole && xmi_can_merge(new, right))
+ state |= NRIGHT_CONTIG;
+ if ((state & NBOTH_CONTIG) == NBOTH_CONTIG &&
+ !xmi_can_merge_all(left, new, right))
+ state &= ~NRIGHT_CONTIG;
+
+ switch (state & (CLEFT_CONTIG | CRIGHT_CONTIG | CHOLE)) {
+ case CLEFT_CONTIG | CRIGHT_CONTIG:
+ /*
+ * left/curr/right are the same mapping, so deleting curr
+ * causes 2 new mappings to be created.
+ */
+ ret += 2;
+ break;
+ case 0:
+ /*
+ * curr is not contiguous with any mapping, so we remove curr
+ * completely
+ */
+ ret--;
+ break;
+ case CHOLE:
+ /* hole, do nothing */
+ break;
+ case CLEFT_CONTIG:
+ case CRIGHT_CONTIG:
+ /* trim either left or right, no change */
+ break;
+ }
+
+ switch (state & (NLEFT_CONTIG | NRIGHT_CONTIG | NHOLE)) {
+ case NLEFT_CONTIG | NRIGHT_CONTIG:
+ /*
+ * left/curr/right will become the same mapping, so adding
+ * curr causes the deletion of right.
+ */
+ ret--;
+ break;
+ case 0:
+ /* new is not contiguous with any mapping */
+ ret++;
+ break;
+ case NHOLE:
+ /* hole, do nothing. */
+ break;
+ case NLEFT_CONTIG:
+ case NRIGHT_CONTIG:
+ /* new is absorbed into left or right, no change */
+ break;
+ }
+
+ trace_xfs_exchmaps_delta_nextents_step(mp, left, curr, new, right, ret,
+ state);
+ return ret;
+}
+
+/* Make sure we don't overflow the extent (mapping) counters. */
+static inline int
+xmi_ensure_delta_nextents(
+ struct xfs_exchmaps_req *req,
+ struct xfs_inode *ip,
+ int64_t delta)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ int whichfork = xfs_exchmaps_reqfork(req);
+ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
+ uint64_t new_nextents;
+ xfs_extnum_t max_nextents;
+
+ if (delta < 0)
+ return 0;
+
+ /*
+ * It's always an error if the delta causes integer overflow. delta
+ * needs an explicit cast here to avoid warnings about implicit casts
+ * coded into the overflow check.
+ */
+ if (check_add_overflow(ifp->if_nextents, (uint64_t)delta,
+ &new_nextents))
+ return -EFBIG;
+
+ if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_REDUCE_MAX_IEXTENTS) &&
+ new_nextents > 10)
+ return -EFBIG;
+
+ /*
+ * We always promote both inodes to have large extent counts if the
+ * superblock feature is enabled, so we only need to check against the
+ * theoretical maximum.
+ */
+ max_nextents = xfs_iext_max_nextents(xfs_has_large_extent_counts(mp),
+ whichfork);
+ if (new_nextents > max_nextents)
+ return -EFBIG;
+
+ return 0;
+}
+
+/* Find the next mapping after irec. */
+static inline int
+xmi_next(
+ struct xfs_inode *ip,
+ int bmap_flags,
+ const struct xfs_bmbt_irec *irec,
+ struct xfs_bmbt_irec *nrec)
+{
+ xfs_fileoff_t off;
+ xfs_filblks_t blockcount;
+ int nimaps = 1;
+ int error;
+
+ off = irec->br_startoff + irec->br_blockcount;
+ blockcount = XFS_MAX_FILEOFF - off;
+ error = xfs_bmapi_read(ip, off, blockcount, nrec, &nimaps, bmap_flags);
+ if (error)
+ return error;
+ if (nrec->br_startblock == DELAYSTARTBLOCK ||
+ nrec->br_startoff != off) {
+ /*
+ * If we don't get the mapping we want, return a zero-length
+ * mapping, which our estimator function will pretend is a hole.
+ * We shouldn't get delalloc reservations.
+ */
+ nrec->br_startblock = HOLESTARTBLOCK;
+ }
+
+ return 0;
+}
+
+int __init
+xfs_exchmaps_intent_init_cache(void)
+{
+ xfs_exchmaps_intent_cache = kmem_cache_create("xfs_exchmaps_intent",
+ sizeof(struct xfs_exchmaps_intent),
+ 0, 0, NULL);
+
+ return xfs_exchmaps_intent_cache != NULL ? 0 : -ENOMEM;
+}
+
+void
+xfs_exchmaps_intent_destroy_cache(void)
+{
+ kmem_cache_destroy(xfs_exchmaps_intent_cache);
+ xfs_exchmaps_intent_cache = NULL;
+}
+
+/*
+ * Decide if we will exchange the reflink flags between the two files after the
+ * exchange. The only time we want to do this is if we're exchanging all
+ * mappings under EOF and the inode reflink flags have different states.
+ */
+static inline bool
+xmi_can_exchange_reflink_flags(
+ const struct xfs_exchmaps_req *req,
+ unsigned int reflink_state)
+{
+ struct xfs_mount *mp = req->ip1->i_mount;
+
+ if (hweight32(reflink_state) != 1)
+ return false;
+ if (req->startoff1 != 0 || req->startoff2 != 0)
+ return false;
+ if (req->blockcount != XFS_B_TO_FSB(mp, req->ip1->i_disk_size))
+ return false;
+ if (req->blockcount != XFS_B_TO_FSB(mp, req->ip2->i_disk_size))
+ return false;
+ return true;
+}
+
+
+/* Allocate and initialize a new incore intent item from a request. */
+struct xfs_exchmaps_intent *
+xfs_exchmaps_init_intent(
+ const struct xfs_exchmaps_req *req)
+{
+ struct xfs_exchmaps_intent *xmi;
+ unsigned int rs = 0;
+
+ xmi = kmem_cache_zalloc(xfs_exchmaps_intent_cache,
+ GFP_NOFS | __GFP_NOFAIL);
+ INIT_LIST_HEAD(&xmi->xmi_list);
+ xmi->xmi_ip1 = req->ip1;
+ xmi->xmi_ip2 = req->ip2;
+ xmi->xmi_startoff1 = req->startoff1;
+ xmi->xmi_startoff2 = req->startoff2;
+ xmi->xmi_blockcount = req->blockcount;
+ xmi->xmi_isize1 = xmi->xmi_isize2 = -1;
+ xmi->xmi_flags = req->flags & XFS_EXCHMAPS_PARAMS;
+
+ if (xfs_exchmaps_whichfork(xmi) == XFS_ATTR_FORK)
+ return xmi;
+
+ if (req->flags & XFS_EXCHMAPS_SET_SIZES) {
+ xmi->xmi_flags |= XFS_EXCHMAPS_SET_SIZES;
+ xmi->xmi_isize1 = req->ip2->i_disk_size;
+ xmi->xmi_isize2 = req->ip1->i_disk_size;
+ }
+
+ /* Record the state of each inode's reflink flag before the op. */
+ if (xfs_is_reflink_inode(req->ip1))
+ rs |= 1;
+ if (xfs_is_reflink_inode(req->ip2))
+ rs |= 2;
+
+ /*
+ * Figure out if we're clearing the reflink flags (which effectively
+ * exchanges them) after the operation.
+ */
+ if (xmi_can_exchange_reflink_flags(req, rs)) {
+ if (rs & 1)
+ xmi->xmi_flags |= XFS_EXCHMAPS_CLEAR_INO1_REFLINK;
+ if (rs & 2)
+ xmi->xmi_flags |= XFS_EXCHMAPS_CLEAR_INO2_REFLINK;
+ }
+
+ return xmi;
+}
+
+/*
+ * Estimate the number of exchange operations and the number of file blocks
+ * in each file that will be affected by the exchange operation.
+ */
+int
+xfs_exchmaps_estimate(
+ struct xfs_exchmaps_req *req)
+{
+ struct xfs_exchmaps_intent *xmi;
+ struct xfs_bmbt_irec irec1, irec2;
+ struct xfs_exchmaps_adjacent adj = ADJACENT_INIT;
+ xfs_filblks_t ip1_blocks = 0, ip2_blocks = 0;
+ int64_t d_nexts1, d_nexts2;
+ int bmap_flags;
+ int error;
+
+ ASSERT(!(req->flags & ~XFS_EXCHMAPS_PARAMS));
+
+ bmap_flags = xfs_bmapi_aflag(xfs_exchmaps_reqfork(req));
+ xmi = xfs_exchmaps_init_intent(req);
+
+ /*
+ * To guard against the possibility of overflowing the extent counters,
+ * we have to estimate an upper bound on the potential increase in that
+ * counter. We can split the mapping at each end of the range, and for
+ * each step of the exchange we can split the mapping that we're
+ * working on if the mappings do not align.
+ */
+ d_nexts1 = d_nexts2 = 3;
+
+ while (xmi_has_more_exchange_work(xmi)) {
+ /*
+ * Walk through the file ranges until we find something to
+ * exchange. Because we're simulating the exchange, pass in
+ * adj to capture skipped mappings for correct estimation of
+ * bmbt record merges.
+ */
+ error = xfs_exchmaps_find_mappings(xmi, &irec1, &irec2, &adj);
+ if (error)
+ goto out_free;
+ if (!xmi_has_more_exchange_work(xmi))
+ break;
+
+ /* Update accounting. */
+ if (xfs_bmap_is_real_extent(&irec1))
+ ip1_blocks += irec1.br_blockcount;
+ if (xfs_bmap_is_real_extent(&irec2))
+ ip2_blocks += irec2.br_blockcount;
+ req->nr_exchanges++;
+
+ /* Read the next mappings from both files. */
+ error = xmi_next(req->ip1, bmap_flags, &irec1, &adj.right1);
+ if (error)
+ goto out_free;
+
+ error = xmi_next(req->ip2, bmap_flags, &irec2, &adj.right2);
+ if (error)
+ goto out_free;
+
+ /* Update extent count deltas. */
+ d_nexts1 += xmi_delta_nextents_step(req->ip1->i_mount,
+ &adj.left1, &irec1, &irec2, &adj.right1);
+
+ d_nexts2 += xmi_delta_nextents_step(req->ip1->i_mount,
+ &adj.left2, &irec2, &irec1, &adj.right2);
+
+ /* Now pretend we exchanged the mappings. */
+ if (xmi_can_merge(&adj.left2, &irec1))
+ adj.left2.br_blockcount += irec1.br_blockcount;
+ else
+ memcpy(&adj.left2, &irec1, sizeof(irec1));
+
+ if (xmi_can_merge(&adj.left1, &irec2))
+ adj.left1.br_blockcount += irec2.br_blockcount;
+ else
+ memcpy(&adj.left1, &irec2, sizeof(irec2));
+
+ xmi_advance(xmi, &irec1);
+ }
+
+ /* Account for the blocks that are being exchanged. */
+ if (XFS_IS_REALTIME_INODE(req->ip1) &&
+ xfs_exchmaps_reqfork(req) == XFS_DATA_FORK) {
+ req->ip1_rtbcount = ip1_blocks;
+ req->ip2_rtbcount = ip2_blocks;
+ } else {
+ req->ip1_bcount = ip1_blocks;
+ req->ip2_bcount = ip2_blocks;
+ }
+
+ /*
+ * Make sure that both forks have enough slack left in their extent
+ * counters that the exchange operation will not overflow.
+ */
+ trace_xfs_exchmaps_delta_nextents(req, d_nexts1, d_nexts2);
+ if (req->ip1 == req->ip2) {
+ error = xmi_ensure_delta_nextents(req, req->ip1,
+ d_nexts1 + d_nexts2);
+ } else {
+ error = xmi_ensure_delta_nextents(req, req->ip1, d_nexts1);
+ if (error)
+ goto out_free;
+ error = xmi_ensure_delta_nextents(req, req->ip2, d_nexts2);
+ }
+ if (error)
+ goto out_free;
+
+ trace_xfs_exchmaps_initial_estimate(req);
+ error = xfs_exchmaps_estimate_overhead(req);
+out_free:
+ kmem_cache_free(xfs_exchmaps_intent_cache, xmi);
+ return error;
+}
+
+/* Set the reflink flag before an operation. */
+static inline void
+xfs_exchmaps_set_reflink(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip)
+{
+ trace_xfs_reflink_set_inode_flag(ip);
+
+ ip->i_diflags2 |= XFS_DIFLAG2_REFLINK;
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+}
+
+/*
+ * If either file has shared blocks and we're exchanging data forks, we must
+ * flag the other file as having shared blocks so that we get the shared-block
+ * rmap functions if we need to fix up the rmaps.
+ */
+void
+xfs_exchmaps_ensure_reflink(
+ struct xfs_trans *tp,
+ const struct xfs_exchmaps_intent *xmi)
+{
+ unsigned int rs = 0;
+
+ if (xfs_is_reflink_inode(xmi->xmi_ip1))
+ rs |= 1;
+ if (xfs_is_reflink_inode(xmi->xmi_ip2))
+ rs |= 2;
+
+ if ((rs & 1) && !xfs_is_reflink_inode(xmi->xmi_ip2))
+ xfs_exchmaps_set_reflink(tp, xmi->xmi_ip2);
+
+ if ((rs & 2) && !xfs_is_reflink_inode(xmi->xmi_ip1))
+ xfs_exchmaps_set_reflink(tp, xmi->xmi_ip1);
+}
+
+/* Set the large extent count flag before an operation if needed. */
+static inline void
+xfs_exchmaps_ensure_large_extent_counts(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip)
+{
+ if (xfs_inode_has_large_extent_counts(ip))
+ return;
+
+ ip->i_diflags2 |= XFS_DIFLAG2_NREXT64;
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+}
+
+/* Widen the extent counter fields of both inodes if necessary. */
+void
+xfs_exchmaps_upgrade_extent_counts(
+ struct xfs_trans *tp,
+ const struct xfs_exchmaps_intent *xmi)
+{
+ if (!xfs_has_large_extent_counts(tp->t_mountp))
+ return;
+
+ xfs_exchmaps_ensure_large_extent_counts(tp, xmi->xmi_ip1);
+ xfs_exchmaps_ensure_large_extent_counts(tp, xmi->xmi_ip2);
+}
+
+/*
+ * Schedule an exchange a range of mappings from one inode to another.
+ *
+ * The use of file mapping exchange log intent items ensures the operation can
+ * be resumed even if the system goes down. The caller must commit the
+ * transaction to start the work.
+ *
+ * The caller must ensure the inodes must be joined to the transaction and
+ * ILOCKd; they will still be joined to the transaction at exit.
+ */
+void
+xfs_exchange_mappings(
+ struct xfs_trans *tp,
+ const struct xfs_exchmaps_req *req)
+{
+ struct xfs_exchmaps_intent *xmi;
+
+ xfs_assert_ilocked(req->ip1, XFS_ILOCK_EXCL);
+ xfs_assert_ilocked(req->ip2, XFS_ILOCK_EXCL);
+ ASSERT(!(req->flags & ~XFS_EXCHMAPS_LOGGED_FLAGS));
+ if (req->flags & XFS_EXCHMAPS_SET_SIZES)
+ ASSERT(!(req->flags & XFS_EXCHMAPS_ATTR_FORK));
+ ASSERT(xfs_has_exchange_range(tp->t_mountp));
+
+ if (req->blockcount == 0)
+ return;
+
+ xmi = xfs_exchmaps_init_intent(req);
+ xfs_exchmaps_defer_add(tp, xmi);
+ xfs_exchmaps_ensure_reflink(tp, xmi);
+ xfs_exchmaps_upgrade_extent_counts(tp, xmi);
+}
diff --git a/libxfs/xfs_exchmaps.h b/libxfs/xfs_exchmaps.h
new file mode 100644
index 000000000..e8fc3f80c
--- /dev/null
+++ b/libxfs/xfs_exchmaps.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2020-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __XFS_EXCHMAPS_H__
+#define __XFS_EXCHMAPS_H__
+
+/* In-core deferred operation info about a file mapping exchange request. */
+struct xfs_exchmaps_intent {
+ /* List of other incore deferred work. */
+ struct list_head xmi_list;
+
+ /* Inodes participating in the operation. */
+ struct xfs_inode *xmi_ip1;
+ struct xfs_inode *xmi_ip2;
+
+ /* File offset range information. */
+ xfs_fileoff_t xmi_startoff1;
+ xfs_fileoff_t xmi_startoff2;
+ xfs_filblks_t xmi_blockcount;
+
+ /* Set these file sizes after the operation, unless negative. */
+ xfs_fsize_t xmi_isize1;
+ xfs_fsize_t xmi_isize2;
+
+ uint64_t xmi_flags; /* XFS_EXCHMAPS_* flags */
+};
+
+/* flags that can be passed to xfs_exchmaps_{estimate,mappings} */
+#define XFS_EXCHMAPS_PARAMS (XFS_EXCHMAPS_ATTR_FORK | \
+ XFS_EXCHMAPS_SET_SIZES | \
+ XFS_EXCHMAPS_INO1_WRITTEN)
+
+static inline int
+xfs_exchmaps_whichfork(const struct xfs_exchmaps_intent *xmi)
+{
+ if (xmi->xmi_flags & XFS_EXCHMAPS_ATTR_FORK)
+ return XFS_ATTR_FORK;
+ return XFS_DATA_FORK;
+}
+
+/* Parameters for a mapping exchange request. */
+struct xfs_exchmaps_req {
+ /* Inodes participating in the operation. */
+ struct xfs_inode *ip1;
+ struct xfs_inode *ip2;
+
+ /* File offset range information. */
+ xfs_fileoff_t startoff1;
+ xfs_fileoff_t startoff2;
+ xfs_filblks_t blockcount;
+
+ /* XFS_EXCHMAPS_* operation flags */
+ uint64_t flags;
+
+ /*
+ * Fields below this line are filled out by xfs_exchmaps_estimate;
+ * callers should initialize this part of the struct to zero.
+ */
+
+ /*
+ * Data device blocks to be moved out of ip1, and free space needed to
+ * handle the bmbt changes.
+ */
+ xfs_filblks_t ip1_bcount;
+
+ /*
+ * Data device blocks to be moved out of ip2, and free space needed to
+ * handle the bmbt changes.
+ */
+ xfs_filblks_t ip2_bcount;
+
+ /* rt blocks to be moved out of ip1. */
+ xfs_filblks_t ip1_rtbcount;
+
+ /* rt blocks to be moved out of ip2. */
+ xfs_filblks_t ip2_rtbcount;
+
+ /* Free space needed to handle the bmbt changes */
+ unsigned long long resblks;
+
+ /* Number of exchanges needed to complete the operation */
+ unsigned long long nr_exchanges;
+};
+
+static inline int
+xfs_exchmaps_reqfork(const struct xfs_exchmaps_req *req)
+{
+ if (req->flags & XFS_EXCHMAPS_ATTR_FORK)
+ return XFS_ATTR_FORK;
+ return XFS_DATA_FORK;
+}
+
+int xfs_exchmaps_estimate(struct xfs_exchmaps_req *req);
+
+extern struct kmem_cache *xfs_exchmaps_intent_cache;
+
+int __init xfs_exchmaps_intent_init_cache(void);
+void xfs_exchmaps_intent_destroy_cache(void);
+
+struct xfs_exchmaps_intent *xfs_exchmaps_init_intent(
+ const struct xfs_exchmaps_req *req);
+void xfs_exchmaps_ensure_reflink(struct xfs_trans *tp,
+ const struct xfs_exchmaps_intent *xmi);
+void xfs_exchmaps_upgrade_extent_counts(struct xfs_trans *tp,
+ const struct xfs_exchmaps_intent *xmi);
+
+int xfs_exchmaps_finish_one(struct xfs_trans *tp,
+ struct xfs_exchmaps_intent *xmi);
+
+int xfs_exchmaps_check_forks(struct xfs_mount *mp,
+ const struct xfs_exchmaps_req *req);
+
+void xfs_exchange_mappings(struct xfs_trans *tp,
+ const struct xfs_exchmaps_req *req);
+
+#endif /* __XFS_EXCHMAPS_H__ */
diff --git a/libxfs/xfs_log_format.h b/libxfs/xfs_log_format.h
index 09024431c..8dbe1f997 100644
--- a/libxfs/xfs_log_format.h
+++ b/libxfs/xfs_log_format.h
@@ -904,7 +904,29 @@ struct xfs_xmi_log_format {
uint64_t xmi_isize2; /* intended file2 size */
};
-#define XFS_EXCHMAPS_LOGGED_FLAGS (0)
+/* Exchange mappings between extended attribute forks instead of data forks. */
+#define XFS_EXCHMAPS_ATTR_FORK (1ULL << 0)
+
+/* Set the file sizes when finished. */
+#define XFS_EXCHMAPS_SET_SIZES (1ULL << 1)
+
+/*
+ * Exchange the mappings of the two files only if the file allocation units
+ * mapped to file1's range have been written.
+ */
+#define XFS_EXCHMAPS_INO1_WRITTEN (1ULL << 2)
+
+/* Clear the reflink flag from inode1 after the operation. */
+#define XFS_EXCHMAPS_CLEAR_INO1_REFLINK (1ULL << 3)
+
+/* Clear the reflink flag from inode2 after the operation. */
+#define XFS_EXCHMAPS_CLEAR_INO2_REFLINK (1ULL << 4)
+
+#define XFS_EXCHMAPS_LOGGED_FLAGS (XFS_EXCHMAPS_ATTR_FORK | \
+ XFS_EXCHMAPS_SET_SIZES | \
+ XFS_EXCHMAPS_INO1_WRITTEN | \
+ XFS_EXCHMAPS_CLEAR_INO1_REFLINK | \
+ XFS_EXCHMAPS_CLEAR_INO2_REFLINK)
/* This is the structure used to lay out an mapping exchange done log item. */
struct xfs_xmd_log_format {
diff --git a/libxfs/xfs_trans_space.h b/libxfs/xfs_trans_space.h
index 87b31c69a..9640fc232 100644
--- a/libxfs/xfs_trans_space.h
+++ b/libxfs/xfs_trans_space.h
@@ -10,6 +10,10 @@
* Components of space reservations.
*/
+/* Worst case number of bmaps that can be held in a block. */
+#define XFS_MAX_CONTIG_BMAPS_PER_BLOCK(mp) \
+ (((mp)->m_bmap_dmxr[0]) - ((mp)->m_bmap_dmnr[0]))
+
/* Worst case number of rmaps that can be held in a block. */
#define XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp) \
(((mp)->m_rmap_mxr[0]) - ((mp)->m_rmap_mnr[0]))
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 007/115] xfs: add error injection to test file mapping exchange recovery
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (5 preceding siblings ...)
2024-07-30 0:25 ` [PATCH 006/115] xfs: create deferred log items for file mapping exchanges Darrick J. Wong
@ 2024-07-30 0:25 ` Darrick J. Wong
2024-07-30 0:25 ` [PATCH 008/115] xfs: condense extended attributes after a mapping exchange operation Darrick J. Wong
` (107 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:25 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 5fd022ec7d420dfca1eaaf997923a5d4dd0dcf62
Add an errortag so that we can test recovery of exchmaps log items.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
io/inject.c | 1 +
libxfs/xfs_errortag.h | 4 +++-
libxfs/xfs_exchmaps.c | 3 +++
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/io/inject.c b/io/inject.c
index 6ef1fc8d2..4aeb6da32 100644
--- a/io/inject.c
+++ b/io/inject.c
@@ -63,6 +63,7 @@ error_tag(char *name)
{ XFS_ERRTAG_ATTR_LEAF_TO_NODE, "attr_leaf_to_node" },
{ XFS_ERRTAG_WB_DELAY_MS, "wb_delay_ms" },
{ XFS_ERRTAG_WRITE_DELAY_MS, "write_delay_ms" },
+ { XFS_ERRTAG_EXCHMAPS_FINISH_ONE, "exchmaps_finish_one" },
{ XFS_ERRTAG_MAX, NULL }
};
int count;
diff --git a/libxfs/xfs_errortag.h b/libxfs/xfs_errortag.h
index 01a9e86b3..7002d7676 100644
--- a/libxfs/xfs_errortag.h
+++ b/libxfs/xfs_errortag.h
@@ -63,7 +63,8 @@
#define XFS_ERRTAG_ATTR_LEAF_TO_NODE 41
#define XFS_ERRTAG_WB_DELAY_MS 42
#define XFS_ERRTAG_WRITE_DELAY_MS 43
-#define XFS_ERRTAG_MAX 44
+#define XFS_ERRTAG_EXCHMAPS_FINISH_ONE 44
+#define XFS_ERRTAG_MAX 45
/*
* Random factors for above tags, 1 means always, 2 means 1/2 time, etc.
@@ -111,5 +112,6 @@
#define XFS_RANDOM_ATTR_LEAF_TO_NODE 1
#define XFS_RANDOM_WB_DELAY_MS 3000
#define XFS_RANDOM_WRITE_DELAY_MS 3000
+#define XFS_RANDOM_EXCHMAPS_FINISH_ONE 1
#endif /* __XFS_ERRORTAG_H_ */
diff --git a/libxfs/xfs_exchmaps.c b/libxfs/xfs_exchmaps.c
index b2c35032d..ef30d0b27 100644
--- a/libxfs/xfs_exchmaps.c
+++ b/libxfs/xfs_exchmaps.c
@@ -434,6 +434,9 @@ xfs_exchmaps_finish_one(
return error;
}
+ if (XFS_TEST_ERROR(false, tp->t_mountp, XFS_ERRTAG_EXCHMAPS_FINISH_ONE))
+ return -EIO;
+
/* If we still have work to do, ask for a new transaction. */
if (xmi_has_more_exchange_work(xmi) || xmi_has_postop_work(xmi)) {
trace_xfs_exchmaps_defer(tp->t_mountp, xmi);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 008/115] xfs: condense extended attributes after a mapping exchange operation
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (6 preceding siblings ...)
2024-07-30 0:25 ` [PATCH 007/115] xfs: add error injection to test file mapping exchange recovery Darrick J. Wong
@ 2024-07-30 0:25 ` Darrick J. Wong
2024-07-30 0:26 ` [PATCH 009/115] xfs: condense directories " Darrick J. Wong
` (106 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:25 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 497d7a2608f8b7329e92bdaaf745ca127a582ad9
Add a new file mapping exchange flag that enables us to perform
post-exchange processing on file2 once we're done exchanging the extent
mappings. If we were swapping mappings between extended attribute
forks, we want to be able to convert file2's attr fork from block to
inline format.
(This implies that all fork contents are exchanged.)
This isn't used anywhere right now, but we need to have the basic ondisk
flags in place so that a future online xattr repair feature can create
salvaged attrs in a temporary file and exchange the attr fork mappings
when ready. If one file is in extents format and the other is inline,
we will have to promote both to extents format to perform the exchange.
After the exchange, we can try to condense the fixed file's attr fork
back down to inline format if possible.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_exchmaps.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++--
libxfs/xfs_exchmaps.h | 5 +++++
2 files changed, 56 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_exchmaps.c b/libxfs/xfs_exchmaps.c
index ef30d0b27..afe2c2d22 100644
--- a/libxfs/xfs_exchmaps.c
+++ b/libxfs/xfs_exchmaps.c
@@ -21,6 +21,10 @@
#include "xfs_errortag.h"
#include "xfs_health.h"
#include "defer_item.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr_leaf.h"
+#include "xfs_attr.h"
struct kmem_cache *xfs_exchmaps_intent_cache;
@@ -118,7 +122,8 @@ static inline bool
xmi_has_postop_work(const struct xfs_exchmaps_intent *xmi)
{
return xmi->xmi_flags & (XFS_EXCHMAPS_CLEAR_INO1_REFLINK |
- XFS_EXCHMAPS_CLEAR_INO2_REFLINK);
+ XFS_EXCHMAPS_CLEAR_INO2_REFLINK |
+ __XFS_EXCHMAPS_INO2_SHORTFORM);
}
/* Check all mappings to make sure we can actually exchange them. */
@@ -357,6 +362,36 @@ xfs_exchmaps_one_step(
xmi_advance(xmi, irec1);
}
+/* Convert inode2's leaf attr fork back to shortform, if possible.. */
+STATIC int
+xfs_exchmaps_attr_to_sf(
+ struct xfs_trans *tp,
+ struct xfs_exchmaps_intent *xmi)
+{
+ struct xfs_da_args args = {
+ .dp = xmi->xmi_ip2,
+ .geo = tp->t_mountp->m_attr_geo,
+ .whichfork = XFS_ATTR_FORK,
+ .trans = tp,
+ };
+ struct xfs_buf *bp;
+ int forkoff;
+ int error;
+
+ if (!xfs_attr_is_leaf(xmi->xmi_ip2))
+ return 0;
+
+ error = xfs_attr3_leaf_read(tp, xmi->xmi_ip2, 0, &bp);
+ if (error)
+ return error;
+
+ forkoff = xfs_attr_shortform_allfit(bp, xmi->xmi_ip2);
+ if (forkoff == 0)
+ return 0;
+
+ return xfs_attr3_leaf_to_shortform(bp, &args, forkoff);
+}
+
/* Clear the reflink flag after an exchange. */
static inline void
xfs_exchmaps_clear_reflink(
@@ -375,6 +410,16 @@ xfs_exchmaps_do_postop_work(
struct xfs_trans *tp,
struct xfs_exchmaps_intent *xmi)
{
+ if (xmi->xmi_flags & __XFS_EXCHMAPS_INO2_SHORTFORM) {
+ int error = 0;
+
+ if (xmi->xmi_flags & XFS_EXCHMAPS_ATTR_FORK)
+ error = xfs_exchmaps_attr_to_sf(tp, xmi);
+ xmi->xmi_flags &= ~__XFS_EXCHMAPS_INO2_SHORTFORM;
+ if (error)
+ return error;
+ }
+
if (xmi->xmi_flags & XFS_EXCHMAPS_CLEAR_INO1_REFLINK) {
xfs_exchmaps_clear_reflink(tp, xmi->xmi_ip1);
xmi->xmi_flags &= ~XFS_EXCHMAPS_CLEAR_INO1_REFLINK;
@@ -806,8 +851,10 @@ xfs_exchmaps_init_intent(
xmi->xmi_isize1 = xmi->xmi_isize2 = -1;
xmi->xmi_flags = req->flags & XFS_EXCHMAPS_PARAMS;
- if (xfs_exchmaps_whichfork(xmi) == XFS_ATTR_FORK)
+ if (xfs_exchmaps_whichfork(xmi) == XFS_ATTR_FORK) {
+ xmi->xmi_flags |= __XFS_EXCHMAPS_INO2_SHORTFORM;
return xmi;
+ }
if (req->flags & XFS_EXCHMAPS_SET_SIZES) {
xmi->xmi_flags |= XFS_EXCHMAPS_SET_SIZES;
@@ -1028,6 +1075,8 @@ xfs_exchange_mappings(
{
struct xfs_exchmaps_intent *xmi;
+ BUILD_BUG_ON(XFS_EXCHMAPS_INTERNAL_FLAGS & XFS_EXCHMAPS_LOGGED_FLAGS);
+
xfs_assert_ilocked(req->ip1, XFS_ILOCK_EXCL);
xfs_assert_ilocked(req->ip2, XFS_ILOCK_EXCL);
ASSERT(!(req->flags & ~XFS_EXCHMAPS_LOGGED_FLAGS));
diff --git a/libxfs/xfs_exchmaps.h b/libxfs/xfs_exchmaps.h
index e8fc3f80c..d8718fca6 100644
--- a/libxfs/xfs_exchmaps.h
+++ b/libxfs/xfs_exchmaps.h
@@ -27,6 +27,11 @@ struct xfs_exchmaps_intent {
uint64_t xmi_flags; /* XFS_EXCHMAPS_* flags */
};
+/* Try to convert inode2 from block to short format at the end, if possible. */
+#define __XFS_EXCHMAPS_INO2_SHORTFORM (1ULL << 63)
+
+#define XFS_EXCHMAPS_INTERNAL_FLAGS (__XFS_EXCHMAPS_INO2_SHORTFORM)
+
/* flags that can be passed to xfs_exchmaps_{estimate,mappings} */
#define XFS_EXCHMAPS_PARAMS (XFS_EXCHMAPS_ATTR_FORK | \
XFS_EXCHMAPS_SET_SIZES | \
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 009/115] xfs: condense directories after a mapping exchange operation
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (7 preceding siblings ...)
2024-07-30 0:25 ` [PATCH 008/115] xfs: condense extended attributes after a mapping exchange operation Darrick J. Wong
@ 2024-07-30 0:26 ` Darrick J. Wong
2024-07-30 0:26 ` [PATCH 010/115] xfs: condense symbolic links " Darrick J. Wong
` (105 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:26 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: da165fbde23b84591b6ccdf6749277d2d767b770
The previous commit added a new file mapping exchange flag that enables
us to perform post-swap processing on file2 once we're done exchanging
extent mappings. Now add this ability for directories.
This isn't used anywhere right now, but we need to have the basic ondisk
flags in place so that a future online directory repair feature can
create salvaged dirents in a temporary directory and exchange the data
fork mappings when ready. If one file is in extents format and the
other is inline, we will have to promote both to extents format to
perform the exchange. After the exchange, we can try to condense the
fixed directory down to inline format if possible.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_exchmaps.c | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/libxfs/xfs_exchmaps.c b/libxfs/xfs_exchmaps.c
index afe2c2d22..6e758cac1 100644
--- a/libxfs/xfs_exchmaps.c
+++ b/libxfs/xfs_exchmaps.c
@@ -25,6 +25,8 @@
#include "xfs_da_btree.h"
#include "xfs_attr_leaf.h"
#include "xfs_attr.h"
+#include "xfs_dir2_priv.h"
+#include "xfs_dir2.h"
struct kmem_cache *xfs_exchmaps_intent_cache;
@@ -392,6 +394,42 @@ xfs_exchmaps_attr_to_sf(
return xfs_attr3_leaf_to_shortform(bp, &args, forkoff);
}
+/* Convert inode2's block dir fork back to shortform, if possible.. */
+STATIC int
+xfs_exchmaps_dir_to_sf(
+ struct xfs_trans *tp,
+ struct xfs_exchmaps_intent *xmi)
+{
+ struct xfs_da_args args = {
+ .dp = xmi->xmi_ip2,
+ .geo = tp->t_mountp->m_dir_geo,
+ .whichfork = XFS_DATA_FORK,
+ .trans = tp,
+ };
+ struct xfs_dir2_sf_hdr sfh;
+ struct xfs_buf *bp;
+ bool isblock;
+ int size;
+ int error;
+
+ error = xfs_dir2_isblock(&args, &isblock);
+ if (error)
+ return error;
+
+ if (!isblock)
+ return 0;
+
+ error = xfs_dir3_block_read(tp, xmi->xmi_ip2, &bp);
+ if (error)
+ return error;
+
+ size = xfs_dir2_block_sfsize(xmi->xmi_ip2, bp->b_addr, &sfh);
+ if (size > xfs_inode_data_fork_size(xmi->xmi_ip2))
+ return 0;
+
+ return xfs_dir2_block_to_sf(&args, bp, size, &sfh);
+}
+
/* Clear the reflink flag after an exchange. */
static inline void
xfs_exchmaps_clear_reflink(
@@ -415,6 +453,8 @@ xfs_exchmaps_do_postop_work(
if (xmi->xmi_flags & XFS_EXCHMAPS_ATTR_FORK)
error = xfs_exchmaps_attr_to_sf(tp, xmi);
+ else if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode))
+ error = xfs_exchmaps_dir_to_sf(tp, xmi);
xmi->xmi_flags &= ~__XFS_EXCHMAPS_INO2_SHORTFORM;
if (error)
return error;
@@ -879,6 +919,9 @@ xfs_exchmaps_init_intent(
xmi->xmi_flags |= XFS_EXCHMAPS_CLEAR_INO2_REFLINK;
}
+ if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode))
+ xmi->xmi_flags |= __XFS_EXCHMAPS_INO2_SHORTFORM;
+
return xmi;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 010/115] xfs: condense symbolic links after a mapping exchange operation
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (8 preceding siblings ...)
2024-07-30 0:26 ` [PATCH 009/115] xfs: condense directories " Darrick J. Wong
@ 2024-07-30 0:26 ` Darrick J. Wong
2024-07-30 0:26 ` [PATCH 011/115] xfs: make file range exchange support realtime files Darrick J. Wong
` (104 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:26 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 33a9be2b7016e79f47c4c1b523a0aa59d41893c0
The previous commit added a new file mapping exchange flag that enables
us to perform post-exchange processing on file2 once we're done
exchanging the extent mappings. Now add this ability for symlinks.
This isn't used anywhere right now, but we need to have the basic ondisk
flags in place so that a future online symlink repair feature can
salvage the remote target in a temporary link and exchange the data fork
mappings when ready. If one file is in extents format and the other is
inline, we will have to promote both to extents format to perform the
exchange. After the exchange, we can try to condense the fixed symlink
down to inline format if possible.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_exchmaps.c | 49 ++++++++++++++++++++++++++++++++++++++++++-
libxfs/xfs_symlink_remote.c | 47 +++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_symlink_remote.h | 1 +
3 files changed, 96 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_exchmaps.c b/libxfs/xfs_exchmaps.c
index 6e758cac1..34ac9d5f2 100644
--- a/libxfs/xfs_exchmaps.c
+++ b/libxfs/xfs_exchmaps.c
@@ -27,6 +27,7 @@
#include "xfs_attr.h"
#include "xfs_dir2_priv.h"
#include "xfs_dir2.h"
+#include "xfs_symlink_remote.h"
struct kmem_cache *xfs_exchmaps_intent_cache;
@@ -430,6 +431,49 @@ xfs_exchmaps_dir_to_sf(
return xfs_dir2_block_to_sf(&args, bp, size, &sfh);
}
+/* Convert inode2's remote symlink target back to shortform, if possible. */
+STATIC int
+xfs_exchmaps_link_to_sf(
+ struct xfs_trans *tp,
+ struct xfs_exchmaps_intent *xmi)
+{
+ struct xfs_inode *ip = xmi->xmi_ip2;
+ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
+ char *buf;
+ int error;
+
+ if (ifp->if_format == XFS_DINODE_FMT_LOCAL ||
+ ip->i_disk_size > xfs_inode_data_fork_size(ip))
+ return 0;
+
+ /* Read the current symlink target into a buffer. */
+ buf = kmalloc(ip->i_disk_size + 1,
+ GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_NOFAIL);
+ if (!buf) {
+ ASSERT(0);
+ return -ENOMEM;
+ }
+
+ error = xfs_symlink_remote_read(ip, buf);
+ if (error)
+ goto free;
+
+ /* Remove the blocks. */
+ error = xfs_symlink_remote_truncate(tp, ip);
+ if (error)
+ goto free;
+
+ /* Convert fork to local format and log our changes. */
+ xfs_idestroy_fork(ifp);
+ ifp->if_bytes = 0;
+ ifp->if_format = XFS_DINODE_FMT_LOCAL;
+ xfs_init_local_fork(ip, XFS_DATA_FORK, buf, ip->i_disk_size);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE);
+free:
+ kfree(buf);
+ return error;
+}
+
/* Clear the reflink flag after an exchange. */
static inline void
xfs_exchmaps_clear_reflink(
@@ -455,6 +499,8 @@ xfs_exchmaps_do_postop_work(
error = xfs_exchmaps_attr_to_sf(tp, xmi);
else if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode))
error = xfs_exchmaps_dir_to_sf(tp, xmi);
+ else if (S_ISLNK(VFS_I(xmi->xmi_ip2)->i_mode))
+ error = xfs_exchmaps_link_to_sf(tp, xmi);
xmi->xmi_flags &= ~__XFS_EXCHMAPS_INO2_SHORTFORM;
if (error)
return error;
@@ -919,7 +965,8 @@ xfs_exchmaps_init_intent(
xmi->xmi_flags |= XFS_EXCHMAPS_CLEAR_INO2_REFLINK;
}
- if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode))
+ if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode) ||
+ S_ISLNK(VFS_I(xmi->xmi_ip2)->i_mode))
xmi->xmi_flags |= __XFS_EXCHMAPS_INO2_SHORTFORM;
return xmi;
diff --git a/libxfs/xfs_symlink_remote.c b/libxfs/xfs_symlink_remote.c
index 875e03bcb..72f175990 100644
--- a/libxfs/xfs_symlink_remote.c
+++ b/libxfs/xfs_symlink_remote.c
@@ -377,3 +377,50 @@ xfs_symlink_write_target(
ASSERT(pathlen == 0);
return 0;
}
+
+/* Remove all the blocks from a symlink and invalidate buffers. */
+int
+xfs_symlink_remote_truncate(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip)
+{
+ struct xfs_bmbt_irec mval[XFS_SYMLINK_MAPS];
+ struct xfs_mount *mp = tp->t_mountp;
+ struct xfs_buf *bp;
+ int nmaps = XFS_SYMLINK_MAPS;
+ int done = 0;
+ int i;
+ int error;
+
+ /* Read mappings and invalidate buffers. */
+ error = xfs_bmapi_read(ip, 0, XFS_MAX_FILEOFF, mval, &nmaps, 0);
+ if (error)
+ return error;
+
+ for (i = 0; i < nmaps; i++) {
+ if (!xfs_bmap_is_real_extent(&mval[i]))
+ break;
+
+ error = xfs_trans_get_buf(tp, mp->m_ddev_targp,
+ XFS_FSB_TO_DADDR(mp, mval[i].br_startblock),
+ XFS_FSB_TO_BB(mp, mval[i].br_blockcount), 0,
+ &bp);
+ if (error)
+ return error;
+
+ xfs_trans_binval(tp, bp);
+ }
+
+ /* Unmap the remote blocks. */
+ error = xfs_bunmapi(tp, ip, 0, XFS_MAX_FILEOFF, 0, nmaps, &done);
+ if (error)
+ return error;
+ if (!done) {
+ ASSERT(done);
+ xfs_inode_mark_sick(ip, XFS_SICK_INO_SYMLINK);
+ return -EFSCORRUPTED;
+ }
+
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ return 0;
+}
diff --git a/libxfs/xfs_symlink_remote.h b/libxfs/xfs_symlink_remote.h
index a63bd38ae..ac3dac8f6 100644
--- a/libxfs/xfs_symlink_remote.h
+++ b/libxfs/xfs_symlink_remote.h
@@ -22,5 +22,6 @@ int xfs_symlink_remote_read(struct xfs_inode *ip, char *link);
int xfs_symlink_write_target(struct xfs_trans *tp, struct xfs_inode *ip,
const char *target_path, int pathlen, xfs_fsblock_t fs_blocks,
uint resblks);
+int xfs_symlink_remote_truncate(struct xfs_trans *tp, struct xfs_inode *ip);
#endif /* __XFS_SYMLINK_REMOTE_H */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 011/115] xfs: make file range exchange support realtime files
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (9 preceding siblings ...)
2024-07-30 0:26 ` [PATCH 010/115] xfs: condense symbolic links " Darrick J. Wong
@ 2024-07-30 0:26 ` Darrick J. Wong
2024-07-30 0:26 ` [PATCH 012/115] xfs: capture inode generation numbers in the ondisk exchmaps log item Darrick J. Wong
` (103 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:26 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: e62941103faa2eedba6a155316e059a490c743a6
Now that bmap items support the realtime device, we can add the
necessary pieces to the file range exchange code to support exchanging
mappings. All we really need to do here is adjust the blockcount
upwards to the end of the rt extent and remove the inode checks.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
include/xfs_inode.h | 10 +++++++
libxfs/xfs_exchmaps.c | 70 ++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 70 insertions(+), 10 deletions(-)
diff --git a/include/xfs_inode.h b/include/xfs_inode.h
index a351bb0d9..825708383 100644
--- a/include/xfs_inode.h
+++ b/include/xfs_inode.h
@@ -325,6 +325,16 @@ static inline bool xfs_inode_has_large_extent_counts(struct xfs_inode *ip)
return ip->i_diflags2 & XFS_DIFLAG2_NREXT64;
}
+
+/*
+ * Decide if this file is a realtime file whose data allocation unit is larger
+ * than a single filesystem block.
+ */
+static inline bool xfs_inode_has_bigrtalloc(struct xfs_inode *ip)
+{
+ return XFS_IS_REALTIME_INODE(ip) && ip->i_mount->m_sb.sb_rextsize > 1;
+}
+
/* Always set the child's GID to this value, even if the parent is setgid. */
#define CRED_FORCE_GID (1U << 0)
struct cred {
diff --git a/libxfs/xfs_exchmaps.c b/libxfs/xfs_exchmaps.c
index 34ac9d5f2..37e58d088 100644
--- a/libxfs/xfs_exchmaps.c
+++ b/libxfs/xfs_exchmaps.c
@@ -149,12 +149,7 @@ xfs_exchmaps_check_forks(
ifp2->if_format == XFS_DINODE_FMT_LOCAL)
return -EINVAL;
- /* We don't support realtime data forks yet. */
- if (!XFS_IS_REALTIME_INODE(req->ip1))
- return 0;
- if (whichfork == XFS_ATTR_FORK)
- return 0;
- return -EINVAL;
+ return 0;
}
#ifdef CONFIG_XFS_QUOTA
@@ -195,6 +190,8 @@ xfs_exchmaps_can_skip_mapping(
struct xfs_exchmaps_intent *xmi,
struct xfs_bmbt_irec *irec)
{
+ struct xfs_mount *mp = xmi->xmi_ip1->i_mount;
+
/* Do not skip this mapping if the caller did not tell us to. */
if (!(xmi->xmi_flags & XFS_EXCHMAPS_INO1_WRITTEN))
return false;
@@ -206,11 +203,64 @@ xfs_exchmaps_can_skip_mapping(
/*
* The mapping is unwritten or a hole. It cannot be a delalloc
* reservation because we already excluded those. It cannot be an
- * unwritten mapping with dirty page cache because we flushed the page
- * cache. We don't support realtime files yet, so we needn't (yet)
- * deal with them.
+ * unwritten extent with dirty page cache because we flushed the page
+ * cache. For files where the allocation unit is 1FSB (files on the
+ * data dev, rt files if the extent size is 1FSB), we can safely
+ * skip this mapping.
*/
- return true;
+ if (!xfs_inode_has_bigrtalloc(xmi->xmi_ip1))
+ return true;
+
+ /*
+ * For a realtime file with a multi-fsb allocation unit, the decision
+ * is trickier because we can only swap full allocation units.
+ * Unwritten mappings can appear in the middle of an rtx if the rtx is
+ * partially written, but they can also appear for preallocations.
+ *
+ * If the mapping is a hole, skip it entirely. Holes should align with
+ * rtx boundaries.
+ */
+ if (!xfs_bmap_is_real_extent(irec))
+ return true;
+
+ /*
+ * All mappings below this point are unwritten.
+ *
+ * - If the beginning is not aligned to an rtx, trim the end of the
+ * mapping so that it does not cross an rtx boundary, and swap it.
+ *
+ * - If both ends are aligned to an rtx, skip the entire mapping.
+ */
+ if (!isaligned_64(irec->br_startoff, mp->m_sb.sb_rextsize)) {
+ xfs_fileoff_t new_end;
+
+ new_end = roundup_64(irec->br_startoff, mp->m_sb.sb_rextsize);
+ irec->br_blockcount = min(irec->br_blockcount,
+ new_end - irec->br_startoff);
+ return false;
+ }
+ if (isaligned_64(irec->br_blockcount, mp->m_sb.sb_rextsize))
+ return true;
+
+ /*
+ * All mappings below this point are unwritten, start on an rtx
+ * boundary, and do not end on an rtx boundary.
+ *
+ * - If the mapping is longer than one rtx, trim the end of the mapping
+ * down to an rtx boundary and skip it.
+ *
+ * - The mapping is shorter than one rtx. Swap it.
+ */
+ if (irec->br_blockcount > mp->m_sb.sb_rextsize) {
+ xfs_fileoff_t new_end;
+
+ new_end = rounddown_64(irec->br_startoff + irec->br_blockcount,
+ mp->m_sb.sb_rextsize);
+ irec->br_blockcount = new_end - irec->br_startoff;
+ return true;
+ }
+
+ return false;
}
/*
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 012/115] xfs: capture inode generation numbers in the ondisk exchmaps log item
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (10 preceding siblings ...)
2024-07-30 0:26 ` [PATCH 011/115] xfs: make file range exchange support realtime files Darrick J. Wong
@ 2024-07-30 0:26 ` Darrick J. Wong
2024-07-30 0:27 ` [PATCH 013/115] xfs: enable logged file mapping exchange feature Darrick J. Wong
` (102 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:26 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 14f19991020b3c712d626727c17599f134cc6efa
Per some very late review comments, capture the generation numbers of
both inodes involved in a file content exchange operation so that we
don't accidentally target files with have been reallocated.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_log_format.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/libxfs/xfs_log_format.h b/libxfs/xfs_log_format.h
index 8dbe1f997..accba2acd 100644
--- a/libxfs/xfs_log_format.h
+++ b/libxfs/xfs_log_format.h
@@ -896,6 +896,8 @@ struct xfs_xmi_log_format {
uint64_t xmi_inode1; /* inumber of first file */
uint64_t xmi_inode2; /* inumber of second file */
+ uint32_t xmi_igen1; /* generation of first file */
+ uint32_t xmi_igen2; /* generation of second file */
uint64_t xmi_startoff1; /* block offset into file1 */
uint64_t xmi_startoff2; /* block offset into file2 */
uint64_t xmi_blockcount; /* number of blocks */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 013/115] xfs: enable logged file mapping exchange feature
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (11 preceding siblings ...)
2024-07-30 0:26 ` [PATCH 012/115] xfs: capture inode generation numbers in the ondisk exchmaps log item Darrick J. Wong
@ 2024-07-30 0:27 ` Darrick J. Wong
2024-07-30 0:27 ` [PATCH 014/115] xfs: add an explicit owner field to xfs_da_args Darrick J. Wong
` (101 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:27 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 0730e8d8ba1d1507f1d7fd719e1f835ce69961fe
Add the XFS_SB_FEAT_INCOMPAT_EXCHRANGE feature to the set of features
that we will permit when mounting a filesystem. This turns on support
for the file range exchange feature.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_format.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h
index ff1e28316..10153ce11 100644
--- a/libxfs/xfs_format.h
+++ b/libxfs/xfs_format.h
@@ -380,7 +380,8 @@ xfs_sb_has_ro_compat_feature(
XFS_SB_FEAT_INCOMPAT_META_UUID | \
XFS_SB_FEAT_INCOMPAT_BIGTIME | \
XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \
- XFS_SB_FEAT_INCOMPAT_NREXT64)
+ XFS_SB_FEAT_INCOMPAT_NREXT64 | \
+ XFS_SB_FEAT_INCOMPAT_EXCHRANGE)
#define XFS_SB_FEAT_INCOMPAT_UNKNOWN ~XFS_SB_FEAT_INCOMPAT_ALL
static inline bool
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 014/115] xfs: add an explicit owner field to xfs_da_args
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (12 preceding siblings ...)
2024-07-30 0:27 ` [PATCH 013/115] xfs: enable logged file mapping exchange feature Darrick J. Wong
@ 2024-07-30 0:27 ` Darrick J. Wong
2024-07-30 0:27 ` [PATCH 015/115] xfs: use the xfs_da_args owner field to set new dir/attr block owner Darrick J. Wong
` (100 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:27 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 9eef772f3a194f6841850e45dacdf4207ec7da84
Add an explicit owner field to xfs_da_args, which will make it easier
for online fsck to set the owner field of the temporary directory and
xattr structures that it builds to repair damaged metadata.
Note: I hopefully found all the xfs_da_args definitions by looking for
automatic stack variable declarations and xfs_da_args.dp assignments:
git grep -E '(args.*dp =|struct xfs_da_args[[:space:]]*[a-z0-9][a-z0-9]*)'
Note that callers of xfs_attr_{get,set,change} can set the owner to zero
(or leave it unset) to have the default set to args->dp.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 4 ++++
libxfs/xfs_attr_leaf.c | 2 ++
libxfs/xfs_bmap.c | 1 +
libxfs/xfs_da_btree.h | 1 +
libxfs/xfs_dir2.c | 5 +++++
libxfs/xfs_exchmaps.c | 2 ++
6 files changed, 15 insertions(+)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index caf04daa7..21b5d922b 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -262,6 +262,8 @@ xfs_attr_get(
if (xfs_is_shutdown(args->dp->i_mount))
return -EIO;
+ if (!args->owner)
+ args->owner = args->dp->i_ino;
args->geo = args->dp->i_mount->m_attr_geo;
args->whichfork = XFS_ATTR_FORK;
args->hashval = xfs_da_hashname(args->name, args->namelen);
@@ -935,6 +937,8 @@ xfs_attr_set(
if (error)
return error;
+ if (!args->owner)
+ args->owner = args->dp->i_ino;
args->geo = mp->m_attr_geo;
args->whichfork = XFS_ATTR_FORK;
args->hashval = xfs_da_hashname(args->name, args->namelen);
diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c
index a44312cdc..393e5e6ca 100644
--- a/libxfs/xfs_attr_leaf.c
+++ b/libxfs/xfs_attr_leaf.c
@@ -901,6 +901,7 @@ xfs_attr_shortform_to_leaf(
nargs.whichfork = XFS_ATTR_FORK;
nargs.trans = args->trans;
nargs.op_flags = XFS_DA_OP_OKNOENT;
+ nargs.owner = args->owner;
sfe = xfs_attr_sf_firstentry(sf);
for (i = 0; i < sf->count; i++) {
@@ -1103,6 +1104,7 @@ xfs_attr3_leaf_to_shortform(
nargs.whichfork = XFS_ATTR_FORK;
nargs.trans = args->trans;
nargs.op_flags = XFS_DA_OP_OKNOENT;
+ nargs.owner = args->owner;
for (i = 0; i < ichdr.count; entry++, i++) {
if (entry->flags & XFS_ATTR_INCOMPLETE)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index b089f53e0..868334229 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -970,6 +970,7 @@ xfs_bmap_add_attrfork_local(
dargs.total = dargs.geo->fsbcount;
dargs.whichfork = XFS_DATA_FORK;
dargs.trans = tp;
+ dargs.owner = ip->i_ino;
return xfs_dir2_sf_to_block(&dargs);
}
diff --git a/libxfs/xfs_da_btree.h b/libxfs/xfs_da_btree.h
index 706baf36e..7fb13f26e 100644
--- a/libxfs/xfs_da_btree.h
+++ b/libxfs/xfs_da_btree.h
@@ -79,6 +79,7 @@ typedef struct xfs_da_args {
int rmtvaluelen2; /* remote attr value length in bytes */
uint32_t op_flags; /* operation flags */
enum xfs_dacmp cmpresult; /* name compare result for lookups */
+ xfs_ino_t owner; /* inode that owns the dir/attr data */
} xfs_da_args_t;
/*
diff --git a/libxfs/xfs_dir2.c b/libxfs/xfs_dir2.c
index 530c3e22a..803fb82b2 100644
--- a/libxfs/xfs_dir2.c
+++ b/libxfs/xfs_dir2.c
@@ -249,6 +249,7 @@ xfs_dir_init(
args->geo = dp->i_mount->m_dir_geo;
args->dp = dp;
args->trans = tp;
+ args->owner = dp->i_ino;
error = xfs_dir2_sf_create(args, pdp->i_ino);
kfree(args);
return error;
@@ -294,6 +295,7 @@ xfs_dir_createname(
args->whichfork = XFS_DATA_FORK;
args->trans = tp;
args->op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
+ args->owner = dp->i_ino;
if (!inum)
args->op_flags |= XFS_DA_OP_JUSTCHECK;
@@ -382,6 +384,7 @@ xfs_dir_lookup(
args->whichfork = XFS_DATA_FORK;
args->trans = tp;
args->op_flags = XFS_DA_OP_OKNOENT;
+ args->owner = dp->i_ino;
if (ci_name)
args->op_flags |= XFS_DA_OP_CILOOKUP;
@@ -455,6 +458,7 @@ xfs_dir_removename(
args->total = total;
args->whichfork = XFS_DATA_FORK;
args->trans = tp;
+ args->owner = dp->i_ino;
if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
rval = xfs_dir2_sf_removename(args);
@@ -516,6 +520,7 @@ xfs_dir_replace(
args->total = total;
args->whichfork = XFS_DATA_FORK;
args->trans = tp;
+ args->owner = dp->i_ino;
if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
rval = xfs_dir2_sf_replace(args);
diff --git a/libxfs/xfs_exchmaps.c b/libxfs/xfs_exchmaps.c
index 37e58d088..6160beef1 100644
--- a/libxfs/xfs_exchmaps.c
+++ b/libxfs/xfs_exchmaps.c
@@ -426,6 +426,7 @@ xfs_exchmaps_attr_to_sf(
.geo = tp->t_mountp->m_attr_geo,
.whichfork = XFS_ATTR_FORK,
.trans = tp,
+ .owner = xmi->xmi_ip2->i_ino,
};
struct xfs_buf *bp;
int forkoff;
@@ -456,6 +457,7 @@ xfs_exchmaps_dir_to_sf(
.geo = tp->t_mountp->m_dir_geo,
.whichfork = XFS_DATA_FORK,
.trans = tp,
+ .owner = xmi->xmi_ip2->i_ino,
};
struct xfs_dir2_sf_hdr sfh;
struct xfs_buf *bp;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 015/115] xfs: use the xfs_da_args owner field to set new dir/attr block owner
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (13 preceding siblings ...)
2024-07-30 0:27 ` [PATCH 014/115] xfs: add an explicit owner field to xfs_da_args Darrick J. Wong
@ 2024-07-30 0:27 ` Darrick J. Wong
2024-07-30 0:27 ` [PATCH 016/115] xfs: validate attr leaf buffer owners Darrick J. Wong
` (99 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:27 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 17a85dc64ae0804d33a2293686fc987a830a462d
When we're creating leaf, data, freespace, or dabtree blocks for
directories and xattrs, use the explicit owner field (instead of the
xfs_inode) to set the owner field. This will enable online repair to
construct replacement data structures in a temporary file without having
to change the owner fields prior to swapping the new and old structures.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr_leaf.c | 2 +-
libxfs/xfs_attr_remote.c | 4 ++--
libxfs/xfs_da_btree.c | 2 +-
libxfs/xfs_dir2_block.c | 19 ++++++++++---------
libxfs/xfs_dir2_data.c | 2 +-
libxfs/xfs_dir2_leaf.c | 11 +++++------
libxfs/xfs_dir2_node.c | 2 +-
7 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c
index 393e5e6ca..ed3b63f8c 100644
--- a/libxfs/xfs_attr_leaf.c
+++ b/libxfs/xfs_attr_leaf.c
@@ -1236,7 +1236,7 @@ xfs_attr3_leaf_create(
ichdr.magic = XFS_ATTR3_LEAF_MAGIC;
hdr3->blkno = cpu_to_be64(xfs_buf_daddr(bp));
- hdr3->owner = cpu_to_be64(dp->i_ino);
+ hdr3->owner = cpu_to_be64(args->owner);
uuid_copy(&hdr3->uuid, &mp->m_sb.sb_meta_uuid);
ichdr.freemap[0].base = sizeof(struct xfs_attr3_leaf_hdr);
diff --git a/libxfs/xfs_attr_remote.c b/libxfs/xfs_attr_remote.c
index 855d090c9..2787d3fa3 100644
--- a/libxfs/xfs_attr_remote.c
+++ b/libxfs/xfs_attr_remote.c
@@ -521,8 +521,8 @@ xfs_attr_rmtval_set_value(
return error;
bp->b_ops = &xfs_attr3_rmt_buf_ops;
- xfs_attr_rmtval_copyin(mp, bp, args->dp->i_ino, &offset,
- &valuelen, &src);
+ xfs_attr_rmtval_copyin(mp, bp, args->owner, &offset, &valuelen,
+ &src);
error = xfs_bwrite(bp); /* GROT: NOTE: synchronous write */
xfs_buf_relse(bp);
diff --git a/libxfs/xfs_da_btree.c b/libxfs/xfs_da_btree.c
index 8ace7622a..3ad58ab04 100644
--- a/libxfs/xfs_da_btree.c
+++ b/libxfs/xfs_da_btree.c
@@ -482,7 +482,7 @@ xfs_da3_node_create(
memset(hdr3, 0, sizeof(struct xfs_da3_node_hdr));
ichdr.magic = XFS_DA3_NODE_MAGIC;
hdr3->info.blkno = cpu_to_be64(xfs_buf_daddr(bp));
- hdr3->info.owner = cpu_to_be64(args->dp->i_ino);
+ hdr3->info.owner = cpu_to_be64(args->owner);
uuid_copy(&hdr3->info.uuid, &mp->m_sb.sb_meta_uuid);
} else {
ichdr.magic = XFS_DA_NODE_MAGIC;
diff --git a/libxfs/xfs_dir2_block.c b/libxfs/xfs_dir2_block.c
index 9d87735e7..c91559e59 100644
--- a/libxfs/xfs_dir2_block.c
+++ b/libxfs/xfs_dir2_block.c
@@ -160,12 +160,13 @@ xfs_dir3_block_read(
static void
xfs_dir3_block_init(
- struct xfs_mount *mp,
- struct xfs_trans *tp,
- struct xfs_buf *bp,
- struct xfs_inode *dp)
+ struct xfs_da_args *args,
+ struct xfs_buf *bp)
{
- struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
+ struct xfs_trans *tp = args->trans;
+ struct xfs_inode *dp = args->dp;
+ struct xfs_mount *mp = dp->i_mount;
+ struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
bp->b_ops = &xfs_dir3_block_buf_ops;
xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DIR_BLOCK_BUF);
@@ -174,7 +175,7 @@ xfs_dir3_block_init(
memset(hdr3, 0, sizeof(*hdr3));
hdr3->magic = cpu_to_be32(XFS_DIR3_BLOCK_MAGIC);
hdr3->blkno = cpu_to_be64(xfs_buf_daddr(bp));
- hdr3->owner = cpu_to_be64(dp->i_ino);
+ hdr3->owner = cpu_to_be64(args->owner);
uuid_copy(&hdr3->uuid, &mp->m_sb.sb_meta_uuid);
return;
@@ -1006,7 +1007,7 @@ xfs_dir2_leaf_to_block(
/*
* Start converting it to block form.
*/
- xfs_dir3_block_init(mp, tp, dbp, dp);
+ xfs_dir3_block_init(args, dbp);
needlog = 1;
needscan = 0;
@@ -1126,7 +1127,7 @@ xfs_dir2_sf_to_block(
error = xfs_dir3_data_init(args, blkno, &bp);
if (error)
goto out_free;
- xfs_dir3_block_init(mp, tp, bp, dp);
+ xfs_dir3_block_init(args, bp);
hdr = bp->b_addr;
/*
@@ -1166,7 +1167,7 @@ xfs_dir2_sf_to_block(
* Create entry for .
*/
dep = bp->b_addr + offset;
- dep->inumber = cpu_to_be64(dp->i_ino);
+ dep->inumber = cpu_to_be64(args->owner);
dep->namelen = 1;
dep->name[0] = '.';
xfs_dir2_data_put_ftype(mp, dep, XFS_DIR3_FT_DIR);
diff --git a/libxfs/xfs_dir2_data.c b/libxfs/xfs_dir2_data.c
index aaf3f62af..6f3ccfeb6 100644
--- a/libxfs/xfs_dir2_data.c
+++ b/libxfs/xfs_dir2_data.c
@@ -722,7 +722,7 @@ xfs_dir3_data_init(
memset(hdr3, 0, sizeof(*hdr3));
hdr3->magic = cpu_to_be32(XFS_DIR3_DATA_MAGIC);
hdr3->blkno = cpu_to_be64(xfs_buf_daddr(bp));
- hdr3->owner = cpu_to_be64(dp->i_ino);
+ hdr3->owner = cpu_to_be64(args->owner);
uuid_copy(&hdr3->uuid, &mp->m_sb.sb_meta_uuid);
} else
diff --git a/libxfs/xfs_dir2_leaf.c b/libxfs/xfs_dir2_leaf.c
index 80cea8a27..8fbda2250 100644
--- a/libxfs/xfs_dir2_leaf.c
+++ b/libxfs/xfs_dir2_leaf.c
@@ -302,12 +302,12 @@ xfs_dir3_leafn_read(
*/
static void
xfs_dir3_leaf_init(
- struct xfs_mount *mp,
- struct xfs_trans *tp,
+ struct xfs_da_args *args,
struct xfs_buf *bp,
- xfs_ino_t owner,
uint16_t type)
{
+ struct xfs_mount *mp = args->dp->i_mount;
+ struct xfs_trans *tp = args->trans;
struct xfs_dir2_leaf *leaf = bp->b_addr;
ASSERT(type == XFS_DIR2_LEAF1_MAGIC || type == XFS_DIR2_LEAFN_MAGIC);
@@ -321,7 +321,7 @@ xfs_dir3_leaf_init(
? cpu_to_be16(XFS_DIR3_LEAF1_MAGIC)
: cpu_to_be16(XFS_DIR3_LEAFN_MAGIC);
leaf3->info.blkno = cpu_to_be64(xfs_buf_daddr(bp));
- leaf3->info.owner = cpu_to_be64(owner);
+ leaf3->info.owner = cpu_to_be64(args->owner);
uuid_copy(&leaf3->info.uuid, &mp->m_sb.sb_meta_uuid);
} else {
memset(leaf, 0, sizeof(*leaf));
@@ -354,7 +354,6 @@ xfs_dir3_leaf_get_buf(
{
struct xfs_inode *dp = args->dp;
struct xfs_trans *tp = args->trans;
- struct xfs_mount *mp = dp->i_mount;
struct xfs_buf *bp;
int error;
@@ -367,7 +366,7 @@ xfs_dir3_leaf_get_buf(
if (error)
return error;
- xfs_dir3_leaf_init(mp, tp, bp, dp->i_ino, magic);
+ xfs_dir3_leaf_init(args, bp, magic);
xfs_dir3_leaf_log_header(args, bp);
if (magic == XFS_DIR2_LEAF1_MAGIC)
xfs_dir3_leaf_log_tail(args, bp);
diff --git a/libxfs/xfs_dir2_node.c b/libxfs/xfs_dir2_node.c
index 44c8f3f2b..b00f78387 100644
--- a/libxfs/xfs_dir2_node.c
+++ b/libxfs/xfs_dir2_node.c
@@ -346,7 +346,7 @@ xfs_dir3_free_get_buf(
hdr.magic = XFS_DIR3_FREE_MAGIC;
hdr3->hdr.blkno = cpu_to_be64(xfs_buf_daddr(bp));
- hdr3->hdr.owner = cpu_to_be64(dp->i_ino);
+ hdr3->hdr.owner = cpu_to_be64(args->owner);
uuid_copy(&hdr3->hdr.uuid, &mp->m_sb.sb_meta_uuid);
} else
hdr.magic = XFS_DIR2_FREE_MAGIC;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 016/115] xfs: validate attr leaf buffer owners
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (14 preceding siblings ...)
2024-07-30 0:27 ` [PATCH 015/115] xfs: use the xfs_da_args owner field to set new dir/attr block owner Darrick J. Wong
@ 2024-07-30 0:27 ` Darrick J. Wong
2024-07-30 0:28 ` [PATCH 017/115] xfs: validate attr remote value " Darrick J. Wong
` (98 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:27 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: f4887fbc41dcb1560ec5da982ac7c6ad04b71de5
Create a leaf block header checking function to validate the owner field
of xattr leaf blocks.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 10 ++++-----
libxfs/xfs_attr_leaf.c | 56 ++++++++++++++++++++++++++++++++++++++++--------
libxfs/xfs_attr_leaf.h | 4 +++
libxfs/xfs_da_btree.c | 42 ++++++++++++++++++++++++++++++++++++
libxfs/xfs_da_btree.h | 1 +
libxfs/xfs_exchmaps.c | 3 ++-
6 files changed, 100 insertions(+), 16 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 21b5d922b..cc291cf76 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -647,8 +647,8 @@ xfs_attr_leaf_remove_attr(
int forkoff;
int error;
- error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno,
- &bp);
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner,
+ args->blkno, &bp);
if (error)
return error;
@@ -679,7 +679,7 @@ xfs_attr_leaf_shrink(
if (!xfs_attr_is_leaf(dp))
return 0;
- error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner, 0, &bp);
if (error)
return error;
@@ -1156,7 +1156,7 @@ xfs_attr_leaf_try_add(
struct xfs_buf *bp;
int error;
- error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner, 0, &bp);
if (error)
return error;
@@ -1204,7 +1204,7 @@ xfs_attr_leaf_hasname(
{
int error = 0;
- error = xfs_attr3_leaf_read(args->trans, args->dp, 0, bp);
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner, 0, bp);
if (error)
return error;
diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c
index ed3b63f8c..47f2836fb 100644
--- a/libxfs/xfs_attr_leaf.c
+++ b/libxfs/xfs_attr_leaf.c
@@ -385,6 +385,27 @@ xfs_attr3_leaf_verify(
return NULL;
}
+xfs_failaddr_t
+xfs_attr3_leaf_header_check(
+ struct xfs_buf *bp,
+ xfs_ino_t owner)
+{
+ struct xfs_mount *mp = bp->b_mount;
+
+ if (xfs_has_crc(mp)) {
+ struct xfs_attr3_leafblock *hdr3 = bp->b_addr;
+
+ if (hdr3->hdr.info.hdr.magic !=
+ cpu_to_be16(XFS_ATTR3_LEAF_MAGIC))
+ return __this_address;
+
+ if (be64_to_cpu(hdr3->hdr.info.owner) != owner)
+ return __this_address;
+ }
+
+ return NULL;
+}
+
static void
xfs_attr3_leaf_write_verify(
struct xfs_buf *bp)
@@ -445,16 +466,30 @@ int
xfs_attr3_leaf_read(
struct xfs_trans *tp,
struct xfs_inode *dp,
+ xfs_ino_t owner,
xfs_dablk_t bno,
struct xfs_buf **bpp)
{
+ xfs_failaddr_t fa;
int err;
err = xfs_da_read_buf(tp, dp, bno, 0, bpp, XFS_ATTR_FORK,
&xfs_attr3_leaf_buf_ops);
- if (!err && tp && *bpp)
+ if (err || !(*bpp))
+ return err;
+
+ fa = xfs_attr3_leaf_header_check(*bpp, owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(*bpp, fa);
+ xfs_trans_brelse(tp, *bpp);
+ *bpp = NULL;
+ xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
+ return -EFSCORRUPTED;
+ }
+
+ if (tp)
xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_ATTR_LEAF_BUF);
- return err;
+ return 0;
}
/*========================================================================
@@ -1157,7 +1192,7 @@ xfs_attr3_leaf_to_node(
error = xfs_da_grow_inode(args, &blkno);
if (error)
goto out;
- error = xfs_attr3_leaf_read(args->trans, dp, 0, &bp1);
+ error = xfs_attr3_leaf_read(args->trans, dp, args->owner, 0, &bp1);
if (error)
goto out;
@@ -1992,7 +2027,7 @@ xfs_attr3_leaf_toosmall(
if (blkno == 0)
continue;
error = xfs_attr3_leaf_read(state->args->trans, state->args->dp,
- blkno, &bp);
+ state->args->owner, blkno, &bp);
if (error)
return error;
@@ -2714,7 +2749,8 @@ xfs_attr3_leaf_clearflag(
/*
* Set up the operation.
*/
- error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp);
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner,
+ args->blkno, &bp);
if (error)
return error;
@@ -2778,7 +2814,8 @@ xfs_attr3_leaf_setflag(
/*
* Set up the operation.
*/
- error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp);
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner,
+ args->blkno, &bp);
if (error)
return error;
@@ -2837,7 +2874,8 @@ xfs_attr3_leaf_flipflags(
/*
* Read the block containing the "old" attr
*/
- error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp1);
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner,
+ args->blkno, &bp1);
if (error)
return error;
@@ -2845,8 +2883,8 @@ xfs_attr3_leaf_flipflags(
* Read the block containing the "new" attr, if it is different
*/
if (args->blkno2 != args->blkno) {
- error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno2,
- &bp2);
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner,
+ args->blkno2, &bp2);
if (error)
return error;
} else {
diff --git a/libxfs/xfs_attr_leaf.h b/libxfs/xfs_attr_leaf.h
index 9b9948639..bac219589 100644
--- a/libxfs/xfs_attr_leaf.h
+++ b/libxfs/xfs_attr_leaf.h
@@ -98,12 +98,14 @@ int xfs_attr_leaf_order(struct xfs_buf *leaf1_bp,
struct xfs_buf *leaf2_bp);
int xfs_attr_leaf_newentsize(struct xfs_da_args *args, int *local);
int xfs_attr3_leaf_read(struct xfs_trans *tp, struct xfs_inode *dp,
- xfs_dablk_t bno, struct xfs_buf **bpp);
+ xfs_ino_t owner, xfs_dablk_t bno, struct xfs_buf **bpp);
void xfs_attr3_leaf_hdr_from_disk(struct xfs_da_geometry *geo,
struct xfs_attr3_icleaf_hdr *to,
struct xfs_attr_leafblock *from);
void xfs_attr3_leaf_hdr_to_disk(struct xfs_da_geometry *geo,
struct xfs_attr_leafblock *to,
struct xfs_attr3_icleaf_hdr *from);
+xfs_failaddr_t xfs_attr3_leaf_header_check(struct xfs_buf *bp,
+ xfs_ino_t owner);
#endif /* __XFS_ATTR_LEAF_H__ */
diff --git a/libxfs/xfs_da_btree.c b/libxfs/xfs_da_btree.c
index 3ad58ab04..28fd87c2d 100644
--- a/libxfs/xfs_da_btree.c
+++ b/libxfs/xfs_da_btree.c
@@ -248,6 +248,25 @@ xfs_da3_node_verify(
return NULL;
}
+xfs_failaddr_t
+xfs_da3_header_check(
+ struct xfs_buf *bp,
+ xfs_ino_t owner)
+{
+ struct xfs_mount *mp = bp->b_mount;
+ struct xfs_da_blkinfo *hdr = bp->b_addr;
+
+ if (!xfs_has_crc(mp))
+ return NULL;
+
+ switch (hdr->magic) {
+ case cpu_to_be16(XFS_ATTR3_LEAF_MAGIC):
+ return xfs_attr3_leaf_header_check(bp, owner);
+ }
+
+ return NULL;
+}
+
static void
xfs_da3_node_write_verify(
struct xfs_buf *bp)
@@ -1587,6 +1606,7 @@ xfs_da3_node_lookup_int(
struct xfs_da_node_entry *btree;
struct xfs_da3_icnode_hdr nodehdr;
struct xfs_da_args *args;
+ xfs_failaddr_t fa;
xfs_dablk_t blkno;
xfs_dahash_t hashval;
xfs_dahash_t btreehashval;
@@ -1625,6 +1645,12 @@ xfs_da3_node_lookup_int(
if (magic == XFS_ATTR_LEAF_MAGIC ||
magic == XFS_ATTR3_LEAF_MAGIC) {
+ fa = xfs_attr3_leaf_header_check(blk->bp, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(blk->bp, fa);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
blk->magic = XFS_ATTR_LEAF_MAGIC;
blk->hashval = xfs_attr_leaf_lasthash(blk->bp, NULL);
break;
@@ -1992,6 +2018,7 @@ xfs_da3_path_shift(
struct xfs_da_node_entry *btree;
struct xfs_da3_icnode_hdr nodehdr;
struct xfs_buf *bp;
+ xfs_failaddr_t fa;
xfs_dablk_t blkno = 0;
int level;
int error;
@@ -2083,6 +2110,12 @@ xfs_da3_path_shift(
break;
case XFS_ATTR_LEAF_MAGIC:
case XFS_ATTR3_LEAF_MAGIC:
+ fa = xfs_attr3_leaf_header_check(blk->bp, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(blk->bp, fa);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
blk->magic = XFS_ATTR_LEAF_MAGIC;
ASSERT(level == path->active-1);
blk->index = 0;
@@ -2286,6 +2319,7 @@ xfs_da3_swap_lastblock(
struct xfs_buf *last_buf;
struct xfs_buf *sib_buf;
struct xfs_buf *par_buf;
+ xfs_failaddr_t fa;
xfs_dahash_t dead_hash;
xfs_fileoff_t lastoff;
xfs_dablk_t dead_blkno;
@@ -2322,6 +2356,14 @@ xfs_da3_swap_lastblock(
error = xfs_da3_node_read(tp, dp, last_blkno, &last_buf, w);
if (error)
return error;
+ fa = xfs_da3_header_check(last_buf, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(last_buf, fa);
+ xfs_trans_brelse(tp, last_buf);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
+
/*
* Copy the last block into the dead buffer and log it.
*/
diff --git a/libxfs/xfs_da_btree.h b/libxfs/xfs_da_btree.h
index 7fb13f26e..99618e0c8 100644
--- a/libxfs/xfs_da_btree.h
+++ b/libxfs/xfs_da_btree.h
@@ -236,6 +236,7 @@ void xfs_da3_node_hdr_from_disk(struct xfs_mount *mp,
struct xfs_da3_icnode_hdr *to, struct xfs_da_intnode *from);
void xfs_da3_node_hdr_to_disk(struct xfs_mount *mp,
struct xfs_da_intnode *to, struct xfs_da3_icnode_hdr *from);
+xfs_failaddr_t xfs_da3_header_check(struct xfs_buf *bp, xfs_ino_t owner);
extern struct kmem_cache *xfs_da_state_cache;
diff --git a/libxfs/xfs_exchmaps.c b/libxfs/xfs_exchmaps.c
index 6160beef1..21c501aab 100644
--- a/libxfs/xfs_exchmaps.c
+++ b/libxfs/xfs_exchmaps.c
@@ -435,7 +435,8 @@ xfs_exchmaps_attr_to_sf(
if (!xfs_attr_is_leaf(xmi->xmi_ip2))
return 0;
- error = xfs_attr3_leaf_read(tp, xmi->xmi_ip2, 0, &bp);
+ error = xfs_attr3_leaf_read(tp, xmi->xmi_ip2, xmi->xmi_ip2->i_ino, 0,
+ &bp);
if (error)
return error;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 017/115] xfs: validate attr remote value buffer owners
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (15 preceding siblings ...)
2024-07-30 0:27 ` [PATCH 016/115] xfs: validate attr leaf buffer owners Darrick J. Wong
@ 2024-07-30 0:28 ` Darrick J. Wong
2024-07-30 0:28 ` [PATCH 018/115] xfs: validate dabtree node " Darrick J. Wong
` (97 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:28 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 8c25dc728bd1ca9344001aa6ef4556885572baa4
Check the owner field of xattr remote value blocks.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr_remote.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/libxfs/xfs_attr_remote.c b/libxfs/xfs_attr_remote.c
index 2787d3fa3..eb15b272b 100644
--- a/libxfs/xfs_attr_remote.c
+++ b/libxfs/xfs_attr_remote.c
@@ -279,12 +279,12 @@ xfs_attr_rmtval_copyout(
struct xfs_mount *mp,
struct xfs_buf *bp,
struct xfs_inode *dp,
+ xfs_ino_t owner,
int *offset,
int *valuelen,
uint8_t **dst)
{
char *src = bp->b_addr;
- xfs_ino_t ino = dp->i_ino;
xfs_daddr_t bno = xfs_buf_daddr(bp);
int len = BBTOB(bp->b_length);
int blksize = mp->m_attr_geo->blksize;
@@ -298,11 +298,11 @@ xfs_attr_rmtval_copyout(
byte_cnt = min(*valuelen, byte_cnt);
if (xfs_has_crc(mp)) {
- if (xfs_attr3_rmt_hdr_ok(src, ino, *offset,
+ if (xfs_attr3_rmt_hdr_ok(src, owner, *offset,
byte_cnt, bno)) {
xfs_alert(mp,
"remote attribute header mismatch bno/off/len/owner (0x%llx/0x%x/Ox%x/0x%llx)",
- bno, *offset, byte_cnt, ino);
+ bno, *offset, byte_cnt, owner);
xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
return -EFSCORRUPTED;
}
@@ -426,8 +426,7 @@ xfs_attr_rmtval_get(
return error;
error = xfs_attr_rmtval_copyout(mp, bp, args->dp,
- &offset, &valuelen,
- &dst);
+ args->owner, &offset, &valuelen, &dst);
xfs_buf_relse(bp);
if (error)
return error;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 018/115] xfs: validate dabtree node buffer owners
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (16 preceding siblings ...)
2024-07-30 0:28 ` [PATCH 017/115] xfs: validate attr remote value " Darrick J. Wong
@ 2024-07-30 0:28 ` Darrick J. Wong
2024-07-30 0:28 ` [PATCH 019/115] xfs: validate directory leaf " Darrick J. Wong
` (96 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:28 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: d44bea9b41ca25f91fd9f25ed2cc3bb2f6dab4bc
Check the owner field of dabtree node blocks.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_da_btree.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_da_btree.h | 1
2 files changed, 110 insertions(+)
diff --git a/libxfs/xfs_da_btree.c b/libxfs/xfs_da_btree.c
index 28fd87c2d..c221cbba4 100644
--- a/libxfs/xfs_da_btree.c
+++ b/libxfs/xfs_da_btree.c
@@ -248,6 +248,26 @@ xfs_da3_node_verify(
return NULL;
}
+xfs_failaddr_t
+xfs_da3_node_header_check(
+ struct xfs_buf *bp,
+ xfs_ino_t owner)
+{
+ struct xfs_mount *mp = bp->b_mount;
+
+ if (xfs_has_crc(mp)) {
+ struct xfs_da3_blkinfo *hdr3 = bp->b_addr;
+
+ if (hdr3->hdr.magic != cpu_to_be16(XFS_DA3_NODE_MAGIC))
+ return __this_address;
+
+ if (be64_to_cpu(hdr3->owner) != owner)
+ return __this_address;
+ }
+
+ return NULL;
+}
+
xfs_failaddr_t
xfs_da3_header_check(
struct xfs_buf *bp,
@@ -262,6 +282,8 @@ xfs_da3_header_check(
switch (hdr->magic) {
case cpu_to_be16(XFS_ATTR3_LEAF_MAGIC):
return xfs_attr3_leaf_header_check(bp, owner);
+ case cpu_to_be16(XFS_DA3_NODE_MAGIC):
+ return xfs_da3_node_header_check(bp, owner);
}
return NULL;
@@ -1214,6 +1236,7 @@ xfs_da3_root_join(
struct xfs_da3_icnode_hdr oldroothdr;
int error;
struct xfs_inode *dp = state->args->dp;
+ xfs_failaddr_t fa;
trace_xfs_da_root_join(state->args);
@@ -1240,6 +1263,13 @@ xfs_da3_root_join(
error = xfs_da3_node_read(args->trans, dp, child, &bp, args->whichfork);
if (error)
return error;
+ fa = xfs_da3_header_check(bp, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(bp, fa);
+ xfs_trans_brelse(args->trans, bp);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
xfs_da_blkinfo_onlychild_validate(bp->b_addr, oldroothdr.level);
/*
@@ -1274,6 +1304,7 @@ xfs_da3_node_toosmall(
struct xfs_da_blkinfo *info;
xfs_dablk_t blkno;
struct xfs_buf *bp;
+ xfs_failaddr_t fa;
struct xfs_da3_icnode_hdr nodehdr;
int count;
int forward;
@@ -1348,6 +1379,13 @@ xfs_da3_node_toosmall(
state->args->whichfork);
if (error)
return error;
+ fa = xfs_da3_node_header_check(bp, state->args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(bp, fa);
+ xfs_trans_brelse(state->args->trans, bp);
+ xfs_da_mark_sick(state->args);
+ return -EFSCORRUPTED;
+ }
node = bp->b_addr;
xfs_da3_node_hdr_from_disk(dp->i_mount, &thdr, node);
@@ -1670,6 +1708,13 @@ xfs_da3_node_lookup_int(
return -EFSCORRUPTED;
}
+ fa = xfs_da3_node_header_check(blk->bp, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(blk->bp, fa);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
+
blk->magic = XFS_DA_NODE_MAGIC;
/*
@@ -1842,6 +1887,7 @@ xfs_da3_blk_link(
struct xfs_da_blkinfo *tmp_info;
struct xfs_da_args *args;
struct xfs_buf *bp;
+ xfs_failaddr_t fa;
int before = 0;
int error;
struct xfs_inode *dp = state->args->dp;
@@ -1885,6 +1931,13 @@ xfs_da3_blk_link(
&bp, args->whichfork);
if (error)
return error;
+ fa = xfs_da3_header_check(bp, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(bp, fa);
+ xfs_trans_brelse(args->trans, bp);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
ASSERT(bp != NULL);
tmp_info = bp->b_addr;
ASSERT(tmp_info->magic == old_info->magic);
@@ -1906,6 +1959,13 @@ xfs_da3_blk_link(
&bp, args->whichfork);
if (error)
return error;
+ fa = xfs_da3_header_check(bp, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(bp, fa);
+ xfs_trans_brelse(args->trans, bp);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
ASSERT(bp != NULL);
tmp_info = bp->b_addr;
ASSERT(tmp_info->magic == old_info->magic);
@@ -1935,6 +1995,7 @@ xfs_da3_blk_unlink(
struct xfs_da_blkinfo *tmp_info;
struct xfs_da_args *args;
struct xfs_buf *bp;
+ xfs_failaddr_t fa;
int error;
/*
@@ -1965,6 +2026,13 @@ xfs_da3_blk_unlink(
&bp, args->whichfork);
if (error)
return error;
+ fa = xfs_da3_header_check(bp, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(bp, fa);
+ xfs_trans_brelse(args->trans, bp);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
ASSERT(bp != NULL);
tmp_info = bp->b_addr;
ASSERT(tmp_info->magic == save_info->magic);
@@ -1982,6 +2050,13 @@ xfs_da3_blk_unlink(
&bp, args->whichfork);
if (error)
return error;
+ fa = xfs_da3_header_check(bp, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(bp, fa);
+ xfs_trans_brelse(args->trans, bp);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
ASSERT(bp != NULL);
tmp_info = bp->b_addr;
ASSERT(tmp_info->magic == save_info->magic);
@@ -2097,6 +2172,12 @@ xfs_da3_path_shift(
switch (be16_to_cpu(info->magic)) {
case XFS_DA_NODE_MAGIC:
case XFS_DA3_NODE_MAGIC:
+ fa = xfs_da3_node_header_check(blk->bp, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(blk->bp, fa);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
blk->magic = XFS_DA_NODE_MAGIC;
xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr,
bp->b_addr);
@@ -2402,6 +2483,13 @@ xfs_da3_swap_lastblock(
error = xfs_da3_node_read(tp, dp, sib_blkno, &sib_buf, w);
if (error)
goto done;
+ fa = xfs_da3_header_check(sib_buf, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(sib_buf, fa);
+ xfs_da_mark_sick(args);
+ error = -EFSCORRUPTED;
+ goto done;
+ }
sib_info = sib_buf->b_addr;
if (XFS_IS_CORRUPT(mp,
be32_to_cpu(sib_info->forw) != last_blkno ||
@@ -2423,6 +2511,13 @@ xfs_da3_swap_lastblock(
error = xfs_da3_node_read(tp, dp, sib_blkno, &sib_buf, w);
if (error)
goto done;
+ fa = xfs_da3_header_check(sib_buf, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(sib_buf, fa);
+ xfs_da_mark_sick(args);
+ error = -EFSCORRUPTED;
+ goto done;
+ }
sib_info = sib_buf->b_addr;
if (XFS_IS_CORRUPT(mp,
be32_to_cpu(sib_info->back) != last_blkno ||
@@ -2446,6 +2541,13 @@ xfs_da3_swap_lastblock(
error = xfs_da3_node_read(tp, dp, par_blkno, &par_buf, w);
if (error)
goto done;
+ fa = xfs_da3_node_header_check(par_buf, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(par_buf, fa);
+ xfs_da_mark_sick(args);
+ error = -EFSCORRUPTED;
+ goto done;
+ }
par_node = par_buf->b_addr;
xfs_da3_node_hdr_from_disk(dp->i_mount, &par_hdr, par_node);
if (XFS_IS_CORRUPT(mp,
@@ -2495,6 +2597,13 @@ xfs_da3_swap_lastblock(
error = xfs_da3_node_read(tp, dp, par_blkno, &par_buf, w);
if (error)
goto done;
+ fa = xfs_da3_node_header_check(par_buf, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(par_buf, fa);
+ xfs_da_mark_sick(args);
+ error = -EFSCORRUPTED;
+ goto done;
+ }
par_node = par_buf->b_addr;
xfs_da3_node_hdr_from_disk(dp->i_mount, &par_hdr, par_node);
if (XFS_IS_CORRUPT(mp, par_hdr.level != level)) {
diff --git a/libxfs/xfs_da_btree.h b/libxfs/xfs_da_btree.h
index 99618e0c8..7a004786e 100644
--- a/libxfs/xfs_da_btree.h
+++ b/libxfs/xfs_da_btree.h
@@ -237,6 +237,7 @@ void xfs_da3_node_hdr_from_disk(struct xfs_mount *mp,
void xfs_da3_node_hdr_to_disk(struct xfs_mount *mp,
struct xfs_da_intnode *to, struct xfs_da3_icnode_hdr *from);
xfs_failaddr_t xfs_da3_header_check(struct xfs_buf *bp, xfs_ino_t owner);
+xfs_failaddr_t xfs_da3_node_header_check(struct xfs_buf *bp, xfs_ino_t owner);
extern struct kmem_cache *xfs_da_state_cache;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 019/115] xfs: validate directory leaf buffer owners
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (17 preceding siblings ...)
2024-07-30 0:28 ` [PATCH 018/115] xfs: validate dabtree node " Darrick J. Wong
@ 2024-07-30 0:28 ` Darrick J. Wong
2024-07-30 0:28 ` [PATCH 020/115] xfs: validate explicit directory data " Darrick J. Wong
` (95 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:28 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 402eef10a1bab0b428c418cfbaaa0a62efc9c951
Check the owner field of directory leaf blocks.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_da_btree.c | 16 ++++++++++++
libxfs/xfs_dir2.h | 2 +
libxfs/xfs_dir2_leaf.c | 65 ++++++++++++++++++++++++++++++++++++++++++++----
libxfs/xfs_dir2_node.c | 3 +-
libxfs/xfs_dir2_priv.h | 4 +--
5 files changed, 81 insertions(+), 9 deletions(-)
diff --git a/libxfs/xfs_da_btree.c b/libxfs/xfs_da_btree.c
index c221cbba4..3c0dc26b7 100644
--- a/libxfs/xfs_da_btree.c
+++ b/libxfs/xfs_da_btree.c
@@ -284,8 +284,12 @@ xfs_da3_header_check(
return xfs_attr3_leaf_header_check(bp, owner);
case cpu_to_be16(XFS_DA3_NODE_MAGIC):
return xfs_da3_node_header_check(bp, owner);
+ case cpu_to_be16(XFS_DIR3_LEAF1_MAGIC):
+ case cpu_to_be16(XFS_DIR3_LEAFN_MAGIC):
+ return xfs_dir3_leaf_header_check(bp, owner);
}
+ ASSERT(0);
return NULL;
}
@@ -1696,6 +1700,12 @@ xfs_da3_node_lookup_int(
if (magic == XFS_DIR2_LEAFN_MAGIC ||
magic == XFS_DIR3_LEAFN_MAGIC) {
+ fa = xfs_dir3_leaf_header_check(blk->bp, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(blk->bp, fa);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
blk->magic = XFS_DIR2_LEAFN_MAGIC;
blk->hashval = xfs_dir2_leaf_lasthash(args->dp,
blk->bp, NULL);
@@ -2204,6 +2214,12 @@ xfs_da3_path_shift(
break;
case XFS_DIR2_LEAFN_MAGIC:
case XFS_DIR3_LEAFN_MAGIC:
+ fa = xfs_dir3_leaf_header_check(blk->bp, args->owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(blk->bp, fa);
+ xfs_da_mark_sick(args);
+ return -EFSCORRUPTED;
+ }
blk->magic = XFS_DIR2_LEAFN_MAGIC;
ASSERT(level == path->active-1);
blk->index = 0;
diff --git a/libxfs/xfs_dir2.h b/libxfs/xfs_dir2.h
index 8497d041f..2f728c26a 100644
--- a/libxfs/xfs_dir2.h
+++ b/libxfs/xfs_dir2.h
@@ -101,6 +101,8 @@ extern struct xfs_dir2_data_free *xfs_dir2_data_freefind(
extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
+xfs_failaddr_t xfs_dir3_leaf_header_check(struct xfs_buf *bp, xfs_ino_t owner);
+
extern const struct xfs_buf_ops xfs_dir3_block_buf_ops;
extern const struct xfs_buf_ops xfs_dir3_leafn_buf_ops;
extern const struct xfs_buf_ops xfs_dir3_leaf1_buf_ops;
diff --git a/libxfs/xfs_dir2_leaf.c b/libxfs/xfs_dir2_leaf.c
index 8fbda2250..6ce2d4b28 100644
--- a/libxfs/xfs_dir2_leaf.c
+++ b/libxfs/xfs_dir2_leaf.c
@@ -206,6 +206,29 @@ xfs_dir3_leaf_verify(
return xfs_dir3_leaf_check_int(mp, &leafhdr, bp->b_addr, true);
}
+xfs_failaddr_t
+xfs_dir3_leaf_header_check(
+ struct xfs_buf *bp,
+ xfs_ino_t owner)
+{
+ struct xfs_mount *mp = bp->b_mount;
+
+ if (xfs_has_crc(mp)) {
+ struct xfs_dir3_leaf *hdr3 = bp->b_addr;
+
+ if (hdr3->hdr.info.hdr.magic !=
+ cpu_to_be16(XFS_DIR3_LEAF1_MAGIC) &&
+ hdr3->hdr.info.hdr.magic !=
+ cpu_to_be16(XFS_DIR3_LEAFN_MAGIC))
+ return __this_address;
+
+ if (be64_to_cpu(hdr3->hdr.info.owner) != owner)
+ return __this_address;
+ }
+
+ return NULL;
+}
+
static void
xfs_dir3_leaf_read_verify(
struct xfs_buf *bp)
@@ -269,32 +292,60 @@ int
xfs_dir3_leaf_read(
struct xfs_trans *tp,
struct xfs_inode *dp,
+ xfs_ino_t owner,
xfs_dablk_t fbno,
struct xfs_buf **bpp)
{
+ xfs_failaddr_t fa;
int err;
err = xfs_da_read_buf(tp, dp, fbno, 0, bpp, XFS_DATA_FORK,
&xfs_dir3_leaf1_buf_ops);
- if (!err && tp && *bpp)
+ if (err || !(*bpp))
+ return err;
+
+ fa = xfs_dir3_leaf_header_check(*bpp, owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(*bpp, fa);
+ xfs_trans_brelse(tp, *bpp);
+ *bpp = NULL;
+ xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);
+ return -EFSCORRUPTED;
+ }
+
+ if (tp)
xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAF1_BUF);
- return err;
+ return 0;
}
int
xfs_dir3_leafn_read(
struct xfs_trans *tp,
struct xfs_inode *dp,
+ xfs_ino_t owner,
xfs_dablk_t fbno,
struct xfs_buf **bpp)
{
+ xfs_failaddr_t fa;
int err;
err = xfs_da_read_buf(tp, dp, fbno, 0, bpp, XFS_DATA_FORK,
&xfs_dir3_leafn_buf_ops);
- if (!err && tp && *bpp)
+ if (err || !(*bpp))
+ return err;
+
+ fa = xfs_dir3_leaf_header_check(*bpp, owner);
+ if (fa) {
+ __xfs_buf_mark_corrupt(*bpp, fa);
+ xfs_trans_brelse(tp, *bpp);
+ *bpp = NULL;
+ xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);
+ return -EFSCORRUPTED;
+ }
+
+ if (tp)
xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAFN_BUF);
- return err;
+ return 0;
}
/*
@@ -644,7 +695,8 @@ xfs_dir2_leaf_addname(
trace_xfs_dir2_leaf_addname(args);
- error = xfs_dir3_leaf_read(tp, dp, args->geo->leafblk, &lbp);
+ error = xfs_dir3_leaf_read(tp, dp, args->owner, args->geo->leafblk,
+ &lbp);
if (error)
return error;
@@ -1235,7 +1287,8 @@ xfs_dir2_leaf_lookup_int(
tp = args->trans;
mp = dp->i_mount;
- error = xfs_dir3_leaf_read(tp, dp, args->geo->leafblk, &lbp);
+ error = xfs_dir3_leaf_read(tp, dp, args->owner, args->geo->leafblk,
+ &lbp);
if (error)
return error;
diff --git a/libxfs/xfs_dir2_node.c b/libxfs/xfs_dir2_node.c
index b00f78387..c0160d725 100644
--- a/libxfs/xfs_dir2_node.c
+++ b/libxfs/xfs_dir2_node.c
@@ -1559,7 +1559,8 @@ xfs_dir2_leafn_toosmall(
/*
* Read the sibling leaf block.
*/
- error = xfs_dir3_leafn_read(state->args->trans, dp, blkno, &bp);
+ error = xfs_dir3_leafn_read(state->args->trans, dp,
+ state->args->owner, blkno, &bp);
if (error)
return error;
diff --git a/libxfs/xfs_dir2_priv.h b/libxfs/xfs_dir2_priv.h
index 1db2e60ba..2f0e3ad47 100644
--- a/libxfs/xfs_dir2_priv.h
+++ b/libxfs/xfs_dir2_priv.h
@@ -95,9 +95,9 @@ void xfs_dir2_leaf_hdr_from_disk(struct xfs_mount *mp,
void xfs_dir2_leaf_hdr_to_disk(struct xfs_mount *mp, struct xfs_dir2_leaf *to,
struct xfs_dir3_icleaf_hdr *from);
int xfs_dir3_leaf_read(struct xfs_trans *tp, struct xfs_inode *dp,
- xfs_dablk_t fbno, struct xfs_buf **bpp);
+ xfs_ino_t owner, xfs_dablk_t fbno, struct xfs_buf **bpp);
int xfs_dir3_leafn_read(struct xfs_trans *tp, struct xfs_inode *dp,
- xfs_dablk_t fbno, struct xfs_buf **bpp);
+ xfs_ino_t owner, xfs_dablk_t fbno, struct xfs_buf **bpp);
extern int xfs_dir2_block_to_leaf(struct xfs_da_args *args,
struct xfs_buf *dbp);
extern int xfs_dir2_leaf_addname(struct xfs_da_args *args);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 020/115] xfs: validate explicit directory data buffer owners
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (18 preceding siblings ...)
2024-07-30 0:28 ` [PATCH 019/115] xfs: validate directory leaf " Darrick J. Wong
@ 2024-07-30 0:28 ` Darrick J. Wong
2024-07-30 0:29 ` [PATCH 021/115] xfs: validate explicit directory block " Darrick J. Wong
` (94 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:28 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: cc6740ddb423db2066f7669eaaa377fdbf84ab1e
Port the existing directory data header checking function to accept an
owner number instead of an xfs_inode, then update the callsites to use
xfs_da_args.owner when possible.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/namei.c | 3 ++-
libxfs/xfs_dir2.h | 1 +
libxfs/xfs_dir2_block.c | 3 ++-
libxfs/xfs_dir2_data.c | 16 ++++++++++------
libxfs/xfs_dir2_leaf.c | 21 +++++++++++----------
libxfs/xfs_dir2_node.c | 7 +++----
libxfs/xfs_dir2_priv.h | 3 ++-
7 files changed, 31 insertions(+), 23 deletions(-)
diff --git a/db/namei.c b/db/namei.c
index 5a6fd2036..d2541b78a 100644
--- a/db/namei.c
+++ b/db/namei.c
@@ -398,7 +398,8 @@ list_leafdir(
libxfs_trim_extent(&map, dabno, geo->leafblk - dabno);
/* Read the directory block of that first mapping. */
- error = xfs_dir3_data_read(NULL, dp, map.br_startoff, 0, &bp);
+ error = xfs_dir3_data_read(NULL, dp, args->owner,
+ map.br_startoff, 0, &bp);
if (error)
break;
diff --git a/libxfs/xfs_dir2.h b/libxfs/xfs_dir2.h
index 2f728c26a..d623bfdcd 100644
--- a/libxfs/xfs_dir2.h
+++ b/libxfs/xfs_dir2.h
@@ -102,6 +102,7 @@ extern struct xfs_dir2_data_free *xfs_dir2_data_freefind(
extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
xfs_failaddr_t xfs_dir3_leaf_header_check(struct xfs_buf *bp, xfs_ino_t owner);
+xfs_failaddr_t xfs_dir3_data_header_check(struct xfs_buf *bp, xfs_ino_t owner);
extern const struct xfs_buf_ops xfs_dir3_block_buf_ops;
extern const struct xfs_buf_ops xfs_dir3_leafn_buf_ops;
diff --git a/libxfs/xfs_dir2_block.c b/libxfs/xfs_dir2_block.c
index c91559e59..6107e01ca 100644
--- a/libxfs/xfs_dir2_block.c
+++ b/libxfs/xfs_dir2_block.c
@@ -979,7 +979,8 @@ xfs_dir2_leaf_to_block(
* Read the data block if we don't already have it, give up if it fails.
*/
if (!dbp) {
- error = xfs_dir3_data_read(tp, dp, args->geo->datablk, 0, &dbp);
+ error = xfs_dir3_data_read(tp, dp, args->owner,
+ args->geo->datablk, 0, &dbp);
if (error)
return error;
}
diff --git a/libxfs/xfs_dir2_data.c b/libxfs/xfs_dir2_data.c
index 6f3ccfeb6..0c77245ee 100644
--- a/libxfs/xfs_dir2_data.c
+++ b/libxfs/xfs_dir2_data.c
@@ -392,17 +392,20 @@ static const struct xfs_buf_ops xfs_dir3_data_reada_buf_ops = {
.verify_write = xfs_dir3_data_write_verify,
};
-static xfs_failaddr_t
+xfs_failaddr_t
xfs_dir3_data_header_check(
- struct xfs_inode *dp,
- struct xfs_buf *bp)
+ struct xfs_buf *bp,
+ xfs_ino_t owner)
{
- struct xfs_mount *mp = dp->i_mount;
+ struct xfs_mount *mp = bp->b_mount;
if (xfs_has_crc(mp)) {
struct xfs_dir3_data_hdr *hdr3 = bp->b_addr;
- if (be64_to_cpu(hdr3->hdr.owner) != dp->i_ino)
+ if (hdr3->hdr.magic != cpu_to_be32(XFS_DIR3_DATA_MAGIC))
+ return __this_address;
+
+ if (be64_to_cpu(hdr3->hdr.owner) != owner)
return __this_address;
}
@@ -413,6 +416,7 @@ int
xfs_dir3_data_read(
struct xfs_trans *tp,
struct xfs_inode *dp,
+ xfs_ino_t owner,
xfs_dablk_t bno,
unsigned int flags,
struct xfs_buf **bpp)
@@ -426,7 +430,7 @@ xfs_dir3_data_read(
return err;
/* Check things that we can't do in the verifier. */
- fa = xfs_dir3_data_header_check(dp, *bpp);
+ fa = xfs_dir3_data_header_check(*bpp, owner);
if (fa) {
__xfs_buf_mark_corrupt(*bpp, fa);
xfs_trans_brelse(tp, *bpp);
diff --git a/libxfs/xfs_dir2_leaf.c b/libxfs/xfs_dir2_leaf.c
index 6ce2d4b28..1c12b5a66 100644
--- a/libxfs/xfs_dir2_leaf.c
+++ b/libxfs/xfs_dir2_leaf.c
@@ -883,9 +883,9 @@ xfs_dir2_leaf_addname(
* Already had space in some data block.
* Just read that one in.
*/
- error = xfs_dir3_data_read(tp, dp,
- xfs_dir2_db_to_da(args->geo, use_block),
- 0, &dbp);
+ error = xfs_dir3_data_read(tp, dp, args->owner,
+ xfs_dir2_db_to_da(args->geo, use_block), 0,
+ &dbp);
if (error) {
xfs_trans_brelse(tp, lbp);
return error;
@@ -1326,9 +1326,9 @@ xfs_dir2_leaf_lookup_int(
if (newdb != curdb) {
if (dbp)
xfs_trans_brelse(tp, dbp);
- error = xfs_dir3_data_read(tp, dp,
- xfs_dir2_db_to_da(args->geo, newdb),
- 0, &dbp);
+ error = xfs_dir3_data_read(tp, dp, args->owner,
+ xfs_dir2_db_to_da(args->geo, newdb), 0,
+ &dbp);
if (error) {
xfs_trans_brelse(tp, lbp);
return error;
@@ -1368,9 +1368,9 @@ xfs_dir2_leaf_lookup_int(
ASSERT(cidb != -1);
if (cidb != curdb) {
xfs_trans_brelse(tp, dbp);
- error = xfs_dir3_data_read(tp, dp,
- xfs_dir2_db_to_da(args->geo, cidb),
- 0, &dbp);
+ error = xfs_dir3_data_read(tp, dp, args->owner,
+ xfs_dir2_db_to_da(args->geo, cidb), 0,
+ &dbp);
if (error) {
xfs_trans_brelse(tp, lbp);
return error;
@@ -1664,7 +1664,8 @@ xfs_dir2_leaf_trim_data(
/*
* Read the offending data block. We need its buffer.
*/
- error = xfs_dir3_data_read(tp, dp, xfs_dir2_db_to_da(geo, db), 0, &dbp);
+ error = xfs_dir3_data_read(tp, dp, args->owner,
+ xfs_dir2_db_to_da(geo, db), 0, &dbp);
if (error)
return error;
diff --git a/libxfs/xfs_dir2_node.c b/libxfs/xfs_dir2_node.c
index c0160d725..690407374 100644
--- a/libxfs/xfs_dir2_node.c
+++ b/libxfs/xfs_dir2_node.c
@@ -860,7 +860,7 @@ xfs_dir2_leafn_lookup_for_entry(
ASSERT(state->extravalid);
curbp = state->extrablk.bp;
} else {
- error = xfs_dir3_data_read(tp, dp,
+ error = xfs_dir3_data_read(tp, dp, args->owner,
xfs_dir2_db_to_da(args->geo,
newdb),
0, &curbp);
@@ -1946,9 +1946,8 @@ xfs_dir2_node_addname_int(
&freehdr, &findex);
} else {
/* Read the data block in. */
- error = xfs_dir3_data_read(tp, dp,
- xfs_dir2_db_to_da(args->geo, dbno),
- 0, &dbp);
+ error = xfs_dir3_data_read(tp, dp, args->owner,
+ xfs_dir2_db_to_da(args->geo, dbno), 0, &dbp);
}
if (error)
return error;
diff --git a/libxfs/xfs_dir2_priv.h b/libxfs/xfs_dir2_priv.h
index 2f0e3ad47..879aa2e9f 100644
--- a/libxfs/xfs_dir2_priv.h
+++ b/libxfs/xfs_dir2_priv.h
@@ -78,7 +78,8 @@ extern void xfs_dir3_data_check(struct xfs_inode *dp, struct xfs_buf *bp);
extern xfs_failaddr_t __xfs_dir3_data_check(struct xfs_inode *dp,
struct xfs_buf *bp);
int xfs_dir3_data_read(struct xfs_trans *tp, struct xfs_inode *dp,
- xfs_dablk_t bno, unsigned int flags, struct xfs_buf **bpp);
+ xfs_ino_t owner, xfs_dablk_t bno, unsigned int flags,
+ struct xfs_buf **bpp);
int xfs_dir3_data_readahead(struct xfs_inode *dp, xfs_dablk_t bno,
unsigned int flags);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 021/115] xfs: validate explicit directory block buffer owners
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (19 preceding siblings ...)
2024-07-30 0:28 ` [PATCH 020/115] xfs: validate explicit directory data " Darrick J. Wong
@ 2024-07-30 0:29 ` Darrick J. Wong
2024-07-30 0:29 ` [PATCH 022/115] xfs: validate explicit directory free block owners Darrick J. Wong
` (93 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:29 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 29b41ce919b7f0b0c2220e088e450d9b132bec36
Port the existing directory block header checking function to accept an
owner number instead of an xfs_inode, then update the callsites to use
xfs_da_args.owner when possible.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/namei.c | 2 +-
libxfs/xfs_dir2.h | 1 +
libxfs/xfs_dir2_block.c | 20 ++++++++++++--------
libxfs/xfs_dir2_priv.h | 4 ++--
libxfs/xfs_exchmaps.c | 2 +-
5 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/db/namei.c b/db/namei.c
index d2541b78a..303ca3448 100644
--- a/db/namei.c
+++ b/db/namei.c
@@ -337,7 +337,7 @@ list_blockdir(
unsigned int end;
int error;
- error = xfs_dir3_block_read(NULL, dp, &bp);
+ error = xfs_dir3_block_read(NULL, dp, args->owner, &bp);
if (error)
return error;
diff --git a/libxfs/xfs_dir2.h b/libxfs/xfs_dir2.h
index d623bfdcd..eb3a5c350 100644
--- a/libxfs/xfs_dir2.h
+++ b/libxfs/xfs_dir2.h
@@ -103,6 +103,7 @@ extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
xfs_failaddr_t xfs_dir3_leaf_header_check(struct xfs_buf *bp, xfs_ino_t owner);
xfs_failaddr_t xfs_dir3_data_header_check(struct xfs_buf *bp, xfs_ino_t owner);
+xfs_failaddr_t xfs_dir3_block_header_check(struct xfs_buf *bp, xfs_ino_t owner);
extern const struct xfs_buf_ops xfs_dir3_block_buf_ops;
extern const struct xfs_buf_ops xfs_dir3_leafn_buf_ops;
diff --git a/libxfs/xfs_dir2_block.c b/libxfs/xfs_dir2_block.c
index 6107e01ca..82da0d327 100644
--- a/libxfs/xfs_dir2_block.c
+++ b/libxfs/xfs_dir2_block.c
@@ -112,17 +112,20 @@ const struct xfs_buf_ops xfs_dir3_block_buf_ops = {
.verify_struct = xfs_dir3_block_verify,
};
-static xfs_failaddr_t
+xfs_failaddr_t
xfs_dir3_block_header_check(
- struct xfs_inode *dp,
- struct xfs_buf *bp)
+ struct xfs_buf *bp,
+ xfs_ino_t owner)
{
- struct xfs_mount *mp = dp->i_mount;
+ struct xfs_mount *mp = bp->b_mount;
if (xfs_has_crc(mp)) {
struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
- if (be64_to_cpu(hdr3->owner) != dp->i_ino)
+ if (hdr3->magic != cpu_to_be32(XFS_DIR3_BLOCK_MAGIC))
+ return __this_address;
+
+ if (be64_to_cpu(hdr3->owner) != owner)
return __this_address;
}
@@ -133,6 +136,7 @@ int
xfs_dir3_block_read(
struct xfs_trans *tp,
struct xfs_inode *dp,
+ xfs_ino_t owner,
struct xfs_buf **bpp)
{
struct xfs_mount *mp = dp->i_mount;
@@ -145,7 +149,7 @@ xfs_dir3_block_read(
return err;
/* Check things that we can't do in the verifier. */
- fa = xfs_dir3_block_header_check(dp, *bpp);
+ fa = xfs_dir3_block_header_check(*bpp, owner);
if (fa) {
__xfs_buf_mark_corrupt(*bpp, fa);
xfs_trans_brelse(tp, *bpp);
@@ -380,7 +384,7 @@ xfs_dir2_block_addname(
tp = args->trans;
/* Read the (one and only) directory block into bp. */
- error = xfs_dir3_block_read(tp, dp, &bp);
+ error = xfs_dir3_block_read(tp, dp, args->owner, &bp);
if (error)
return error;
@@ -695,7 +699,7 @@ xfs_dir2_block_lookup_int(
dp = args->dp;
tp = args->trans;
- error = xfs_dir3_block_read(tp, dp, &bp);
+ error = xfs_dir3_block_read(tp, dp, args->owner, &bp);
if (error)
return error;
diff --git a/libxfs/xfs_dir2_priv.h b/libxfs/xfs_dir2_priv.h
index 879aa2e9f..adbc544c9 100644
--- a/libxfs/xfs_dir2_priv.h
+++ b/libxfs/xfs_dir2_priv.h
@@ -50,8 +50,8 @@ extern int xfs_dir_cilookup_result(struct xfs_da_args *args,
/* xfs_dir2_block.c */
-extern int xfs_dir3_block_read(struct xfs_trans *tp, struct xfs_inode *dp,
- struct xfs_buf **bpp);
+int xfs_dir3_block_read(struct xfs_trans *tp, struct xfs_inode *dp,
+ xfs_ino_t owner, struct xfs_buf **bpp);
extern int xfs_dir2_block_addname(struct xfs_da_args *args);
extern int xfs_dir2_block_lookup(struct xfs_da_args *args);
extern int xfs_dir2_block_removename(struct xfs_da_args *args);
diff --git a/libxfs/xfs_exchmaps.c b/libxfs/xfs_exchmaps.c
index 21c501aab..71408d713 100644
--- a/libxfs/xfs_exchmaps.c
+++ b/libxfs/xfs_exchmaps.c
@@ -473,7 +473,7 @@ xfs_exchmaps_dir_to_sf(
if (!isblock)
return 0;
- error = xfs_dir3_block_read(tp, xmi->xmi_ip2, &bp);
+ error = xfs_dir3_block_read(tp, xmi->xmi_ip2, xmi->xmi_ip2->i_ino, &bp);
if (error)
return error;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 022/115] xfs: validate explicit directory free block owners
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (20 preceding siblings ...)
2024-07-30 0:29 ` [PATCH 021/115] xfs: validate explicit directory block " Darrick J. Wong
@ 2024-07-30 0:29 ` Darrick J. Wong
2024-07-30 0:29 ` [PATCH 023/115] xfs: use atomic extent swapping to fix user file fork data Darrick J. Wong
` (92 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:29 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: fe6c9f8e48e0dcbfc3dba17edd88490c8579b34b
Port the existing directory freespace block header checking function to
accept an owner number instead of an xfs_inode, then update the
callsites to use xfs_da_args.owner when possible.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_dir2_leaf.c | 3 ++-
libxfs/xfs_dir2_node.c | 32 ++++++++++++++++++--------------
libxfs/xfs_dir2_priv.h | 4 ++--
3 files changed, 22 insertions(+), 17 deletions(-)
diff --git a/libxfs/xfs_dir2_leaf.c b/libxfs/xfs_dir2_leaf.c
index 1c12b5a66..7c0bba512 100644
--- a/libxfs/xfs_dir2_leaf.c
+++ b/libxfs/xfs_dir2_leaf.c
@@ -1804,7 +1804,8 @@ xfs_dir2_node_to_leaf(
/*
* Read the freespace block.
*/
- error = xfs_dir2_free_read(tp, dp, args->geo->freeblk, &fbp);
+ error = xfs_dir2_free_read(tp, dp, args->owner, args->geo->freeblk,
+ &fbp);
if (error)
return error;
xfs_dir2_free_hdr_from_disk(mp, &freehdr, fbp->b_addr);
diff --git a/libxfs/xfs_dir2_node.c b/libxfs/xfs_dir2_node.c
index 690407374..c94d00eb9 100644
--- a/libxfs/xfs_dir2_node.c
+++ b/libxfs/xfs_dir2_node.c
@@ -172,11 +172,11 @@ const struct xfs_buf_ops xfs_dir3_free_buf_ops = {
/* Everything ok in the free block header? */
static xfs_failaddr_t
xfs_dir3_free_header_check(
- struct xfs_inode *dp,
- xfs_dablk_t fbno,
- struct xfs_buf *bp)
+ struct xfs_buf *bp,
+ xfs_ino_t owner,
+ xfs_dablk_t fbno)
{
- struct xfs_mount *mp = dp->i_mount;
+ struct xfs_mount *mp = bp->b_mount;
int maxbests = mp->m_dir_geo->free_max_bests;
unsigned int firstdb;
@@ -192,7 +192,7 @@ xfs_dir3_free_header_check(
return __this_address;
if (be32_to_cpu(hdr3->nvalid) < be32_to_cpu(hdr3->nused))
return __this_address;
- if (be64_to_cpu(hdr3->hdr.owner) != dp->i_ino)
+ if (be64_to_cpu(hdr3->hdr.owner) != owner)
return __this_address;
} else {
struct xfs_dir2_free_hdr *hdr = bp->b_addr;
@@ -211,6 +211,7 @@ static int
__xfs_dir3_free_read(
struct xfs_trans *tp,
struct xfs_inode *dp,
+ xfs_ino_t owner,
xfs_dablk_t fbno,
unsigned int flags,
struct xfs_buf **bpp)
@@ -224,7 +225,7 @@ __xfs_dir3_free_read(
return err;
/* Check things that we can't do in the verifier. */
- fa = xfs_dir3_free_header_check(dp, fbno, *bpp);
+ fa = xfs_dir3_free_header_check(*bpp, owner, fbno);
if (fa) {
__xfs_buf_mark_corrupt(*bpp, fa);
xfs_trans_brelse(tp, *bpp);
@@ -296,20 +297,23 @@ int
xfs_dir2_free_read(
struct xfs_trans *tp,
struct xfs_inode *dp,
+ xfs_ino_t owner,
xfs_dablk_t fbno,
struct xfs_buf **bpp)
{
- return __xfs_dir3_free_read(tp, dp, fbno, 0, bpp);
+ return __xfs_dir3_free_read(tp, dp, owner, fbno, 0, bpp);
}
static int
xfs_dir2_free_try_read(
struct xfs_trans *tp,
struct xfs_inode *dp,
+ xfs_ino_t owner,
xfs_dablk_t fbno,
struct xfs_buf **bpp)
{
- return __xfs_dir3_free_read(tp, dp, fbno, XFS_DABUF_MAP_HOLE_OK, bpp);
+ return __xfs_dir3_free_read(tp, dp, owner, fbno, XFS_DABUF_MAP_HOLE_OK,
+ bpp);
}
static int
@@ -714,7 +718,7 @@ xfs_dir2_leafn_lookup_for_addname(
if (curbp)
xfs_trans_brelse(tp, curbp);
- error = xfs_dir2_free_read(tp, dp,
+ error = xfs_dir2_free_read(tp, dp, args->owner,
xfs_dir2_db_to_da(args->geo,
newfdb),
&curbp);
@@ -1353,8 +1357,8 @@ xfs_dir2_leafn_remove(
* read in the free block.
*/
fdb = xfs_dir2_db_to_fdb(geo, db);
- error = xfs_dir2_free_read(tp, dp, xfs_dir2_db_to_da(geo, fdb),
- &fbp);
+ error = xfs_dir2_free_read(tp, dp, args->owner,
+ xfs_dir2_db_to_da(geo, fdb), &fbp);
if (error)
return error;
free = fbp->b_addr;
@@ -1713,7 +1717,7 @@ xfs_dir2_node_add_datablk(
* that was just allocated.
*/
fbno = xfs_dir2_db_to_fdb(args->geo, *dbno);
- error = xfs_dir2_free_try_read(tp, dp,
+ error = xfs_dir2_free_try_read(tp, dp, args->owner,
xfs_dir2_db_to_da(args->geo, fbno), &fbp);
if (error)
return error;
@@ -1860,7 +1864,7 @@ xfs_dir2_node_find_freeblk(
* so this might not succeed. This should be really rare, so
* there's no reason to avoid it.
*/
- error = xfs_dir2_free_try_read(tp, dp,
+ error = xfs_dir2_free_try_read(tp, dp, args->owner,
xfs_dir2_db_to_da(args->geo, fbno),
&fbp);
if (error)
@@ -2299,7 +2303,7 @@ xfs_dir2_node_trim_free(
/*
* Read the freespace block.
*/
- error = xfs_dir2_free_try_read(tp, dp, fo, &bp);
+ error = xfs_dir2_free_try_read(tp, dp, args->owner, fo, &bp);
if (error)
return error;
/*
diff --git a/libxfs/xfs_dir2_priv.h b/libxfs/xfs_dir2_priv.h
index adbc544c9..3befb3250 100644
--- a/libxfs/xfs_dir2_priv.h
+++ b/libxfs/xfs_dir2_priv.h
@@ -155,8 +155,8 @@ extern int xfs_dir2_node_removename(struct xfs_da_args *args);
extern int xfs_dir2_node_replace(struct xfs_da_args *args);
extern int xfs_dir2_node_trim_free(struct xfs_da_args *args, xfs_fileoff_t fo,
int *rvalp);
-extern int xfs_dir2_free_read(struct xfs_trans *tp, struct xfs_inode *dp,
- xfs_dablk_t fbno, struct xfs_buf **bpp);
+int xfs_dir2_free_read(struct xfs_trans *tp, struct xfs_inode *dp,
+ xfs_ino_t owner, xfs_dablk_t fbno, struct xfs_buf **bpp);
/* xfs_dir2_sf.c */
xfs_ino_t xfs_dir2_sf_get_ino(struct xfs_mount *mp, struct xfs_dir2_sf_hdr *hdr,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 023/115] xfs: use atomic extent swapping to fix user file fork data
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (21 preceding siblings ...)
2024-07-30 0:29 ` [PATCH 022/115] xfs: validate explicit directory free block owners Darrick J. Wong
@ 2024-07-30 0:29 ` Darrick J. Wong
2024-07-30 0:30 ` [PATCH 024/115] xfs: repair extended attributes Darrick J. Wong
` (91 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:29 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 629fdaf5f5b1b7f7107ed4de04e0991a99501ced
Build on the code that was recently added to the temporary repair file
code so that we can atomically switch the contents of any file fork,
even if the fork is in local format. The upcoming functions to repair
xattrs, directories, and symlinks will need that capability.
Repair can lock out access to these user files by holding IOLOCK_EXCL on
these user files. Therefore, it is safe to drop the ILOCK of both the
file being repaired and the tempfile being used for staging, and cancel
the scrub transaction. We do this so that we can reuse the resource
estimation and transaction allocation functions used by a regular file
exchange operation.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_exchmaps.c | 2 +-
libxfs/xfs_exchmaps.h | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_exchmaps.c b/libxfs/xfs_exchmaps.c
index 71408d713..a8a51ce53 100644
--- a/libxfs/xfs_exchmaps.c
+++ b/libxfs/xfs_exchmaps.c
@@ -672,7 +672,7 @@ xfs_exchmaps_rmapbt_blocks(
}
/* Estimate the bmbt and rmapbt overhead required to exchange mappings. */
-static int
+int
xfs_exchmaps_estimate_overhead(
struct xfs_exchmaps_req *req)
{
diff --git a/libxfs/xfs_exchmaps.h b/libxfs/xfs_exchmaps.h
index d8718fca6..fa822dff2 100644
--- a/libxfs/xfs_exchmaps.h
+++ b/libxfs/xfs_exchmaps.h
@@ -97,6 +97,7 @@ xfs_exchmaps_reqfork(const struct xfs_exchmaps_req *req)
return XFS_DATA_FORK;
}
+int xfs_exchmaps_estimate_overhead(struct xfs_exchmaps_req *req);
int xfs_exchmaps_estimate(struct xfs_exchmaps_req *req);
extern struct kmem_cache *xfs_exchmaps_intent_cache;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 024/115] xfs: repair extended attributes
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (22 preceding siblings ...)
2024-07-30 0:29 ` [PATCH 023/115] xfs: use atomic extent swapping to fix user file fork data Darrick J. Wong
@ 2024-07-30 0:30 ` Darrick J. Wong
2024-07-30 0:30 ` [PATCH 025/115] xfs: expose xfs_bmap_local_to_extents for online repair Darrick J. Wong
` (90 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:30 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: e47dcf113ae348678143cc935a1183059c02c9ad
If the extended attributes look bad, try to sift through the rubble to
find whatever keys/values we can, stage a new attribute structure in a
temporary file and use the atomic extent swapping mechanism to commit
the results in bulk.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 2 +-
libxfs/xfs_attr.h | 2 ++
libxfs/xfs_da_format.h | 5 +++++
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index cc291cf76..07f873927 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -1053,7 +1053,7 @@ xfs_attr_set(
* External routines when attribute list is inside the inode
*========================================================================*/
-static inline int xfs_attr_sf_totsize(struct xfs_inode *dp)
+int xfs_attr_sf_totsize(struct xfs_inode *dp)
{
struct xfs_attr_sf_hdr *sf = dp->i_af.if_data;
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index 81be9b3e4..e4f550085 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -618,4 +618,6 @@ extern struct kmem_cache *xfs_attr_intent_cache;
int __init xfs_attr_intent_init_cache(void);
void xfs_attr_intent_destroy_cache(void);
+int xfs_attr_sf_totsize(struct xfs_inode *dp);
+
#endif /* __XFS_ATTR_H__ */
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index 060e5c96b..aac3fe039 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -721,6 +721,11 @@ struct xfs_attr3_leafblock {
#define XFS_ATTR_INCOMPLETE (1u << XFS_ATTR_INCOMPLETE_BIT)
#define XFS_ATTR_NSP_ONDISK_MASK (XFS_ATTR_ROOT | XFS_ATTR_SECURE)
+#define XFS_ATTR_NAMESPACE_STR \
+ { XFS_ATTR_LOCAL, "local" }, \
+ { XFS_ATTR_ROOT, "root" }, \
+ { XFS_ATTR_SECURE, "secure" }
+
/*
* Alignment for namelist and valuelist entries (since they are mixed
* there can be only one alignment value)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 025/115] xfs: expose xfs_bmap_local_to_extents for online repair
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (23 preceding siblings ...)
2024-07-30 0:30 ` [PATCH 024/115] xfs: repair extended attributes Darrick J. Wong
@ 2024-07-30 0:30 ` Darrick J. Wong
2024-07-30 0:30 ` [PATCH 026/115] xfs: pass the owner to xfs_symlink_write_target Darrick J. Wong
` (89 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:30 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: ef744be416b5c649d287604730400dfa728779fe
Allow online repair to call xfs_bmap_local_to_extents and add a void *
argument at the end so that online repair can pass its own context.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_bmap.c | 11 ++++++-----
libxfs/xfs_bmap.h | 6 ++++++
libxfs/xfs_symlink_remote.c | 3 ++-
libxfs/xfs_symlink_remote.h | 3 ++-
4 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 868334229..63feb20e2 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -773,7 +773,7 @@ xfs_bmap_local_to_extents_empty(
}
-STATIC int /* error */
+int /* error */
xfs_bmap_local_to_extents(
xfs_trans_t *tp, /* transaction pointer */
xfs_inode_t *ip, /* incore inode pointer */
@@ -783,7 +783,8 @@ xfs_bmap_local_to_extents(
void (*init_fn)(struct xfs_trans *tp,
struct xfs_buf *bp,
struct xfs_inode *ip,
- struct xfs_ifork *ifp))
+ struct xfs_ifork *ifp, void *priv),
+ void *priv)
{
int error = 0;
int flags; /* logging flags returned */
@@ -844,7 +845,7 @@ xfs_bmap_local_to_extents(
* log here. Note that init_fn must also set the buffer log item type
* correctly.
*/
- init_fn(tp, bp, ip, ifp);
+ init_fn(tp, bp, ip, ifp, priv);
/* account for the change in fork size */
xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
@@ -976,8 +977,8 @@ xfs_bmap_add_attrfork_local(
if (S_ISLNK(VFS_I(ip)->i_mode))
return xfs_bmap_local_to_extents(tp, ip, 1, flags,
- XFS_DATA_FORK,
- xfs_symlink_local_to_remote);
+ XFS_DATA_FORK, xfs_symlink_local_to_remote,
+ NULL);
/* should only be called for types that support local format data */
ASSERT(0);
diff --git a/libxfs/xfs_bmap.h b/libxfs/xfs_bmap.h
index b8bdbf156..32fb2a455 100644
--- a/libxfs/xfs_bmap.h
+++ b/libxfs/xfs_bmap.h
@@ -179,6 +179,12 @@ unsigned int xfs_bmap_compute_attr_offset(struct xfs_mount *mp);
int xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd);
void xfs_bmap_local_to_extents_empty(struct xfs_trans *tp,
struct xfs_inode *ip, int whichfork);
+int xfs_bmap_local_to_extents(struct xfs_trans *tp, struct xfs_inode *ip,
+ xfs_extlen_t total, int *logflagsp, int whichfork,
+ void (*init_fn)(struct xfs_trans *tp, struct xfs_buf *bp,
+ struct xfs_inode *ip, struct xfs_ifork *ifp,
+ void *priv),
+ void *priv);
void xfs_bmap_compute_maxlevels(struct xfs_mount *mp, int whichfork);
int xfs_bmap_first_unused(struct xfs_trans *tp, struct xfs_inode *ip,
xfs_extlen_t len, xfs_fileoff_t *unused, int whichfork);
diff --git a/libxfs/xfs_symlink_remote.c b/libxfs/xfs_symlink_remote.c
index 72f175990..fbcd1aebb 100644
--- a/libxfs/xfs_symlink_remote.c
+++ b/libxfs/xfs_symlink_remote.c
@@ -166,7 +166,8 @@ xfs_symlink_local_to_remote(
struct xfs_trans *tp,
struct xfs_buf *bp,
struct xfs_inode *ip,
- struct xfs_ifork *ifp)
+ struct xfs_ifork *ifp,
+ void *priv)
{
struct xfs_mount *mp = ip->i_mount;
char *buf;
diff --git a/libxfs/xfs_symlink_remote.h b/libxfs/xfs_symlink_remote.h
index ac3dac8f6..83b89a1de 100644
--- a/libxfs/xfs_symlink_remote.h
+++ b/libxfs/xfs_symlink_remote.h
@@ -16,7 +16,8 @@ int xfs_symlink_hdr_set(struct xfs_mount *mp, xfs_ino_t ino, uint32_t offset,
bool xfs_symlink_hdr_ok(xfs_ino_t ino, uint32_t offset,
uint32_t size, struct xfs_buf *bp);
void xfs_symlink_local_to_remote(struct xfs_trans *tp, struct xfs_buf *bp,
- struct xfs_inode *ip, struct xfs_ifork *ifp);
+ struct xfs_inode *ip, struct xfs_ifork *ifp,
+ void *priv);
xfs_failaddr_t xfs_symlink_shortform_verify(void *sfp, int64_t size);
int xfs_symlink_remote_read(struct xfs_inode *ip, char *link);
int xfs_symlink_write_target(struct xfs_trans *tp, struct xfs_inode *ip,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 026/115] xfs: pass the owner to xfs_symlink_write_target
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (24 preceding siblings ...)
2024-07-30 0:30 ` [PATCH 025/115] xfs: expose xfs_bmap_local_to_extents for online repair Darrick J. Wong
@ 2024-07-30 0:30 ` Darrick J. Wong
2024-07-30 0:30 ` [PATCH 027/115] xfs: check unused nlink fields in the ondisk inode Darrick J. Wong
` (88 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:30 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: ea8214c3195c2ed3a205dea42bbe7746712fc461
Require callers of xfs_symlink_write_target to pass the owner number
explicitly. This sets us up for online repair to be able to write a
remote symlink target to sc->tempip with sc->ip's inumber in the block
heaader.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_symlink_remote.c | 4 ++--
libxfs/xfs_symlink_remote.h | 4 ++--
mkfs/proto.c | 3 ++-
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/libxfs/xfs_symlink_remote.c b/libxfs/xfs_symlink_remote.c
index fbcd1aebb..2ad586f39 100644
--- a/libxfs/xfs_symlink_remote.c
+++ b/libxfs/xfs_symlink_remote.c
@@ -308,6 +308,7 @@ int
xfs_symlink_write_target(
struct xfs_trans *tp,
struct xfs_inode *ip,
+ xfs_ino_t owner,
const char *target_path,
int pathlen,
xfs_fsblock_t fs_blocks,
@@ -362,8 +363,7 @@ xfs_symlink_write_target(
byte_cnt = min(byte_cnt, pathlen);
buf = bp->b_addr;
- buf += xfs_symlink_hdr_set(mp, ip->i_ino, offset, byte_cnt,
- bp);
+ buf += xfs_symlink_hdr_set(mp, owner, offset, byte_cnt, bp);
memcpy(buf, cur_chunk, byte_cnt);
diff --git a/libxfs/xfs_symlink_remote.h b/libxfs/xfs_symlink_remote.h
index 83b89a1de..c1672fe1f 100644
--- a/libxfs/xfs_symlink_remote.h
+++ b/libxfs/xfs_symlink_remote.h
@@ -21,8 +21,8 @@ void xfs_symlink_local_to_remote(struct xfs_trans *tp, struct xfs_buf *bp,
xfs_failaddr_t xfs_symlink_shortform_verify(void *sfp, int64_t size);
int xfs_symlink_remote_read(struct xfs_inode *ip, char *link);
int xfs_symlink_write_target(struct xfs_trans *tp, struct xfs_inode *ip,
- const char *target_path, int pathlen, xfs_fsblock_t fs_blocks,
- uint resblks);
+ xfs_ino_t owner, const char *target_path, int pathlen,
+ xfs_fsblock_t fs_blocks, uint resblks);
int xfs_symlink_remote_truncate(struct xfs_trans *tp, struct xfs_inode *ip);
#endif /* __XFS_SYMLINK_REMOTE_H */
diff --git a/mkfs/proto.c b/mkfs/proto.c
index a923f9c10..5125ee44f 100644
--- a/mkfs/proto.c
+++ b/mkfs/proto.c
@@ -252,7 +252,8 @@ writesymlink(
xfs_extlen_t nb = XFS_B_TO_FSB(mp, len);
int error;
- error = -libxfs_symlink_write_target(tp, ip, buf, len, nb, nb);
+ error = -libxfs_symlink_write_target(tp, ip, ip->i_ino, buf, len, nb,
+ nb);
if (error) {
fprintf(stderr,
_("%s: error %d creating symlink to '%s'.\n"), progname, error, buf);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 027/115] xfs: check unused nlink fields in the ondisk inode
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (25 preceding siblings ...)
2024-07-30 0:30 ` [PATCH 026/115] xfs: pass the owner to xfs_symlink_write_target Darrick J. Wong
@ 2024-07-30 0:30 ` Darrick J. Wong
2024-07-30 0:31 ` [PATCH 028/115] xfs: try to avoid allocating from sick inode clusters Darrick J. Wong
` (87 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:30 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 40cb8613d6122ca80a9e42e4cecc4d308c3b80fb
v2/v3 inodes use di_nlink and not di_onlink; and v1 inodes use di_onlink
and not di_nlink. Whichever field is not in use, make sure its contents
are zero, and teach xfs_scrub to fix that if it is.
This clears a bunch of missing scrub failure errors in xfs/385 for
core.onlink.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_inode_buf.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/libxfs/xfs_inode_buf.c b/libxfs/xfs_inode_buf.c
index 82cf64db9..aee581d53 100644
--- a/libxfs/xfs_inode_buf.c
+++ b/libxfs/xfs_inode_buf.c
@@ -488,6 +488,14 @@ xfs_dinode_verify(
return __this_address;
}
+ if (dip->di_version > 1) {
+ if (dip->di_onlink)
+ return __this_address;
+ } else {
+ if (dip->di_nlink)
+ return __this_address;
+ }
+
/* don't allow invalid i_size */
di_size = be64_to_cpu(dip->di_size);
if (di_size & (1ULL << 63))
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 028/115] xfs: try to avoid allocating from sick inode clusters
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (26 preceding siblings ...)
2024-07-30 0:30 ` [PATCH 027/115] xfs: check unused nlink fields in the ondisk inode Darrick J. Wong
@ 2024-07-30 0:31 ` Darrick J. Wong
2024-07-30 0:31 ` [PATCH 029/115] xfs: pin inodes that would otherwise overflow link count Darrick J. Wong
` (86 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:31 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 2935213a6831a0087442d406301c2cdcc87b0f61
I noticed that xfs/413 and xfs/375 occasionally failed while fuzzing
core.mode of an inode. The root cause of these problems is that the
field we fuzzed (core.mode or core.magic, typically) causes the entire
inode cluster buffer verification to fail, which affects several inodes
at once. The repair process tries to create either a /lost+found or a
temporary repair file, but regrettably it picks the same inode cluster
that we just corrupted, with the result that repair triggers the demise
of the filesystem.
Try avoid this by making the inode allocation path detect when the perag
health status indicates that someone has found bad inode cluster
buffers, and try to read the inode cluster buffer. If the cluster
buffer fails the verifiers, try another AG. This isn't foolproof and
can result in premature ENOSPC, but that might be better than shutting
down.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/util.c | 6 ++++++
libxfs/xfs_ialloc.c | 40 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 46 insertions(+)
diff --git a/libxfs/util.c b/libxfs/util.c
index 841f4b963..2656c99a8 100644
--- a/libxfs/util.c
+++ b/libxfs/util.c
@@ -732,6 +732,12 @@ void xfs_fs_mark_sick(struct xfs_mount *mp, unsigned int mask) { }
void xfs_agno_mark_sick(struct xfs_mount *mp, xfs_agnumber_t agno,
unsigned int mask) { }
void xfs_ag_mark_sick(struct xfs_perag *pag, unsigned int mask) { }
+void xfs_ag_measure_sickness(struct xfs_perag *pag, unsigned int *sick,
+ unsigned int *checked)
+{
+ *sick = 0;
+ *checked = 0;
+}
void xfs_bmap_mark_sick(struct xfs_inode *ip, int whichfork) { }
void xfs_btree_mark_sick(struct xfs_btree_cur *cur) { }
void xfs_dirattr_mark_sick(struct xfs_inode *ip, int whichfork) { }
diff --git a/libxfs/xfs_ialloc.c b/libxfs/xfs_ialloc.c
index 992b8348a..d8697561e 100644
--- a/libxfs/xfs_ialloc.c
+++ b/libxfs/xfs_ialloc.c
@@ -1052,6 +1052,33 @@ xfs_inobt_first_free_inode(
return xfs_lowbit64(realfree);
}
+/*
+ * If this AG has corrupt inodes, check if allocating this inode would fail
+ * with corruption errors. Returns 0 if we're clear, or EAGAIN to try again
+ * somewhere else.
+ */
+static int
+xfs_dialloc_check_ino(
+ struct xfs_perag *pag,
+ struct xfs_trans *tp,
+ xfs_ino_t ino)
+{
+ struct xfs_imap imap;
+ struct xfs_buf *bp;
+ int error;
+
+ error = xfs_imap(pag, tp, ino, &imap, 0);
+ if (error)
+ return -EAGAIN;
+
+ error = xfs_imap_to_bp(pag->pag_mount, tp, &imap, &bp);
+ if (error)
+ return -EAGAIN;
+
+ xfs_trans_brelse(tp, bp);
+ return 0;
+}
+
/*
* Allocate an inode using the inobt-only algorithm.
*/
@@ -1304,6 +1331,13 @@ xfs_dialloc_ag_inobt(
ASSERT((XFS_AGINO_TO_OFFSET(mp, rec.ir_startino) %
XFS_INODES_PER_CHUNK) == 0);
ino = XFS_AGINO_TO_INO(mp, pag->pag_agno, rec.ir_startino + offset);
+
+ if (xfs_ag_has_sickness(pag, XFS_SICK_AG_INODES)) {
+ error = xfs_dialloc_check_ino(pag, tp, ino);
+ if (error)
+ goto error0;
+ }
+
rec.ir_free &= ~XFS_INOBT_MASK(offset);
rec.ir_freecount--;
error = xfs_inobt_update(cur, &rec);
@@ -1579,6 +1613,12 @@ xfs_dialloc_ag(
XFS_INODES_PER_CHUNK) == 0);
ino = XFS_AGINO_TO_INO(mp, pag->pag_agno, rec.ir_startino + offset);
+ if (xfs_ag_has_sickness(pag, XFS_SICK_AG_INODES)) {
+ error = xfs_dialloc_check_ino(pag, tp, ino);
+ if (error)
+ goto error_cur;
+ }
+
/*
* Modify or remove the finobt record.
*/
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 029/115] xfs: pin inodes that would otherwise overflow link count
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (27 preceding siblings ...)
2024-07-30 0:31 ` [PATCH 028/115] xfs: try to avoid allocating from sick inode clusters Darrick J. Wong
@ 2024-07-30 0:31 ` Darrick J. Wong
2024-07-30 0:31 ` [PATCH 030/115] xfs: Increase XFS_DEFER_OPS_NR_INODES to 5 Darrick J. Wong
` (85 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:31 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 5f204051d998ec3d7306db0d749bddcbf4c97693
The VFS inc_nlink function does not explicitly check for integer
overflows in the i_nlink field. Instead, it checks the link count
against s_max_links in the vfs_{link,create,rename} functions. XFS
sets the maximum link count to 2.1 billion, so integer overflows should
not be a problem.
However. It's possible that online repair could find that a file has
more than four billion links, particularly if the link count got
corrupted while creating hardlinks to the file. The di_nlinkv2 field is
not large enough to store a value larger than 2^32, so we ought to
define a magic pin value of ~0U which means that the inode never gets
deleted. This will prevent a UAF error if the repair finds this
situation and users begin deleting links to the file.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_format.h | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h
index 10153ce11..f1818c54a 100644
--- a/libxfs/xfs_format.h
+++ b/libxfs/xfs_format.h
@@ -899,6 +899,12 @@ static inline uint xfs_dinode_size(int version)
*/
#define XFS_MAXLINK ((1U << 31) - 1U)
+/*
+ * Any file that hits the maximum ondisk link count should be pinned to avoid
+ * a use-after-free situation.
+ */
+#define XFS_NLINK_PINNED (~0U)
+
/*
* Values for di_format
*
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 030/115] xfs: Increase XFS_DEFER_OPS_NR_INODES to 5
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (28 preceding siblings ...)
2024-07-30 0:31 ` [PATCH 029/115] xfs: pin inodes that would otherwise overflow link count Darrick J. Wong
@ 2024-07-30 0:31 ` Darrick J. Wong
2024-07-30 0:31 ` [PATCH 031/115] xfs: make XFS_TRANS_LOWMODE match the other XFS_TRANS_ definitions Darrick J. Wong
` (84 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:31 UTC (permalink / raw)
To: djwong, cem
Cc: Allison Henderson, Catherine Hoang, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: 7560c937b4b5a3c671053be86ff00156a6fdd399
Renames that generate parent pointer updates can join up to 5
inodes locked in sorted order. So we need to increase the
number of defer ops inodes and relock them in the same way.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Catherine Hoang <catherine.hoang@oracle.com>
[djwong: have one sorting function]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/libxfs_priv.h | 2 ++
libxfs/xfs_defer.c | 6 +++++-
libxfs/xfs_defer.h | 8 +++++++-
3 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index 9ddba767b..aa0a3adb4 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -440,6 +440,8 @@ xfs_buf_readahead(
})
#define xfs_lock_two_inodes(ip0,mode0,ip1,mode1) ((void) 0)
#define xfs_assert_ilocked(ip, flags) ((void) 0)
+#define xfs_lock_inodes(i_tab, nr, mode) ((void) 0)
+#define xfs_sort_inodes(i_tab, nr) ((void) 0)
/* space allocation */
#define XFS_EXTENT_BUSY_DISCARDED 0x01 /* undergoing a discard op. */
diff --git a/libxfs/xfs_defer.c b/libxfs/xfs_defer.c
index 3a91bb3a5..7cf392e2f 100644
--- a/libxfs/xfs_defer.c
+++ b/libxfs/xfs_defer.c
@@ -1086,7 +1086,11 @@ xfs_defer_ops_continue(
ASSERT(!(tp->t_flags & XFS_TRANS_DIRTY));
/* Lock the captured resources to the new transaction. */
- if (dfc->dfc_held.dr_inos == 2)
+ if (dfc->dfc_held.dr_inos > 2) {
+ xfs_sort_inodes(dfc->dfc_held.dr_ip, dfc->dfc_held.dr_inos);
+ xfs_lock_inodes(dfc->dfc_held.dr_ip, dfc->dfc_held.dr_inos,
+ XFS_ILOCK_EXCL);
+ } else if (dfc->dfc_held.dr_inos == 2)
xfs_lock_two_inodes(dfc->dfc_held.dr_ip[0], XFS_ILOCK_EXCL,
dfc->dfc_held.dr_ip[1], XFS_ILOCK_EXCL);
else if (dfc->dfc_held.dr_inos == 1)
diff --git a/libxfs/xfs_defer.h b/libxfs/xfs_defer.h
index 81cca60d7..8b338031e 100644
--- a/libxfs/xfs_defer.h
+++ b/libxfs/xfs_defer.h
@@ -77,7 +77,13 @@ extern const struct xfs_defer_op_type xfs_exchmaps_defer_type;
/*
* Deferred operation item relogging limits.
*/
-#define XFS_DEFER_OPS_NR_INODES 2 /* join up to two inodes */
+
+/*
+ * Rename w/ parent pointers can require up to 5 inodes with deferred ops to
+ * be joined to the transaction: src_dp, target_dp, src_ip, target_ip, and wip.
+ * These inodes are locked in sorted order by their inode numbers
+ */
+#define XFS_DEFER_OPS_NR_INODES 5
#define XFS_DEFER_OPS_NR_BUFS 2 /* join up to two buffers */
/* Resources that must be held across a transaction roll. */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 031/115] xfs: make XFS_TRANS_LOWMODE match the other XFS_TRANS_ definitions
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (29 preceding siblings ...)
2024-07-30 0:31 ` [PATCH 030/115] xfs: Increase XFS_DEFER_OPS_NR_INODES to 5 Darrick J. Wong
@ 2024-07-30 0:31 ` Darrick J. Wong
2024-07-30 0:32 ` [PATCH 032/115] xfs: refactor realtime inode locking Darrick J. Wong
` (83 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:31 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Dave Chinner, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 330c4f94b0d73e440de3f738a625e38defe1bc15
Commit bb7b1c9c5dd3 ("xfs: tag transactions that contain intent done
items") switched the XFS_TRANS_ definitions to be bit based, and using
comments above the definitions. As XFS_TRANS_LOWMODE was last and has
a big fat comment it was missed. Switch it to the same style.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_shared.h | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/libxfs/xfs_shared.h b/libxfs/xfs_shared.h
index dfd61fa83..f35640ad3 100644
--- a/libxfs/xfs_shared.h
+++ b/libxfs/xfs_shared.h
@@ -124,7 +124,6 @@ void xfs_log_get_max_trans_res(struct xfs_mount *mp,
#define XFS_TRANS_RES_FDBLKS (1u << 6)
/* Transaction contains an intent done log item */
#define XFS_TRANS_HAS_INTENT_DONE (1u << 7)
-
/*
* LOWMODE is used by the allocator to activate the lowspace algorithm - when
* free space is running low the extent allocator may choose to allocate an
@@ -136,7 +135,7 @@ void xfs_log_get_max_trans_res(struct xfs_mount *mp,
* for free space from AG 0. If the correct transaction reservations have been
* made then this algorithm will eventually find all the space it needs.
*/
-#define XFS_TRANS_LOWMODE 0x100 /* allocate in low space mode */
+#define XFS_TRANS_LOWMODE (1u << 8)
/*
* Field values for xfs_trans_mod_sb.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 032/115] xfs: refactor realtime inode locking
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (30 preceding siblings ...)
2024-07-30 0:31 ` [PATCH 031/115] xfs: make XFS_TRANS_LOWMODE match the other XFS_TRANS_ definitions Darrick J. Wong
@ 2024-07-30 0:32 ` Darrick J. Wong
2024-07-30 0:32 ` [PATCH 033/115] xfs: free RT extents after updating the bmap btree Darrick J. Wong
` (82 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:32 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Dave Chinner, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: b7e23c0e2e3b1c520a3370f058870b914071a470
Create helper functions to deal with locking realtime metadata inodes.
This enables us to maintain correct locking order once we start adding
the realtime rmap and refcount btree inodes.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/libxfs_priv.h | 3 +++
libxfs/xfs_bmap.c | 7 ++----
libxfs/xfs_rtbitmap.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_rtbitmap.h | 17 +++++++++++++++
4 files changed, 79 insertions(+), 5 deletions(-)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index aa0a3adb4..fa7cad0e0 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -170,6 +170,9 @@ enum ce { CE_DEBUG, CE_CONT, CE_NOTE, CE_WARN, CE_ALERT, CE_PANIC };
#define XFS_ERRLEVEL_LOW 1
#define XFS_ILOCK_EXCL 0
+#define XFS_ILOCK_SHARED 0
+#define XFS_ILOCK_RTBITMAP 0
+#define XFS_ILOCK_RTSUM 0
#define XFS_STATS_INC(mp, count) do { (mp) = (mp); } while (0)
#define XFS_STATS_DEC(mp, count, x) do { (mp) = (mp); } while (0)
#define XFS_STATS_ADD(mp, count, x) do { (mp) = (mp); } while (0)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 63feb20e2..f339e16a1 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -5412,12 +5412,9 @@ __xfs_bunmapi(
if (isrt) {
/*
- * Synchronize by locking the bitmap inode.
+ * Synchronize by locking the realtime bitmap.
*/
- xfs_ilock(mp->m_rbmip, XFS_ILOCK_EXCL|XFS_ILOCK_RTBITMAP);
- xfs_trans_ijoin(tp, mp->m_rbmip, XFS_ILOCK_EXCL);
- xfs_ilock(mp->m_rsumip, XFS_ILOCK_EXCL|XFS_ILOCK_RTSUM);
- xfs_trans_ijoin(tp, mp->m_rsumip, XFS_ILOCK_EXCL);
+ xfs_rtbitmap_lock(tp, mp);
}
extno = 0;
diff --git a/libxfs/xfs_rtbitmap.c b/libxfs/xfs_rtbitmap.c
index 543cfd2fb..58a3ba992 100644
--- a/libxfs/xfs_rtbitmap.c
+++ b/libxfs/xfs_rtbitmap.c
@@ -1166,3 +1166,60 @@ xfs_rtsummary_wordcount(
blocks = xfs_rtsummary_blockcount(mp, rsumlevels, rbmblocks);
return XFS_FSB_TO_B(mp, blocks) >> XFS_WORDLOG;
}
+
+/*
+ * Lock both realtime free space metadata inodes for a freespace update. If a
+ * transaction is given, the inodes will be joined to the transaction and the
+ * ILOCKs will be released on transaction commit.
+ */
+void
+xfs_rtbitmap_lock(
+ struct xfs_trans *tp,
+ struct xfs_mount *mp)
+{
+ xfs_ilock(mp->m_rbmip, XFS_ILOCK_EXCL | XFS_ILOCK_RTBITMAP);
+ if (tp)
+ xfs_trans_ijoin(tp, mp->m_rbmip, XFS_ILOCK_EXCL);
+
+ xfs_ilock(mp->m_rsumip, XFS_ILOCK_EXCL | XFS_ILOCK_RTSUM);
+ if (tp)
+ xfs_trans_ijoin(tp, mp->m_rsumip, XFS_ILOCK_EXCL);
+}
+
+/* Unlock both realtime free space metadata inodes after a freespace update. */
+void
+xfs_rtbitmap_unlock(
+ struct xfs_mount *mp)
+{
+ xfs_iunlock(mp->m_rsumip, XFS_ILOCK_EXCL | XFS_ILOCK_RTSUM);
+ xfs_iunlock(mp->m_rbmip, XFS_ILOCK_EXCL | XFS_ILOCK_RTBITMAP);
+}
+
+/*
+ * Lock the realtime free space metadata inodes for a freespace scan. Callers
+ * must walk metadata blocks in order of increasing file offset.
+ */
+void
+xfs_rtbitmap_lock_shared(
+ struct xfs_mount *mp,
+ unsigned int rbmlock_flags)
+{
+ if (rbmlock_flags & XFS_RBMLOCK_BITMAP)
+ xfs_ilock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
+
+ if (rbmlock_flags & XFS_RBMLOCK_SUMMARY)
+ xfs_ilock(mp->m_rsumip, XFS_ILOCK_SHARED | XFS_ILOCK_RTSUM);
+}
+
+/* Unlock the realtime free space metadata inodes after a freespace scan. */
+void
+xfs_rtbitmap_unlock_shared(
+ struct xfs_mount *mp,
+ unsigned int rbmlock_flags)
+{
+ if (rbmlock_flags & XFS_RBMLOCK_SUMMARY)
+ xfs_iunlock(mp->m_rsumip, XFS_ILOCK_SHARED | XFS_ILOCK_RTSUM);
+
+ if (rbmlock_flags & XFS_RBMLOCK_BITMAP)
+ xfs_iunlock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
+}
diff --git a/libxfs/xfs_rtbitmap.h b/libxfs/xfs_rtbitmap.h
index 152a66750..6186585f2 100644
--- a/libxfs/xfs_rtbitmap.h
+++ b/libxfs/xfs_rtbitmap.h
@@ -360,6 +360,19 @@ xfs_filblks_t xfs_rtsummary_blockcount(struct xfs_mount *mp,
unsigned int rsumlevels, xfs_extlen_t rbmblocks);
unsigned long long xfs_rtsummary_wordcount(struct xfs_mount *mp,
unsigned int rsumlevels, xfs_extlen_t rbmblocks);
+
+void xfs_rtbitmap_lock(struct xfs_trans *tp, struct xfs_mount *mp);
+void xfs_rtbitmap_unlock(struct xfs_mount *mp);
+
+/* Lock the rt bitmap inode in shared mode */
+#define XFS_RBMLOCK_BITMAP (1U << 0)
+/* Lock the rt summary inode in shared mode */
+#define XFS_RBMLOCK_SUMMARY (1U << 1)
+
+void xfs_rtbitmap_lock_shared(struct xfs_mount *mp,
+ unsigned int rbmlock_flags);
+void xfs_rtbitmap_unlock_shared(struct xfs_mount *mp,
+ unsigned int rbmlock_flags);
#else /* CONFIG_XFS_RT */
# define xfs_rtfree_extent(t,b,l) (-ENOSYS)
# define xfs_rtfree_blocks(t,rb,rl) (-ENOSYS)
@@ -378,6 +391,10 @@ xfs_rtbitmap_blockcount(struct xfs_mount *mp, xfs_rtbxlen_t rtextents)
# define xfs_rtbitmap_wordcount(mp, r) (0)
# define xfs_rtsummary_blockcount(mp, l, b) (0)
# define xfs_rtsummary_wordcount(mp, l, b) (0)
+# define xfs_rtbitmap_lock(tp, mp) do { } while (0)
+# define xfs_rtbitmap_unlock(mp) do { } while (0)
+# define xfs_rtbitmap_lock_shared(mp, lf) do { } while (0)
+# define xfs_rtbitmap_unlock_shared(mp, lf) do { } while (0)
#endif /* CONFIG_XFS_RT */
#endif /* __XFS_RTBITMAP_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 033/115] xfs: free RT extents after updating the bmap btree
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (31 preceding siblings ...)
2024-07-30 0:32 ` [PATCH 032/115] xfs: refactor realtime inode locking Darrick J. Wong
@ 2024-07-30 0:32 ` Darrick J. Wong
2024-07-30 0:32 ` [PATCH 034/115] xfs: move RT inode locking out of __xfs_bunmapi Darrick J. Wong
` (81 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:32 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Dave Chinner, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 9871d0963751293bf0587759a9b6b8f808e35c7c
Currently xfs_bmap_del_extent_real frees RT extents before updating
the bmap btree, while it frees regular blocks after performing the bmap
btree update for convoluted historic reasons. Switch to free the RT
blocks in the same place as the regular data blocks instead to simply
the code and fix a very theoretical bug.
A short history of this code researched by Dave Chiner below:
The truncate for data device extents was originally a two-phase
operation. First it removed the bmapbt record, but because this can
free BMBT extents, it can use up all the free space tree reservation
space. So the transaction gets rolled to commit the BMBT change and
the xfs_bmap_finish() call that frees the data extent runs with a
new transaction reservation that allows different free space btrees
to be logged without overrun.
However, on crash, this could lose the free space because there was
nothing to tell recovery about the extents removed from the BMBT,
hence EFIs were introduced. They tie the extent free operation to the
bmapbt record removal commit for recovery of the second phase of the
extent removal process.
Then RT extents came along. RT extent freeing does not require a
free space btree reservation because the free space metadata is
static and transaction size is bound. Hence we don't need to care if
the BMBT record removal modifies the per-ag free space trees and we
don't need a two-phase extent remove transaction. The only thing we
have to care about is not losing space on crash.
Hence instead of recording the extent for freeing in the bmap list
for xfs_bmap_finish() to process in a new transaction, it simply
freed the rtextent directly. So the original code (from 1994) simply
replaced the "free AG extent later" queueing with a direct free.
This code was originally at the start of xfs_dmap_del_extent(), but
the xfs_bmap_add_free() got moved to the end of the function via the
"do_fx" flag (the current code logic) in 1997 (commit c4fac74eaa58
in the historic xfs-import tree) because there was a shutdown occurring
because of a case where splitting the extent record failed because the
BMBT split and the filesystem didn't have enough space for the split to
be done. (FWIW, I'm not sure this can happen anymore.)
The commit backed out the BMBT change on ENOSPC error, and in doing
so I think this actually breaks RT free space tracking. However, it
then returns an ENOSPC error, and we have a dirty transaction in the
RT case so this will shut down the filesysetm when the transaction
is cancelled. Hence the corrupted "bmbt now points at freed rt dev
space" condition never make it to disk, but it's still the wrong way
to handle the issue.
IOWs, this proposed change fixes that "shutdown at ENOSPC on rt
devices" situation that was introduced by the above commit back in
1997.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 26 +++++++++-----------------
1 file changed, 9 insertions(+), 17 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index f339e16a1..dd91fb2aa 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -5103,8 +5103,7 @@ xfs_bmap_del_extent_real(
{
xfs_fsblock_t del_endblock=0; /* first block past del */
xfs_fileoff_t del_endoff; /* first offset past del */
- int do_fx; /* free extent at end of routine */
- int error; /* error return value */
+ int error = 0; /* error return value */
struct xfs_bmbt_irec got; /* current extent entry */
xfs_fileoff_t got_endoff; /* first offset past got */
int i; /* temp state */
@@ -5147,20 +5146,10 @@ xfs_bmap_del_extent_real(
return -ENOSPC;
*logflagsp = XFS_ILOG_CORE;
- if (xfs_ifork_is_realtime(ip, whichfork)) {
- if (!(bflags & XFS_BMAPI_REMAP)) {
- error = xfs_rtfree_blocks(tp, del->br_startblock,
- del->br_blockcount);
- if (error)
- return error;
- }
-
- do_fx = 0;
+ if (xfs_ifork_is_realtime(ip, whichfork))
qfield = XFS_TRANS_DQ_RTBCOUNT;
- } else {
- do_fx = 1;
+ else
qfield = XFS_TRANS_DQ_BCOUNT;
- }
nblks = del->br_blockcount;
del_endblock = del->br_startblock + del->br_blockcount;
@@ -5308,18 +5297,21 @@ xfs_bmap_del_extent_real(
/*
* If we need to, add to list of extents to delete.
*/
- if (do_fx && !(bflags & XFS_BMAPI_REMAP)) {
+ if (!(bflags & XFS_BMAPI_REMAP)) {
if (xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK) {
xfs_refcount_decrease_extent(tp, del);
+ } else if (xfs_ifork_is_realtime(ip, whichfork)) {
+ error = xfs_rtfree_blocks(tp, del->br_startblock,
+ del->br_blockcount);
} else {
error = xfs_free_extent_later(tp, del->br_startblock,
del->br_blockcount, NULL,
XFS_AG_RESV_NONE,
((bflags & XFS_BMAPI_NODISCARD) ||
del->br_state == XFS_EXT_UNWRITTEN));
- if (error)
- return error;
}
+ if (error)
+ return error;
}
/*
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 034/115] xfs: move RT inode locking out of __xfs_bunmapi
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (32 preceding siblings ...)
2024-07-30 0:32 ` [PATCH 033/115] xfs: free RT extents after updating the bmap btree Darrick J. Wong
@ 2024-07-30 0:32 ` Darrick J. Wong
2024-07-30 0:32 ` [PATCH 035/115] xfs: split xfs_mod_freecounter Darrick J. Wong
` (80 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:32 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Dave Chinner, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: de37dbd0ccc6933fbf4bd7b3ccbc5ac640e80b28
__xfs_bunmapi is a bit of an odd place to lock the rtbitmap and rtsummary
inodes given that it is very high level code. While this only looks ugly
right now, it will become a problem when supporting delayed allocations
for RT inodes as __xfs_bunmapi might end up deleting only delalloc extents
and thus never unlock the rt inodes.
Move the locking into xfs_bmap_del_extent_real just before the call to
xfs_rtfree_blocks instead and use a new flag in the transaction to ensure
that the locking happens only once.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 15 ++++++++-------
libxfs/xfs_shared.h | 3 +++
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index dd91fb2aa..c16db82a2 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -5301,6 +5301,14 @@ xfs_bmap_del_extent_real(
if (xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK) {
xfs_refcount_decrease_extent(tp, del);
} else if (xfs_ifork_is_realtime(ip, whichfork)) {
+ /*
+ * Ensure the bitmap and summary inodes are locked
+ * and joined to the transaction before modifying them.
+ */
+ if (!(tp->t_flags & XFS_TRANS_RTBITMAP_LOCKED)) {
+ tp->t_flags |= XFS_TRANS_RTBITMAP_LOCKED;
+ xfs_rtbitmap_lock(tp, mp);
+ }
error = xfs_rtfree_blocks(tp, del->br_startblock,
del->br_blockcount);
} else {
@@ -5402,13 +5410,6 @@ __xfs_bunmapi(
} else
cur = NULL;
- if (isrt) {
- /*
- * Synchronize by locking the realtime bitmap.
- */
- xfs_rtbitmap_lock(tp, mp);
- }
-
extno = 0;
while (end != (xfs_fileoff_t)-1 && end >= start &&
(nexts == 0 || extno < nexts)) {
diff --git a/libxfs/xfs_shared.h b/libxfs/xfs_shared.h
index f35640ad3..34f104ed3 100644
--- a/libxfs/xfs_shared.h
+++ b/libxfs/xfs_shared.h
@@ -137,6 +137,9 @@ void xfs_log_get_max_trans_res(struct xfs_mount *mp,
*/
#define XFS_TRANS_LOWMODE (1u << 8)
+/* Transaction has locked the rtbitmap and rtsum inodes */
+#define XFS_TRANS_RTBITMAP_LOCKED (1u << 9)
+
/*
* Field values for xfs_trans_mod_sb.
*/
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 035/115] xfs: split xfs_mod_freecounter
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (33 preceding siblings ...)
2024-07-30 0:32 ` [PATCH 034/115] xfs: move RT inode locking out of __xfs_bunmapi Darrick J. Wong
@ 2024-07-30 0:32 ` Darrick J. Wong
2024-07-30 0:33 ` [PATCH 036/115] xfs: reinstate RT support in xfs_bmapi_reserve_delalloc Darrick J. Wong
` (79 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:32 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Dave Chinner, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: f30f656e25eb72c4309e76b16fa45062e183a2ee
xfs_mod_freecounter has two entirely separate code paths for adding or
subtracting from the free counters. Only the subtract case looks at the
rsvd flag and can return an error.
Split xfs_mod_freecounter into separate helpers for subtracting or
adding the freecounter, and remove all the impossible to reach error
handling for the addition case.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/libxfs_priv.h | 10 +++++++---
libxfs/xfs_ag.c | 4 +---
libxfs/xfs_ag_resv.c | 24 ++++++------------------
libxfs/xfs_ag_resv.h | 2 +-
libxfs/xfs_alloc.c | 4 ++--
libxfs/xfs_bmap.c | 23 ++++++++++++-----------
6 files changed, 29 insertions(+), 38 deletions(-)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index fa7cad0e0..40c418f54 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -541,10 +541,14 @@ struct xfs_buf *xfs_trans_buf_item_match(struct xfs_trans *,
struct xfs_buftarg *, struct xfs_buf_map *, int);
/* local source files */
-#define xfs_mod_fdblocks(mp, delta, rsvd) \
- libxfs_mod_incore_sb(mp, XFS_TRANS_SB_FDBLOCKS, delta, rsvd)
-#define xfs_mod_frextents(mp, delta) \
+#define xfs_add_fdblocks(mp, delta) \
+ libxfs_mod_incore_sb(mp, XFS_TRANS_SB_FDBLOCKS, delta, false)
+#define xfs_dec_fdblocks(mp, delta, rsvd) \
+ libxfs_mod_incore_sb(mp, XFS_TRANS_SB_FDBLOCKS, -(int64_t)(delta), rsvd)
+#define xfs_add_frextents(mp, delta) \
libxfs_mod_incore_sb(mp, XFS_TRANS_SB_FREXTENTS, delta, 0)
+#define xfs_dec_frextents(mp, delta) \
+ libxfs_mod_incore_sb(mp, XFS_TRANS_SB_FREXTENTS, -(int64_t)(delta), 0)
int libxfs_mod_incore_sb(struct xfs_mount *, int, int64_t, int);
/* percpu counters in mp are #defined to the superblock sb_ counters */
#define xfs_reinit_percpu_counters(mp)
diff --git a/libxfs/xfs_ag.c b/libxfs/xfs_ag.c
index ad721c192..47522d0fc 100644
--- a/libxfs/xfs_ag.c
+++ b/libxfs/xfs_ag.c
@@ -961,9 +961,7 @@ xfs_ag_shrink_space(
* Disable perag reservations so it doesn't cause the allocation request
* to fail. We'll reestablish reservation before we return.
*/
- error = xfs_ag_resv_free(pag);
- if (error)
- return error;
+ xfs_ag_resv_free(pag);
/* internal log shouldn't also show up in the free space btrees */
error = xfs_alloc_vextent_exact_bno(&args,
diff --git a/libxfs/xfs_ag_resv.c b/libxfs/xfs_ag_resv.c
index 3a80b1613..7e5cbe0cb 100644
--- a/libxfs/xfs_ag_resv.c
+++ b/libxfs/xfs_ag_resv.c
@@ -125,14 +125,13 @@ xfs_ag_resv_needed(
}
/* Clean out a reservation */
-static int
+static void
__xfs_ag_resv_free(
struct xfs_perag *pag,
enum xfs_ag_resv_type type)
{
struct xfs_ag_resv *resv;
xfs_extlen_t oldresv;
- int error;
trace_xfs_ag_resv_free(pag, type, 0);
@@ -148,30 +147,19 @@ __xfs_ag_resv_free(
oldresv = resv->ar_orig_reserved;
else
oldresv = resv->ar_reserved;
- error = xfs_mod_fdblocks(pag->pag_mount, oldresv, true);
+ xfs_add_fdblocks(pag->pag_mount, oldresv);
resv->ar_reserved = 0;
resv->ar_asked = 0;
resv->ar_orig_reserved = 0;
-
- if (error)
- trace_xfs_ag_resv_free_error(pag->pag_mount, pag->pag_agno,
- error, _RET_IP_);
- return error;
}
/* Free a per-AG reservation. */
-int
+void
xfs_ag_resv_free(
struct xfs_perag *pag)
{
- int error;
- int err2;
-
- error = __xfs_ag_resv_free(pag, XFS_AG_RESV_RMAPBT);
- err2 = __xfs_ag_resv_free(pag, XFS_AG_RESV_METADATA);
- if (err2 && !error)
- error = err2;
- return error;
+ __xfs_ag_resv_free(pag, XFS_AG_RESV_RMAPBT);
+ __xfs_ag_resv_free(pag, XFS_AG_RESV_METADATA);
}
static int
@@ -215,7 +203,7 @@ __xfs_ag_resv_init(
if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_AG_RESV_FAIL))
error = -ENOSPC;
else
- error = xfs_mod_fdblocks(mp, -(int64_t)hidden_space, true);
+ error = xfs_dec_fdblocks(mp, hidden_space, true);
if (error) {
trace_xfs_ag_resv_init_error(pag->pag_mount, pag->pag_agno,
error, _RET_IP_);
diff --git a/libxfs/xfs_ag_resv.h b/libxfs/xfs_ag_resv.h
index b74b21000..ff20ed93d 100644
--- a/libxfs/xfs_ag_resv.h
+++ b/libxfs/xfs_ag_resv.h
@@ -6,7 +6,7 @@
#ifndef __XFS_AG_RESV_H__
#define __XFS_AG_RESV_H__
-int xfs_ag_resv_free(struct xfs_perag *pag);
+void xfs_ag_resv_free(struct xfs_perag *pag);
int xfs_ag_resv_init(struct xfs_perag *pag, struct xfs_trans *tp);
bool xfs_ag_resv_critical(struct xfs_perag *pag, enum xfs_ag_resv_type type);
diff --git a/libxfs/xfs_alloc.c b/libxfs/xfs_alloc.c
index 0eefb16cc..b86f788f4 100644
--- a/libxfs/xfs_alloc.c
+++ b/libxfs/xfs_alloc.c
@@ -75,7 +75,7 @@ xfs_prealloc_blocks(
}
/*
- * The number of blocks per AG that we withhold from xfs_mod_fdblocks to
+ * The number of blocks per AG that we withhold from xfs_dec_fdblocks to
* guarantee that we can refill the AGFL prior to allocating space in a nearly
* full AG. Although the space described by the free space btrees, the
* blocks used by the freesp btrees themselves, and the blocks owned by the
@@ -85,7 +85,7 @@ xfs_prealloc_blocks(
* until the fs goes down, we subtract this many AG blocks from the incore
* fdblocks to ensure user allocation does not overcommit the space the
* filesystem needs for the AGFLs. The rmap btree uses a per-AG reservation to
- * withhold space from xfs_mod_fdblocks, so we do not account for that here.
+ * withhold space from xfs_dec_fdblocks, so we do not account for that here.
*/
#define XFS_ALLOCBT_AGFL_RESERVE 4
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index c16db82a2..3f70f24ba 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -1979,10 +1979,11 @@ xfs_bmap_add_extent_delay_real(
}
/* adjust for changes in reserved delayed indirect blocks */
- if (da_new != da_old) {
- ASSERT(state == 0 || da_new < da_old);
- error = xfs_mod_fdblocks(mp, (int64_t)(da_old - da_new),
- false);
+ if (da_new < da_old) {
+ xfs_add_fdblocks(mp, da_old - da_new);
+ } else if (da_new > da_old) {
+ ASSERT(state == 0);
+ error = xfs_dec_fdblocks(mp, da_new - da_old, false);
}
xfs_bmap_check_leaf_extents(bma->cur, bma->ip, whichfork);
@@ -2684,8 +2685,8 @@ xfs_bmap_add_extent_hole_delay(
}
if (oldlen != newlen) {
ASSERT(oldlen > newlen);
- xfs_mod_fdblocks(ip->i_mount, (int64_t)(oldlen - newlen),
- false);
+ xfs_add_fdblocks(ip->i_mount, oldlen - newlen);
+
/*
* Nothing to do for disk quota accounting here.
*/
@@ -4104,11 +4105,11 @@ xfs_bmapi_reserve_delalloc(
indlen = (xfs_extlen_t)xfs_bmap_worst_indlen(ip, alen);
ASSERT(indlen > 0);
- error = xfs_mod_fdblocks(mp, -((int64_t)alen), false);
+ error = xfs_dec_fdblocks(mp, alen, false);
if (error)
goto out_unreserve_quota;
- error = xfs_mod_fdblocks(mp, -((int64_t)indlen), false);
+ error = xfs_dec_fdblocks(mp, indlen, false);
if (error)
goto out_unreserve_blocks;
@@ -4136,7 +4137,7 @@ xfs_bmapi_reserve_delalloc(
return 0;
out_unreserve_blocks:
- xfs_mod_fdblocks(mp, alen, false);
+ xfs_add_fdblocks(mp, alen);
out_unreserve_quota:
if (XFS_IS_QUOTA_ON(mp))
xfs_quota_unreserve_blkres(ip, alen);
@@ -4922,7 +4923,7 @@ xfs_bmap_del_extent_delay(
ASSERT(got_endoff >= del_endoff);
if (isrt)
- xfs_mod_frextents(mp, xfs_rtb_to_rtx(mp, del->br_blockcount));
+ xfs_add_frextents(mp, xfs_rtb_to_rtx(mp, del->br_blockcount));
/*
* Update the inode delalloc counter now and wait to update the
@@ -5009,7 +5010,7 @@ xfs_bmap_del_extent_delay(
if (!isrt)
da_diff += del->br_blockcount;
if (da_diff) {
- xfs_mod_fdblocks(mp, da_diff, false);
+ xfs_add_fdblocks(mp, da_diff);
xfs_mod_delalloc(mp, -da_diff);
}
return error;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 036/115] xfs: reinstate RT support in xfs_bmapi_reserve_delalloc
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (34 preceding siblings ...)
2024-07-30 0:32 ` [PATCH 035/115] xfs: split xfs_mod_freecounter Darrick J. Wong
@ 2024-07-30 0:33 ` Darrick J. Wong
2024-07-30 0:33 ` [PATCH 037/115] xfs: cleanup fdblock/frextent accounting in xfs_bmap_del_extent_delay Darrick J. Wong
` (78 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:33 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Dave Chinner, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: dc1b17a25c321c8f1b4f90f9d6f8afb1d132b69c
Allocate data blocks for RT inodes using xfs_dec_frextents. While at
it optimize the data device case by doing only a single xfs_dec_fdblocks
call for the extent itself and the indirect blocks.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 3f70f24ba..1319f1c90 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4063,6 +4063,7 @@ xfs_bmapi_reserve_delalloc(
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
xfs_extlen_t alen;
xfs_extlen_t indlen;
+ uint64_t fdblocks;
int error;
xfs_fileoff_t aoff = off;
@@ -4105,14 +4106,18 @@ xfs_bmapi_reserve_delalloc(
indlen = (xfs_extlen_t)xfs_bmap_worst_indlen(ip, alen);
ASSERT(indlen > 0);
- error = xfs_dec_fdblocks(mp, alen, false);
- if (error)
- goto out_unreserve_quota;
+ fdblocks = indlen;
+ if (XFS_IS_REALTIME_INODE(ip)) {
+ error = xfs_dec_frextents(mp, xfs_rtb_to_rtx(mp, alen));
+ if (error)
+ goto out_unreserve_quota;
+ } else {
+ fdblocks += alen;
+ }
- error = xfs_dec_fdblocks(mp, indlen, false);
+ error = xfs_dec_fdblocks(mp, fdblocks, false);
if (error)
- goto out_unreserve_blocks;
-
+ goto out_unreserve_frextents;
ip->i_delayed_blks += alen;
xfs_mod_delalloc(ip->i_mount, alen + indlen);
@@ -4136,8 +4141,9 @@ xfs_bmapi_reserve_delalloc(
return 0;
-out_unreserve_blocks:
- xfs_add_fdblocks(mp, alen);
+out_unreserve_frextents:
+ if (XFS_IS_REALTIME_INODE(ip))
+ xfs_add_frextents(mp, xfs_rtb_to_rtx(mp, alen));
out_unreserve_quota:
if (XFS_IS_QUOTA_ON(mp))
xfs_quota_unreserve_blkres(ip, alen);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 037/115] xfs: cleanup fdblock/frextent accounting in xfs_bmap_del_extent_delay
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (35 preceding siblings ...)
2024-07-30 0:33 ` [PATCH 036/115] xfs: reinstate RT support in xfs_bmapi_reserve_delalloc Darrick J. Wong
@ 2024-07-30 0:33 ` Darrick J. Wong
2024-07-30 0:33 ` [PATCH 038/115] xfs: support RT inodes in xfs_mod_delalloc Darrick J. Wong
` (77 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:33 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Dave Chinner, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 7e77d57a1fea5f6bfe166210385ba9f227a606d1
The code to account fdblocks and frextents in xfs_bmap_del_extent_delay
is a bit weird in that it accounts frextents before the iext tree
manipulations and fdblocks after it. Given that the iext tree
manipulations cannot fail currently that's not really a problem, but
still odd. Move the frextent manipulation to the end, and use a
fdblocks variable to account of the unconditional indirect blocks and
the data blocks only freed for !RT. This prepares for following
updates in the area and already makes the code more readable.
Also remove the !isrt assert given that this code clearly handles
rt extents correctly, and we'll soon reinstate delalloc support for
RT inodes.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 1319f1c90..5de8c72a8 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4913,6 +4913,7 @@ xfs_bmap_del_extent_delay(
xfs_fileoff_t del_endoff, got_endoff;
xfs_filblks_t got_indlen, new_indlen, stolen;
uint32_t state = xfs_bmap_fork_to_state(whichfork);
+ uint64_t fdblocks;
int error = 0;
bool isrt;
@@ -4928,15 +4929,11 @@ xfs_bmap_del_extent_delay(
ASSERT(got->br_startoff <= del->br_startoff);
ASSERT(got_endoff >= del_endoff);
- if (isrt)
- xfs_add_frextents(mp, xfs_rtb_to_rtx(mp, del->br_blockcount));
-
/*
* Update the inode delalloc counter now and wait to update the
* sb counters as we might have to borrow some blocks for the
* indirect block accounting.
*/
- ASSERT(!isrt);
error = xfs_quota_unreserve_blkres(ip, del->br_blockcount);
if (error)
return error;
@@ -5013,12 +5010,15 @@ xfs_bmap_del_extent_delay(
ASSERT(da_old >= da_new);
da_diff = da_old - da_new;
- if (!isrt)
- da_diff += del->br_blockcount;
- if (da_diff) {
- xfs_add_fdblocks(mp, da_diff);
- xfs_mod_delalloc(mp, -da_diff);
- }
+ fdblocks = da_diff;
+
+ if (isrt)
+ xfs_add_frextents(mp, xfs_rtb_to_rtx(mp, del->br_blockcount));
+ else
+ fdblocks += del->br_blockcount;
+
+ xfs_add_fdblocks(mp, fdblocks);
+ xfs_mod_delalloc(mp, -(int64_t)fdblocks);
return error;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 038/115] xfs: support RT inodes in xfs_mod_delalloc
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (36 preceding siblings ...)
2024-07-30 0:33 ` [PATCH 037/115] xfs: cleanup fdblock/frextent accounting in xfs_bmap_del_extent_delay Darrick J. Wong
@ 2024-07-30 0:33 ` Darrick J. Wong
2024-07-30 0:33 ` [PATCH 039/115] xfs: rework splitting of indirect block reservations Darrick J. Wong
` (76 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:33 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Dave Chinner, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 7099bd0f243fa7511de6e95b0b8807ba7d3e5204
To prepare for re-enabling delalloc on RT devices, track the data blocks
(which use the RT device when the inode sits on it) and the indirect
blocks (which don't) separately to xfs_mod_delalloc, and add a new
percpu counter to also track the RT delalloc blocks.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/libxfs_priv.h | 2 +-
libxfs/xfs_bmap.c | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index 40c418f54..cfe96b05a 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -141,7 +141,7 @@ enum ce { CE_DEBUG, CE_CONT, CE_NOTE, CE_WARN, CE_ALERT, CE_PANIC };
#define xfs_force_shutdown(d,n) ((void) 0)
-#define xfs_mod_delalloc(a,b) ((void) 0)
+#define xfs_mod_delalloc(a,b,c) ((void) 0)
/* stop unused var warnings by assigning mp to itself */
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 5de8c72a8..79cde87d0 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -1971,7 +1971,7 @@ xfs_bmap_add_extent_delay_real(
}
if (da_new != da_old)
- xfs_mod_delalloc(mp, (int64_t)da_new - da_old);
+ xfs_mod_delalloc(bma->ip, 0, (int64_t)da_new - da_old);
if (bma->cur) {
da_new += bma->cur->bc_bmap.allocated;
@@ -2690,7 +2690,7 @@ xfs_bmap_add_extent_hole_delay(
/*
* Nothing to do for disk quota accounting here.
*/
- xfs_mod_delalloc(ip->i_mount, (int64_t)newlen - oldlen);
+ xfs_mod_delalloc(ip, 0, (int64_t)newlen - oldlen);
}
}
@@ -3367,7 +3367,7 @@ xfs_bmap_alloc_account(
* yet.
*/
if (ap->wasdel) {
- xfs_mod_delalloc(ap->ip->i_mount, -(int64_t)ap->length);
+ xfs_mod_delalloc(ap->ip, -(int64_t)ap->length, 0);
return;
}
@@ -3391,7 +3391,7 @@ xfs_bmap_alloc_account(
xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
if (ap->wasdel) {
ap->ip->i_delayed_blks -= ap->length;
- xfs_mod_delalloc(ap->ip->i_mount, -(int64_t)ap->length);
+ xfs_mod_delalloc(ap->ip, -(int64_t)ap->length, 0);
fld = isrt ? XFS_TRANS_DQ_DELRTBCOUNT : XFS_TRANS_DQ_DELBCOUNT;
} else {
fld = isrt ? XFS_TRANS_DQ_RTBCOUNT : XFS_TRANS_DQ_BCOUNT;
@@ -4120,7 +4120,7 @@ xfs_bmapi_reserve_delalloc(
goto out_unreserve_frextents;
ip->i_delayed_blks += alen;
- xfs_mod_delalloc(ip->i_mount, alen + indlen);
+ xfs_mod_delalloc(ip, alen, indlen);
got->br_startoff = aoff;
got->br_startblock = nullstartblock(indlen);
@@ -5018,7 +5018,7 @@ xfs_bmap_del_extent_delay(
fdblocks += del->br_blockcount;
xfs_add_fdblocks(mp, fdblocks);
- xfs_mod_delalloc(mp, -(int64_t)fdblocks);
+ xfs_mod_delalloc(ip, -(int64_t)del->br_blockcount, -da_diff);
return error;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 039/115] xfs: rework splitting of indirect block reservations
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (37 preceding siblings ...)
2024-07-30 0:33 ` [PATCH 038/115] xfs: support RT inodes in xfs_mod_delalloc Darrick J. Wong
@ 2024-07-30 0:33 ` Darrick J. Wong
2024-07-30 0:34 ` [PATCH 040/115] xfs: stop the steal (of data blocks for RT indirect blocks) Darrick J. Wong
` (75 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:33 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Dave Chinner, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: da2b9c3a8d2cbdeec3f13cebf4c6c86c13e1077e
Move the check if we have enough indirect blocks and the stealing of
the deleted extent blocks out of xfs_bmap_split_indlen and into the
caller to prepare for handling delayed allocation of RT extents that
can't easily be stolen.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 38 ++++++++++++++++----------------------
1 file changed, 16 insertions(+), 22 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 79cde87d0..7b18477e0 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4825,31 +4825,17 @@ xfs_bmapi_remap(
* ores == 1). The number of stolen blocks is returned. The availability and
* subsequent accounting of stolen blocks is the responsibility of the caller.
*/
-static xfs_filblks_t
+static void
xfs_bmap_split_indlen(
xfs_filblks_t ores, /* original res. */
xfs_filblks_t *indlen1, /* ext1 worst indlen */
- xfs_filblks_t *indlen2, /* ext2 worst indlen */
- xfs_filblks_t avail) /* stealable blocks */
+ xfs_filblks_t *indlen2) /* ext2 worst indlen */
{
xfs_filblks_t len1 = *indlen1;
xfs_filblks_t len2 = *indlen2;
xfs_filblks_t nres = len1 + len2; /* new total res. */
- xfs_filblks_t stolen = 0;
xfs_filblks_t resfactor;
- /*
- * Steal as many blocks as we can to try and satisfy the worst case
- * indlen for both new extents.
- */
- if (ores < nres && avail)
- stolen = XFS_FILBLKS_MIN(nres - ores, avail);
- ores += stolen;
-
- /* nothing else to do if we've satisfied the new reservation */
- if (ores >= nres)
- return stolen;
-
/*
* We can't meet the total required reservation for the two extents.
* Calculate the percent of the overall shortage between both extents
@@ -4894,8 +4880,6 @@ xfs_bmap_split_indlen(
*indlen1 = len1;
*indlen2 = len2;
-
- return stolen;
}
int
@@ -4911,7 +4895,7 @@ xfs_bmap_del_extent_delay(
struct xfs_bmbt_irec new;
int64_t da_old, da_new, da_diff = 0;
xfs_fileoff_t del_endoff, got_endoff;
- xfs_filblks_t got_indlen, new_indlen, stolen;
+ xfs_filblks_t got_indlen, new_indlen, stolen = 0;
uint32_t state = xfs_bmap_fork_to_state(whichfork);
uint64_t fdblocks;
int error = 0;
@@ -4990,8 +4974,19 @@ xfs_bmap_del_extent_delay(
new_indlen = xfs_bmap_worst_indlen(ip, new.br_blockcount);
WARN_ON_ONCE(!got_indlen || !new_indlen);
- stolen = xfs_bmap_split_indlen(da_old, &got_indlen, &new_indlen,
- del->br_blockcount);
+ /*
+ * Steal as many blocks as we can to try and satisfy the worst
+ * case indlen for both new extents.
+ */
+ da_new = got_indlen + new_indlen;
+ if (da_new > da_old) {
+ stolen = XFS_FILBLKS_MIN(da_new - da_old,
+ del->br_blockcount);
+ da_old += stolen;
+ }
+ if (da_new > da_old)
+ xfs_bmap_split_indlen(da_old, &got_indlen, &new_indlen);
+ da_new = got_indlen + new_indlen;
got->br_startblock = nullstartblock((int)got_indlen);
@@ -5003,7 +4998,6 @@ xfs_bmap_del_extent_delay(
xfs_iext_next(ifp, icur);
xfs_iext_insert(ip, icur, &new, state);
- da_new = got_indlen + new_indlen - stolen;
del->br_blockcount -= stolen;
break;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 040/115] xfs: stop the steal (of data blocks for RT indirect blocks)
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (38 preceding siblings ...)
2024-07-30 0:33 ` [PATCH 039/115] xfs: rework splitting of indirect block reservations Darrick J. Wong
@ 2024-07-30 0:34 ` Darrick J. Wong
2024-07-30 0:34 ` [PATCH 041/115] xfs: remove XFS_DA_OP_REMOVE Darrick J. Wong
` (74 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:34 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Dave Chinner, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: bd1753d8c42b6bd5d9a81c81d1ce6e3affe3a59f
When xfs_bmap_del_extent_delay has to split an indirect block it tries
to steal blocks from the the part that gets unmapped to increase the
indirect block reservation that now needs to cover for two extents
instead of one.
This works perfectly fine on the data device, where the data and
indirect blocks come from the same pool. It has no chance of working
when the inode sits on the RT device. To support re-enabling delalloc
for inodes on the RT device, make this behavior conditional on not
being for rt extents.
Note that split of delalloc extents should only happen on writeback
failure, as for other kinds of hole punching we first write back all
data and thus convert the delalloc reservations covering the hole to
a real allocation.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 7b18477e0..81dccf275 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4977,9 +4977,14 @@ xfs_bmap_del_extent_delay(
/*
* Steal as many blocks as we can to try and satisfy the worst
* case indlen for both new extents.
+ *
+ * However, we can't just steal reservations from the data
+ * blocks if this is an RT inodes as the data and metadata
+ * blocks come from different pools. We'll have to live with
+ * under-filled indirect reservation in this case.
*/
da_new = got_indlen + new_indlen;
- if (da_new > da_old) {
+ if (da_new > da_old && !isrt) {
stolen = XFS_FILBLKS_MIN(da_new - da_old,
del->br_blockcount);
da_old += stolen;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 041/115] xfs: remove XFS_DA_OP_REMOVE
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (39 preceding siblings ...)
2024-07-30 0:34 ` [PATCH 040/115] xfs: stop the steal (of data blocks for RT indirect blocks) Darrick J. Wong
@ 2024-07-30 0:34 ` Darrick J. Wong
2024-07-30 0:34 ` [PATCH 042/115] xfs: remove XFS_DA_OP_NOTIME Darrick J. Wong
` (73 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:34 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: f566d5b9fb7136d39d4e9c54d84c82835b539b4e
Nobody checks this flag, so get rid of it.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.h | 1 -
libxfs/xfs_da_btree.h | 6 ++----
2 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index e4f550085..670ab2a61 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -590,7 +590,6 @@ xfs_attr_init_add_state(struct xfs_da_args *args)
static inline enum xfs_delattr_state
xfs_attr_init_remove_state(struct xfs_da_args *args)
{
- args->op_flags |= XFS_DA_OP_REMOVE;
if (xfs_attr_is_shortform(args->dp))
return XFS_DAS_SF_REMOVE;
if (xfs_attr_is_leaf(args->dp))
diff --git a/libxfs/xfs_da_btree.h b/libxfs/xfs_da_btree.h
index 7a004786e..76e764080 100644
--- a/libxfs/xfs_da_btree.h
+++ b/libxfs/xfs_da_btree.h
@@ -91,9 +91,8 @@ typedef struct xfs_da_args {
#define XFS_DA_OP_OKNOENT (1u << 3) /* lookup op, ENOENT ok, else die */
#define XFS_DA_OP_CILOOKUP (1u << 4) /* lookup returns CI name if found */
#define XFS_DA_OP_NOTIME (1u << 5) /* don't update inode timestamps */
-#define XFS_DA_OP_REMOVE (1u << 6) /* this is a remove operation */
-#define XFS_DA_OP_RECOVERY (1u << 7) /* Log recovery operation */
-#define XFS_DA_OP_LOGGED (1u << 8) /* Use intent items to track op */
+#define XFS_DA_OP_RECOVERY (1u << 6) /* Log recovery operation */
+#define XFS_DA_OP_LOGGED (1u << 7) /* Use intent items to track op */
#define XFS_DA_OP_FLAGS \
{ XFS_DA_OP_JUSTCHECK, "JUSTCHECK" }, \
@@ -102,7 +101,6 @@ typedef struct xfs_da_args {
{ XFS_DA_OP_OKNOENT, "OKNOENT" }, \
{ XFS_DA_OP_CILOOKUP, "CILOOKUP" }, \
{ XFS_DA_OP_NOTIME, "NOTIME" }, \
- { XFS_DA_OP_REMOVE, "REMOVE" }, \
{ XFS_DA_OP_RECOVERY, "RECOVERY" }, \
{ XFS_DA_OP_LOGGED, "LOGGED" }
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 042/115] xfs: remove XFS_DA_OP_NOTIME
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (40 preceding siblings ...)
2024-07-30 0:34 ` [PATCH 041/115] xfs: remove XFS_DA_OP_REMOVE Darrick J. Wong
@ 2024-07-30 0:34 ` Darrick J. Wong
2024-07-30 0:34 ` [PATCH 043/115] xfs: remove xfs_da_args.attr_flags Darrick J. Wong
` (72 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:34 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 779a4b606c7678343e3603398fe07de38b817ef4
The only user of this flag sets it prior to an xfs_attr_get_ilocked
call, which doesn't update anything. Get rid of the flag.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 5 ++---
libxfs/xfs_da_btree.h | 6 ++----
2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 07f873927..958c6d720 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -363,7 +363,7 @@ xfs_attr_try_sf_addname(
* Commit the shortform mods, and we're done.
* NOTE: this is also the error path (EEXIST, etc).
*/
- if (!error && !(args->op_flags & XFS_DA_OP_NOTIME))
+ if (!error)
xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
if (xfs_has_wsync(dp->i_mount))
@@ -1031,8 +1031,7 @@ xfs_attr_set(
if (xfs_has_wsync(mp))
xfs_trans_set_sync(args->trans);
- if (!(args->op_flags & XFS_DA_OP_NOTIME))
- xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
+ xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
/*
* Commit the last in the sequence of transactions.
diff --git a/libxfs/xfs_da_btree.h b/libxfs/xfs_da_btree.h
index 76e764080..b04a3290f 100644
--- a/libxfs/xfs_da_btree.h
+++ b/libxfs/xfs_da_btree.h
@@ -90,9 +90,8 @@ typedef struct xfs_da_args {
#define XFS_DA_OP_ADDNAME (1u << 2) /* this is an add operation */
#define XFS_DA_OP_OKNOENT (1u << 3) /* lookup op, ENOENT ok, else die */
#define XFS_DA_OP_CILOOKUP (1u << 4) /* lookup returns CI name if found */
-#define XFS_DA_OP_NOTIME (1u << 5) /* don't update inode timestamps */
-#define XFS_DA_OP_RECOVERY (1u << 6) /* Log recovery operation */
-#define XFS_DA_OP_LOGGED (1u << 7) /* Use intent items to track op */
+#define XFS_DA_OP_RECOVERY (1u << 5) /* Log recovery operation */
+#define XFS_DA_OP_LOGGED (1u << 6) /* Use intent items to track op */
#define XFS_DA_OP_FLAGS \
{ XFS_DA_OP_JUSTCHECK, "JUSTCHECK" }, \
@@ -100,7 +99,6 @@ typedef struct xfs_da_args {
{ XFS_DA_OP_ADDNAME, "ADDNAME" }, \
{ XFS_DA_OP_OKNOENT, "OKNOENT" }, \
{ XFS_DA_OP_CILOOKUP, "CILOOKUP" }, \
- { XFS_DA_OP_NOTIME, "NOTIME" }, \
{ XFS_DA_OP_RECOVERY, "RECOVERY" }, \
{ XFS_DA_OP_LOGGED, "LOGGED" }
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 043/115] xfs: remove xfs_da_args.attr_flags
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (41 preceding siblings ...)
2024-07-30 0:34 ` [PATCH 042/115] xfs: remove XFS_DA_OP_NOTIME Darrick J. Wong
@ 2024-07-30 0:34 ` Darrick J. Wong
2024-07-30 0:35 ` [PATCH 044/115] xfs: make attr removal an explicit operation Darrick J. Wong
` (71 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:34 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 54275d8496f3e4764302cebc0e9517d950ba6589
This field only ever contains XATTR_{CREATE,REPLACE}, and it only goes
as deep as xfs_attr_set. Remove the field from the structure and
replace it with an enum specifying exactly what kind of change we want
to make to the xattr structure. Upsert is the name that we'll give to
the flags==0 operation, because we're either updating an existing value
or inserting it, and the caller doesn't care.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/attrset.c | 11 +++++------
libxfs/xfs_attr.c | 7 ++++---
libxfs/xfs_attr.h | 9 ++++++++-
libxfs/xfs_da_btree.h | 1 -
4 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/db/attrset.c b/db/attrset.c
index 0d8d70a84..736482fea 100644
--- a/db/attrset.c
+++ b/db/attrset.c
@@ -69,6 +69,7 @@ attr_set_f(
{
struct xfs_da_args args = { };
char *sp;
+ enum xfs_attr_update op = XFS_ATTRUPDATE_UPSERTR;
int c;
if (cur_typ == NULL) {
@@ -98,12 +99,10 @@ attr_set_f(
/* modifiers */
case 'C':
- args.attr_flags |= XATTR_CREATE;
- args.attr_flags &= ~XATTR_REPLACE;
+ op = XFS_ATTRUPDATE_CREATE;
break;
case 'R':
- args.attr_flags |= XATTR_REPLACE;
- args.attr_flags &= ~XATTR_CREATE;
+ op = XFS_ATTRUPDATE_REPLACE;
break;
case 'n':
@@ -162,7 +161,7 @@ attr_set_f(
goto out;
}
- if (libxfs_attr_set(&args)) {
+ if (libxfs_attr_set(&args, op)) {
dbprintf(_("failed to set attr %s on inode %llu\n"),
args.name, (unsigned long long)iocur_top->ino);
goto out;
@@ -248,7 +247,7 @@ attr_remove_f(
goto out;
}
- if (libxfs_attr_set(&args)) {
+ if (libxfs_attr_set(&args, XFS_ATTRUPDATE_UPSERTR)) {
dbprintf(_("failed to remove attr %s from inode %llu\n"),
(unsigned char *)args.name,
(unsigned long long)iocur_top->ino);
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 958c6d720..9a6787624 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -920,7 +920,8 @@ xfs_attr_defer_add(
*/
int
xfs_attr_set(
- struct xfs_da_args *args)
+ struct xfs_da_args *args,
+ enum xfs_attr_update op)
{
struct xfs_inode *dp = args->dp;
struct xfs_mount *mp = dp->i_mount;
@@ -1006,7 +1007,7 @@ xfs_attr_set(
}
/* Pure create fails if the attr already exists */
- if (args->attr_flags & XATTR_CREATE)
+ if (op == XFS_ATTRUPDATE_CREATE)
goto out_trans_cancel;
xfs_attr_defer_add(args, XFS_ATTRI_OP_FLAGS_REPLACE);
break;
@@ -1016,7 +1017,7 @@ xfs_attr_set(
goto out_trans_cancel;
/* Pure replace fails if no existing attr to replace. */
- if (args->attr_flags & XATTR_REPLACE)
+ if (op == XFS_ATTRUPDATE_REPLACE)
goto out_trans_cancel;
xfs_attr_defer_add(args, XFS_ATTRI_OP_FLAGS_SET);
break;
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index 670ab2a61..228360f7c 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -544,7 +544,14 @@ int xfs_inode_hasattr(struct xfs_inode *ip);
bool xfs_attr_is_leaf(struct xfs_inode *ip);
int xfs_attr_get_ilocked(struct xfs_da_args *args);
int xfs_attr_get(struct xfs_da_args *args);
-int xfs_attr_set(struct xfs_da_args *args);
+
+enum xfs_attr_update {
+ XFS_ATTRUPDATE_UPSERTR, /* set/remove value, replace any existing attr */
+ XFS_ATTRUPDATE_CREATE, /* set value, fail if attr already exists */
+ XFS_ATTRUPDATE_REPLACE, /* set value, fail if attr does not exist */
+};
+
+int xfs_attr_set(struct xfs_da_args *args, enum xfs_attr_update op);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
bool xfs_attr_namecheck(const void *name, size_t length);
diff --git a/libxfs/xfs_da_btree.h b/libxfs/xfs_da_btree.h
index b04a3290f..706b529a8 100644
--- a/libxfs/xfs_da_btree.h
+++ b/libxfs/xfs_da_btree.h
@@ -60,7 +60,6 @@ typedef struct xfs_da_args {
void *value; /* set of bytes (maybe contain NULLs) */
int valuelen; /* length of value */
unsigned int attr_filter; /* XFS_ATTR_{ROOT,SECURE,INCOMPLETE} */
- unsigned int attr_flags; /* XATTR_{CREATE,REPLACE} */
xfs_dahash_t hashval; /* hash value of name */
xfs_ino_t inumber; /* input/output inode number */
struct xfs_inode *dp; /* directory inode to manipulate */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 044/115] xfs: make attr removal an explicit operation
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (42 preceding siblings ...)
2024-07-30 0:34 ` [PATCH 043/115] xfs: remove xfs_da_args.attr_flags Darrick J. Wong
@ 2024-07-30 0:35 ` Darrick J. Wong
2024-07-30 0:35 ` [PATCH 045/115] xfs: rearrange xfs_da_args a bit to use less space Darrick J. Wong
` (70 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:35 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: c27411d4c640037d70e2fa5751616e175e52ca5e
Parent pointers match attrs on name+value, unlike everything else which
matches on only the name. Therefore, we cannot keep using the heuristic
that !value means remove. Make this an explicit operation code.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/attrset.c | 4 ++--
libxfs/xfs_attr.c | 19 ++++++++++---------
libxfs/xfs_attr.h | 3 ++-
3 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/db/attrset.c b/db/attrset.c
index 736482fea..a59d5473e 100644
--- a/db/attrset.c
+++ b/db/attrset.c
@@ -69,7 +69,7 @@ attr_set_f(
{
struct xfs_da_args args = { };
char *sp;
- enum xfs_attr_update op = XFS_ATTRUPDATE_UPSERTR;
+ enum xfs_attr_update op = XFS_ATTRUPDATE_UPSERT;
int c;
if (cur_typ == NULL) {
@@ -247,7 +247,7 @@ attr_remove_f(
goto out;
}
- if (libxfs_attr_set(&args, XFS_ATTRUPDATE_UPSERTR)) {
+ if (libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE)) {
dbprintf(_("failed to remove attr %s from inode %llu\n"),
(unsigned char *)args.name,
(unsigned long long)iocur_top->ino);
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 9a6787624..5249f9be0 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -914,10 +914,6 @@ xfs_attr_defer_add(
trace_xfs_attr_defer_add(new->xattri_dela_state, args->dp);
}
-/*
- * Note: If args->value is NULL the attribute will be removed, just like the
- * Linux ->setattr API.
- */
int
xfs_attr_set(
struct xfs_da_args *args,
@@ -953,7 +949,10 @@ xfs_attr_set(
args->op_flags = XFS_DA_OP_OKNOENT |
(args->op_flags & XFS_DA_OP_LOGGED);
- if (args->value) {
+ switch (op) {
+ case XFS_ATTRUPDATE_UPSERT:
+ case XFS_ATTRUPDATE_CREATE:
+ case XFS_ATTRUPDATE_REPLACE:
XFS_STATS_INC(mp, xs_attr_set);
args->total = xfs_attr_calc_size(args, &local);
@@ -973,9 +972,11 @@ xfs_attr_set(
if (!local)
rmt_blks = xfs_attr3_rmt_blocks(mp, args->valuelen);
- } else {
+ break;
+ case XFS_ATTRUPDATE_REMOVE:
XFS_STATS_INC(mp, xs_attr_remove);
rmt_blks = xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX);
+ break;
}
/*
@@ -987,7 +988,7 @@ xfs_attr_set(
if (error)
return error;
- if (args->value || xfs_inode_hasattr(dp)) {
+ if (op != XFS_ATTRUPDATE_REMOVE || xfs_inode_hasattr(dp)) {
error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK,
XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
if (error == -EFBIG)
@@ -1000,7 +1001,7 @@ xfs_attr_set(
error = xfs_attr_lookup(args);
switch (error) {
case -EEXIST:
- if (!args->value) {
+ if (op == XFS_ATTRUPDATE_REMOVE) {
/* if no value, we are performing a remove operation */
xfs_attr_defer_add(args, XFS_ATTRI_OP_FLAGS_REMOVE);
break;
@@ -1013,7 +1014,7 @@ xfs_attr_set(
break;
case -ENOATTR:
/* Can't remove what isn't there. */
- if (!args->value)
+ if (op == XFS_ATTRUPDATE_REMOVE)
goto out_trans_cancel;
/* Pure replace fails if no existing attr to replace. */
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index 228360f7c..c8005f521 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -546,7 +546,8 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
int xfs_attr_get(struct xfs_da_args *args);
enum xfs_attr_update {
- XFS_ATTRUPDATE_UPSERTR, /* set/remove value, replace any existing attr */
+ XFS_ATTRUPDATE_REMOVE, /* remove attr */
+ XFS_ATTRUPDATE_UPSERT, /* set value, replace any existing attr */
XFS_ATTRUPDATE_CREATE, /* set value, fail if attr already exists */
XFS_ATTRUPDATE_REPLACE, /* set value, fail if attr does not exist */
};
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 045/115] xfs: rearrange xfs_da_args a bit to use less space
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (43 preceding siblings ...)
2024-07-30 0:35 ` [PATCH 044/115] xfs: make attr removal an explicit operation Darrick J. Wong
@ 2024-07-30 0:35 ` Darrick J. Wong
2024-07-30 0:35 ` [PATCH 046/115] xfs: attr fork iext must be loaded before calling xfs_attr_is_leaf Darrick J. Wong
` (69 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:35 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: cda60317ac57add7a0a2865aa29afbc6caad3e9a
A few notes about struct xfs_da_args:
The XFS_ATTR_* flags only go up as far as XFS_ATTR_INCOMPLETE, which
means that attr_filter could be a u8 field.
I've reduced the number of XFS_DA_OP_* flags down to the point where
op_flags would also fit into a u8.
filetype has 7 bytes of slack after it, which is wasteful.
namelen will never be greater than MAXNAMELEN, which is 256. This field
could be reduced to a short.
Rearrange the fields in xfs_da_args to waste less space. This reduces
the structure size from 136 bytes to 128. Later when we add extra
fields to support parent pointer replacement, this will only bloat the
structure to 144 bytes, instead of 168.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_da_btree.h | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/libxfs/xfs_da_btree.h b/libxfs/xfs_da_btree.h
index 706b529a8..17cef594b 100644
--- a/libxfs/xfs_da_btree.h
+++ b/libxfs/xfs_da_btree.h
@@ -54,16 +54,20 @@ enum xfs_dacmp {
*/
typedef struct xfs_da_args {
struct xfs_da_geometry *geo; /* da block geometry */
- const uint8_t *name; /* string (maybe not NULL terminated) */
- int namelen; /* length of string (maybe no NULL) */
- uint8_t filetype; /* filetype of inode for directories */
+ const uint8_t *name; /* string (maybe not NULL terminated) */
void *value; /* set of bytes (maybe contain NULLs) */
- int valuelen; /* length of value */
- unsigned int attr_filter; /* XFS_ATTR_{ROOT,SECURE,INCOMPLETE} */
- xfs_dahash_t hashval; /* hash value of name */
- xfs_ino_t inumber; /* input/output inode number */
struct xfs_inode *dp; /* directory inode to manipulate */
struct xfs_trans *trans; /* current trans (changes over time) */
+
+ xfs_ino_t inumber; /* input/output inode number */
+ xfs_ino_t owner; /* inode that owns the dir/attr data */
+
+ int valuelen; /* length of value */
+ uint8_t filetype; /* filetype of inode for directories */
+ uint8_t op_flags; /* operation flags */
+ uint8_t attr_filter; /* XFS_ATTR_{ROOT,SECURE,INCOMPLETE} */
+ short namelen; /* length of string (maybe no NULL) */
+ xfs_dahash_t hashval; /* hash value of name */
xfs_extlen_t total; /* total blocks needed, for 1st bmap */
int whichfork; /* data or attribute fork */
xfs_dablk_t blkno; /* blkno of attr leaf of interest */
@@ -76,9 +80,7 @@ typedef struct xfs_da_args {
xfs_dablk_t rmtblkno2; /* remote attr value starting blkno */
int rmtblkcnt2; /* remote attr value block count */
int rmtvaluelen2; /* remote attr value length in bytes */
- uint32_t op_flags; /* operation flags */
enum xfs_dacmp cmpresult; /* name compare result for lookups */
- xfs_ino_t owner; /* inode that owns the dir/attr data */
} xfs_da_args_t;
/*
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 046/115] xfs: attr fork iext must be loaded before calling xfs_attr_is_leaf
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (44 preceding siblings ...)
2024-07-30 0:35 ` [PATCH 045/115] xfs: rearrange xfs_da_args a bit to use less space Darrick J. Wong
@ 2024-07-30 0:35 ` Darrick J. Wong
2024-07-30 0:36 ` [PATCH 047/115] xfs: fix missing check for invalid attr flags Darrick J. Wong
` (68 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:35 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: ef80de940a6344da1d4f12c948a0ad4d6ff6e841
Christoph noticed that the xfs_attr_is_leaf in xfs_attr_get_ilocked can
access the incore extent tree of the attr fork, but nothing in the
xfs_attr_get path guarantees that the incore tree is actually loaded.
Most of the time it is, but seeing as xfs_attr_is_leaf ignores the
return value of xfs_iext_get_extent I guess we've been making choices
based on random stack contents and nobody's complained?
Reported-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 5249f9be0..8e9e23836 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -85,6 +85,8 @@ xfs_attr_is_leaf(
struct xfs_iext_cursor icur;
struct xfs_bmbt_irec imap;
+ ASSERT(!xfs_need_iread_extents(ifp));
+
if (ifp->if_nextents != 1 || ifp->if_format != XFS_DINODE_FMT_EXTENTS)
return false;
@@ -222,11 +224,21 @@ int
xfs_attr_get_ilocked(
struct xfs_da_args *args)
{
+ int error;
+
xfs_assert_ilocked(args->dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL);
if (!xfs_inode_hasattr(args->dp))
return -ENOATTR;
+ /*
+ * The incore attr fork iext tree must be loaded for xfs_attr_is_leaf
+ * to work correctly.
+ */
+ error = xfs_iread_extents(args->trans, args->dp, XFS_ATTR_FORK);
+ if (error)
+ return error;
+
if (args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL)
return xfs_attr_shortform_getvalue(args);
if (xfs_attr_is_leaf(args->dp))
@@ -868,6 +880,11 @@ xfs_attr_lookup(
return -ENOATTR;
}
+ /* Prerequisite for xfs_attr_is_leaf */
+ error = xfs_iread_extents(args->trans, args->dp, XFS_ATTR_FORK);
+ if (error)
+ return error;
+
if (xfs_attr_is_leaf(dp)) {
error = xfs_attr_leaf_hasname(args, &bp);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 047/115] xfs: fix missing check for invalid attr flags
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (45 preceding siblings ...)
2024-07-30 0:35 ` [PATCH 046/115] xfs: attr fork iext must be loaded before calling xfs_attr_is_leaf Darrick J. Wong
@ 2024-07-30 0:36 ` Darrick J. Wong
2024-07-30 0:36 ` [PATCH 048/115] xfs: restructure xfs_attr_complete_op a bit Darrick J. Wong
` (67 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:36 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: f660ec8eaeb50d0317c29601aacabdb15e5f2203
The xattr scrubber doesn't check for undefined flags in shortform attr
entries. Therefore, define a mask XFS_ATTR_ONDISK_MASK that has all
possible XFS_ATTR_* flags in it, and use that to check for unknown bits
in xchk_xattr_actor.
Refactor the check in the dabtree scanner function to use the new mask
as well. The redundant checks need to be in place because the dabtree
check examines the hash mappings and therefore needs to decode the attr
leaf entries to compute the namehash. This happens before the walk of
the xattr entries themselves.
Fixes: ae0506eba78fd ("xfs: check used space of shortform xattr structures")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_da_format.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index aac3fe039..ecd0616f5 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -719,8 +719,13 @@ struct xfs_attr3_leafblock {
#define XFS_ATTR_ROOT (1u << XFS_ATTR_ROOT_BIT)
#define XFS_ATTR_SECURE (1u << XFS_ATTR_SECURE_BIT)
#define XFS_ATTR_INCOMPLETE (1u << XFS_ATTR_INCOMPLETE_BIT)
+
#define XFS_ATTR_NSP_ONDISK_MASK (XFS_ATTR_ROOT | XFS_ATTR_SECURE)
+#define XFS_ATTR_ONDISK_MASK (XFS_ATTR_NSP_ONDISK_MASK | \
+ XFS_ATTR_LOCAL | \
+ XFS_ATTR_INCOMPLETE)
+
#define XFS_ATTR_NAMESPACE_STR \
{ XFS_ATTR_LOCAL, "local" }, \
{ XFS_ATTR_ROOT, "root" }, \
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 048/115] xfs: restructure xfs_attr_complete_op a bit
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (46 preceding siblings ...)
2024-07-30 0:36 ` [PATCH 047/115] xfs: fix missing check for invalid attr flags Darrick J. Wong
@ 2024-07-30 0:36 ` Darrick J. Wong
2024-07-30 0:36 ` [PATCH 049/115] xfs: use helpers to extract xattr op from opflags Darrick J. Wong
` (66 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:36 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 992c3b5c3fe6f42778436649ddae2b7a2984b7aa
Eliminate the local variable from this function so that we can
streamline things a bit later when we add the PPTR_REPLACE op code.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 8e9e23836..26674116f 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -430,14 +430,13 @@ xfs_attr_complete_op(
enum xfs_delattr_state replace_state)
{
struct xfs_da_args *args = attr->xattri_da_args;
- bool do_replace = args->op_flags & XFS_DA_OP_REPLACE;
+
+ if (!(args->op_flags & XFS_DA_OP_REPLACE))
+ replace_state = XFS_DAS_DONE;
args->op_flags &= ~XFS_DA_OP_REPLACE;
args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
- if (do_replace)
- return replace_state;
-
- return XFS_DAS_DONE;
+ return replace_state;
}
static int
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 049/115] xfs: use helpers to extract xattr op from opflags
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (47 preceding siblings ...)
2024-07-30 0:36 ` [PATCH 048/115] xfs: restructure xfs_attr_complete_op a bit Darrick J. Wong
@ 2024-07-30 0:36 ` Darrick J. Wong
2024-07-30 0:36 ` [PATCH 050/115] xfs: enforce one namespace per attribute Darrick J. Wong
` (65 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:36 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 2a2c05d013d0562076ec475a6deb0991ce1942ca
Create helper functions to extract the xattr op from the ondisk xattri
log item and the incore attr intent item. These will get more use in
the patches that follow.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index c8005f521..79b457adb 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -529,6 +529,11 @@ struct xfs_attr_intent {
struct xfs_bmbt_irec xattri_map;
};
+static inline unsigned int
+xfs_attr_intent_op(const struct xfs_attr_intent *attr)
+{
+ return attr->xattri_op_flags & XFS_ATTRI_OP_FLAGS_TYPE_MASK;
+}
/*========================================================================
* Function prototypes for the kernel.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 050/115] xfs: enforce one namespace per attribute
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (48 preceding siblings ...)
2024-07-30 0:36 ` [PATCH 049/115] xfs: use helpers to extract xattr op from opflags Darrick J. Wong
@ 2024-07-30 0:36 ` Darrick J. Wong
2024-07-30 0:37 ` [PATCH 051/115] xfs: rearrange xfs_attr_match parameters Darrick J. Wong
` (64 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:36 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: ea0b3e814741fb64e7785b564ea619578058e0b0
Create a standardized helper function to enforce one namespace bit per
extended attribute, and refactor all the open-coded hweight logic. This
function is not a static inline to avoid porting hassles in userspace.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 11 +++++++++++
libxfs/xfs_attr.h | 4 +++-
libxfs/xfs_attr_leaf.c | 7 ++++++-
repair/attr_repair.c | 7 ++++---
4 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 26674116f..e0a3bc702 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -1530,12 +1530,23 @@ xfs_attr_node_get(
return error;
}
+/* Enforce that there is at most one namespace bit per attr. */
+inline bool xfs_attr_check_namespace(unsigned int attr_flags)
+{
+ return hweight32(attr_flags & XFS_ATTR_NSP_ONDISK_MASK) < 2;
+}
+
/* Returns true if the attribute entry name is valid. */
bool
xfs_attr_namecheck(
+ unsigned int attr_flags,
const void *name,
size_t length)
{
+ /* Only one namespace bit allowed. */
+ if (!xfs_attr_check_namespace(attr_flags))
+ return false;
+
/*
* MAXNAMELEN includes the trailing null, but (name/length) leave it
* out, so use >= for the length check.
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index 79b457adb..cd106b0a4 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -560,7 +560,9 @@ enum xfs_attr_update {
int xfs_attr_set(struct xfs_da_args *args, enum xfs_attr_update op);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
-bool xfs_attr_namecheck(const void *name, size_t length);
+bool xfs_attr_check_namespace(unsigned int attr_flags);
+bool xfs_attr_namecheck(unsigned int attr_flags, 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);
diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c
index 47f2836fb..346f0127d 100644
--- a/libxfs/xfs_attr_leaf.c
+++ b/libxfs/xfs_attr_leaf.c
@@ -947,6 +947,11 @@ xfs_attr_shortform_to_leaf(
nargs.hashval = xfs_da_hashname(sfe->nameval,
sfe->namelen);
nargs.attr_filter = sfe->flags & XFS_ATTR_NSP_ONDISK_MASK;
+ if (!xfs_attr_check_namespace(sfe->flags)) {
+ xfs_da_mark_sick(args);
+ error = -EFSCORRUPTED;
+ goto out;
+ }
error = xfs_attr3_leaf_lookup_int(bp, &nargs); /* set a->index */
ASSERT(error == -ENOATTR);
error = xfs_attr3_leaf_add(bp, &nargs);
@@ -1060,7 +1065,7 @@ xfs_attr_shortform_verify(
* one namespace flag per xattr, so we can just count the
* bits (i.e. hweight) here.
*/
- if (hweight8(sfep->flags & XFS_ATTR_NSP_ONDISK_MASK) > 1)
+ if (!xfs_attr_check_namespace(sfep->flags))
return __this_address;
sfep = next_sfep;
diff --git a/repair/attr_repair.c b/repair/attr_repair.c
index 206a97d66..0f2f7a284 100644
--- a/repair/attr_repair.c
+++ b/repair/attr_repair.c
@@ -292,7 +292,8 @@ process_shortform_attr(
}
/* namecheck checks for null chars in attr names. */
- if (!libxfs_attr_namecheck(currententry->nameval,
+ if (!libxfs_attr_namecheck(currententry->flags,
+ currententry->nameval,
currententry->namelen)) {
do_warn(
_("entry contains illegal character in shortform attribute name\n"));
@@ -473,7 +474,7 @@ process_leaf_attr_local(
local = xfs_attr3_leaf_name_local(leaf, i);
if (local->namelen == 0 ||
- !libxfs_attr_namecheck(local->nameval,
+ !libxfs_attr_namecheck(entry->flags, local->nameval,
local->namelen)) {
do_warn(
_("attribute entry %d in attr block %u, inode %" PRIu64 " has bad name (namelen = %d)\n"),
@@ -529,7 +530,7 @@ process_leaf_attr_remote(
remotep = xfs_attr3_leaf_name_remote(leaf, i);
if (remotep->namelen == 0 ||
- !libxfs_attr_namecheck(remotep->name,
+ !libxfs_attr_namecheck(entry->flags, remotep->name,
remotep->namelen) ||
be32_to_cpu(entry->hashval) !=
libxfs_da_hashname((unsigned char *)&remotep->name[0],
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 051/115] xfs: rearrange xfs_attr_match parameters
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (49 preceding siblings ...)
2024-07-30 0:36 ` [PATCH 050/115] xfs: enforce one namespace per attribute Darrick J. Wong
@ 2024-07-30 0:37 ` Darrick J. Wong
2024-07-30 0:37 ` [PATCH 052/115] xfs: check the flags earlier in xfs_attr_match Darrick J. Wong
` (63 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:37 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 63211876ced33fbb730f515e8d830de53533fc82
Rearrange the parameters to this function so that they match the order
of attr listent: attr_flags -> name -> namelen -> value -> valuelen.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr_leaf.c | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c
index 346f0127d..a3859961f 100644
--- a/libxfs/xfs_attr_leaf.c
+++ b/libxfs/xfs_attr_leaf.c
@@ -507,9 +507,9 @@ xfs_attr3_leaf_read(
static bool
xfs_attr_match(
struct xfs_da_args *args,
- uint8_t namelen,
- unsigned char *name,
- int flags)
+ unsigned int attr_flags,
+ const unsigned char *name,
+ unsigned int namelen)
{
if (args->namelen != namelen)
@@ -519,12 +519,12 @@ xfs_attr_match(
/* Recovery ignores the INCOMPLETE flag. */
if ((args->op_flags & XFS_DA_OP_RECOVERY) &&
- args->attr_filter == (flags & XFS_ATTR_NSP_ONDISK_MASK))
+ args->attr_filter == (attr_flags & XFS_ATTR_NSP_ONDISK_MASK))
return true;
/* All remaining matches need to be filtered by INCOMPLETE state. */
if (args->attr_filter !=
- (flags & (XFS_ATTR_NSP_ONDISK_MASK | XFS_ATTR_INCOMPLETE)))
+ (attr_flags & (XFS_ATTR_NSP_ONDISK_MASK | XFS_ATTR_INCOMPLETE)))
return false;
return true;
}
@@ -743,8 +743,8 @@ xfs_attr_sf_findname(
for (sfe = xfs_attr_sf_firstentry(sf);
sfe < xfs_attr_sf_endptr(sf);
sfe = xfs_attr_sf_nextentry(sfe)) {
- if (xfs_attr_match(args, sfe->namelen, sfe->nameval,
- sfe->flags))
+ if (xfs_attr_match(args, sfe->flags, sfe->nameval,
+ sfe->namelen))
return sfe;
}
@@ -2440,15 +2440,16 @@ xfs_attr3_leaf_lookup_int(
*/
if (entry->flags & XFS_ATTR_LOCAL) {
name_loc = xfs_attr3_leaf_name_local(leaf, probe);
- if (!xfs_attr_match(args, name_loc->namelen,
- name_loc->nameval, entry->flags))
+ if (!xfs_attr_match(args, entry->flags,
+ name_loc->nameval,
+ name_loc->namelen))
continue;
args->index = probe;
return -EEXIST;
} else {
name_rmt = xfs_attr3_leaf_name_remote(leaf, probe);
- if (!xfs_attr_match(args, name_rmt->namelen,
- name_rmt->name, entry->flags))
+ if (!xfs_attr_match(args, entry->flags, name_rmt->name,
+ name_rmt->namelen))
continue;
args->index = probe;
args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 052/115] xfs: check the flags earlier in xfs_attr_match
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (50 preceding siblings ...)
2024-07-30 0:37 ` [PATCH 051/115] xfs: rearrange xfs_attr_match parameters Darrick J. Wong
@ 2024-07-30 0:37 ` Darrick J. Wong
2024-07-30 0:37 ` [PATCH 053/115] xfs: move xfs_attr_defer_add to xfs_attr_item.c Darrick J. Wong
` (62 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:37 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: f49af061f49c004fb6df7f791f39f9ed370f767b
Checking the flags match is much cheaper than a memcmp, so do it early
on in xfs_attr_match, and also add a little helper to calculate the
match mask right under the comment explaining the logic for it.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_attr_leaf.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c
index a3859961f..c6322fbd2 100644
--- a/libxfs/xfs_attr_leaf.c
+++ b/libxfs/xfs_attr_leaf.c
@@ -504,6 +504,13 @@ xfs_attr3_leaf_read(
* INCOMPLETE flag will not be set in attr->attr_filter, but rather
* XFS_DA_OP_RECOVERY will be set in args->op_flags.
*/
+static inline unsigned int xfs_attr_match_mask(const struct xfs_da_args *args)
+{
+ if (args->op_flags & XFS_DA_OP_RECOVERY)
+ return XFS_ATTR_NSP_ONDISK_MASK;
+ return XFS_ATTR_NSP_ONDISK_MASK | XFS_ATTR_INCOMPLETE;
+}
+
static bool
xfs_attr_match(
struct xfs_da_args *args,
@@ -511,21 +518,15 @@ xfs_attr_match(
const unsigned char *name,
unsigned int namelen)
{
+ unsigned int mask = xfs_attr_match_mask(args);
if (args->namelen != namelen)
return false;
+ if ((args->attr_filter & mask) != (attr_flags & mask))
+ return false;
if (memcmp(args->name, name, namelen) != 0)
return false;
- /* Recovery ignores the INCOMPLETE flag. */
- if ((args->op_flags & XFS_DA_OP_RECOVERY) &&
- args->attr_filter == (attr_flags & XFS_ATTR_NSP_ONDISK_MASK))
- return true;
-
- /* All remaining matches need to be filtered by INCOMPLETE state. */
- if (args->attr_filter !=
- (attr_flags & (XFS_ATTR_NSP_ONDISK_MASK | XFS_ATTR_INCOMPLETE)))
- return false;
return true;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 053/115] xfs: move xfs_attr_defer_add to xfs_attr_item.c
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (51 preceding siblings ...)
2024-07-30 0:37 ` [PATCH 052/115] xfs: check the flags earlier in xfs_attr_match Darrick J. Wong
@ 2024-07-30 0:37 ` Darrick J. Wong
2024-07-30 0:37 ` [PATCH 054/115] xfs: create a separate hashname function for extended attributes Darrick J. Wong
` (61 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:37 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 9713dc88773d066413ae23aa474b13241507a89e
Move the code that adds the incore xfs_attr_item deferred work data to a
transaction live with the ATTRI log item code. This means that the
upper level extended attribute code no longer has to know about the
inner workings of the ATTRI log items.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/defer_item.c | 28 ++++++++++++++++++++++++++++
libxfs/defer_item.h | 8 ++++++++
libxfs/xfs_attr.c | 38 ++++----------------------------------
3 files changed, 40 insertions(+), 34 deletions(-)
diff --git a/libxfs/defer_item.c b/libxfs/defer_item.c
index fd329e77c..9955e189d 100644
--- a/libxfs/defer_item.c
+++ b/libxfs/defer_item.c
@@ -670,6 +670,34 @@ xfs_attr_cancel_item(
xfs_attr_free_item(attr);
}
+void
+xfs_attr_defer_add(
+ struct xfs_da_args *args,
+ enum xfs_attr_defer_op op)
+{
+ struct xfs_attr_intent *new;
+
+ new = kmem_cache_zalloc(xfs_attr_intent_cache, GFP_NOFS | __GFP_NOFAIL);
+ new->xattri_da_args = args;
+
+ switch (op) {
+ case XFS_ATTR_DEFER_SET:
+ new->xattri_op_flags = XFS_ATTRI_OP_FLAGS_SET;
+ new->xattri_dela_state = xfs_attr_init_add_state(args);
+ break;
+ case XFS_ATTR_DEFER_REPLACE:
+ new->xattri_op_flags = XFS_ATTRI_OP_FLAGS_REPLACE;
+ new->xattri_dela_state = xfs_attr_init_replace_state(args);
+ break;
+ case XFS_ATTR_DEFER_REMOVE:
+ new->xattri_op_flags = XFS_ATTRI_OP_FLAGS_REMOVE;
+ new->xattri_dela_state = xfs_attr_init_remove_state(args);
+ break;
+ }
+
+ xfs_defer_add(args->trans, &new->xattri_list, &xfs_attr_defer_type);
+}
+
const struct xfs_defer_op_type xfs_attr_defer_type = {
.name = "attr",
.max_items = 1,
diff --git a/libxfs/defer_item.h b/libxfs/defer_item.h
index a5a07867c..df2b8d68b 100644
--- a/libxfs/defer_item.h
+++ b/libxfs/defer_item.h
@@ -10,6 +10,14 @@ struct xfs_bmap_intent;
void xfs_bmap_defer_add(struct xfs_trans *tp, struct xfs_bmap_intent *bi);
+enum xfs_attr_defer_op {
+ XFS_ATTR_DEFER_SET,
+ XFS_ATTR_DEFER_REMOVE,
+ XFS_ATTR_DEFER_REPLACE,
+};
+
+void xfs_attr_defer_add(struct xfs_da_args *args, enum xfs_attr_defer_op op);
+
struct xfs_exchmaps_intent;
void xfs_exchmaps_defer_add(struct xfs_trans *tp,
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index e0a3bc702..7f64d8a2e 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -24,6 +24,7 @@
#include "xfs_quota_defs.h"
#include "xfs_trans_space.h"
#include "xfs_trace.h"
+#include "defer_item.h"
struct kmem_cache *xfs_attr_intent_cache;
@@ -899,37 +900,6 @@ xfs_attr_lookup(
return error;
}
-static void
-xfs_attr_defer_add(
- struct xfs_da_args *args,
- unsigned int op_flags)
-{
-
- struct xfs_attr_intent *new;
-
- new = kmem_cache_zalloc(xfs_attr_intent_cache,
- GFP_KERNEL | __GFP_NOFAIL);
- new->xattri_op_flags = op_flags;
- new->xattri_da_args = args;
-
- switch (op_flags) {
- case XFS_ATTRI_OP_FLAGS_SET:
- new->xattri_dela_state = xfs_attr_init_add_state(args);
- break;
- case XFS_ATTRI_OP_FLAGS_REPLACE:
- new->xattri_dela_state = xfs_attr_init_replace_state(args);
- break;
- case XFS_ATTRI_OP_FLAGS_REMOVE:
- new->xattri_dela_state = xfs_attr_init_remove_state(args);
- break;
- default:
- ASSERT(0);
- }
-
- xfs_defer_add(args->trans, &new->xattri_list, &xfs_attr_defer_type);
- trace_xfs_attr_defer_add(new->xattri_dela_state, args->dp);
-}
-
int
xfs_attr_set(
struct xfs_da_args *args,
@@ -1019,14 +989,14 @@ 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_ATTRI_OP_FLAGS_REMOVE);
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE);
break;
}
/* Pure create fails if the attr already exists */
if (op == XFS_ATTRUPDATE_CREATE)
goto out_trans_cancel;
- xfs_attr_defer_add(args, XFS_ATTRI_OP_FLAGS_REPLACE);
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE);
break;
case -ENOATTR:
/* Can't remove what isn't there. */
@@ -1036,7 +1006,7 @@ 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_ATTRI_OP_FLAGS_SET);
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
break;
default:
goto out_trans_cancel;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 054/115] xfs: create a separate hashname function for extended attributes
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (52 preceding siblings ...)
2024-07-30 0:37 ` [PATCH 053/115] xfs: move xfs_attr_defer_add to xfs_attr_item.c Darrick J. Wong
@ 2024-07-30 0:37 ` Darrick J. Wong
2024-07-30 0:38 ` [PATCH 055/115] xfs: add parent pointer support to attribute code Darrick J. Wong
` (60 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:37 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: a64e0134754bf88021e937aa34f1fbb5b524e585
Create a separate function to compute name hashvalues for extended
attributes. When we get to parent pointers we'll be altering the rules
so that metadump obfuscation doesn't turn heinous.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 28 ++++++++++++++++++++++++++--
libxfs/xfs_attr.h | 14 ++++++++++++++
libxfs/xfs_attr_leaf.c | 3 +--
3 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 7f64d8a2e..91e7961c2 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -279,7 +279,7 @@ xfs_attr_get(
args->owner = args->dp->i_ino;
args->geo = args->dp->i_mount->m_attr_geo;
args->whichfork = XFS_ATTR_FORK;
- args->hashval = xfs_da_hashname(args->name, args->namelen);
+ xfs_attr_sethash(args);
/* Entirely possible to look up a name which doesn't exist */
args->op_flags = XFS_DA_OP_OKNOENT;
@@ -414,6 +414,30 @@ xfs_attr_sf_addname(
return error;
}
+/* Compute the hash value for a user/root/secure extended attribute */
+xfs_dahash_t
+xfs_attr_hashname(
+ const uint8_t *name,
+ int namelen)
+{
+ return xfs_da_hashname(name, namelen);
+}
+
+/* Compute the hash value for any extended attribute from any namespace. */
+xfs_dahash_t
+xfs_attr_hashval(
+ struct xfs_mount *mp,
+ unsigned int attr_flags,
+ const uint8_t *name,
+ int namelen,
+ const void *value,
+ int valuelen)
+{
+ ASSERT(xfs_attr_check_namespace(attr_flags));
+
+ return xfs_attr_hashname(name, namelen);
+}
+
/*
* Handle the state change on completion of a multi-state attr operation.
*
@@ -924,7 +948,7 @@ xfs_attr_set(
args->owner = args->dp->i_ino;
args->geo = mp->m_attr_geo;
args->whichfork = XFS_ATTR_FORK;
- args->hashval = xfs_da_hashname(args->name, args->namelen);
+ xfs_attr_sethash(args);
/*
* We have no control over the attribute names that userspace passes us
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index cd106b0a4..c63b1d610 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -628,6 +628,20 @@ xfs_attr_init_replace_state(struct xfs_da_args *args)
return xfs_attr_init_add_state(args);
}
+xfs_dahash_t xfs_attr_hashname(const uint8_t *name, int namelen);
+
+xfs_dahash_t xfs_attr_hashval(struct xfs_mount *mp, unsigned int attr_flags,
+ const uint8_t *name, int namelen, const void *value,
+ int valuelen);
+
+/* Set the hash value for any extended attribute from any namespace. */
+static inline void xfs_attr_sethash(struct xfs_da_args *args)
+{
+ args->hashval = xfs_attr_hashval(args->dp->i_mount, args->attr_filter,
+ args->name, args->namelen,
+ args->value, args->valuelen);
+}
+
extern struct kmem_cache *xfs_attr_intent_cache;
int __init xfs_attr_intent_init_cache(void);
void xfs_attr_intent_destroy_cache(void);
diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c
index c6322fbd2..212347bc3 100644
--- a/libxfs/xfs_attr_leaf.c
+++ b/libxfs/xfs_attr_leaf.c
@@ -945,14 +945,13 @@ xfs_attr_shortform_to_leaf(
nargs.namelen = sfe->namelen;
nargs.value = &sfe->nameval[nargs.namelen];
nargs.valuelen = sfe->valuelen;
- nargs.hashval = xfs_da_hashname(sfe->nameval,
- sfe->namelen);
nargs.attr_filter = sfe->flags & XFS_ATTR_NSP_ONDISK_MASK;
if (!xfs_attr_check_namespace(sfe->flags)) {
xfs_da_mark_sick(args);
error = -EFSCORRUPTED;
goto out;
}
+ xfs_attr_sethash(&nargs);
error = xfs_attr3_leaf_lookup_int(bp, &nargs); /* set a->index */
ASSERT(error == -ENOATTR);
error = xfs_attr3_leaf_add(bp, &nargs);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 055/115] xfs: add parent pointer support to attribute code
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (53 preceding siblings ...)
2024-07-30 0:37 ` [PATCH 054/115] xfs: create a separate hashname function for extended attributes Darrick J. Wong
@ 2024-07-30 0:38 ` Darrick J. Wong
2024-07-30 0:38 ` [PATCH 056/115] xfs: define parent pointer ondisk extended attribute format Darrick J. Wong
` (59 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:38 UTC (permalink / raw)
To: djwong, cem
Cc: Mark Tinguely, Dave Chinner, Allison Henderson, Christoph Hellwig,
linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: 98493ff878859eb0adefbc57a49ad47a92dfd252
Add the new parent attribute type. XFS_ATTR_PARENT is used only for parent pointer
entries; it uses reserved blocks like XFS_ATTR_ROOT.
Signed-off-by: Mark Tinguely <mark.tinguely@oracle.com>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_da_format.h | 9 +++++++--
libxfs/xfs_log_format.h | 1 +
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index ecd0616f5..0c80f7ab9 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -714,13 +714,17 @@ struct xfs_attr3_leafblock {
#define XFS_ATTR_LOCAL_BIT 0 /* attr is stored locally */
#define XFS_ATTR_ROOT_BIT 1 /* limit access to trusted attrs */
#define XFS_ATTR_SECURE_BIT 2 /* limit access to secure attrs */
+#define XFS_ATTR_PARENT_BIT 3 /* parent pointer attrs */
#define XFS_ATTR_INCOMPLETE_BIT 7 /* attr in middle of create/delete */
#define XFS_ATTR_LOCAL (1u << XFS_ATTR_LOCAL_BIT)
#define XFS_ATTR_ROOT (1u << XFS_ATTR_ROOT_BIT)
#define XFS_ATTR_SECURE (1u << XFS_ATTR_SECURE_BIT)
+#define XFS_ATTR_PARENT (1u << XFS_ATTR_PARENT_BIT)
#define XFS_ATTR_INCOMPLETE (1u << XFS_ATTR_INCOMPLETE_BIT)
-#define XFS_ATTR_NSP_ONDISK_MASK (XFS_ATTR_ROOT | XFS_ATTR_SECURE)
+#define XFS_ATTR_NSP_ONDISK_MASK (XFS_ATTR_ROOT | \
+ XFS_ATTR_SECURE | \
+ XFS_ATTR_PARENT)
#define XFS_ATTR_ONDISK_MASK (XFS_ATTR_NSP_ONDISK_MASK | \
XFS_ATTR_LOCAL | \
@@ -729,7 +733,8 @@ struct xfs_attr3_leafblock {
#define XFS_ATTR_NAMESPACE_STR \
{ XFS_ATTR_LOCAL, "local" }, \
{ XFS_ATTR_ROOT, "root" }, \
- { XFS_ATTR_SECURE, "secure" }
+ { XFS_ATTR_SECURE, "secure" }, \
+ { XFS_ATTR_PARENT, "parent" }
/*
* Alignment for namelist and valuelist entries (since they are mixed
diff --git a/libxfs/xfs_log_format.h b/libxfs/xfs_log_format.h
index accba2acd..020aebd10 100644
--- a/libxfs/xfs_log_format.h
+++ b/libxfs/xfs_log_format.h
@@ -1034,6 +1034,7 @@ struct xfs_icreate_log {
*/
#define XFS_ATTRI_FILTER_MASK (XFS_ATTR_ROOT | \
XFS_ATTR_SECURE | \
+ XFS_ATTR_PARENT | \
XFS_ATTR_INCOMPLETE)
/*
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 056/115] xfs: define parent pointer ondisk extended attribute format
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (54 preceding siblings ...)
2024-07-30 0:38 ` [PATCH 055/115] xfs: add parent pointer support to attribute code Darrick J. Wong
@ 2024-07-30 0:38 ` Darrick J. Wong
2024-07-30 0:38 ` [PATCH 057/115] xfs: allow xattr matching on name and value for parent pointers Darrick J. Wong
` (58 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:38 UTC (permalink / raw)
To: djwong, cem; +Cc: Allison Henderson, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: 8337d58ab2868f231a29824cd86d2e309bd36fa9
We need to define the parent pointer attribute format before we start
adding support for it into all the code that needs to use it. The EA
format we will use encodes the following information:
name={dirent name}
value={parent inumber, parent inode generation}
hash=xfs_dir2_hashname(dirent name) ^ (parent_inumber)
The inode/gen gives all the information we need to reliably identify the
parent without requiring child->parent lock ordering, and allows
userspace to do pathname component level reconstruction without the
kernel ever needing to verify the parent itself as part of ioctl calls.
By using the name-value lookup mode in the extended attribute code to
match parent pointers using both the xattr name and value, we can
identify the exact parent pointer EA we need to modify/remove in
rename/unlink operations without searching the entire EA space.
By storing the dirent name, we have enough information to be able to
validate and reconstruct damaged directory trees. Earlier iterations of
this patchset encoded the directory offset in the parent pointer key,
but this format required repair to keep that in sync across directory
rebuilds, which is unnecessary complexity.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_da_format.h | 13 +++++++++++++
libxfs/xfs_ondisk.h | 1 +
2 files changed, 14 insertions(+)
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index 0c80f7ab9..1395ad193 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -890,4 +890,17 @@ static inline unsigned int xfs_dir2_dirblock_bytes(struct xfs_sb *sbp)
xfs_failaddr_t xfs_da3_blkinfo_verify(struct xfs_buf *bp,
struct xfs_da3_blkinfo *hdr3);
+/*
+ * Parent pointer attribute format definition
+ *
+ * The xattr name contains the dirent name.
+ * The xattr value encodes the parent inode number and generation to ease
+ * opening parents by handle.
+ * The xattr hashval is xfs_dir2_namehash() ^ p_ino
+ */
+struct xfs_parent_rec {
+ __be64 p_ino;
+ __be32 p_gen;
+} __packed;
+
#endif /* __XFS_DA_FORMAT_H__ */
diff --git a/libxfs/xfs_ondisk.h b/libxfs/xfs_ondisk.h
index 81885a6a0..25952ef58 100644
--- a/libxfs/xfs_ondisk.h
+++ b/libxfs/xfs_ondisk.h
@@ -119,6 +119,7 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_OFFSET(xfs_dir2_sf_entry_t, offset, 1);
XFS_CHECK_OFFSET(xfs_dir2_sf_entry_t, name, 3);
XFS_CHECK_STRUCT_SIZE(xfs_dir2_sf_hdr_t, 10);
+ XFS_CHECK_STRUCT_SIZE(struct xfs_parent_rec, 12);
/* log structures */
XFS_CHECK_STRUCT_SIZE(struct xfs_buf_log_format, 88);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 057/115] xfs: allow xattr matching on name and value for parent pointers
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (55 preceding siblings ...)
2024-07-30 0:38 ` [PATCH 056/115] xfs: define parent pointer ondisk extended attribute format Darrick J. Wong
@ 2024-07-30 0:38 ` Darrick J. Wong
2024-07-30 0:38 ` [PATCH 058/115] xfs: create attr log item opcodes and formats " Darrick J. Wong
` (57 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:38 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: f041455eb5773eda3291903ad6d1f33d4798e9a2
If a file is hardlinked with the same name but from multiple parents,
the parent pointers will all have the same dirent name (== attr name)
but with different parent_ino/parent_gen values. To disambiguate, we
need to be able to match on both the attr name and the attr value. This
is in contrast to regular xattrs, which are matchtg edit
d only on name.
Therefore, plumb in the ability to match shortform and local attrs on
name and value in the XFS_ATTR_PARENT namespace. Parent pointer attr
values are never large enough to be stored in a remote attr, so we need
can reject these cases as corruption.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr_leaf.c | 52 ++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 46 insertions(+), 6 deletions(-)
diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c
index 212347bc3..faa357f15 100644
--- a/libxfs/xfs_attr_leaf.c
+++ b/libxfs/xfs_attr_leaf.c
@@ -511,12 +511,37 @@ static inline unsigned int xfs_attr_match_mask(const struct xfs_da_args *args)
return XFS_ATTR_NSP_ONDISK_MASK | XFS_ATTR_INCOMPLETE;
}
+static inline bool
+xfs_attr_parent_match(
+ const struct xfs_da_args *args,
+ const void *value,
+ unsigned int valuelen)
+{
+ ASSERT(args->value != NULL);
+
+ /* Parent pointers do not use remote values */
+ if (!value)
+ return false;
+
+ /*
+ * The only value we support is a parent rec. However, we'll accept
+ * any valuelen so that offline repair can delete ATTR_PARENT values
+ * that are not parent pointers.
+ */
+ if (valuelen != args->valuelen)
+ return false;
+
+ return memcmp(args->value, value, valuelen) == 0;
+}
+
static bool
xfs_attr_match(
struct xfs_da_args *args,
unsigned int attr_flags,
const unsigned char *name,
- unsigned int namelen)
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen)
{
unsigned int mask = xfs_attr_match_mask(args);
@@ -527,6 +552,9 @@ xfs_attr_match(
if (memcmp(args->name, name, namelen) != 0)
return false;
+ if (attr_flags & XFS_ATTR_PARENT)
+ return xfs_attr_parent_match(args, value, valuelen);
+
return true;
}
@@ -536,6 +564,13 @@ xfs_attr_copy_value(
unsigned char *value,
int valuelen)
{
+ /*
+ * Parent pointer lookups require the caller to specify the name and
+ * value, so don't copy anything.
+ */
+ if (args->attr_filter & XFS_ATTR_PARENT)
+ return 0;
+
/*
* No copy if all we have to do is get the length
*/
@@ -745,7 +780,8 @@ xfs_attr_sf_findname(
sfe < xfs_attr_sf_endptr(sf);
sfe = xfs_attr_sf_nextentry(sfe)) {
if (xfs_attr_match(args, sfe->flags, sfe->nameval,
- sfe->namelen))
+ sfe->namelen, &sfe->nameval[sfe->namelen],
+ sfe->valuelen))
return sfe;
}
@@ -2441,18 +2477,22 @@ xfs_attr3_leaf_lookup_int(
if (entry->flags & XFS_ATTR_LOCAL) {
name_loc = xfs_attr3_leaf_name_local(leaf, probe);
if (!xfs_attr_match(args, entry->flags,
- name_loc->nameval,
- name_loc->namelen))
+ name_loc->nameval, name_loc->namelen,
+ &name_loc->nameval[name_loc->namelen],
+ be16_to_cpu(name_loc->valuelen)))
continue;
args->index = probe;
return -EEXIST;
} else {
+ unsigned int valuelen;
+
name_rmt = xfs_attr3_leaf_name_remote(leaf, probe);
+ valuelen = be32_to_cpu(name_rmt->valuelen);
if (!xfs_attr_match(args, entry->flags, name_rmt->name,
- name_rmt->namelen))
+ name_rmt->namelen, NULL, valuelen))
continue;
args->index = probe;
- args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen);
+ args->rmtvaluelen = valuelen;
args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
args->rmtblkcnt = xfs_attr3_rmt_blocks(
args->dp->i_mount,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 058/115] xfs: create attr log item opcodes and formats for parent pointers
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (56 preceding siblings ...)
2024-07-30 0:38 ` [PATCH 057/115] xfs: allow xattr matching on name and value for parent pointers Darrick J. Wong
@ 2024-07-30 0:38 ` Darrick J. Wong
2024-07-30 0:39 ` [PATCH 059/115] xfs: record inode generation in xattr update log intent items Darrick J. Wong
` (56 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:38 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 5773f7f82be5aa98e4883566072d33342814cebe
Make the necessary alterations to the extended attribute log intent item
ondisk format so that we can log parent pointer operations. This
requires the creation of new opcodes specific to parent pointers, and a
new four-argument replace operation to handle renames. At this point
this part of the patchset has changed so much from what Allison original
wrote that I no longer think her SoB applies.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 19 +++++++++++++++++++
libxfs/xfs_attr.h | 4 ++--
libxfs/xfs_da_btree.h | 4 ++++
libxfs/xfs_log_format.h | 22 +++++++++++++++++++---
4 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 91e7961c2..c67cdc77a 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -438,6 +438,23 @@ xfs_attr_hashval(
return xfs_attr_hashname(name, namelen);
}
+/*
+ * PPTR_REPLACE operations require the caller to set the old and new names and
+ * values explicitly. Update the canonical fields to the new name and value
+ * here now that the removal phase has finished.
+ */
+static void
+xfs_attr_update_pptr_replace_args(
+ struct xfs_da_args *args)
+{
+ ASSERT(args->new_namelen > 0);
+ args->name = args->new_name;
+ args->namelen = args->new_namelen;
+ args->value = args->new_value;
+ args->valuelen = args->new_valuelen;
+ xfs_attr_sethash(args);
+}
+
/*
* Handle the state change on completion of a multi-state attr operation.
*
@@ -458,6 +475,8 @@ xfs_attr_complete_op(
if (!(args->op_flags & XFS_DA_OP_REPLACE))
replace_state = XFS_DAS_DONE;
+ else if (xfs_attr_intent_op(attr) == XFS_ATTRI_OP_FLAGS_PPTR_REPLACE)
+ xfs_attr_update_pptr_replace_args(args);
args->op_flags &= ~XFS_DA_OP_REPLACE;
args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index c63b1d610..d0ed7ea58 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -510,8 +510,8 @@ struct xfs_attr_intent {
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.
+ * Shared buffer containing the attr name, new name, and value so that
+ * the logging code can share large memory buffers between log items.
*/
struct xfs_attri_log_nameval *xattri_nameval;
diff --git a/libxfs/xfs_da_btree.h b/libxfs/xfs_da_btree.h
index 17cef594b..354d5d650 100644
--- a/libxfs/xfs_da_btree.h
+++ b/libxfs/xfs_da_btree.h
@@ -55,7 +55,9 @@ enum xfs_dacmp {
typedef struct xfs_da_args {
struct xfs_da_geometry *geo; /* da block geometry */
const uint8_t *name; /* string (maybe not NULL terminated) */
+ const uint8_t *new_name; /* new attr name */
void *value; /* set of bytes (maybe contain NULLs) */
+ void *new_value; /* new xattr value (may contain NULLs) */
struct xfs_inode *dp; /* directory inode to manipulate */
struct xfs_trans *trans; /* current trans (changes over time) */
@@ -63,10 +65,12 @@ typedef struct xfs_da_args {
xfs_ino_t owner; /* inode that owns the dir/attr data */
int valuelen; /* length of value */
+ int new_valuelen; /* length of new_value */
uint8_t filetype; /* filetype of inode for directories */
uint8_t op_flags; /* operation flags */
uint8_t attr_filter; /* XFS_ATTR_{ROOT,SECURE,INCOMPLETE} */
short namelen; /* length of string (maybe no NULL) */
+ short new_namelen; /* length of new attr name */
xfs_dahash_t hashval; /* hash value of name */
xfs_extlen_t total; /* total blocks needed, for 1st bmap */
int whichfork; /* data or attribute fork */
diff --git a/libxfs/xfs_log_format.h b/libxfs/xfs_log_format.h
index 020aebd10..632dd9732 100644
--- a/libxfs/xfs_log_format.h
+++ b/libxfs/xfs_log_format.h
@@ -115,11 +115,13 @@ struct xfs_unmount_log_format {
#define XLOG_REG_TYPE_BUD_FORMAT 26
#define XLOG_REG_TYPE_ATTRI_FORMAT 27
#define XLOG_REG_TYPE_ATTRD_FORMAT 28
-#define XLOG_REG_TYPE_ATTR_NAME 29
+#define XLOG_REG_TYPE_ATTR_NAME 29
#define XLOG_REG_TYPE_ATTR_VALUE 30
#define XLOG_REG_TYPE_XMI_FORMAT 31
#define XLOG_REG_TYPE_XMD_FORMAT 32
-#define XLOG_REG_TYPE_MAX 32
+#define XLOG_REG_TYPE_ATTR_NEWNAME 33
+#define XLOG_REG_TYPE_ATTR_NEWVALUE 34
+#define XLOG_REG_TYPE_MAX 34
/*
* Flags to log operation header
@@ -1026,6 +1028,9 @@ struct xfs_icreate_log {
#define XFS_ATTRI_OP_FLAGS_SET 1 /* Set the attribute */
#define XFS_ATTRI_OP_FLAGS_REMOVE 2 /* Remove the attribute */
#define XFS_ATTRI_OP_FLAGS_REPLACE 3 /* Replace the attribute */
+#define XFS_ATTRI_OP_FLAGS_PPTR_SET 4 /* Set parent pointer */
+#define XFS_ATTRI_OP_FLAGS_PPTR_REMOVE 5 /* Remove parent pointer */
+#define XFS_ATTRI_OP_FLAGS_PPTR_REPLACE 6 /* Replace parent pointer */
#define XFS_ATTRI_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
/*
@@ -1048,7 +1053,18 @@ struct xfs_attri_log_format {
uint64_t alfi_id; /* attri identifier */
uint64_t alfi_ino; /* the inode for this attr operation */
uint32_t alfi_op_flags; /* marks the op as a set or remove */
- uint32_t alfi_name_len; /* attr name length */
+ union {
+ uint32_t alfi_name_len; /* attr name length */
+ struct {
+ /*
+ * For PPTR_REPLACE, these are the lengths of the old
+ * and new attr names. The new and old values must
+ * have the same length.
+ */
+ uint16_t alfi_old_name_len;
+ uint16_t alfi_new_name_len;
+ };
+ };
uint32_t alfi_value_len; /* attr value length */
uint32_t alfi_attr_filter;/* attr filter flags */
};
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 059/115] xfs: record inode generation in xattr update log intent items
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (57 preceding siblings ...)
2024-07-30 0:38 ` [PATCH 058/115] xfs: create attr log item opcodes and formats " Darrick J. Wong
@ 2024-07-30 0:39 ` Darrick J. Wong
2024-07-30 0:39 ` [PATCH 060/115] xfs: add parent pointer validator functions Darrick J. Wong
` (55 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:39 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: ae673f534a30976ce5e709c4535a59c12b786ef3
For parent pointer updates, record the i_generation of the file that is
being updated so that we don't accidentally jump generations.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_log_format.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libxfs/xfs_log_format.h b/libxfs/xfs_log_format.h
index 632dd9732..3e6682ed6 100644
--- a/libxfs/xfs_log_format.h
+++ b/libxfs/xfs_log_format.h
@@ -1049,7 +1049,7 @@ struct xfs_icreate_log {
struct xfs_attri_log_format {
uint16_t alfi_type; /* attri log item type */
uint16_t alfi_size; /* size of this item */
- uint32_t __pad; /* pad to 64 bit aligned */
+ uint32_t alfi_igen; /* generation of alfi_ino for pptr ops */
uint64_t alfi_id; /* attri identifier */
uint64_t alfi_ino; /* the inode for this attr operation */
uint32_t alfi_op_flags; /* marks the op as a set or remove */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 060/115] xfs: add parent pointer validator functions
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (58 preceding siblings ...)
2024-07-30 0:39 ` [PATCH 059/115] xfs: record inode generation in xattr update log intent items Darrick J. Wong
@ 2024-07-30 0:39 ` Darrick J. Wong
2024-07-30 0:39 ` [PATCH 061/115] xfs: extend transaction reservations for parent attributes Darrick J. Wong
` (54 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:39 UTC (permalink / raw)
To: djwong, cem; +Cc: Allison Henderson, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: a08d6729637428b6ef8c6a5a94d8c6db7b805a44
The attr name of a parent pointer is a string, and the attr value of a
parent pointer is (more or less) a file handle. So we need to modify
attr_namecheck to verify the parent pointer name, and add a
xfs_parent_valuecheck function to sanitize the handle. At the same
time, we need to validate attr values during log recovery if the xattr
is really a parent pointer.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: move functions to xfs_parent.c, adjust for new disk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/Makefile | 2 +
libxfs/xfs_attr.c | 5 +++
libxfs/xfs_parent.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_parent.h | 15 +++++++++
4 files changed, 111 insertions(+)
create mode 100644 libxfs/xfs_parent.c
create mode 100644 libxfs/xfs_parent.h
diff --git a/libxfs/Makefile b/libxfs/Makefile
index e3fa18fee..2a5cead9a 100644
--- a/libxfs/Makefile
+++ b/libxfs/Makefile
@@ -50,6 +50,7 @@ HFILES = \
xfs_ialloc_btree.h \
xfs_inode_buf.h \
xfs_inode_fork.h \
+ xfs_parent.h \
xfs_quota_defs.h \
xfs_refcount.h \
xfs_refcount_btree.h \
@@ -102,6 +103,7 @@ CFILES = buf_mem.c \
xfs_inode_fork.c \
xfs_ialloc_btree.c \
xfs_log_rlimit.c \
+ xfs_parent.c \
xfs_refcount.c \
xfs_refcount_btree.c \
xfs_rmap.c \
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index c67cdc77a..345132921 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -25,6 +25,7 @@
#include "xfs_trans_space.h"
#include "xfs_trace.h"
#include "defer_item.h"
+#include "xfs_parent.h"
struct kmem_cache *xfs_attr_intent_cache;
@@ -1567,6 +1568,10 @@ xfs_attr_namecheck(
if (length >= MAXNAMELEN)
return false;
+ /* Parent pointers have their own validation. */
+ if (attr_flags & XFS_ATTR_PARENT)
+ return xfs_parent_namecheck(attr_flags, name, length);
+
/* There shouldn't be any nulls here */
return !memchr(name, 0, length);
}
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
new file mode 100644
index 000000000..50da527b6
--- /dev/null
+++ b/libxfs/xfs_parent.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2024 Oracle.
+ * All rights reserved.
+ */
+#include "libxfs_priv.h"
+#include "xfs_fs.h"
+#include "xfs_format.h"
+#include "xfs_da_format.h"
+#include "xfs_log_format.h"
+#include "xfs_shared.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_inode.h"
+#include "xfs_trace.h"
+#include "xfs_trans.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr.h"
+#include "xfs_dir2.h"
+#include "xfs_dir2_priv.h"
+#include "xfs_attr_sf.h"
+#include "xfs_bmap.h"
+#include "xfs_defer.h"
+#include "xfs_parent.h"
+#include "xfs_trans_space.h"
+
+/*
+ * Parent pointer attribute handling.
+ *
+ * Because the attribute name is a filename component, it will never be longer
+ * than 255 bytes and must not contain nulls or slashes. These are roughly the
+ * same constraints that apply to attribute names.
+ *
+ * The attribute value must always be a struct xfs_parent_rec. This means the
+ * attribute will never be in remote format because 12 bytes is nowhere near
+ * xfs_attr_leaf_entsize_local_max() (~75% of block size).
+ *
+ * Creating a new parent attribute will always create a new attribute - there
+ * should never, ever be an existing attribute in the tree for a new inode.
+ * ENOSPC behavior is problematic - creating the inode without the parent
+ * pointer is effectively a corruption, so we allow parent attribute creation
+ * to dip into the reserve block pool to avoid unexpected ENOSPC errors from
+ * occurring.
+ */
+
+/* Return true if parent pointer attr name is valid. */
+bool
+xfs_parent_namecheck(
+ unsigned int attr_flags,
+ const void *name,
+ size_t length)
+{
+ /*
+ * Parent pointers always use logged operations, so there should never
+ * be incomplete xattrs.
+ */
+ if (attr_flags & XFS_ATTR_INCOMPLETE)
+ return false;
+
+ return xfs_dir2_namecheck(name, length);
+}
+
+/* Return true if parent pointer attr value is valid. */
+bool
+xfs_parent_valuecheck(
+ struct xfs_mount *mp,
+ const void *value,
+ size_t valuelen)
+{
+ const struct xfs_parent_rec *rec = value;
+
+ if (!xfs_has_parent(mp))
+ return false;
+
+ /* The xattr value must be a parent record. */
+ if (valuelen != sizeof(struct xfs_parent_rec))
+ return false;
+
+ /* The parent record must be local. */
+ if (value == NULL)
+ return false;
+
+ /* The parent inumber must be valid. */
+ if (!xfs_verify_dir_ino(mp, be64_to_cpu(rec->p_ino)))
+ return false;
+
+ return true;
+}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
new file mode 100644
index 000000000..ef8aff860
--- /dev/null
+++ b/libxfs/xfs_parent.h
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2024 Oracle.
+ * All Rights Reserved.
+ */
+#ifndef __XFS_PARENT_H__
+#define __XFS_PARENT_H__
+
+/* Metadata validators */
+bool xfs_parent_namecheck(unsigned int attr_flags, const void *name,
+ size_t length);
+bool xfs_parent_valuecheck(struct xfs_mount *mp, const void *value,
+ size_t valuelen);
+
+#endif /* __XFS_PARENT_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 061/115] xfs: extend transaction reservations for parent attributes
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (59 preceding siblings ...)
2024-07-30 0:39 ` [PATCH 060/115] xfs: add parent pointer validator functions Darrick J. Wong
@ 2024-07-30 0:39 ` Darrick J. Wong
2024-07-30 0:39 ` [PATCH 062/115] xfs: create a hashname function for parent pointers Darrick J. Wong
` (53 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:39 UTC (permalink / raw)
To: djwong, cem; +Cc: Dave Chinner, Allison Henderson, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: 7dba4a5fe1c5cdf0859830380c52f29295cbf345
We need to add, remove or modify parent pointer attributes during
create/link/unlink/rename operations atomically with the dirents in the
parent directories being modified. This means they need to be modified
in the same transaction as the parent directories, and so we need to add
the required space for the attribute modifications to the transaction
reservations.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: fix indenting errors, adjust for new log format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/libxfs_priv.h | 1
libxfs/xfs_trans_resv.c | 324 +++++++++++++++++++++++++++++++++++++++--------
2 files changed, 273 insertions(+), 52 deletions(-)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index cfe96b05a..4136b277f 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -492,6 +492,7 @@ static inline int retzero(void) { return 0; }
#define xfs_icreate_log(tp, agno, agbno, cnt, isize, len, gen) ((void) 0)
#define xfs_sb_validate_fsb_count(sbp, nblks) (0)
+#define xlog_calc_iovec_len(len) roundup(len, sizeof(uint32_t))
/*
* Prototypes for kernel static functions that are aren't in their
diff --git a/libxfs/xfs_trans_resv.c b/libxfs/xfs_trans_resv.c
index 82b3d1522..dc405a943 100644
--- a/libxfs/xfs_trans_resv.c
+++ b/libxfs/xfs_trans_resv.c
@@ -19,6 +19,7 @@
#include "xfs_trans_space.h"
#include "xfs_quota_defs.h"
#include "xfs_rtbitmap.h"
+#include "xfs_da_format.h"
#define _ALLOC true
#define _FREE false
@@ -421,29 +422,110 @@ xfs_calc_itruncate_reservation_minlogsize(
return xfs_calc_itruncate_reservation(mp, true);
}
+static inline unsigned int xfs_calc_pptr_link_overhead(void)
+{
+ return sizeof(struct xfs_attri_log_format) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_rec)) +
+ xlog_calc_iovec_len(MAXNAMELEN - 1);
+}
+static inline unsigned int xfs_calc_pptr_unlink_overhead(void)
+{
+ return sizeof(struct xfs_attri_log_format) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_rec)) +
+ xlog_calc_iovec_len(MAXNAMELEN - 1);
+}
+static inline unsigned int xfs_calc_pptr_replace_overhead(void)
+{
+ return sizeof(struct xfs_attri_log_format) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_rec)) +
+ xlog_calc_iovec_len(MAXNAMELEN - 1) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_rec)) +
+ xlog_calc_iovec_len(MAXNAMELEN - 1);
+}
+
/*
* In renaming a files we can modify:
* the five inodes involved: 5 * inode size
* the two directory btrees: 2 * (max depth + v2) * dir block size
* the two directory bmap btrees: 2 * max depth * block size
* And the bmap_finish transaction can free dir and bmap blocks (two sets
- * of bmap blocks) giving:
+ * of bmap blocks) giving (t2):
* the agf for the ags in which the blocks live: 3 * sector size
* the agfl for the ags in which the blocks live: 3 * sector size
* the superblock for the free block count: sector size
* the allocation btrees: 3 exts * 2 trees * (2 * max depth - 1) * block size
+ * If parent pointers are enabled (t3), then each transaction in the chain
+ * must be capable of setting or removing the extended attribute
+ * containing the parent information. It must also be able to handle
+ * the three xattr intent items that track the progress of the parent
+ * pointer update.
*/
STATIC uint
xfs_calc_rename_reservation(
struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- max((xfs_calc_inode_res(mp, 5) +
- xfs_calc_buf_res(2 * XFS_DIROP_LOG_COUNT(mp),
- XFS_FSB_TO_B(mp, 1))),
- (xfs_calc_buf_res(7, mp->m_sb.sb_sectsize) +
- xfs_calc_buf_res(xfs_allocfree_block_count(mp, 3),
- XFS_FSB_TO_B(mp, 1))));
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ t1 = xfs_calc_inode_res(mp, 5) +
+ xfs_calc_buf_res(2 * XFS_DIROP_LOG_COUNT(mp),
+ XFS_FSB_TO_B(mp, 1));
+
+ t2 = xfs_calc_buf_res(7, mp->m_sb.sb_sectsize) +
+ xfs_calc_buf_res(xfs_allocfree_block_count(mp, 3),
+ XFS_FSB_TO_B(mp, 1));
+
+ if (xfs_has_parent(mp)) {
+ unsigned int rename_overhead, exchange_overhead;
+
+ t3 = max(resp->tr_attrsetm.tr_logres,
+ resp->tr_attrrm.tr_logres);
+
+ /*
+ * For a standard rename, the three xattr intent log items
+ * are (1) replacing the pptr for the source file; (2)
+ * removing the pptr on the dest file; and (3) adding a
+ * pptr for the whiteout file in the src dir.
+ *
+ * For an RENAME_EXCHANGE, there are two xattr intent
+ * items to replace the pptr for both src and dest
+ * files. Link counts don't change and there is no
+ * whiteout.
+ *
+ * In the worst case we can end up relogging all log
+ * intent items to allow the log tail to move ahead, so
+ * they become overhead added to each transaction in a
+ * processing chain.
+ */
+ rename_overhead = xfs_calc_pptr_replace_overhead() +
+ xfs_calc_pptr_unlink_overhead() +
+ xfs_calc_pptr_link_overhead();
+ exchange_overhead = 2 * xfs_calc_pptr_replace_overhead();
+
+ overhead += max(rename_overhead, exchange_overhead);
+ }
+
+ return overhead + max3(t1, t2, t3);
+}
+
+static inline unsigned int
+xfs_rename_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ /* One for the rename, one more for freeing blocks */
+ unsigned int ret = XFS_RENAME_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to remove or add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += max(resp->tr_attrsetm.tr_logcount,
+ resp->tr_attrrm.tr_logcount);
+
+ return ret;
}
/*
@@ -460,6 +542,23 @@ xfs_calc_iunlink_remove_reservation(
2 * M_IGEO(mp)->inode_cluster_size;
}
+static inline unsigned int
+xfs_link_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_LINK_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
+
/*
* For creating a link to an inode:
* the parent directory inode: inode size
@@ -476,14 +575,23 @@ STATIC uint
xfs_calc_link_reservation(
struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- xfs_calc_iunlink_remove_reservation(mp) +
- max((xfs_calc_inode_res(mp, 2) +
- xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
- XFS_FSB_TO_B(mp, 1))),
- (xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) +
- xfs_calc_buf_res(xfs_allocfree_block_count(mp, 1),
- XFS_FSB_TO_B(mp, 1))));
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ overhead += xfs_calc_iunlink_remove_reservation(mp);
+ t1 = xfs_calc_inode_res(mp, 2) +
+ xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1));
+ t2 = xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) +
+ xfs_calc_buf_res(xfs_allocfree_block_count(mp, 1),
+ XFS_FSB_TO_B(mp, 1));
+
+ if (xfs_has_parent(mp)) {
+ t3 = resp->tr_attrsetm.tr_logres;
+ overhead += xfs_calc_pptr_link_overhead();
+ }
+
+ return overhead + max3(t1, t2, t3);
}
/*
@@ -498,6 +606,23 @@ xfs_calc_iunlink_add_reservation(xfs_mount_t *mp)
M_IGEO(mp)->inode_cluster_size;
}
+static inline unsigned int
+xfs_remove_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_REMOVE_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrrm.tr_logcount;
+
+ return ret;
+}
+
/*
* For removing a directory entry we can modify:
* the parent directory inode: inode size
@@ -514,14 +639,24 @@ STATIC uint
xfs_calc_remove_reservation(
struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- xfs_calc_iunlink_add_reservation(mp) +
- max((xfs_calc_inode_res(mp, 2) +
- xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
- XFS_FSB_TO_B(mp, 1))),
- (xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) +
- xfs_calc_buf_res(xfs_allocfree_block_count(mp, 2),
- XFS_FSB_TO_B(mp, 1))));
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ overhead += xfs_calc_iunlink_add_reservation(mp);
+
+ t1 = xfs_calc_inode_res(mp, 2) +
+ xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1));
+ t2 = xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) +
+ xfs_calc_buf_res(xfs_allocfree_block_count(mp, 2),
+ XFS_FSB_TO_B(mp, 1));
+
+ if (xfs_has_parent(mp)) {
+ t3 = resp->tr_attrrm.tr_logres;
+ overhead += xfs_calc_pptr_unlink_overhead();
+ }
+
+ return overhead + max3(t1, t2, t3);
}
/*
@@ -570,12 +705,40 @@ xfs_calc_icreate_resv_alloc(
xfs_calc_finobt_res(mp);
}
+static inline unsigned int
+xfs_icreate_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_CREATE_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
+
STATIC uint
-xfs_calc_icreate_reservation(xfs_mount_t *mp)
+xfs_calc_icreate_reservation(
+ struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- max(xfs_calc_icreate_resv_alloc(mp),
- xfs_calc_create_resv_modify(mp));
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ t1 = xfs_calc_icreate_resv_alloc(mp);
+ t2 = xfs_calc_create_resv_modify(mp);
+
+ if (xfs_has_parent(mp)) {
+ t3 = resp->tr_attrsetm.tr_logres;
+ overhead += xfs_calc_pptr_link_overhead();
+ }
+
+ return overhead + max3(t1, t2, t3);
}
STATIC uint
@@ -588,6 +751,23 @@ xfs_calc_create_tmpfile_reservation(
return res + xfs_calc_iunlink_add_reservation(mp);
}
+static inline unsigned int
+xfs_mkdir_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_MKDIR_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
+
/*
* Making a new directory is the same as creating a new file.
*/
@@ -598,6 +778,22 @@ xfs_calc_mkdir_reservation(
return xfs_calc_icreate_reservation(mp);
}
+static inline unsigned int
+xfs_symlink_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_SYMLINK_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
/*
* Making a new symplink is the same as creating a new file, but
@@ -910,6 +1106,52 @@ xfs_calc_sb_reservation(
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
}
+/*
+ * Namespace reservations.
+ *
+ * These get tricky when parent pointers are enabled as we have attribute
+ * modifications occurring from within these transactions. Rather than confuse
+ * each of these reservation calculations with the conditional attribute
+ * reservations, add them here in a clear and concise manner. This requires that
+ * the attribute reservations have already been calculated.
+ *
+ * Note that we only include the static attribute reservation here; the runtime
+ * reservation will have to be modified by the size of the attributes being
+ * added/removed/modified. See the comments on the attribute reservation
+ * calculations for more details.
+ */
+STATIC void
+xfs_calc_namespace_reservations(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ ASSERT(resp->tr_attrsetm.tr_logres > 0);
+
+ resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
+ resp->tr_rename.tr_logcount = xfs_rename_log_count(mp, resp);
+ resp->tr_rename.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_link.tr_logres = xfs_calc_link_reservation(mp);
+ resp->tr_link.tr_logcount = xfs_link_log_count(mp, resp);
+ resp->tr_link.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_remove.tr_logres = xfs_calc_remove_reservation(mp);
+ resp->tr_remove.tr_logcount = xfs_remove_log_count(mp, resp);
+ resp->tr_remove.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_symlink.tr_logres = xfs_calc_symlink_reservation(mp);
+ resp->tr_symlink.tr_logcount = xfs_symlink_log_count(mp, resp);
+ resp->tr_symlink.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_create.tr_logres = xfs_calc_icreate_reservation(mp);
+ resp->tr_create.tr_logcount = xfs_icreate_log_count(mp, resp);
+ resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
+ resp->tr_mkdir.tr_logcount = xfs_mkdir_log_count(mp, resp);
+ resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+}
+
void
xfs_trans_resv_calc(
struct xfs_mount *mp,
@@ -929,35 +1171,11 @@ xfs_trans_resv_calc(
resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
- resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
- resp->tr_rename.tr_logcount = XFS_RENAME_LOG_COUNT;
- resp->tr_rename.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_link.tr_logres = xfs_calc_link_reservation(mp);
- resp->tr_link.tr_logcount = XFS_LINK_LOG_COUNT;
- resp->tr_link.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_remove.tr_logres = xfs_calc_remove_reservation(mp);
- resp->tr_remove.tr_logcount = XFS_REMOVE_LOG_COUNT;
- resp->tr_remove.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_symlink.tr_logres = xfs_calc_symlink_reservation(mp);
- resp->tr_symlink.tr_logcount = XFS_SYMLINK_LOG_COUNT;
- resp->tr_symlink.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_create.tr_logres = xfs_calc_icreate_reservation(mp);
- resp->tr_create.tr_logcount = XFS_CREATE_LOG_COUNT;
- resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
resp->tr_create_tmpfile.tr_logres =
xfs_calc_create_tmpfile_reservation(mp);
resp->tr_create_tmpfile.tr_logcount = XFS_CREATE_TMPFILE_LOG_COUNT;
resp->tr_create_tmpfile.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
- resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
- resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
- resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
resp->tr_ifree.tr_logres = xfs_calc_ifree_reservation(mp);
resp->tr_ifree.tr_logcount = XFS_INACTIVE_LOG_COUNT;
resp->tr_ifree.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
@@ -987,6 +1205,8 @@ xfs_trans_resv_calc(
resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
resp->tr_qm_dqalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+ xfs_calc_namespace_reservations(mp, resp);
+
/*
* The following transactions are logged in logical format with
* a default log count.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 062/115] xfs: create a hashname function for parent pointers
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (60 preceding siblings ...)
2024-07-30 0:39 ` [PATCH 061/115] xfs: extend transaction reservations for parent attributes Darrick J. Wong
@ 2024-07-30 0:39 ` Darrick J. Wong
2024-07-30 0:40 ` [PATCH 063/115] xfs: parent pointer attribute creation Darrick J. Wong
` (52 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:39 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: fb102fe7fe02e70f8a49cc7f74bc0769cdab2912
Although directory entry and parent pointer recordsets look very similar
(name -> ino), there's one major difference between them: a file can be
hardlinked from multiple parent directories with the same filename.
This is common in shared container environments where a base directory
tree might be hardlink-copied multiple times. IOWs the same 'ls'
program might be hardlinked to multiple /srv/*/bin/ls paths.
We don't want parent pointer operations to bog down on hash collisions
between the same dirent name, so create a special hash function that
mixes in the parent directory inode number.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/libxfs_priv.h | 18 ++++++++++++++++++
libxfs/xfs_attr.c | 3 +++
libxfs/xfs_parent.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_parent.h | 5 +++++
4 files changed, 73 insertions(+)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index 4136b277f..023444082 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -618,4 +618,22 @@ int xfs_bmap_last_extent(struct xfs_trans *tp, struct xfs_inode *ip,
/* xfs_inode.h */
#define xfs_iflags_set(ip, flags) do { } while (0)
+/* linux/wordpart.h */
+
+/**
+ * upper_32_bits - return bits 32-63 of a number
+ * @n: the number we're accessing
+ *
+ * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress
+ * the "right shift count >= width of type" warning when that quantity is
+ * 32-bits.
+ */
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+
+/**
+ * lower_32_bits - return bits 0-31 of a number
+ * @n: the number we're accessing
+ */
+#define lower_32_bits(n) ((uint32_t)((n) & 0xffffffff))
+
#endif /* __LIBXFS_INTERNAL_XFS_H__ */
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 345132921..344b34aa4 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -436,6 +436,9 @@ xfs_attr_hashval(
{
ASSERT(xfs_attr_check_namespace(attr_flags));
+ if (attr_flags & XFS_ATTR_PARENT)
+ return xfs_parent_hashattr(mp, name, namelen, value, valuelen);
+
return xfs_attr_hashname(name, namelen);
}
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index 50da527b6..7447acc2c 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -87,3 +87,50 @@ xfs_parent_valuecheck(
return true;
}
+
+/* Compute the attribute name hash for a parent pointer. */
+xfs_dahash_t
+xfs_parent_hashval(
+ struct xfs_mount *mp,
+ const uint8_t *name,
+ int namelen,
+ xfs_ino_t parent_ino)
+{
+ struct xfs_name xname = {
+ .name = name,
+ .len = namelen,
+ };
+
+ /*
+ * Use the same dirent name hash as would be used on the directory, but
+ * mix in the parent inode number to avoid collisions on hardlinked
+ * files with identical names but different parents.
+ */
+ return xfs_dir2_hashname(mp, &xname) ^
+ upper_32_bits(parent_ino) ^ lower_32_bits(parent_ino);
+}
+
+/* Compute the attribute name hash from the xattr components. */
+xfs_dahash_t
+xfs_parent_hashattr(
+ struct xfs_mount *mp,
+ const uint8_t *name,
+ int namelen,
+ const void *value,
+ int valuelen)
+{
+ const struct xfs_parent_rec *rec = value;
+
+ /* Requires a local attr value in xfs_parent_rec format */
+ if (valuelen != sizeof(struct xfs_parent_rec)) {
+ ASSERT(valuelen == sizeof(struct xfs_parent_rec));
+ return 0;
+ }
+
+ if (!value) {
+ ASSERT(value != NULL);
+ return 0;
+ }
+
+ return xfs_parent_hashval(mp, name, namelen, be64_to_cpu(rec->p_ino));
+}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index ef8aff860..6a4028871 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -12,4 +12,9 @@ bool xfs_parent_namecheck(unsigned int attr_flags, const void *name,
bool xfs_parent_valuecheck(struct xfs_mount *mp, const void *value,
size_t valuelen);
+xfs_dahash_t xfs_parent_hashval(struct xfs_mount *mp, const uint8_t *name,
+ int namelen, xfs_ino_t parent_ino);
+xfs_dahash_t xfs_parent_hashattr(struct xfs_mount *mp, const uint8_t *name,
+ int namelen, const void *value, int valuelen);
+
#endif /* __XFS_PARENT_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 063/115] xfs: parent pointer attribute creation
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (61 preceding siblings ...)
2024-07-30 0:39 ` [PATCH 062/115] xfs: create a hashname function for parent pointers Darrick J. Wong
@ 2024-07-30 0:40 ` Darrick J. Wong
2024-07-30 0:40 ` [PATCH 064/115] xfs: add parent attributes to link Darrick J. Wong
` (51 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:40 UTC (permalink / raw)
To: djwong, cem; +Cc: Dave Chinner, Allison Henderson, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: b7c62d90c12c6cc86f10b8a62cefe0029374b6ff
Add parent pointer attribute during xfs_create, and subroutines to
initialize attributes. Note that the xfs_attr_intent object contains a
pointer to the caller's xfs_da_args object, so the latter must persist
until transaction commit.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: shorten names, adjust to new format, set init_xattrs for parent
pointers]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
include/libxfs.h | 1 +
include/xfs_inode.h | 6 ++++
libxfs/Makefile | 1 +
libxfs/init.c | 3 ++
libxfs/libxfs_api_defs.h | 2 +
libxfs/xfs_parent.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_parent.h | 65 ++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_trans_space.c | 52 +++++++++++++++++++++++++++++++++++
libxfs/xfs_trans_space.h | 9 +++---
repair/phase6.c | 8 +++--
10 files changed, 207 insertions(+), 8 deletions(-)
create mode 100644 libxfs/xfs_trans_space.c
diff --git a/include/libxfs.h b/include/libxfs.h
index fb8efb696..e760a46d8 100644
--- a/include/libxfs.h
+++ b/include/libxfs.h
@@ -90,6 +90,7 @@ struct iomap;
#include "libxfs/xfile.h"
#include "libxfs/buf_mem.h"
#include "xfs_btree_mem.h"
+#include "xfs_parent.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
diff --git a/include/xfs_inode.h b/include/xfs_inode.h
index 825708383..c6e4f84bd 100644
--- a/include/xfs_inode.h
+++ b/include/xfs_inode.h
@@ -268,6 +268,12 @@ static inline struct inode *VFS_I(struct xfs_inode *ip)
return &ip->i_vnode;
}
+/* convert from const xfs inode to const vfs inode */
+static inline const struct inode *VFS_IC(const struct xfs_inode *ip)
+{
+ return &ip->i_vnode;
+}
+
/* We only have i_size in the xfs inode in userspace */
static inline loff_t i_size_read(struct inode *inode)
{
diff --git a/libxfs/Makefile b/libxfs/Makefile
index 2a5cead9a..9fb53d9cc 100644
--- a/libxfs/Makefile
+++ b/libxfs/Makefile
@@ -113,6 +113,7 @@ CFILES = buf_mem.c \
xfs_symlink_remote.c \
xfs_trans_inode.c \
xfs_trans_resv.c \
+ xfs_trans_space.c \
xfs_types.c
#
diff --git a/libxfs/init.c b/libxfs/init.c
index de91bbf3c..95de1e6d1 100644
--- a/libxfs/init.c
+++ b/libxfs/init.c
@@ -214,6 +214,8 @@ init_caches(void)
"xfs_extfree_item");
xfs_trans_cache = kmem_cache_init(
sizeof(struct xfs_trans), "xfs_trans");
+ xfs_parent_args_cache = kmem_cache_init(
+ sizeof(struct xfs_parent_args), "xfs_parent_args");
}
static int
@@ -231,6 +233,7 @@ destroy_caches(void)
xfs_btree_destroy_cur_caches();
leaked += kmem_cache_destroy(xfs_extfree_item_cache);
leaked += kmem_cache_destroy(xfs_trans_cache);
+ leaked += kmem_cache_destroy(xfs_parent_args_cache);
return leaked;
}
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index 16f6513f6..9b44b7709 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -98,6 +98,7 @@
#define xfs_calc_dquots_per_chunk libxfs_calc_dquots_per_chunk
#define xfs_cntbt_init_cursor libxfs_cntbt_init_cursor
#define xfs_compute_rextslog libxfs_compute_rextslog
+#define xfs_create_space_res libxfs_create_space_res
#define xfs_da3_node_hdr_from_disk libxfs_da3_node_hdr_from_disk
#define xfs_da_get_buf libxfs_da_get_buf
#define xfs_da_hashname libxfs_da_hashname
@@ -186,6 +187,7 @@
#define xfs_log_get_max_trans_res libxfs_log_get_max_trans_res
#define xfs_log_sb libxfs_log_sb
#define xfs_mode_to_ftype libxfs_mode_to_ftype
+#define xfs_mkdir_space_res libxfs_mkdir_space_res
#define xfs_perag_get libxfs_perag_get
#define xfs_perag_hold libxfs_perag_hold
#define xfs_perag_put libxfs_perag_put
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index 7447acc2c..b0516c3f6 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -24,6 +24,10 @@
#include "xfs_defer.h"
#include "xfs_parent.h"
#include "xfs_trans_space.h"
+#include "defer_item.h"
+#include "xfs_health.h"
+
+struct kmem_cache *xfs_parent_args_cache;
/*
* Parent pointer attribute handling.
@@ -134,3 +138,67 @@ xfs_parent_hashattr(
return xfs_parent_hashval(mp, name, namelen, be64_to_cpu(rec->p_ino));
}
+
+/*
+ * Initialize the parent pointer arguments structure. Caller must have zeroed
+ * the contents of @args. @tp is only required for updates.
+ */
+static void
+xfs_parent_da_args_init(
+ struct xfs_da_args *args,
+ struct xfs_trans *tp,
+ struct xfs_parent_rec *rec,
+ struct xfs_inode *child,
+ xfs_ino_t owner,
+ const struct xfs_name *parent_name)
+{
+ args->geo = child->i_mount->m_attr_geo;
+ args->whichfork = XFS_ATTR_FORK;
+ args->attr_filter = XFS_ATTR_PARENT;
+ args->op_flags = XFS_DA_OP_LOGGED | XFS_DA_OP_OKNOENT;
+ args->trans = tp;
+ args->dp = child;
+ args->owner = owner;
+ args->name = parent_name->name;
+ args->namelen = parent_name->len;
+ args->value = rec;
+ args->valuelen = sizeof(struct xfs_parent_rec);
+ xfs_attr_sethash(args);
+}
+
+/* Make sure the incore state is ready for a parent pointer query/update. */
+static inline int
+xfs_parent_iread_extents(
+ struct xfs_trans *tp,
+ struct xfs_inode *child)
+{
+ /* Parent pointers require that the attr fork must exist. */
+ if (XFS_IS_CORRUPT(child->i_mount, !xfs_inode_has_attr_fork(child))) {
+ xfs_inode_mark_sick(child, XFS_SICK_INO_PARENT);
+ return -EFSCORRUPTED;
+ }
+
+ return xfs_iread_extents(tp, child, XFS_ATTR_FORK);
+}
+
+/* Add a parent pointer to reflect a dirent addition. */
+int
+xfs_parent_addname(
+ struct xfs_trans *tp,
+ struct xfs_parent_args *ppargs,
+ struct xfs_inode *dp,
+ const struct xfs_name *parent_name,
+ struct xfs_inode *child)
+{
+ int error;
+
+ error = xfs_parent_iread_extents(tp, child);
+ if (error)
+ return error;
+
+ 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;
+}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index 6a4028871..6de24e3ef 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -17,4 +17,69 @@ xfs_dahash_t xfs_parent_hashval(struct xfs_mount *mp, const uint8_t *name,
xfs_dahash_t xfs_parent_hashattr(struct xfs_mount *mp, const uint8_t *name,
int namelen, const void *value, int valuelen);
+/* Initializes a xfs_parent_rec to be stored as an attribute name. */
+static inline void
+xfs_parent_rec_init(
+ struct xfs_parent_rec *rec,
+ xfs_ino_t ino,
+ uint32_t gen)
+{
+ rec->p_ino = cpu_to_be64(ino);
+ rec->p_gen = cpu_to_be32(gen);
+}
+
+/* Initializes a xfs_parent_rec to be stored as an attribute name. */
+static inline void
+xfs_inode_to_parent_rec(
+ struct xfs_parent_rec *rec,
+ const struct xfs_inode *dp)
+{
+ xfs_parent_rec_init(rec, dp->i_ino, VFS_IC(dp)->i_generation);
+}
+
+extern struct kmem_cache *xfs_parent_args_cache;
+
+/*
+ * Parent pointer information needed to pass around the deferred xattr update
+ * machinery.
+ */
+struct xfs_parent_args {
+ struct xfs_parent_rec rec;
+ struct xfs_da_args args;
+};
+
+/*
+ * Start a parent pointer update by allocating the context object we need to
+ * perform a parent pointer update.
+ */
+static inline int
+xfs_parent_start(
+ struct xfs_mount *mp,
+ struct xfs_parent_args **ppargsp)
+{
+ if (!xfs_has_parent(mp)) {
+ *ppargsp = NULL;
+ return 0;
+ }
+
+ *ppargsp = kmem_cache_zalloc(xfs_parent_args_cache, GFP_KERNEL);
+ if (!*ppargsp)
+ return -ENOMEM;
+ return 0;
+}
+
+/* Finish a parent pointer update by freeing the context object. */
+static inline void
+xfs_parent_finish(
+ struct xfs_mount *mp,
+ struct xfs_parent_args *ppargs)
+{
+ if (ppargs)
+ kmem_cache_free(xfs_parent_args_cache, ppargs);
+}
+
+int xfs_parent_addname(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
+ struct xfs_inode *dp, const struct xfs_name *parent_name,
+ struct xfs_inode *child);
+
#endif /* __XFS_PARENT_H__ */
diff --git a/libxfs/xfs_trans_space.c b/libxfs/xfs_trans_space.c
new file mode 100644
index 000000000..3408e700f
--- /dev/null
+++ b/libxfs/xfs_trans_space.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2000,2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ */
+#include "libxfs_priv.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_da_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_da_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_trans_space.h"
+
+/* Calculate the disk space required to add a parent pointer. */
+unsigned int
+xfs_parent_calc_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ /*
+ * Parent pointers are always the first attr in an attr tree, and never
+ * larger than a block
+ */
+ return XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) +
+ XFS_NEXTENTADD_SPACE_RES(mp, namelen, XFS_ATTR_FORK);
+}
+
+unsigned int
+xfs_create_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ unsigned int ret;
+
+ ret = XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp, namelen);
+ if (xfs_has_parent(mp))
+ ret += xfs_parent_calc_space_res(mp, namelen);
+
+ return ret;
+}
+
+unsigned int
+xfs_mkdir_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ return xfs_create_space_res(mp, namelen);
+}
diff --git a/libxfs/xfs_trans_space.h b/libxfs/xfs_trans_space.h
index 9640fc232..6cda87153 100644
--- a/libxfs/xfs_trans_space.h
+++ b/libxfs/xfs_trans_space.h
@@ -80,8 +80,6 @@
/* This macro is not used - see inline code in xfs_attr_set */
#define XFS_ATTRSET_SPACE_RES(mp, v) \
(XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) + XFS_B_TO_FSB(mp, v))
-#define XFS_CREATE_SPACE_RES(mp,nl) \
- (XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_DIOSTRAT_SPACE_RES(mp, v) \
(XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK) + (v))
#define XFS_GROWFS_SPACE_RES(mp) \
@@ -90,8 +88,6 @@
((b) + XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK))
#define XFS_LINK_SPACE_RES(mp,nl) \
XFS_DIRENTER_SPACE_RES(mp,nl)
-#define XFS_MKDIR_SPACE_RES(mp,nl) \
- (XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_QM_DQALLOC_SPACE_RES(mp) \
(XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK) + \
XFS_DQUOT_CLUSTER_SIZE_FSB)
@@ -106,5 +102,10 @@
#define XFS_IFREE_SPACE_RES(mp) \
(xfs_has_finobt(mp) ? M_IGEO(mp)->inobt_maxlevels : 0)
+unsigned int xfs_parent_calc_space_res(struct xfs_mount *mp,
+ unsigned int namelen);
+
+unsigned int xfs_create_space_res(struct xfs_mount *mp, unsigned int namelen);
+unsigned int xfs_mkdir_space_res(struct xfs_mount *mp, unsigned int namelen);
#endif /* __XFS_TRANS_SPACE_H__ */
diff --git a/repair/phase6.c b/repair/phase6.c
index e6103f768..c4260fb91 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -927,7 +927,7 @@ mk_orphanage(xfs_mount_t *mp)
/*
* could not be found, create it
*/
- nres = XFS_MKDIR_SPACE_RES(mp, xname.len);
+ nres = libxfs_mkdir_space_res(mp, xname.len);
i = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_mkdir, nres, 0, 0, &tp);
if (i)
res_failed(i);
@@ -1343,7 +1343,7 @@ longform_dir2_rebuild(
p->name.name[1] == '.'))))
continue;
- nres = XFS_CREATE_SPACE_RES(mp, p->name.len);
+ nres = libxfs_create_space_res(mp, p->name.len);
error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_create,
nres, 0, 0, &tp);
if (error)
@@ -2955,7 +2955,7 @@ _("error %d fixing shortform directory %llu\n"),
do_warn(_("recreating root directory .. entry\n"));
- nres = XFS_MKDIR_SPACE_RES(mp, 2);
+ nres = libxfs_mkdir_space_res(mp, 2);
error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_mkdir,
nres, 0, 0, &tp);
if (error)
@@ -3010,7 +3010,7 @@ _("error %d fixing shortform directory %llu\n"),
do_warn(
_("creating missing \".\" entry in dir ino %" PRIu64 "\n"), ino);
- nres = XFS_MKDIR_SPACE_RES(mp, 1);
+ nres = libxfs_mkdir_space_res(mp, 1);
error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_mkdir,
nres, 0, 0, &tp);
if (error)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 064/115] xfs: add parent attributes to link
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (62 preceding siblings ...)
2024-07-30 0:40 ` [PATCH 063/115] xfs: parent pointer attribute creation Darrick J. Wong
@ 2024-07-30 0:40 ` Darrick J. Wong
2024-07-30 0:40 ` [PATCH 065/115] xfs: add parent attributes to symlink Darrick J. Wong
` (50 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:40 UTC (permalink / raw)
To: djwong, cem; +Cc: Dave Chinner, Allison Henderson, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: f1097be220fa938de5114db57a1ddb5de2bf6046
This patch modifies xfs_link to add a parent pointer to the inode.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: minor rebase fixes]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_trans_space.c | 14 ++++++++++++++
libxfs/xfs_trans_space.h | 3 +--
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_trans_space.c b/libxfs/xfs_trans_space.c
index 3408e700f..039bbd91e 100644
--- a/libxfs/xfs_trans_space.c
+++ b/libxfs/xfs_trans_space.c
@@ -50,3 +50,17 @@ xfs_mkdir_space_res(
{
return xfs_create_space_res(mp, namelen);
}
+
+unsigned int
+xfs_link_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ unsigned int ret;
+
+ ret = XFS_DIRENTER_SPACE_RES(mp, namelen);
+ if (xfs_has_parent(mp))
+ ret += xfs_parent_calc_space_res(mp, namelen);
+
+ return ret;
+}
diff --git a/libxfs/xfs_trans_space.h b/libxfs/xfs_trans_space.h
index 6cda87153..553963400 100644
--- a/libxfs/xfs_trans_space.h
+++ b/libxfs/xfs_trans_space.h
@@ -86,8 +86,6 @@
(2 * (mp)->m_alloc_maxlevels)
#define XFS_GROWFSRT_SPACE_RES(mp,b) \
((b) + XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK))
-#define XFS_LINK_SPACE_RES(mp,nl) \
- XFS_DIRENTER_SPACE_RES(mp,nl)
#define XFS_QM_DQALLOC_SPACE_RES(mp) \
(XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK) + \
XFS_DQUOT_CLUSTER_SIZE_FSB)
@@ -107,5 +105,6 @@ unsigned int xfs_parent_calc_space_res(struct xfs_mount *mp,
unsigned int xfs_create_space_res(struct xfs_mount *mp, unsigned int namelen);
unsigned int xfs_mkdir_space_res(struct xfs_mount *mp, unsigned int namelen);
+unsigned int xfs_link_space_res(struct xfs_mount *mp, unsigned int namelen);
#endif /* __XFS_TRANS_SPACE_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 065/115] xfs: add parent attributes to symlink
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (63 preceding siblings ...)
2024-07-30 0:40 ` [PATCH 064/115] xfs: add parent attributes to link Darrick J. Wong
@ 2024-07-30 0:40 ` Darrick J. Wong
2024-07-30 0:40 ` [PATCH 066/115] xfs: remove parent pointers in unlink Darrick J. Wong
` (49 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:40 UTC (permalink / raw)
To: djwong, cem; +Cc: Allison Henderson, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: 5d31a85dcc1fa4c5d4a925c6da67751653a700ba
This patch modifies xfs_symlink to add a parent pointer to the inode.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: minor rebase fixups]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_trans_space.c | 17 +++++++++++++++++
libxfs/xfs_trans_space.h | 4 ++--
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_trans_space.c b/libxfs/xfs_trans_space.c
index 039bbd91e..bf4a41492 100644
--- a/libxfs/xfs_trans_space.c
+++ b/libxfs/xfs_trans_space.c
@@ -64,3 +64,20 @@ xfs_link_space_res(
return ret;
}
+
+unsigned int
+xfs_symlink_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen,
+ unsigned int fsblocks)
+{
+ unsigned int ret;
+
+ ret = XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp, namelen) +
+ fsblocks;
+
+ if (xfs_has_parent(mp))
+ ret += xfs_parent_calc_space_res(mp, namelen);
+
+ return ret;
+}
diff --git a/libxfs/xfs_trans_space.h b/libxfs/xfs_trans_space.h
index 553963400..354ad1d6e 100644
--- a/libxfs/xfs_trans_space.h
+++ b/libxfs/xfs_trans_space.h
@@ -95,8 +95,6 @@
XFS_DIRREMOVE_SPACE_RES(mp)
#define XFS_RENAME_SPACE_RES(mp,nl) \
(XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
-#define XFS_SYMLINK_SPACE_RES(mp,nl,b) \
- (XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl) + (b))
#define XFS_IFREE_SPACE_RES(mp) \
(xfs_has_finobt(mp) ? M_IGEO(mp)->inobt_maxlevels : 0)
@@ -106,5 +104,7 @@ unsigned int xfs_parent_calc_space_res(struct xfs_mount *mp,
unsigned int xfs_create_space_res(struct xfs_mount *mp, unsigned int namelen);
unsigned int xfs_mkdir_space_res(struct xfs_mount *mp, unsigned int namelen);
unsigned int xfs_link_space_res(struct xfs_mount *mp, unsigned int namelen);
+unsigned int xfs_symlink_space_res(struct xfs_mount *mp, unsigned int namelen,
+ unsigned int fsblocks);
#endif /* __XFS_TRANS_SPACE_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 066/115] xfs: remove parent pointers in unlink
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (64 preceding siblings ...)
2024-07-30 0:40 ` [PATCH 065/115] xfs: add parent attributes to symlink Darrick J. Wong
@ 2024-07-30 0:40 ` Darrick J. Wong
2024-07-30 0:41 ` [PATCH 067/115] xfs: Add parent pointers to rename Darrick J. Wong
` (48 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:40 UTC (permalink / raw)
To: djwong, cem; +Cc: Dave Chinner, Allison Henderson, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: d2d18330f63cd70b50eddac76de7c59a36f2faa7
This patch removes the parent pointer attribute during unlink
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: adjust to new ondisk format, minor rebase fixes]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/libxfs_api_defs.h | 1 +
libxfs/xfs_parent.c | 22 ++++++++++++++++++++++
libxfs/xfs_parent.h | 3 +++
libxfs/xfs_trans_space.c | 13 +++++++++++++
libxfs/xfs_trans_space.h | 3 +--
repair/phase6.c | 6 +++---
6 files changed, 43 insertions(+), 5 deletions(-)
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index 9b44b7709..896b6c953 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -204,6 +204,7 @@
#define xfs_refcountbt_stage_cursor libxfs_refcountbt_stage_cursor
#define xfs_refcount_get_rec libxfs_refcount_get_rec
#define xfs_refcount_lookup_le libxfs_refcount_lookup_le
+#define xfs_remove_space_res libxfs_remove_space_res
#define xfs_rmap_alloc libxfs_rmap_alloc
#define xfs_rmapbt_calc_reserves libxfs_rmapbt_calc_reserves
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index b0516c3f6..0657728e6 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -202,3 +202,25 @@ xfs_parent_addname(
xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_SET);
return 0;
}
+
+/* Remove a parent pointer to reflect a dirent removal. */
+int
+xfs_parent_removename(
+ struct xfs_trans *tp,
+ struct xfs_parent_args *ppargs,
+ struct xfs_inode *dp,
+ const struct xfs_name *parent_name,
+ struct xfs_inode *child)
+{
+ int error;
+
+ error = xfs_parent_iread_extents(tp, child);
+ if (error)
+ return error;
+
+ 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;
+}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index 6de24e3ef..4a7fd48c2 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -81,5 +81,8 @@ xfs_parent_finish(
int xfs_parent_addname(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
struct xfs_inode *dp, const struct xfs_name *parent_name,
struct xfs_inode *child);
+int xfs_parent_removename(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
+ struct xfs_inode *dp, const struct xfs_name *parent_name,
+ struct xfs_inode *child);
#endif /* __XFS_PARENT_H__ */
diff --git a/libxfs/xfs_trans_space.c b/libxfs/xfs_trans_space.c
index bf4a41492..86a91a3a8 100644
--- a/libxfs/xfs_trans_space.c
+++ b/libxfs/xfs_trans_space.c
@@ -81,3 +81,16 @@ xfs_symlink_space_res(
return ret;
}
+
+unsigned int
+xfs_remove_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ unsigned int ret = XFS_DIRREMOVE_SPACE_RES(mp);
+
+ if (xfs_has_parent(mp))
+ ret += xfs_parent_calc_space_res(mp, namelen);
+
+ return ret;
+}
diff --git a/libxfs/xfs_trans_space.h b/libxfs/xfs_trans_space.h
index 354ad1d6e..a4490813c 100644
--- a/libxfs/xfs_trans_space.h
+++ b/libxfs/xfs_trans_space.h
@@ -91,8 +91,6 @@
XFS_DQUOT_CLUSTER_SIZE_FSB)
#define XFS_QM_QINOCREATE_SPACE_RES(mp) \
XFS_IALLOC_SPACE_RES(mp)
-#define XFS_REMOVE_SPACE_RES(mp) \
- XFS_DIRREMOVE_SPACE_RES(mp)
#define XFS_RENAME_SPACE_RES(mp,nl) \
(XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_IFREE_SPACE_RES(mp) \
@@ -106,5 +104,6 @@ unsigned int xfs_mkdir_space_res(struct xfs_mount *mp, unsigned int namelen);
unsigned int xfs_link_space_res(struct xfs_mount *mp, unsigned int namelen);
unsigned int xfs_symlink_space_res(struct xfs_mount *mp, unsigned int namelen,
unsigned int fsblocks);
+unsigned int xfs_remove_space_res(struct xfs_mount *mp, unsigned int namelen);
#endif /* __XFS_TRANS_SPACE_H__ */
diff --git a/repair/phase6.c b/repair/phase6.c
index c4260fb91..2eba9772d 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -1283,7 +1283,7 @@ longform_dir2_rebuild(
libxfs_dir_ino_validate(mp, pip.i_ino))
pip.i_ino = mp->m_sb.sb_rootino;
- nres = XFS_REMOVE_SPACE_RES(mp);
+ nres = libxfs_remove_space_res(mp, 0);
error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove, nres, 0, 0, &tp);
if (error)
res_failed(error);
@@ -1389,7 +1389,7 @@ dir2_kill_block(
int nres;
xfs_trans_t *tp;
- nres = XFS_REMOVE_SPACE_RES(mp);
+ nres = libxfs_remove_space_res(mp, 0);
error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove, nres, 0, 0, &tp);
if (error)
res_failed(error);
@@ -2908,7 +2908,7 @@ process_dir_inode(
* inode but it's easier than wedging a
* new define in ourselves.
*/
- nres = no_modify ? 0 : XFS_REMOVE_SPACE_RES(mp);
+ nres = no_modify ? 0 : libxfs_remove_space_res(mp, 0);
error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove,
nres, 0, 0, &tp);
if (error)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 067/115] xfs: Add parent pointers to rename
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (65 preceding siblings ...)
2024-07-30 0:40 ` [PATCH 066/115] xfs: remove parent pointers in unlink Darrick J. Wong
@ 2024-07-30 0:41 ` Darrick J. Wong
2024-07-30 0:41 ` [PATCH 068/115] xfs: don't return XFS_ATTR_PARENT attributes via listxattr Darrick J. Wong
` (47 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:41 UTC (permalink / raw)
To: djwong, cem; +Cc: Allison Henderson, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: 5a8338c88284df4e9e697225aa65f2709333a659
This patch removes the old parent pointer attribute during the rename
operation, and re-adds the updated parent pointer.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: adjust to new ondisk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_parent.c | 30 ++++++++++++++++++++++++++++++
libxfs/xfs_parent.h | 6 ++++++
libxfs/xfs_trans_space.c | 25 +++++++++++++++++++++++++
libxfs/xfs_trans_space.h | 6 ++++--
4 files changed, 65 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index 0657728e6..a53b7d13d 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -224,3 +224,33 @@ xfs_parent_removename(
xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REMOVE);
return 0;
}
+
+/* Replace one parent pointer with another to reflect a rename. */
+int
+xfs_parent_replacename(
+ struct xfs_trans *tp,
+ struct xfs_parent_args *ppargs,
+ struct xfs_inode *old_dp,
+ const struct xfs_name *old_name,
+ struct xfs_inode *new_dp,
+ const struct xfs_name *new_name,
+ struct xfs_inode *child)
+{
+ int error;
+
+ error = xfs_parent_iread_extents(tp, child);
+ if (error)
+ return error;
+
+ xfs_inode_to_parent_rec(&ppargs->rec, old_dp);
+ xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
+ 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;
+}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index 4a7fd48c2..768633b31 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -45,6 +45,7 @@ extern struct kmem_cache *xfs_parent_args_cache;
*/
struct xfs_parent_args {
struct xfs_parent_rec rec;
+ struct xfs_parent_rec new_rec;
struct xfs_da_args args;
};
@@ -84,5 +85,10 @@ int xfs_parent_addname(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
int xfs_parent_removename(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
struct xfs_inode *dp, const struct xfs_name *parent_name,
struct xfs_inode *child);
+int xfs_parent_replacename(struct xfs_trans *tp,
+ struct xfs_parent_args *ppargs,
+ struct xfs_inode *old_dp, const struct xfs_name *old_name,
+ struct xfs_inode *new_dp, const struct xfs_name *new_name,
+ struct xfs_inode *child);
#endif /* __XFS_PARENT_H__ */
diff --git a/libxfs/xfs_trans_space.c b/libxfs/xfs_trans_space.c
index 86a91a3a8..373f5cc24 100644
--- a/libxfs/xfs_trans_space.c
+++ b/libxfs/xfs_trans_space.c
@@ -94,3 +94,28 @@ xfs_remove_space_res(
return ret;
}
+
+unsigned int
+xfs_rename_space_res(
+ struct xfs_mount *mp,
+ unsigned int src_namelen,
+ bool target_exists,
+ unsigned int target_namelen,
+ bool has_whiteout)
+{
+ unsigned int ret;
+
+ ret = XFS_DIRREMOVE_SPACE_RES(mp) +
+ XFS_DIRENTER_SPACE_RES(mp, target_namelen);
+
+ if (xfs_has_parent(mp)) {
+ if (has_whiteout)
+ ret += xfs_parent_calc_space_res(mp, src_namelen);
+ ret += 2 * xfs_parent_calc_space_res(mp, target_namelen);
+ }
+
+ if (target_exists)
+ ret += xfs_parent_calc_space_res(mp, target_namelen);
+
+ return ret;
+}
diff --git a/libxfs/xfs_trans_space.h b/libxfs/xfs_trans_space.h
index a4490813c..1155ff2d3 100644
--- a/libxfs/xfs_trans_space.h
+++ b/libxfs/xfs_trans_space.h
@@ -91,8 +91,6 @@
XFS_DQUOT_CLUSTER_SIZE_FSB)
#define XFS_QM_QINOCREATE_SPACE_RES(mp) \
XFS_IALLOC_SPACE_RES(mp)
-#define XFS_RENAME_SPACE_RES(mp,nl) \
- (XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_IFREE_SPACE_RES(mp) \
(xfs_has_finobt(mp) ? M_IGEO(mp)->inobt_maxlevels : 0)
@@ -106,4 +104,8 @@ unsigned int xfs_symlink_space_res(struct xfs_mount *mp, unsigned int namelen,
unsigned int fsblocks);
unsigned int xfs_remove_space_res(struct xfs_mount *mp, unsigned int namelen);
+unsigned int xfs_rename_space_res(struct xfs_mount *mp,
+ unsigned int src_namelen, bool target_exists,
+ unsigned int target_namelen, bool has_whiteout);
+
#endif /* __XFS_TRANS_SPACE_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 068/115] xfs: don't return XFS_ATTR_PARENT attributes via listxattr
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (66 preceding siblings ...)
2024-07-30 0:41 ` [PATCH 067/115] xfs: Add parent pointers to rename Darrick J. Wong
@ 2024-07-30 0:41 ` Darrick J. Wong
2024-07-30 0:41 ` [PATCH 069/115] xfs: pass the attr value to put_listent when possible Darrick J. Wong
` (46 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:41 UTC (permalink / raw)
To: djwong, cem; +Cc: Allison Henderson, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: daf9f884906bcfcffe26967aee9ece893fba019b
Parent pointers are internal filesystem metadata. They're not intended
to be directly visible to userspace, so filter them out of
xfs_xattr_put_listent so that they don't appear in listxattr.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Inspired-by: Andrey Albershteyn <aalbersh@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: change this to XFS_ATTR_PRIVATE_NSP_MASK per fsverity patchset]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_da_format.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index 1395ad193..ebde6eb1d 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -726,6 +726,9 @@ struct xfs_attr3_leafblock {
XFS_ATTR_SECURE | \
XFS_ATTR_PARENT)
+/* Private attr namespaces not exposed to userspace */
+#define XFS_ATTR_PRIVATE_NSP_MASK (XFS_ATTR_PARENT)
+
#define XFS_ATTR_ONDISK_MASK (XFS_ATTR_NSP_ONDISK_MASK | \
XFS_ATTR_LOCAL | \
XFS_ATTR_INCOMPLETE)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 069/115] xfs: pass the attr value to put_listent when possible
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (67 preceding siblings ...)
2024-07-30 0:41 ` [PATCH 068/115] xfs: don't return XFS_ATTR_PARENT attributes via listxattr Darrick J. Wong
@ 2024-07-30 0:41 ` Darrick J. Wong
2024-07-30 0:42 ` [PATCH 070/115] xfs: split out handle management helpers a bit Darrick J. Wong
` (45 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:41 UTC (permalink / raw)
To: djwong, cem; +Cc: Allison Henderson, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: 8f4b980ee67fe53a77b70b1fdd8e15f2fe37180c
Pass the attr value to put_listent when we have local xattrs or
shortform xattrs. This will enable the GETPARENTS ioctl to use
xfs_attr_list as its backend.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.h | 5 +++--
libxfs/xfs_attr_sf.h | 1 +
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index d0ed7ea58..d12583dd7 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -47,8 +47,9 @@ struct xfs_attrlist_cursor_kern {
/* void; state communicated via *context */
-typedef void (*put_listent_func_t)(struct xfs_attr_list_context *, int,
- unsigned char *, int, int);
+typedef void (*put_listent_func_t)(struct xfs_attr_list_context *context,
+ int flags, unsigned char *name, int namelen, void *value,
+ int valuelen);
struct xfs_attr_list_context {
struct xfs_trans *tp;
diff --git a/libxfs/xfs_attr_sf.h b/libxfs/xfs_attr_sf.h
index bc4422223..73bdc0e55 100644
--- a/libxfs/xfs_attr_sf.h
+++ b/libxfs/xfs_attr_sf.h
@@ -16,6 +16,7 @@ typedef struct xfs_attr_sf_sort {
uint8_t flags; /* flags bits (see xfs_attr_leaf.h) */
xfs_dahash_t hash; /* this entry's hash value */
unsigned char *name; /* name value, pointer into buffer */
+ void *value;
} xfs_attr_sf_sort_t;
#define XFS_ATTR_SF_ENTSIZE_MAX /* max space for name&value */ \
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 070/115] xfs: split out handle management helpers a bit
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (68 preceding siblings ...)
2024-07-30 0:41 ` [PATCH 069/115] xfs: pass the attr value to put_listent when possible Darrick J. Wong
@ 2024-07-30 0:42 ` Darrick J. Wong
2024-07-30 0:42 ` [PATCH 071/115] xfs: add parent pointer ioctls Darrick J. Wong
` (44 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:42 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: b8c9d4253da43c02b287831f7e576568f24fbe58
Split out the functions that generate file/fs handles and map them back
into dentries in preparation for the GETPARENTS ioctl next.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_fs.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index 53526fca7..97384ab95 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -633,7 +633,9 @@ typedef struct xfs_fsop_attrmulti_handlereq {
/*
* per machine unique filesystem identifier types.
*/
-typedef struct { __u32 val[2]; } xfs_fsid_t; /* file system id type */
+typedef struct xfs_fsid {
+ __u32 val[2]; /* file system id type */
+} xfs_fsid_t;
typedef struct xfs_fid {
__u16 fid_len; /* length of remainder */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 071/115] xfs: add parent pointer ioctls
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (69 preceding siblings ...)
2024-07-30 0:42 ` [PATCH 070/115] xfs: split out handle management helpers a bit Darrick J. Wong
@ 2024-07-30 0:42 ` Darrick J. Wong
2024-07-30 0:42 ` [PATCH 072/115] xfs: don't remove the attr fork when parent pointers are enabled Darrick J. Wong
` (43 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:42 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 233f4e12bbb2c5fb1588b857336a26e8bb6942af
This patch adds a pair of new file ioctls to retrieve the parent pointer
of a given inode. They both return the same results, but one operates
on the file descriptor passed to ioctl() whereas the other allows the
caller to specify a file handle for which the caller wants results.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_fs.h | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_ondisk.h | 5 +++
libxfs/xfs_parent.c | 34 +++++++++++++++++++++++
libxfs/xfs_parent.h | 5 +++
4 files changed, 118 insertions(+)
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index 97384ab95..ea654df05 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -816,6 +816,78 @@ struct xfs_exchange_range {
XFS_EXCHANGE_RANGE_DRY_RUN | \
XFS_EXCHANGE_RANGE_FILE1_WRITTEN)
+/* Iterating parent pointers of files. */
+
+/* target was the root directory */
+#define XFS_GETPARENTS_OFLAG_ROOT (1U << 0)
+
+/* Cursor is done iterating pptrs */
+#define XFS_GETPARENTS_OFLAG_DONE (1U << 1)
+
+#define XFS_GETPARENTS_OFLAGS_ALL (XFS_GETPARENTS_OFLAG_ROOT | \
+ XFS_GETPARENTS_OFLAG_DONE)
+
+#define XFS_GETPARENTS_IFLAGS_ALL (0)
+
+struct xfs_getparents_rec {
+ struct xfs_handle gpr_parent; /* Handle to parent */
+ __u32 gpr_reclen; /* Length of entire record */
+ __u32 gpr_reserved; /* zero */
+ char gpr_name[]; /* Null-terminated filename */
+};
+
+/* Iterate through this file's directory parent pointers */
+struct xfs_getparents {
+ /*
+ * Structure to track progress in iterating the parent pointers.
+ * Must be initialized to zeroes before the first ioctl call, and
+ * not touched by callers after that.
+ */
+ struct xfs_attrlist_cursor gp_cursor;
+
+ /* Input flags: XFS_GETPARENTS_IFLAG* */
+ __u16 gp_iflags;
+
+ /* Output flags: XFS_GETPARENTS_OFLAG* */
+ __u16 gp_oflags;
+
+ /* Size of the gp_buffer in bytes */
+ __u32 gp_bufsize;
+
+ /* Must be set to zero */
+ __u64 gp_reserved;
+
+ /* Pointer to a buffer in which to place xfs_getparents_rec */
+ __u64 gp_buffer;
+};
+
+static inline struct xfs_getparents_rec *
+xfs_getparents_first_rec(struct xfs_getparents *gp)
+{
+ return (struct xfs_getparents_rec *)(uintptr_t)gp->gp_buffer;
+}
+
+static inline struct xfs_getparents_rec *
+xfs_getparents_next_rec(struct xfs_getparents *gp,
+ struct xfs_getparents_rec *gpr)
+{
+ void *next = ((void *)gpr + gpr->gpr_reclen);
+ void *end = (void *)(uintptr_t)(gp->gp_buffer + gp->gp_bufsize);
+
+ if (next >= end)
+ return NULL;
+
+ return next;
+}
+
+/* Iterate through this file handle's directory parent pointers. */
+struct xfs_getparents_by_handle {
+ /* Handle to file whose parents we want. */
+ struct xfs_handle gph_handle;
+
+ struct xfs_getparents gph_request;
+};
+
/*
* ioctl commands that are used by Linux filesystems
*/
@@ -851,6 +923,8 @@ struct xfs_exchange_range {
/* XFS_IOC_GETFSMAP ------ hoisted 59 */
#define XFS_IOC_SCRUB_METADATA _IOWR('X', 60, struct xfs_scrub_metadata)
#define XFS_IOC_AG_GEOMETRY _IOWR('X', 61, struct xfs_ag_geometry)
+#define XFS_IOC_GETPARENTS _IOWR('X', 62, struct xfs_getparents)
+#define XFS_IOC_GETPARENTS_BY_HANDLE _IOWR('X', 63, struct xfs_getparents_by_handle)
/*
* ioctl commands that replace IRIX syssgi()'s
diff --git a/libxfs/xfs_ondisk.h b/libxfs/xfs_ondisk.h
index 25952ef58..e8cdd77d0 100644
--- a/libxfs/xfs_ondisk.h
+++ b/libxfs/xfs_ondisk.h
@@ -156,6 +156,11 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_OFFSET(struct xfs_efi_log_format_32, efi_extents, 16);
XFS_CHECK_OFFSET(struct xfs_efi_log_format_64, efi_extents, 16);
+ /* parent pointer ioctls */
+ XFS_CHECK_STRUCT_SIZE(struct xfs_getparents_rec, 32);
+ XFS_CHECK_STRUCT_SIZE(struct xfs_getparents, 40);
+ XFS_CHECK_STRUCT_SIZE(struct xfs_getparents_by_handle, 64);
+
/*
* The v5 superblock format extended several v4 header structures with
* additional data. While new fields are only accessible on v5
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index a53b7d13d..bb0465197 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -254,3 +254,37 @@ xfs_parent_replacename(
xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REPLACE);
return 0;
}
+
+/*
+ * Extract parent pointer information from any parent pointer xattr into
+ * @parent_ino/gen. The last two parameters can be NULL pointers.
+ *
+ * Returns 0 if this is not a parent pointer xattr at all; or -EFSCORRUPTED for
+ * garbage.
+ */
+int
+xfs_parent_from_attr(
+ struct xfs_mount *mp,
+ unsigned int attr_flags,
+ const unsigned char *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen,
+ xfs_ino_t *parent_ino,
+ uint32_t *parent_gen)
+{
+ const struct xfs_parent_rec *rec = value;
+
+ ASSERT(attr_flags & XFS_ATTR_PARENT);
+
+ if (!xfs_parent_namecheck(attr_flags, name, namelen))
+ return -EFSCORRUPTED;
+ if (!xfs_parent_valuecheck(mp, value, valuelen))
+ return -EFSCORRUPTED;
+
+ if (parent_ino)
+ *parent_ino = be64_to_cpu(rec->p_ino);
+ if (parent_gen)
+ *parent_gen = be32_to_cpu(rec->p_gen);
+ return 0;
+}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index 768633b31..d7ab09e73 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -91,4 +91,9 @@ int xfs_parent_replacename(struct xfs_trans *tp,
struct xfs_inode *new_dp, const struct xfs_name *new_name,
struct xfs_inode *child);
+int xfs_parent_from_attr(struct xfs_mount *mp, unsigned int attr_flags,
+ const unsigned char *name, unsigned int namelen,
+ const void *value, unsigned int valuelen,
+ xfs_ino_t *parent_ino, uint32_t *parent_gen);
+
#endif /* __XFS_PARENT_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 072/115] xfs: don't remove the attr fork when parent pointers are enabled
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (70 preceding siblings ...)
2024-07-30 0:42 ` [PATCH 071/115] xfs: add parent pointer ioctls Darrick J. Wong
@ 2024-07-30 0:42 ` Darrick J. Wong
2024-07-30 0:42 ` [PATCH 073/115] xfs: add a incompat feature bit for parent pointers Darrick J. Wong
` (42 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:42 UTC (permalink / raw)
To: djwong, cem; +Cc: Allison Henderson, Christoph Hellwig, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: 7dafb449b7922c1eec6fee3ed85b679d51f0f431
When an inode is removed, it may also cause the attribute fork to be
removed if it is the last attribute. This transaction gets flushed to
the log, but if the system goes down before we could inactivate the symlink,
the log recovery tries to inactivate this inode (since it is on the unlinked
list) but the verifier trips over the remote value and leaks it.
Hence we ended up with a file in this odd state on a "clean" mount. The
"obvious" fix is to prohibit erasure of the attr fork to avoid tripping
over the verifiers when pptrs are enabled.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr_leaf.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c
index faa357f15..ce20d81a4 100644
--- a/libxfs/xfs_attr_leaf.c
+++ b/libxfs/xfs_attr_leaf.c
@@ -888,7 +888,8 @@ xfs_attr_sf_removename(
*/
if (totsize == sizeof(struct xfs_attr_sf_hdr) && xfs_has_attr2(mp) &&
(dp->i_df.if_format != XFS_DINODE_FMT_BTREE) &&
- !(args->op_flags & (XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE))) {
+ !(args->op_flags & (XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE)) &&
+ !xfs_has_parent(mp)) {
xfs_attr_fork_remove(dp, args->trans);
} else {
xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
@@ -897,7 +898,8 @@ xfs_attr_sf_removename(
ASSERT(totsize > sizeof(struct xfs_attr_sf_hdr) ||
(args->op_flags & XFS_DA_OP_ADDNAME) ||
!xfs_has_attr2(mp) ||
- dp->i_df.if_format == XFS_DINODE_FMT_BTREE);
+ dp->i_df.if_format == XFS_DINODE_FMT_BTREE ||
+ xfs_has_parent(mp));
xfs_trans_log_inode(args->trans, dp,
XFS_ILOG_CORE | XFS_ILOG_ADATA);
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 073/115] xfs: add a incompat feature bit for parent pointers
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (71 preceding siblings ...)
2024-07-30 0:42 ` [PATCH 072/115] xfs: don't remove the attr fork when parent pointers are enabled Darrick J. Wong
@ 2024-07-30 0:42 ` Darrick J. Wong
2024-07-30 0:43 ` [PATCH 074/115] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res Darrick J. Wong
` (41 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:42 UTC (permalink / raw)
To: djwong, cem
Cc: Mark Tinguely, Dave Chinner, Allison Henderson, Darrick J. Wong,
linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: 5f98ec1cb5c264e4815e21d632ee0b3d6e700e3d
Create an incompat feature bit and a fs geometry flag so that we can
enable the feature in the ondisk superblock and advertise its existence
to userspace.
Signed-off-by: Mark Tinguely <mark.tinguely@oracle.com>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_format.h | 1 +
libxfs/xfs_fs.h | 1 +
libxfs/xfs_sb.c | 4 ++++
3 files changed, 6 insertions(+)
diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h
index f1818c54a..b457e457e 100644
--- a/libxfs/xfs_format.h
+++ b/libxfs/xfs_format.h
@@ -374,6 +374,7 @@ xfs_sb_has_ro_compat_feature(
#define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */
#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */
#define XFS_SB_FEAT_INCOMPAT_EXCHRANGE (1 << 6) /* exchangerange supported */
+#define XFS_SB_FEAT_INCOMPAT_PARENT (1 << 7) /* parent pointers */
#define XFS_SB_FEAT_INCOMPAT_ALL \
(XFS_SB_FEAT_INCOMPAT_FTYPE | \
XFS_SB_FEAT_INCOMPAT_SPINODES | \
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index ea654df05..dd13bfa50 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -240,6 +240,7 @@ typedef struct xfs_fsop_resblks {
#define XFS_FSOP_GEOM_FLAGS_INOBTCNT (1 << 22) /* inobt btree counter */
#define XFS_FSOP_GEOM_FLAGS_NREXT64 (1 << 23) /* large extent counters */
#define XFS_FSOP_GEOM_FLAGS_EXCHANGE_RANGE (1 << 24) /* exchange range */
+#define XFS_FSOP_GEOM_FLAGS_PARENT (1 << 25) /* linux parent pointers */
/*
* Minimum and maximum sizes need for growth checks.
diff --git a/libxfs/xfs_sb.c b/libxfs/xfs_sb.c
index 2db43b805..f45ffd994 100644
--- a/libxfs/xfs_sb.c
+++ b/libxfs/xfs_sb.c
@@ -175,6 +175,8 @@ xfs_sb_version_to_features(
features |= XFS_FEAT_NREXT64;
if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_EXCHRANGE)
features |= XFS_FEAT_EXCHANGE_RANGE;
+ if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_PARENT)
+ features |= XFS_FEAT_PARENT;
return features;
}
@@ -1251,6 +1253,8 @@ xfs_fs_geometry(
geo->flags |= XFS_FSOP_GEOM_FLAGS_BIGTIME;
if (xfs_has_inobtcounts(mp))
geo->flags |= XFS_FSOP_GEOM_FLAGS_INOBTCNT;
+ if (xfs_has_parent(mp))
+ geo->flags |= XFS_FSOP_GEOM_FLAGS_PARENT;
if (xfs_has_sector(mp)) {
geo->flags |= XFS_FSOP_GEOM_FLAGS_SECTOR;
geo->logsectsize = sbp->sb_logsectsize;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 074/115] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (72 preceding siblings ...)
2024-07-30 0:42 ` [PATCH 073/115] xfs: add a incompat feature bit for parent pointers Darrick J. Wong
@ 2024-07-30 0:43 ` Darrick J. Wong
2024-07-30 0:43 ` [PATCH 075/115] xfs: drop compatibility minimum log size computations for reflink Darrick J. Wong
` (40 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:43 UTC (permalink / raw)
To: djwong, cem; +Cc: Allison Henderson, Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 7ea816ca4043c2bc6052f696b6aebe2c22980a03
Dave and I were discussing some recent test regressions as a result of
me turning on nrext64=1 on realtime filesystems, when we noticed that
the minimum log size of a 32M filesystem jumped from 954 blocks to 4287
blocks.
Digging through xfs_log_calc_max_attrsetm_res, Dave noticed that @size
contains the maximum estimated amount of space needed for a local format
xattr, in bytes, but we feed this quantity to XFS_NEXTENTADD_SPACE_RES,
which requires units of blocks. This has resulted in an overestimation
of the minimum log size over the years.
We should nominally correct this, but there's a backwards compatibility
problem -- if we enable it now, the minimum log size will decrease. If
a corrected mkfs formats a filesystem with this new smaller log size, a
user will encounter mount failures on an uncorrected kernel due to the
larger minimum log size computations there.
Therefore, turn this on for parent pointers because it wasn't merged at
all upstream when this issue was discovered.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_log_rlimit.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/libxfs/xfs_log_rlimit.c b/libxfs/xfs_log_rlimit.c
index cba24493f..a7bbd2933 100644
--- a/libxfs/xfs_log_rlimit.c
+++ b/libxfs/xfs_log_rlimit.c
@@ -16,6 +16,29 @@
#include "xfs_bmap_btree.h"
#include "xfs_trace.h"
+/*
+ * Shortly after enabling the large extents count feature in 2023, longstanding
+ * bugs were found in the code that computes the minimum log size. Luckily,
+ * the bugs resulted in over-estimates of that size, so there's no impact to
+ * existing users. However, we don't want to reduce the minimum log size
+ * because that can create the situation where a newer mkfs writes a new
+ * filesystem that an older kernel won't mount.
+ *
+ * Therefore, we only may correct the computation starting with filesystem
+ * features that didn't exist in 2023. In other words, only turn this on if
+ * the filesystem has parent pointers.
+ *
+ * This function can be called before the XFS_HAS_* flags have been set up,
+ * (e.g. mkfs) so we must check the ondisk superblock.
+ */
+static inline bool
+xfs_want_minlogsize_fixes(
+ struct xfs_sb *sb)
+{
+ return xfs_sb_is_v5(sb) &&
+ xfs_sb_has_incompat_feature(sb, XFS_SB_FEAT_INCOMPAT_PARENT);
+}
+
/*
* Calculate the maximum length in bytes that would be required for a local
* attribute value as large attributes out of line are not logged.
@@ -31,6 +54,15 @@ xfs_log_calc_max_attrsetm_res(
MAXNAMELEN - 1;
nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
nblks += XFS_B_TO_FSB(mp, size);
+
+ /*
+ * If the feature set is new enough, correct a unit conversion error in
+ * the xattr transaction reservation code that resulted in oversized
+ * minimum log size computations.
+ */
+ if (xfs_want_minlogsize_fixes(&mp->m_sb))
+ size = XFS_B_TO_FSB(mp, size);
+
nblks += XFS_NEXTENTADD_SPACE_RES(mp, size, XFS_ATTR_FORK);
return M_RES(mp)->tr_attrsetm.tr_logres +
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 075/115] xfs: drop compatibility minimum log size computations for reflink
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (73 preceding siblings ...)
2024-07-30 0:43 ` [PATCH 074/115] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res Darrick J. Wong
@ 2024-07-30 0:43 ` Darrick J. Wong
2024-07-30 0:43 ` [PATCH 076/115] xfs: enable parent pointers Darrick J. Wong
` (39 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:43 UTC (permalink / raw)
To: djwong, cem; +Cc: Allison Henderson, Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 6ed858c7c678218aa8df9d9e75d5e9955c105415
Let's also drop the oversized minimum log computations for reflink and
rmap that were the result of bugs introduced many years ago.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_log_rlimit.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/libxfs/xfs_log_rlimit.c b/libxfs/xfs_log_rlimit.c
index a7bbd2933..246d5f486 100644
--- a/libxfs/xfs_log_rlimit.c
+++ b/libxfs/xfs_log_rlimit.c
@@ -24,6 +24,11 @@
* because that can create the situation where a newer mkfs writes a new
* filesystem that an older kernel won't mount.
*
+ * Several years prior, we also discovered that the transaction reservations
+ * for rmap and reflink operations were unnecessarily large. That was fixed,
+ * but the minimum log size computation was left alone to avoid the
+ * compatibility problems noted above. Fix that too.
+ *
* Therefore, we only may correct the computation starting with filesystem
* features that didn't exist in 2023. In other words, only turn this on if
* the filesystem has parent pointers.
@@ -80,6 +85,15 @@ xfs_log_calc_trans_resv_for_minlogblocks(
{
unsigned int rmap_maxlevels = mp->m_rmap_maxlevels;
+ /*
+ * If the feature set is new enough, drop the oversized minimum log
+ * size computation introduced by the original reflink code.
+ */
+ if (xfs_want_minlogsize_fixes(&mp->m_sb)) {
+ xfs_trans_resv_calc(mp, resv);
+ return;
+ }
+
/*
* In the early days of rmap+reflink, we always set the rmap maxlevels
* to 9 even if the AG was small enough that it would never grow to
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 076/115] xfs: enable parent pointers
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (74 preceding siblings ...)
2024-07-30 0:43 ` [PATCH 075/115] xfs: drop compatibility minimum log size computations for reflink Darrick J. Wong
@ 2024-07-30 0:43 ` Darrick J. Wong
2024-07-30 0:43 ` [PATCH 077/115] xfs: check dirents have " Darrick J. Wong
` (38 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:43 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 67ac7091e35bd34b75c0ec77331b53ca052e0cb3
Add parent pointers to the list of supported features.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_format.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h
index b457e457e..61f51becf 100644
--- a/libxfs/xfs_format.h
+++ b/libxfs/xfs_format.h
@@ -382,7 +382,8 @@ xfs_sb_has_ro_compat_feature(
XFS_SB_FEAT_INCOMPAT_BIGTIME | \
XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \
XFS_SB_FEAT_INCOMPAT_NREXT64 | \
- XFS_SB_FEAT_INCOMPAT_EXCHRANGE)
+ XFS_SB_FEAT_INCOMPAT_EXCHRANGE | \
+ XFS_SB_FEAT_INCOMPAT_PARENT)
#define XFS_SB_FEAT_INCOMPAT_UNKNOWN ~XFS_SB_FEAT_INCOMPAT_ALL
static inline bool
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 077/115] xfs: check dirents have parent pointers
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (75 preceding siblings ...)
2024-07-30 0:43 ` [PATCH 076/115] xfs: enable parent pointers Darrick J. Wong
@ 2024-07-30 0:43 ` Darrick J. Wong
2024-07-30 0:44 ` [PATCH 078/115] xfs: remove some boilerplate from xfs_attr_set Darrick J. Wong
` (37 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:43 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 61b3f0df5c235806d372aaf696ce9aee7746d18f
If the fs has parent pointers, we need to check that each child dirent
points to a file that has a parent pointer pointing back at us.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_parent.c | 22 ++++++++++++++++++++++
libxfs/xfs_parent.h | 5 +++++
2 files changed, 27 insertions(+)
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index bb0465197..8c29ba61c 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -288,3 +288,25 @@ xfs_parent_from_attr(
*parent_gen = be32_to_cpu(rec->p_gen);
return 0;
}
+
+/*
+ * Look up a parent pointer record (@parent_name -> @pptr) of @ip.
+ *
+ * Caller must hold at least ILOCK_SHARED. The scratchpad need not be
+ * initialized.
+ *
+ * Returns 0 if the pointer is found, -ENOATTR if there is no match, or a
+ * negative errno.
+ */
+int
+xfs_parent_lookup(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ const struct xfs_name *parent_name,
+ struct xfs_parent_rec *pptr,
+ struct xfs_da_args *scratch)
+{
+ memset(scratch, 0, sizeof(struct xfs_da_args));
+ xfs_parent_da_args_init(scratch, tp, pptr, ip, ip->i_ino, parent_name);
+ return xfs_attr_get_ilocked(scratch);
+}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index d7ab09e73..977885823 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -96,4 +96,9 @@ int xfs_parent_from_attr(struct xfs_mount *mp, unsigned int attr_flags,
const void *value, unsigned int valuelen,
xfs_ino_t *parent_ino, uint32_t *parent_gen);
+/* Repair functions */
+int xfs_parent_lookup(struct xfs_trans *tp, struct xfs_inode *ip,
+ const struct xfs_name *name, struct xfs_parent_rec *pptr,
+ struct xfs_da_args *scratch);
+
#endif /* __XFS_PARENT_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 078/115] xfs: remove some boilerplate from xfs_attr_set
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (76 preceding siblings ...)
2024-07-30 0:43 ` [PATCH 077/115] xfs: check dirents have " Darrick J. Wong
@ 2024-07-30 0:44 ` Darrick J. Wong
2024-07-30 0:44 ` [PATCH 079/115] xfs: make the reserved block permission flag explicit in xfs_attr_set Darrick J. Wong
` (36 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:44 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: e7420e75ef04787bc51688fc9bbca7da4d164a1e
In preparation for online/offline repair wanting to use xfs_attr_set,
move some of the boilerplate out of this function into the callers.
Repair can initialize the da_args completely, and the userspace flag
handling/twisting goes away once we move it to xfs_attr_change.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 33 ++++++++++++---------------------
1 file changed, 12 insertions(+), 21 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 344b34aa4..1034579a1 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -947,6 +947,16 @@ xfs_attr_lookup(
return error;
}
+/*
+ * Make a change to the xattr structure.
+ *
+ * The caller must have initialized @args, attached dquots, and must not hold
+ * any ILOCKs.
+ *
+ * Returns -EEXIST for XFS_ATTRUPDATE_CREATE if the name already exists.
+ * Returns -ENOATTR for XFS_ATTRUPDATE_REMOVE if the name does not exist.
+ * Returns 0 on success, or a negative errno if something else went wrong.
+ */
int
xfs_attr_set(
struct xfs_da_args *args,
@@ -960,27 +970,7 @@ xfs_attr_set(
int rmt_blks = 0;
unsigned int total;
- if (xfs_is_shutdown(dp->i_mount))
- return -EIO;
-
- error = xfs_qm_dqattach(dp);
- if (error)
- return error;
-
- if (!args->owner)
- args->owner = args->dp->i_ino;
- args->geo = mp->m_attr_geo;
- args->whichfork = XFS_ATTR_FORK;
- xfs_attr_sethash(args);
-
- /*
- * We have no control over the attribute names that userspace passes us
- * to remove, so we have to allow the name lookup prior to attribute
- * removal to fail as well. Preserve the logged flag, since we need
- * to pass that through to the logging code.
- */
- args->op_flags = XFS_DA_OP_OKNOENT |
- (args->op_flags & XFS_DA_OP_LOGGED);
+ ASSERT(!args->trans);
switch (op) {
case XFS_ATTRUPDATE_UPSERT:
@@ -1075,6 +1065,7 @@ xfs_attr_set(
error = xfs_trans_commit(args->trans);
out_unlock:
xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ args->trans = NULL;
return error;
out_trans_cancel:
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 079/115] xfs: make the reserved block permission flag explicit in xfs_attr_set
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (77 preceding siblings ...)
2024-07-30 0:44 ` [PATCH 078/115] xfs: remove some boilerplate from xfs_attr_set Darrick J. Wong
@ 2024-07-30 0:44 ` Darrick J. Wong
2024-07-30 0:44 ` [PATCH 080/115] xfs: add raw parent pointer apis to support repair Darrick J. Wong
` (35 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:44 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: bf61c36a45d4c215994699a7a06a00c58d22e8a2
Make the use of reserved blocks an explicit parameter to xfs_attr_set.
Userspace setting XFS_ATTR_ROOT attrs should continue to be able to use
it, but for online repairs we can back out and therefore do not care.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/attrset.c | 4 ++--
libxfs/xfs_attr.c | 6 +++---
libxfs/xfs_attr.h | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/db/attrset.c b/db/attrset.c
index a59d5473e..3b5db7c2a 100644
--- a/db/attrset.c
+++ b/db/attrset.c
@@ -161,7 +161,7 @@ attr_set_f(
goto out;
}
- if (libxfs_attr_set(&args, op)) {
+ if (libxfs_attr_set(&args, op, false)) {
dbprintf(_("failed to set attr %s on inode %llu\n"),
args.name, (unsigned long long)iocur_top->ino);
goto out;
@@ -247,7 +247,7 @@ attr_remove_f(
goto out;
}
- if (libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE)) {
+ if (libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE, false)) {
dbprintf(_("failed to remove attr %s from inode %llu\n"),
(unsigned char *)args.name,
(unsigned long long)iocur_top->ino);
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 1034579a1..32dd7bcf5 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -951,7 +951,7 @@ xfs_attr_lookup(
* Make a change to the xattr structure.
*
* The caller must have initialized @args, attached dquots, and must not hold
- * any ILOCKs.
+ * any ILOCKs. Reserved data blocks may be used if @rsvd is set.
*
* Returns -EEXIST for XFS_ATTRUPDATE_CREATE if the name already exists.
* Returns -ENOATTR for XFS_ATTRUPDATE_REMOVE if the name does not exist.
@@ -960,12 +960,12 @@ xfs_attr_lookup(
int
xfs_attr_set(
struct xfs_da_args *args,
- enum xfs_attr_update op)
+ enum xfs_attr_update op,
+ bool rsvd)
{
struct xfs_inode *dp = args->dp;
struct xfs_mount *mp = dp->i_mount;
struct xfs_trans_res tres;
- bool rsvd = (args->attr_filter & XFS_ATTR_ROOT);
int error, local;
int rmt_blks = 0;
unsigned int total;
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index d12583dd7..43dee4cba 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -558,7 +558,7 @@ enum xfs_attr_update {
XFS_ATTRUPDATE_REPLACE, /* set value, fail if attr does not exist */
};
-int xfs_attr_set(struct xfs_da_args *args, enum xfs_attr_update op);
+int xfs_attr_set(struct xfs_da_args *args, enum xfs_attr_update op, bool rsvd);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
bool xfs_attr_check_namespace(unsigned int attr_flags);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 080/115] xfs: add raw parent pointer apis to support repair
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (78 preceding siblings ...)
2024-07-30 0:44 ` [PATCH 079/115] xfs: make the reserved block permission flag explicit in xfs_attr_set Darrick J. Wong
@ 2024-07-30 0:44 ` Darrick J. Wong
2024-07-30 0:44 ` [PATCH 081/115] xfs: remove pointless unlocked assertion Darrick J. Wong
` (34 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:44 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 5769aa41ee34d4d1cc2b35376107b8e9694698f0
Add a couple of utility functions to set or remove parent pointers from
a file. These functions will be used by repair code, hence they skip
the xattr logging that regular parent pointer updates use.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_dir2.c | 2 +-
libxfs/xfs_dir2.h | 2 +-
libxfs/xfs_parent.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_parent.h | 6 +++++
4 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_dir2.c b/libxfs/xfs_dir2.c
index 803fb82b2..e309e1e58 100644
--- a/libxfs/xfs_dir2.c
+++ b/libxfs/xfs_dir2.c
@@ -433,7 +433,7 @@ int
xfs_dir_removename(
struct xfs_trans *tp,
struct xfs_inode *dp,
- struct xfs_name *name,
+ const struct xfs_name *name,
xfs_ino_t ino,
xfs_extlen_t total) /* bmap's total block count */
{
diff --git a/libxfs/xfs_dir2.h b/libxfs/xfs_dir2.h
index eb3a5c350..b580a78bc 100644
--- a/libxfs/xfs_dir2.h
+++ b/libxfs/xfs_dir2.h
@@ -58,7 +58,7 @@ extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
const struct xfs_name *name, xfs_ino_t *inum,
struct xfs_name *ci_name);
extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
- struct xfs_name *name, xfs_ino_t ino,
+ const struct xfs_name *name, xfs_ino_t ino,
xfs_extlen_t tot);
extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
const struct xfs_name *name, xfs_ino_t inum,
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index 8c29ba61c..84220f10a 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -310,3 +310,67 @@ xfs_parent_lookup(
xfs_parent_da_args_init(scratch, tp, pptr, ip, ip->i_ino, parent_name);
return xfs_attr_get_ilocked(scratch);
}
+
+/* Sanity-check a parent pointer before we try to perform repairs. */
+static inline bool
+xfs_parent_sanity_check(
+ struct xfs_mount *mp,
+ const struct xfs_name *parent_name,
+ const struct xfs_parent_rec *pptr)
+{
+ if (!xfs_parent_namecheck(XFS_ATTR_PARENT, parent_name->name,
+ parent_name->len))
+ return false;
+
+ if (!xfs_parent_valuecheck(mp, pptr, sizeof(*pptr)))
+ return false;
+
+ return true;
+}
+
+
+/*
+ * Attach the parent pointer (@parent_name -> @pptr) to @ip immediately.
+ * Caller must not have a transaction or hold the ILOCK. This is for
+ * specialized repair functions only. The scratchpad need not be initialized.
+ */
+int
+xfs_parent_set(
+ struct xfs_inode *ip,
+ xfs_ino_t owner,
+ const struct xfs_name *parent_name,
+ struct xfs_parent_rec *pptr,
+ struct xfs_da_args *scratch)
+{
+ if (!xfs_parent_sanity_check(ip->i_mount, parent_name, pptr)) {
+ ASSERT(0);
+ return -EFSCORRUPTED;
+ }
+
+ memset(scratch, 0, sizeof(struct xfs_da_args));
+ xfs_parent_da_args_init(scratch, NULL, pptr, ip, owner, parent_name);
+ return xfs_attr_set(scratch, XFS_ATTRUPDATE_CREATE, false);
+}
+
+/*
+ * Remove the parent pointer (@parent_name -> @pptr) from @ip immediately.
+ * Caller must not have a transaction or hold the ILOCK. This is for
+ * specialized repair functions only. The scratchpad need not be initialized.
+ */
+int
+xfs_parent_unset(
+ struct xfs_inode *ip,
+ xfs_ino_t owner,
+ const struct xfs_name *parent_name,
+ struct xfs_parent_rec *pptr,
+ struct xfs_da_args *scratch)
+{
+ if (!xfs_parent_sanity_check(ip->i_mount, parent_name, pptr)) {
+ ASSERT(0);
+ return -EFSCORRUPTED;
+ }
+
+ memset(scratch, 0, sizeof(struct xfs_da_args));
+ xfs_parent_da_args_init(scratch, NULL, pptr, ip, owner, parent_name);
+ return xfs_attr_set(scratch, XFS_ATTRUPDATE_REMOVE, false);
+}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index 977885823..b8036527c 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -100,5 +100,11 @@ int xfs_parent_from_attr(struct xfs_mount *mp, unsigned int attr_flags,
int xfs_parent_lookup(struct xfs_trans *tp, struct xfs_inode *ip,
const struct xfs_name *name, struct xfs_parent_rec *pptr,
struct xfs_da_args *scratch);
+int xfs_parent_set(struct xfs_inode *ip, xfs_ino_t owner,
+ const struct xfs_name *name, struct xfs_parent_rec *pptr,
+ struct xfs_da_args *scratch);
+int xfs_parent_unset(struct xfs_inode *ip, xfs_ino_t owner,
+ const struct xfs_name *name, struct xfs_parent_rec *pptr,
+ struct xfs_da_args *scratch);
#endif /* __XFS_PARENT_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 081/115] xfs: remove pointless unlocked assertion
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (79 preceding siblings ...)
2024-07-30 0:44 ` [PATCH 080/115] xfs: add raw parent pointer apis to support repair Darrick J. Wong
@ 2024-07-30 0:44 ` Darrick J. Wong
2024-07-30 0:45 ` [PATCH 082/115] xfs: split xfs_bmap_add_attrfork into two pieces Darrick J. Wong
` (33 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:44 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 13db7007892694c891fc37feccbd2ac8f227af78
Remove this assertion about the inode not having an attr fork from
xfs_bmap_add_attrfork because the function handles that case just fine.
Weirder still, the function actually /requires/ the caller not to hold
the ILOCK, which means that its accesses are not stabilized.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_bmap.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 81dccf275..4cd0ffa42 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -1035,8 +1035,6 @@ xfs_bmap_add_attrfork(
int logflags; /* logging flags */
int error; /* error return value */
- ASSERT(xfs_inode_has_attr_fork(ip) == 0);
-
mp = ip->i_mount;
ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 082/115] xfs: split xfs_bmap_add_attrfork into two pieces
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (80 preceding siblings ...)
2024-07-30 0:44 ` [PATCH 081/115] xfs: remove pointless unlocked assertion Darrick J. Wong
@ 2024-07-30 0:45 ` Darrick J. Wong
2024-07-30 0:45 ` [PATCH 083/115] xfs: actually rebuild the parent pointer xattrs Darrick J. Wong
` (32 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:45 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 55edcd1f86474f973fccf5c5ccc8bc7908893142
Split this function into two pieces -- one to make the actual changes to
the inode core to add the attr fork, and another one to deal with
getting the transaction and locking the inodes.
The next couple of patches will need this to be split into two. One
patch implements committing new parent pointer recordsets to damaged
files. If one file has an attr fork and the other does not, we have to
create the missing attr fork before the atomic swap transaction, and can
use the behavior encoded in the current xfs_bmap_add_attrfork.
The second patch adapts /lost+found adoptions to handle parent pointers
correctly. The adoption process will add a parent pointer to a child
that is being moved to /lost+found, but this requires that the attr fork
already exists. We don't know if we're actually going to commit the
adoption until we've already reserved a transaction and taken the
ILOCKs, which means that we must have a way to bypass the start of the
current xfs_bmap_add_attrfork.
Therefore, create xfs_attr_add_fork as the helper that creates a
transaction and takes locks; and make xfs_bmap_add_attrfork the function
that updates the inode core and allocates the incore attr fork.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 39 ++++++++++++++++++++++++++++++++++++++-
libxfs/xfs_bmap.c | 36 ++++++++++--------------------------
libxfs/xfs_bmap.h | 3 ++-
3 files changed, 50 insertions(+), 28 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 32dd7bcf5..f94e74879 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -947,6 +947,43 @@ xfs_attr_lookup(
return error;
}
+STATIC int
+xfs_attr_add_fork(
+ struct xfs_inode *ip, /* incore inode pointer */
+ int size, /* space new attribute needs */
+ int rsvd) /* xact may use reserved blks */
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_trans *tp; /* transaction pointer */
+ unsigned int blks; /* space reservation */
+ int error; /* error return value */
+
+ ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
+
+ blks = XFS_ADDAFORK_SPACE_RES(mp);
+
+ error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_addafork, blks, 0,
+ rsvd, &tp);
+ if (error)
+ return error;
+
+ if (xfs_inode_has_attr_fork(ip))
+ goto trans_cancel;
+
+ error = xfs_bmap_add_attrfork(tp, ip, size, rsvd);
+ if (error)
+ goto trans_cancel;
+
+ error = xfs_trans_commit(tp);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ return error;
+
+trans_cancel:
+ xfs_trans_cancel(tp);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ return error;
+}
+
/*
* Make a change to the xattr structure.
*
@@ -988,7 +1025,7 @@ xfs_attr_set(
xfs_attr_sf_entsize_byname(args->namelen,
args->valuelen);
- error = xfs_bmap_add_attrfork(dp, sf_size, rsvd);
+ error = xfs_attr_add_fork(dp, sf_size, rsvd);
if (error)
return error;
}
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 4cd0ffa42..def73fd50 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -1019,38 +1019,29 @@ xfs_bmap_set_attrforkoff(
}
/*
- * Convert inode from non-attributed to attributed.
- * Must not be in a transaction, ip must not be locked.
+ * Convert inode from non-attributed to attributed. Caller must hold the
+ * ILOCK_EXCL and the file cannot have an attr fork.
*/
int /* error code */
xfs_bmap_add_attrfork(
- xfs_inode_t *ip, /* incore inode pointer */
+ struct xfs_trans *tp,
+ struct xfs_inode *ip, /* incore inode pointer */
int size, /* space new attribute needs */
int rsvd) /* xact may use reserved blks */
{
- xfs_mount_t *mp; /* mount structure */
- xfs_trans_t *tp; /* transaction pointer */
- int blks; /* space reservation */
+ struct xfs_mount *mp = tp->t_mountp;
int version = 1; /* superblock attr version */
int logflags; /* logging flags */
int error; /* error return value */
- mp = ip->i_mount;
+ xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
-
- blks = XFS_ADDAFORK_SPACE_RES(mp);
-
- error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_addafork, blks, 0,
- rsvd, &tp);
- if (error)
- return error;
- if (xfs_inode_has_attr_fork(ip))
- goto trans_cancel;
+ ASSERT(!xfs_inode_has_attr_fork(ip));
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
error = xfs_bmap_set_attrforkoff(ip, size, &version);
if (error)
- goto trans_cancel;
+ return error;
xfs_ifork_init_attr(ip, XFS_DINODE_FMT_EXTENTS, 0);
logflags = 0;
@@ -1071,7 +1062,7 @@ xfs_bmap_add_attrfork(
if (logflags)
xfs_trans_log_inode(tp, ip, logflags);
if (error)
- goto trans_cancel;
+ return error;
if (!xfs_has_attr(mp) ||
(!xfs_has_attr2(mp) && version == 2)) {
bool log_sb = false;
@@ -1090,14 +1081,7 @@ xfs_bmap_add_attrfork(
xfs_log_sb(tp);
}
- error = xfs_trans_commit(tp);
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
- return error;
-
-trans_cancel:
- xfs_trans_cancel(tp);
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
- return error;
+ return 0;
}
/*
diff --git a/libxfs/xfs_bmap.h b/libxfs/xfs_bmap.h
index 32fb2a455..e98849eb9 100644
--- a/libxfs/xfs_bmap.h
+++ b/libxfs/xfs_bmap.h
@@ -176,7 +176,8 @@ int xfs_bmap_longest_free_extent(struct xfs_perag *pag,
void xfs_trim_extent(struct xfs_bmbt_irec *irec, xfs_fileoff_t bno,
xfs_filblks_t len);
unsigned int xfs_bmap_compute_attr_offset(struct xfs_mount *mp);
-int xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd);
+int xfs_bmap_add_attrfork(struct xfs_trans *tp, struct xfs_inode *ip,
+ int size, int rsvd);
void xfs_bmap_local_to_extents_empty(struct xfs_trans *tp,
struct xfs_inode *ip, int whichfork);
int xfs_bmap_local_to_extents(struct xfs_trans *tp, struct xfs_inode *ip,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 083/115] xfs: actually rebuild the parent pointer xattrs
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (81 preceding siblings ...)
2024-07-30 0:45 ` [PATCH 082/115] xfs: split xfs_bmap_add_attrfork into two pieces Darrick J. Wong
@ 2024-07-30 0:45 ` Darrick J. Wong
2024-07-30 0:45 ` [PATCH 084/115] xfs: teach online scrub to find directory tree structure problems Darrick J. Wong
` (31 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:45 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: a26dc21309af68623b82b4e366cbbeb5a85ce65b
Once we've assembled all the parent pointers for a file, we need to
are embedded in the xattr structure, which means that we must write a
new extended attribute structure, again, atomically. Therefore, we must
copy the non-parent-pointer attributes from the file being repaired into
the temporary file's extended attributes and then call the atomic extent
swap mechanism to exchange the blocks.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 2 +-
libxfs/xfs_attr.h | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index f94e74879..52fcb1c4c 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -947,7 +947,7 @@ xfs_attr_lookup(
return error;
}
-STATIC int
+int
xfs_attr_add_fork(
struct xfs_inode *ip, /* incore inode pointer */
int size, /* space new attribute needs */
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index 43dee4cba..088cb7b30 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -648,5 +648,6 @@ int __init xfs_attr_intent_init_cache(void);
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);
#endif /* __XFS_ATTR_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 084/115] xfs: teach online scrub to find directory tree structure problems
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (82 preceding siblings ...)
2024-07-30 0:45 ` [PATCH 083/115] xfs: actually rebuild the parent pointer xattrs Darrick J. Wong
@ 2024-07-30 0:45 ` Darrick J. Wong
2024-07-30 0:45 ` [PATCH 085/115] xfs: report directory tree corruption in the health information Darrick J. Wong
` (30 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:45 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 928b721a11789a9363d6d7c32a1f3166a79f3b5f
Create a new scrubber that detects corruptions within the directory tree
structure itself. It can detect directories with multiple parents;
loops within the directory tree; and directory loops not accessible from
the root.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_fs.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index dd13bfa50..85f2a7e20 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -719,9 +719,10 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_QUOTACHECK 25 /* quota counters */
#define XFS_SCRUB_TYPE_NLINKS 26 /* inode link counts */
#define XFS_SCRUB_TYPE_HEALTHY 27 /* everything checked out ok */
+#define XFS_SCRUB_TYPE_DIRTREE 28 /* directory tree structure */
/* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR 28
+#define XFS_SCRUB_TYPE_NR 29
/* i: Repair this metadata. */
#define XFS_SCRUB_IFLAG_REPAIR (1u << 0)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 085/115] xfs: report directory tree corruption in the health information
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (83 preceding siblings ...)
2024-07-30 0:45 ` [PATCH 084/115] xfs: teach online scrub to find directory tree structure problems Darrick J. Wong
@ 2024-07-30 0:45 ` Darrick J. Wong
2024-07-30 0:46 ` [PATCH 086/115] xfs: introduce vectored scrub mode Darrick J. Wong
` (29 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:45 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 37056912d5721324ac28787a4f903798f7361099
Report directories that are the source of corruption in the directory
tree.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_fs.h | 1 +
libxfs/xfs_health.h | 4 +++-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index 85f2a7e20..7ae1912cd 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -411,6 +411,7 @@ struct xfs_bulkstat {
#define XFS_BS_SICK_XATTR (1 << 5) /* extended attributes */
#define XFS_BS_SICK_SYMLINK (1 << 6) /* symbolic link remote target */
#define XFS_BS_SICK_PARENT (1 << 7) /* parent pointers */
+#define XFS_BS_SICK_DIRTREE (1 << 8) /* directory tree structure */
/*
* Project quota id helpers (previously projid was 16bit only
diff --git a/libxfs/xfs_health.h b/libxfs/xfs_health.h
index 3c64b5f9b..b0edb4288 100644
--- a/libxfs/xfs_health.h
+++ b/libxfs/xfs_health.h
@@ -95,6 +95,7 @@ struct xfs_da_args;
/* Don't propagate sick status to ag health summary during inactivation */
#define XFS_SICK_INO_FORGET (1 << 12)
+#define XFS_SICK_INO_DIRTREE (1 << 13) /* directory tree structure */
/* Primary evidence of health problems in a given group. */
#define XFS_SICK_FS_PRIMARY (XFS_SICK_FS_COUNTERS | \
@@ -125,7 +126,8 @@ struct xfs_da_args;
XFS_SICK_INO_DIR | \
XFS_SICK_INO_XATTR | \
XFS_SICK_INO_SYMLINK | \
- XFS_SICK_INO_PARENT)
+ XFS_SICK_INO_PARENT | \
+ XFS_SICK_INO_DIRTREE)
#define XFS_SICK_INO_ZAPPED (XFS_SICK_INO_BMBTD_ZAPPED | \
XFS_SICK_INO_BMBTA_ZAPPED | \
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 086/115] xfs: introduce vectored scrub mode
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (84 preceding siblings ...)
2024-07-30 0:45 ` [PATCH 085/115] xfs: report directory tree corruption in the health information Darrick J. Wong
@ 2024-07-30 0:46 ` Darrick J. Wong
2024-07-30 0:46 ` [PATCH 087/115] xfs: factor out a xfs_dir_lookup_args helper Darrick J. Wong
` (28 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:46 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: c77b37584c2d1054452853e47e42c7350b8fe687
Introduce a variant on XFS_SCRUB_METADATA that allows for a vectored
mode. The caller specifies the principal metadata object that they want
to scrub (allocation group, inode, etc.) once, followed by an array of
scrub types they want called on that object. The kernel runs the scrub
operations and writes the output flags and errno code to the
corresponding array element.
A new pseudo scrub type BARRIER is introduced to force the kernel to
return to userspace if any corruptions have been found when scrubbing
the previous scrub types in the array. This enables userspace to
schedule, for example, the sequence:
1. data fork
2. barrier
3. directory
If the data fork scrub is clean, then the kernel will perform the
directory scrub. If not, the barrier in 2 will exit back to userspace.
The alternative would have been an interface where userspace passes a
pointer to an empty buffer, and the kernel formats that with
xfs_scrub_vecs that tell userspace what it scrubbed and what the outcome
was. With that the kernel would have to communicate that the buffer
needed to have been at least X size, even though for our cases
XFS_SCRUB_TYPE_NR + 2 would always be enough.
Compared to that, this design keeps all the dependency policy and
ordering logic in userspace where it already resides instead of
duplicating it in the kernel. The downside of that is that it needs the
barrier logic.
When running fstests in "rebuild all metadata after each test" mode, I
observed a 10% reduction in runtime due to fewer transitions across the
system call boundary.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_fs.h | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index 7ae1912cd..97996cb79 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -725,6 +725,15 @@ struct xfs_scrub_metadata {
/* Number of scrub subcommands. */
#define XFS_SCRUB_TYPE_NR 29
+/*
+ * This special type code only applies to the vectored scrub implementation.
+ *
+ * If any of the previous scrub vectors recorded runtime errors or have
+ * sv_flags bits set that match the OFLAG bits in the barrier vector's
+ * sv_flags, set the barrier's sv_ret to -ECANCELED and return to userspace.
+ */
+#define XFS_SCRUB_TYPE_BARRIER (0xFFFFFFFF)
+
/* i: Repair this metadata. */
#define XFS_SCRUB_IFLAG_REPAIR (1u << 0)
@@ -769,6 +778,29 @@ struct xfs_scrub_metadata {
XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED)
#define XFS_SCRUB_FLAGS_ALL (XFS_SCRUB_FLAGS_IN | XFS_SCRUB_FLAGS_OUT)
+/* Vectored scrub calls to reduce the number of kernel transitions. */
+
+struct xfs_scrub_vec {
+ __u32 sv_type; /* XFS_SCRUB_TYPE_* */
+ __u32 sv_flags; /* XFS_SCRUB_FLAGS_* */
+ __s32 sv_ret; /* 0 or a negative error code */
+ __u32 sv_reserved; /* must be zero */
+};
+
+/* Vectored metadata scrub control structure. */
+struct xfs_scrub_vec_head {
+ __u64 svh_ino; /* inode number. */
+ __u32 svh_gen; /* inode generation. */
+ __u32 svh_agno; /* ag number. */
+ __u32 svh_flags; /* XFS_SCRUB_VEC_FLAGS_* */
+ __u16 svh_rest_us; /* wait this much time between vector items */
+ __u16 svh_nr; /* number of svh_vectors */
+ __u64 svh_reserved; /* must be zero */
+ __u64 svh_vectors; /* pointer to buffer of xfs_scrub_vec */
+};
+
+#define XFS_SCRUB_VEC_FLAGS_ALL (0)
+
/*
* ioctl limits
*/
@@ -928,6 +960,7 @@ struct xfs_getparents_by_handle {
#define XFS_IOC_AG_GEOMETRY _IOWR('X', 61, struct xfs_ag_geometry)
#define XFS_IOC_GETPARENTS _IOWR('X', 62, struct xfs_getparents)
#define XFS_IOC_GETPARENTS_BY_HANDLE _IOWR('X', 63, struct xfs_getparents_by_handle)
+#define XFS_IOC_SCRUBV_METADATA _IOWR('X', 64, struct xfs_scrub_vec_head)
/*
* ioctl commands that replace IRIX syssgi()'s
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 087/115] xfs: factor out a xfs_dir_lookup_args helper
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (85 preceding siblings ...)
2024-07-30 0:46 ` [PATCH 086/115] xfs: introduce vectored scrub mode Darrick J. Wong
@ 2024-07-30 0:46 ` Darrick J. Wong
2024-07-30 0:46 ` [PATCH 088/115] xfs: factor out a xfs_dir_createname_args helper Darrick J. Wong
` (27 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:46 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 14ee22fef420c864c0869419e54aa4e88f64b4e6
Add a helper to switch between the different directory formats for
lookup and to handle the -EEXIST return for a successful lookup.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_dir2.c | 66 ++++++++++++++++++++++++++++++++---------------------
libxfs/xfs_dir2.h | 2 ++
2 files changed, 42 insertions(+), 26 deletions(-)
diff --git a/libxfs/xfs_dir2.c b/libxfs/xfs_dir2.c
index e309e1e58..0cf4120fe 100644
--- a/libxfs/xfs_dir2.c
+++ b/libxfs/xfs_dir2.c
@@ -351,6 +351,45 @@ xfs_dir_cilookup_result(
return -EEXIST;
}
+int
+xfs_dir_lookup_args(
+ struct xfs_da_args *args)
+{
+ bool is_block, is_leaf;
+ int error;
+
+ if (args->dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
+ error = xfs_dir2_sf_lookup(args);
+ goto out;
+ }
+
+ /* dir2 functions require that the data fork is loaded */
+ error = xfs_iread_extents(args->trans, args->dp, XFS_DATA_FORK);
+ if (error)
+ goto out;
+
+ error = xfs_dir2_isblock(args, &is_block);
+ if (error)
+ goto out;
+
+ if (is_block) {
+ error = xfs_dir2_block_lookup(args);
+ goto out;
+ }
+
+ error = xfs_dir2_isleaf(args, &is_leaf);
+ if (error)
+ goto out;
+ if (is_leaf)
+ error = xfs_dir2_leaf_lookup(args);
+ else
+ error = xfs_dir2_node_lookup(args);
+out:
+ if (error != -EEXIST)
+ return error;
+ return 0;
+}
+
/*
* Lookup a name in a directory, give back the inode number.
* If ci_name is not NULL, returns the actual name in ci_name if it differs
@@ -367,7 +406,6 @@ xfs_dir_lookup(
{
struct xfs_da_args *args;
int rval;
- bool v;
int lock_mode;
ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
@@ -389,30 +427,7 @@ xfs_dir_lookup(
args->op_flags |= XFS_DA_OP_CILOOKUP;
lock_mode = xfs_ilock_data_map_shared(dp);
- if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
- rval = xfs_dir2_sf_lookup(args);
- goto out_check_rval;
- }
-
- rval = xfs_dir2_isblock(args, &v);
- if (rval)
- goto out_free;
- if (v) {
- rval = xfs_dir2_block_lookup(args);
- goto out_check_rval;
- }
-
- rval = xfs_dir2_isleaf(args, &v);
- if (rval)
- goto out_free;
- if (v)
- rval = xfs_dir2_leaf_lookup(args);
- else
- rval = xfs_dir2_node_lookup(args);
-
-out_check_rval:
- if (rval == -EEXIST)
- rval = 0;
+ rval = xfs_dir_lookup_args(args);
if (!rval) {
*inum = args->inumber;
if (ci_name) {
@@ -420,7 +435,6 @@ xfs_dir_lookup(
ci_name->len = args->valuelen;
}
}
-out_free:
xfs_iunlock(dp, lock_mode);
kfree(args);
return rval;
diff --git a/libxfs/xfs_dir2.h b/libxfs/xfs_dir2.h
index b580a78bc..982c2249b 100644
--- a/libxfs/xfs_dir2.h
+++ b/libxfs/xfs_dir2.h
@@ -66,6 +66,8 @@ extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
extern int xfs_dir_canenter(struct xfs_trans *tp, struct xfs_inode *dp,
struct xfs_name *name);
+int xfs_dir_lookup_args(struct xfs_da_args *args);
+
/*
* Direct call from the bmap code, bypassing the generic directory layer.
*/
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 088/115] xfs: factor out a xfs_dir_createname_args helper
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (86 preceding siblings ...)
2024-07-30 0:46 ` [PATCH 087/115] xfs: factor out a xfs_dir_lookup_args helper Darrick J. Wong
@ 2024-07-30 0:46 ` Darrick J. Wong
2024-07-30 0:46 ` [PATCH 089/115] xfs: factor out a xfs_dir_removename_args helper Darrick J. Wong
` (26 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:46 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 4d893a40514e9c4ee6e3be48ed1b77efb38c313b
Add a helper to switch between the different directory formats for
creating a directory entry and to handle the XFS_DA_OP_JUSTCHECK flag
based on the passed in ino number field.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_dir2.c | 53 ++++++++++++++++++++++++++++-------------------------
libxfs/xfs_dir2.h | 1 +
2 files changed, 29 insertions(+), 25 deletions(-)
diff --git a/libxfs/xfs_dir2.c b/libxfs/xfs_dir2.c
index 0cf4120fe..97c7ddc92 100644
--- a/libxfs/xfs_dir2.c
+++ b/libxfs/xfs_dir2.c
@@ -255,6 +255,33 @@ xfs_dir_init(
return error;
}
+int
+xfs_dir_createname_args(
+ struct xfs_da_args *args)
+{
+ bool is_block, is_leaf;
+ int error;
+
+ if (!args->inumber)
+ args->op_flags |= XFS_DA_OP_JUSTCHECK;
+
+ if (args->dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
+ return xfs_dir2_sf_addname(args);
+
+ error = xfs_dir2_isblock(args, &is_block);
+ if (error)
+ return error;
+ if (is_block)
+ return xfs_dir2_block_addname(args);
+
+ error = xfs_dir2_isleaf(args, &is_leaf);
+ if (error)
+ return error;
+ if (is_leaf)
+ return xfs_dir2_leaf_addname(args);
+ return xfs_dir2_node_addname(args);
+}
+
/*
* Enter a name in a directory, or check for available space.
* If inum is 0, only the available space test is performed.
@@ -269,7 +296,6 @@ xfs_dir_createname(
{
struct xfs_da_args *args;
int rval;
- bool v;
ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
@@ -296,31 +322,8 @@ xfs_dir_createname(
args->trans = tp;
args->op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
args->owner = dp->i_ino;
- if (!inum)
- args->op_flags |= XFS_DA_OP_JUSTCHECK;
- if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
- rval = xfs_dir2_sf_addname(args);
- goto out_free;
- }
-
- rval = xfs_dir2_isblock(args, &v);
- if (rval)
- goto out_free;
- if (v) {
- rval = xfs_dir2_block_addname(args);
- goto out_free;
- }
-
- rval = xfs_dir2_isleaf(args, &v);
- if (rval)
- goto out_free;
- if (v)
- rval = xfs_dir2_leaf_addname(args);
- else
- rval = xfs_dir2_node_addname(args);
-
-out_free:
+ rval = xfs_dir_createname_args(args);
kfree(args);
return rval;
}
diff --git a/libxfs/xfs_dir2.h b/libxfs/xfs_dir2.h
index 982c2249b..f5361dd7b 100644
--- a/libxfs/xfs_dir2.h
+++ b/libxfs/xfs_dir2.h
@@ -67,6 +67,7 @@ extern int xfs_dir_canenter(struct xfs_trans *tp, struct xfs_inode *dp,
struct xfs_name *name);
int xfs_dir_lookup_args(struct xfs_da_args *args);
+int xfs_dir_createname_args(struct xfs_da_args *args);
/*
* Direct call from the bmap code, bypassing the generic directory layer.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 089/115] xfs: factor out a xfs_dir_removename_args helper
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (87 preceding siblings ...)
2024-07-30 0:46 ` [PATCH 088/115] xfs: factor out a xfs_dir_createname_args helper Darrick J. Wong
@ 2024-07-30 0:46 ` Darrick J. Wong
2024-07-30 0:47 ` [PATCH 090/115] xfs: factor out a xfs_dir_replace_args helper Darrick J. Wong
` (25 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:46 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 3866e6e669e2c1b3eebf580b8779ea55838c3f5a
Add a helper to switch between the different directory formats for
removing a directory entry.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_dir2.c | 48 +++++++++++++++++++++++++-----------------------
libxfs/xfs_dir2.h | 1 +
2 files changed, 26 insertions(+), 23 deletions(-)
diff --git a/libxfs/xfs_dir2.c b/libxfs/xfs_dir2.c
index 97c7ddc92..e2e0832c1 100644
--- a/libxfs/xfs_dir2.c
+++ b/libxfs/xfs_dir2.c
@@ -443,6 +443,30 @@ xfs_dir_lookup(
return rval;
}
+int
+xfs_dir_removename_args(
+ struct xfs_da_args *args)
+{
+ bool is_block, is_leaf;
+ int error;
+
+ if (args->dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
+ return xfs_dir2_sf_removename(args);
+
+ error = xfs_dir2_isblock(args, &is_block);
+ if (error)
+ return error;
+ if (is_block)
+ return xfs_dir2_block_removename(args);
+
+ error = xfs_dir2_isleaf(args, &is_leaf);
+ if (error)
+ return error;
+ if (is_leaf)
+ return xfs_dir2_leaf_removename(args);
+ return xfs_dir2_node_removename(args);
+}
+
/*
* Remove an entry from a directory.
*/
@@ -456,7 +480,6 @@ xfs_dir_removename(
{
struct xfs_da_args *args;
int rval;
- bool v;
ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
XFS_STATS_INC(dp->i_mount, xs_dir_remove);
@@ -476,28 +499,7 @@ xfs_dir_removename(
args->whichfork = XFS_DATA_FORK;
args->trans = tp;
args->owner = dp->i_ino;
-
- if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
- rval = xfs_dir2_sf_removename(args);
- goto out_free;
- }
-
- rval = xfs_dir2_isblock(args, &v);
- if (rval)
- goto out_free;
- if (v) {
- rval = xfs_dir2_block_removename(args);
- goto out_free;
- }
-
- rval = xfs_dir2_isleaf(args, &v);
- if (rval)
- goto out_free;
- if (v)
- rval = xfs_dir2_leaf_removename(args);
- else
- rval = xfs_dir2_node_removename(args);
-out_free:
+ rval = xfs_dir_removename_args(args);
kfree(args);
return rval;
}
diff --git a/libxfs/xfs_dir2.h b/libxfs/xfs_dir2.h
index f5361dd7b..3db54801d 100644
--- a/libxfs/xfs_dir2.h
+++ b/libxfs/xfs_dir2.h
@@ -68,6 +68,7 @@ extern int xfs_dir_canenter(struct xfs_trans *tp, struct xfs_inode *dp,
int xfs_dir_lookup_args(struct xfs_da_args *args);
int xfs_dir_createname_args(struct xfs_da_args *args);
+int xfs_dir_removename_args(struct xfs_da_args *args);
/*
* Direct call from the bmap code, bypassing the generic directory layer.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 090/115] xfs: factor out a xfs_dir_replace_args helper
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (88 preceding siblings ...)
2024-07-30 0:46 ` [PATCH 089/115] xfs: factor out a xfs_dir_removename_args helper Darrick J. Wong
@ 2024-07-30 0:47 ` Darrick J. Wong
2024-07-30 0:47 ` [PATCH 091/115] xfs: refactor dir format helpers Darrick J. Wong
` (24 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:47 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: dfe5febe2b6a175d730861441bff4f726fc58a6c
Add a helper to switch between the different directory formats for
removing a directory entry.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_dir2.c | 49 ++++++++++++++++++++++++++-----------------------
libxfs/xfs_dir2.h | 1 +
2 files changed, 27 insertions(+), 23 deletions(-)
diff --git a/libxfs/xfs_dir2.c b/libxfs/xfs_dir2.c
index e2e0832c1..55cf39e11 100644
--- a/libxfs/xfs_dir2.c
+++ b/libxfs/xfs_dir2.c
@@ -504,6 +504,31 @@ xfs_dir_removename(
return rval;
}
+int
+xfs_dir_replace_args(
+ struct xfs_da_args *args)
+{
+ bool is_block, is_leaf;
+ int error;
+
+ if (args->dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
+ return xfs_dir2_sf_replace(args);
+
+ error = xfs_dir2_isblock(args, &is_block);
+ if (error)
+ return error;
+ if (is_block)
+ return xfs_dir2_block_replace(args);
+
+ error = xfs_dir2_isleaf(args, &is_leaf);
+ if (error)
+ return error;
+ if (is_leaf)
+ return xfs_dir2_leaf_replace(args);
+
+ return xfs_dir2_node_replace(args);
+}
+
/*
* Replace the inode number of a directory entry.
*/
@@ -517,7 +542,6 @@ xfs_dir_replace(
{
struct xfs_da_args *args;
int rval;
- bool v;
ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
@@ -540,28 +564,7 @@ xfs_dir_replace(
args->whichfork = XFS_DATA_FORK;
args->trans = tp;
args->owner = dp->i_ino;
-
- if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
- rval = xfs_dir2_sf_replace(args);
- goto out_free;
- }
-
- rval = xfs_dir2_isblock(args, &v);
- if (rval)
- goto out_free;
- if (v) {
- rval = xfs_dir2_block_replace(args);
- goto out_free;
- }
-
- rval = xfs_dir2_isleaf(args, &v);
- if (rval)
- goto out_free;
- if (v)
- rval = xfs_dir2_leaf_replace(args);
- else
- rval = xfs_dir2_node_replace(args);
-out_free:
+ rval = xfs_dir_replace_args(args);
kfree(args);
return rval;
}
diff --git a/libxfs/xfs_dir2.h b/libxfs/xfs_dir2.h
index 3db54801d..6c00fe24a 100644
--- a/libxfs/xfs_dir2.h
+++ b/libxfs/xfs_dir2.h
@@ -69,6 +69,7 @@ extern int xfs_dir_canenter(struct xfs_trans *tp, struct xfs_inode *dp,
int xfs_dir_lookup_args(struct xfs_da_args *args);
int xfs_dir_createname_args(struct xfs_da_args *args);
int xfs_dir_removename_args(struct xfs_da_args *args);
+int xfs_dir_replace_args(struct xfs_da_args *args);
/*
* Direct call from the bmap code, bypassing the generic directory layer.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 091/115] xfs: refactor dir format helpers
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (89 preceding siblings ...)
2024-07-30 0:47 ` [PATCH 090/115] xfs: factor out a xfs_dir_replace_args helper Darrick J. Wong
@ 2024-07-30 0:47 ` Darrick J. Wong
2024-07-30 0:47 ` [PATCH 092/115] xfs: make the seq argument to xfs_bmapi_convert_delalloc() optional Darrick J. Wong
` (23 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:47 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: e58ac1770ded2a316447ca7608bb7809af82eca6
Add a new enum and a xfs_dir2_format helper that returns it to allow
the code to switch on the format of a directory in a single operation
and switch all helpers of xfs_dir2_isblock and xfs_dir2_isleaf to it.
This also removes the explicit xfs_iread_extents call in a few of the
call sites given that xfs_bmap_last_offset already takes care of it
underneath.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
db/namei.c | 18 ++--
libxfs/libxfs_api_defs.h | 3 -
libxfs/xfs_dir2.c | 188 ++++++++++++++++++----------------------------
libxfs/xfs_dir2.h | 12 ++-
libxfs/xfs_exchmaps.c | 9 --
repair/phase6.c | 21 ++---
6 files changed, 105 insertions(+), 146 deletions(-)
diff --git a/db/namei.c b/db/namei.c
index 303ca3448..6de062161 100644
--- a/db/namei.c
+++ b/db/namei.c
@@ -449,18 +449,18 @@ listdir(
.geo = dp->i_mount->m_dir_geo,
};
int error;
- bool isblock;
- if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
+ switch (libxfs_dir2_format(&args, &error)) {
+ case XFS_DIR2_FMT_SF:
return list_sfdir(&args);
-
- error = -libxfs_dir2_isblock(&args, &isblock);
- if (error)
- return error;
-
- if (isblock)
+ case XFS_DIR2_FMT_BLOCK:
return list_blockdir(&args);
- return list_leafdir(&args);
+ case XFS_DIR2_FMT_LEAF:
+ case XFS_DIR2_FMT_NODE:
+ return list_leafdir(&args);
+ default:
+ return error;
+ }
}
/* List the inode number of the currently selected inode. */
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index 896b6c953..cc670d93a 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -122,9 +122,8 @@
#define xfs_dir2_data_use_free libxfs_dir2_data_use_free
#define xfs_dir2_free_hdr_from_disk libxfs_dir2_free_hdr_from_disk
#define xfs_dir2_hashname libxfs_dir2_hashname
-#define xfs_dir2_isblock libxfs_dir2_isblock
-#define xfs_dir2_isleaf libxfs_dir2_isleaf
#define xfs_dir2_leaf_hdr_from_disk libxfs_dir2_leaf_hdr_from_disk
+#define xfs_dir2_format libxfs_dir2_format
#define xfs_dir2_namecheck libxfs_dir2_namecheck
#define xfs_dir2_sf_entsize libxfs_dir2_sf_entsize
#define xfs_dir2_sf_get_ftype libxfs_dir2_sf_get_ftype
diff --git a/libxfs/xfs_dir2.c b/libxfs/xfs_dir2.c
index 55cf39e11..9cf05ec51 100644
--- a/libxfs/xfs_dir2.c
+++ b/libxfs/xfs_dir2.c
@@ -255,31 +255,60 @@ xfs_dir_init(
return error;
}
+enum xfs_dir2_fmt
+xfs_dir2_format(
+ struct xfs_da_args *args,
+ int *error)
+{
+ struct xfs_inode *dp = args->dp;
+ struct xfs_mount *mp = dp->i_mount;
+ struct xfs_da_geometry *geo = mp->m_dir_geo;
+ xfs_fileoff_t eof;
+
+ xfs_assert_ilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL);
+
+ *error = 0;
+ if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
+ return XFS_DIR2_FMT_SF;
+
+ *error = xfs_bmap_last_offset(dp, &eof, XFS_DATA_FORK);
+ if (*error)
+ return XFS_DIR2_FMT_ERROR;
+
+ if (eof == XFS_B_TO_FSB(mp, geo->blksize)) {
+ if (XFS_IS_CORRUPT(mp, dp->i_disk_size != geo->blksize)) {
+ xfs_da_mark_sick(args);
+ *error = -EFSCORRUPTED;
+ return XFS_DIR2_FMT_ERROR;
+ }
+ return XFS_DIR2_FMT_BLOCK;
+ }
+ if (eof == geo->leafblk + geo->fsbcount)
+ return XFS_DIR2_FMT_LEAF;
+ return XFS_DIR2_FMT_NODE;
+}
+
int
xfs_dir_createname_args(
struct xfs_da_args *args)
{
- bool is_block, is_leaf;
int error;
if (!args->inumber)
args->op_flags |= XFS_DA_OP_JUSTCHECK;
- if (args->dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
+ switch (xfs_dir2_format(args, &error)) {
+ case XFS_DIR2_FMT_SF:
return xfs_dir2_sf_addname(args);
-
- error = xfs_dir2_isblock(args, &is_block);
- if (error)
- return error;
- if (is_block)
+ case XFS_DIR2_FMT_BLOCK:
return xfs_dir2_block_addname(args);
-
- error = xfs_dir2_isleaf(args, &is_leaf);
- if (error)
- return error;
- if (is_leaf)
+ case XFS_DIR2_FMT_LEAF:
return xfs_dir2_leaf_addname(args);
- return xfs_dir2_node_addname(args);
+ case XFS_DIR2_FMT_NODE:
+ return xfs_dir2_node_addname(args);
+ default:
+ return error;
+ }
}
/*
@@ -358,36 +387,25 @@ int
xfs_dir_lookup_args(
struct xfs_da_args *args)
{
- bool is_block, is_leaf;
int error;
- if (args->dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
+ switch (xfs_dir2_format(args, &error)) {
+ case XFS_DIR2_FMT_SF:
error = xfs_dir2_sf_lookup(args);
- goto out;
- }
-
- /* dir2 functions require that the data fork is loaded */
- error = xfs_iread_extents(args->trans, args->dp, XFS_DATA_FORK);
- if (error)
- goto out;
-
- error = xfs_dir2_isblock(args, &is_block);
- if (error)
- goto out;
-
- if (is_block) {
+ break;
+ case XFS_DIR2_FMT_BLOCK:
error = xfs_dir2_block_lookup(args);
- goto out;
- }
-
- error = xfs_dir2_isleaf(args, &is_leaf);
- if (error)
- goto out;
- if (is_leaf)
+ break;
+ case XFS_DIR2_FMT_LEAF:
error = xfs_dir2_leaf_lookup(args);
- else
+ break;
+ case XFS_DIR2_FMT_NODE:
error = xfs_dir2_node_lookup(args);
-out:
+ break;
+ default:
+ break;
+ }
+
if (error != -EEXIST)
return error;
return 0;
@@ -447,24 +465,20 @@ int
xfs_dir_removename_args(
struct xfs_da_args *args)
{
- bool is_block, is_leaf;
int error;
- if (args->dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
+ switch (xfs_dir2_format(args, &error)) {
+ case XFS_DIR2_FMT_SF:
return xfs_dir2_sf_removename(args);
-
- error = xfs_dir2_isblock(args, &is_block);
- if (error)
- return error;
- if (is_block)
+ case XFS_DIR2_FMT_BLOCK:
return xfs_dir2_block_removename(args);
-
- error = xfs_dir2_isleaf(args, &is_leaf);
- if (error)
- return error;
- if (is_leaf)
+ case XFS_DIR2_FMT_LEAF:
return xfs_dir2_leaf_removename(args);
- return xfs_dir2_node_removename(args);
+ case XFS_DIR2_FMT_NODE:
+ return xfs_dir2_node_removename(args);
+ default:
+ return error;
+ }
}
/*
@@ -508,25 +522,20 @@ int
xfs_dir_replace_args(
struct xfs_da_args *args)
{
- bool is_block, is_leaf;
int error;
- if (args->dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
+ switch (xfs_dir2_format(args, &error)) {
+ case XFS_DIR2_FMT_SF:
return xfs_dir2_sf_replace(args);
-
- error = xfs_dir2_isblock(args, &is_block);
- if (error)
- return error;
- if (is_block)
+ case XFS_DIR2_FMT_BLOCK:
return xfs_dir2_block_replace(args);
-
- error = xfs_dir2_isleaf(args, &is_leaf);
- if (error)
- return error;
- if (is_leaf)
+ case XFS_DIR2_FMT_LEAF:
return xfs_dir2_leaf_replace(args);
-
- return xfs_dir2_node_replace(args);
+ case XFS_DIR2_FMT_NODE:
+ return xfs_dir2_node_replace(args);
+ default:
+ return error;
+ }
}
/*
@@ -632,57 +641,6 @@ xfs_dir2_grow_inode(
return 0;
}
-/*
- * See if the directory is a single-block form directory.
- */
-int
-xfs_dir2_isblock(
- struct xfs_da_args *args,
- bool *isblock)
-{
- struct xfs_mount *mp = args->dp->i_mount;
- xfs_fileoff_t eof;
- int error;
-
- error = xfs_bmap_last_offset(args->dp, &eof, XFS_DATA_FORK);
- if (error)
- return error;
-
- *isblock = false;
- if (XFS_FSB_TO_B(mp, eof) != args->geo->blksize)
- return 0;
-
- *isblock = true;
- if (XFS_IS_CORRUPT(mp, args->dp->i_disk_size != args->geo->blksize)) {
- xfs_da_mark_sick(args);
- return -EFSCORRUPTED;
- }
- return 0;
-}
-
-/*
- * See if the directory is a single-leaf form directory.
- */
-int
-xfs_dir2_isleaf(
- struct xfs_da_args *args,
- bool *isleaf)
-{
- xfs_fileoff_t eof;
- int error;
-
- error = xfs_bmap_last_offset(args->dp, &eof, XFS_DATA_FORK);
- if (error)
- return error;
-
- *isleaf = false;
- if (eof != args->geo->leafblk + args->geo->fsbcount)
- return 0;
-
- *isleaf = true;
- return 0;
-}
-
/*
* Remove the given block from the directory.
* This routine is used for data and free blocks, leaf/node are done
diff --git a/libxfs/xfs_dir2.h b/libxfs/xfs_dir2.h
index 6c00fe24a..6dbe6e9ec 100644
--- a/libxfs/xfs_dir2.h
+++ b/libxfs/xfs_dir2.h
@@ -36,6 +36,16 @@ xfs_dir2_samename(
return !memcmp(n1->name, n2->name, n1->len);
}
+enum xfs_dir2_fmt {
+ XFS_DIR2_FMT_SF,
+ XFS_DIR2_FMT_BLOCK,
+ XFS_DIR2_FMT_LEAF,
+ XFS_DIR2_FMT_NODE,
+ XFS_DIR2_FMT_ERROR,
+};
+
+enum xfs_dir2_fmt xfs_dir2_format(struct xfs_da_args *args, int *error);
+
/*
* Convert inode mode to directory entry filetype
*/
@@ -79,8 +89,6 @@ extern int xfs_dir2_sf_to_block(struct xfs_da_args *args);
/*
* Interface routines used by userspace utilities
*/
-extern int xfs_dir2_isblock(struct xfs_da_args *args, bool *isblock);
-extern int xfs_dir2_isleaf(struct xfs_da_args *args, bool *isleaf);
extern int xfs_dir2_shrink_inode(struct xfs_da_args *args, xfs_dir2_db_t db,
struct xfs_buf *bp);
diff --git a/libxfs/xfs_exchmaps.c b/libxfs/xfs_exchmaps.c
index a8a51ce53..08bfe6f1b 100644
--- a/libxfs/xfs_exchmaps.c
+++ b/libxfs/xfs_exchmaps.c
@@ -462,17 +462,12 @@ xfs_exchmaps_dir_to_sf(
};
struct xfs_dir2_sf_hdr sfh;
struct xfs_buf *bp;
- bool isblock;
int size;
- int error;
+ int error = 0;
- error = xfs_dir2_isblock(&args, &isblock);
- if (error)
+ if (xfs_dir2_format(&args, &error) != XFS_DIR2_FMT_BLOCK)
return error;
- if (!isblock)
- return 0;
-
error = xfs_dir3_block_read(tp, xmi->xmi_ip2, xmi->xmi_ip2->i_ino, &bp);
if (error)
return error;
diff --git a/repair/phase6.c b/repair/phase6.c
index 2eba9772d..1e985e7db 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -2269,12 +2269,12 @@ longform_dir2_entry_check(
xfs_dablk_t da_bno;
freetab_t *freetab;
int i;
- bool isblock;
- bool isleaf;
+ enum xfs_dir2_fmt fmt;
xfs_fileoff_t next_da_bno;
int seeval;
int fixit = 0;
struct xfs_da_args args;
+ int error;
*need_dot = 1;
freetab = malloc(FREETAB_SIZE(ip->i_disk_size / mp->m_dir_geo->blksize));
@@ -2294,8 +2294,7 @@ longform_dir2_entry_check(
/* is this a block, leaf, or node directory? */
args.dp = ip;
args.geo = mp->m_dir_geo;
- libxfs_dir2_isblock(&args, &isblock);
- libxfs_dir2_isleaf(&args, &isleaf);
+ fmt = libxfs_dir2_format(&args, &error);
/* check directory "data" blocks (ie. name/inode pairs) */
for (da_bno = 0, next_da_bno = 0;
@@ -2318,7 +2317,7 @@ longform_dir2_entry_check(
break;
}
- if (isblock)
+ if (fmt == XFS_DIR2_FMT_BLOCK)
ops = &xfs_dir3_block_buf_ops;
else
ops = &xfs_dir3_data_buf_ops;
@@ -2335,7 +2334,7 @@ longform_dir2_entry_check(
* block form and we fail, there isn't anything else to
* read, and nothing we can do but trash it.
*/
- if (isblock) {
+ if (fmt == XFS_DIR2_FMT_BLOCK) {
fixit++;
goto out_fix;
}
@@ -2349,7 +2348,7 @@ longform_dir2_entry_check(
error = check_dir3_header(mp, bp, ino);
if (error) {
fixit++;
- if (isblock)
+ if (fmt == XFS_DIR2_FMT_BLOCK)
goto out_fix;
libxfs_buf_relse(bp);
@@ -2360,8 +2359,8 @@ longform_dir2_entry_check(
longform_dir2_entry_check_data(mp, ip, num_illegal, need_dot,
irec, ino_offset, bp, hashtab,
- &freetab, da_bno, isblock);
- if (isblock)
+ &freetab, da_bno, fmt == XFS_DIR2_FMT_BLOCK);
+ if (fmt == XFS_DIR2_FMT_BLOCK)
break;
libxfs_buf_relse(bp);
@@ -2371,7 +2370,7 @@ longform_dir2_entry_check(
if (!dotdot_update) {
/* check btree and freespace */
- if (isblock) {
+ if (fmt == XFS_DIR2_FMT_BLOCK) {
struct xfs_dir2_data_hdr *block;
xfs_dir2_block_tail_t *btp;
xfs_dir2_leaf_entry_t *blp;
@@ -2384,7 +2383,7 @@ longform_dir2_entry_check(
be32_to_cpu(btp->stale));
if (dir_hash_check(hashtab, ip, seeval))
fixit |= 1;
- } else if (isleaf) {
+ } else if (fmt == XFS_DIR2_FMT_LEAF) {
fixit |= longform_dir2_check_leaf(mp, ip, hashtab,
freetab);
} else {
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 092/115] xfs: make the seq argument to xfs_bmapi_convert_delalloc() optional
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (90 preceding siblings ...)
2024-07-30 0:47 ` [PATCH 091/115] xfs: refactor dir format helpers Darrick J. Wong
@ 2024-07-30 0:47 ` Darrick J. Wong
2024-07-30 0:48 ` [PATCH 093/115] xfs: make xfs_bmapi_convert_delalloc() to allocate the target offset Darrick J. Wong
` (22 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:47 UTC (permalink / raw)
To: djwong, cem; +Cc: Zhang Yi, Christoph Hellwig, Chandan Babu R, linux-xfs
From: Zhang Yi <yi.zhang@huawei.com>
Source kernel commit: fc8d0ba0ff5fe4700fa02008b7751ec6b84b7677
Allow callers to pass a NULLL seq argument if they don't care about
the fork sequence number.
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index def73fd50..33d0764a0 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4642,7 +4642,8 @@ xfs_bmapi_convert_delalloc(
if (!isnullstartblock(bma.got.br_startblock)) {
xfs_bmbt_to_iomap(ip, iomap, &bma.got, 0, flags,
xfs_iomap_inode_sequence(ip, flags));
- *seq = READ_ONCE(ifp->if_seq);
+ if (seq)
+ *seq = READ_ONCE(ifp->if_seq);
goto out_trans_cancel;
}
@@ -4693,7 +4694,8 @@ xfs_bmapi_convert_delalloc(
ASSERT(!isnullstartblock(bma.got.br_startblock));
xfs_bmbt_to_iomap(ip, iomap, &bma.got, 0, flags,
xfs_iomap_inode_sequence(ip, flags));
- *seq = READ_ONCE(ifp->if_seq);
+ if (seq)
+ *seq = READ_ONCE(ifp->if_seq);
if (whichfork == XFS_COW_FORK)
xfs_refcount_alloc_cow_extent(tp, bma.blkno, bma.length);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 093/115] xfs: make xfs_bmapi_convert_delalloc() to allocate the target offset
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (91 preceding siblings ...)
2024-07-30 0:47 ` [PATCH 092/115] xfs: make the seq argument to xfs_bmapi_convert_delalloc() optional Darrick J. Wong
@ 2024-07-30 0:48 ` Darrick J. Wong
2024-07-30 0:48 ` [PATCH 094/115] xfs: fix error returns from xfs_bmapi_write Darrick J. Wong
` (21 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:48 UTC (permalink / raw)
To: djwong, cem; +Cc: Zhang Yi, Christoph Hellwig, Chandan Babu R, linux-xfs
From: Zhang Yi <yi.zhang@huawei.com>
Source kernel commit: 2e08371a83f1c06fd85eea8cd37c87a224cc4cc4
Since xfs_bmapi_convert_delalloc() only attempts to allocate the entire
delalloc extent and require multiple invocations to allocate the target
offset. So xfs_convert_blocks() add a loop to do this job and we call it
in the write back path, but xfs_convert_blocks() isn't a common helper.
Let's do it in xfs_bmapi_convert_delalloc() and drop
xfs_convert_blocks(), preparing for the post EOF delalloc blocks
converting in the buffered write begin path.
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/libxfs_priv.h | 5 ++++-
libxfs/xfs_bmap.c | 34 ++++++++++++++++++++++++++++++++--
2 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index 023444082..90b2db091 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -79,7 +79,10 @@ extern struct kmem_cache *xfs_trans_cache;
#define crc32c(c,p,l) crc32c_le((c),(unsigned char const *)(p),(l))
/* fake up kernel's iomap, (not) used in xfs_bmap.[ch] */
-struct iomap;
+struct iomap {
+ unsigned long long offset; /* do not use */
+ unsigned long long length; /* do not use */
+};
#define cancel_delayed_work_sync(work) do { } while(0)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 33d0764a0..9fce05e1f 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4584,8 +4584,8 @@ xfs_bmapi_write(
* invocations to allocate the target offset if a large enough physical extent
* is not available.
*/
-int
-xfs_bmapi_convert_delalloc(
+static int
+xfs_bmapi_convert_one_delalloc(
struct xfs_inode *ip,
int whichfork,
xfs_off_t offset,
@@ -4718,6 +4718,36 @@ xfs_bmapi_convert_delalloc(
return error;
}
+/*
+ * Pass in a dellalloc extent and convert it to real extents, return the real
+ * extent that maps offset_fsb in iomap.
+ */
+int
+xfs_bmapi_convert_delalloc(
+ struct xfs_inode *ip,
+ int whichfork,
+ loff_t offset,
+ struct iomap *iomap,
+ unsigned int *seq)
+{
+ int error;
+
+ /*
+ * Attempt to allocate whatever delalloc extent currently backs offset
+ * and put the result into iomap. Allocate in a loop because it may
+ * take several attempts to allocate real blocks for a contiguous
+ * delalloc extent if free space is sufficiently fragmented.
+ */
+ do {
+ error = xfs_bmapi_convert_one_delalloc(ip, whichfork, offset,
+ iomap, seq);
+ if (error)
+ return error;
+ } while (iomap->offset + iomap->length <= offset);
+
+ return 0;
+}
+
int
xfs_bmapi_remap(
struct xfs_trans *tp,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 094/115] xfs: fix error returns from xfs_bmapi_write
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (92 preceding siblings ...)
2024-07-30 0:48 ` [PATCH 093/115] xfs: make xfs_bmapi_convert_delalloc() to allocate the target offset Darrick J. Wong
@ 2024-07-30 0:48 ` Darrick J. Wong
2024-07-30 0:48 ` [PATCH 095/115] xfs: remove the unusued tmp_logflags variable in xfs_bmapi_allocate Darrick J. Wong
` (20 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:48 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, 刘通, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 6773da870ab89123d1b513da63ed59e32a29cb77
xfs_bmapi_write can return 0 without actually returning a mapping in
mval in two different cases:
1) when there is absolutely no space available to do an allocation
2) when converting delalloc space, and the allocation is so small
that it only covers parts of the delalloc extent before the
range requested by the caller
Callers at best can handle one of these cases, but in many cases can't
cope with either one. Switch xfs_bmapi_write to always return a
mapping or return an error code instead. For case 1) above ENOSPC is
the obvious choice which is very much what the callers expect anyway.
For case 2) there is no really good error code, so pick a funky one
from the SysV streams portfolio.
This fixes the reproducer here:
https://lore.kernel.org/linux-xfs/CAEJPjCvT3Uag-pMTYuigEjWZHn1sGMZ0GCjVVCv29tNHK76Cgg@mail.gmail.com0/
which uses reserved blocks to create file systems that are gravely
out of space and thus cause at least xfs_file_alloc_space to hang
and trigger the lack of ENOSPC handling in xfs_dquot_disk_alloc.
Note that this patch does not actually make any caller but
xfs_alloc_file_space deal intelligently with case 2) above.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reported-by: 刘通 <lyutoon@gmail.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_attr_remote.c | 1 -
libxfs/xfs_bmap.c | 46 +++++++++++++++++++++++++++++++++++++---------
libxfs/xfs_da_btree.c | 20 +++++---------------
3 files changed, 42 insertions(+), 25 deletions(-)
diff --git a/libxfs/xfs_attr_remote.c b/libxfs/xfs_attr_remote.c
index eb15b272b..282cf2cb9 100644
--- a/libxfs/xfs_attr_remote.c
+++ b/libxfs/xfs_attr_remote.c
@@ -624,7 +624,6 @@ xfs_attr_rmtval_set_blk(
if (error)
return error;
- ASSERT(nmap == 1);
ASSERT((map->br_startblock != DELAYSTARTBLOCK) &&
(map->br_startblock != HOLESTARTBLOCK));
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 9fce05e1f..84760c889 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4211,8 +4211,10 @@ xfs_bmapi_allocate(
} else {
error = xfs_bmap_alloc_userdata(bma);
}
- if (error || bma->blkno == NULLFSBLOCK)
+ if (error)
return error;
+ if (bma->blkno == NULLFSBLOCK)
+ return -ENOSPC;
if (bma->flags & XFS_BMAPI_ZERO) {
error = xfs_zero_extent(bma->ip, bma->blkno, bma->length);
@@ -4391,6 +4393,15 @@ xfs_bmapi_finish(
* extent state if necessary. Details behaviour is controlled by the flags
* parameter. Only allocates blocks from a single allocation group, to avoid
* locking problems.
+ *
+ * Returns 0 on success and places the extent mappings in mval. nmaps is used
+ * as an input/output parameter where the caller specifies the maximum number
+ * of mappings that may be returned and xfs_bmapi_write passes back the number
+ * of mappings (including existing mappings) it found.
+ *
+ * Returns a negative error code on failure, including -ENOSPC when it could not
+ * allocate any blocks and -ENOSR when it did allocate blocks to convert a
+ * delalloc range, but those blocks were before the passed in range.
*/
int
xfs_bmapi_write(
@@ -4519,10 +4530,16 @@ xfs_bmapi_write(
ASSERT(len > 0);
ASSERT(bma.length > 0);
error = xfs_bmapi_allocate(&bma);
- if (error)
+ if (error) {
+ /*
+ * If we already allocated space in a previous
+ * iteration return what we go so far when
+ * running out of space.
+ */
+ if (error == -ENOSPC && bma.nallocs)
+ break;
goto error0;
- if (bma.blkno == NULLFSBLOCK)
- break;
+ }
/*
* If this is a CoW allocation, record the data in
@@ -4560,7 +4577,6 @@ xfs_bmapi_write(
if (!xfs_iext_next_extent(ifp, &bma.icur, &bma.got))
eof = true;
}
- *nmap = n;
error = xfs_bmap_btree_to_extents(tp, ip, bma.cur, &bma.logflags,
whichfork);
@@ -4571,7 +4587,22 @@ xfs_bmapi_write(
ifp->if_nextents > XFS_IFORK_MAXEXT(ip, whichfork));
xfs_bmapi_finish(&bma, whichfork, 0);
xfs_bmap_validate_ret(orig_bno, orig_len, orig_flags, orig_mval,
- orig_nmap, *nmap);
+ orig_nmap, n);
+
+ /*
+ * When converting delayed allocations, xfs_bmapi_allocate ignores
+ * the passed in bno and always converts from the start of the found
+ * delalloc extent.
+ *
+ * To avoid a successful return with *nmap set to 0, return the magic
+ * -ENOSR error code for this particular case so that the caller can
+ * handle it.
+ */
+ if (!n) {
+ ASSERT(bma.nallocs >= *nmap);
+ return -ENOSR;
+ }
+ *nmap = n;
return 0;
error0:
xfs_bmapi_finish(&bma, whichfork, error);
@@ -4679,9 +4710,6 @@ xfs_bmapi_convert_one_delalloc(
if (error)
goto out_finish;
- error = -ENOSPC;
- if (WARN_ON_ONCE(bma.blkno == NULLFSBLOCK))
- goto out_finish;
if (WARN_ON_ONCE(!xfs_valid_startblock(ip, bma.got.br_startblock))) {
xfs_bmap_mark_sick(ip, whichfork);
error = -EFSCORRUPTED;
diff --git a/libxfs/xfs_da_btree.c b/libxfs/xfs_da_btree.c
index 3c0dc26b7..820e85752 100644
--- a/libxfs/xfs_da_btree.c
+++ b/libxfs/xfs_da_btree.c
@@ -2293,8 +2293,8 @@ xfs_da_grow_inode_int(
struct xfs_inode *dp = args->dp;
int w = args->whichfork;
xfs_rfsblock_t nblks = dp->i_nblocks;
- struct xfs_bmbt_irec map, *mapp;
- int nmap, error, got, i, mapi;
+ struct xfs_bmbt_irec map, *mapp = ↦
+ int nmap, error, got, i, mapi = 1;
/*
* Find a spot in the file space to put the new block.
@@ -2310,14 +2310,7 @@ xfs_da_grow_inode_int(
error = xfs_bmapi_write(tp, dp, *bno, count,
xfs_bmapi_aflag(w)|XFS_BMAPI_METADATA|XFS_BMAPI_CONTIG,
args->total, &map, &nmap);
- if (error)
- return error;
-
- ASSERT(nmap <= 1);
- if (nmap == 1) {
- mapp = ↦
- mapi = 1;
- } else if (nmap == 0 && count > 1) {
+ if (error == -ENOSPC && count > 1) {
xfs_fileoff_t b;
int c;
@@ -2335,16 +2328,13 @@ xfs_da_grow_inode_int(
args->total, &mapp[mapi], &nmap);
if (error)
goto out_free_map;
- if (nmap < 1)
- break;
mapi += nmap;
b = mapp[mapi - 1].br_startoff +
mapp[mapi - 1].br_blockcount;
}
- } else {
- mapi = 0;
- mapp = NULL;
}
+ if (error)
+ goto out_free_map;
/*
* Count the blocks we got, make sure it matches the total.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 095/115] xfs: remove the unusued tmp_logflags variable in xfs_bmapi_allocate
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (93 preceding siblings ...)
2024-07-30 0:48 ` [PATCH 094/115] xfs: fix error returns from xfs_bmapi_write Darrick J. Wong
@ 2024-07-30 0:48 ` Darrick J. Wong
2024-07-30 0:48 ` [PATCH 096/115] xfs: lift a xfs_valid_startblock into xfs_bmapi_allocate Darrick J. Wong
` (19 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:48 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: b11ed354c9f725ece2b9440dd6343b42cd5d031c
tmp_logflags is initialized to 0 and then ORed into bma->logflags, which
isn't actually doing anything.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 84760c889..f236e40d1 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4176,7 +4176,6 @@ xfs_bmapi_allocate(
struct xfs_mount *mp = bma->ip->i_mount;
int whichfork = xfs_bmapi_whichfork(bma->flags);
struct xfs_ifork *ifp = xfs_ifork_ptr(bma->ip, whichfork);
- int tmp_logflags = 0;
int error;
ASSERT(bma->length > 0);
@@ -4247,8 +4246,6 @@ xfs_bmapi_allocate(
error = xfs_bmap_add_extent_hole_real(bma->tp, bma->ip,
whichfork, &bma->icur, &bma->cur, &bma->got,
&bma->logflags, bma->flags);
-
- bma->logflags |= tmp_logflags;
if (error)
return error;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 096/115] xfs: lift a xfs_valid_startblock into xfs_bmapi_allocate
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (94 preceding siblings ...)
2024-07-30 0:48 ` [PATCH 095/115] xfs: remove the unusued tmp_logflags variable in xfs_bmapi_allocate Darrick J. Wong
@ 2024-07-30 0:48 ` Darrick J. Wong
2024-07-30 0:49 ` [PATCH 097/115] xfs: don't open code XFS_FILBLKS_MIN in xfs_bmapi_write Darrick J. Wong
` (18 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:48 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 04c609e6e5066294b60329420d3711e990c47abf
xfs_bmapi_convert_delalloc has a xfs_valid_startblock check on the block
allocated by xfs_bmapi_allocate. Lift it into xfs_bmapi_allocate as
we should assert the same for xfs_bmapi_write.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index f236e40d1..5b1c305ec 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4215,6 +4215,11 @@ xfs_bmapi_allocate(
if (bma->blkno == NULLFSBLOCK)
return -ENOSPC;
+ if (WARN_ON_ONCE(!xfs_valid_startblock(bma->ip, bma->blkno))) {
+ xfs_bmap_mark_sick(bma->ip, whichfork);
+ return -EFSCORRUPTED;
+ }
+
if (bma->flags & XFS_BMAPI_ZERO) {
error = xfs_zero_extent(bma->ip, bma->blkno, bma->length);
if (error)
@@ -4707,12 +4712,6 @@ xfs_bmapi_convert_one_delalloc(
if (error)
goto out_finish;
- if (WARN_ON_ONCE(!xfs_valid_startblock(ip, bma.got.br_startblock))) {
- xfs_bmap_mark_sick(ip, whichfork);
- error = -EFSCORRUPTED;
- goto out_finish;
- }
-
XFS_STATS_ADD(mp, xs_xstrat_bytes, XFS_FSB_TO_B(mp, bma.length));
XFS_STATS_INC(mp, xs_xstrat_quick);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 097/115] xfs: don't open code XFS_FILBLKS_MIN in xfs_bmapi_write
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (95 preceding siblings ...)
2024-07-30 0:48 ` [PATCH 096/115] xfs: lift a xfs_valid_startblock into xfs_bmapi_allocate Darrick J. Wong
@ 2024-07-30 0:49 ` Darrick J. Wong
2024-07-30 0:49 ` [PATCH 098/115] xfs: pass the actual offset and len to allocate to xfs_bmapi_allocate Darrick J. Wong
` (17 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:49 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 9d06960341ec5f45d3d65bdead3fbce753455e8a
XFS_FILBLKS_MIN uses min_t and thus does the comparison using the correct
xfs_filblks_t type. Use it in xfs_bmapi_write and slightly adjust the
comment document th potential pitfall to take account of this
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 5b1c305ec..87f0a2853 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4522,14 +4522,11 @@ xfs_bmapi_write(
* allocation length request (which can be 64 bits in
* length) and the bma length request, which is
* xfs_extlen_t and therefore 32 bits. Hence we have to
- * check for 32-bit overflows and handle them here.
+ * be careful and do the min() using the larger type to
+ * avoid overflows.
*/
- if (len > (xfs_filblks_t)XFS_MAX_BMBT_EXTLEN)
- bma.length = XFS_MAX_BMBT_EXTLEN;
- else
- bma.length = len;
+ bma.length = XFS_FILBLKS_MIN(len, XFS_MAX_BMBT_EXTLEN);
- ASSERT(len > 0);
ASSERT(bma.length > 0);
error = xfs_bmapi_allocate(&bma);
if (error) {
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 098/115] xfs: pass the actual offset and len to allocate to xfs_bmapi_allocate
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (96 preceding siblings ...)
2024-07-30 0:49 ` [PATCH 097/115] xfs: don't open code XFS_FILBLKS_MIN in xfs_bmapi_write Darrick J. Wong
@ 2024-07-30 0:49 ` Darrick J. Wong
2024-07-30 0:49 ` [PATCH 099/115] xfs: remove the xfs_iext_peek_prev_extent call in xfs_bmapi_allocate Darrick J. Wong
` (16 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:49 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 2a9b99d45be0981536f6d3faf40ae3f58febdd49
xfs_bmapi_allocate currently overwrites offset and len when converting
delayed allocations, and duplicates the length cap done for non-delalloc
allocations. Move all that logic into the callers to avoid duplication
and to make the calling conventions more obvious.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 32 ++++++++++++++++++--------------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 87f0a2853..a498894fc 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4179,21 +4179,11 @@ xfs_bmapi_allocate(
int error;
ASSERT(bma->length > 0);
+ ASSERT(bma->length <= XFS_MAX_BMBT_EXTLEN);
- /*
- * For the wasdelay case, we could also just allocate the stuff asked
- * for in this bmap call but that wouldn't be as good.
- */
if (bma->wasdel) {
- bma->length = (xfs_extlen_t)bma->got.br_blockcount;
- bma->offset = bma->got.br_startoff;
if (!xfs_iext_peek_prev_extent(ifp, &bma->icur, &bma->prev))
bma->prev.br_startoff = NULLFILEOFF;
- } else {
- bma->length = XFS_FILBLKS_MIN(bma->length, XFS_MAX_BMBT_EXTLEN);
- if (!bma->eof)
- bma->length = XFS_FILBLKS_MIN(bma->length,
- bma->got.br_startoff - bma->offset);
}
if (bma->flags & XFS_BMAPI_CONTIG)
@@ -4527,6 +4517,15 @@ xfs_bmapi_write(
*/
bma.length = XFS_FILBLKS_MIN(len, XFS_MAX_BMBT_EXTLEN);
+ if (wasdelay) {
+ bma.offset = bma.got.br_startoff;
+ bma.length = bma.got.br_blockcount;
+ } else {
+ if (!eof)
+ bma.length = XFS_FILBLKS_MIN(bma.length,
+ bma.got.br_startoff - bno);
+ }
+
ASSERT(bma.length > 0);
error = xfs_bmapi_allocate(&bma);
if (error) {
@@ -4680,10 +4679,15 @@ xfs_bmapi_convert_one_delalloc(
bma.tp = tp;
bma.ip = ip;
bma.wasdel = true;
+ bma.minleft = xfs_bmapi_minleft(tp, ip, whichfork);
+
+ /*
+ * Always allocate convert from the start of the delalloc extent even if
+ * that is outside the passed in range to create large contiguous
+ * extents on disk.
+ */
bma.offset = bma.got.br_startoff;
- bma.length = max_t(xfs_filblks_t, bma.got.br_blockcount,
- XFS_MAX_BMBT_EXTLEN);
- bma.minleft = xfs_bmapi_minleft(tp, ip, whichfork);
+ bma.length = bma.got.br_blockcount;
/*
* When we're converting the delalloc reservations backing dirty pages
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 099/115] xfs: remove the xfs_iext_peek_prev_extent call in xfs_bmapi_allocate
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (97 preceding siblings ...)
2024-07-30 0:49 ` [PATCH 098/115] xfs: pass the actual offset and len to allocate to xfs_bmapi_allocate Darrick J. Wong
@ 2024-07-30 0:49 ` Darrick J. Wong
2024-07-30 0:49 ` [PATCH 100/115] xfs: fix xfs_bmap_add_extent_delay_real for partial conversions Darrick J. Wong
` (15 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:49 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: a8bb258f703f42c322638022afa16808ca4a7d25
Both callers of xfs_bmapi_allocate already initialize bma->prev, don't
redo that in xfs_bmapi_allocate.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index a498894fc..4279ab83d 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4181,11 +4181,6 @@ xfs_bmapi_allocate(
ASSERT(bma->length > 0);
ASSERT(bma->length <= XFS_MAX_BMBT_EXTLEN);
- if (bma->wasdel) {
- if (!xfs_iext_peek_prev_extent(ifp, &bma->icur, &bma->prev))
- bma->prev.br_startoff = NULLFILEOFF;
- }
-
if (bma->flags & XFS_BMAPI_CONTIG)
bma->minlen = bma->length;
else
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 100/115] xfs: fix xfs_bmap_add_extent_delay_real for partial conversions
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (98 preceding siblings ...)
2024-07-30 0:49 ` [PATCH 099/115] xfs: remove the xfs_iext_peek_prev_extent call in xfs_bmapi_allocate Darrick J. Wong
@ 2024-07-30 0:49 ` Darrick J. Wong
2024-07-30 0:50 ` [PATCH 101/115] xfs: do not allocate the entire delalloc extent in xfs_bmapi_write Darrick J. Wong
` (14 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:49 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: d69bee6a35d3c5e4873b9e164dd1a9711351a97c
xfs_bmap_add_extent_delay_real takes parts or all of a delalloc extent
and converts them to a real extent. It is written to deal with any
potential overlap of the to be converted range with the delalloc extent,
but it turns out that currently only converting the entire extents, or a
part starting at the beginning is actually exercised, as the only caller
always tries to convert the entire delalloc extent, and either succeeds
or at least progresses partially from the start.
If it only converts a tiny part of a delalloc extent, the indirect block
calculation for the new delalloc extent (da_new) might be equivalent to that
of the existing delalloc extent (da_old). If this extent conversion now
requires allocating an indirect block that gets accounted into da_new,
leading to the assert that da_new must be smaller or equal to da_new
unless we split the extent to trigger.
Except for the assert that case is actually handled by just trying to
allocate more space, as that already handled for the split case (which
currently can't be reached at all), so just reusing it should be fine.
Except that without dipping into the reserved block pool that would make
it a bit too easy to trigger a fs shutdown due to ENOSPC. So in addition
to adjusting the assert, also dip into the reserved block pool.
Note that I could only reproduce the assert with a change to only convert
the actually asked range instead of the full delalloc extent from
xfs_bmapi_write.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 4279ab83d..9af65a182 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -1564,6 +1564,7 @@ xfs_bmap_add_extent_delay_real(
if (error)
goto done;
}
+ ASSERT(da_new <= da_old);
break;
case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG:
@@ -1594,6 +1595,7 @@ xfs_bmap_add_extent_delay_real(
if (error)
goto done;
}
+ ASSERT(da_new <= da_old);
break;
case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
@@ -1628,6 +1630,7 @@ xfs_bmap_add_extent_delay_real(
if (error)
goto done;
}
+ ASSERT(da_new <= da_old);
break;
case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
@@ -1662,6 +1665,7 @@ xfs_bmap_add_extent_delay_real(
goto done;
}
}
+ ASSERT(da_new <= da_old);
break;
case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG:
@@ -1700,6 +1704,7 @@ xfs_bmap_add_extent_delay_real(
if (error)
goto done;
}
+ ASSERT(da_new <= da_old);
break;
case BMAP_LEFT_FILLING:
@@ -1790,6 +1795,7 @@ xfs_bmap_add_extent_delay_real(
xfs_iext_update_extent(bma->ip, state, &bma->icur, &PREV);
xfs_iext_next(ifp, &bma->icur);
xfs_iext_update_extent(bma->ip, state, &bma->icur, &RIGHT);
+ ASSERT(da_new <= da_old);
break;
case BMAP_RIGHT_FILLING:
@@ -1839,6 +1845,7 @@ xfs_bmap_add_extent_delay_real(
PREV.br_blockcount = temp;
xfs_iext_insert(bma->ip, &bma->icur, &PREV, state);
xfs_iext_next(ifp, &bma->icur);
+ ASSERT(da_new <= da_old);
break;
case 0:
@@ -1961,12 +1968,10 @@ xfs_bmap_add_extent_delay_real(
}
/* adjust for changes in reserved delayed indirect blocks */
- if (da_new < da_old) {
+ if (da_new < da_old)
xfs_add_fdblocks(mp, da_old - da_new);
- } else if (da_new > da_old) {
- ASSERT(state == 0);
- error = xfs_dec_fdblocks(mp, da_new - da_old, false);
- }
+ else if (da_new > da_old)
+ error = xfs_dec_fdblocks(mp, da_new - da_old, true);
xfs_bmap_check_leaf_extents(bma->cur, bma->ip, whichfork);
done:
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 101/115] xfs: do not allocate the entire delalloc extent in xfs_bmapi_write
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (99 preceding siblings ...)
2024-07-30 0:49 ` [PATCH 100/115] xfs: fix xfs_bmap_add_extent_delay_real for partial conversions Darrick J. Wong
@ 2024-07-30 0:50 ` Darrick J. Wong
2024-07-30 0:50 ` [PATCH 102/115] xfs: use unsigned ints for non-negative quantities in xfs_attr_remote.c Darrick J. Wong
` (13 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:50 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 21255afdd7296f57dd65f815301426bcf911c82d
While trying to convert the entire delalloc extent is a good decision
for regular writeback as it leads to larger contigous on-disk extents,
but for other callers of xfs_bmapi_write is is rather questionable as
it forced them to loop creating new transactions just in case there
is no large enough contiguous extent to cover the whole delalloc
reservation.
Change xfs_bmapi_write to only allocate the passed in range instead,
whіle the writeback path through xfs_bmapi_convert_delalloc and
xfs_bmapi_allocate still always converts the full extents.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 9af65a182..e6d700138 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4518,8 +4518,9 @@ xfs_bmapi_write(
bma.length = XFS_FILBLKS_MIN(len, XFS_MAX_BMBT_EXTLEN);
if (wasdelay) {
- bma.offset = bma.got.br_startoff;
- bma.length = bma.got.br_blockcount;
+ bma.length = XFS_FILBLKS_MIN(bma.length,
+ bma.got.br_blockcount -
+ (bno - bma.got.br_startoff));
} else {
if (!eof)
bma.length = XFS_FILBLKS_MIN(bma.length,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 102/115] xfs: use unsigned ints for non-negative quantities in xfs_attr_remote.c
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (100 preceding siblings ...)
2024-07-30 0:50 ` [PATCH 101/115] xfs: do not allocate the entire delalloc extent in xfs_bmapi_write Darrick J. Wong
@ 2024-07-30 0:50 ` Darrick J. Wong
2024-07-30 0:50 ` [PATCH 103/115] xfs: turn XFS_ATTR3_RMT_BUF_SPACE into a function Darrick J. Wong
` (12 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:50 UTC (permalink / raw)
To: djwong, cem; +Cc: Andrey Albershteyn, Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: a86f8671d03e6eb31abaefdf6928b92df0a2368c
In the next few patches we're going to refactor the attr remote code so
that we can support headerless remote xattr values for storing merkle
tree blocks. For now, let's change the code to use unsigned int to
describe quantities of bytes and blocks that cannot be negative.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Andrey Albershteyn <aalbersh@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr_remote.c | 61 +++++++++++++++++++++++-----------------------
libxfs/xfs_attr_remote.h | 2 +-
2 files changed, 31 insertions(+), 32 deletions(-)
diff --git a/libxfs/xfs_attr_remote.c b/libxfs/xfs_attr_remote.c
index 282cf2cb9..34e74fd57 100644
--- a/libxfs/xfs_attr_remote.c
+++ b/libxfs/xfs_attr_remote.c
@@ -46,13 +46,13 @@
* Each contiguous block has a header, so it is not just a simple attribute
* length to FSB conversion.
*/
-int
+unsigned int
xfs_attr3_rmt_blocks(
- struct xfs_mount *mp,
- int attrlen)
+ struct xfs_mount *mp,
+ unsigned int attrlen)
{
if (xfs_has_crc(mp)) {
- int buflen = XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
+ unsigned int buflen = XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
return (attrlen + buflen - 1) / buflen;
}
return XFS_B_TO_FSB(mp, attrlen);
@@ -91,7 +91,6 @@ xfs_attr3_rmt_verify(
struct xfs_mount *mp,
struct xfs_buf *bp,
void *ptr,
- int fsbsize,
xfs_daddr_t bno)
{
struct xfs_attr3_rmt_hdr *rmt = ptr;
@@ -102,7 +101,7 @@ xfs_attr3_rmt_verify(
return __this_address;
if (be64_to_cpu(rmt->rm_blkno) != bno)
return __this_address;
- if (be32_to_cpu(rmt->rm_bytes) > fsbsize - sizeof(*rmt))
+ if (be32_to_cpu(rmt->rm_bytes) > mp->m_attr_geo->blksize - sizeof(*rmt))
return __this_address;
if (be32_to_cpu(rmt->rm_offset) +
be32_to_cpu(rmt->rm_bytes) > XFS_XATTR_SIZE_MAX)
@@ -121,9 +120,9 @@ __xfs_attr3_rmt_read_verify(
{
struct xfs_mount *mp = bp->b_mount;
char *ptr;
- int len;
+ unsigned int len;
xfs_daddr_t bno;
- int blksize = mp->m_attr_geo->blksize;
+ unsigned int blksize = mp->m_attr_geo->blksize;
/* no verification of non-crc buffers */
if (!xfs_has_crc(mp))
@@ -140,7 +139,7 @@ __xfs_attr3_rmt_read_verify(
*failaddr = __this_address;
return -EFSBADCRC;
}
- *failaddr = xfs_attr3_rmt_verify(mp, bp, ptr, blksize, bno);
+ *failaddr = xfs_attr3_rmt_verify(mp, bp, ptr, bno);
if (*failaddr)
return -EFSCORRUPTED;
len -= blksize;
@@ -185,7 +184,7 @@ xfs_attr3_rmt_write_verify(
{
struct xfs_mount *mp = bp->b_mount;
xfs_failaddr_t fa;
- int blksize = mp->m_attr_geo->blksize;
+ unsigned int blksize = mp->m_attr_geo->blksize;
char *ptr;
int len;
xfs_daddr_t bno;
@@ -202,7 +201,7 @@ xfs_attr3_rmt_write_verify(
while (len > 0) {
struct xfs_attr3_rmt_hdr *rmt = (struct xfs_attr3_rmt_hdr *)ptr;
- fa = xfs_attr3_rmt_verify(mp, bp, ptr, blksize, bno);
+ fa = xfs_attr3_rmt_verify(mp, bp, ptr, bno);
if (fa) {
xfs_verifier_error(bp, -EFSCORRUPTED, fa);
return;
@@ -280,20 +279,20 @@ xfs_attr_rmtval_copyout(
struct xfs_buf *bp,
struct xfs_inode *dp,
xfs_ino_t owner,
- int *offset,
- int *valuelen,
+ unsigned int *offset,
+ unsigned int *valuelen,
uint8_t **dst)
{
char *src = bp->b_addr;
xfs_daddr_t bno = xfs_buf_daddr(bp);
- int len = BBTOB(bp->b_length);
- int blksize = mp->m_attr_geo->blksize;
+ unsigned int len = BBTOB(bp->b_length);
+ unsigned int blksize = mp->m_attr_geo->blksize;
ASSERT(len >= blksize);
while (len > 0 && *valuelen > 0) {
- int hdr_size = 0;
- int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
+ unsigned int hdr_size = 0;
+ unsigned int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
byte_cnt = min(*valuelen, byte_cnt);
@@ -329,20 +328,20 @@ xfs_attr_rmtval_copyin(
struct xfs_mount *mp,
struct xfs_buf *bp,
xfs_ino_t ino,
- int *offset,
- int *valuelen,
+ unsigned int *offset,
+ unsigned int *valuelen,
uint8_t **src)
{
char *dst = bp->b_addr;
xfs_daddr_t bno = xfs_buf_daddr(bp);
- int len = BBTOB(bp->b_length);
- int blksize = mp->m_attr_geo->blksize;
+ unsigned int len = BBTOB(bp->b_length);
+ unsigned int blksize = mp->m_attr_geo->blksize;
ASSERT(len >= blksize);
while (len > 0 && *valuelen > 0) {
- int hdr_size;
- int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
+ unsigned int hdr_size;
+ unsigned int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
byte_cnt = min(*valuelen, byte_cnt);
hdr_size = xfs_attr3_rmt_hdr_set(mp, dst, ino, *offset,
@@ -388,12 +387,12 @@ xfs_attr_rmtval_get(
struct xfs_buf *bp;
xfs_dablk_t lblkno = args->rmtblkno;
uint8_t *dst = args->value;
- int valuelen;
+ unsigned int valuelen;
int nmap;
int error;
- int blkcnt = args->rmtblkcnt;
+ unsigned int blkcnt = args->rmtblkcnt;
int i;
- int offset = 0;
+ unsigned int offset = 0;
trace_xfs_attr_rmtval_get(args);
@@ -451,7 +450,7 @@ xfs_attr_rmt_find_hole(
struct xfs_inode *dp = args->dp;
struct xfs_mount *mp = dp->i_mount;
int error;
- int blkcnt;
+ unsigned int blkcnt;
xfs_fileoff_t lfileoff = 0;
/*
@@ -480,11 +479,11 @@ xfs_attr_rmtval_set_value(
struct xfs_bmbt_irec map;
xfs_dablk_t lblkno;
uint8_t *src = args->value;
- int blkcnt;
- int valuelen;
+ unsigned int blkcnt;
+ unsigned int valuelen;
int nmap;
int error;
- int offset = 0;
+ unsigned int offset = 0;
/*
* Roll through the "value", copying the attribute value to the
@@ -643,7 +642,7 @@ xfs_attr_rmtval_invalidate(
struct xfs_da_args *args)
{
xfs_dablk_t lblkno;
- int blkcnt;
+ unsigned int blkcnt;
int error;
/*
diff --git a/libxfs/xfs_attr_remote.h b/libxfs/xfs_attr_remote.h
index d097ec6c4..c64b04f91 100644
--- a/libxfs/xfs_attr_remote.h
+++ b/libxfs/xfs_attr_remote.h
@@ -6,7 +6,7 @@
#ifndef __XFS_ATTR_REMOTE_H__
#define __XFS_ATTR_REMOTE_H__
-int xfs_attr3_rmt_blocks(struct xfs_mount *mp, int attrlen);
+unsigned int xfs_attr3_rmt_blocks(struct xfs_mount *mp, unsigned int attrlen);
int xfs_attr_rmtval_get(struct xfs_da_args *args);
int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 103/115] xfs: turn XFS_ATTR3_RMT_BUF_SPACE into a function
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (101 preceding siblings ...)
2024-07-30 0:50 ` [PATCH 102/115] xfs: use unsigned ints for non-negative quantities in xfs_attr_remote.c Darrick J. Wong
@ 2024-07-30 0:50 ` Darrick J. Wong
2024-07-30 0:50 ` [PATCH 104/115] xfs: create a helper to compute the blockcount of a max sized remote value Darrick J. Wong
` (11 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:50 UTC (permalink / raw)
To: djwong, cem; +Cc: Andrey Albershteyn, Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: a5714b67cad586f44777ad834e577037ce6b64fd
Turn this into a properly typechecked function, and actually use the
correct blocksize for extended attributes. The function cannot be
static inline because xfsprogs userspace uses it.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Andrey Albershteyn <aalbersh@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/attr.c | 2 +-
db/metadump.c | 8 ++++----
libxfs/xfs_attr_remote.c | 19 ++++++++++++++++---
libxfs/xfs_da_format.h | 4 +---
4 files changed, 22 insertions(+), 11 deletions(-)
diff --git a/db/attr.c b/db/attr.c
index ba722e146..de68d6276 100644
--- a/db/attr.c
+++ b/db/attr.c
@@ -214,7 +214,7 @@ attr3_remote_data_count(
if (hdr->rm_magic != cpu_to_be32(XFS_ATTR3_RMT_MAGIC))
return 0;
- buf_space = XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
+ buf_space = xfs_attr3_rmt_buf_space(mp);
if (be32_to_cpu(hdr->rm_bytes) > buf_space)
return buf_space;
return be32_to_cpu(hdr->rm_bytes);
diff --git a/db/metadump.c b/db/metadump.c
index a656ef574..9457e02e8 100644
--- a/db/metadump.c
+++ b/db/metadump.c
@@ -1369,7 +1369,7 @@ add_remote_vals(
attr_data.remote_vals[attr_data.remote_val_count] = blockidx;
attr_data.remote_val_count++;
blockidx++;
- length -= XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
+ length -= xfs_attr3_rmt_buf_space(mp);
}
if (attr_data.remote_val_count >= MAX_REMOTE_VALS) {
@@ -1405,8 +1405,8 @@ process_attr_block(
attr_data.remote_vals[i] == offset)
/* Macros to handle both attr and attr3 */
memset(block +
- (bs - XFS_ATTR3_RMT_BUF_SPACE(mp, bs)),
- 'v', XFS_ATTR3_RMT_BUF_SPACE(mp, bs));
+ (bs - xfs_attr3_rmt_buf_space(mp)),
+ 'v', xfs_attr3_rmt_buf_space(mp));
}
return;
}
@@ -1418,7 +1418,7 @@ process_attr_block(
if (nentries == 0 ||
nentries * sizeof(xfs_attr_leaf_entry_t) +
xfs_attr3_leaf_hdr_size(leaf) >
- XFS_ATTR3_RMT_BUF_SPACE(mp, bs)) {
+ xfs_attr3_rmt_buf_space(mp)) {
if (metadump.show_warnings)
print_warning("invalid attr count in inode %llu",
(long long)metadump.cur_ino);
diff --git a/libxfs/xfs_attr_remote.c b/libxfs/xfs_attr_remote.c
index 34e74fd57..58078465b 100644
--- a/libxfs/xfs_attr_remote.c
+++ b/libxfs/xfs_attr_remote.c
@@ -42,6 +42,19 @@
* the logging system and therefore never have a log item.
*/
+/* How many bytes can be stored in a remote value buffer? */
+inline unsigned int
+xfs_attr3_rmt_buf_space(
+ struct xfs_mount *mp)
+{
+ unsigned int blocksize = mp->m_attr_geo->blksize;
+
+ if (xfs_has_crc(mp))
+ return blocksize - sizeof(struct xfs_attr3_rmt_hdr);
+
+ return blocksize;
+}
+
/*
* Each contiguous block has a header, so it is not just a simple attribute
* length to FSB conversion.
@@ -52,7 +65,7 @@ xfs_attr3_rmt_blocks(
unsigned int attrlen)
{
if (xfs_has_crc(mp)) {
- unsigned int buflen = XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
+ unsigned int buflen = xfs_attr3_rmt_buf_space(mp);
return (attrlen + buflen - 1) / buflen;
}
return XFS_B_TO_FSB(mp, attrlen);
@@ -292,7 +305,7 @@ xfs_attr_rmtval_copyout(
while (len > 0 && *valuelen > 0) {
unsigned int hdr_size = 0;
- unsigned int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
+ unsigned int byte_cnt = xfs_attr3_rmt_buf_space(mp);
byte_cnt = min(*valuelen, byte_cnt);
@@ -341,7 +354,7 @@ xfs_attr_rmtval_copyin(
while (len > 0 && *valuelen > 0) {
unsigned int hdr_size;
- unsigned int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
+ unsigned int byte_cnt = xfs_attr3_rmt_buf_space(mp);
byte_cnt = min(*valuelen, byte_cnt);
hdr_size = xfs_attr3_rmt_hdr_set(mp, dst, ino, *offset,
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index ebde6eb1d..86de99e2f 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -880,9 +880,7 @@ struct xfs_attr3_rmt_hdr {
#define XFS_ATTR3_RMT_CRC_OFF offsetof(struct xfs_attr3_rmt_hdr, rm_crc)
-#define XFS_ATTR3_RMT_BUF_SPACE(mp, bufsize) \
- ((bufsize) - (xfs_has_crc((mp)) ? \
- sizeof(struct xfs_attr3_rmt_hdr) : 0))
+unsigned int xfs_attr3_rmt_buf_space(struct xfs_mount *mp);
/* Number of bytes in a directory block. */
static inline unsigned int xfs_dir2_dirblock_bytes(struct xfs_sb *sbp)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 104/115] xfs: create a helper to compute the blockcount of a max sized remote value
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (102 preceding siblings ...)
2024-07-30 0:50 ` [PATCH 103/115] xfs: turn XFS_ATTR3_RMT_BUF_SPACE into a function Darrick J. Wong
@ 2024-07-30 0:50 ` Darrick J. Wong
2024-07-30 0:51 ` [PATCH 105/115] xfs: minor cleanups of xfs_attr3_rmt_blocks Darrick J. Wong
` (10 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:50 UTC (permalink / raw)
To: djwong, cem; +Cc: Andrey Albershteyn, Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 204a26aa1d5a891154c9275fe4022e28793ca112
Create a helper function to compute the number of fsblocks needed to
store a maximally-sized extended attribute value.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Andrey Albershteyn <aalbersh@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 2 +-
libxfs/xfs_attr_remote.h | 6 ++++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 52fcb1c4c..99648d78c 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -1035,7 +1035,7 @@ xfs_attr_set(
break;
case XFS_ATTRUPDATE_REMOVE:
XFS_STATS_INC(mp, xs_attr_remove);
- rmt_blks = xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX);
+ rmt_blks = xfs_attr3_max_rmt_blocks(mp);
break;
}
diff --git a/libxfs/xfs_attr_remote.h b/libxfs/xfs_attr_remote.h
index c64b04f91..e3c6c7d77 100644
--- a/libxfs/xfs_attr_remote.h
+++ b/libxfs/xfs_attr_remote.h
@@ -8,6 +8,12 @@
unsigned int xfs_attr3_rmt_blocks(struct xfs_mount *mp, unsigned int attrlen);
+/* Number of rmt blocks needed to store the maximally sized attr value */
+static inline unsigned int xfs_attr3_max_rmt_blocks(struct xfs_mount *mp)
+{
+ return xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX);
+}
+
int xfs_attr_rmtval_get(struct xfs_da_args *args);
int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
xfs_buf_flags_t incore_flags);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 105/115] xfs: minor cleanups of xfs_attr3_rmt_blocks
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (103 preceding siblings ...)
2024-07-30 0:50 ` [PATCH 104/115] xfs: create a helper to compute the blockcount of a max sized remote value Darrick J. Wong
@ 2024-07-30 0:51 ` Darrick J. Wong
2024-07-30 0:51 ` [PATCH 106/115] xfs: xfs_quota_unreserve_blkres can't fail Darrick J. Wong
` (9 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:51 UTC (permalink / raw)
To: djwong, cem; +Cc: Andrey Albershteyn, Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 3791a053294b037a6bf62df03480f5c5ddfd4d1b
Clean up the type signature of this function since we don't have
negative attr lengths or block counts.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Andrey Albershteyn <aalbersh@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr_remote.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libxfs/xfs_attr_remote.c b/libxfs/xfs_attr_remote.c
index 58078465b..a048aa5f2 100644
--- a/libxfs/xfs_attr_remote.c
+++ b/libxfs/xfs_attr_remote.c
@@ -55,19 +55,19 @@ xfs_attr3_rmt_buf_space(
return blocksize;
}
-/*
- * Each contiguous block has a header, so it is not just a simple attribute
- * length to FSB conversion.
- */
+/* Compute number of fsblocks needed to store a remote attr value */
unsigned int
xfs_attr3_rmt_blocks(
struct xfs_mount *mp,
unsigned int attrlen)
{
- if (xfs_has_crc(mp)) {
- unsigned int buflen = xfs_attr3_rmt_buf_space(mp);
- return (attrlen + buflen - 1) / buflen;
- }
+ /*
+ * Each contiguous block has a header, so it is not just a simple
+ * attribute length to FSB conversion.
+ */
+ if (xfs_has_crc(mp))
+ return howmany(attrlen, xfs_attr3_rmt_buf_space(mp));
+
return XFS_B_TO_FSB(mp, attrlen);
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 106/115] xfs: xfs_quota_unreserve_blkres can't fail
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (104 preceding siblings ...)
2024-07-30 0:51 ` [PATCH 105/115] xfs: minor cleanups of xfs_attr3_rmt_blocks Darrick J. Wong
@ 2024-07-30 0:51 ` Darrick J. Wong
2024-07-30 0:51 ` [PATCH 107/115] xfs: simplify iext overflow checking and upgrade Darrick J. Wong
` (8 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:51 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: cc3c92e7e79eb5f7f3ec4d5790ade384b7d294f7
Unreserving quotas can't fail due to quota limits, and we'll notice a
shut down file system a bit later in all the callers anyway. Return
void and remove the error checking and propagation in the callers.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_bmap.c | 16 +++++-----------
libxfs/xfs_bmap.h | 2 +-
2 files changed, 6 insertions(+), 12 deletions(-)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index e6d700138..4a365f1a1 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4922,7 +4922,7 @@ xfs_bmap_split_indlen(
*indlen2 = len2;
}
-int
+void
xfs_bmap_del_extent_delay(
struct xfs_inode *ip,
int whichfork,
@@ -4938,7 +4938,6 @@ xfs_bmap_del_extent_delay(
xfs_filblks_t got_indlen, new_indlen, stolen = 0;
uint32_t state = xfs_bmap_fork_to_state(whichfork);
uint64_t fdblocks;
- int error = 0;
bool isrt;
XFS_STATS_INC(mp, xs_del_exlist);
@@ -4958,9 +4957,7 @@ xfs_bmap_del_extent_delay(
* sb counters as we might have to borrow some blocks for the
* indirect block accounting.
*/
- error = xfs_quota_unreserve_blkres(ip, del->br_blockcount);
- if (error)
- return error;
+ xfs_quota_unreserve_blkres(ip, del->br_blockcount);
ip->i_delayed_blks -= del->br_blockcount;
if (got->br_startoff == del->br_startoff)
@@ -5058,7 +5055,6 @@ xfs_bmap_del_extent_delay(
xfs_add_fdblocks(mp, fdblocks);
xfs_mod_delalloc(ip, -(int64_t)del->br_blockcount, -da_diff);
- return error;
}
void
@@ -5616,18 +5612,16 @@ __xfs_bunmapi(
delete:
if (wasdel) {
- error = xfs_bmap_del_extent_delay(ip, whichfork, &icur,
- &got, &del);
+ xfs_bmap_del_extent_delay(ip, whichfork, &icur, &got, &del);
} else {
error = xfs_bmap_del_extent_real(ip, tp, &icur, cur,
&del, &tmp_logflags, whichfork,
flags);
logflags |= tmp_logflags;
+ if (error)
+ goto error0;
}
- if (error)
- goto error0;
-
end = del.br_startoff - 1;
nodelete:
/*
diff --git a/libxfs/xfs_bmap.h b/libxfs/xfs_bmap.h
index e98849eb9..667b0c2b3 100644
--- a/libxfs/xfs_bmap.h
+++ b/libxfs/xfs_bmap.h
@@ -202,7 +202,7 @@ int xfs_bmapi_write(struct xfs_trans *tp, struct xfs_inode *ip,
int xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
xfs_fileoff_t bno, xfs_filblks_t len, uint32_t flags,
xfs_extnum_t nexts, int *done);
-int xfs_bmap_del_extent_delay(struct xfs_inode *ip, int whichfork,
+void xfs_bmap_del_extent_delay(struct xfs_inode *ip, int whichfork,
struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *got,
struct xfs_bmbt_irec *del);
void xfs_bmap_del_extent_cow(struct xfs_inode *ip,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 107/115] xfs: simplify iext overflow checking and upgrade
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (105 preceding siblings ...)
2024-07-30 0:51 ` [PATCH 106/115] xfs: xfs_quota_unreserve_blkres can't fail Darrick J. Wong
@ 2024-07-30 0:51 ` Darrick J. Wong
2024-07-30 0:51 ` [PATCH 108/115] xfs: Stop using __maybe_unused in xfs_alloc.c Darrick J. Wong
` (7 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:51 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Dave Chinner, Chandan Babu R, linux-xfs
From: Christoph Hellwig <hch@lst.de>
Source kernel commit: 25576c5420e61dea4c2b52942460f2221b8e46e8
Currently the calls to xfs_iext_count_may_overflow and
xfs_iext_count_upgrade are always paired. Merge them into a single
function to simplify the callers and the actual check and upgrade
logic itself.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_attr.c | 5 +---
libxfs/xfs_bmap.c | 5 +---
libxfs/xfs_inode_fork.c | 63 +++++++++++++++++++++--------------------------
libxfs/xfs_inode_fork.h | 6 +---
4 files changed, 32 insertions(+), 47 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 99648d78c..9d32aa406 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -1049,11 +1049,8 @@ xfs_attr_set(
return error;
if (op != XFS_ATTRUPDATE_REMOVE || xfs_inode_hasattr(dp)) {
- error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK,
+ error = xfs_iext_count_extend(args->trans, dp, XFS_ATTR_FORK,
XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
- if (error == -EFBIG)
- error = xfs_iext_count_upgrade(args->trans, dp,
- XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
if (error)
goto out_trans_cancel;
}
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 4a365f1a1..347b44423 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4645,11 +4645,8 @@ xfs_bmapi_convert_one_delalloc(
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, 0);
- error = xfs_iext_count_may_overflow(ip, whichfork,
+ error = xfs_iext_count_extend(tp, ip, whichfork,
XFS_IEXT_ADD_NOSPLIT_CNT);
- if (error == -EFBIG)
- error = xfs_iext_count_upgrade(tp, ip,
- XFS_IEXT_ADD_NOSPLIT_CNT);
if (error)
goto out_trans_cancel;
diff --git a/libxfs/xfs_inode_fork.c b/libxfs/xfs_inode_fork.c
index d9f0a21ac..cd5e2e729 100644
--- a/libxfs/xfs_inode_fork.c
+++ b/libxfs/xfs_inode_fork.c
@@ -763,53 +763,46 @@ xfs_ifork_verify_local_attr(
return 0;
}
-int
-xfs_iext_count_may_overflow(
- struct xfs_inode *ip,
- int whichfork,
- int nr_to_add)
-{
- struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
- uint64_t max_exts;
- uint64_t nr_exts;
-
- if (whichfork == XFS_COW_FORK)
- return 0;
-
- max_exts = xfs_iext_max_nextents(xfs_inode_has_large_extent_counts(ip),
- whichfork);
-
- if (XFS_TEST_ERROR(false, ip->i_mount, XFS_ERRTAG_REDUCE_MAX_IEXTENTS))
- max_exts = 10;
-
- nr_exts = ifp->if_nextents + nr_to_add;
- if (nr_exts < ifp->if_nextents || nr_exts > max_exts)
- return -EFBIG;
-
- return 0;
-}
-
/*
- * Upgrade this inode's extent counter fields to be able to handle a potential
- * increase in the extent count by nr_to_add. Normally this is the same
- * quantity that caused xfs_iext_count_may_overflow() to return -EFBIG.
+ * Check if the inode fork supports adding nr_to_add more extents.
+ *
+ * If it doesn't but we can upgrade it to large extent counters, do the upgrade.
+ * If we can't upgrade or are already using big counters but still can't fit the
+ * additional extents, return -EFBIG.
*/
int
-xfs_iext_count_upgrade(
+xfs_iext_count_extend(
struct xfs_trans *tp,
struct xfs_inode *ip,
+ int whichfork,
uint nr_to_add)
{
+ struct xfs_mount *mp = ip->i_mount;
+ bool has_large =
+ xfs_inode_has_large_extent_counts(ip);
+ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
+ uint64_t nr_exts;
+
ASSERT(nr_to_add <= XFS_MAX_EXTCNT_UPGRADE_NR);
- if (!xfs_has_large_extent_counts(ip->i_mount) ||
- xfs_inode_has_large_extent_counts(ip) ||
- XFS_TEST_ERROR(false, ip->i_mount, XFS_ERRTAG_REDUCE_MAX_IEXTENTS))
+ if (whichfork == XFS_COW_FORK)
+ return 0;
+
+ /* no point in upgrading if if_nextents overflows */
+ nr_exts = ifp->if_nextents + nr_to_add;
+ if (nr_exts < ifp->if_nextents)
return -EFBIG;
- ip->i_diflags2 |= XFS_DIFLAG2_NREXT64;
- xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_REDUCE_MAX_IEXTENTS) &&
+ nr_exts > 10)
+ return -EFBIG;
+ if (nr_exts > xfs_iext_max_nextents(has_large, whichfork)) {
+ if (has_large || !xfs_has_large_extent_counts(mp))
+ return -EFBIG;
+ ip->i_diflags2 |= XFS_DIFLAG2_NREXT64;
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ }
return 0;
}
diff --git a/libxfs/xfs_inode_fork.h b/libxfs/xfs_inode_fork.h
index bd53eb951..2373d12fd 100644
--- a/libxfs/xfs_inode_fork.h
+++ b/libxfs/xfs_inode_fork.h
@@ -256,10 +256,8 @@ extern void xfs_ifork_init_cow(struct xfs_inode *ip);
int xfs_ifork_verify_local_data(struct xfs_inode *ip);
int xfs_ifork_verify_local_attr(struct xfs_inode *ip);
-int xfs_iext_count_may_overflow(struct xfs_inode *ip, int whichfork,
- int nr_to_add);
-int xfs_iext_count_upgrade(struct xfs_trans *tp, struct xfs_inode *ip,
- uint nr_to_add);
+int xfs_iext_count_extend(struct xfs_trans *tp, struct xfs_inode *ip,
+ int whichfork, uint nr_to_add);
bool xfs_ifork_is_realtime(struct xfs_inode *ip, int whichfork);
/* returns true if the fork has extents but they are not read in yet. */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 108/115] xfs: Stop using __maybe_unused in xfs_alloc.c
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (106 preceding siblings ...)
2024-07-30 0:51 ` [PATCH 107/115] xfs: simplify iext overflow checking and upgrade Darrick J. Wong
@ 2024-07-30 0:51 ` Darrick J. Wong
2024-07-30 0:52 ` [PATCH 109/115] xfs: fix xfs_init_attr_trans not handling explicit operation codes Darrick J. Wong
` (6 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:51 UTC (permalink / raw)
To: djwong, cem
Cc: Dave Chinner, John Garry, Christoph Hellwig, Chandan Babu R,
linux-xfs
From: John Garry <john.g.garry@oracle.com>
Source kernel commit: b33874fb7f28326380562f208d948bab785fbd6f
In both xfs_alloc_cur_finish() and xfs_alloc_ag_vextent_exact(), local
variable @afg is tagged as __maybe_unused. Otherwise an unused variable
warning would be generated for when building with W=1 and CONFIG_XFS_DEBUG
unset. In both cases, the variable is unused as it is only referenced in
an ASSERT() call, which is compiled out (in this config).
It is generally a poor programming style to use __maybe_unused for
variables.
The ASSERT() call is to verify that agbno of the end of the extent is
within bounds for both functions. @afg is used as an intermediate variable
to find the AG length.
However xfs_verify_agbext() already exists to verify a valid extent range.
The arguments for calling xfs_verify_agbext() are already available, so use
that instead.
An advantage of using xfs_verify_agbext() is that it verifies that both the
start and the end of the extent are within the bounds of the AG and
catches overflows.
Suggested-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: John Garry <john.g.garry@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_alloc.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/libxfs/xfs_alloc.c b/libxfs/xfs_alloc.c
index b86f788f4..45feff034 100644
--- a/libxfs/xfs_alloc.c
+++ b/libxfs/xfs_alloc.c
@@ -1004,13 +1004,12 @@ xfs_alloc_cur_finish(
struct xfs_alloc_arg *args,
struct xfs_alloc_cur *acur)
{
- struct xfs_agf __maybe_unused *agf = args->agbp->b_addr;
int error;
ASSERT(acur->cnt && acur->bnolt);
ASSERT(acur->bno >= acur->rec_bno);
ASSERT(acur->bno + acur->len <= acur->rec_bno + acur->rec_len);
- ASSERT(acur->rec_bno + acur->rec_len <= be32_to_cpu(agf->agf_length));
+ ASSERT(xfs_verify_agbext(args->pag, acur->rec_bno, acur->rec_len));
error = xfs_alloc_fixup_trees(acur->cnt, acur->bnolt, acur->rec_bno,
acur->rec_len, acur->bno, acur->len, 0);
@@ -1213,7 +1212,6 @@ STATIC int /* error */
xfs_alloc_ag_vextent_exact(
xfs_alloc_arg_t *args) /* allocation argument structure */
{
- struct xfs_agf __maybe_unused *agf = args->agbp->b_addr;
struct xfs_btree_cur *bno_cur;/* by block-number btree cursor */
struct xfs_btree_cur *cnt_cur;/* by count btree cursor */
int error;
@@ -1293,7 +1291,7 @@ xfs_alloc_ag_vextent_exact(
*/
cnt_cur = xfs_cntbt_init_cursor(args->mp, args->tp, args->agbp,
args->pag);
- ASSERT(args->agbno + args->len <= be32_to_cpu(agf->agf_length));
+ ASSERT(xfs_verify_agbext(args->pag, args->agbno, args->len));
error = xfs_alloc_fixup_trees(cnt_cur, bno_cur, fbno, flen, args->agbno,
args->len, XFSA_FIXUP_BNO_OK);
if (error) {
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 109/115] xfs: fix xfs_init_attr_trans not handling explicit operation codes
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (107 preceding siblings ...)
2024-07-30 0:51 ` [PATCH 108/115] xfs: Stop using __maybe_unused in xfs_alloc.c Darrick J. Wong
@ 2024-07-30 0:52 ` Darrick J. Wong
2024-07-30 0:52 ` [PATCH 110/115] xfs: allow symlinks with short remote targets Darrick J. Wong
` (5 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:52 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 06c37c719d22339ad09b93735923e9b1a9794871
When we were converting the attr code to use an explicit operation code
instead of keying off of attr->value being null, we forgot to change the
code that initializes the transaction reservation. Split the function
into two helpers that handle the !remove and remove cases, then fix both
callsites to handle this correctly.
Fixes: c27411d4c640 ("xfs: make attr removal an explicit operation")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_attr.c | 38 ++++++++++++++++++--------------------
libxfs/xfs_attr.h | 3 +--
2 files changed, 19 insertions(+), 22 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 9d32aa406..9e1cce577 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -328,26 +328,20 @@ xfs_attr_calc_size(
return nblks;
}
-/* Initialize transaction reservation for attr operations */
-void
-xfs_init_attr_trans(
- struct xfs_da_args *args,
- struct xfs_trans_res *tres,
- unsigned int *total)
+/* Initialize transaction reservation for an xattr set/replace/upsert */
+inline struct xfs_trans_res
+xfs_attr_set_resv(
+ const struct xfs_da_args *args)
{
- struct xfs_mount *mp = args->dp->i_mount;
+ struct xfs_mount *mp = args->dp->i_mount;
+ struct xfs_trans_res ret = {
+ .tr_logres = M_RES(mp)->tr_attrsetm.tr_logres +
+ M_RES(mp)->tr_attrsetrt.tr_logres * args->total,
+ .tr_logcount = XFS_ATTRSET_LOG_COUNT,
+ .tr_logflags = XFS_TRANS_PERM_LOG_RES,
+ };
- if (args->value) {
- tres->tr_logres = M_RES(mp)->tr_attrsetm.tr_logres +
- M_RES(mp)->tr_attrsetrt.tr_logres *
- args->total;
- tres->tr_logcount = XFS_ATTRSET_LOG_COUNT;
- tres->tr_logflags = XFS_TRANS_PERM_LOG_RES;
- *total = args->total;
- } else {
- *tres = M_RES(mp)->tr_attrrm;
- *total = XFS_ATTRRM_SPACE_RES(mp);
- }
+ return ret;
}
/*
@@ -1005,7 +999,7 @@ xfs_attr_set(
struct xfs_trans_res tres;
int error, local;
int rmt_blks = 0;
- unsigned int total;
+ unsigned int total = 0;
ASSERT(!args->trans);
@@ -1032,10 +1026,15 @@ xfs_attr_set(
if (!local)
rmt_blks = xfs_attr3_rmt_blocks(mp, args->valuelen);
+
+ tres = xfs_attr_set_resv(args);
+ total = args->total;
break;
case XFS_ATTRUPDATE_REMOVE:
XFS_STATS_INC(mp, xs_attr_remove);
rmt_blks = xfs_attr3_max_rmt_blocks(mp);
+ tres = M_RES(mp)->tr_attrrm;
+ total = XFS_ATTRRM_SPACE_RES(mp);
break;
}
@@ -1043,7 +1042,6 @@ xfs_attr_set(
* Root fork attributes can use reserved data blocks for this
* operation if necessary
*/
- xfs_init_attr_trans(args, &tres, &total);
error = xfs_trans_alloc_inode(dp, &tres, total, 0, rsvd, &args->trans);
if (error)
return error;
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index 088cb7b30..0e51d0723 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -565,8 +565,7 @@ bool xfs_attr_check_namespace(unsigned int attr_flags);
bool xfs_attr_namecheck(unsigned int attr_flags, 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);
+struct xfs_trans_res xfs_attr_set_resv(const struct xfs_da_args *args);
/*
* Check to see if the attr should be upgraded from non-existent or shortform to
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 110/115] xfs: allow symlinks with short remote targets
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (108 preceding siblings ...)
2024-07-30 0:52 ` [PATCH 109/115] xfs: fix xfs_init_attr_trans not handling explicit operation codes Darrick J. Wong
@ 2024-07-30 0:52 ` Darrick J. Wong
2024-07-30 0:52 ` [PATCH 111/115] xfs: Add cond_resched to block unmap range and reflink remap path Darrick J. Wong
` (4 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:52 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 6d3103369360d96f52336571138980d4c831c091
An internal user complained about log recovery failing on a symlink
("Bad dinode after recovery") with the following (excerpted) format:
core.magic = 0x494e
core.mode = 0120777
core.version = 3
core.format = 2 (extents)
core.nlinkv2 = 1
core.nextents = 1
core.size = 297
core.nblocks = 1
core.naextents = 0
core.forkoff = 0
core.aformat = 2 (extents)
u3.bmx[0] = [startoff,startblock,blockcount,extentflag]
0:[0,12,1,0]
This is a symbolic link with a 297-byte target stored in a disk block,
which is to say this is a symlink with a remote target. The forkoff is
0, which is to say that there's 512 - 176 == 336 bytes in the inode core
to store the data fork.
Eventually, testing of generic/388 failed with the same inode corruption
message during inode recovery. In writing a debugging patch to call
xfs_dinode_verify on dirty inode log items when we're committing
transactions, I observed that xfs/298 can reproduce the problem quite
quickly.
xfs/298 creates a symbolic link, adds some extended attributes, then
deletes them all. The test failure occurs when the final removexattr
also deletes the attr fork because that does not convert the remote
symlink back into a shortform symlink. That is how we trip this test.
The only reason why xfs/298 only triggers with the debug patch added is
that it deletes the symlink, so the final iflush shows the inode as
free.
I wrote a quick fstest to emulate the behavior of xfs/298, except that
it leaves the symlinks on the filesystem after inducing the "corrupt"
state. Kernels going back at least as far as 4.18 have written out
symlink inodes in this manner and prior to 1eb70f54c445f they did not
object to reading them back in.
Because we've been writing out inodes this way for quite some time, the
only way to fix this is to relax the check for symbolic links.
Directories don't have this problem because di_size is bumped to
blocksize during the sf->data conversion.
Fixes: 1eb70f54c445f ("xfs: validate inode fork size against fork format")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfs_inode_buf.c | 28 ++++++++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/libxfs/xfs_inode_buf.c b/libxfs/xfs_inode_buf.c
index aee581d53..5c4e66a25 100644
--- a/libxfs/xfs_inode_buf.c
+++ b/libxfs/xfs_inode_buf.c
@@ -371,17 +371,37 @@ xfs_dinode_verify_fork(
/*
* For fork types that can contain local data, check that the fork
* format matches the size of local data contained within the fork.
- *
- * For all types, check that when the size says the should be in extent
- * or btree format, the inode isn't claiming it is in local format.
*/
if (whichfork == XFS_DATA_FORK) {
- if (S_ISDIR(mode) || S_ISLNK(mode)) {
+ /*
+ * A directory small enough to fit in the inode must be stored
+ * in local format. The directory sf <-> extents conversion
+ * code updates the directory size accordingly.
+ */
+ if (S_ISDIR(mode)) {
if (be64_to_cpu(dip->di_size) <= fork_size &&
fork_format != XFS_DINODE_FMT_LOCAL)
return __this_address;
}
+ /*
+ * A symlink with a target small enough to fit in the inode can
+ * be stored in extents format if xattrs were added (thus
+ * converting the data fork from shortform to remote format)
+ * and then removed.
+ */
+ if (S_ISLNK(mode)) {
+ if (be64_to_cpu(dip->di_size) <= fork_size &&
+ fork_format != XFS_DINODE_FMT_EXTENTS &&
+ fork_format != XFS_DINODE_FMT_LOCAL)
+ return __this_address;
+ }
+
+ /*
+ * For all types, check that when the size says the fork should
+ * be in extent or btree format, the inode isn't claiming to be
+ * in local format.
+ */
if (be64_to_cpu(dip->di_size) > fork_size &&
fork_format == XFS_DINODE_FMT_LOCAL)
return __this_address;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 111/115] xfs: Add cond_resched to block unmap range and reflink remap path
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (109 preceding siblings ...)
2024-07-30 0:52 ` [PATCH 110/115] xfs: allow symlinks with short remote targets Darrick J. Wong
@ 2024-07-30 0:52 ` Darrick J. Wong
2024-07-30 0:52 ` [PATCH 112/115] xfs: make sure sb_fdblocks is non-negative Darrick J. Wong
` (3 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:52 UTC (permalink / raw)
To: djwong, cem
Cc: Ojaswin Mujoo, Ritesh Harjani (IBM), Disha Goel, Chandan Babu R,
linux-xfs
From: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Source kernel commit: b0c6bcd58d44b1b843d1b7218db5a1efe917d27e
An async dio write to a sparse file can generate a lot of extents
and when we unlink this file (using rm), the kernel can be busy in umapping
and freeing those extents as part of transaction processing.
Similarly xfs reflink remapping path can also iterate over a million
extent entries in xfs_reflink_remap_blocks().
Since we can busy loop in these two functions, so let's add cond_resched()
to avoid softlockup messages like these.
watchdog: BUG: soft lockup - CPU#1 stuck for 22s! [kworker/1:0:82435]
CPU: 1 PID: 82435 Comm: kworker/1:0 Tainted: G S L 6.9.0-rc5-0-default #1
Workqueue: xfs-inodegc/sda2 xfs_inodegc_worker
NIP [c000000000beea10] xfs_extent_busy_trim+0x100/0x290
LR [c000000000bee958] xfs_extent_busy_trim+0x48/0x290
Call Trace:
xfs_alloc_get_rec+0x54/0x1b0 (unreliable)
xfs_alloc_compute_aligned+0x5c/0x144
xfs_alloc_ag_vextent_size+0x238/0x8d4
xfs_alloc_fix_freelist+0x540/0x694
xfs_free_extent_fix_freelist+0x84/0xe0
__xfs_free_extent+0x74/0x1ec
xfs_extent_free_finish_item+0xcc/0x214
xfs_defer_finish_one+0x194/0x388
xfs_defer_finish_noroll+0x1b4/0x5c8
xfs_defer_finish+0x2c/0xc4
xfs_bunmapi_range+0xa4/0x100
xfs_itruncate_extents_flags+0x1b8/0x2f4
xfs_inactive_truncate+0xe0/0x124
xfs_inactive+0x30c/0x3e0
xfs_inodegc_worker+0x140/0x234
process_scheduled_works+0x240/0x57c
worker_thread+0x198/0x468
kthread+0x138/0x140
start_kernel_thread+0x14/0x18
run fstests generic/175 at 2024-02-02 04:40:21
[ C17] watchdog: BUG: soft lockup - CPU#17 stuck for 23s! [xfs_io:7679]
watchdog: BUG: soft lockup - CPU#17 stuck for 23s! [xfs_io:7679]
CPU: 17 PID: 7679 Comm: xfs_io Kdump: loaded Tainted: G X 6.4.0
NIP [c008000005e3ec94] xfs_rmapbt_diff_two_keys+0x54/0xe0 [xfs]
LR [c008000005e08798] xfs_btree_get_leaf_keys+0x110/0x1e0 [xfs]
Call Trace:
0xc000000014107c00 (unreliable)
__xfs_btree_updkeys+0x8c/0x2c0 [xfs]
xfs_btree_update_keys+0x150/0x170 [xfs]
xfs_btree_lshift+0x534/0x660 [xfs]
xfs_btree_make_block_unfull+0x19c/0x240 [xfs]
xfs_btree_insrec+0x4e4/0x630 [xfs]
xfs_btree_insert+0x104/0x2d0 [xfs]
xfs_rmap_insert+0xc4/0x260 [xfs]
xfs_rmap_map_shared+0x228/0x630 [xfs]
xfs_rmap_finish_one+0x2d4/0x350 [xfs]
xfs_rmap_update_finish_item+0x44/0xc0 [xfs]
xfs_defer_finish_noroll+0x2e4/0x740 [xfs]
__xfs_trans_commit+0x1f4/0x400 [xfs]
xfs_reflink_remap_extent+0x2d8/0x650 [xfs]
xfs_reflink_remap_blocks+0x154/0x320 [xfs]
xfs_file_remap_range+0x138/0x3a0 [xfs]
do_clone_file_range+0x11c/0x2f0
vfs_clone_file_range+0x60/0x1c0
ioctl_file_clone+0x78/0x140
sys_ioctl+0x934/0x1270
system_call_exception+0x158/0x320
system_call_vectored_common+0x15c/0x2ec
Cc: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Tested-by: Disha Goel<disgoel@linux.ibm.com>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/libxfs_priv.h | 2 ++
libxfs/xfs_bmap.c | 1 +
2 files changed, 3 insertions(+)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index 90b2db091..64bc10e10 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -639,4 +639,6 @@ int xfs_bmap_last_extent(struct xfs_trans *tp, struct xfs_inode *ip,
*/
#define lower_32_bits(n) ((uint32_t)((n) & 0xffffffff))
+#define cond_resched() ((void)0)
+
#endif /* __LIBXFS_INTERNAL_XFS_H__ */
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 347b44423..a0dda4640 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -6377,6 +6377,7 @@ xfs_bunmapi_range(
error = xfs_defer_finish(tpp);
if (error)
goto out;
+ cond_resched();
}
out:
return error;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 112/115] xfs: make sure sb_fdblocks is non-negative
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (110 preceding siblings ...)
2024-07-30 0:52 ` [PATCH 111/115] xfs: Add cond_resched to block unmap range and reflink remap path Darrick J. Wong
@ 2024-07-30 0:52 ` Darrick J. Wong
2024-07-30 0:53 ` [PATCH 113/115] xfs: restrict when we try to align cow fork delalloc to cowextsz hints Darrick J. Wong
` (2 subsequent siblings)
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:52 UTC (permalink / raw)
To: djwong, cem; +Cc: Wengang Wang, Chandan Babu R, linux-xfs
From: Wengang Wang <wen.gang.wang@oracle.com>
Source kernel commit: 58f880711f2ba53fd5e959875aff5b3bf6d5c32e
A user with a completely full filesystem experienced an unexpected
shutdown when the filesystem tried to write the superblock during
runtime.
kernel shows the following dmesg:
[ 8.176281] XFS (dm-4): Metadata corruption detected at xfs_sb_write_verify+0x60/0x120 [xfs], xfs_sb block 0x0
[ 8.177417] XFS (dm-4): Unmount and run xfs_repair
[ 8.178016] XFS (dm-4): First 128 bytes of corrupted metadata buffer:
[ 8.178703] 00000000: 58 46 53 42 00 00 10 00 00 00 00 00 01 90 00 00 XFSB............
[ 8.179487] 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
[ 8.180312] 00000020: cf 12 dc 89 ca 26 45 29 92 e6 e3 8d 3b b8 a2 c3 .....&E)....;...
[ 8.181150] 00000030: 00 00 00 00 01 00 00 06 00 00 00 00 00 00 00 80 ................
[ 8.182003] 00000040: 00 00 00 00 00 00 00 81 00 00 00 00 00 00 00 82 ................
[ 8.182004] 00000050: 00 00 00 01 00 64 00 00 00 00 00 04 00 00 00 00 .....d..........
[ 8.182004] 00000060: 00 00 64 00 b4 a5 02 00 02 00 00 08 00 00 00 00 ..d.............
[ 8.182005] 00000070: 00 00 00 00 00 00 00 00 0c 09 09 03 17 00 00 19 ................
[ 8.182008] XFS (dm-4): Corruption of in-memory data detected. Shutting down filesystem
[ 8.182010] XFS (dm-4): Please unmount the filesystem and rectify the problem(s)
When xfs_log_sb writes super block to disk, b_fdblocks is fetched from
m_fdblocks without any lock. As m_fdblocks can experience a positive ->
negative -> positive changing when the FS reaches fullness (see
xfs_mod_fdblocks). So there is a chance that sb_fdblocks is negative, and
because sb_fdblocks is type of unsigned long long, it reads super big.
And sb_fdblocks being bigger than sb_dblocks is a problem during log
recovery, xfs_validate_sb_write() complains.
Fix:
As sb_fdblocks will be re-calculated during mount when lazysbcount is
enabled, We just need to make xfs_validate_sb_write() happy -- make sure
sb_fdblocks is not nenative. This patch also takes care of other percpu
counters in xfs_log_sb.
Signed-off-by: Wengang Wang <wen.gang.wang@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/libxfs_priv.h | 2 +-
libxfs/xfs_sb.c | 7 ++++---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index 64bc10e10..5d1aa23c7 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -210,7 +210,7 @@ static inline bool WARN_ON(bool expr) {
#define WARN_ON_ONCE(e) WARN_ON(e)
#define percpu_counter_read(x) (*x)
#define percpu_counter_read_positive(x) ((*x) > 0 ? (*x) : 0)
-#define percpu_counter_sum(x) (*x)
+#define percpu_counter_sum_positive(x) ((*x) > 0 ? (*x) : 0)
/*
* get_random_u32 is used for di_gen inode allocation, it must be zero for
diff --git a/libxfs/xfs_sb.c b/libxfs/xfs_sb.c
index f45ffd994..bedb36a06 100644
--- a/libxfs/xfs_sb.c
+++ b/libxfs/xfs_sb.c
@@ -1035,11 +1035,12 @@ xfs_log_sb(
* and hence we don't need have to update it here.
*/
if (xfs_has_lazysbcount(mp)) {
- mp->m_sb.sb_icount = percpu_counter_sum(&mp->m_icount);
+ mp->m_sb.sb_icount = percpu_counter_sum_positive(&mp->m_icount);
mp->m_sb.sb_ifree = min_t(uint64_t,
- percpu_counter_sum(&mp->m_ifree),
+ percpu_counter_sum_positive(&mp->m_ifree),
mp->m_sb.sb_icount);
- mp->m_sb.sb_fdblocks = percpu_counter_sum(&mp->m_fdblocks);
+ mp->m_sb.sb_fdblocks =
+ percpu_counter_sum_positive(&mp->m_fdblocks);
}
xfs_sb_to_disk(bp->b_addr, &mp->m_sb);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 113/115] xfs: restrict when we try to align cow fork delalloc to cowextsz hints
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (111 preceding siblings ...)
2024-07-30 0:52 ` [PATCH 112/115] xfs: make sure sb_fdblocks is non-negative Darrick J. Wong
@ 2024-07-30 0:53 ` Darrick J. Wong
2024-07-30 0:53 ` [PATCH 114/115] xfs: allow unlinked symlinks and dirs with zero size Darrick J. Wong
2024-07-30 0:53 ` [PATCH 115/115] xfs: fix direction in XFS_IOC_EXCHANGE_RANGE Darrick J. Wong
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:53 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 288e1f693f04e66be99f27e7cbe4a45936a66745
xfs/205 produces the following failure when always_cow is enabled:
This is the result of overly aggressive attempts to align cow fork
delalloc reservations to the CoW extent size hint. Looking at the trace
data, we're trying to append a single fsblock to the "fred" file.
Trying to create a speculative post-eof reservation fails because
there's not enough space.
We then set @prealloc_blocks to zero and try again, but the cowextsz
alignment code triggers, which expands our request for a 1-fsblock
reservation into a 39-block reservation. There's not enough space for
that, so the whole write fails with ENOSPC even though there's
sufficient space in the filesystem to allocate the single block that we
need to land the write.
There are two things wrong here -- first, we shouldn't be attempting
speculative preallocations beyond what was requested when we're low on
space. Second, if we've already computed a posteof preallocation, we
shouldn't bother trying to align that to the cowextsize hint.
Fix both of these problems by adding a flag that only enables the
expansion of the delalloc reservation to the cowextsize if we're doing a
non-extending write, and only if we're not doing an ENOSPC retry. This
requires us to move the ENOSPC retry logic to xfs_bmapi_reserve_delalloc.
I probably should have caught this six years ago when 6ca30729c206d was
being reviewed, but oh well. Update the comments to reflect what the
code does now.
Fixes: 6ca30729c206d ("xfs: bmap code cleanup")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
include/xfs_trace.h | 1 +
libxfs/xfs_bmap.c | 31 +++++++++++++++++++++++++++----
2 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/include/xfs_trace.h b/include/xfs_trace.h
index 2e5e89d65..fe0854b20 100644
--- a/include/xfs_trace.h
+++ b/include/xfs_trace.h
@@ -68,6 +68,7 @@
#define trace_xfs_log_recover_item_add(a,b,c,d) ((void) 0)
#define trace_xfs_da_btree_corrupt(a,b) ((void) 0)
+#define trace_xfs_delalloc_enospc(...) ((void) 0)
#define trace_xfs_btree_corrupt(a,b) ((void) 0)
#define trace_xfs_btree_updkeys(a,b,c) ((void) 0)
#define trace_xfs_btree_overlapped_query_range(a,b,c) ((void) 0)
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index a0dda4640..e60d11470 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4052,20 +4052,32 @@ xfs_bmapi_reserve_delalloc(
xfs_extlen_t indlen;
uint64_t fdblocks;
int error;
- xfs_fileoff_t aoff = off;
+ xfs_fileoff_t aoff;
+ bool use_cowextszhint =
+ whichfork == XFS_COW_FORK && !prealloc;
+retry:
/*
* Cap the alloc length. Keep track of prealloc so we know whether to
* tag the inode before we return.
*/
+ aoff = off;
alen = XFS_FILBLKS_MIN(len + prealloc, XFS_MAX_BMBT_EXTLEN);
if (!eof)
alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
if (prealloc && alen >= len)
prealloc = alen - len;
- /* Figure out the extent size, adjust alen */
- if (whichfork == XFS_COW_FORK) {
+ /*
+ * If we're targetting the COW fork but aren't creating a speculative
+ * posteof preallocation, try to expand the reservation to align with
+ * the COW extent size hint if there's sufficient free space.
+ *
+ * Unlike the data fork, the CoW cancellation functions will free all
+ * the reservations at inactivation, so we don't require that every
+ * delalloc reservation have a dirty pagecache.
+ */
+ if (use_cowextszhint) {
struct xfs_bmbt_irec prev;
xfs_extlen_t extsz = xfs_get_cowextsz_hint(ip);
@@ -4084,7 +4096,7 @@ xfs_bmapi_reserve_delalloc(
*/
error = xfs_quota_reserve_blkres(ip, alen);
if (error)
- return error;
+ goto out;
/*
* Split changing sb for alen and indlen since they could be coming
@@ -4134,6 +4146,17 @@ xfs_bmapi_reserve_delalloc(
out_unreserve_quota:
if (XFS_IS_QUOTA_ON(mp))
xfs_quota_unreserve_blkres(ip, alen);
+out:
+ if (error == -ENOSPC || error == -EDQUOT) {
+ trace_xfs_delalloc_enospc(ip, off, len);
+
+ if (prealloc || use_cowextszhint) {
+ /* retry without any preallocation */
+ use_cowextszhint = false;
+ prealloc = 0;
+ goto retry;
+ }
+ }
return error;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 114/115] xfs: allow unlinked symlinks and dirs with zero size
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (112 preceding siblings ...)
2024-07-30 0:53 ` [PATCH 113/115] xfs: restrict when we try to align cow fork delalloc to cowextsz hints Darrick J. Wong
@ 2024-07-30 0:53 ` Darrick J. Wong
2024-07-30 0:53 ` [PATCH 115/115] xfs: fix direction in XFS_IOC_EXCHANGE_RANGE Darrick J. Wong
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:53 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: 1ec9307fc066dd8a140d5430f8a7576aa9d78cd3
For a very very long time, inode inactivation has set the inode size to
zero before unmapping the extents associated with the data fork.
Unfortunately, commit 3c6f46eacd876 changed the inode verifier to
prohibit zero-length symlinks and directories. If an inode happens to
get logged in this state and the system crashes before freeing the
inode, log recovery will also fail on the broken inode.
Therefore, allow zero-size symlinks and directories as long as the link
count is zero; nobody will be able to open these files by handle so
there isn't any risk of data exposure.
Fixes: 3c6f46eacd876 ("xfs: sanity check directory inode di_size")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_inode_buf.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/libxfs/xfs_inode_buf.c b/libxfs/xfs_inode_buf.c
index 5c4e66a25..856659cc3 100644
--- a/libxfs/xfs_inode_buf.c
+++ b/libxfs/xfs_inode_buf.c
@@ -376,10 +376,13 @@ xfs_dinode_verify_fork(
/*
* A directory small enough to fit in the inode must be stored
* in local format. The directory sf <-> extents conversion
- * code updates the directory size accordingly.
+ * code updates the directory size accordingly. Directories
+ * being truncated have zero size and are not subject to this
+ * check.
*/
if (S_ISDIR(mode)) {
- if (be64_to_cpu(dip->di_size) <= fork_size &&
+ if (dip->di_size &&
+ be64_to_cpu(dip->di_size) <= fork_size &&
fork_format != XFS_DINODE_FMT_LOCAL)
return __this_address;
}
@@ -525,9 +528,19 @@ xfs_dinode_verify(
if (mode && xfs_mode_to_ftype(mode) == XFS_DIR3_FT_UNKNOWN)
return __this_address;
- /* No zero-length symlinks/dirs. */
- if ((S_ISLNK(mode) || S_ISDIR(mode)) && di_size == 0)
- return __this_address;
+ /*
+ * No zero-length symlinks/dirs unless they're unlinked and hence being
+ * inactivated.
+ */
+ if ((S_ISLNK(mode) || S_ISDIR(mode)) && di_size == 0) {
+ if (dip->di_version > 1) {
+ if (dip->di_nlink)
+ return __this_address;
+ } else {
+ if (dip->di_onlink)
+ return __this_address;
+ }
+ }
fa = xfs_dinode_verify_nrext64(mp, dip);
if (fa)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 115/115] xfs: fix direction in XFS_IOC_EXCHANGE_RANGE
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
` (113 preceding siblings ...)
2024-07-30 0:53 ` [PATCH 114/115] xfs: allow unlinked symlinks and dirs with zero size Darrick J. Wong
@ 2024-07-30 0:53 ` Darrick J. Wong
114 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:53 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, Chandan Babu R, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Source kernel commit: dc5e1cbae270b625dcb978f8ea762eb16a93a016
The kernel reads userspace's buffer but does not write it back.
Therefore this is really an _IOW ioctl. Change this before 6.10 final
releases.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
---
libxfs/xfs_fs.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index 97996cb79..454b63ef7 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -996,7 +996,7 @@ struct xfs_getparents_by_handle {
#define XFS_IOC_FSGEOMETRY _IOR ('X', 126, struct xfs_fsop_geom)
#define XFS_IOC_BULKSTAT _IOR ('X', 127, struct xfs_bulkstat_req)
#define XFS_IOC_INUMBERS _IOR ('X', 128, struct xfs_inumbers_req)
-#define XFS_IOC_EXCHANGE_RANGE _IOWR('X', 129, struct xfs_exchange_range)
+#define XFS_IOC_EXCHANGE_RANGE _IOW ('X', 129, struct xfs_exchange_range)
/* XFS_IOC_GETFSUUID ---------- deprecated 140 */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 01/12] man: document the exchange-range ioctl
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
@ 2024-07-30 0:54 ` Darrick J. Wong
2024-07-30 0:54 ` [PATCH 02/12] man: document XFS_FSOP_GEOM_FLAGS_EXCHRANGE Darrick J. Wong
` (10 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:54 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Document the new file data exchange ioctl.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man2/ioctl_xfs_exchange_range.2 | 278 +++++++++++++++++++++++++++++++++++
1 file changed, 278 insertions(+)
create mode 100644 man/man2/ioctl_xfs_exchange_range.2
diff --git a/man/man2/ioctl_xfs_exchange_range.2 b/man/man2/ioctl_xfs_exchange_range.2
new file mode 100644
index 000000000..450db9c25
--- /dev/null
+++ b/man/man2/ioctl_xfs_exchange_range.2
@@ -0,0 +1,278 @@
+.\" Copyright (c) 2020-2024 Oracle. All rights reserved.
+.\"
+.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
+.\" This is free documentation; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public License as
+.\" published by the Free Software Foundation; either version 2 of
+.\" the License, or (at your option) any later version.
+.\"
+.\" The GNU General Public License's references to "object code"
+.\" and "executables" are to be interpreted as the output of any
+.\" document formatting or typesetting system, including
+.\" intermediate and printed output.
+.\"
+.\" This manual is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public
+.\" License along with this manual; if not, see
+.\" <http://www.gnu.org/licenses/>.
+.\" %%%LICENSE_END
+.TH IOCTL-XFS-EXCHANGE-RANGE 2 2024-02-10 "XFS"
+.SH NAME
+ioctl_xfs_exchange_range \- exchange the contents of parts of two files
+.SH SYNOPSIS
+.br
+.B #include <sys/ioctl.h>
+.br
+.B #include <xfs/xfs_fs.h>
+.PP
+.BI "int ioctl(int " file2_fd ", XFS_IOC_EXCHANGE_RANGE, struct xfs_exchange_range *" arg );
+.SH DESCRIPTION
+Given a range of bytes in a first file
+.B file1_fd
+and a second range of bytes in a second file
+.BR file2_fd ,
+this
+.BR ioctl (2)
+exchanges the contents of the two ranges.
+.PP
+Exchanges are atomic with regards to concurrent file operations.
+Implementations must guarantee that readers see either the old contents or the
+new contents in their entirety, even if the system fails.
+.PP
+The system call parameters are conveyed in structures of the following form:
+.PP
+.in +4n
+.EX
+struct xfs_exchange_range {
+ __s32 file1_fd;
+ __u32 pad;
+ __u64 file1_offset;
+ __u64 file2_offset;
+ __u64 length;
+ __u64 flags;
+};
+.EE
+.in
+.PP
+The field
+.I pad
+must be zero.
+.PP
+The fields
+.IR file1_fd ", " file1_offset ", and " length
+define the first range of bytes to be exchanged.
+.PP
+The fields
+.IR file2_fd ", " file2_offset ", and " length
+define the second range of bytes to be exchanged.
+.PP
+Both files must be from the same filesystem mount.
+If the two file descriptors represent the same file, the byte ranges must not
+overlap.
+Most disk-based filesystems require that the starts of both ranges must be
+aligned to the file block size.
+If this is the case, the ends of the ranges must also be so aligned unless the
+.B XFS_EXCHANGE_RANGE_TO_EOF
+flag is set.
+
+.PP
+The field
+.I flags
+control the behavior of the exchange operation.
+.RS 0.4i
+.TP
+.B XFS_EXCHANGE_RANGE_TO_EOF
+Ignore the
+.I length
+parameter.
+All bytes in
+.I file1_fd
+from
+.I file1_offset
+to EOF are moved to
+.IR file2_fd ,
+and file2's size is set to
+.RI ( file2_offset "+(" file1_length - file1_offset )).
+Meanwhile, all bytes in file2 from
+.I file2_offset
+to EOF are moved to file1 and file1's size is set to
+.RI ( file1_offset "+(" file2_length - file2_offset )).
+.TP
+.B XFS_EXCHANGE_RANGE_DSYNC
+Ensure that all modified in-core data in both file ranges and all metadata
+updates pertaining to the exchange operation are flushed to persistent storage
+before the call returns.
+Opening either file descriptor with
+.BR O_SYNC " or " O_DSYNC
+will have the same effect.
+.TP
+.B XFS_EXCHANGE_RANGE_FILE1_WRITTEN
+Only exchange sub-ranges of
+.I file1_fd
+that are known to contain data written by application software.
+Each sub-range may be expanded (both upwards and downwards) to align with the
+file allocation unit.
+For files on the data device, this is one filesystem block.
+For files on the realtime device, this is the realtime extent size.
+This facility can be used to implement fast atomic scatter-gather writes of any
+complexity for software-defined storage targets if all writes are aligned to
+the file allocation unit.
+.TP
+.B XFS_EXCHANGE_RANGE_DRY_RUN
+Check the parameters and the feasibility of the operation, but do not change
+anything.
+.RE
+.PP
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EBADF
+.IR file1_fd
+is not open for reading and writing or is open for append-only writes; or
+.IR file2_fd
+is not open for reading and writing or is open for append-only writes.
+.TP
+.B EINVAL
+The parameters are not correct for these files.
+This error can also appear if either file descriptor represents
+a device, FIFO, or socket.
+Disk filesystems generally require the offset and length arguments
+to be aligned to the fundamental block sizes of both files.
+.TP
+.B EIO
+An I/O error occurred.
+.TP
+.B EISDIR
+One of the files is a directory.
+.TP
+.B ENOMEM
+The kernel was unable to allocate sufficient memory to perform the
+operation.
+.TP
+.B ENOSPC
+There is not enough free space in the filesystem exchange the contents safely.
+.TP
+.B EOPNOTSUPP
+The filesystem does not support exchanging bytes between the two
+files.
+.TP
+.B EPERM
+.IR file1_fd " or " file2_fd
+are immutable.
+.TP
+.B ETXTBSY
+One of the files is a swap file.
+.TP
+.B EUCLEAN
+The filesystem is corrupt.
+.TP
+.B EXDEV
+.IR file1_fd " and " file2_fd
+are not on the same mounted filesystem.
+.SH CONFORMING TO
+This API is XFS-specific.
+.SH USE CASES
+.PP
+Several use cases are imagined for this system call.
+In all cases, application software must coordinate updates to the file
+because the exchange is performed unconditionally.
+.PP
+The first is a data storage program that wants to commit non-contiguous updates
+to a file atomically and coordinates write access to that file.
+This can be done by creating a temporary file, calling
+.BR FICLONE (2)
+to share the contents, and staging the updates into the temporary file.
+The
+.B FULL_FILES
+flag is recommended for this purpose.
+The temporary file can be deleted or punched out afterwards.
+.PP
+An example program might look like this:
+.PP
+.in +4n
+.EX
+int fd = open("/some/file", O_RDWR);
+int temp_fd = open("/some", O_TMPFILE | O_RDWR);
+
+ioctl(temp_fd, FICLONE, fd);
+
+/* append 1MB of records */
+lseek(temp_fd, 0, SEEK_END);
+write(temp_fd, data1, 1000000);
+
+/* update record index */
+pwrite(temp_fd, data1, 600, 98765);
+pwrite(temp_fd, data2, 320, 54321);
+pwrite(temp_fd, data2, 15, 0);
+
+/* commit the entire update */
+struct xfs_exchange_range args = {
+ .file1_fd = temp_fd,
+ .flags = XFS_EXCHANGE_RANGE_TO_EOF,
+};
+
+ioctl(fd, XFS_IOC_EXCHANGE_RANGE, &args);
+.EE
+.in
+.PP
+The second is a software-defined storage host (e.g. a disk jukebox) which
+implements an atomic scatter-gather write command.
+Provided the exported disk's logical block size matches the file's allocation
+unit size, this can be done by creating a temporary file and writing the data
+at the appropriate offsets.
+It is recommended that the temporary file be truncated to the size of the
+regular file before any writes are staged to the temporary file to avoid issues
+with zeroing during EOF extension.
+Use this call with the
+.B FILE1_WRITTEN
+flag to exchange only the file allocation units involved in the emulated
+device's write command.
+The temporary file should be truncated or punched out completely before being
+reused to stage another write.
+.PP
+An example program might look like this:
+.PP
+.in +4n
+.EX
+int fd = open("/some/file", O_RDWR);
+int temp_fd = open("/some", O_TMPFILE | O_RDWR);
+struct stat sb;
+int blksz;
+
+fstat(fd, &sb);
+blksz = sb.st_blksize;
+
+/* land scatter gather writes between 100fsb and 500fsb */
+pwrite(temp_fd, data1, blksz * 2, blksz * 100);
+pwrite(temp_fd, data2, blksz * 20, blksz * 480);
+pwrite(temp_fd, data3, blksz * 7, blksz * 257);
+
+/* commit the entire update */
+struct xfs_exchange_range args = {
+ .file1_fd = temp_fd,
+ .file1_offset = blksz * 100,
+ .file2_offset = blksz * 100,
+ .length = blksz * 400,
+ .flags = XFS_EXCHANGE_RANGE_FILE1_WRITTEN |
+ XFS_EXCHANGE_RANGE_FILE1_DSYNC,
+};
+
+ioctl(fd, XFS_IOC_EXCHANGE_RANGE, &args);
+.EE
+.in
+.B
+.SH NOTES
+.PP
+Some filesystems may limit the amount of data or the number of extents that can
+be exchanged in a single call.
+.SH SEE ALSO
+.BR ioctl (2)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 02/12] man: document XFS_FSOP_GEOM_FLAGS_EXCHRANGE
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
2024-07-30 0:54 ` [PATCH 01/12] man: document the exchange-range ioctl Darrick J. Wong
@ 2024-07-30 0:54 ` Darrick J. Wong
2024-07-30 0:54 ` [PATCH 03/12] libhandle: add support for bulkstat v5 Darrick J. Wong
` (9 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:54 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Document this new feature flag in the fs geometry ioctl.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man2/ioctl_xfs_fsgeometry.2 | 3 +++
1 file changed, 3 insertions(+)
diff --git a/man/man2/ioctl_xfs_fsgeometry.2 b/man/man2/ioctl_xfs_fsgeometry.2
index f59a6e8a6..54fd89390 100644
--- a/man/man2/ioctl_xfs_fsgeometry.2
+++ b/man/man2/ioctl_xfs_fsgeometry.2
@@ -211,6 +211,9 @@ Filesystem stores reverse mappings of blocks to owners.
.TP
.B XFS_FSOP_GEOM_FLAGS_REFLINK
Filesystem supports sharing blocks between files.
+.TP
+.B XFS_FSOP_GEOM_FLAGS_EXCHRANGE
+Filesystem can exchange file contents atomically via XFS_IOC_EXCHANGE_RANGE.
.RE
.SH XFS METADATA HEALTH REPORTING
.PP
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 03/12] libhandle: add support for bulkstat v5
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
2024-07-30 0:54 ` [PATCH 01/12] man: document the exchange-range ioctl Darrick J. Wong
2024-07-30 0:54 ` [PATCH 02/12] man: document XFS_FSOP_GEOM_FLAGS_EXCHRANGE Darrick J. Wong
@ 2024-07-30 0:54 ` Darrick J. Wong
2024-07-30 0:54 ` [PATCH 04/12] libfrog: add support for exchange range ioctl family Darrick J. Wong
` (8 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:54 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add support to libhandle for generating file handles with bulkstat v5
structures. xfs_fsr will need this to be able to interface with the new
vfs range swap ioctl, and other client programs will probably want this
over time.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
include/jdm.h | 23 +++++++++++
libhandle/jdm.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 140 insertions(+)
diff --git a/include/jdm.h b/include/jdm.h
index c57fcae7f..50c2296b4 100644
--- a/include/jdm.h
+++ b/include/jdm.h
@@ -11,6 +11,7 @@ typedef void jdm_fshandle_t; /* filesystem handle */
typedef void jdm_filehandle_t; /* filehandle */
struct xfs_bstat;
+struct xfs_bulkstat;
struct attrlist_cursor;
struct parent;
@@ -23,6 +24,9 @@ jdm_new_filehandle( jdm_filehandle_t **handlep, /* new filehandle */
jdm_fshandle_t *fshandlep, /* filesystem filehandle */
struct xfs_bstat *sp); /* bulkstat info */
+void jdm_new_filehandle_v5(jdm_filehandle_t **handlep, size_t *hlen,
+ jdm_fshandle_t *fshandlep, struct xfs_bulkstat *sp);
+
extern void
jdm_delete_filehandle( jdm_filehandle_t *handlep,/* filehandle to delete */
size_t hlen); /* filehandle size */
@@ -32,35 +36,54 @@ jdm_open( jdm_fshandle_t *fshandlep,
struct xfs_bstat *sp,
intgen_t oflags);
+intgen_t jdm_open_v5(jdm_fshandle_t *fshandlep, struct xfs_bulkstat *sp,
+ intgen_t oflags);
+
extern intgen_t
jdm_readlink( jdm_fshandle_t *fshandlep,
struct xfs_bstat *sp,
char *bufp,
size_t bufsz);
+intgen_t jdm_readlink_v5(jdm_fshandle_t *fshandlep, struct xfs_bulkstat *sp,
+ char *bufp, size_t bufsz);
+
extern intgen_t
jdm_attr_multi( jdm_fshandle_t *fshp,
struct xfs_bstat *statp,
char *bufp, int rtrvcnt, int flags);
+intgen_t jdm_attr_multi_v5(jdm_fshandle_t *fshp, struct xfs_bulkstat *statp,
+ char *bufp, int rtrvcnt, int flags);
+
extern intgen_t
jdm_attr_list( jdm_fshandle_t *fshp,
struct xfs_bstat *statp,
char *bufp, size_t bufsz, int flags,
struct attrlist_cursor *cursor);
+intgen_t jdm_attr_list_v5(jdm_fshandle_t *fshp, struct xfs_bulkstat *statp,
+ char *bufp, size_t bufsz, int flags,
+ struct attrlist_cursor *cursor);
+
extern int
jdm_parents( jdm_fshandle_t *fshp,
struct xfs_bstat *statp,
struct parent *bufp, size_t bufsz,
unsigned int *count);
+int jdm_parents_v5(jdm_fshandle_t *fshp, struct xfs_bulkstat *statp,
+ struct parent *bufp, size_t bufsz, unsigned int *count);
+
extern int
jdm_parentpaths( jdm_fshandle_t *fshp,
struct xfs_bstat *statp,
struct parent *bufp, size_t bufsz,
unsigned int *count);
+int jdm_parentpaths_v5(jdm_fshandle_t *fshp, struct xfs_bulkstat *statp,
+ struct parent *bufp, size_t bufsz, unsigned int *count);
+
/* macro for determining the size of a structure member */
#define sizeofmember( t, m ) sizeof( ( ( t * )0 )->m )
diff --git a/libhandle/jdm.c b/libhandle/jdm.c
index 07b0c6098..e21aff2b2 100644
--- a/libhandle/jdm.c
+++ b/libhandle/jdm.c
@@ -41,6 +41,19 @@ jdm_fill_filehandle( filehandle_t *handlep,
handlep->fh_ino = statp->bs_ino;
}
+static void
+jdm_fill_filehandle_v5(
+ struct filehandle *handlep,
+ struct fshandle *fshandlep,
+ struct xfs_bulkstat *statp)
+{
+ handlep->fh_fshandle = *fshandlep;
+ handlep->fh_sz_following = FILEHANDLE_SZ_FOLLOWING;
+ memset(handlep->fh_pad, 0, FILEHANDLE_SZ_PAD);
+ handlep->fh_gen = statp->bs_gen;
+ handlep->fh_ino = statp->bs_ino;
+}
+
jdm_fshandle_t *
jdm_getfshandle( char *mntpnt )
{
@@ -90,6 +103,22 @@ jdm_new_filehandle( jdm_filehandle_t **handlep,
jdm_fill_filehandle(*handlep, (fshandle_t *) fshandlep, statp);
}
+void
+jdm_new_filehandle_v5(
+ jdm_filehandle_t **handlep,
+ size_t *hlen,
+ jdm_fshandle_t *fshandlep,
+ struct xfs_bulkstat *statp)
+{
+ /* allocate and fill filehandle */
+ *hlen = sizeof(filehandle_t);
+ *handlep = (filehandle_t *) malloc(*hlen);
+ if (!*handlep)
+ return;
+
+ jdm_fill_filehandle_v5(*handlep, (struct fshandle *)fshandlep, statp);
+}
+
/* ARGSUSED */
void
jdm_delete_filehandle( jdm_filehandle_t *handlep, size_t hlen )
@@ -111,6 +140,19 @@ jdm_open( jdm_fshandle_t *fshp, struct xfs_bstat *statp, intgen_t oflags )
return fd;
}
+intgen_t
+jdm_open_v5(
+ jdm_fshandle_t *fshp,
+ struct xfs_bulkstat *statp,
+ intgen_t oflags)
+{
+ struct fshandle *fshandlep = (struct fshandle *)fshp;
+ struct filehandle filehandle;
+
+ jdm_fill_filehandle_v5(&filehandle, fshandlep, statp);
+ return open_by_fshandle(&filehandle, sizeof(filehandle), oflags);
+}
+
intgen_t
jdm_readlink( jdm_fshandle_t *fshp,
struct xfs_bstat *statp,
@@ -128,6 +170,20 @@ jdm_readlink( jdm_fshandle_t *fshp,
return rval;
}
+intgen_t
+jdm_readlink_v5(
+ jdm_fshandle_t *fshp,
+ struct xfs_bulkstat *statp,
+ char *bufp,
+ size_t bufsz)
+{
+ struct fshandle *fshandlep = (struct fshandle *)fshp;
+ struct filehandle filehandle;
+
+ jdm_fill_filehandle_v5(&filehandle, fshandlep, statp);
+ return readlink_by_handle(&filehandle, sizeof(filehandle), bufp, bufsz);
+}
+
int
jdm_attr_multi( jdm_fshandle_t *fshp,
struct xfs_bstat *statp,
@@ -145,6 +201,22 @@ jdm_attr_multi( jdm_fshandle_t *fshp,
return rval;
}
+int
+jdm_attr_multi_v5(
+ jdm_fshandle_t *fshp,
+ struct xfs_bulkstat *statp,
+ char *bufp,
+ int rtrvcnt,
+ int flags)
+{
+ struct fshandle *fshandlep = (struct fshandle *)fshp;
+ struct filehandle filehandle;
+
+ jdm_fill_filehandle_v5(&filehandle, fshandlep, statp);
+ return attr_multi_by_handle(&filehandle, sizeof(filehandle), bufp,
+ rtrvcnt, flags);
+}
+
int
jdm_attr_list( jdm_fshandle_t *fshp,
struct xfs_bstat *statp,
@@ -166,6 +238,27 @@ jdm_attr_list( jdm_fshandle_t *fshp,
return rval;
}
+int
+jdm_attr_list_v5(
+ jdm_fshandle_t *fshp,
+ struct xfs_bulkstat *statp,
+ char *bufp,
+ size_t bufsz,
+ int flags,
+ struct attrlist_cursor *cursor)
+{
+ struct fshandle *fshandlep = (struct fshandle *)fshp;
+ struct filehandle filehandle;
+
+ /* prevent needless EINVAL from the kernel */
+ if (bufsz > XFS_XATTR_LIST_MAX)
+ bufsz = XFS_XATTR_LIST_MAX;
+
+ jdm_fill_filehandle_v5(&filehandle, fshandlep, statp);
+ return attr_list_by_handle(&filehandle, sizeof(filehandle), bufp,
+ bufsz, flags, cursor);
+}
+
int
jdm_parents( jdm_fshandle_t *fshp,
struct xfs_bstat *statp,
@@ -176,6 +269,18 @@ jdm_parents( jdm_fshandle_t *fshp,
return -1;
}
+int
+jdm_parents_v5(
+ jdm_fshandle_t *fshp,
+ struct xfs_bulkstat *statp,
+ struct parent *bufp,
+ size_t bufsz,
+ unsigned int *count)
+{
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
int
jdm_parentpaths( jdm_fshandle_t *fshp,
struct xfs_bstat *statp,
@@ -185,3 +290,15 @@ jdm_parentpaths( jdm_fshandle_t *fshp,
errno = EOPNOTSUPP;
return -1;
}
+
+int
+jdm_parentpaths_v5(
+ jdm_fshandle_t *fshp,
+ struct xfs_bulkstat *statp,
+ struct parent *bufp,
+ size_t bufsz,
+ unsigned int *count)
+{
+ errno = EOPNOTSUPP;
+ return -1;
+}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 04/12] libfrog: add support for exchange range ioctl family
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 0:54 ` [PATCH 03/12] libhandle: add support for bulkstat v5 Darrick J. Wong
@ 2024-07-30 0:54 ` Darrick J. Wong
2024-07-30 0:55 ` [PATCH 05/12] xfs_db: advertise exchange-range in the version command Darrick J. Wong
` (7 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:54 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add some library code to support the new file range exchange and commit
ioctls.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libfrog/Makefile | 2 ++
libfrog/file_exchange.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++
libfrog/file_exchange.h | 15 ++++++++++++++
3 files changed, 69 insertions(+)
create mode 100644 libfrog/file_exchange.c
create mode 100644 libfrog/file_exchange.h
diff --git a/libfrog/Makefile b/libfrog/Makefile
index cafee073f..53e3c3492 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -18,6 +18,7 @@ bitmap.c \
bulkstat.c \
convert.c \
crc32.c \
+file_exchange.c \
fsgeom.c \
list_sort.c \
linux.c \
@@ -42,6 +43,7 @@ crc32defs.h \
crc32table.h \
dahashselftest.h \
div64.h \
+file_exchange.h \
fsgeom.h \
logging.h \
paths.h \
diff --git a/libfrog/file_exchange.c b/libfrog/file_exchange.c
new file mode 100644
index 000000000..29fdc17e5
--- /dev/null
+++ b/libfrog/file_exchange.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <string.h>
+#include "xfs.h"
+#include "fsgeom.h"
+#include "bulkstat.h"
+#include "libfrog/file_exchange.h"
+
+/* Prepare for a file contents exchange. */
+void
+xfrog_exchangerange_prep(
+ struct xfs_exchange_range *fxr,
+ off_t file2_offset,
+ int file1_fd,
+ off_t file1_offset,
+ uint64_t length)
+{
+ memset(fxr, 0, sizeof(*fxr));
+
+ fxr->file1_fd = file1_fd;
+ fxr->file1_offset = file1_offset;
+ fxr->length = length;
+ fxr->file2_offset = file2_offset;
+}
+
+/*
+ * Execute an exchange-range operation. Returns 0 for success or a negative
+ * errno.
+ */
+int
+xfrog_exchangerange(
+ int file2_fd,
+ struct xfs_exchange_range *fxr,
+ uint64_t flags)
+{
+ int ret;
+
+ fxr->flags = flags;
+
+ ret = ioctl(file2_fd, XFS_IOC_EXCHANGE_RANGE, fxr);
+ if (ret)
+ return -errno;
+
+ return 0;
+}
diff --git a/libfrog/file_exchange.h b/libfrog/file_exchange.h
new file mode 100644
index 000000000..b6f6f9f69
--- /dev/null
+++ b/libfrog/file_exchange.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2020-2024 Oracle. All rights reserved.
+ * All Rights Reserved.
+ */
+#ifndef __LIBFROG_FILE_EXCHANGE_H__
+#define __LIBFROG_FILE_EXCHANGE_H__
+
+void xfrog_exchangerange_prep(struct xfs_exchange_range *fxr,
+ off_t file2_offset, int file1_fd,
+ off_t file1_offset, uint64_t length);
+int xfrog_exchangerange(int file2_fd, struct xfs_exchange_range *fxr,
+ uint64_t flags);
+
+#endif /* __LIBFROG_FILE_EXCHANGE_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 05/12] xfs_db: advertise exchange-range in the version command
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 0:54 ` [PATCH 04/12] libfrog: add support for exchange range ioctl family Darrick J. Wong
@ 2024-07-30 0:55 ` Darrick J. Wong
2024-07-30 0:55 ` [PATCH 06/12] xfs_logprint: support dumping exchmaps log items Darrick J. Wong
` (6 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:55 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Amend the version command to advertise exchange-range support.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/sb.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/db/sb.c b/db/sb.c
index b48767f47..c39011634 100644
--- a/db/sb.c
+++ b/db/sb.c
@@ -706,6 +706,8 @@ version_string(
strcat(s, ",NEEDSREPAIR");
if (xfs_has_large_extent_counts(mp))
strcat(s, ",NREXT64");
+ if (xfs_has_exchange_range(mp))
+ strcat(s, ",EXCHANGE");
return s;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 06/12] xfs_logprint: support dumping exchmaps log items
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 0:55 ` [PATCH 05/12] xfs_db: advertise exchange-range in the version command Darrick J. Wong
@ 2024-07-30 0:55 ` Darrick J. Wong
2024-07-30 0:55 ` [PATCH 07/12] xfs_fsr: convert to bulkstat v5 ioctls Darrick J. Wong
` (5 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:55 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Support dumping exchmaps log items.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
logprint/log_misc.c | 11 ++++
logprint/log_print_all.c | 12 ++++
logprint/log_redo.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++
logprint/logprint.h | 6 ++
4 files changed, 157 insertions(+)
diff --git a/logprint/log_misc.c b/logprint/log_misc.c
index 9d3811340..8e86ac347 100644
--- a/logprint/log_misc.c
+++ b/logprint/log_misc.c
@@ -1052,6 +1052,17 @@ xlog_print_record(
be32_to_cpu(op_head->oh_len));
break;
}
+ case XFS_LI_XMI: {
+ skip = xlog_print_trans_xmi(&ptr,
+ be32_to_cpu(op_head->oh_len),
+ continued);
+ break;
+ }
+ case XFS_LI_XMD: {
+ skip = xlog_print_trans_xmd(&ptr,
+ be32_to_cpu(op_head->oh_len));
+ break;
+ }
case XFS_LI_QUOTAOFF: {
skip = xlog_print_trans_qoff(&ptr,
be32_to_cpu(op_head->oh_len));
diff --git a/logprint/log_print_all.c b/logprint/log_print_all.c
index f436e1091..a4a5e41f1 100644
--- a/logprint/log_print_all.c
+++ b/logprint/log_print_all.c
@@ -440,6 +440,12 @@ xlog_recover_print_logitem(
case XFS_LI_BUI:
xlog_recover_print_bui(item);
break;
+ case XFS_LI_XMD:
+ xlog_recover_print_xmd(item);
+ break;
+ case XFS_LI_XMI:
+ xlog_recover_print_xmi(item);
+ break;
case XFS_LI_DQUOT:
xlog_recover_print_dquot(item);
break;
@@ -498,6 +504,12 @@ xlog_recover_print_item(
case XFS_LI_BUI:
printf("BUI");
break;
+ case XFS_LI_XMD:
+ printf("XMD");
+ break;
+ case XFS_LI_XMI:
+ printf("XMI");
+ break;
case XFS_LI_DQUOT:
printf("DQ ");
break;
diff --git a/logprint/log_redo.c b/logprint/log_redo.c
index edf7e0fbf..ca6dadd75 100644
--- a/logprint/log_redo.c
+++ b/logprint/log_redo.c
@@ -847,3 +847,131 @@ xlog_recover_print_attrd(
f->alfd_size,
(unsigned long long)f->alfd_alf_id);
}
+
+/* Atomic Extent Swapping Items */
+
+static int
+xfs_xmi_copy_format(
+ struct xfs_xmi_log_format *xmi,
+ uint len,
+ struct xfs_xmi_log_format *dst_fmt,
+ int continued)
+{
+ if (len == sizeof(struct xfs_xmi_log_format) || continued) {
+ memcpy(dst_fmt, xmi, len);
+ return 0;
+ }
+ fprintf(stderr, _("%s: bad size of XMI format: %u; expected %zu\n"),
+ progname, len, sizeof(struct xfs_xmi_log_format));
+ return 1;
+}
+
+int
+xlog_print_trans_xmi(
+ char **ptr,
+ uint src_len,
+ int continued)
+{
+ struct xfs_xmi_log_format *src_f, *f = NULL;
+ int error = 0;
+
+ src_f = malloc(src_len);
+ if (src_f == NULL) {
+ fprintf(stderr, _("%s: %s: malloc failed\n"),
+ progname, __func__);
+ exit(1);
+ }
+ memcpy(src_f, *ptr, src_len);
+ *ptr += src_len;
+
+ /* convert to native format */
+ if (continued && src_len < sizeof(struct xfs_xmi_log_format)) {
+ printf(_("XMI: Not enough data to decode further\n"));
+ error = 1;
+ goto error;
+ }
+
+ f = malloc(sizeof(struct xfs_xmi_log_format));
+ if (f == NULL) {
+ fprintf(stderr, _("%s: %s: malloc failed\n"),
+ progname, __func__);
+ exit(1);
+ }
+ if (xfs_xmi_copy_format(src_f, src_len, f, continued)) {
+ error = 1;
+ goto error;
+ }
+
+ printf(_("XMI: #regs: %d num_extents: 1 id: 0x%llx\n"),
+ f->xmi_size, (unsigned long long)f->xmi_id);
+
+ if (continued) {
+ printf(_("XMI extent data skipped (CONTINUE set, no space)\n"));
+ goto error;
+ }
+
+ printf("(ino1: 0x%llx, igen1: 0x%x, ino2: 0x%llx, igen2: 0x%x, off1: %lld, off2: %lld, len: %lld, flags: 0x%llx)\n",
+ (unsigned long long)f->xmi_inode1,
+ (unsigned int)f->xmi_igen1,
+ (unsigned long long)f->xmi_inode2,
+ (unsigned int)f->xmi_igen2,
+ (unsigned long long)f->xmi_startoff1,
+ (unsigned long long)f->xmi_startoff2,
+ (unsigned long long)f->xmi_blockcount,
+ (unsigned long long)f->xmi_flags);
+error:
+ free(src_f);
+ free(f);
+ return error;
+}
+
+void
+xlog_recover_print_xmi(
+ struct xlog_recover_item *item)
+{
+ char *src_f;
+ uint src_len;
+
+ src_f = item->ri_buf[0].i_addr;
+ src_len = item->ri_buf[0].i_len;
+
+ xlog_print_trans_xmi(&src_f, src_len, 0);
+}
+
+int
+xlog_print_trans_xmd(
+ char **ptr,
+ uint len)
+{
+ struct xfs_xmd_log_format *f;
+ struct xfs_xmd_log_format lbuf;
+
+ /* size without extents at end */
+ uint core_size = sizeof(struct xfs_xmd_log_format);
+
+ memcpy(&lbuf, *ptr, min(core_size, len));
+ f = &lbuf;
+ *ptr += len;
+ if (len >= core_size) {
+ printf(_("XMD: #regs: %d id: 0x%llx\n"),
+ f->xmd_size,
+ (unsigned long long)f->xmd_xmi_id);
+
+ /* don't print extents as they are not used */
+
+ return 0;
+ } else {
+ printf(_("XMD: Not enough data to decode further\n"));
+ return 1;
+ }
+}
+
+void
+xlog_recover_print_xmd(
+ struct xlog_recover_item *item)
+{
+ char *f;
+
+ f = item->ri_buf[0].i_addr;
+ xlog_print_trans_xmd(&f, sizeof(struct xfs_xmd_log_format));
+}
diff --git a/logprint/logprint.h b/logprint/logprint.h
index b4479c240..25c043485 100644
--- a/logprint/logprint.h
+++ b/logprint/logprint.h
@@ -65,4 +65,10 @@ extern void xlog_recover_print_attri(struct xlog_recover_item *item);
extern int xlog_print_trans_attrd(char **ptr, uint len);
extern void xlog_recover_print_attrd(struct xlog_recover_item *item);
extern void xlog_print_op_header(xlog_op_header_t *op_head, int i, char **ptr);
+
+int xlog_print_trans_xmi(char **ptr, uint src_len, int continued);
+void xlog_recover_print_xmi(struct xlog_recover_item *item);
+int xlog_print_trans_xmd(char **ptr, uint len);
+void xlog_recover_print_xmd(struct xlog_recover_item *item);
+
#endif /* LOGPRINT_H */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 07/12] xfs_fsr: convert to bulkstat v5 ioctls
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
` (5 preceding siblings ...)
2024-07-30 0:55 ` [PATCH 06/12] xfs_logprint: support dumping exchmaps log items Darrick J. Wong
@ 2024-07-30 0:55 ` Darrick J. Wong
2024-07-30 0:55 ` [PATCH 08/12] xfs_fsr: skip the xattr/forkoff levering with the newer swapext implementations Darrick J. Wong
` (4 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:55 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Now that libhandle can, er, handle bulkstat information coming from the
v5 bulkstat ioctl, port xfs_fsr to use the new interfaces instead of
repeatedly converting things back and forth.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
fsr/xfs_fsr.c | 153 +++++++++++++++++++++++++++++++-----------------------
libfrog/fsgeom.c | 45 ++++++++++++----
libfrog/fsgeom.h | 1
3 files changed, 121 insertions(+), 78 deletions(-)
diff --git a/fsr/xfs_fsr.c b/fsr/xfs_fsr.c
index 06cc0552f..c6dbfb22a 100644
--- a/fsr/xfs_fsr.c
+++ b/fsr/xfs_fsr.c
@@ -65,10 +65,10 @@ static int pagesize;
void usage(int ret);
static int fsrfile(char *fname, xfs_ino_t ino);
-static int fsrfile_common( char *fname, char *tname, char *mnt,
- int fd, struct xfs_bstat *statp);
-static int packfile(char *fname, char *tname, int fd,
- struct xfs_bstat *statp, struct fsxattr *fsxp);
+static int fsrfile_common(char *fname, char *tname, char *mnt,
+ struct xfs_fd *file_fd, struct xfs_bulkstat *statp);
+static int packfile(char *fname, char *tname, struct xfs_fd *file_fd,
+ struct xfs_bulkstat *statp, struct fsxattr *fsxp);
static void fsrdir(char *dirname);
static int fsrfs(char *mntdir, xfs_ino_t ino, int targetrange);
static void initallfs(char *mtab);
@@ -80,7 +80,7 @@ int xfs_getrt(int fd, struct statvfs *sfbp);
char * gettmpname(char *fname);
char * getparent(char *fname);
int fsrprintf(const char *fmt, ...);
-int read_fd_bmap(int, struct xfs_bstat *, int *);
+int read_fd_bmap(int, struct xfs_bulkstat *, int *);
static void tmp_init(char *mnt);
static char * tmp_next(char *mnt);
static void tmp_close(char *mnt);
@@ -102,6 +102,26 @@ static int nfrags = 0; /* Debug option: Coerse into specific number
* of extents */
static int openopts = O_CREAT|O_EXCL|O_RDWR|O_DIRECT;
+/*
+ * Open a file on an XFS filesystem from file handle components and fs geometry
+ * data. Returns zero or a negative error code.
+ */
+static int
+open_handle(
+ struct xfs_fd *xfd,
+ jdm_fshandle_t *fshandle,
+ struct xfs_bulkstat *bulkstat,
+ struct xfs_fsop_geom *fsgeom,
+ int flags)
+{
+ xfd->fd = jdm_open_v5(fshandle, bulkstat, flags);
+ if (xfd->fd < 0)
+ return errno;
+
+ xfd_install_geometry(xfd, fsgeom);
+ return 0;
+}
+
static int
xfs_swapext(int fd, xfs_swapext_t *sx)
{
@@ -627,7 +647,6 @@ static int
fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
{
struct xfs_fd fsxfd = XFS_FD_INIT_EMPTY;
- int fd;
int count = 0;
int ret;
char fname[64];
@@ -665,10 +684,10 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
}
while ((ret = -xfrog_bulkstat(&fsxfd, breq) == 0)) {
- struct xfs_bstat bs1;
struct xfs_bulkstat *buf = breq->bulkstat;
struct xfs_bulkstat *p;
struct xfs_bulkstat *endp;
+ struct xfs_fd file_fd = XFS_FD_INIT_EMPTY;
uint32_t buflenout = breq->hdr.ocount;
if (buflenout == 0)
@@ -685,15 +704,9 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
(p->bs_extents64 < 2))
continue;
- ret = -xfrog_bulkstat_v5_to_v1(&fsxfd, &bs1, p);
+ ret = open_handle(&file_fd, fshandlep, p,
+ &fsxfd.fsgeom, O_RDWR | O_DIRECT);
if (ret) {
- fsrprintf(_("bstat conversion error: %s\n"),
- strerror(ret));
- continue;
- }
-
- fd = jdm_open(fshandlep, &bs1, O_RDWR | O_DIRECT);
- if (fd < 0) {
/* This probably means the file was
* removed while in progress of handling
* it. Just quietly ignore this file.
@@ -710,11 +723,12 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
/* Get a tmp file name */
tname = tmp_next(mntdir);
- ret = fsrfile_common(fname, tname, mntdir, fd, &bs1);
+ ret = fsrfile_common(fname, tname, mntdir, &file_fd,
+ p);
leftoffino = p->bs_ino;
- close(fd);
+ xfd_close(&file_fd);
if (ret == 0) {
if (--count <= 0)
@@ -762,9 +776,8 @@ fsrfile(
{
struct xfs_fd fsxfd = XFS_FD_INIT_EMPTY;
struct xfs_bulkstat bulkstat;
- struct xfs_bstat statbuf;
+ struct xfs_fd file_fd = XFS_FD_INIT_EMPTY;
jdm_fshandle_t *fshandlep;
- int fd = -1;
int error = -1;
char *tname;
@@ -792,17 +805,12 @@ fsrfile(
fname, strerror(error));
goto out;
}
- error = -xfrog_bulkstat_v5_to_v1(&fsxfd, &statbuf, &bulkstat);
- if (error) {
- fsrprintf(_("bstat conversion error on %s: %s\n"),
- fname, strerror(error));
- goto out;
- }
- fd = jdm_open(fshandlep, &statbuf, O_RDWR|O_DIRECT);
- if (fd < 0) {
+ error = open_handle(&file_fd, fshandlep, &bulkstat, &fsxfd.fsgeom,
+ O_RDWR | O_DIRECT);
+ if (error) {
fsrprintf(_("unable to open handle %s: %s\n"),
- fname, strerror(errno));
+ fname, strerror(error));
goto out;
}
@@ -810,14 +818,13 @@ fsrfile(
memcpy(&fsgeom, &fsxfd.fsgeom, sizeof(fsgeom));
tname = gettmpname(fname);
-
if (tname)
- error = fsrfile_common(fname, tname, NULL, fd, &statbuf);
+ error = fsrfile_common(fname, tname, NULL, &file_fd,
+ &bulkstat);
out:
xfd_close(&fsxfd);
- if (fd >= 0)
- close(fd);
+ xfd_close(&file_fd);
free(fshandlep);
return error;
@@ -843,8 +850,8 @@ fsrfile_common(
char *fname,
char *tname,
char *fsname,
- int fd,
- struct xfs_bstat *statp)
+ struct xfs_fd *file_fd,
+ struct xfs_bulkstat *statp)
{
int error;
struct statvfs vfss;
@@ -854,7 +861,7 @@ fsrfile_common(
if (vflag)
fsrprintf("%s\n", fname);
- if (fsync(fd) < 0) {
+ if (fsync(file_fd->fd) < 0) {
fsrprintf(_("sync failed: %s: %s\n"), fname, strerror(errno));
return -1;
}
@@ -878,7 +885,7 @@ fsrfile_common(
fl.l_whence = SEEK_SET;
fl.l_start = (off_t)0;
fl.l_len = 0;
- if ((fcntl(fd, F_GETLK, &fl)) < 0 ) {
+ if ((fcntl(file_fd->fd, F_GETLK, &fl)) < 0 ) {
if (vflag)
fsrprintf(_("locking check failed: %s\n"),
fname);
@@ -896,7 +903,7 @@ fsrfile_common(
/*
* Check if there is room to copy the file.
*
- * Note that xfs_bstat.bs_blksize returns the filesystem blocksize,
+ * Note that xfs_bulkstat.bs_blksize returns the filesystem blocksize,
* not the optimal I/O size as struct stat.
*/
if (statvfs(fsname ? fsname : fname, &vfss) < 0) {
@@ -913,7 +920,7 @@ fsrfile_common(
return 1;
}
- if ((ioctl(fd, FS_IOC_FSGETXATTR, &fsx)) < 0) {
+ if ((ioctl(file_fd->fd, FS_IOC_FSGETXATTR, &fsx)) < 0) {
fsrprintf(_("failed to get inode attrs: %s\n"), fname);
return(-1);
}
@@ -929,7 +936,7 @@ fsrfile_common(
return(0);
}
if (fsx.fsx_xflags & FS_XFLAG_REALTIME) {
- if (xfs_getrt(fd, &vfss) < 0) {
+ if (xfs_getrt(file_fd->fd, &vfss) < 0) {
fsrprintf(_("cannot get realtime geometry for: %s\n"),
fname);
return(-1);
@@ -955,7 +962,7 @@ fsrfile_common(
* file we're defragging, in packfile().
*/
- if ((error = packfile(fname, tname, fd, statp, &fsx)))
+ if ((error = packfile(fname, tname, file_fd, statp, &fsx)))
return error;
return -1; /* no error */
}
@@ -979,7 +986,7 @@ static int
fsr_setup_attr_fork(
int fd,
int tfd,
- struct xfs_bstat *bstatp)
+ struct xfs_bulkstat *bstatp)
{
struct xfs_fd txfd = XFS_FD_INIT(tfd);
struct stat tstatbuf;
@@ -1161,23 +1168,28 @@ fsr_setup_attr_fork(
* 1: No change / No Error
*/
static int
-packfile(char *fname, char *tname, int fd,
- struct xfs_bstat *statp, struct fsxattr *fsxp)
+packfile(
+ char *fname,
+ char *tname,
+ struct xfs_fd *file_fd,
+ struct xfs_bulkstat *statp,
+ struct fsxattr *fsxp)
{
- int tfd = -1;
- int srval;
- int retval = -1; /* Failure is the default */
- int nextents, extent, cur_nextents, new_nextents;
- unsigned blksz_dio;
- unsigned dio_min;
- struct dioattr dio;
- static xfs_swapext_t sx;
- struct xfs_flock64 space;
- off_t cnt, pos;
- void *fbuf = NULL;
- int ct, wc, wc_b4;
- char ffname[SMBUFSZ];
- int ffd = -1;
+ int tfd = -1;
+ int srval;
+ int retval = -1; /* Failure is the default */
+ int nextents, extent, cur_nextents, new_nextents;
+ unsigned blksz_dio;
+ unsigned dio_min;
+ struct dioattr dio;
+ static xfs_swapext_t sx;
+ struct xfs_flock64 space;
+ off_t cnt, pos;
+ void *fbuf = NULL;
+ int ct, wc, wc_b4;
+ char ffname[SMBUFSZ];
+ int ffd = -1;
+ int error;
/*
* Work out the extent map - nextents will be set to the
@@ -1185,7 +1197,7 @@ packfile(char *fname, char *tname, int fd,
* into account holes), cur_nextents is the current number
* of extents.
*/
- nextents = read_fd_bmap(fd, statp, &cur_nextents);
+ nextents = read_fd_bmap(file_fd->fd, statp, &cur_nextents);
if (cur_nextents == 1 || cur_nextents <= nextents) {
if (vflag)
@@ -1208,7 +1220,7 @@ packfile(char *fname, char *tname, int fd,
unlink(tname);
/* Setup extended attributes */
- if (fsr_setup_attr_fork(fd, tfd, statp) != 0) {
+ if (fsr_setup_attr_fork(file_fd->fd, tfd, statp) != 0) {
fsrprintf(_("failed to set ATTR fork on tmp: %s:\n"), tname);
goto out;
}
@@ -1266,6 +1278,8 @@ packfile(char *fname, char *tname, int fd,
for (extent = 0; extent < nextents; extent++) {
pos = outmap[extent].bmv_offset;
if (outmap[extent].bmv_block == -1) {
+ off_t where;
+
space.l_whence = SEEK_SET;
space.l_start = pos;
space.l_len = outmap[extent].bmv_length;
@@ -1273,7 +1287,8 @@ packfile(char *fname, char *tname, int fd,
fsrprintf(_("could not trunc tmp %s\n"),
tname);
}
- if (lseek(tfd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
+ where = lseek(tfd, outmap[extent].bmv_length, SEEK_CUR);
+ if (where < 0) {
fsrprintf(_("could not lseek in tmpfile: %s : %s\n"),
tname, strerror(errno));
goto out;
@@ -1326,7 +1341,7 @@ packfile(char *fname, char *tname, int fd,
tname, strerror(errno));
goto out;
}
- if (lseek(fd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
+ if (lseek(file_fd->fd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
fsrprintf(_("could not lseek in file: %s : %s\n"),
fname, strerror(errno));
goto out;
@@ -1346,7 +1361,7 @@ packfile(char *fname, char *tname, int fd,
ct = min(cnt + dio_min - (cnt % dio_min),
blksz_dio);
}
- ct = read(fd, fbuf, ct);
+ ct = read(file_fd->fd, fbuf, ct);
if (ct == 0) {
/* EOF, stop trying to read */
extent = nextents;
@@ -1417,9 +1432,15 @@ packfile(char *fname, char *tname, int fd,
goto out;
}
- sx.sx_stat = *statp; /* struct copy */
+ error = -xfrog_bulkstat_v5_to_v1(file_fd, &sx.sx_stat, statp);
+ if (error) {
+ fsrprintf(_("bstat conversion error on %s: %s\n"),
+ fname, strerror(error));
+ goto out;
+ }
+
sx.sx_version = XFS_SX_VERSION;
- sx.sx_fdtarget = fd;
+ sx.sx_fdtarget = file_fd->fd;
sx.sx_fdtmp = tfd;
sx.sx_offset = 0;
sx.sx_length = statp->bs_size;
@@ -1433,7 +1454,7 @@ packfile(char *fname, char *tname, int fd,
}
/* Swap the extents */
- srval = xfs_swapext(fd, &sx);
+ srval = xfs_swapext(file_fd->fd, &sx);
if (srval < 0) {
if (errno == ENOTSUP) {
if (vflag || dflag)
@@ -1529,7 +1550,7 @@ getparent(char *fname)
#define MAPSIZE 128
#define OUTMAP_SIZE_INCREMENT MAPSIZE
-int read_fd_bmap(int fd, struct xfs_bstat *sin, int *cur_nextents)
+int read_fd_bmap(int fd, struct xfs_bulkstat *sin, int *cur_nextents)
{
int i, cnt;
struct getbmap map[MAPSIZE];
diff --git a/libfrog/fsgeom.c b/libfrog/fsgeom.c
index 3e7f0797d..6980d3ffa 100644
--- a/libfrog/fsgeom.c
+++ b/libfrog/fsgeom.c
@@ -102,29 +102,50 @@ xfrog_geometry(
return -errno;
}
-/*
- * Prepare xfs_fd structure for future ioctl operations by computing the xfs
- * geometry for @xfd->fd. Returns zero or a negative error code.
- */
-int
-xfd_prepare_geometry(
+/* Compute conversion factors of an xfs_fd structure. */
+static void
+xfd_compute_conversion_factors(
struct xfs_fd *xfd)
{
- int ret;
-
- ret = xfrog_geometry(xfd->fd, &xfd->fsgeom);
- if (ret)
- return ret;
-
xfd->agblklog = log2_roundup(xfd->fsgeom.agblocks);
xfd->blocklog = highbit32(xfd->fsgeom.blocksize);
xfd->inodelog = highbit32(xfd->fsgeom.inodesize);
xfd->inopblog = xfd->blocklog - xfd->inodelog;
xfd->aginolog = xfd->agblklog + xfd->inopblog;
xfd->blkbb_log = xfd->blocklog - BBSHIFT;
+}
+
+/*
+ * Prepare xfs_fd structure for future ioctl operations by computing the xfs
+ * geometry for @xfd->fd. Returns zero or a negative error code.
+ */
+int
+xfd_prepare_geometry(
+ struct xfs_fd *xfd)
+{
+ int ret;
+
+ ret = xfrog_geometry(xfd->fd, &xfd->fsgeom);
+ if (ret)
+ return ret;
+
+ xfd_compute_conversion_factors(xfd);
return 0;
}
+/*
+ * Prepare xfs_fd structure for future ioctl operations by computing the xfs
+ * geometry for @xfd->fd. Returns zero or a negative error code.
+ */
+void
+xfd_install_geometry(
+ struct xfs_fd *xfd,
+ struct xfs_fsop_geom *fsgeom)
+{
+ memcpy(&xfd->fsgeom, fsgeom, sizeof(*fsgeom));
+ xfd_compute_conversion_factors(xfd);
+}
+
/* Open a file on an XFS filesystem. Returns zero or a negative error code. */
int
xfd_open(
diff --git a/libfrog/fsgeom.h b/libfrog/fsgeom.h
index ca38324e8..f327dc7d7 100644
--- a/libfrog/fsgeom.h
+++ b/libfrog/fsgeom.h
@@ -55,6 +55,7 @@ struct xfs_fd {
#define XFS_FD_INIT_EMPTY XFS_FD_INIT(-1)
int xfd_prepare_geometry(struct xfs_fd *xfd);
+void xfd_install_geometry(struct xfs_fd *xfd, struct xfs_fsop_geom *fsgeom);
int xfd_open(struct xfs_fd *xfd, const char *pathname, int flags);
int xfd_close(struct xfs_fd *xfd);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 08/12] xfs_fsr: skip the xattr/forkoff levering with the newer swapext implementations
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
` (6 preceding siblings ...)
2024-07-30 0:55 ` [PATCH 07/12] xfs_fsr: convert to bulkstat v5 ioctls Darrick J. Wong
@ 2024-07-30 0:55 ` Darrick J. Wong
2024-07-30 0:56 ` [PATCH 09/12] xfs_io: create exchangerange command to test file range exchange ioctl Darrick J. Wong
` (3 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:55 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
The newer swapext implementations in the kernel run at a high enough
level (above the bmap layer) that it's no longer required to manipulate
bs_forkoff by creating garbage xattrs to get the extent tree that we
want. If we detect the newer algorithms, skip this error prone step.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
fsr/xfs_fsr.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/fsr/xfs_fsr.c b/fsr/xfs_fsr.c
index c6dbfb22a..22e134adf 100644
--- a/fsr/xfs_fsr.c
+++ b/fsr/xfs_fsr.c
@@ -999,6 +999,20 @@ fsr_setup_attr_fork(
if (!(bstatp->bs_xflags & FS_XFLAG_HASATTR))
return 0;
+ /*
+ * If the filesystem has the ability to perform atomic file mapping
+ * exchanges, the file extent swap implementation uses a higher level
+ * algorithm that calls into the bmap code instead of playing games
+ * with swapping the extent forks.
+ *
+ * This new functionality does not require specific values of
+ * bs_forkoff, unlike the old fork swap code. Leave the extended
+ * attributes alone if we know we're not using the old fork swap
+ * strategy. This eliminates a major source of runtime errors in fsr.
+ */
+ if (fsgeom.flags & XFS_FSOP_GEOM_FLAGS_EXCHANGE_RANGE)
+ return 0;
+
/*
* use the old method if we have attr1 or the kernel does not yet
* support passing the fork offset in the bulkstat data.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 09/12] xfs_io: create exchangerange command to test file range exchange ioctl
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
` (7 preceding siblings ...)
2024-07-30 0:55 ` [PATCH 08/12] xfs_fsr: skip the xattr/forkoff levering with the newer swapext implementations Darrick J. Wong
@ 2024-07-30 0:56 ` Darrick J. Wong
2024-07-30 0:56 ` [PATCH 10/12] libfrog: advertise exchange-range support Darrick J. Wong
` (2 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:56 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create a new xfs_io command to make raw calls to the
XFS_IOC_EXCHANGE_RANGE ioctl.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
io/Makefile | 48 ++++++++++++++--
io/exchrange.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++++
io/init.c | 1
io/io.h | 1
man/man8/xfs_io.8 | 40 ++++++++++++++
5 files changed, 239 insertions(+), 7 deletions(-)
create mode 100644 io/exchrange.c
diff --git a/io/Makefile b/io/Makefile
index 17d499de9..3192b813c 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -8,13 +8,47 @@ include $(TOPDIR)/include/builddefs
LTCOMMAND = xfs_io
LSRCFILES = xfs_bmap.sh xfs_freeze.sh xfs_mkfile.sh
HFILES = init.h io.h
-CFILES = init.c \
- attr.c bmap.c bulkstat.c crc32cselftest.c cowextsize.c encrypt.c \
- file.c freeze.c fsuuid.c fsync.c getrusage.c imap.c inject.c label.c \
- link.c mmap.c open.c parent.c pread.c prealloc.c pwrite.c reflink.c \
- resblks.c scrub.c seek.c shutdown.c stat.c swapext.c sync.c \
- truncate.c utimes.c fadvise.c sendfile.c madvise.c mincore.c fiemap.c \
- sync_file_range.c readdir.c
+CFILES = \
+ attr.c \
+ bmap.c \
+ bulkstat.c \
+ cowextsize.c \
+ crc32cselftest.c \
+ encrypt.c \
+ exchrange.c \
+ fadvise.c \
+ fiemap.c \
+ file.c \
+ freeze.c \
+ fsuuid.c \
+ fsync.c \
+ getrusage.c \
+ imap.c \
+ init.c \
+ inject.c \
+ label.c \
+ link.c \
+ madvise.c \
+ mincore.c \
+ mmap.c \
+ open.c \
+ parent.c \
+ pread.c \
+ prealloc.c \
+ pwrite.c \
+ readdir.c \
+ reflink.c \
+ resblks.c \
+ scrub.c \
+ seek.c \
+ sendfile.c \
+ shutdown.c \
+ stat.c \
+ swapext.c \
+ sync.c \
+ sync_file_range.c \
+ truncate.c \
+ utimes.c
LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG) $(LIBPTHREAD) $(LIBUUID)
LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG)
diff --git a/io/exchrange.c b/io/exchrange.c
new file mode 100644
index 000000000..016429280
--- /dev/null
+++ b/io/exchrange.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "command.h"
+#include "input.h"
+#include "init.h"
+#include "io.h"
+#include "libfrog/logging.h"
+#include "libfrog/fsgeom.h"
+#include "libfrog/file_exchange.h"
+#include "libfrog/bulkstat.h"
+
+static void
+exchangerange_help(void)
+{
+ printf(_(
+"\n"
+" Exchange file data between the open file descriptor and the supplied filename.\n"
+" -C -- Print timing information in a condensed format\n"
+" -d N -- Start exchanging contents at this position in the open file\n"
+" -f -- Flush changed file data and metadata to disk\n"
+" -l N -- Exchange this many bytes between the two files instead of to EOF\n"
+" -n -- Dry run; do all the parameter validation but do not change anything.\n"
+" -s N -- Start exchanging contents at this position in the supplied file\n"
+" -t -- Print timing information\n"
+" -w -- Only exchange written ranges in the supplied file\n"
+));
+}
+
+static int
+exchangerange_f(
+ int argc,
+ char **argv)
+{
+ struct xfs_exchange_range fxr;
+ struct stat stat;
+ struct timeval t1, t2;
+ uint64_t flags = XFS_EXCHANGE_RANGE_TO_EOF;
+ int64_t src_offset = 0;
+ int64_t dest_offset = 0;
+ int64_t length = -1;
+ size_t fsblocksize, fssectsize;
+ int condensed = 0, quiet_flag = 1;
+ int c;
+ int fd;
+ int ret;
+
+ init_cvtnum(&fsblocksize, &fssectsize);
+ while ((c = getopt(argc, argv, "Ccd:fl:ns:tw")) != -1) {
+ switch (c) {
+ case 'C':
+ condensed = 1;
+ break;
+ case 'd':
+ dest_offset = cvtnum(fsblocksize, fssectsize, optarg);
+ if (dest_offset < 0) {
+ printf(
+ _("non-numeric open file offset argument -- %s\n"),
+ optarg);
+ return 0;
+ }
+ break;
+ case 'f':
+ flags |= XFS_EXCHANGE_RANGE_DSYNC;
+ break;
+ case 'l':
+ length = cvtnum(fsblocksize, fssectsize, optarg);
+ if (length < 0) {
+ printf(
+ _("non-numeric length argument -- %s\n"),
+ optarg);
+ return 0;
+ }
+ flags &= ~XFS_EXCHANGE_RANGE_TO_EOF;
+ break;
+ case 'n':
+ flags |= XFS_EXCHANGE_RANGE_DRY_RUN;
+ break;
+ case 's':
+ src_offset = cvtnum(fsblocksize, fssectsize, optarg);
+ if (src_offset < 0) {
+ printf(
+ _("non-numeric supplied file offset argument -- %s\n"),
+ optarg);
+ return 0;
+ }
+ break;
+ case 't':
+ quiet_flag = 0;
+ break;
+ case 'w':
+ flags |= XFS_EXCHANGE_RANGE_FILE1_WRITTEN;
+ break;
+ default:
+ exchangerange_help();
+ return 0;
+ }
+ }
+ if (optind != argc - 1) {
+ exchangerange_help();
+ return 0;
+ }
+
+ /* open the donor file */
+ fd = openfile(argv[optind], NULL, 0, 0, NULL);
+ if (fd < 0)
+ return 0;
+
+ ret = fstat(file->fd, &stat);
+ if (ret) {
+ perror("fstat");
+ exitcode = 1;
+ goto out;
+ }
+ if (length < 0)
+ length = stat.st_size;
+
+ xfrog_exchangerange_prep(&fxr, dest_offset, fd, src_offset, length);
+ ret = xfrog_exchangerange(file->fd, &fxr, flags);
+ if (ret) {
+ xfrog_perror(ret, "exchangerange");
+ exitcode = 1;
+ goto out;
+ }
+ if (quiet_flag)
+ goto out;
+
+ gettimeofday(&t2, NULL);
+ t2 = tsub(t2, t1);
+
+ report_io_times("exchangerange", &t2, dest_offset, length, length, 1,
+ condensed);
+out:
+ close(fd);
+ return 0;
+}
+
+static struct cmdinfo exchangerange_cmd = {
+ .name = "exchangerange",
+ .cfunc = exchangerange_f,
+ .argmin = 1,
+ .argmax = -1,
+ .flags = CMD_FLAG_ONESHOT | CMD_NOMAP_OK,
+ .help = exchangerange_help,
+};
+
+void
+exchangerange_init(void)
+{
+ exchangerange_cmd.args = _("[-Cfntw] [-d dest_offset] [-s src_offset] [-l length] <donorfile>");
+ exchangerange_cmd.oneline = _("Exchange contents between files.");
+
+ add_command(&exchangerange_cmd);
+}
diff --git a/io/init.c b/io/init.c
index 104cd2c12..37e0f093c 100644
--- a/io/init.c
+++ b/io/init.c
@@ -88,6 +88,7 @@ init_commands(void)
truncate_init();
utimes_init();
crc32cselftest_init();
+ exchangerange_init();
}
/*
diff --git a/io/io.h b/io/io.h
index e06dff53f..fdb715ff0 100644
--- a/io/io.h
+++ b/io/io.h
@@ -149,3 +149,4 @@ extern void scrub_init(void);
extern void repair_init(void);
extern void crc32cselftest_init(void);
extern void bulkstat_init(void);
+void exchangerange_init(void);
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 3ce280a75..2a7c67f7c 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -713,6 +713,46 @@ Swaps extent forks between files. The current open file is the target. The donor
file is specified by path. Note that file data is not copied (file content moves
with the fork(s)).
.TP
+.BI "exchangerange [OPTIONS]" donor_file "
+Exchanges contents between files.
+The current open file is the target.
+The donor file is specified by path.
+Note that file data is not copied (file content moves with the fork(s)).
+Options include:
+.RS 1.0i
+.PD 0
+.TP 0.4i
+.TP
+.B \-C
+Print timing information in a condensed format.
+.TP
+.BI \-d " dest_offset"
+Swap extents with open file beginning at
+.IR dest_offset .
+.TP
+.B \-f
+Flush changed file data and file metadata to disk.
+.TP
+.BI \-l " length"
+Swap up to
+.I length
+bytes of data.
+.TP
+.B \-n
+Perform all the parameter validation checks but don't change anything.
+.TP
+.BI \-s " src_offset"
+Swap extents with donor file beginning at
+.IR src_offset .
+.TP
+.B \-t
+Print timing information.
+.TP
+.B \-w
+Only exchange written ranges in the supplied file.
+.RE
+.PD
+.TP
.BI "set_encpolicy [ \-c " mode " ] [ \-n " mode " ] [ \-f " flags " ] [ \-s " log2_dusize " ] [ \-v " version " ] [ " keyspec " ]"
On filesystems that support encryption, assign an encryption policy to the
current file.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 10/12] libfrog: advertise exchange-range support
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
` (8 preceding siblings ...)
2024-07-30 0:56 ` [PATCH 09/12] xfs_io: create exchangerange command to test file range exchange ioctl Darrick J. Wong
@ 2024-07-30 0:56 ` Darrick J. Wong
2024-07-30 0:56 ` [PATCH 11/12] xfs_repair: add exchange-range to file systems Darrick J. Wong
2024-07-30 0:56 ` [PATCH 12/12] mkfs: add a formatting option for exchange-range Darrick J. Wong
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:56 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Report the presence of exchange range for a given filesystem.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libfrog/fsgeom.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libfrog/fsgeom.c b/libfrog/fsgeom.c
index 6980d3ffa..71a8e4bb9 100644
--- a/libfrog/fsgeom.c
+++ b/libfrog/fsgeom.c
@@ -31,6 +31,7 @@ xfs_report_geom(
int bigtime_enabled;
int inobtcount;
int nrext64;
+ int exchangerange;
isint = geo->logstart > 0;
lazycount = geo->flags & XFS_FSOP_GEOM_FLAGS_LAZYSB ? 1 : 0;
@@ -49,12 +50,14 @@ xfs_report_geom(
bigtime_enabled = geo->flags & XFS_FSOP_GEOM_FLAGS_BIGTIME ? 1 : 0;
inobtcount = geo->flags & XFS_FSOP_GEOM_FLAGS_INOBTCNT ? 1 : 0;
nrext64 = geo->flags & XFS_FSOP_GEOM_FLAGS_NREXT64 ? 1 : 0;
+ exchangerange = geo->flags & XFS_FSOP_GEOM_FLAGS_EXCHANGE_RANGE ? 1 : 0;
printf(_(
"meta-data=%-22s isize=%-6d agcount=%u, agsize=%u blks\n"
" =%-22s sectsz=%-5u attr=%u, projid32bit=%u\n"
" =%-22s crc=%-8u finobt=%u, sparse=%u, rmapbt=%u\n"
" =%-22s reflink=%-4u bigtime=%u inobtcount=%u nrext64=%u\n"
+" =%-22s exchange=%-3u\n"
"data =%-22s bsize=%-6u blocks=%llu, imaxpct=%u\n"
" =%-22s sunit=%-6u swidth=%u blks\n"
"naming =version %-14u bsize=%-6u ascii-ci=%d, ftype=%d\n"
@@ -65,6 +68,7 @@ xfs_report_geom(
"", geo->sectsize, attrversion, projid32bit,
"", crcs_enabled, finobt_enabled, spinodes, rmapbt_enabled,
"", reflink_enabled, bigtime_enabled, inobtcount, nrext64,
+ "", exchangerange,
"", geo->blocksize, (unsigned long long)geo->datablocks,
geo->imaxpct,
"", geo->sunit, geo->swidth,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 11/12] xfs_repair: add exchange-range to file systems
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
` (9 preceding siblings ...)
2024-07-30 0:56 ` [PATCH 10/12] libfrog: advertise exchange-range support Darrick J. Wong
@ 2024-07-30 0:56 ` Darrick J. Wong
2024-07-30 0:56 ` [PATCH 12/12] mkfs: add a formatting option for exchange-range Darrick J. Wong
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:56 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Enable upgrading existing filesystems to support the file exchange range
feature.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man8/xfs_admin.8 | 7 +++++++
repair/globals.c | 1 +
repair/globals.h | 1 +
repair/phase2.c | 30 ++++++++++++++++++++++++++++++
repair/xfs_repair.c | 11 +++++++++++
5 files changed, 50 insertions(+)
diff --git a/man/man8/xfs_admin.8 b/man/man8/xfs_admin.8
index 4794d6774..63f8ee903 100644
--- a/man/man8/xfs_admin.8
+++ b/man/man8/xfs_admin.8
@@ -156,6 +156,13 @@ data fork extent count will be 2^48 - 1, while the maximum attribute fork
extent count will be 2^32 - 1. The filesystem cannot be downgraded after this
feature is enabled. Once enabled, the filesystem will not be mountable by
older kernels. This feature was added to Linux 5.19.
+.TP 0.4i
+.B exchange
+Upgrade a filesystem to support atomic file content exchanges through the
+XFS_IOC_EXCHANGE_RANGE ioctl, and to support online repairs of directories,
+extended attributes, symbolic links, and realtime free space metadata.
+The filesystem cannot be downgraded after this feature is enabled.
+Once enabled, the filesystem will not be mountable by older kernels.
.RE
.TP
.BI \-U " uuid"
diff --git a/repair/globals.c b/repair/globals.c
index 24f720c46..c0c45df51 100644
--- a/repair/globals.c
+++ b/repair/globals.c
@@ -52,6 +52,7 @@ bool features_changed; /* did we change superblock feature bits? */
bool add_inobtcount; /* add inode btree counts to AGI */
bool add_bigtime; /* add support for timestamps up to 2486 */
bool add_nrext64;
+bool add_exchrange; /* add file content exchange support */
/* misc status variables */
diff --git a/repair/globals.h b/repair/globals.h
index b83a8ae65..1eadfdbf9 100644
--- a/repair/globals.h
+++ b/repair/globals.h
@@ -93,6 +93,7 @@ extern bool features_changed; /* did we change superblock feature bits? */
extern bool add_inobtcount; /* add inode btree counts to AGI */
extern bool add_bigtime; /* add support for timestamps up to 2486 */
extern bool add_nrext64;
+extern bool add_exchrange; /* add file content exchange support */
/* misc status variables */
diff --git a/repair/phase2.c b/repair/phase2.c
index 063748179..83f0c539b 100644
--- a/repair/phase2.c
+++ b/repair/phase2.c
@@ -182,6 +182,34 @@ set_nrext64(
return true;
}
+static bool
+set_exchrange(
+ struct xfs_mount *mp,
+ struct xfs_sb *new_sb)
+{
+ if (xfs_has_exchange_range(mp)) {
+ printf(_("Filesystem already supports exchange-range.\n"));
+ exit(0);
+ }
+
+ if (!xfs_has_crc(mp)) {
+ printf(
+ _("File exchange-range feature only supported on V5 filesystems.\n"));
+ exit(0);
+ }
+
+ if (!xfs_has_reflink(mp)) {
+ printf(
+ _("File exchange-range feature cannot be added without reflink.\n"));
+ exit(0);
+ }
+
+ printf(_("Adding file exchange-range support to filesystem.\n"));
+ new_sb->sb_features_ro_compat |= XFS_SB_FEAT_INCOMPAT_EXCHRANGE;
+ new_sb->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR;
+ return true;
+}
+
struct check_state {
struct xfs_sb sb;
uint64_t features;
@@ -290,6 +318,8 @@ upgrade_filesystem(
dirty |= set_bigtime(mp, &new_sb);
if (add_nrext64)
dirty |= set_nrext64(mp, &new_sb);
+ if (add_exchrange)
+ dirty |= set_exchrange(mp, &new_sb);
if (!dirty)
return;
diff --git a/repair/xfs_repair.c b/repair/xfs_repair.c
index 88aa75542..e325d61f1 100644
--- a/repair/xfs_repair.c
+++ b/repair/xfs_repair.c
@@ -69,6 +69,7 @@ enum c_opt_nums {
CONVERT_INOBTCOUNT,
CONVERT_BIGTIME,
CONVERT_NREXT64,
+ CONVERT_EXCHRANGE,
C_MAX_OPTS,
};
@@ -77,6 +78,7 @@ static char *c_opts[] = {
[CONVERT_INOBTCOUNT] = "inobtcount",
[CONVERT_BIGTIME] = "bigtime",
[CONVERT_NREXT64] = "nrext64",
+ [CONVERT_EXCHRANGE] = "exchange",
[C_MAX_OPTS] = NULL,
};
@@ -360,6 +362,15 @@ process_args(int argc, char **argv)
_("-c nrext64 only supports upgrades\n"));
add_nrext64 = true;
break;
+ case CONVERT_EXCHRANGE:
+ if (!val)
+ do_abort(
+ _("-c exchange requires a parameter\n"));
+ if (strtol(val, NULL, 0) != 1)
+ do_abort(
+ _("-c exchange only supports upgrades\n"));
+ add_exchrange = true;
+ break;
default:
unknown('c', val);
break;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 12/12] mkfs: add a formatting option for exchange-range
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
` (10 preceding siblings ...)
2024-07-30 0:56 ` [PATCH 11/12] xfs_repair: add exchange-range to file systems Darrick J. Wong
@ 2024-07-30 0:56 ` Darrick J. Wong
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:56 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Allow users to enable the logged file mapping exchange intent items on a
filesystem, which in turn enables XFS_IOC_EXCHANGE_RANGE and online
repair of metadata that lives in files, e.g. directories and xattrs.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man8/mkfs.xfs.8.in | 7 +++++++
mkfs/lts_4.19.conf | 1 +
mkfs/lts_5.10.conf | 1 +
mkfs/lts_5.15.conf | 1 +
mkfs/lts_5.4.conf | 1 +
mkfs/lts_6.1.conf | 1 +
mkfs/lts_6.6.conf | 1 +
mkfs/xfs_mkfs.c | 26 ++++++++++++++++++++++++--
8 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/man/man8/mkfs.xfs.8.in b/man/man8/mkfs.xfs.8.in
index 8060d342c..d5a0783ac 100644
--- a/man/man8/mkfs.xfs.8.in
+++ b/man/man8/mkfs.xfs.8.in
@@ -670,6 +670,13 @@ If the value is omitted, 1 is assumed.
This feature will be enabled when possible.
This feature is only available for filesystems formatted with -m crc=1.
.TP
+.BI exchange[= value]
+When enabled, application programs can exchange file contents atomically
+via the XFS_IOC_EXCHANGE_RANGE ioctl.
+Online repair uses this functionality to rebuild extended attributes,
+directories, symbolic links, and realtime metadata files.
+This feature is disabled by default.
+This feature is only available for filesystems formatted with -m crc=1.
.RE
.PP
.PD 0
diff --git a/mkfs/lts_4.19.conf b/mkfs/lts_4.19.conf
index 8b2bdd7a3..92e8eba6b 100644
--- a/mkfs/lts_4.19.conf
+++ b/mkfs/lts_4.19.conf
@@ -12,3 +12,4 @@ rmapbt=0
[inode]
sparse=1
nrext64=0
+exchange=0
diff --git a/mkfs/lts_5.10.conf b/mkfs/lts_5.10.conf
index 40189310a..34e7662cd 100644
--- a/mkfs/lts_5.10.conf
+++ b/mkfs/lts_5.10.conf
@@ -12,3 +12,4 @@ rmapbt=0
[inode]
sparse=1
nrext64=0
+exchange=0
diff --git a/mkfs/lts_5.15.conf b/mkfs/lts_5.15.conf
index aeecc0355..a36a5c2b7 100644
--- a/mkfs/lts_5.15.conf
+++ b/mkfs/lts_5.15.conf
@@ -12,3 +12,4 @@ rmapbt=0
[inode]
sparse=1
nrext64=0
+exchange=0
diff --git a/mkfs/lts_5.4.conf b/mkfs/lts_5.4.conf
index 0a40718b8..4204d5b8f 100644
--- a/mkfs/lts_5.4.conf
+++ b/mkfs/lts_5.4.conf
@@ -12,3 +12,4 @@ rmapbt=0
[inode]
sparse=1
nrext64=0
+exchange=0
diff --git a/mkfs/lts_6.1.conf b/mkfs/lts_6.1.conf
index 452abdf82..9a90def8f 100644
--- a/mkfs/lts_6.1.conf
+++ b/mkfs/lts_6.1.conf
@@ -12,3 +12,4 @@ rmapbt=0
[inode]
sparse=1
nrext64=0
+exchange=0
diff --git a/mkfs/lts_6.6.conf b/mkfs/lts_6.6.conf
index 244f8eaf7..3f7fb6519 100644
--- a/mkfs/lts_6.6.conf
+++ b/mkfs/lts_6.6.conf
@@ -12,3 +12,4 @@ rmapbt=1
[inode]
sparse=1
nrext64=1
+exchange=0
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 6d2469c3c..991ecbdd0 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -90,6 +90,7 @@ enum {
I_PROJID32BIT,
I_SPINODES,
I_NREXT64,
+ I_EXCHANGE,
I_MAX_OPTS,
};
@@ -469,6 +470,7 @@ static struct opt_params iopts = {
[I_PROJID32BIT] = "projid32bit",
[I_SPINODES] = "sparse",
[I_NREXT64] = "nrext64",
+ [I_EXCHANGE] = "exchange",
[I_MAX_OPTS] = NULL,
},
.subopt_params = {
@@ -523,7 +525,13 @@ static struct opt_params iopts = {
.minval = 0,
.maxval = 1,
.defaultval = 1,
- }
+ },
+ { .index = I_EXCHANGE,
+ .conflicts = { { NULL, LAST_CONFLICT } },
+ .minval = 0,
+ .maxval = 1,
+ .defaultval = 1,
+ },
},
};
@@ -889,6 +897,7 @@ struct sb_feat_args {
bool nodalign;
bool nortalign;
bool nrext64;
+ bool exchrange; /* XFS_SB_FEAT_INCOMPAT_EXCHRANGE */
};
struct cli_params {
@@ -1024,7 +1033,8 @@ usage( void )
sectsize=num,concurrency=num]\n\
/* force overwrite */ [-f]\n\
/* inode size */ [-i perblock=n|size=num,maxpct=n,attr=0|1|2,\n\
- projid32bit=0|1,sparse=0|1,nrext64=0|1]\n\
+ projid32bit=0|1,sparse=0|1,nrext64=0|1,\n\
+ exchange=0|1]\n\
/* no discard */ [-K]\n\
/* log subvol */ [-l agnum=n,internal,size=num,logdev=xxx,version=n\n\
sunit=value|su=num,sectsize=num,lazy-count=0|1,\n\
@@ -1722,6 +1732,9 @@ inode_opts_parser(
case I_NREXT64:
cli->sb_feat.nrext64 = getnum(value, opts, subopt);
break;
+ case I_EXCHANGE:
+ cli->sb_feat.exchrange = getnum(value, opts, subopt);
+ break;
default:
return -EINVAL;
}
@@ -2365,6 +2378,13 @@ _("64 bit extent count not supported without CRC support\n"));
usage();
}
cli->sb_feat.nrext64 = false;
+
+ if (cli->sb_feat.exchrange && cli_opt_set(&iopts, I_EXCHANGE)) {
+ fprintf(stderr,
+_("exchange-range not supported without CRC support\n"));
+ usage();
+ }
+ cli->sb_feat.exchrange = false;
}
if (!cli->sb_feat.finobt) {
@@ -3498,6 +3518,8 @@ sb_set_features(
if (fp->nrext64)
sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_NREXT64;
+ if (fp->exchrange)
+ sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_EXCHRANGE;
}
/*
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/1] xfs_{db,repair}: add an explicit owner field to xfs_da_args
2024-07-30 0:17 ` [PATCHSET v30.9 04/23] xfsprogs: set and validate dir/attr block owners Darrick J. Wong
@ 2024-07-30 0:57 ` Darrick J. Wong
0 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:57 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Update these two utilities to set the owner field of the da args
structure prior to calling directory and extended attribute functions.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/namei.c | 1 +
repair/phase6.c | 3 +++
2 files changed, 4 insertions(+)
diff --git a/db/namei.c b/db/namei.c
index 6de062161..41ccaa04b 100644
--- a/db/namei.c
+++ b/db/namei.c
@@ -447,6 +447,7 @@ listdir(
struct xfs_da_args args = {
.dp = dp,
.geo = dp->i_mount->m_dir_geo,
+ .owner = dp->i_ino,
};
int error;
diff --git a/repair/phase6.c b/repair/phase6.c
index 1e985e7db..92a58db0d 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -1401,6 +1401,7 @@ dir2_kill_block(
args.trans = tp;
args.whichfork = XFS_DATA_FORK;
args.geo = mp->m_dir_geo;
+ args.owner = ip->i_ino;
if (da_bno >= mp->m_dir_geo->leafblk && da_bno < mp->m_dir_geo->freeblk)
error = -libxfs_da_shrink_inode(&args, da_bno, bp);
else
@@ -1505,6 +1506,7 @@ longform_dir2_entry_check_data(
struct xfs_da_args da = {
.dp = ip,
.geo = mp->m_dir_geo,
+ .owner = ip->i_ino,
};
@@ -2294,6 +2296,7 @@ longform_dir2_entry_check(
/* is this a block, leaf, or node directory? */
args.dp = ip;
args.geo = mp->m_dir_geo;
+ args.owner = ip->i_ino;
fmt = libxfs_dir2_format(&args, &error);
/* check directory "data" blocks (ie. name/inode pairs) */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/2] libxfs: port the bumplink function from the kernel
2024-07-30 0:17 ` [PATCHSET v30.9 05/23] xfsprogs: inode-related repair fixes Darrick J. Wong
@ 2024-07-30 0:57 ` Darrick J. Wong
2024-07-30 0:57 ` [PATCH 2/2] mkfs/repair: pin inodes that would otherwise overflow link count Darrick J. Wong
1 sibling, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:57 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Port the xfs_bumplink function from the kernel and use it to replace raw
calls to inc_nlink. The next patch will need this common function to
prevent integer overflows in the link count.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
include/xfs_inode.h | 2 ++
libxfs/util.c | 17 +++++++++++++++++
mkfs/proto.c | 4 ++--
repair/phase6.c | 10 +++++-----
4 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/include/xfs_inode.h b/include/xfs_inode.h
index c6e4f84bd..45339b426 100644
--- a/include/xfs_inode.h
+++ b/include/xfs_inode.h
@@ -359,6 +359,8 @@ extern void libxfs_trans_ichgtime(struct xfs_trans *,
struct xfs_inode *, int);
extern int libxfs_iflush_int (struct xfs_inode *, struct xfs_buf *);
+void libxfs_bumplink(struct xfs_trans *tp, struct xfs_inode *ip);
+
/* Inode Cache Interfaces */
extern int libxfs_iget(struct xfs_mount *, struct xfs_trans *, xfs_ino_t,
uint, struct xfs_inode **);
diff --git a/libxfs/util.c b/libxfs/util.c
index 2656c99a8..dc54e3ee6 100644
--- a/libxfs/util.c
+++ b/libxfs/util.c
@@ -240,6 +240,23 @@ xfs_inode_propagate_flags(
ip->i_diflags |= di_flags;
}
+/*
+ * Increment the link count on an inode & log the change.
+ */
+void
+libxfs_bumplink(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip)
+{
+ struct inode *inode = VFS_I(ip);
+
+ xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
+
+ inc_nlink(inode);
+
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+}
+
/*
* Initialise a newly allocated inode and return the in-core inode to the
* caller locked exclusively.
diff --git a/mkfs/proto.c b/mkfs/proto.c
index 5125ee44f..a9a9b704a 100644
--- a/mkfs/proto.c
+++ b/mkfs/proto.c
@@ -591,7 +591,7 @@ parseproto(
&creds, fsxp, &ip);
if (error)
fail(_("Inode allocation failed"), error);
- inc_nlink(VFS_I(ip)); /* account for . */
+ libxfs_bumplink(tp, ip); /* account for . */
if (!pip) {
pip = ip;
mp->m_sb.sb_rootino = ip->i_ino;
@@ -601,7 +601,7 @@ parseproto(
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_DIR;
newdirent(mp, tp, pip, &xname, ip->i_ino);
- inc_nlink(VFS_I(pip));
+ libxfs_bumplink(tp, pip);
libxfs_trans_log_inode(tp, pip, XFS_ILOG_CORE);
}
newdirectory(mp, tp, ip, pip);
diff --git a/repair/phase6.c b/repair/phase6.c
index 92a58db0d..47dd9de27 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -947,7 +947,7 @@ mk_orphanage(xfs_mount_t *mp)
do_error(_("%s inode allocation failed %d\n"),
ORPHANAGE, error);
}
- inc_nlink(VFS_I(ip)); /* account for . */
+ libxfs_bumplink(tp, ip); /* account for . */
ino = ip->i_ino;
irec = find_inode_rec(mp,
@@ -999,7 +999,7 @@ mk_orphanage(xfs_mount_t *mp)
* for .. in the new directory, and update the irec copy of the
* on-disk nlink so we don't fail the link count check later.
*/
- inc_nlink(VFS_I(pip));
+ libxfs_bumplink(tp, pip);
irec = find_inode_rec(mp, XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino),
XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino));
add_inode_ref(irec, 0);
@@ -1094,7 +1094,7 @@ mv_orphanage(
if (irec)
add_inode_ref(irec, ino_offset);
else
- inc_nlink(VFS_I(orphanage_ip));
+ libxfs_bumplink(tp, orphanage_ip);
libxfs_trans_log_inode(tp, orphanage_ip, XFS_ILOG_CORE);
err = -libxfs_dir_createname(tp, ino_p, &xfs_name_dotdot,
@@ -1103,7 +1103,7 @@ mv_orphanage(
do_error(
_("creation of .. entry failed (%d)\n"), err);
- inc_nlink(VFS_I(ino_p));
+ libxfs_bumplink(tp, ino_p);
libxfs_trans_log_inode(tp, ino_p, XFS_ILOG_CORE);
err = -libxfs_trans_commit(tp);
if (err)
@@ -1128,7 +1128,7 @@ mv_orphanage(
if (irec)
add_inode_ref(irec, ino_offset);
else
- inc_nlink(VFS_I(orphanage_ip));
+ libxfs_bumplink(tp, orphanage_ip);
libxfs_trans_log_inode(tp, orphanage_ip, XFS_ILOG_CORE);
/*
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/2] mkfs/repair: pin inodes that would otherwise overflow link count
2024-07-30 0:17 ` [PATCHSET v30.9 05/23] xfsprogs: inode-related repair fixes Darrick J. Wong
2024-07-30 0:57 ` [PATCH 1/2] libxfs: port the bumplink function from the kernel Darrick J. Wong
@ 2024-07-30 0:57 ` Darrick J. Wong
1 sibling, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:57 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Update userspace utilities not to allow integer overflows of inode link
counts to result in a file that is referenced by parent directories but
has zero link count.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/util.c | 3 ++-
repair/incore_ino.c | 14 +++++++++-----
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/libxfs/util.c b/libxfs/util.c
index dc54e3ee6..74eea0fcb 100644
--- a/libxfs/util.c
+++ b/libxfs/util.c
@@ -252,7 +252,8 @@ libxfs_bumplink(
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
- inc_nlink(inode);
+ if (inode->i_nlink != XFS_NLINK_PINNED)
+ inc_nlink(inode);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
}
diff --git a/repair/incore_ino.c b/repair/incore_ino.c
index 0dd7a2f06..6618e534a 100644
--- a/repair/incore_ino.c
+++ b/repair/incore_ino.c
@@ -89,26 +89,30 @@ nlink_grow_16_to_32(ino_tree_node_t *irec)
void add_inode_ref(struct ino_tree_node *irec, int ino_offset)
{
+ union ino_nlink *c;
+
ASSERT(irec->ino_un.ex_data != NULL);
pthread_mutex_lock(&irec->lock);
+ c = &irec->ino_un.ex_data->counted_nlinks;
switch (irec->nlink_size) {
case sizeof(uint8_t):
- if (irec->ino_un.ex_data->counted_nlinks.un8[ino_offset] < 0xff) {
- irec->ino_un.ex_data->counted_nlinks.un8[ino_offset]++;
+ if (c->un8[ino_offset] < 0xff) {
+ c->un8[ino_offset]++;
break;
}
nlink_grow_8_to_16(irec);
/*FALLTHRU*/
case sizeof(uint16_t):
- if (irec->ino_un.ex_data->counted_nlinks.un16[ino_offset] < 0xffff) {
- irec->ino_un.ex_data->counted_nlinks.un16[ino_offset]++;
+ if (c->un16[ino_offset] < 0xffff) {
+ c->un16[ino_offset]++;
break;
}
nlink_grow_16_to_32(irec);
/*FALLTHRU*/
case sizeof(uint32_t):
- irec->ino_un.ex_data->counted_nlinks.un32[ino_offset]++;
+ if (c->un32[ino_offset] != XFS_NLINK_PINNED)
+ c->un32[ino_offset]++;
break;
default:
ASSERT(0);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/5] xfs_scrub: remove ALP_* flags namespace
2024-07-30 0:18 ` [PATCHSET v30.9 06/23] xfs_scrub: fixes to the repair code Darrick J. Wong
@ 2024-07-30 0:57 ` Darrick J. Wong
2024-07-30 0:58 ` [PATCH 2/5] xfs_scrub: move repair functions to repair.c Darrick J. Wong
` (3 subsequent siblings)
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:57 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
In preparation to move all the repair code to repair.[ch], remove the
ALP_* flags namespace since it mostly overlaps with XRM_*. Rename the
clunky "COMPLAIN_IF_UNFIXED" flag to "FINAL_WARNING", because that's
what it really means.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase3.c | 2 +-
scrub/phase4.c | 2 +-
scrub/phase5.c | 2 +-
scrub/phase7.c | 2 +-
scrub/repair.c | 4 ++--
scrub/repair.h | 16 ++++++++++++----
scrub/scrub.c | 10 +++++-----
scrub/scrub.h | 10 ----------
8 files changed, 23 insertions(+), 25 deletions(-)
diff --git a/scrub/phase3.c b/scrub/phase3.c
index 4235c228c..9a26b9203 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -88,7 +88,7 @@ try_inode_repair(
return 0;
ret = action_list_process(ictx->ctx, fd, alist,
- ALP_REPAIR_ONLY | ALP_NOPROGRESS);
+ XRM_REPAIR_ONLY | XRM_NOPROGRESS);
if (ret)
return ret;
diff --git a/scrub/phase4.c b/scrub/phase4.c
index 8807f147a..d42e67637 100644
--- a/scrub/phase4.c
+++ b/scrub/phase4.c
@@ -54,7 +54,7 @@ repair_ag(
} while (unfixed > 0);
/* Try once more, but this time complain if we can't fix things. */
- flags |= ALP_COMPLAIN_IF_UNFIXED;
+ flags |= XRM_FINAL_WARNING;
ret = action_list_process(ctx, -1, alist, flags);
if (ret)
*aborted = true;
diff --git a/scrub/phase5.c b/scrub/phase5.c
index b4c635d34..940e434c3 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -422,7 +422,7 @@ fs_scan_worker(
}
ret = action_list_process(ctx, ctx->mnt.fd, &item->alist,
- ALP_COMPLAIN_IF_UNFIXED | ALP_NOPROGRESS);
+ XRM_FINAL_WARNING | XRM_NOPROGRESS);
if (ret) {
str_liberror(ctx, ret, _("repairing fs scan metadata"));
*item->abortedp = true;
diff --git a/scrub/phase7.c b/scrub/phase7.c
index 93a074f11..820a68f99 100644
--- a/scrub/phase7.c
+++ b/scrub/phase7.c
@@ -122,7 +122,7 @@ phase7_func(
if (error)
return error;
error = action_list_process(ctx, -1, &alist,
- ALP_COMPLAIN_IF_UNFIXED | ALP_NOPROGRESS);
+ XRM_FINAL_WARNING | XRM_NOPROGRESS);
if (error)
return error;
diff --git a/scrub/repair.c b/scrub/repair.c
index 9ade805e1..61d62ab6b 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -274,7 +274,7 @@ action_list_process(
fix = xfs_repair_metadata(ctx, xfdp, aitem, repair_flags);
switch (fix) {
case CHECK_DONE:
- if (!(repair_flags & ALP_NOPROGRESS))
+ if (!(repair_flags & XRM_NOPROGRESS))
progress_add(1);
alist->nr--;
list_del(&aitem->list);
@@ -316,7 +316,7 @@ action_list_process_or_defer(
int ret;
ret = action_list_process(ctx, -1, alist,
- ALP_REPAIR_ONLY | ALP_NOPROGRESS);
+ XRM_REPAIR_ONLY | XRM_NOPROGRESS);
if (ret)
return ret;
diff --git a/scrub/repair.h b/scrub/repair.h
index aa3ea1361..6b6f64691 100644
--- a/scrub/repair.h
+++ b/scrub/repair.h
@@ -32,10 +32,18 @@ void action_list_find_mustfix(struct action_list *actions,
unsigned long long *broken_primaries,
unsigned long long *broken_secondaries);
-/* Passed through to xfs_repair_metadata() */
-#define ALP_REPAIR_ONLY (XRM_REPAIR_ONLY)
-#define ALP_COMPLAIN_IF_UNFIXED (XRM_COMPLAIN_IF_UNFIXED)
-#define ALP_NOPROGRESS (1U << 31)
+/*
+ * Only ask the kernel to repair this object if the kernel directly told us it
+ * was corrupt. Objects that are only flagged as having cross-referencing
+ * errors or flagged as eligible for optimization are left for later.
+ */
+#define XRM_REPAIR_ONLY (1U << 0)
+
+/* This is the last repair attempt; complain if still broken even after fix. */
+#define XRM_FINAL_WARNING (1U << 1)
+
+/* Don't call progress_add after repairing an item. */
+#define XRM_NOPROGRESS (1U << 2)
int action_list_process(struct scrub_ctx *ctx, int fd,
struct action_list *alist, unsigned int repair_flags);
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 7cb94af3d..f4b152a1c 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -743,7 +743,7 @@ _("Filesystem is shut down, aborting."));
* could fix this, it's at least worth trying the scan
* again to see if another repair fixed it.
*/
- if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
+ if (!(repair_flags & XRM_FINAL_WARNING))
return CHECK_RETRY;
fallthrough;
case EINVAL:
@@ -773,13 +773,13 @@ _("Read-only filesystem; cannot make changes."));
* to requeue the repair for later and don't say a
* thing. Otherwise, print error and bail out.
*/
- if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
+ if (!(repair_flags & XRM_FINAL_WARNING))
return CHECK_RETRY;
str_liberror(ctx, error, descr_render(&dsc));
return CHECK_DONE;
}
- if (repair_flags & XRM_COMPLAIN_IF_UNFIXED)
+ if (repair_flags & XRM_FINAL_WARNING)
scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
if (needs_repair(&meta)) {
/*
@@ -787,7 +787,7 @@ _("Read-only filesystem; cannot make changes."));
* just requeue this and try again later. Otherwise we
* log the error loudly and don't try again.
*/
- if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
+ if (!(repair_flags & XRM_FINAL_WARNING))
return CHECK_RETRY;
str_corrupt(ctx, descr_render(&dsc),
_("Repair unsuccessful; offline repair required."));
@@ -799,7 +799,7 @@ _("Repair unsuccessful; offline repair required."));
* caller to run xfs_repair; otherwise, we'll keep trying to
* reverify the cross-referencing as repairs progress.
*/
- if (repair_flags & XRM_COMPLAIN_IF_UNFIXED) {
+ if (repair_flags & XRM_FINAL_WARNING) {
str_info(ctx, descr_render(&dsc),
_("Seems correct but cross-referencing failed; offline repair recommended."));
} else {
diff --git a/scrub/scrub.h b/scrub/scrub.h
index cb33ddb46..5359548b0 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -54,16 +54,6 @@ struct action_item {
__u32 agno;
};
-/*
- * Only ask the kernel to repair this object if the kernel directly told us it
- * was corrupt. Objects that are only flagged as having cross-referencing
- * errors or flagged as eligible for optimization are left for later.
- */
-#define XRM_REPAIR_ONLY (1U << 0)
-
-/* Complain if still broken even after fix. */
-#define XRM_COMPLAIN_IF_UNFIXED (1U << 1)
-
enum check_outcome xfs_repair_metadata(struct scrub_ctx *ctx,
struct xfs_fd *xfdp, struct action_item *aitem,
unsigned int repair_flags);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/5] xfs_scrub: move repair functions to repair.c
2024-07-30 0:18 ` [PATCHSET v30.9 06/23] xfs_scrub: fixes to the repair code Darrick J. Wong
2024-07-30 0:57 ` [PATCH 1/5] xfs_scrub: remove ALP_* flags namespace Darrick J. Wong
@ 2024-07-30 0:58 ` Darrick J. Wong
2024-07-30 0:58 ` [PATCH 3/5] xfs_scrub: log when a repair was unnecessary Darrick J. Wong
` (2 subsequent siblings)
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:58 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Move all the repair functions to repair.c.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase1.c | 2
scrub/repair.c | 169 +++++++++++++++++++++++++++++++++++++++++
scrub/scrub.c | 204 +------------------------------------------------
scrub/scrub.h | 6 -
scrub/scrub_private.h | 55 +++++++++++++
5 files changed, 230 insertions(+), 206 deletions(-)
create mode 100644 scrub/scrub_private.h
diff --git a/scrub/phase1.c b/scrub/phase1.c
index 96138e03e..81b0918a1 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -210,7 +210,7 @@ _("Kernel metadata scrubbing facility is not available."));
}
/* Do we need kernel-assisted metadata repair? */
- if (ctx->mode != SCRUB_MODE_DRY_RUN && !xfs_can_repair(ctx)) {
+ if (ctx->mode != SCRUB_MODE_DRY_RUN && !can_repair(ctx)) {
str_error(ctx, ctx->mntpoint,
_("Kernel metadata repair facility is not available. Use -n to scrub."));
return ECANCELED;
diff --git a/scrub/repair.c b/scrub/repair.c
index 61d62ab6b..54bd09575 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -10,11 +10,180 @@
#include <sys/statvfs.h>
#include "list.h"
#include "libfrog/paths.h"
+#include "libfrog/fsgeom.h"
+#include "libfrog/scrub.h"
#include "xfs_scrub.h"
#include "common.h"
#include "scrub.h"
#include "progress.h"
#include "repair.h"
+#include "descr.h"
+#include "scrub_private.h"
+
+/* General repair routines. */
+
+/* Repair some metadata. */
+static enum check_outcome
+xfs_repair_metadata(
+ struct scrub_ctx *ctx,
+ struct xfs_fd *xfdp,
+ struct action_item *aitem,
+ unsigned int repair_flags)
+{
+ struct xfs_scrub_metadata meta = { 0 };
+ struct xfs_scrub_metadata oldm;
+ DEFINE_DESCR(dsc, ctx, format_scrub_descr);
+ int error;
+
+ assert(aitem->type < XFS_SCRUB_TYPE_NR);
+ assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
+ meta.sm_type = aitem->type;
+ meta.sm_flags = aitem->flags | XFS_SCRUB_IFLAG_REPAIR;
+ if (use_force_rebuild)
+ meta.sm_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
+ switch (xfrog_scrubbers[aitem->type].group) {
+ case XFROG_SCRUB_GROUP_AGHEADER:
+ case XFROG_SCRUB_GROUP_PERAG:
+ meta.sm_agno = aitem->agno;
+ break;
+ case XFROG_SCRUB_GROUP_INODE:
+ meta.sm_ino = aitem->ino;
+ meta.sm_gen = aitem->gen;
+ break;
+ default:
+ break;
+ }
+
+ if (!is_corrupt(&meta) && (repair_flags & XRM_REPAIR_ONLY))
+ return CHECK_RETRY;
+
+ memcpy(&oldm, &meta, sizeof(oldm));
+ descr_set(&dsc, &oldm);
+
+ if (needs_repair(&meta))
+ str_info(ctx, descr_render(&dsc), _("Attempting repair."));
+ else if (debug || verbose)
+ str_info(ctx, descr_render(&dsc),
+ _("Attempting optimization."));
+
+ error = -xfrog_scrub_metadata(xfdp, &meta);
+ switch (error) {
+ case 0:
+ /* No operational errors encountered. */
+ break;
+ case EDEADLOCK:
+ case EBUSY:
+ /* Filesystem is busy, try again later. */
+ if (debug || verbose)
+ str_info(ctx, descr_render(&dsc),
+_("Filesystem is busy, deferring repair."));
+ return CHECK_RETRY;
+ case ESHUTDOWN:
+ /* Filesystem is already shut down, abort. */
+ str_error(ctx, descr_render(&dsc),
+_("Filesystem is shut down, aborting."));
+ return CHECK_ABORT;
+ case ENOTTY:
+ case EOPNOTSUPP:
+ /*
+ * If the kernel cannot perform the optimization that we
+ * requested; or we forced a repair but the kernel doesn't know
+ * how to perform the repair, don't requeue the request. Mark
+ * it done and move on.
+ */
+ if (is_unoptimized(&oldm) ||
+ debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
+ return CHECK_DONE;
+ /*
+ * If we're in no-complain mode, requeue the check for
+ * later. It's possible that an error in another
+ * component caused us to flag an error in this
+ * component. Even if the kernel didn't think it
+ * could fix this, it's at least worth trying the scan
+ * again to see if another repair fixed it.
+ */
+ if (!(repair_flags & XRM_FINAL_WARNING))
+ return CHECK_RETRY;
+ fallthrough;
+ case EINVAL:
+ /* Kernel doesn't know how to repair this? */
+ str_corrupt(ctx, descr_render(&dsc),
+_("Don't know how to fix; offline repair required."));
+ return CHECK_DONE;
+ case EROFS:
+ /* Read-only filesystem, can't fix. */
+ if (verbose || debug || needs_repair(&oldm))
+ str_error(ctx, descr_render(&dsc),
+_("Read-only filesystem; cannot make changes."));
+ return CHECK_ABORT;
+ case ENOENT:
+ /* Metadata not present, just skip it. */
+ return CHECK_DONE;
+ case ENOMEM:
+ case ENOSPC:
+ /* Don't care if preen fails due to low resources. */
+ if (is_unoptimized(&oldm) && !needs_repair(&oldm))
+ return CHECK_DONE;
+ fallthrough;
+ default:
+ /*
+ * Operational error. If the caller doesn't want us
+ * to complain about repair failures, tell the caller
+ * to requeue the repair for later and don't say a
+ * thing. Otherwise, print error and bail out.
+ */
+ if (!(repair_flags & XRM_FINAL_WARNING))
+ return CHECK_RETRY;
+ str_liberror(ctx, error, descr_render(&dsc));
+ return CHECK_DONE;
+ }
+
+ if (repair_flags & XRM_FINAL_WARNING)
+ scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
+ if (needs_repair(&meta)) {
+ /*
+ * Still broken; if we've been told not to complain then we
+ * just requeue this and try again later. Otherwise we
+ * log the error loudly and don't try again.
+ */
+ if (!(repair_flags & XRM_FINAL_WARNING))
+ return CHECK_RETRY;
+ str_corrupt(ctx, descr_render(&dsc),
+_("Repair unsuccessful; offline repair required."));
+ } else if (xref_failed(&meta)) {
+ /*
+ * This metadata object itself looks ok, but we still noticed
+ * inconsistencies when comparing it with the other filesystem
+ * metadata. If we're in "final warning" mode, advise the
+ * caller to run xfs_repair; otherwise, we'll keep trying to
+ * reverify the cross-referencing as repairs progress.
+ */
+ if (repair_flags & XRM_FINAL_WARNING) {
+ str_info(ctx, descr_render(&dsc),
+ _("Seems correct but cross-referencing failed; offline repair recommended."));
+ } else {
+ if (verbose)
+ str_info(ctx, descr_render(&dsc),
+ _("Seems correct but cross-referencing failed; will keep checking."));
+ return CHECK_RETRY;
+ }
+ } else {
+ /* Clean operation, no corruption detected. */
+ if (is_corrupt(&oldm))
+ record_repair(ctx, descr_render(&dsc),
+ _("Repairs successful."));
+ else if (xref_disagrees(&oldm))
+ record_repair(ctx, descr_render(&dsc),
+ _("Repairs successful after discrepancy in cross-referencing."));
+ else if (xref_failed(&oldm))
+ record_repair(ctx, descr_render(&dsc),
+ _("Repairs successful after cross-referencing failure."));
+ else
+ record_preen(ctx, descr_render(&dsc),
+ _("Optimization successful."));
+ }
+ return CHECK_DONE;
+}
/*
* Prioritize action items in order of how long we can wait.
diff --git a/scrub/scrub.c b/scrub/scrub.c
index f4b152a1c..595839130 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -20,11 +20,12 @@
#include "scrub.h"
#include "repair.h"
#include "descr.h"
+#include "scrub_private.h"
/* Online scrub and repair wrappers. */
/* Format a scrub description. */
-static int
+int
format_scrub_descr(
struct scrub_ctx *ctx,
char *buf,
@@ -52,46 +53,8 @@ format_scrub_descr(
return -1;
}
-/* Predicates for scrub flag state. */
-
-static inline bool is_corrupt(struct xfs_scrub_metadata *sm)
-{
- return sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT;
-}
-
-static inline bool is_unoptimized(struct xfs_scrub_metadata *sm)
-{
- return sm->sm_flags & XFS_SCRUB_OFLAG_PREEN;
-}
-
-static inline bool xref_failed(struct xfs_scrub_metadata *sm)
-{
- return sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL;
-}
-
-static inline bool xref_disagrees(struct xfs_scrub_metadata *sm)
-{
- return sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT;
-}
-
-static inline bool is_incomplete(struct xfs_scrub_metadata *sm)
-{
- return sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE;
-}
-
-static inline bool is_suspicious(struct xfs_scrub_metadata *sm)
-{
- return sm->sm_flags & XFS_SCRUB_OFLAG_WARNING;
-}
-
-/* Should we fix it? */
-static inline bool needs_repair(struct xfs_scrub_metadata *sm)
-{
- return is_corrupt(sm) || xref_disagrees(sm);
-}
-
/* Warn about strange circumstances after scrub. */
-static inline void
+void
scrub_warn_incomplete_scrub(
struct scrub_ctx *ctx,
struct descr *dsc,
@@ -647,7 +610,7 @@ can_scrub_parent(
}
bool
-xfs_can_repair(
+can_repair(
struct scrub_ctx *ctx)
{
return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, XFS_SCRUB_IFLAG_REPAIR);
@@ -660,162 +623,3 @@ can_force_rebuild(
return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE,
XFS_SCRUB_IFLAG_REPAIR | XFS_SCRUB_IFLAG_FORCE_REBUILD);
}
-
-/* General repair routines. */
-
-/* Repair some metadata. */
-enum check_outcome
-xfs_repair_metadata(
- struct scrub_ctx *ctx,
- struct xfs_fd *xfdp,
- struct action_item *aitem,
- unsigned int repair_flags)
-{
- struct xfs_scrub_metadata meta = { 0 };
- struct xfs_scrub_metadata oldm;
- DEFINE_DESCR(dsc, ctx, format_scrub_descr);
- int error;
-
- assert(aitem->type < XFS_SCRUB_TYPE_NR);
- assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
- meta.sm_type = aitem->type;
- meta.sm_flags = aitem->flags | XFS_SCRUB_IFLAG_REPAIR;
- if (use_force_rebuild)
- meta.sm_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
- switch (xfrog_scrubbers[aitem->type].group) {
- case XFROG_SCRUB_GROUP_AGHEADER:
- case XFROG_SCRUB_GROUP_PERAG:
- meta.sm_agno = aitem->agno;
- break;
- case XFROG_SCRUB_GROUP_INODE:
- meta.sm_ino = aitem->ino;
- meta.sm_gen = aitem->gen;
- break;
- default:
- break;
- }
-
- if (!is_corrupt(&meta) && (repair_flags & XRM_REPAIR_ONLY))
- return CHECK_RETRY;
-
- memcpy(&oldm, &meta, sizeof(oldm));
- descr_set(&dsc, &oldm);
-
- if (needs_repair(&meta))
- str_info(ctx, descr_render(&dsc), _("Attempting repair."));
- else if (debug || verbose)
- str_info(ctx, descr_render(&dsc),
- _("Attempting optimization."));
-
- error = -xfrog_scrub_metadata(xfdp, &meta);
- switch (error) {
- case 0:
- /* No operational errors encountered. */
- break;
- case EDEADLOCK:
- case EBUSY:
- /* Filesystem is busy, try again later. */
- if (debug || verbose)
- str_info(ctx, descr_render(&dsc),
-_("Filesystem is busy, deferring repair."));
- return CHECK_RETRY;
- case ESHUTDOWN:
- /* Filesystem is already shut down, abort. */
- str_error(ctx, descr_render(&dsc),
-_("Filesystem is shut down, aborting."));
- return CHECK_ABORT;
- case ENOTTY:
- case EOPNOTSUPP:
- /*
- * If the kernel cannot perform the optimization that we
- * requested; or we forced a repair but the kernel doesn't know
- * how to perform the repair, don't requeue the request. Mark
- * it done and move on.
- */
- if (is_unoptimized(&oldm) ||
- debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
- return CHECK_DONE;
- /*
- * If we're in no-complain mode, requeue the check for
- * later. It's possible that an error in another
- * component caused us to flag an error in this
- * component. Even if the kernel didn't think it
- * could fix this, it's at least worth trying the scan
- * again to see if another repair fixed it.
- */
- if (!(repair_flags & XRM_FINAL_WARNING))
- return CHECK_RETRY;
- fallthrough;
- case EINVAL:
- /* Kernel doesn't know how to repair this? */
- str_corrupt(ctx, descr_render(&dsc),
-_("Don't know how to fix; offline repair required."));
- return CHECK_DONE;
- case EROFS:
- /* Read-only filesystem, can't fix. */
- if (verbose || debug || needs_repair(&oldm))
- str_error(ctx, descr_render(&dsc),
-_("Read-only filesystem; cannot make changes."));
- return CHECK_ABORT;
- case ENOENT:
- /* Metadata not present, just skip it. */
- return CHECK_DONE;
- case ENOMEM:
- case ENOSPC:
- /* Don't care if preen fails due to low resources. */
- if (is_unoptimized(&oldm) && !needs_repair(&oldm))
- return CHECK_DONE;
- fallthrough;
- default:
- /*
- * Operational error. If the caller doesn't want us
- * to complain about repair failures, tell the caller
- * to requeue the repair for later and don't say a
- * thing. Otherwise, print error and bail out.
- */
- if (!(repair_flags & XRM_FINAL_WARNING))
- return CHECK_RETRY;
- str_liberror(ctx, error, descr_render(&dsc));
- return CHECK_DONE;
- }
-
- if (repair_flags & XRM_FINAL_WARNING)
- scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
- if (needs_repair(&meta)) {
- /*
- * Still broken; if we've been told not to complain then we
- * just requeue this and try again later. Otherwise we
- * log the error loudly and don't try again.
- */
- if (!(repair_flags & XRM_FINAL_WARNING))
- return CHECK_RETRY;
- str_corrupt(ctx, descr_render(&dsc),
-_("Repair unsuccessful; offline repair required."));
- } else if (xref_failed(&meta)) {
- /*
- * This metadata object itself looks ok, but we still noticed
- * inconsistencies when comparing it with the other filesystem
- * metadata. If we're in "final warning" mode, advise the
- * caller to run xfs_repair; otherwise, we'll keep trying to
- * reverify the cross-referencing as repairs progress.
- */
- if (repair_flags & XRM_FINAL_WARNING) {
- str_info(ctx, descr_render(&dsc),
- _("Seems correct but cross-referencing failed; offline repair recommended."));
- } else {
- if (verbose)
- str_info(ctx, descr_render(&dsc),
- _("Seems correct but cross-referencing failed; will keep checking."));
- return CHECK_RETRY;
- }
- } else {
- /* Clean operation, no corruption detected. */
- if (needs_repair(&oldm))
- record_repair(ctx, descr_render(&dsc),
- _("Repairs successful."));
- else
- record_preen(ctx, descr_render(&dsc),
- _("Optimization successful."));
- }
- return CHECK_DONE;
-}
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 5359548b0..133445e8d 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -38,7 +38,7 @@ bool can_scrub_dir(struct scrub_ctx *ctx);
bool can_scrub_attr(struct scrub_ctx *ctx);
bool can_scrub_symlink(struct scrub_ctx *ctx);
bool can_scrub_parent(struct scrub_ctx *ctx);
-bool xfs_can_repair(struct scrub_ctx *ctx);
+bool can_repair(struct scrub_ctx *ctx);
bool can_force_rebuild(struct scrub_ctx *ctx);
int scrub_file(struct scrub_ctx *ctx, int fd, const struct xfs_bulkstat *bstat,
@@ -54,8 +54,4 @@ struct action_item {
__u32 agno;
};
-enum check_outcome xfs_repair_metadata(struct scrub_ctx *ctx,
- struct xfs_fd *xfdp, struct action_item *aitem,
- unsigned int repair_flags);
-
#endif /* XFS_SCRUB_SCRUB_H_ */
diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h
new file mode 100644
index 000000000..a24d485a2
--- /dev/null
+++ b/scrub/scrub_private.h
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef XFS_SCRUB_SCRUB_PRIVATE_H_
+#define XFS_SCRUB_SCRUB_PRIVATE_H_
+
+/* Shared code between scrub.c and repair.c. */
+
+int format_scrub_descr(struct scrub_ctx *ctx, char *buf, size_t buflen,
+ void *where);
+
+/* Predicates for scrub flag state. */
+
+static inline bool is_corrupt(struct xfs_scrub_metadata *sm)
+{
+ return sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT;
+}
+
+static inline bool is_unoptimized(struct xfs_scrub_metadata *sm)
+{
+ return sm->sm_flags & XFS_SCRUB_OFLAG_PREEN;
+}
+
+static inline bool xref_failed(struct xfs_scrub_metadata *sm)
+{
+ return sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL;
+}
+
+static inline bool xref_disagrees(struct xfs_scrub_metadata *sm)
+{
+ return sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT;
+}
+
+static inline bool is_incomplete(struct xfs_scrub_metadata *sm)
+{
+ return sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE;
+}
+
+static inline bool is_suspicious(struct xfs_scrub_metadata *sm)
+{
+ return sm->sm_flags & XFS_SCRUB_OFLAG_WARNING;
+}
+
+/* Should we fix it? */
+static inline bool needs_repair(struct xfs_scrub_metadata *sm)
+{
+ return is_corrupt(sm) || xref_disagrees(sm);
+}
+
+void scrub_warn_incomplete_scrub(struct scrub_ctx *ctx, struct descr *dsc,
+ struct xfs_scrub_metadata *meta);
+
+#endif /* XFS_SCRUB_SCRUB_PRIVATE_H_ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/5] xfs_scrub: log when a repair was unnecessary
2024-07-30 0:18 ` [PATCHSET v30.9 06/23] xfs_scrub: fixes to the repair code Darrick J. Wong
2024-07-30 0:57 ` [PATCH 1/5] xfs_scrub: remove ALP_* flags namespace Darrick J. Wong
2024-07-30 0:58 ` [PATCH 2/5] xfs_scrub: move repair functions to repair.c Darrick J. Wong
@ 2024-07-30 0:58 ` Darrick J. Wong
2024-07-30 0:58 ` [PATCH 4/5] xfs_scrub: require primary superblock repairs to complete before proceeding Darrick J. Wong
2024-07-30 0:58 ` [PATCH 5/5] xfs_scrub: actually try to fix summary counters ahead of repairs Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:58 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If the kernel tells us that a filesystem object didn't need repairs, we
should log that with a message specific to that outcome.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/scrub/repair.c b/scrub/repair.c
index 54bd09575..50f168d24 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -167,6 +167,10 @@ _("Repair unsuccessful; offline repair required."));
_("Seems correct but cross-referencing failed; will keep checking."));
return CHECK_RETRY;
}
+ } else if (meta.sm_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) {
+ if (verbose)
+ str_info(ctx, descr_render(&dsc),
+ _("No modification needed."));
} else {
/* Clean operation, no corruption detected. */
if (is_corrupt(&oldm))
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/5] xfs_scrub: require primary superblock repairs to complete before proceeding
2024-07-30 0:18 ` [PATCHSET v30.9 06/23] xfs_scrub: fixes to the repair code Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 0:58 ` [PATCH 3/5] xfs_scrub: log when a repair was unnecessary Darrick J. Wong
@ 2024-07-30 0:58 ` Darrick J. Wong
2024-07-30 0:58 ` [PATCH 5/5] xfs_scrub: actually try to fix summary counters ahead of repairs Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:58 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Phase 2 of the xfs_scrub program calls the kernel to check the primary
superblock before scanning the rest of the filesystem. Though doing so
is a no-op now (since the primary super must pass all checks as a
prerequisite for mounting), the goal of this code is to enable future
kernel code to intercept an xfs_scrub run before it actually does
anything. If this some day involves fixing the primary superblock, it
seems reasonable to require that /all/ repairs complete successfully
before moving on to the rest of the filesystem.
Unfortunately, that's not what xfs_scrub does now -- primary super
repairs that fail are theoretically deferred to phase 4! So make this
mandatory.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase2.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 80c77b287..2d49c604e 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -174,7 +174,8 @@ phase2_func(
ret = scrub_primary_super(ctx, &alist);
if (ret)
goto out_wq;
- ret = action_list_process_or_defer(ctx, 0, &alist);
+ ret = action_list_process(ctx, -1, &alist,
+ XRM_FINAL_WARNING | XRM_NOPROGRESS);
if (ret)
goto out_wq;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/5] xfs_scrub: actually try to fix summary counters ahead of repairs
2024-07-30 0:18 ` [PATCHSET v30.9 06/23] xfs_scrub: fixes to the repair code Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 0:58 ` [PATCH 4/5] xfs_scrub: require primary superblock repairs to complete before proceeding Darrick J. Wong
@ 2024-07-30 0:58 ` Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:58 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
A while ago, I decided to make phase 4 check the summary counters before
it starts any other repairs, having observed that repairs of primary
metadata can fail because the summary counters (incorrectly) claim that
there aren't enough free resources in the filesystem. However, if
problems are found in the summary counters, the repair work will be run
as part of the AG 0 repairs, which means that it runs concurrently with
other scrubbers. This doesn't quite get us to the intended goal, so try
to fix the scrubbers ahead of time. If that fails, tough, we'll get
back to it in phase 7 if scrub gets that far.
Fixes: cbaf1c9d91a0 ("xfs_scrub: check summary counters")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase4.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/scrub/phase4.c b/scrub/phase4.c
index d42e67637..0c67abf64 100644
--- a/scrub/phase4.c
+++ b/scrub/phase4.c
@@ -129,6 +129,7 @@ phase4_func(
struct scrub_ctx *ctx)
{
struct xfs_fsop_geom fsgeom;
+ struct action_list alist;
int ret;
if (!have_action_items(ctx))
@@ -136,11 +137,13 @@ phase4_func(
/*
* Check the summary counters early. Normally we do this during phase
- * seven, but some of the cross-referencing requires fairly-accurate
- * counters, so counter repairs have to be put on the list now so that
- * they get fixed before we stop retrying unfixed metadata repairs.
+ * seven, but some of the cross-referencing requires fairly accurate
+ * summary counters. Check and try to repair them now to minimize the
+ * chance that repairs of primary metadata fail due to secondary
+ * metadata. If repairs fails, we'll come back during phase 7.
*/
- ret = scrub_fs_counters(ctx, &ctx->action_lists[0]);
+ action_list_init(&alist);
+ ret = scrub_fs_counters(ctx, &alist);
if (ret)
return ret;
@@ -155,11 +158,18 @@ phase4_func(
return ret;
if (fsgeom.sick & XFS_FSOP_GEOM_SICK_QUOTACHECK) {
- ret = scrub_quotacheck(ctx, &ctx->action_lists[0]);
+ ret = scrub_quotacheck(ctx, &alist);
if (ret)
return ret;
}
+ /* Repair counters before starting on the rest. */
+ ret = action_list_process(ctx, -1, &alist,
+ XRM_REPAIR_ONLY | XRM_NOPROGRESS);
+ if (ret)
+ return ret;
+ action_list_discard(&alist);
+
ret = repair_everything(ctx);
if (ret)
return ret;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/8] xfs_scrub: fix missing scrub coverage for broken inodes
2024-07-30 0:18 ` [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs Darrick J. Wong
@ 2024-07-30 0:59 ` Darrick J. Wong
2024-07-30 0:59 ` [PATCH 2/8] xfs_scrub: collapse trivial superblock scrub helpers Darrick J. Wong
` (6 subsequent siblings)
7 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:59 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If INUMBERS says that an inode is allocated, but BULKSTAT skips over the
inode and BULKSTAT_SINGLE errors out when loading the inumber, there are
two possibilities: One, we're racing with ifree; or two, the inode is
corrupt and iget failed.
When this happens, the scrub_scan_all_inodes code will insert a dummy
bulkstat record with all fields zeroed except bs_ino and bs_blksize.
Hence the use of i_mode switches in phase3 to schedule file content
scrubbing are not entirely correct -- bs_mode==0 means "type unknown",
which ought to mean "schedule all scrubbers".
Unfortunately, the current code doesn't do that, so instead we schedule
no content scrubs. If the broken file was actually a directory, we fail
to check the directory contents for further corruptions.
Found by using fuzzing with xfs/385 and core.format = 0.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase3.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/scrub/phase3.c b/scrub/phase3.c
index 9a26b9203..b03b55250 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -166,16 +166,29 @@ scrub_inode(
if (error)
goto out;
- if (S_ISLNK(bstat->bs_mode)) {
+ /*
+ * Check file data contents, e.g. symlink and directory entries.
+ *
+ * Note: bs_mode==0 occurs when inumbers says an inode is allocated,
+ * bulkstat skips the inode, and bulkstat_single errors out when
+ * loading the inode. This could be due to racing with ifree, but it
+ * could be a corrupt inode. Either way, schedule all the data fork
+ * content scrubbers. Better to have them return -ENOENT than miss
+ * some coverage.
+ */
+ if (S_ISLNK(bstat->bs_mode) || !bstat->bs_mode) {
/* Check symlink contents. */
error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_SYMLINK,
&alist);
- } else if (S_ISDIR(bstat->bs_mode)) {
+ if (error)
+ goto out;
+ }
+ if (S_ISDIR(bstat->bs_mode) || !bstat->bs_mode) {
/* Check the directory entries. */
error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_DIR, &alist);
+ if (error)
+ goto out;
}
- if (error)
- goto out;
/* Check all the extended attributes. */
error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_XATTR, &alist);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/8] xfs_scrub: collapse trivial superblock scrub helpers
2024-07-30 0:18 ` [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs Darrick J. Wong
2024-07-30 0:59 ` [PATCH 1/8] xfs_scrub: fix missing scrub coverage for broken inodes Darrick J. Wong
@ 2024-07-30 0:59 ` Darrick J. Wong
2024-07-30 0:59 ` [PATCH 3/8] xfs_scrub: get rid of trivial fs metadata scanner helpers Darrick J. Wong
` (5 subsequent siblings)
7 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:59 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Remove the trivial primary super scrub helper function since it makes
tracing code paths difficult and will become annoying in the patches
that follow.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase2.c | 9 +++++----
scrub/scrub.c | 16 +---------------
scrub/scrub.h | 3 ++-
3 files changed, 8 insertions(+), 20 deletions(-)
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 2d49c604e..ec72bb5b7 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -166,12 +166,13 @@ phase2_func(
}
/*
- * In case we ever use the primary super scrubber to perform fs
- * upgrades (followed by a full scrub), do that before we launch
- * anything else.
+ * Scrub primary superblock. This will be useful if we ever need to
+ * hook a filesystem-wide pre-scrub activity (e.g. enable filesystem
+ * upgrades) off of the sb 0 scrubber (which currently does nothing).
+ * If errors occur, this function will log them and return nonzero.
*/
action_list_init(&alist);
- ret = scrub_primary_super(ctx, &alist);
+ ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_SB, 0, &alist);
if (ret)
goto out_wq;
ret = action_list_process(ctx, -1, &alist,
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 595839130..c2e56e5f1 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -259,7 +259,7 @@ scrub_save_repair(
* Returns 0 for success. If errors occur, this function will log them and
* return a positive error code.
*/
-static int
+int
scrub_meta_type(
struct scrub_ctx *ctx,
unsigned int type,
@@ -325,20 +325,6 @@ scrub_group(
return 0;
}
-/*
- * Scrub primary superblock. This will be useful if we ever need to hook
- * a filesystem-wide pre-scrub activity off of the sb 0 scrubber (which
- * currently does nothing). If errors occur, this function will log them and
- * return nonzero.
- */
-int
-scrub_primary_super(
- struct scrub_ctx *ctx,
- struct action_list *alist)
-{
- return scrub_meta_type(ctx, XFS_SCRUB_TYPE_SB, 0, alist);
-}
-
/* Scrub each AG's header blocks. */
int
scrub_ag_headers(
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 133445e8d..fef8a5960 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -17,7 +17,6 @@ enum check_outcome {
struct action_item;
void scrub_report_preen_triggers(struct scrub_ctx *ctx);
-int scrub_primary_super(struct scrub_ctx *ctx, struct action_list *alist);
int scrub_ag_headers(struct scrub_ctx *ctx, xfs_agnumber_t agno,
struct action_list *alist);
int scrub_ag_metadata(struct scrub_ctx *ctx, xfs_agnumber_t agno,
@@ -30,6 +29,8 @@ int scrub_fs_counters(struct scrub_ctx *ctx, struct action_list *alist);
int scrub_quotacheck(struct scrub_ctx *ctx, struct action_list *alist);
int scrub_nlinks(struct scrub_ctx *ctx, struct action_list *alist);
int scrub_clean_health(struct scrub_ctx *ctx, struct action_list *alist);
+int scrub_meta_type(struct scrub_ctx *ctx, unsigned int type,
+ xfs_agnumber_t agno, struct action_list *alist);
bool can_scrub_fs_metadata(struct scrub_ctx *ctx);
bool can_scrub_inode(struct scrub_ctx *ctx);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/8] xfs_scrub: get rid of trivial fs metadata scanner helpers
2024-07-30 0:18 ` [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs Darrick J. Wong
2024-07-30 0:59 ` [PATCH 1/8] xfs_scrub: fix missing scrub coverage for broken inodes Darrick J. Wong
2024-07-30 0:59 ` [PATCH 2/8] xfs_scrub: collapse trivial superblock scrub helpers Darrick J. Wong
@ 2024-07-30 0:59 ` Darrick J. Wong
2024-07-30 1:00 ` [PATCH 4/8] xfs_scrub: split up the mustfix repairs and difficulty assessment functions Darrick J. Wong
` (4 subsequent siblings)
7 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 0:59 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Get rid of these pointless wrappers.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase1.c | 2 +-
scrub/phase4.c | 9 +++++----
scrub/phase5.c | 15 +++++++--------
scrub/scrub.c | 36 ------------------------------------
scrub/scrub.h | 4 ----
5 files changed, 13 insertions(+), 53 deletions(-)
diff --git a/scrub/phase1.c b/scrub/phase1.c
index 81b0918a1..a61e154a8 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -61,7 +61,7 @@ report_to_kernel(
return 0;
action_list_init(&alist);
- ret = scrub_clean_health(ctx, &alist);
+ ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_HEALTHY, 0, &alist);
if (ret)
return ret;
diff --git a/scrub/phase4.c b/scrub/phase4.c
index 0c67abf64..d01dc89f4 100644
--- a/scrub/phase4.c
+++ b/scrub/phase4.c
@@ -136,14 +136,14 @@ phase4_func(
goto maybe_trim;
/*
- * Check the summary counters early. Normally we do this during phase
- * seven, but some of the cross-referencing requires fairly accurate
+ * Check the resource usage counters early. Normally we do this during
+ * phase 7, but some of the cross-referencing requires fairly accurate
* summary counters. Check and try to repair them now to minimize the
* chance that repairs of primary metadata fail due to secondary
* metadata. If repairs fails, we'll come back during phase 7.
*/
action_list_init(&alist);
- ret = scrub_fs_counters(ctx, &alist);
+ ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_FSCOUNTERS, 0, &alist);
if (ret)
return ret;
@@ -158,7 +158,8 @@ phase4_func(
return ret;
if (fsgeom.sick & XFS_FSOP_GEOM_SICK_QUOTACHECK) {
- ret = scrub_quotacheck(ctx, &alist);
+ ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_QUOTACHECK, 0,
+ &alist);
if (ret)
return ret;
}
diff --git a/scrub/phase5.c b/scrub/phase5.c
index 940e434c3..68d35cd58 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -384,12 +384,10 @@ check_fs_label(
return error;
}
-typedef int (*fs_scan_item_fn)(struct scrub_ctx *, struct action_list *);
-
struct fs_scan_item {
struct action_list alist;
bool *abortedp;
- fs_scan_item_fn scrub_fn;
+ unsigned int scrub_type;
};
/* Run one full-fs scan scrubber in this thread. */
@@ -414,7 +412,7 @@ fs_scan_worker(
nanosleep(&tv, NULL);
}
- ret = item->scrub_fn(ctx, &item->alist);
+ ret = scrub_meta_type(ctx, item->scrub_type, 0, &item->alist);
if (ret) {
str_liberror(ctx, ret, _("checking fs scan metadata"));
*item->abortedp = true;
@@ -440,7 +438,7 @@ queue_fs_scan(
struct workqueue *wq,
bool *abortedp,
xfs_agnumber_t nr,
- fs_scan_item_fn scrub_fn)
+ unsigned int scrub_type)
{
struct fs_scan_item *item;
struct scrub_ctx *ctx = wq->wq_ctx;
@@ -453,7 +451,7 @@ queue_fs_scan(
return ret;
}
action_list_init(&item->alist);
- item->scrub_fn = scrub_fn;
+ item->scrub_type = scrub_type;
item->abortedp = abortedp;
ret = -workqueue_add(wq, fs_scan_worker, nr, item);
@@ -485,14 +483,15 @@ run_kernel_fs_scan_scrubbers(
* The nlinks scanner is much faster than quotacheck because it only
* walks directories, so we start it first.
*/
- ret = queue_fs_scan(&wq_fs_scan, &aborted, nr, scrub_nlinks);
+ ret = queue_fs_scan(&wq_fs_scan, &aborted, nr, XFS_SCRUB_TYPE_NLINKS);
if (ret)
goto wait;
if (nr_threads > 1)
nr++;
- ret = queue_fs_scan(&wq_fs_scan, &aborted, nr, scrub_quotacheck);
+ ret = queue_fs_scan(&wq_fs_scan, &aborted, nr,
+ XFS_SCRUB_TYPE_QUOTACHECK);
if (ret)
goto wait;
diff --git a/scrub/scrub.c b/scrub/scrub.c
index c2e56e5f1..6e857c79d 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -366,42 +366,6 @@ scrub_summary_metadata(
return scrub_group(ctx, XFROG_SCRUB_GROUP_SUMMARY, 0, alist);
}
-/* Scrub /only/ the superblock summary counters. */
-int
-scrub_fs_counters(
- struct scrub_ctx *ctx,
- struct action_list *alist)
-{
- return scrub_meta_type(ctx, XFS_SCRUB_TYPE_FSCOUNTERS, 0, alist);
-}
-
-/* Scrub /only/ the quota counters. */
-int
-scrub_quotacheck(
- struct scrub_ctx *ctx,
- struct action_list *alist)
-{
- return scrub_meta_type(ctx, XFS_SCRUB_TYPE_QUOTACHECK, 0, alist);
-}
-
-/* Scrub /only/ the file link counters. */
-int
-scrub_nlinks(
- struct scrub_ctx *ctx,
- struct action_list *alist)
-{
- return scrub_meta_type(ctx, XFS_SCRUB_TYPE_NLINKS, 0, alist);
-}
-
-/* Update incore health records if we were clean. */
-int
-scrub_clean_health(
- struct scrub_ctx *ctx,
- struct action_list *alist)
-{
- return scrub_meta_type(ctx, XFS_SCRUB_TYPE_HEALTHY, 0, alist);
-}
-
/* How many items do we have to check? */
unsigned int
scrub_estimate_ag_work(
diff --git a/scrub/scrub.h b/scrub/scrub.h
index fef8a5960..98819a25b 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -25,10 +25,6 @@ int scrub_fs_metadata(struct scrub_ctx *ctx, unsigned int scrub_type,
struct action_list *alist);
int scrub_iscan_metadata(struct scrub_ctx *ctx, struct action_list *alist);
int scrub_summary_metadata(struct scrub_ctx *ctx, struct action_list *alist);
-int scrub_fs_counters(struct scrub_ctx *ctx, struct action_list *alist);
-int scrub_quotacheck(struct scrub_ctx *ctx, struct action_list *alist);
-int scrub_nlinks(struct scrub_ctx *ctx, struct action_list *alist);
-int scrub_clean_health(struct scrub_ctx *ctx, struct action_list *alist);
int scrub_meta_type(struct scrub_ctx *ctx, unsigned int type,
xfs_agnumber_t agno, struct action_list *alist);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/8] xfs_scrub: split up the mustfix repairs and difficulty assessment functions
2024-07-30 0:18 ` [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 0:59 ` [PATCH 3/8] xfs_scrub: get rid of trivial fs metadata scanner helpers Darrick J. Wong
@ 2024-07-30 1:00 ` Darrick J. Wong
2024-07-30 1:00 ` [PATCH 5/8] xfs_scrub: add missing repair types to the mustfix and difficulty assessment Darrick J. Wong
` (3 subsequent siblings)
7 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:00 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Currently, action_list_find_mustfix does two things -- it figures out
which repairs must be tried during phase 2 to enable the inode scan in
phase 3; and it figures out if xfs_scrub should warn about secondary and
primary metadata corruption that might make repair difficult.
Split these into separate functions to make each more coherent. A long
time from now we'll need this to enable warnings about difficult rt
repairs, but for now this is merely a code cleanup.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase2.c | 15 +++++++--------
scrub/repair.c | 38 +++++++++++++++++++++++++++-----------
scrub/repair.h | 10 +++++++---
3 files changed, 41 insertions(+), 22 deletions(-)
diff --git a/scrub/phase2.c b/scrub/phase2.c
index ec72bb5b7..4c0d20a8e 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -42,9 +42,8 @@ scan_ag_metadata(
struct scan_ctl *sctl = arg;
struct action_list alist;
struct action_list immediate_alist;
- unsigned long long broken_primaries;
- unsigned long long broken_secondaries;
char descr[DESCR_BUFSZ];
+ unsigned int difficulty;
int ret;
if (sctl->aborted)
@@ -79,12 +78,12 @@ scan_ag_metadata(
* the inobt from rmapbt data, but if the rmapbt is broken even
* at this early phase then we are sunk.
*/
- broken_secondaries = 0;
- broken_primaries = 0;
- action_list_find_mustfix(&alist, &immediate_alist,
- &broken_primaries, &broken_secondaries);
- if (broken_secondaries && !debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) {
- if (broken_primaries)
+ difficulty = action_list_difficulty(&alist);
+ action_list_find_mustfix(&alist, &immediate_alist);
+
+ if ((difficulty & REPAIR_DIFFICULTY_SECONDARY) &&
+ !debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) {
+ if (difficulty & REPAIR_DIFFICULTY_PRIMARY)
str_info(ctx, descr,
_("Corrupt primary and secondary block mapping metadata."));
else
diff --git a/scrub/repair.c b/scrub/repair.c
index 50f168d24..8ee9102ab 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -290,9 +290,7 @@ xfs_action_item_compare(
void
action_list_find_mustfix(
struct action_list *alist,
- struct action_list *immediate_alist,
- unsigned long long *broken_primaries,
- unsigned long long *broken_secondaries)
+ struct action_list *immediate_alist)
{
struct action_item *n;
struct action_item *aitem;
@@ -301,25 +299,43 @@ action_list_find_mustfix(
if (!(aitem->flags & XFS_SCRUB_OFLAG_CORRUPT))
continue;
switch (aitem->type) {
- case XFS_SCRUB_TYPE_RMAPBT:
- (*broken_secondaries)++;
- break;
case XFS_SCRUB_TYPE_FINOBT:
case XFS_SCRUB_TYPE_INOBT:
alist->nr--;
list_move_tail(&aitem->list, &immediate_alist->list);
immediate_alist->nr++;
- fallthrough;
+ break;
+ }
+ }
+}
+
+/* Determine if primary or secondary metadata are inconsistent. */
+unsigned int
+action_list_difficulty(
+ const struct action_list *alist)
+{
+ struct action_item *aitem, *n;
+ unsigned int ret = 0;
+
+ list_for_each_entry_safe(aitem, n, &alist->list, list) {
+ if (!(aitem->flags & XFS_SCRUB_OFLAG_CORRUPT))
+ continue;
+
+ switch (aitem->type) {
+ case XFS_SCRUB_TYPE_RMAPBT:
+ ret |= REPAIR_DIFFICULTY_SECONDARY;
+ break;
+ case XFS_SCRUB_TYPE_FINOBT:
+ case XFS_SCRUB_TYPE_INOBT:
case XFS_SCRUB_TYPE_BNOBT:
case XFS_SCRUB_TYPE_CNTBT:
case XFS_SCRUB_TYPE_REFCNTBT:
- (*broken_primaries)++;
- break;
- default:
- abort();
+ ret |= REPAIR_DIFFICULTY_PRIMARY;
break;
}
}
+
+ return ret;
}
/*
diff --git a/scrub/repair.h b/scrub/repair.h
index 6b6f64691..b61bd29c8 100644
--- a/scrub/repair.h
+++ b/scrub/repair.h
@@ -28,9 +28,13 @@ void action_list_discard(struct action_list *alist);
void action_list_splice(struct action_list *dest, struct action_list *src);
void action_list_find_mustfix(struct action_list *actions,
- struct action_list *immediate_alist,
- unsigned long long *broken_primaries,
- unsigned long long *broken_secondaries);
+ struct action_list *immediate_alist);
+
+/* Primary metadata is corrupt */
+#define REPAIR_DIFFICULTY_PRIMARY (1U << 0)
+/* Secondary metadata is corrupt */
+#define REPAIR_DIFFICULTY_SECONDARY (1U << 1)
+unsigned int action_list_difficulty(const struct action_list *actions);
/*
* Only ask the kernel to repair this object if the kernel directly told us it
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/8] xfs_scrub: add missing repair types to the mustfix and difficulty assessment
2024-07-30 0:18 ` [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:00 ` [PATCH 4/8] xfs_scrub: split up the mustfix repairs and difficulty assessment functions Darrick J. Wong
@ 2024-07-30 1:00 ` Darrick J. Wong
2024-07-30 1:00 ` [PATCH 6/8] xfs_scrub: any inconsistency in metadata should trigger difficulty warnings Darrick J. Wong
` (2 subsequent siblings)
7 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:00 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add a few scrub types that ought to trigger a mustfix (such as AGI
corruption) and all the AG space metadata to the repair difficulty
assessment.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/scrub/repair.c b/scrub/repair.c
index 8ee9102ab..33a803110 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -299,6 +299,7 @@ action_list_find_mustfix(
if (!(aitem->flags & XFS_SCRUB_OFLAG_CORRUPT))
continue;
switch (aitem->type) {
+ case XFS_SCRUB_TYPE_AGI:
case XFS_SCRUB_TYPE_FINOBT:
case XFS_SCRUB_TYPE_INOBT:
alist->nr--;
@@ -325,11 +326,17 @@ action_list_difficulty(
case XFS_SCRUB_TYPE_RMAPBT:
ret |= REPAIR_DIFFICULTY_SECONDARY;
break;
+ case XFS_SCRUB_TYPE_SB:
+ case XFS_SCRUB_TYPE_AGF:
+ case XFS_SCRUB_TYPE_AGFL:
+ case XFS_SCRUB_TYPE_AGI:
case XFS_SCRUB_TYPE_FINOBT:
case XFS_SCRUB_TYPE_INOBT:
case XFS_SCRUB_TYPE_BNOBT:
case XFS_SCRUB_TYPE_CNTBT:
case XFS_SCRUB_TYPE_REFCNTBT:
+ case XFS_SCRUB_TYPE_RTBITMAP:
+ case XFS_SCRUB_TYPE_RTSUM:
ret |= REPAIR_DIFFICULTY_PRIMARY;
break;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 6/8] xfs_scrub: any inconsistency in metadata should trigger difficulty warnings
2024-07-30 0:18 ` [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 1:00 ` [PATCH 5/8] xfs_scrub: add missing repair types to the mustfix and difficulty assessment Darrick J. Wong
@ 2024-07-30 1:00 ` Darrick J. Wong
2024-07-30 1:00 ` [PATCH 7/8] xfs_scrub: warn about difficult repairs to rt and quota metadata Darrick J. Wong
2024-07-30 1:01 ` [PATCH 8/8] xfs_scrub: enable users to bump information messages to warnings Darrick J. Wong
7 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:00 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Any inconsistency in the space metadata can be a sign that repairs will
be difficult, so set off the warning if there were cross referencing
problems too.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/scrub/repair.c b/scrub/repair.c
index 33a803110..30817d268 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -319,7 +319,9 @@ action_list_difficulty(
unsigned int ret = 0;
list_for_each_entry_safe(aitem, n, &alist->list, list) {
- if (!(aitem->flags & XFS_SCRUB_OFLAG_CORRUPT))
+ if (!(aitem->flags & (XFS_SCRUB_OFLAG_CORRUPT |
+ XFS_SCRUB_OFLAG_XCORRUPT |
+ XFS_SCRUB_OFLAG_XFAIL)))
continue;
switch (aitem->type) {
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 7/8] xfs_scrub: warn about difficult repairs to rt and quota metadata
2024-07-30 0:18 ` [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs Darrick J. Wong
` (5 preceding siblings ...)
2024-07-30 1:00 ` [PATCH 6/8] xfs_scrub: any inconsistency in metadata should trigger difficulty warnings Darrick J. Wong
@ 2024-07-30 1:00 ` Darrick J. Wong
2024-07-30 1:01 ` [PATCH 8/8] xfs_scrub: enable users to bump information messages to warnings Darrick J. Wong
7 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:00 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Warn the user if there are problems with the rt or quota metadata that
might make repairs difficult. For now there aren't any corruption
conditions that would trigger this, but we don't want to leave a gap.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase2.c | 37 +++++++++++++++++++++++++------------
1 file changed, 25 insertions(+), 12 deletions(-)
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 4c0d20a8e..3e88c969b 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -31,6 +31,25 @@ struct scan_ctl {
bool aborted;
};
+/* Warn about the types of mutual inconsistencies that may make repairs hard. */
+static inline void
+warn_repair_difficulties(
+ struct scrub_ctx *ctx,
+ unsigned int difficulty,
+ const char *descr)
+{
+ if (!(difficulty & REPAIR_DIFFICULTY_SECONDARY))
+ return;
+ if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
+ return;
+
+ if (difficulty & REPAIR_DIFFICULTY_PRIMARY)
+ str_info(ctx, descr, _("Corrupt primary and secondary metadata."));
+ else
+ str_info(ctx, descr, _("Corrupt secondary metadata."));
+ str_info(ctx, descr, _("Filesystem might not be repairable."));
+}
+
/* Scrub each AG's metadata btrees. */
static void
scan_ag_metadata(
@@ -80,18 +99,7 @@ scan_ag_metadata(
*/
difficulty = action_list_difficulty(&alist);
action_list_find_mustfix(&alist, &immediate_alist);
-
- if ((difficulty & REPAIR_DIFFICULTY_SECONDARY) &&
- !debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) {
- if (difficulty & REPAIR_DIFFICULTY_PRIMARY)
- str_info(ctx, descr,
-_("Corrupt primary and secondary block mapping metadata."));
- else
- str_info(ctx, descr,
-_("Corrupt secondary block mapping metadata."));
- str_info(ctx, descr,
-_("Filesystem might not be repairable."));
- }
+ warn_repair_difficulties(ctx, difficulty, descr);
/* Repair (inode) btree damage. */
ret = action_list_process_or_defer(ctx, agno, &immediate_alist);
@@ -115,6 +123,7 @@ scan_fs_metadata(
struct action_list alist;
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
struct scan_ctl *sctl = arg;
+ unsigned int difficulty;
int ret;
if (sctl->aborted)
@@ -127,6 +136,10 @@ scan_fs_metadata(
goto out;
}
+ /* Complain about metadata corruptions that might not be fixable. */
+ difficulty = action_list_difficulty(&alist);
+ warn_repair_difficulties(ctx, difficulty, xfrog_scrubbers[type].descr);
+
action_list_defer(ctx, 0, &alist);
out:
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 8/8] xfs_scrub: enable users to bump information messages to warnings
2024-07-30 0:18 ` [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs Darrick J. Wong
` (6 preceding siblings ...)
2024-07-30 1:00 ` [PATCH 7/8] xfs_scrub: warn about difficult repairs to rt and quota metadata Darrick J. Wong
@ 2024-07-30 1:01 ` Darrick J. Wong
7 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:01 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add a -o iwarn option that enables users to specify that informational
messages (such as incomplete scans, or confusing names) should be
treated as warnings.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man8/xfs_scrub.8 | 19 +++++++++++++++++++
scrub/common.c | 2 ++
scrub/xfs_scrub.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
scrub/xfs_scrub.h | 1 +
4 files changed, 66 insertions(+), 1 deletion(-)
diff --git a/man/man8/xfs_scrub.8 b/man/man8/xfs_scrub.8
index e881ae76a..404baba69 100644
--- a/man/man8/xfs_scrub.8
+++ b/man/man8/xfs_scrub.8
@@ -85,6 +85,25 @@ Search this file for mounted filesystems instead of /etc/mtab.
.B \-n
Only check filesystem metadata.
Do not repair or optimize anything.
+.HP
+.B \-o
+.I subopt\c
+[\c
+.B =\c
+.IR value ]
+.BR
+Override what the program might conclude about the filesystem
+if left to its own devices.
+.IP
+The
+.IR subopt ions
+supported are:
+.RS 1.0i
+.TP
+.BI iwarn
+Treat informational messages as warnings.
+This will result in a nonzero return code, and a higher logging level.
+.RE
.TP
.BI \-T
Print timing and memory usage information for each phase.
diff --git a/scrub/common.c b/scrub/common.c
index 283ac84e2..aca596487 100644
--- a/scrub/common.c
+++ b/scrub/common.c
@@ -110,6 +110,8 @@ __str_out(
/* print strerror or format of choice but not both */
assert(!(error && format));
+ if (level == S_INFO && info_is_warning)
+ level = S_WARN;
if (level >= S_INFO)
stream = stdout;
diff --git a/scrub/xfs_scrub.c b/scrub/xfs_scrub.c
index 50565857d..e49538ca1 100644
--- a/scrub/xfs_scrub.c
+++ b/scrub/xfs_scrub.c
@@ -160,6 +160,9 @@ bool is_service;
/* Set to true if the kernel supports XFS_SCRUB_IFLAG_FORCE_REBUILD */
bool use_force_rebuild;
+/* Should we count informational messages as warnings? */
+bool info_is_warning;
+
#define SCRUB_RET_SUCCESS (0) /* no problems left behind */
#define SCRUB_RET_CORRUPT (1) /* corruption remains on fs */
#define SCRUB_RET_UNOPTIMIZED (2) /* fs could be optimized */
@@ -604,6 +607,43 @@ report_outcome(
# define XFS_SCRUB_HAVE_UNICODE "-"
#endif
+/*
+ * -o: user-supplied override options
+ */
+enum o_opt_nums {
+ IWARN = 0,
+ O_MAX_OPTS,
+};
+
+static char *o_opts[] = {
+ [IWARN] = "iwarn",
+ [O_MAX_OPTS] = NULL,
+};
+
+static void
+parse_o_opts(
+ struct scrub_ctx *ctx,
+ char *p)
+{
+ while (*p != '\0') {
+ char *val;
+
+ switch (getsubopt(&p, o_opts, &val)) {
+ case IWARN:
+ if (val) {
+ fprintf(stderr,
+ _("iwarn does not take an argument\n"));
+ usage();
+ }
+ info_is_warning = true;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+}
+
int
main(
int argc,
@@ -637,7 +677,7 @@ main(
pthread_mutex_init(&ctx.lock, NULL);
ctx.mode = SCRUB_MODE_REPAIR;
ctx.error_action = ERRORS_CONTINUE;
- while ((c = getopt(argc, argv, "a:bC:de:km:nTvxV")) != EOF) {
+ while ((c = getopt(argc, argv, "a:bC:de:km:no:TvxV")) != EOF) {
switch (c) {
case 'a':
ctx.max_errors = cvt_u64(optarg, 10);
@@ -687,6 +727,9 @@ main(
case 'n':
ctx.mode = SCRUB_MODE_DRY_RUN;
break;
+ case 'o':
+ parse_o_opts(&ctx, optarg);
+ break;
case 'T':
display_rusage = true;
break;
diff --git a/scrub/xfs_scrub.h b/scrub/xfs_scrub.h
index 34d850d8d..1151ee9ff 100644
--- a/scrub/xfs_scrub.h
+++ b/scrub/xfs_scrub.h
@@ -22,6 +22,7 @@ extern bool stderr_isatty;
extern bool stdout_isatty;
extern bool is_service;
extern bool use_force_rebuild;
+extern bool info_is_warning;
enum scrub_mode {
SCRUB_MODE_DRY_RUN,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/9] xfs_scrub: track repair items by principal, not by individual repairs
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
@ 2024-07-30 1:01 ` Darrick J. Wong
2024-07-30 1:01 ` [PATCH 2/9] xfs_scrub: use repair_item to direct repair activities Darrick J. Wong
` (7 subsequent siblings)
8 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:01 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create a new structure to track scrub and repair state by principal
filesystem object (e.g. ag number or inode number/generation) so that we
can more easily examine and ensure that we satisfy repair order
dependencies. This transposition will eventually enable bulk scrub
operations and will also save a lot of memory if a given object needs a
lot of work.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase1.c | 4 ++
scrub/phase2.c | 14 ++++++--
scrub/phase3.c | 19 ++++++-----
scrub/phase4.c | 6 ++--
scrub/phase5.c | 5 ++-
scrub/phase7.c | 4 ++
scrub/scrub.c | 68 ++++++++++++++++++++++++++++++++--------
scrub/scrub.h | 83 +++++++++++++++++++++++++++++++++++++++++++++----
scrub/scrub_private.h | 19 +++++++++++
9 files changed, 185 insertions(+), 37 deletions(-)
diff --git a/scrub/phase1.c b/scrub/phase1.c
index a61e154a8..9920f29a6 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -52,6 +52,7 @@ static int
report_to_kernel(
struct scrub_ctx *ctx)
{
+ struct scrub_item sri;
struct action_list alist;
int ret;
@@ -60,8 +61,9 @@ report_to_kernel(
ctx->warnings_found)
return 0;
+ scrub_item_init_fs(&sri);
action_list_init(&alist);
- ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_HEALTHY, 0, &alist);
+ ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_HEALTHY, 0, &alist, &sri);
if (ret)
return ret;
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 3e88c969b..518923d66 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -57,6 +57,7 @@ scan_ag_metadata(
xfs_agnumber_t agno,
void *arg)
{
+ struct scrub_item sri;
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
struct scan_ctl *sctl = arg;
struct action_list alist;
@@ -68,6 +69,7 @@ scan_ag_metadata(
if (sctl->aborted)
return;
+ scrub_item_init_ag(&sri, agno);
action_list_init(&alist);
action_list_init(&immediate_alist);
snprintf(descr, DESCR_BUFSZ, _("AG %u"), agno);
@@ -76,7 +78,7 @@ scan_ag_metadata(
* First we scrub and fix the AG headers, because we need
* them to work well enough to check the AG btrees.
*/
- ret = scrub_ag_headers(ctx, agno, &alist);
+ ret = scrub_ag_headers(ctx, agno, &alist, &sri);
if (ret)
goto err;
@@ -86,7 +88,7 @@ scan_ag_metadata(
goto err;
/* Now scrub the AG btrees. */
- ret = scrub_ag_metadata(ctx, agno, &alist);
+ ret = scrub_ag_metadata(ctx, agno, &alist, &sri);
if (ret)
goto err;
@@ -120,6 +122,7 @@ scan_fs_metadata(
xfs_agnumber_t type,
void *arg)
{
+ struct scrub_item sri;
struct action_list alist;
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
struct scan_ctl *sctl = arg;
@@ -129,8 +132,9 @@ scan_fs_metadata(
if (sctl->aborted)
goto out;
+ scrub_item_init_fs(&sri);
action_list_init(&alist);
- ret = scrub_fs_metadata(ctx, type, &alist);
+ ret = scrub_fs_metadata(ctx, type, &alist, &sri);
if (ret) {
sctl->aborted = true;
goto out;
@@ -162,6 +166,7 @@ phase2_func(
.rbm_done = false,
};
struct action_list alist;
+ struct scrub_item sri;
const struct xfrog_scrub_descr *sc = xfrog_scrubbers;
xfs_agnumber_t agno;
unsigned int type;
@@ -183,8 +188,9 @@ phase2_func(
* upgrades) off of the sb 0 scrubber (which currently does nothing).
* If errors occur, this function will log them and return nonzero.
*/
+ scrub_item_init_ag(&sri, 0);
action_list_init(&alist);
- ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_SB, 0, &alist);
+ ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_SB, 0, &alist, &sri);
if (ret)
goto out_wq;
ret = action_list_process(ctx, -1, &alist,
diff --git a/scrub/phase3.c b/scrub/phase3.c
index b03b55250..642b8406e 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -105,12 +105,14 @@ scrub_inode(
void *arg)
{
struct action_list alist;
+ struct scrub_item sri;
struct scrub_inode_ctx *ictx = arg;
struct ptcounter *icount = ictx->icount;
xfs_agnumber_t agno;
int fd = -1;
int error;
+ scrub_item_init_file(&sri, bstat);
action_list_init(&alist);
agno = cvt_ino_to_agno(&ctx->mnt, bstat->bs_ino);
background_sleep();
@@ -143,7 +145,7 @@ scrub_inode(
fd = scrub_open_handle(handle);
/* Scrub the inode. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_INODE, &alist);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_INODE, &alist, &sri);
if (error)
goto out;
@@ -152,13 +154,13 @@ scrub_inode(
goto out;
/* Scrub all block mappings. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTD, &alist);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTD, &alist, &sri);
if (error)
goto out;
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTA, &alist);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTA, &alist, &sri);
if (error)
goto out;
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTC, &alist);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTC, &alist, &sri);
if (error)
goto out;
@@ -179,24 +181,25 @@ scrub_inode(
if (S_ISLNK(bstat->bs_mode) || !bstat->bs_mode) {
/* Check symlink contents. */
error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_SYMLINK,
- &alist);
+ &alist, &sri);
if (error)
goto out;
}
if (S_ISDIR(bstat->bs_mode) || !bstat->bs_mode) {
/* Check the directory entries. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_DIR, &alist);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_DIR, &alist,
+ &sri);
if (error)
goto out;
}
/* Check all the extended attributes. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_XATTR, &alist);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_XATTR, &alist, &sri);
if (error)
goto out;
/* Check parent pointers. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_PARENT, &alist);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_PARENT, &alist, &sri);
if (error)
goto out;
diff --git a/scrub/phase4.c b/scrub/phase4.c
index d01dc89f4..1c4aab996 100644
--- a/scrub/phase4.c
+++ b/scrub/phase4.c
@@ -130,6 +130,7 @@ phase4_func(
{
struct xfs_fsop_geom fsgeom;
struct action_list alist;
+ struct scrub_item sri;
int ret;
if (!have_action_items(ctx))
@@ -142,8 +143,9 @@ phase4_func(
* chance that repairs of primary metadata fail due to secondary
* metadata. If repairs fails, we'll come back during phase 7.
*/
+ scrub_item_init_fs(&sri);
action_list_init(&alist);
- ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_FSCOUNTERS, 0, &alist);
+ ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_FSCOUNTERS, 0, &alist, &sri);
if (ret)
return ret;
@@ -159,7 +161,7 @@ phase4_func(
if (fsgeom.sick & XFS_FSOP_GEOM_SICK_QUOTACHECK) {
ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_QUOTACHECK, 0,
- &alist);
+ &alist, &sri);
if (ret)
return ret;
}
diff --git a/scrub/phase5.c b/scrub/phase5.c
index 68d35cd58..ace6c3a98 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -385,6 +385,7 @@ check_fs_label(
}
struct fs_scan_item {
+ struct scrub_item sri;
struct action_list alist;
bool *abortedp;
unsigned int scrub_type;
@@ -412,7 +413,8 @@ fs_scan_worker(
nanosleep(&tv, NULL);
}
- ret = scrub_meta_type(ctx, item->scrub_type, 0, &item->alist);
+ ret = scrub_meta_type(ctx, item->scrub_type, 0, &item->alist,
+ &item->sri);
if (ret) {
str_liberror(ctx, ret, _("checking fs scan metadata"));
*item->abortedp = true;
@@ -450,6 +452,7 @@ queue_fs_scan(
str_liberror(ctx, ret, _("setting up fs scan"));
return ret;
}
+ scrub_item_init_fs(&item->sri);
action_list_init(&item->alist);
item->scrub_type = scrub_type;
item->abortedp = abortedp;
diff --git a/scrub/phase7.c b/scrub/phase7.c
index 820a68f99..314a886b0 100644
--- a/scrub/phase7.c
+++ b/scrub/phase7.c
@@ -99,6 +99,7 @@ phase7_func(
struct scrub_ctx *ctx)
{
struct summary_counts totalcount = {0};
+ struct scrub_item sri;
struct action_list alist;
struct ptvar *ptvar;
unsigned long long used_data;
@@ -117,8 +118,9 @@ phase7_func(
int error;
/* Check and fix the summary metadata. */
+ scrub_item_init_fs(&sri);
action_list_init(&alist);
- error = scrub_summary_metadata(ctx, &alist);
+ error = scrub_summary_metadata(ctx, &alist, &sri);
if (error)
return error;
error = action_list_process(ctx, -1, &alist,
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 6e857c79d..e242e38ed 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -264,7 +264,8 @@ scrub_meta_type(
struct scrub_ctx *ctx,
unsigned int type,
xfs_agnumber_t agno,
- struct action_list *alist)
+ struct action_list *alist,
+ struct scrub_item *sri)
{
struct xfs_scrub_metadata meta = {
.sm_type = type,
@@ -283,11 +284,13 @@ scrub_meta_type(
case CHECK_ABORT:
return ECANCELED;
case CHECK_REPAIR:
+ scrub_item_save_state(sri, type, meta.sm_flags);
ret = scrub_save_repair(ctx, alist, &meta);
if (ret)
return ret;
fallthrough;
case CHECK_DONE:
+ scrub_item_clean_state(sri, type);
return 0;
default:
/* CHECK_RETRY should never happen. */
@@ -305,7 +308,8 @@ scrub_group(
struct scrub_ctx *ctx,
enum xfrog_scrub_group group,
xfs_agnumber_t agno,
- struct action_list *alist)
+ struct action_list *alist,
+ struct scrub_item *sri)
{
const struct xfrog_scrub_descr *sc;
unsigned int type;
@@ -317,7 +321,7 @@ scrub_group(
if (sc->group != group)
continue;
- ret = scrub_meta_type(ctx, type, agno, alist);
+ ret = scrub_meta_type(ctx, type, agno, alist, sri);
if (ret)
return ret;
}
@@ -330,9 +334,10 @@ int
scrub_ag_headers(
struct scrub_ctx *ctx,
xfs_agnumber_t agno,
- struct action_list *alist)
+ struct action_list *alist,
+ struct scrub_item *sri)
{
- return scrub_group(ctx, XFROG_SCRUB_GROUP_AGHEADER, agno, alist);
+ return scrub_group(ctx, XFROG_SCRUB_GROUP_AGHEADER, agno, alist, sri);
}
/* Scrub each AG's metadata btrees. */
@@ -340,9 +345,10 @@ int
scrub_ag_metadata(
struct scrub_ctx *ctx,
xfs_agnumber_t agno,
- struct action_list *alist)
+ struct action_list *alist,
+ struct scrub_item *sri)
{
- return scrub_group(ctx, XFROG_SCRUB_GROUP_PERAG, agno, alist);
+ return scrub_group(ctx, XFROG_SCRUB_GROUP_PERAG, agno, alist, sri);
}
/* Scrub whole-filesystem metadata. */
@@ -350,20 +356,22 @@ int
scrub_fs_metadata(
struct scrub_ctx *ctx,
unsigned int type,
- struct action_list *alist)
+ struct action_list *alist,
+ struct scrub_item *sri)
{
ASSERT(xfrog_scrubbers[type].group == XFROG_SCRUB_GROUP_FS);
- return scrub_meta_type(ctx, type, 0, alist);
+ return scrub_meta_type(ctx, type, 0, alist, sri);
}
/* Scrub all FS summary metadata. */
int
scrub_summary_metadata(
struct scrub_ctx *ctx,
- struct action_list *alist)
+ struct action_list *alist,
+ struct scrub_item *sri)
{
- return scrub_group(ctx, XFROG_SCRUB_GROUP_SUMMARY, 0, alist);
+ return scrub_group(ctx, XFROG_SCRUB_GROUP_SUMMARY, 0, alist, sri);
}
/* How many items do we have to check? */
@@ -425,7 +433,8 @@ scrub_file(
int fd,
const struct xfs_bulkstat *bstat,
unsigned int type,
- struct action_list *alist)
+ struct action_list *alist,
+ struct scrub_item *sri)
{
struct xfs_scrub_metadata meta = {0};
struct xfs_fd xfd;
@@ -454,12 +463,45 @@ scrub_file(
fix = xfs_check_metadata(ctx, xfdp, &meta, true);
if (fix == CHECK_ABORT)
return ECANCELED;
- if (fix == CHECK_DONE)
+ if (fix == CHECK_DONE) {
+ scrub_item_clean_state(sri, type);
return 0;
+ }
+ scrub_item_save_state(sri, type, meta.sm_flags);
return scrub_save_repair(ctx, alist, &meta);
}
+/* Dump a scrub item for debugging purposes. */
+void
+scrub_item_dump(
+ struct scrub_item *sri,
+ unsigned int group_mask,
+ const char *tag)
+{
+ unsigned int i;
+
+ if (group_mask == 0)
+ group_mask = -1U;
+
+ printf("DUMP SCRUB ITEM FOR %s\n", tag);
+ if (sri->sri_ino != -1ULL)
+ printf("ino 0x%llx gen %u\n", (unsigned long long)sri->sri_ino,
+ sri->sri_gen);
+ if (sri->sri_agno != -1U)
+ printf("agno %u\n", sri->sri_agno);
+
+ foreach_scrub_type(i) {
+ unsigned int g = 1U << xfrog_scrubbers[i].group;
+
+ if (g & group_mask)
+ printf("[%u]: type '%s' state 0x%x\n", i,
+ xfrog_scrubbers[i].name,
+ sri->sri_state[i]);
+ }
+ fflush(stdout);
+}
+
/*
* Test the availability of a kernel scrub command. If errors occur (or the
* scrub ioctl is rejected) the errors will be logged and this function will
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 98819a25b..21ea4147e 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -16,17 +16,85 @@ enum check_outcome {
struct action_item;
+/*
+ * These flags record the metadata object state that the kernel returned.
+ * We want to remember if the object was corrupt, if the cross-referencing
+ * revealed inconsistencies (xcorrupt), if the cross referencing itself failed
+ * (xfail) or if the object is correct but could be optimised (preen).
+ */
+#define SCRUB_ITEM_CORRUPT (XFS_SCRUB_OFLAG_CORRUPT) /* (1 << 1) */
+#define SCRUB_ITEM_PREEN (XFS_SCRUB_OFLAG_PREEN) /* (1 << 2) */
+#define SCRUB_ITEM_XFAIL (XFS_SCRUB_OFLAG_XFAIL) /* (1 << 3) */
+#define SCRUB_ITEM_XCORRUPT (XFS_SCRUB_OFLAG_XCORRUPT) /* (1 << 4) */
+
+/* All of the state flags that we need to prioritize repair work. */
+#define SCRUB_ITEM_REPAIR_ANY (SCRUB_ITEM_CORRUPT | \
+ SCRUB_ITEM_PREEN | \
+ SCRUB_ITEM_XFAIL | \
+ SCRUB_ITEM_XCORRUPT)
+
+struct scrub_item {
+ /*
+ * Information we need to call the scrub and repair ioctls. Per-AG
+ * items should set the ino/gen fields to -1; per-inode items should
+ * set sri_agno to -1; and per-fs items should set all three fields to
+ * -1. Or use the macros below.
+ */
+ __u64 sri_ino;
+ __u32 sri_gen;
+ __u32 sri_agno;
+
+ /* Scrub item state flags, one for each XFS_SCRUB_TYPE. */
+ __u8 sri_state[XFS_SCRUB_TYPE_NR];
+};
+
+#define foreach_scrub_type(loopvar) \
+ for ((loopvar) = 0; (loopvar) < XFS_SCRUB_TYPE_NR; (loopvar)++)
+
+static inline void
+scrub_item_init_ag(struct scrub_item *sri, xfs_agnumber_t agno)
+{
+ memset(sri, 0, sizeof(*sri));
+ sri->sri_agno = agno;
+ sri->sri_ino = -1ULL;
+ sri->sri_gen = -1U;
+}
+
+static inline void
+scrub_item_init_fs(struct scrub_item *sri)
+{
+ memset(sri, 0, sizeof(*sri));
+ sri->sri_agno = -1U;
+ sri->sri_ino = -1ULL;
+ sri->sri_gen = -1U;
+}
+
+static inline void
+scrub_item_init_file(struct scrub_item *sri, const struct xfs_bulkstat *bstat)
+{
+ memset(sri, 0, sizeof(*sri));
+ sri->sri_agno = -1U;
+ sri->sri_ino = bstat->bs_ino;
+ sri->sri_gen = bstat->bs_gen;
+}
+
+void scrub_item_dump(struct scrub_item *sri, unsigned int group_mask,
+ const char *tag);
+
void scrub_report_preen_triggers(struct scrub_ctx *ctx);
int scrub_ag_headers(struct scrub_ctx *ctx, xfs_agnumber_t agno,
- struct action_list *alist);
+ struct action_list *alist, struct scrub_item *sri);
int scrub_ag_metadata(struct scrub_ctx *ctx, xfs_agnumber_t agno,
- struct action_list *alist);
+ struct action_list *alist, struct scrub_item *sri);
int scrub_fs_metadata(struct scrub_ctx *ctx, unsigned int scrub_type,
- struct action_list *alist);
-int scrub_iscan_metadata(struct scrub_ctx *ctx, struct action_list *alist);
-int scrub_summary_metadata(struct scrub_ctx *ctx, struct action_list *alist);
+ struct action_list *alist, struct scrub_item *sri);
+int scrub_iscan_metadata(struct scrub_ctx *ctx, struct action_list *alist,
+ struct scrub_item *sri);
+int scrub_summary_metadata(struct scrub_ctx *ctx, struct action_list *alist,
+ struct scrub_item *sri);
int scrub_meta_type(struct scrub_ctx *ctx, unsigned int type,
- xfs_agnumber_t agno, struct action_list *alist);
+ xfs_agnumber_t agno, struct action_list *alist,
+ struct scrub_item *sri);
bool can_scrub_fs_metadata(struct scrub_ctx *ctx);
bool can_scrub_inode(struct scrub_ctx *ctx);
@@ -39,7 +107,8 @@ bool can_repair(struct scrub_ctx *ctx);
bool can_force_rebuild(struct scrub_ctx *ctx);
int scrub_file(struct scrub_ctx *ctx, int fd, const struct xfs_bulkstat *bstat,
- unsigned int type, struct action_list *alist);
+ unsigned int type, struct action_list *alist,
+ struct scrub_item *sri);
/* Repair parameters are the scrub inputs and retry count. */
struct action_item {
diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h
index a24d485a2..090efb54c 100644
--- a/scrub/scrub_private.h
+++ b/scrub/scrub_private.h
@@ -52,4 +52,23 @@ static inline bool needs_repair(struct xfs_scrub_metadata *sm)
void scrub_warn_incomplete_scrub(struct scrub_ctx *ctx, struct descr *dsc,
struct xfs_scrub_metadata *meta);
+/* Scrub item functions */
+
+static inline void
+scrub_item_save_state(
+ struct scrub_item *sri,
+ unsigned int scrub_type,
+ unsigned int scrub_flags)
+{
+ sri->sri_state[scrub_type] = scrub_flags & SCRUB_ITEM_REPAIR_ANY;
+}
+
+static inline void
+scrub_item_clean_state(
+ struct scrub_item *sri,
+ unsigned int scrub_type)
+{
+ sri->sri_state[scrub_type] = 0;
+}
+
#endif /* XFS_SCRUB_SCRUB_PRIVATE_H_ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/9] xfs_scrub: use repair_item to direct repair activities
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
2024-07-30 1:01 ` [PATCH 1/9] xfs_scrub: track repair items by principal, not by individual repairs Darrick J. Wong
@ 2024-07-30 1:01 ` Darrick J. Wong
2024-07-30 1:01 ` [PATCH 3/9] xfs_scrub: remove action lists from phaseX code Darrick J. Wong
` (6 subsequent siblings)
8 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:01 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Now that the new scrub_item tracks the state of any filesystem object
needing any kind of repair, use it to drive filesystem repairs and
updates to the in-kernel health status when repair finishes.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase1.c | 2
scrub/phase2.c | 24 ++--
scrub/phase3.c | 57 ++++----
scrub/phase4.c | 7 -
scrub/phase5.c | 2
scrub/phase7.c | 3
scrub/repair.c | 381 +++++++++++++++++++++++++++++++-------------------------
scrub/repair.h | 45 +++++--
scrub/scrub.c | 44 ------
scrub/scrub.h | 12 --
10 files changed, 298 insertions(+), 279 deletions(-)
diff --git a/scrub/phase1.c b/scrub/phase1.c
index 9920f29a6..b1bbc694e 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -71,7 +71,7 @@ report_to_kernel(
* Complain if we cannot fail the clean bill of health, unless we're
* just testing repairs.
*/
- if (action_list_length(&alist) > 0 &&
+ if (repair_item_count_needsrepair(&sri) != 0 &&
!debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) {
str_info(ctx, _("Couldn't upload clean bill of health."), NULL);
action_list_discard(&alist);
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 518923d66..26ce58180 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -58,6 +58,7 @@ scan_ag_metadata(
void *arg)
{
struct scrub_item sri;
+ struct scrub_item fix_now;
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
struct scan_ctl *sctl = arg;
struct action_list alist;
@@ -83,7 +84,7 @@ scan_ag_metadata(
goto err;
/* Repair header damage. */
- ret = action_list_process_or_defer(ctx, agno, &alist);
+ ret = repair_item_corruption(ctx, &sri);
if (ret)
goto err;
@@ -99,17 +100,19 @@ scan_ag_metadata(
* the inobt from rmapbt data, but if the rmapbt is broken even
* at this early phase then we are sunk.
*/
- difficulty = action_list_difficulty(&alist);
- action_list_find_mustfix(&alist, &immediate_alist);
+ difficulty = repair_item_difficulty(&sri);
+ repair_item_mustfix(&sri, &fix_now);
warn_repair_difficulties(ctx, difficulty, descr);
/* Repair (inode) btree damage. */
- ret = action_list_process_or_defer(ctx, agno, &immediate_alist);
+ ret = repair_item_corruption(ctx, &fix_now);
if (ret)
goto err;
/* Everything else gets fixed during phase 4. */
- action_list_defer(ctx, agno, &alist);
+ ret = repair_item_defer(ctx, &sri);
+ if (ret)
+ goto err;
return;
err:
sctl->aborted = true;
@@ -141,10 +144,14 @@ scan_fs_metadata(
}
/* Complain about metadata corruptions that might not be fixable. */
- difficulty = action_list_difficulty(&alist);
+ difficulty = repair_item_difficulty(&sri);
warn_repair_difficulties(ctx, difficulty, xfrog_scrubbers[type].descr);
- action_list_defer(ctx, 0, &alist);
+ ret = repair_item_defer(ctx, &sri);
+ if (ret) {
+ sctl->aborted = true;
+ goto out;
+ }
out:
if (type == XFS_SCRUB_TYPE_RTBITMAP) {
@@ -193,8 +200,7 @@ phase2_func(
ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_SB, 0, &alist, &sri);
if (ret)
goto out_wq;
- ret = action_list_process(ctx, -1, &alist,
- XRM_FINAL_WARNING | XRM_NOPROGRESS);
+ ret = repair_item_completely(ctx, &sri);
if (ret)
goto out_wq;
diff --git a/scrub/phase3.c b/scrub/phase3.c
index 642b8406e..e602d8c7e 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -55,45 +55,48 @@ report_close_error(
* Defer all the repairs until phase 4, being careful about locking since the
* inode scrub threads are not per-AG.
*/
-static void
+static int
defer_inode_repair(
- struct scrub_inode_ctx *ictx,
- xfs_agnumber_t agno,
- struct action_list *alist)
+ struct scrub_inode_ctx *ictx,
+ const struct xfs_bulkstat *bstat,
+ struct scrub_item *sri)
{
- if (alist->nr == 0)
- return;
+ struct action_item *aitem = NULL;
+ xfs_agnumber_t agno;
+ int ret;
+ ret = repair_item_to_action_item(ictx->ctx, sri, &aitem);
+ if (ret || !aitem)
+ return ret;
+
+ agno = cvt_ino_to_agno(&ictx->ctx->mnt, bstat->bs_ino);
pthread_mutex_lock(&ictx->locks[agno]);
- action_list_defer(ictx->ctx, agno, alist);
+ action_list_add(&ictx->ctx->action_lists[agno], aitem);
pthread_mutex_unlock(&ictx->locks[agno]);
+ return 0;
}
-/* Run repair actions now and defer unfinished items for later. */
+/* Run repair actions now and leave unfinished items for later. */
static int
try_inode_repair(
- struct scrub_inode_ctx *ictx,
- int fd,
- xfs_agnumber_t agno,
- struct action_list *alist)
+ struct scrub_inode_ctx *ictx,
+ struct scrub_item *sri,
+ int fd,
+ const struct xfs_bulkstat *bstat)
{
- int ret;
-
/*
* If at the start of phase 3 we already had ag/rt metadata repairs
* queued up for phase 4, leave the action list untouched so that file
- * metadata repairs will be deferred in scan order until phase 4.
+ * metadata repairs will be deferred until phase 4.
*/
if (ictx->always_defer_repairs)
return 0;
- ret = action_list_process(ictx->ctx, fd, alist,
- XRM_REPAIR_ONLY | XRM_NOPROGRESS);
- if (ret)
- return ret;
-
- defer_inode_repair(ictx, agno, alist);
- return 0;
+ /*
+ * Try to repair the file metadata. Unfixed metadata will remain in
+ * the scrub item state to be queued as a single action item.
+ */
+ return repair_file_corruption(ictx->ctx, sri, fd);
}
/* Verify the contents, xattrs, and extent maps of an inode. */
@@ -108,13 +111,11 @@ scrub_inode(
struct scrub_item sri;
struct scrub_inode_ctx *ictx = arg;
struct ptcounter *icount = ictx->icount;
- xfs_agnumber_t agno;
int fd = -1;
int error;
scrub_item_init_file(&sri, bstat);
action_list_init(&alist);
- agno = cvt_ino_to_agno(&ctx->mnt, bstat->bs_ino);
background_sleep();
/*
@@ -149,7 +150,7 @@ scrub_inode(
if (error)
goto out;
- error = try_inode_repair(ictx, fd, agno, &alist);
+ error = try_inode_repair(ictx, &sri, fd, bstat);
if (error)
goto out;
@@ -164,7 +165,7 @@ scrub_inode(
if (error)
goto out;
- error = try_inode_repair(ictx, fd, agno, &alist);
+ error = try_inode_repair(ictx, &sri, fd, bstat);
if (error)
goto out;
@@ -204,7 +205,7 @@ scrub_inode(
goto out;
/* Try to repair the file while it's open. */
- error = try_inode_repair(ictx, fd, agno, &alist);
+ error = try_inode_repair(ictx, &sri, fd, bstat);
if (error)
goto out;
@@ -221,7 +222,7 @@ scrub_inode(
progress_add(1);
if (!error && !ictx->aborted)
- defer_inode_repair(ictx, agno, &alist);
+ error = defer_inode_repair(ictx, bstat, &sri);
if (fd >= 0) {
int err2;
diff --git a/scrub/phase4.c b/scrub/phase4.c
index 1c4aab996..98518635b 100644
--- a/scrub/phase4.c
+++ b/scrub/phase4.c
@@ -40,7 +40,7 @@ repair_ag(
/* Repair anything broken until we fail to make progress. */
do {
- ret = action_list_process(ctx, -1, alist, flags);
+ ret = action_list_process(ctx, alist, flags);
if (ret) {
*aborted = true;
return;
@@ -55,7 +55,7 @@ repair_ag(
/* Try once more, but this time complain if we can't fix things. */
flags |= XRM_FINAL_WARNING;
- ret = action_list_process(ctx, -1, alist, flags);
+ ret = action_list_process(ctx, alist, flags);
if (ret)
*aborted = true;
}
@@ -167,8 +167,7 @@ phase4_func(
}
/* Repair counters before starting on the rest. */
- ret = action_list_process(ctx, -1, &alist,
- XRM_REPAIR_ONLY | XRM_NOPROGRESS);
+ ret = repair_item_corruption(ctx, &sri);
if (ret)
return ret;
action_list_discard(&alist);
diff --git a/scrub/phase5.c b/scrub/phase5.c
index ace6c3a98..79bfea8f6 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -421,7 +421,7 @@ fs_scan_worker(
goto out;
}
- ret = action_list_process(ctx, ctx->mnt.fd, &item->alist,
+ ret = action_list_process(ctx, &item->alist,
XRM_FINAL_WARNING | XRM_NOPROGRESS);
if (ret) {
str_liberror(ctx, ret, _("repairing fs scan metadata"));
diff --git a/scrub/phase7.c b/scrub/phase7.c
index 314a886b0..404bfb822 100644
--- a/scrub/phase7.c
+++ b/scrub/phase7.c
@@ -123,8 +123,7 @@ phase7_func(
error = scrub_summary_metadata(ctx, &alist, &sri);
if (error)
return error;
- error = action_list_process(ctx, -1, &alist,
- XRM_FINAL_WARNING | XRM_NOPROGRESS);
+ error = repair_item_completely(ctx, &sri);
if (error)
return error;
diff --git a/scrub/repair.c b/scrub/repair.c
index 30817d268..6e09c592e 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -27,7 +27,8 @@ static enum check_outcome
xfs_repair_metadata(
struct scrub_ctx *ctx,
struct xfs_fd *xfdp,
- struct action_item *aitem,
+ unsigned int scrub_type,
+ struct scrub_item *sri,
unsigned int repair_flags)
{
struct xfs_scrub_metadata meta = { 0 };
@@ -35,20 +36,20 @@ xfs_repair_metadata(
DEFINE_DESCR(dsc, ctx, format_scrub_descr);
int error;
- assert(aitem->type < XFS_SCRUB_TYPE_NR);
+ assert(scrub_type < XFS_SCRUB_TYPE_NR);
assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
- meta.sm_type = aitem->type;
- meta.sm_flags = aitem->flags | XFS_SCRUB_IFLAG_REPAIR;
+ meta.sm_type = scrub_type;
+ meta.sm_flags = XFS_SCRUB_IFLAG_REPAIR;
if (use_force_rebuild)
meta.sm_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
- switch (xfrog_scrubbers[aitem->type].group) {
+ switch (xfrog_scrubbers[scrub_type].group) {
case XFROG_SCRUB_GROUP_AGHEADER:
case XFROG_SCRUB_GROUP_PERAG:
- meta.sm_agno = aitem->agno;
+ meta.sm_agno = sri->sri_agno;
break;
case XFROG_SCRUB_GROUP_INODE:
- meta.sm_ino = aitem->ino;
- meta.sm_gen = aitem->gen;
+ meta.sm_ino = sri->sri_ino;
+ meta.sm_gen = sri->sri_gen;
break;
default:
break;
@@ -58,9 +59,10 @@ xfs_repair_metadata(
return CHECK_RETRY;
memcpy(&oldm, &meta, sizeof(oldm));
+ oldm.sm_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
descr_set(&dsc, &oldm);
- if (needs_repair(&meta))
+ if (needs_repair(&oldm))
str_info(ctx, descr_render(&dsc), _("Attempting repair."));
else if (debug || verbose)
str_info(ctx, descr_render(&dsc),
@@ -92,8 +94,10 @@ _("Filesystem is shut down, aborting."));
* it done and move on.
*/
if (is_unoptimized(&oldm) ||
- debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
+ debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) {
+ scrub_item_clean_state(sri, scrub_type);
return CHECK_DONE;
+ }
/*
* If we're in no-complain mode, requeue the check for
* later. It's possible that an error in another
@@ -109,6 +113,7 @@ _("Filesystem is shut down, aborting."));
/* Kernel doesn't know how to repair this? */
str_corrupt(ctx, descr_render(&dsc),
_("Don't know how to fix; offline repair required."));
+ scrub_item_clean_state(sri, scrub_type);
return CHECK_DONE;
case EROFS:
/* Read-only filesystem, can't fix. */
@@ -118,23 +123,28 @@ _("Read-only filesystem; cannot make changes."));
return CHECK_ABORT;
case ENOENT:
/* Metadata not present, just skip it. */
+ scrub_item_clean_state(sri, scrub_type);
return CHECK_DONE;
case ENOMEM:
case ENOSPC:
/* Don't care if preen fails due to low resources. */
- if (is_unoptimized(&oldm) && !needs_repair(&oldm))
+ if (is_unoptimized(&oldm) && !needs_repair(&oldm)) {
+ scrub_item_clean_state(sri, scrub_type);
return CHECK_DONE;
+ }
fallthrough;
default:
/*
- * Operational error. If the caller doesn't want us
- * to complain about repair failures, tell the caller
- * to requeue the repair for later and don't say a
- * thing. Otherwise, print error and bail out.
+ * Operational error. If the caller doesn't want us to
+ * complain about repair failures, tell the caller to requeue
+ * the repair for later and don't say a thing. Otherwise,
+ * print an error, mark the item clean because we're done with
+ * trying to repair it, and bail out.
*/
if (!(repair_flags & XRM_FINAL_WARNING))
return CHECK_RETRY;
str_liberror(ctx, error, descr_render(&dsc));
+ scrub_item_clean_state(sri, scrub_type);
return CHECK_DONE;
}
@@ -186,12 +196,13 @@ _("Repair unsuccessful; offline repair required."));
record_preen(ctx, descr_render(&dsc),
_("Optimization successful."));
}
+
+ scrub_item_clean_state(sri, scrub_type);
return CHECK_DONE;
}
/*
* Prioritize action items in order of how long we can wait.
- * 0 = do it now, 10000 = do it later.
*
* To minimize the amount of repair work, we want to prioritize metadata
* objects by perceived corruptness. If CORRUPT is set, the fields are
@@ -207,104 +218,34 @@ _("Repair unsuccessful; offline repair required."));
* in order.
*/
-/* Sort action items in severity order. */
-static int
-PRIO(
- const struct action_item *aitem,
- int order)
-{
- if (aitem->flags & XFS_SCRUB_OFLAG_CORRUPT)
- return order;
- else if (aitem->flags & XFS_SCRUB_OFLAG_XCORRUPT)
- return 100 + order;
- else if (aitem->flags & XFS_SCRUB_OFLAG_XFAIL)
- return 200 + order;
- else if (aitem->flags & XFS_SCRUB_OFLAG_PREEN)
- return 300 + order;
- abort();
-}
-
-/* Sort the repair items in dependency order. */
-static int
-xfs_action_item_priority(
- const struct action_item *aitem)
-{
- switch (aitem->type) {
- case XFS_SCRUB_TYPE_SB:
- case XFS_SCRUB_TYPE_AGF:
- case XFS_SCRUB_TYPE_AGFL:
- case XFS_SCRUB_TYPE_AGI:
- case XFS_SCRUB_TYPE_BNOBT:
- case XFS_SCRUB_TYPE_CNTBT:
- case XFS_SCRUB_TYPE_INOBT:
- case XFS_SCRUB_TYPE_FINOBT:
- case XFS_SCRUB_TYPE_REFCNTBT:
- case XFS_SCRUB_TYPE_RMAPBT:
- case XFS_SCRUB_TYPE_INODE:
- case XFS_SCRUB_TYPE_BMBTD:
- case XFS_SCRUB_TYPE_BMBTA:
- case XFS_SCRUB_TYPE_BMBTC:
- return PRIO(aitem, aitem->type - 1);
- case XFS_SCRUB_TYPE_DIR:
- case XFS_SCRUB_TYPE_XATTR:
- case XFS_SCRUB_TYPE_SYMLINK:
- case XFS_SCRUB_TYPE_PARENT:
- return PRIO(aitem, XFS_SCRUB_TYPE_DIR);
- case XFS_SCRUB_TYPE_RTBITMAP:
- case XFS_SCRUB_TYPE_RTSUM:
- return PRIO(aitem, XFS_SCRUB_TYPE_RTBITMAP);
- case XFS_SCRUB_TYPE_UQUOTA:
- case XFS_SCRUB_TYPE_GQUOTA:
- case XFS_SCRUB_TYPE_PQUOTA:
- return PRIO(aitem, XFS_SCRUB_TYPE_UQUOTA);
- case XFS_SCRUB_TYPE_QUOTACHECK:
- /* This should always go after [UGP]QUOTA no matter what. */
- return PRIO(aitem, aitem->type);
- case XFS_SCRUB_TYPE_FSCOUNTERS:
- /* This should always go after AG headers no matter what. */
- return PRIO(aitem, INT_MAX);
- }
- abort();
-}
-
-/* Make sure that btrees get repaired before headers. */
-static int
-xfs_action_item_compare(
- void *priv,
- const struct list_head *a,
- const struct list_head *b)
-{
- const struct action_item *ra;
- const struct action_item *rb;
-
- ra = container_of(a, struct action_item, list);
- rb = container_of(b, struct action_item, list);
-
- return xfs_action_item_priority(ra) - xfs_action_item_priority(rb);
-}
+struct action_item {
+ struct list_head list;
+ struct scrub_item sri;
+};
/*
* Figure out which AG metadata must be fixed before we can move on
* to the inode scan.
*/
void
-action_list_find_mustfix(
- struct action_list *alist,
- struct action_list *immediate_alist)
+repair_item_mustfix(
+ struct scrub_item *sri,
+ struct scrub_item *fix_now)
{
- struct action_item *n;
- struct action_item *aitem;
+ unsigned int scrub_type;
- list_for_each_entry_safe(aitem, n, &alist->list, list) {
- if (!(aitem->flags & XFS_SCRUB_OFLAG_CORRUPT))
+ assert(sri->sri_agno != -1U);
+ scrub_item_init_ag(fix_now, sri->sri_agno);
+
+ foreach_scrub_type(scrub_type) {
+ if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_CORRUPT))
continue;
- switch (aitem->type) {
+
+ switch (scrub_type) {
case XFS_SCRUB_TYPE_AGI:
case XFS_SCRUB_TYPE_FINOBT:
case XFS_SCRUB_TYPE_INOBT:
- alist->nr--;
- list_move_tail(&aitem->list, &immediate_alist->list);
- immediate_alist->nr++;
+ fix_now->sri_state[scrub_type] |= SCRUB_ITEM_CORRUPT;
break;
}
}
@@ -312,19 +253,19 @@ action_list_find_mustfix(
/* Determine if primary or secondary metadata are inconsistent. */
unsigned int
-action_list_difficulty(
- const struct action_list *alist)
+repair_item_difficulty(
+ const struct scrub_item *sri)
{
- struct action_item *aitem, *n;
- unsigned int ret = 0;
+ unsigned int scrub_type;
+ unsigned int ret = 0;
- list_for_each_entry_safe(aitem, n, &alist->list, list) {
- if (!(aitem->flags & (XFS_SCRUB_OFLAG_CORRUPT |
- XFS_SCRUB_OFLAG_XCORRUPT |
- XFS_SCRUB_OFLAG_XFAIL)))
+ foreach_scrub_type(scrub_type) {
+ if (!(sri->sri_state[scrub_type] & (XFS_SCRUB_OFLAG_CORRUPT |
+ XFS_SCRUB_OFLAG_XCORRUPT |
+ XFS_SCRUB_OFLAG_XFAIL)))
continue;
- switch (aitem->type) {
+ switch (scrub_type) {
case XFS_SCRUB_TYPE_RMAPBT:
ret |= REPAIR_DIFFICULTY_SECONDARY;
break;
@@ -404,13 +345,19 @@ action_list_init(
alist->sorted = false;
}
-/* Number of repairs in this list. */
+/* Number of pending repairs in this list. */
unsigned long long
action_list_length(
struct action_list *alist)
{
- return alist->nr;
-};
+ struct action_item *aitem;
+ unsigned long long ret = 0;
+
+ list_for_each_entry(aitem, &alist->list, list)
+ ret += repair_item_count_needsrepair(&aitem->sri);
+
+ return ret;
+}
/* Add to the list of repairs. */
void
@@ -423,60 +370,78 @@ action_list_add(
alist->sorted = false;
}
-/* Splice two repair lists. */
-void
-action_list_splice(
- struct action_list *dest,
- struct action_list *src)
-{
- if (src->nr == 0)
- return;
-
- list_splice_tail_init(&src->list, &dest->list);
- dest->nr += src->nr;
- src->nr = 0;
- dest->sorted = false;
-}
-
/* Repair everything on this list. */
int
action_list_process(
struct scrub_ctx *ctx,
- int fd,
struct action_list *alist,
unsigned int repair_flags)
+{
+ struct action_item *aitem;
+ struct action_item *n;
+ int ret;
+
+ list_for_each_entry_safe(aitem, n, &alist->list, list) {
+ if (scrub_excessive_errors(ctx))
+ return ECANCELED;
+
+ ret = repair_item(ctx, &aitem->sri, repair_flags);
+ if (ret)
+ break;
+
+ if (repair_item_count_needsrepair(&aitem->sri) == 0) {
+ list_del(&aitem->list);
+ free(aitem);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * For a given filesystem object, perform all repairs of a given class
+ * (corrupt, xcorrupt, xfail, preen) if the repair item says it's needed.
+ */
+static int
+repair_item_class(
+ struct scrub_ctx *ctx,
+ struct scrub_item *sri,
+ int override_fd,
+ uint8_t repair_mask,
+ unsigned int flags)
{
struct xfs_fd xfd;
struct xfs_fd *xfdp = &ctx->mnt;
- struct action_item *aitem;
- struct action_item *n;
- enum check_outcome fix;
+ unsigned int scrub_type;
+
+ if (ctx->mode < SCRUB_MODE_REPAIR)
+ return 0;
/*
* If the caller passed us a file descriptor for a scrub, use it
* instead of scrub-by-handle because this enables the kernel to skip
* costly inode btree lookups.
*/
- if (fd >= 0) {
+ if (override_fd >= 0) {
memcpy(&xfd, xfdp, sizeof(xfd));
- xfd.fd = fd;
+ xfd.fd = override_fd;
xfdp = &xfd;
}
- if (!alist->sorted) {
- list_sort(NULL, &alist->list, xfs_action_item_compare);
- alist->sorted = true;
- }
+ foreach_scrub_type(scrub_type) {
+ enum check_outcome fix;
- list_for_each_entry_safe(aitem, n, &alist->list, list) {
- fix = xfs_repair_metadata(ctx, xfdp, aitem, repair_flags);
+ if (scrub_excessive_errors(ctx))
+ return ECANCELED;
+
+ if (!(sri->sri_state[scrub_type] & repair_mask))
+ continue;
+
+ fix = xfs_repair_metadata(ctx, xfdp, scrub_type, sri, flags);
switch (fix) {
case CHECK_DONE:
- if (!(repair_flags & XRM_NOPROGRESS))
+ if (!(flags & XRM_NOPROGRESS))
progress_add(1);
- alist->nr--;
- list_del(&aitem->list);
- free(aitem);
continue;
case CHECK_ABORT:
return ECANCELED;
@@ -487,37 +452,113 @@ action_list_process(
}
}
- if (scrub_excessive_errors(ctx))
- return ECANCELED;
+ return 0;
+}
+
+/*
+ * Repair all parts (i.e. scrub types) of this filesystem object for which
+ * corruption has been observed directly. Other types of repair work (fixing
+ * cross referencing problems and preening) are deferred.
+ *
+ * This function should only be called to perform spot repairs of fs objects
+ * during phase 2 and 3 while we still have open handles to those objects.
+ */
+int
+repair_item_corruption(
+ struct scrub_ctx *ctx,
+ struct scrub_item *sri)
+{
+ return repair_item_class(ctx, sri, -1, SCRUB_ITEM_CORRUPT,
+ XRM_REPAIR_ONLY | XRM_NOPROGRESS);
+}
+
+/* Repair all parts of this file, similar to repair_item_corruption. */
+int
+repair_file_corruption(
+ struct scrub_ctx *ctx,
+ struct scrub_item *sri,
+ int override_fd)
+{
+ return repair_item_class(ctx, sri, override_fd, SCRUB_ITEM_CORRUPT,
+ XRM_REPAIR_ONLY | XRM_NOPROGRESS);
+}
+
+/*
+ * Repair everything in this filesystem object that needs it. This includes
+ * cross-referencing and preening.
+ */
+int
+repair_item(
+ struct scrub_ctx *ctx,
+ struct scrub_item *sri,
+ unsigned int flags)
+{
+ int ret;
+
+ ret = repair_item_class(ctx, sri, -1, SCRUB_ITEM_CORRUPT, flags);
+ if (ret)
+ return ret;
+
+ ret = repair_item_class(ctx, sri, -1, SCRUB_ITEM_XCORRUPT, flags);
+ if (ret)
+ return ret;
+
+ ret = repair_item_class(ctx, sri, -1, SCRUB_ITEM_XFAIL, flags);
+ if (ret)
+ return ret;
+
+ return repair_item_class(ctx, sri, -1, SCRUB_ITEM_PREEN, flags);
+}
+
+/* Create an action item around a scrub item that needs repairs. */
+int
+repair_item_to_action_item(
+ struct scrub_ctx *ctx,
+ const struct scrub_item *sri,
+ struct action_item **aitemp)
+{
+ struct action_item *aitem;
+
+ if (repair_item_count_needsrepair(sri) == 0)
+ return 0;
+
+ aitem = malloc(sizeof(struct action_item));
+ if (!aitem) {
+ int error = errno;
+
+ str_liberror(ctx, error, _("creating repair action item"));
+ return error;
+ }
+
+ INIT_LIST_HEAD(&aitem->list);
+ memcpy(&aitem->sri, sri, sizeof(struct scrub_item));
+
+ *aitemp = aitem;
return 0;
}
/* Defer all the repairs until phase 4. */
-void
-action_list_defer(
- struct scrub_ctx *ctx,
- xfs_agnumber_t agno,
- struct action_list *alist)
+int
+repair_item_defer(
+ struct scrub_ctx *ctx,
+ const struct scrub_item *sri)
{
+ struct action_item *aitem = NULL;
+ unsigned int agno;
+ int error;
+
+ error = repair_item_to_action_item(ctx, sri, &aitem);
+ if (error || !aitem)
+ return error;
+
+ if (sri->sri_agno != -1U)
+ agno = sri->sri_agno;
+ else if (sri->sri_ino != -1ULL && sri->sri_gen != -1U)
+ agno = cvt_ino_to_agno(&ctx->mnt, sri->sri_ino);
+ else
+ agno = 0;
ASSERT(agno < ctx->mnt.fsgeom.agcount);
- action_list_splice(&ctx->action_lists[agno], alist);
-}
-
-/* Run actions now and defer unfinished items for later. */
-int
-action_list_process_or_defer(
- struct scrub_ctx *ctx,
- xfs_agnumber_t agno,
- struct action_list *alist)
-{
- int ret;
-
- ret = action_list_process(ctx, -1, alist,
- XRM_REPAIR_ONLY | XRM_NOPROGRESS);
- if (ret)
- return ret;
-
- action_list_defer(ctx, agno, alist);
+ action_list_add(&ctx->action_lists[agno], aitem);
return 0;
}
diff --git a/scrub/repair.h b/scrub/repair.h
index b61bd29c8..463a3f9bf 100644
--- a/scrub/repair.h
+++ b/scrub/repair.h
@@ -12,6 +12,8 @@ struct action_list {
bool sorted;
};
+struct action_item;
+
int action_lists_alloc(size_t nr, struct action_list **listsp);
void action_lists_free(struct action_list **listsp);
@@ -25,16 +27,14 @@ static inline bool action_list_empty(const struct action_list *alist)
unsigned long long action_list_length(struct action_list *alist);
void action_list_add(struct action_list *dest, struct action_item *item);
void action_list_discard(struct action_list *alist);
-void action_list_splice(struct action_list *dest, struct action_list *src);
-void action_list_find_mustfix(struct action_list *actions,
- struct action_list *immediate_alist);
+void repair_item_mustfix(struct scrub_item *sri, struct scrub_item *fix_now);
/* Primary metadata is corrupt */
#define REPAIR_DIFFICULTY_PRIMARY (1U << 0)
/* Secondary metadata is corrupt */
#define REPAIR_DIFFICULTY_SECONDARY (1U << 1)
-unsigned int action_list_difficulty(const struct action_list *actions);
+unsigned int repair_item_difficulty(const struct scrub_item *sri);
/*
* Only ask the kernel to repair this object if the kernel directly told us it
@@ -49,11 +49,36 @@ unsigned int action_list_difficulty(const struct action_list *actions);
/* Don't call progress_add after repairing an item. */
#define XRM_NOPROGRESS (1U << 2)
-int action_list_process(struct scrub_ctx *ctx, int fd,
- struct action_list *alist, unsigned int repair_flags);
-void action_list_defer(struct scrub_ctx *ctx, xfs_agnumber_t agno,
- struct action_list *alist);
-int action_list_process_or_defer(struct scrub_ctx *ctx, xfs_agnumber_t agno,
- struct action_list *alist);
+int action_list_process(struct scrub_ctx *ctx, struct action_list *alist,
+ unsigned int repair_flags);
+int repair_item_corruption(struct scrub_ctx *ctx, struct scrub_item *sri);
+int repair_file_corruption(struct scrub_ctx *ctx, struct scrub_item *sri,
+ int override_fd);
+int repair_item(struct scrub_ctx *ctx, struct scrub_item *sri,
+ unsigned int repair_flags);
+int repair_item_to_action_item(struct scrub_ctx *ctx,
+ const struct scrub_item *sri, struct action_item **aitemp);
+int repair_item_defer(struct scrub_ctx *ctx, const struct scrub_item *sri);
+
+static inline unsigned int
+repair_item_count_needsrepair(
+ const struct scrub_item *sri)
+{
+ unsigned int scrub_type;
+ unsigned int nr = 0;
+
+ foreach_scrub_type(scrub_type)
+ if (sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY)
+ nr++;
+ return nr;
+}
+
+static inline int
+repair_item_completely(
+ struct scrub_ctx *ctx,
+ struct scrub_item *sri)
+{
+ return repair_item(ctx, sri, XRM_FINAL_WARNING | XRM_NOPROGRESS);
+}
#endif /* XFS_SCRUB_REPAIR_H_ */
diff --git a/scrub/scrub.c b/scrub/scrub.c
index e242e38ed..54f397fb9 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -217,42 +217,6 @@ _("Optimizations of %s are possible."), _(xfrog_scrubbers[i].descr));
}
}
-/* Save a scrub context for later repairs. */
-static int
-scrub_save_repair(
- struct scrub_ctx *ctx,
- struct action_list *alist,
- struct xfs_scrub_metadata *meta)
-{
- struct action_item *aitem;
-
- /* Schedule this item for later repairs. */
- aitem = malloc(sizeof(struct action_item));
- if (!aitem) {
- str_errno(ctx, _("adding item to repair list"));
- return errno;
- }
-
- memset(aitem, 0, sizeof(*aitem));
- aitem->type = meta->sm_type;
- aitem->flags = meta->sm_flags;
- switch (xfrog_scrubbers[meta->sm_type].group) {
- case XFROG_SCRUB_GROUP_AGHEADER:
- case XFROG_SCRUB_GROUP_PERAG:
- aitem->agno = meta->sm_agno;
- break;
- case XFROG_SCRUB_GROUP_INODE:
- aitem->ino = meta->sm_ino;
- aitem->gen = meta->sm_gen;
- break;
- default:
- break;
- }
-
- action_list_add(alist, aitem);
- return 0;
-}
-
/*
* Scrub a single XFS_SCRUB_TYPE_*, saving corruption reports for later.
*
@@ -272,7 +236,6 @@ scrub_meta_type(
.sm_agno = agno,
};
enum check_outcome fix;
- int ret;
background_sleep();
@@ -285,10 +248,7 @@ scrub_meta_type(
return ECANCELED;
case CHECK_REPAIR:
scrub_item_save_state(sri, type, meta.sm_flags);
- ret = scrub_save_repair(ctx, alist, &meta);
- if (ret)
- return ret;
- fallthrough;
+ return 0;
case CHECK_DONE:
scrub_item_clean_state(sri, type);
return 0;
@@ -469,7 +429,7 @@ scrub_file(
}
scrub_item_save_state(sri, type, meta.sm_flags);
- return scrub_save_repair(ctx, alist, &meta);
+ return 0;
}
/* Dump a scrub item for debugging purposes. */
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 21ea4147e..0d6825a5a 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -14,8 +14,6 @@ enum check_outcome {
CHECK_RETRY, /* repair failed, try again later */
};
-struct action_item;
-
/*
* These flags record the metadata object state that the kernel returned.
* We want to remember if the object was corrupt, if the cross-referencing
@@ -110,14 +108,4 @@ int scrub_file(struct scrub_ctx *ctx, int fd, const struct xfs_bulkstat *bstat,
unsigned int type, struct action_list *alist,
struct scrub_item *sri);
-/* Repair parameters are the scrub inputs and retry count. */
-struct action_item {
- struct list_head list;
- __u64 ino;
- __u32 type;
- __u32 flags;
- __u32 gen;
- __u32 agno;
-};
-
#endif /* XFS_SCRUB_SCRUB_H_ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/9] xfs_scrub: remove action lists from phaseX code
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
2024-07-30 1:01 ` [PATCH 1/9] xfs_scrub: track repair items by principal, not by individual repairs Darrick J. Wong
2024-07-30 1:01 ` [PATCH 2/9] xfs_scrub: use repair_item to direct repair activities Darrick J. Wong
@ 2024-07-30 1:01 ` Darrick J. Wong
2024-07-30 1:02 ` [PATCH 4/9] xfs_scrub: remove scrub_metadata_file Darrick J. Wong
` (5 subsequent siblings)
8 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:01 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Now that we track repair schedules by filesystem object (and not
individual repairs) we can get rid of all the onstack list heads and
whatnot in the phaseX code.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase1.c | 5 +----
scrub/phase2.c | 16 ++++------------
scrub/phase3.c | 19 ++++++++-----------
scrub/phase4.c | 8 ++------
scrub/phase5.c | 8 ++------
scrub/phase7.c | 4 +---
scrub/scrub.c | 37 ++++++++++++++++++++-----------------
scrub/scrub.h | 16 +++++-----------
8 files changed, 43 insertions(+), 70 deletions(-)
diff --git a/scrub/phase1.c b/scrub/phase1.c
index b1bbc694e..1e56f9fb1 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -53,7 +53,6 @@ report_to_kernel(
struct scrub_ctx *ctx)
{
struct scrub_item sri;
- struct action_list alist;
int ret;
if (!ctx->scrub_setup_succeeded || ctx->corruptions_found ||
@@ -62,8 +61,7 @@ report_to_kernel(
return 0;
scrub_item_init_fs(&sri);
- action_list_init(&alist);
- ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_HEALTHY, 0, &alist, &sri);
+ ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_HEALTHY, &sri);
if (ret)
return ret;
@@ -74,7 +72,6 @@ report_to_kernel(
if (repair_item_count_needsrepair(&sri) != 0 &&
!debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) {
str_info(ctx, _("Couldn't upload clean bill of health."), NULL);
- action_list_discard(&alist);
}
return 0;
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 26ce58180..4d4552d84 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -61,8 +61,6 @@ scan_ag_metadata(
struct scrub_item fix_now;
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
struct scan_ctl *sctl = arg;
- struct action_list alist;
- struct action_list immediate_alist;
char descr[DESCR_BUFSZ];
unsigned int difficulty;
int ret;
@@ -71,15 +69,13 @@ scan_ag_metadata(
return;
scrub_item_init_ag(&sri, agno);
- action_list_init(&alist);
- action_list_init(&immediate_alist);
snprintf(descr, DESCR_BUFSZ, _("AG %u"), agno);
/*
* First we scrub and fix the AG headers, because we need
* them to work well enough to check the AG btrees.
*/
- ret = scrub_ag_headers(ctx, agno, &alist, &sri);
+ ret = scrub_ag_headers(ctx, &sri);
if (ret)
goto err;
@@ -89,7 +85,7 @@ scan_ag_metadata(
goto err;
/* Now scrub the AG btrees. */
- ret = scrub_ag_metadata(ctx, agno, &alist, &sri);
+ ret = scrub_ag_metadata(ctx, &sri);
if (ret)
goto err;
@@ -126,7 +122,6 @@ scan_fs_metadata(
void *arg)
{
struct scrub_item sri;
- struct action_list alist;
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
struct scan_ctl *sctl = arg;
unsigned int difficulty;
@@ -136,8 +131,7 @@ scan_fs_metadata(
goto out;
scrub_item_init_fs(&sri);
- action_list_init(&alist);
- ret = scrub_fs_metadata(ctx, type, &alist, &sri);
+ ret = scrub_fs_metadata(ctx, type, &sri);
if (ret) {
sctl->aborted = true;
goto out;
@@ -172,7 +166,6 @@ phase2_func(
.aborted = false,
.rbm_done = false,
};
- struct action_list alist;
struct scrub_item sri;
const struct xfrog_scrub_descr *sc = xfrog_scrubbers;
xfs_agnumber_t agno;
@@ -196,8 +189,7 @@ phase2_func(
* If errors occur, this function will log them and return nonzero.
*/
scrub_item_init_ag(&sri, 0);
- action_list_init(&alist);
- ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_SB, 0, &alist, &sri);
+ ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_SB, &sri);
if (ret)
goto out_wq;
ret = repair_item_completely(ctx, &sri);
diff --git a/scrub/phase3.c b/scrub/phase3.c
index e602d8c7e..fa2eef4de 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -107,7 +107,6 @@ scrub_inode(
struct xfs_bulkstat *bstat,
void *arg)
{
- struct action_list alist;
struct scrub_item sri;
struct scrub_inode_ctx *ictx = arg;
struct ptcounter *icount = ictx->icount;
@@ -115,7 +114,6 @@ scrub_inode(
int error;
scrub_item_init_file(&sri, bstat);
- action_list_init(&alist);
background_sleep();
/*
@@ -146,7 +144,7 @@ scrub_inode(
fd = scrub_open_handle(handle);
/* Scrub the inode. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_INODE, &alist, &sri);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_INODE, &sri);
if (error)
goto out;
@@ -155,13 +153,13 @@ scrub_inode(
goto out;
/* Scrub all block mappings. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTD, &alist, &sri);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTD, &sri);
if (error)
goto out;
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTA, &alist, &sri);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTA, &sri);
if (error)
goto out;
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTC, &alist, &sri);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTC, &sri);
if (error)
goto out;
@@ -182,25 +180,24 @@ scrub_inode(
if (S_ISLNK(bstat->bs_mode) || !bstat->bs_mode) {
/* Check symlink contents. */
error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_SYMLINK,
- &alist, &sri);
+ &sri);
if (error)
goto out;
}
if (S_ISDIR(bstat->bs_mode) || !bstat->bs_mode) {
/* Check the directory entries. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_DIR, &alist,
- &sri);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_DIR, &sri);
if (error)
goto out;
}
/* Check all the extended attributes. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_XATTR, &alist, &sri);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_XATTR, &sri);
if (error)
goto out;
/* Check parent pointers. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_PARENT, &alist, &sri);
+ error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_PARENT, &sri);
if (error)
goto out;
diff --git a/scrub/phase4.c b/scrub/phase4.c
index 98518635b..230c559f0 100644
--- a/scrub/phase4.c
+++ b/scrub/phase4.c
@@ -129,7 +129,6 @@ phase4_func(
struct scrub_ctx *ctx)
{
struct xfs_fsop_geom fsgeom;
- struct action_list alist;
struct scrub_item sri;
int ret;
@@ -144,8 +143,7 @@ phase4_func(
* metadata. If repairs fails, we'll come back during phase 7.
*/
scrub_item_init_fs(&sri);
- action_list_init(&alist);
- ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_FSCOUNTERS, 0, &alist, &sri);
+ ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_FSCOUNTERS, &sri);
if (ret)
return ret;
@@ -160,8 +158,7 @@ phase4_func(
return ret;
if (fsgeom.sick & XFS_FSOP_GEOM_SICK_QUOTACHECK) {
- ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_QUOTACHECK, 0,
- &alist, &sri);
+ ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_QUOTACHECK, &sri);
if (ret)
return ret;
}
@@ -170,7 +167,6 @@ phase4_func(
ret = repair_item_corruption(ctx, &sri);
if (ret)
return ret;
- action_list_discard(&alist);
ret = repair_everything(ctx);
if (ret)
diff --git a/scrub/phase5.c b/scrub/phase5.c
index 79bfea8f6..6c9a518db 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -386,7 +386,6 @@ check_fs_label(
struct fs_scan_item {
struct scrub_item sri;
- struct action_list alist;
bool *abortedp;
unsigned int scrub_type;
};
@@ -413,16 +412,14 @@ fs_scan_worker(
nanosleep(&tv, NULL);
}
- ret = scrub_meta_type(ctx, item->scrub_type, 0, &item->alist,
- &item->sri);
+ ret = scrub_meta_type(ctx, item->scrub_type, &item->sri);
if (ret) {
str_liberror(ctx, ret, _("checking fs scan metadata"));
*item->abortedp = true;
goto out;
}
- ret = action_list_process(ctx, &item->alist,
- XRM_FINAL_WARNING | XRM_NOPROGRESS);
+ ret = repair_item_completely(ctx, &item->sri);
if (ret) {
str_liberror(ctx, ret, _("repairing fs scan metadata"));
*item->abortedp = true;
@@ -453,7 +450,6 @@ queue_fs_scan(
return ret;
}
scrub_item_init_fs(&item->sri);
- action_list_init(&item->alist);
item->scrub_type = scrub_type;
item->abortedp = abortedp;
diff --git a/scrub/phase7.c b/scrub/phase7.c
index 404bfb822..02da6b42b 100644
--- a/scrub/phase7.c
+++ b/scrub/phase7.c
@@ -100,7 +100,6 @@ phase7_func(
{
struct summary_counts totalcount = {0};
struct scrub_item sri;
- struct action_list alist;
struct ptvar *ptvar;
unsigned long long used_data;
unsigned long long used_rt;
@@ -119,8 +118,7 @@ phase7_func(
/* Check and fix the summary metadata. */
scrub_item_init_fs(&sri);
- action_list_init(&alist);
- error = scrub_summary_metadata(ctx, &alist, &sri);
+ error = scrub_summary_metadata(ctx, &sri);
if (error)
return error;
error = repair_item_completely(ctx, &sri);
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 54f397fb9..ca3eea42e 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -219,6 +219,7 @@ _("Optimizations of %s are possible."), _(xfrog_scrubbers[i].descr));
/*
* Scrub a single XFS_SCRUB_TYPE_*, saving corruption reports for later.
+ * Do not call this function to repair file metadata.
*
* Returns 0 for success. If errors occur, this function will log them and
* return a positive error code.
@@ -227,18 +228,29 @@ int
scrub_meta_type(
struct scrub_ctx *ctx,
unsigned int type,
- xfs_agnumber_t agno,
- struct action_list *alist,
struct scrub_item *sri)
{
struct xfs_scrub_metadata meta = {
.sm_type = type,
- .sm_agno = agno,
};
enum check_outcome fix;
background_sleep();
+ switch (xfrog_scrubbers[type].group) {
+ case XFROG_SCRUB_GROUP_AGHEADER:
+ case XFROG_SCRUB_GROUP_PERAG:
+ meta.sm_agno = sri->sri_agno;
+ break;
+ case XFROG_SCRUB_GROUP_FS:
+ case XFROG_SCRUB_GROUP_SUMMARY:
+ case XFROG_SCRUB_GROUP_NONE:
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
/* Check the item. */
fix = xfs_check_metadata(ctx, &ctx->mnt, &meta, false);
progress_add(1);
@@ -267,8 +279,6 @@ static bool
scrub_group(
struct scrub_ctx *ctx,
enum xfrog_scrub_group group,
- xfs_agnumber_t agno,
- struct action_list *alist,
struct scrub_item *sri)
{
const struct xfrog_scrub_descr *sc;
@@ -281,7 +291,7 @@ scrub_group(
if (sc->group != group)
continue;
- ret = scrub_meta_type(ctx, type, agno, alist, sri);
+ ret = scrub_meta_type(ctx, type, sri);
if (ret)
return ret;
}
@@ -293,22 +303,18 @@ scrub_group(
int
scrub_ag_headers(
struct scrub_ctx *ctx,
- xfs_agnumber_t agno,
- struct action_list *alist,
struct scrub_item *sri)
{
- return scrub_group(ctx, XFROG_SCRUB_GROUP_AGHEADER, agno, alist, sri);
+ return scrub_group(ctx, XFROG_SCRUB_GROUP_AGHEADER, sri);
}
/* Scrub each AG's metadata btrees. */
int
scrub_ag_metadata(
struct scrub_ctx *ctx,
- xfs_agnumber_t agno,
- struct action_list *alist,
struct scrub_item *sri)
{
- return scrub_group(ctx, XFROG_SCRUB_GROUP_PERAG, agno, alist, sri);
+ return scrub_group(ctx, XFROG_SCRUB_GROUP_PERAG, sri);
}
/* Scrub whole-filesystem metadata. */
@@ -316,22 +322,20 @@ int
scrub_fs_metadata(
struct scrub_ctx *ctx,
unsigned int type,
- struct action_list *alist,
struct scrub_item *sri)
{
ASSERT(xfrog_scrubbers[type].group == XFROG_SCRUB_GROUP_FS);
- return scrub_meta_type(ctx, type, 0, alist, sri);
+ return scrub_meta_type(ctx, type, sri);
}
/* Scrub all FS summary metadata. */
int
scrub_summary_metadata(
struct scrub_ctx *ctx,
- struct action_list *alist,
struct scrub_item *sri)
{
- return scrub_group(ctx, XFROG_SCRUB_GROUP_SUMMARY, 0, alist, sri);
+ return scrub_group(ctx, XFROG_SCRUB_GROUP_SUMMARY, sri);
}
/* How many items do we have to check? */
@@ -393,7 +397,6 @@ scrub_file(
int fd,
const struct xfs_bulkstat *bstat,
unsigned int type,
- struct action_list *alist,
struct scrub_item *sri)
{
struct xfs_scrub_metadata meta = {0};
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 0d6825a5a..b2e91efac 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -80,18 +80,13 @@ void scrub_item_dump(struct scrub_item *sri, unsigned int group_mask,
const char *tag);
void scrub_report_preen_triggers(struct scrub_ctx *ctx);
-int scrub_ag_headers(struct scrub_ctx *ctx, xfs_agnumber_t agno,
- struct action_list *alist, struct scrub_item *sri);
-int scrub_ag_metadata(struct scrub_ctx *ctx, xfs_agnumber_t agno,
- struct action_list *alist, struct scrub_item *sri);
+int scrub_ag_headers(struct scrub_ctx *ctx, struct scrub_item *sri);
+int scrub_ag_metadata(struct scrub_ctx *ctx, struct scrub_item *sri);
int scrub_fs_metadata(struct scrub_ctx *ctx, unsigned int scrub_type,
- struct action_list *alist, struct scrub_item *sri);
-int scrub_iscan_metadata(struct scrub_ctx *ctx, struct action_list *alist,
- struct scrub_item *sri);
-int scrub_summary_metadata(struct scrub_ctx *ctx, struct action_list *alist,
struct scrub_item *sri);
+int scrub_iscan_metadata(struct scrub_ctx *ctx, struct scrub_item *sri);
+int scrub_summary_metadata(struct scrub_ctx *ctx, struct scrub_item *sri);
int scrub_meta_type(struct scrub_ctx *ctx, unsigned int type,
- xfs_agnumber_t agno, struct action_list *alist,
struct scrub_item *sri);
bool can_scrub_fs_metadata(struct scrub_ctx *ctx);
@@ -105,7 +100,6 @@ bool can_repair(struct scrub_ctx *ctx);
bool can_force_rebuild(struct scrub_ctx *ctx);
int scrub_file(struct scrub_ctx *ctx, int fd, const struct xfs_bulkstat *bstat,
- unsigned int type, struct action_list *alist,
- struct scrub_item *sri);
+ unsigned int type, struct scrub_item *sri);
#endif /* XFS_SCRUB_SCRUB_H_ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/9] xfs_scrub: remove scrub_metadata_file
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:01 ` [PATCH 3/9] xfs_scrub: remove action lists from phaseX code Darrick J. Wong
@ 2024-07-30 1:02 ` Darrick J. Wong
2024-07-30 1:02 ` [PATCH 5/9] xfs_scrub: boost the repair priority of dependencies of damaged items Darrick J. Wong
` (4 subsequent siblings)
8 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:02 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Collapse this function with scrub_meta_type.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase2.c | 2 +-
scrub/scrub.c | 12 ------------
scrub/scrub.h | 2 --
3 files changed, 1 insertion(+), 15 deletions(-)
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 4d4552d84..4d90291ed 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -131,7 +131,7 @@ scan_fs_metadata(
goto out;
scrub_item_init_fs(&sri);
- ret = scrub_fs_metadata(ctx, type, &sri);
+ ret = scrub_meta_type(ctx, type, &sri);
if (ret) {
sctl->aborted = true;
goto out;
diff --git a/scrub/scrub.c b/scrub/scrub.c
index ca3eea42e..5c14ed209 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -317,18 +317,6 @@ scrub_ag_metadata(
return scrub_group(ctx, XFROG_SCRUB_GROUP_PERAG, sri);
}
-/* Scrub whole-filesystem metadata. */
-int
-scrub_fs_metadata(
- struct scrub_ctx *ctx,
- unsigned int type,
- struct scrub_item *sri)
-{
- ASSERT(xfrog_scrubbers[type].group == XFROG_SCRUB_GROUP_FS);
-
- return scrub_meta_type(ctx, type, sri);
-}
-
/* Scrub all FS summary metadata. */
int
scrub_summary_metadata(
diff --git a/scrub/scrub.h b/scrub/scrub.h
index b2e91efac..874e1fe13 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -82,8 +82,6 @@ void scrub_item_dump(struct scrub_item *sri, unsigned int group_mask,
void scrub_report_preen_triggers(struct scrub_ctx *ctx);
int scrub_ag_headers(struct scrub_ctx *ctx, struct scrub_item *sri);
int scrub_ag_metadata(struct scrub_ctx *ctx, struct scrub_item *sri);
-int scrub_fs_metadata(struct scrub_ctx *ctx, unsigned int scrub_type,
- struct scrub_item *sri);
int scrub_iscan_metadata(struct scrub_ctx *ctx, struct scrub_item *sri);
int scrub_summary_metadata(struct scrub_ctx *ctx, struct scrub_item *sri);
int scrub_meta_type(struct scrub_ctx *ctx, unsigned int type,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/9] xfs_scrub: boost the repair priority of dependencies of damaged items
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:02 ` [PATCH 4/9] xfs_scrub: remove scrub_metadata_file Darrick J. Wong
@ 2024-07-30 1:02 ` Darrick J. Wong
2024-07-30 1:02 ` [PATCH 6/9] xfs_scrub: clean up repair_item_difficulty a little Darrick J. Wong
` (3 subsequent siblings)
8 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:02 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
In XFS, certain types of metadata objects depend on the correctness of
lower level metadata objects. For example, directory blocks are stored
in the data fork of directory files, which means that any issues with
the inode core and the data fork should be dealt with before we try to
repair a directory.
xfs_scrub prioritises repairs by the severity of what the kernel scrub
function reports -- anything directly observed to be corrupt get
repaired first, then anything that had trouble with cross referencing,
and finally anything that was correct but could be further optimised.
Returning to the above example, if a directory data fork mapping offset
is off by a bit flip, scrub will mark that as failing cross referencing,
but it'll mark the directory as corrupt. Repair should check out the
mapping problem before it tackles the directory.
Do this by embedding a dependency table and using it to boost the
priority of the repair_item fields as needed.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libfrog/scrub.c | 1
scrub/repair.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++-
scrub/scrub.h | 12 ++++++
scrub/scrub_private.h | 8 ++++
4 files changed, 117 insertions(+), 3 deletions(-)
diff --git a/libfrog/scrub.c b/libfrog/scrub.c
index 1df2965fe..baaa4b4d9 100644
--- a/libfrog/scrub.c
+++ b/libfrog/scrub.c
@@ -150,6 +150,7 @@ const struct xfrog_scrub_descr xfrog_scrubbers[XFS_SCRUB_TYPE_NR] = {
.group = XFROG_SCRUB_GROUP_NONE,
},
};
+#undef DEP
/* Invoke the scrub ioctl. Returns zero or negative error code. */
int
diff --git a/scrub/repair.c b/scrub/repair.c
index 6e09c592e..5f13f3c7a 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -22,6 +22,29 @@
/* General repair routines. */
+/*
+ * Bitmap showing the correctness dependencies between scrub types for repairs.
+ * There are no edges between AG btrees and AG headers because we can't mount
+ * the filesystem if the btree root pointers in the AG headers are wrong.
+ * Dependencies cannot cross scrub groups.
+ */
+#define DEP(x) (1U << (x))
+static const unsigned int repair_deps[XFS_SCRUB_TYPE_NR] = {
+ [XFS_SCRUB_TYPE_BMBTD] = DEP(XFS_SCRUB_TYPE_INODE),
+ [XFS_SCRUB_TYPE_BMBTA] = DEP(XFS_SCRUB_TYPE_INODE),
+ [XFS_SCRUB_TYPE_BMBTC] = DEP(XFS_SCRUB_TYPE_INODE),
+ [XFS_SCRUB_TYPE_DIR] = DEP(XFS_SCRUB_TYPE_BMBTD),
+ [XFS_SCRUB_TYPE_XATTR] = DEP(XFS_SCRUB_TYPE_BMBTA),
+ [XFS_SCRUB_TYPE_SYMLINK] = DEP(XFS_SCRUB_TYPE_BMBTD),
+ [XFS_SCRUB_TYPE_PARENT] = DEP(XFS_SCRUB_TYPE_DIR) |
+ DEP(XFS_SCRUB_TYPE_XATTR),
+ [XFS_SCRUB_TYPE_QUOTACHECK] = DEP(XFS_SCRUB_TYPE_UQUOTA) |
+ DEP(XFS_SCRUB_TYPE_GQUOTA) |
+ DEP(XFS_SCRUB_TYPE_PQUOTA),
+ [XFS_SCRUB_TYPE_RTSUM] = DEP(XFS_SCRUB_TYPE_RTBITMAP),
+};
+#undef DEP
+
/* Repair some metadata. */
static enum check_outcome
xfs_repair_metadata(
@@ -34,8 +57,16 @@ xfs_repair_metadata(
struct xfs_scrub_metadata meta = { 0 };
struct xfs_scrub_metadata oldm;
DEFINE_DESCR(dsc, ctx, format_scrub_descr);
+ bool repair_only;
int error;
+ /*
+ * If the caller boosted the priority of this scrub type on behalf of a
+ * higher level repair by setting IFLAG_REPAIR, turn off REPAIR_ONLY.
+ */
+ repair_only = (repair_flags & XRM_REPAIR_ONLY) &&
+ scrub_item_type_boosted(sri, scrub_type);
+
assert(scrub_type < XFS_SCRUB_TYPE_NR);
assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
meta.sm_type = scrub_type;
@@ -55,7 +86,7 @@ xfs_repair_metadata(
break;
}
- if (!is_corrupt(&meta) && (repair_flags & XRM_REPAIR_ONLY))
+ if (!is_corrupt(&meta) && repair_only)
return CHECK_RETRY;
memcpy(&oldm, &meta, sizeof(oldm));
@@ -223,6 +254,60 @@ struct action_item {
struct scrub_item sri;
};
+/*
+ * The operation of higher level metadata objects depends on the correctness of
+ * lower level metadata objects. This means that if X depends on Y, we must
+ * investigate and correct all the observed issues with Y before we try to make
+ * a correction to X. For all scheduled repair activity on X, boost the
+ * priority of repairs on all the Ys to ensure this correctness.
+ */
+static void
+repair_item_boost_priorities(
+ struct scrub_item *sri)
+{
+ unsigned int scrub_type;
+
+ foreach_scrub_type(scrub_type) {
+ unsigned int dep_mask = repair_deps[scrub_type];
+ unsigned int b;
+
+ if (repair_item_count_needsrepair(sri) == 0 || !dep_mask)
+ continue;
+
+ /*
+ * Check if the repairs for this scrub type depend on any other
+ * scrub types that have been flagged with cross-referencing
+ * errors and are not already tagged for the highest priority
+ * repair (SCRUB_ITEM_CORRUPT). If so, boost the priority of
+ * that scrub type (via SCRUB_ITEM_BOOST_REPAIR) so that any
+ * problems with the dependencies will (hopefully) be fixed
+ * before we start repairs on this scrub type.
+ *
+ * So far in the history of xfs_scrub we have maintained that
+ * lower numbered scrub types do not depend on higher numbered
+ * scrub types, so we need only process the bit mask once.
+ */
+ for (b = 0; b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
+ if (!dep_mask)
+ break;
+ if (!(dep_mask & 1))
+ continue;
+ if (!(sri->sri_state[b] & SCRUB_ITEM_REPAIR_XREF))
+ continue;
+ if (sri->sri_state[b] & SCRUB_ITEM_CORRUPT)
+ continue;
+ sri->sri_state[b] |= SCRUB_ITEM_BOOST_REPAIR;
+ }
+ }
+}
+
+/*
+ * These are the scrub item state bits that must be copied when scheduling
+ * a (per-AG) scrub type for immediate repairs. The original state tracking
+ * bits are left untouched to force a rescan in phase 4.
+ */
+#define MUSTFIX_STATES (SCRUB_ITEM_CORRUPT | \
+ SCRUB_ITEM_BOOST_REPAIR)
/*
* Figure out which AG metadata must be fixed before we can move on
* to the inode scan.
@@ -235,17 +320,21 @@ repair_item_mustfix(
unsigned int scrub_type;
assert(sri->sri_agno != -1U);
+ repair_item_boost_priorities(sri);
scrub_item_init_ag(fix_now, sri->sri_agno);
foreach_scrub_type(scrub_type) {
- if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_CORRUPT))
+ unsigned int state;
+
+ state = sri->sri_state[scrub_type] & MUSTFIX_STATES;
+ if (!state)
continue;
switch (scrub_type) {
case XFS_SCRUB_TYPE_AGI:
case XFS_SCRUB_TYPE_FINOBT:
case XFS_SCRUB_TYPE_INOBT:
- fix_now->sri_state[scrub_type] |= SCRUB_ITEM_CORRUPT;
+ fix_now->sri_state[scrub_type] = state;
break;
}
}
@@ -479,6 +568,8 @@ repair_file_corruption(
struct scrub_item *sri,
int override_fd)
{
+ repair_item_boost_priorities(sri);
+
return repair_item_class(ctx, sri, override_fd, SCRUB_ITEM_CORRUPT,
XRM_REPAIR_ONLY | XRM_NOPROGRESS);
}
@@ -495,6 +586,8 @@ repair_item(
{
int ret;
+ repair_item_boost_priorities(sri);
+
ret = repair_item_class(ctx, sri, -1, SCRUB_ITEM_CORRUPT, flags);
if (ret)
return ret;
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 874e1fe13..f22a95262 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -14,6 +14,14 @@ enum check_outcome {
CHECK_RETRY, /* repair failed, try again later */
};
+/*
+ * This flag boosts the repair priority of a scrub item when a dependent scrub
+ * item is scheduled for repair. Use a separate flag to preserve the
+ * corruption state that we got from the kernel. Priority boost is cleared the
+ * next time xfs_repair_metadata is called.
+ */
+#define SCRUB_ITEM_BOOST_REPAIR (1 << 0)
+
/*
* These flags record the metadata object state that the kernel returned.
* We want to remember if the object was corrupt, if the cross-referencing
@@ -31,6 +39,10 @@ enum check_outcome {
SCRUB_ITEM_XFAIL | \
SCRUB_ITEM_XCORRUPT)
+/* Cross-referencing failures only. */
+#define SCRUB_ITEM_REPAIR_XREF (SCRUB_ITEM_XFAIL | \
+ SCRUB_ITEM_XCORRUPT)
+
struct scrub_item {
/*
* Information we need to call the scrub and repair ioctls. Per-AG
diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h
index 090efb54c..08b9130cb 100644
--- a/scrub/scrub_private.h
+++ b/scrub/scrub_private.h
@@ -71,4 +71,12 @@ scrub_item_clean_state(
sri->sri_state[scrub_type] = 0;
}
+static inline bool
+scrub_item_type_boosted(
+ struct scrub_item *sri,
+ unsigned int scrub_type)
+{
+ return sri->sri_state[scrub_type] & SCRUB_ITEM_BOOST_REPAIR;
+}
+
#endif /* XFS_SCRUB_SCRUB_PRIVATE_H_ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 6/9] xfs_scrub: clean up repair_item_difficulty a little
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 1:02 ` [PATCH 5/9] xfs_scrub: boost the repair priority of dependencies of damaged items Darrick J. Wong
@ 2024-07-30 1:02 ` Darrick J. Wong
2024-07-30 1:02 ` [PATCH 7/9] xfs_scrub: check dependencies of a scrub type before repairing Darrick J. Wong
` (2 subsequent siblings)
8 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:02 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Document the flags handling in repair_item_difficulty.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/scrub/repair.c b/scrub/repair.c
index 5f13f3c7a..d4521f50c 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -340,6 +340,15 @@ repair_item_mustfix(
}
}
+/*
+ * These scrub item states correspond to metadata that is inconsistent in some
+ * way and must be repaired. If too many metadata objects share these states,
+ * this can make repairs difficult.
+ */
+#define HARDREPAIR_STATES (SCRUB_ITEM_CORRUPT | \
+ SCRUB_ITEM_XCORRUPT | \
+ SCRUB_ITEM_XFAIL)
+
/* Determine if primary or secondary metadata are inconsistent. */
unsigned int
repair_item_difficulty(
@@ -349,9 +358,10 @@ repair_item_difficulty(
unsigned int ret = 0;
foreach_scrub_type(scrub_type) {
- if (!(sri->sri_state[scrub_type] & (XFS_SCRUB_OFLAG_CORRUPT |
- XFS_SCRUB_OFLAG_XCORRUPT |
- XFS_SCRUB_OFLAG_XFAIL)))
+ unsigned int state;
+
+ state = sri->sri_state[scrub_type] & HARDREPAIR_STATES;
+ if (!state)
continue;
switch (scrub_type) {
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 7/9] xfs_scrub: check dependencies of a scrub type before repairing
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
` (5 preceding siblings ...)
2024-07-30 1:02 ` [PATCH 6/9] xfs_scrub: clean up repair_item_difficulty a little Darrick J. Wong
@ 2024-07-30 1:02 ` Darrick J. Wong
2024-07-30 1:03 ` [PATCH 8/9] xfs_scrub: retry incomplete repairs Darrick J. Wong
2024-07-30 1:03 ` [PATCH 9/9] xfs_scrub: remove unused action_list fields Darrick J. Wong
8 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:02 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Now that we have a map of a scrub type to its dependent scrub types, use
this information to avoid trying to fix higher level metadata before the
lower levels have passed.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 32 ++++++++++++++++++++++++++++++++
scrub/scrub.h | 5 +++++
2 files changed, 37 insertions(+)
diff --git a/scrub/repair.c b/scrub/repair.c
index d4521f50c..9b4b5d016 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -497,6 +497,29 @@ action_list_process(
return ret;
}
+/* Decide if the dependent scrub types of the given scrub type are ok. */
+static bool
+repair_item_dependencies_ok(
+ const struct scrub_item *sri,
+ unsigned int scrub_type)
+{
+ unsigned int dep_mask = repair_deps[scrub_type];
+ unsigned int b;
+
+ for (b = 0; dep_mask && b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
+ if (!(dep_mask & 1))
+ continue;
+ /*
+ * If this lower level object also needs repair, we can't fix
+ * the higher level item.
+ */
+ if (sri->sri_state[b] & SCRUB_ITEM_NEEDSREPAIR)
+ return false;
+ }
+
+ return true;
+}
+
/*
* For a given filesystem object, perform all repairs of a given class
* (corrupt, xcorrupt, xfail, preen) if the repair item says it's needed.
@@ -536,6 +559,15 @@ repair_item_class(
if (!(sri->sri_state[scrub_type] & repair_mask))
continue;
+ /*
+ * Don't try to repair higher level items if their lower-level
+ * dependencies haven't been verified, unless this is our last
+ * chance to fix things without complaint.
+ */
+ if (!(flags & XRM_FINAL_WARNING) &&
+ !repair_item_dependencies_ok(sri, scrub_type))
+ continue;
+
fix = xfs_repair_metadata(ctx, xfdp, scrub_type, sri, flags);
switch (fix) {
case CHECK_DONE:
diff --git a/scrub/scrub.h b/scrub/scrub.h
index f22a95262..3ae0bfd29 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -43,6 +43,11 @@ enum check_outcome {
#define SCRUB_ITEM_REPAIR_XREF (SCRUB_ITEM_XFAIL | \
SCRUB_ITEM_XCORRUPT)
+/* Mask of bits signalling that a piece of metadata requires attention. */
+#define SCRUB_ITEM_NEEDSREPAIR (SCRUB_ITEM_CORRUPT | \
+ SCRUB_ITEM_XFAIL | \
+ SCRUB_ITEM_XCORRUPT)
+
struct scrub_item {
/*
* Information we need to call the scrub and repair ioctls. Per-AG
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 8/9] xfs_scrub: retry incomplete repairs
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
` (6 preceding siblings ...)
2024-07-30 1:02 ` [PATCH 7/9] xfs_scrub: check dependencies of a scrub type before repairing Darrick J. Wong
@ 2024-07-30 1:03 ` Darrick J. Wong
2024-07-30 1:03 ` [PATCH 9/9] xfs_scrub: remove unused action_list fields Darrick J. Wong
8 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:03 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If a repair says it didn't do anything on account of not being able to
complete a scan of the metadata, retry the repair a few times; if even
that doesn't work, we can delay it to phase 4.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 15 ++++++++++++++-
scrub/scrub.c | 3 +--
scrub/scrub_private.h | 10 ++++++++++
3 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/scrub/repair.c b/scrub/repair.c
index 9b4b5d016..2b863bb41 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -58,6 +58,7 @@ xfs_repair_metadata(
struct xfs_scrub_metadata oldm;
DEFINE_DESCR(dsc, ctx, format_scrub_descr);
bool repair_only;
+ unsigned int tries = 0;
int error;
/*
@@ -99,6 +100,7 @@ xfs_repair_metadata(
str_info(ctx, descr_render(&dsc),
_("Attempting optimization."));
+retry:
error = -xfrog_scrub_metadata(xfdp, &meta);
switch (error) {
case 0:
@@ -179,9 +181,20 @@ _("Read-only filesystem; cannot make changes."));
return CHECK_DONE;
}
+ /*
+ * If the kernel says the repair was incomplete or that there was a
+ * cross-referencing discrepancy but no obvious corruption, we'll try
+ * the repair again, just in case the fs was busy. Only retry so many
+ * times.
+ */
+ if (want_retry(&meta) && tries < 10) {
+ tries++;
+ goto retry;
+ }
+
if (repair_flags & XRM_FINAL_WARNING)
scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
- if (needs_repair(&meta)) {
+ if (needs_repair(&meta) || is_incomplete(&meta)) {
/*
* Still broken; if we've been told not to complain then we
* just requeue this and try again later. Otherwise we
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 5c14ed209..5fc549f97 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -137,8 +137,7 @@ _("Filesystem is shut down, aborting."));
* we'll try the scan again, just in case the fs was busy.
* Only retry so many times.
*/
- if (tries < 10 && (is_incomplete(meta) ||
- (xref_disagrees(meta) && !is_corrupt(meta)))) {
+ if (want_retry(meta) && tries < 10) {
tries++;
goto retry;
}
diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h
index 08b9130cb..53372e1f3 100644
--- a/scrub/scrub_private.h
+++ b/scrub/scrub_private.h
@@ -49,6 +49,16 @@ static inline bool needs_repair(struct xfs_scrub_metadata *sm)
return is_corrupt(sm) || xref_disagrees(sm);
}
+/*
+ * We want to retry an operation if the kernel says it couldn't complete the
+ * scan/repair; or if there were cross-referencing problems but the object was
+ * not obviously corrupt.
+ */
+static inline bool want_retry(struct xfs_scrub_metadata *sm)
+{
+ return is_incomplete(sm) || (xref_disagrees(sm) && !is_corrupt(sm));
+}
+
void scrub_warn_incomplete_scrub(struct scrub_ctx *ctx, struct descr *dsc,
struct xfs_scrub_metadata *meta);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 9/9] xfs_scrub: remove unused action_list fields
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
` (7 preceding siblings ...)
2024-07-30 1:03 ` [PATCH 8/9] xfs_scrub: retry incomplete repairs Darrick J. Wong
@ 2024-07-30 1:03 ` Darrick J. Wong
8 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:03 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Remove some fields since we don't need them anymore.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 5 -----
scrub/repair.h | 2 --
2 files changed, 7 deletions(-)
diff --git a/scrub/repair.c b/scrub/repair.c
index 2b863bb41..a3a8fb311 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -432,7 +432,6 @@ action_list_discard(
struct action_item *n;
list_for_each_entry_safe(aitem, n, &alist->list, list) {
- alist->nr--;
list_del(&aitem->list);
free(aitem);
}
@@ -453,8 +452,6 @@ action_list_init(
struct action_list *alist)
{
INIT_LIST_HEAD(&alist->list);
- alist->nr = 0;
- alist->sorted = false;
}
/* Number of pending repairs in this list. */
@@ -478,8 +475,6 @@ action_list_add(
struct action_item *aitem)
{
list_add_tail(&aitem->list, &alist->list);
- alist->nr++;
- alist->sorted = false;
}
/* Repair everything on this list. */
diff --git a/scrub/repair.h b/scrub/repair.h
index 463a3f9bf..a38cdd5e6 100644
--- a/scrub/repair.h
+++ b/scrub/repair.h
@@ -8,8 +8,6 @@
struct action_list {
struct list_head list;
- unsigned long long nr;
- bool sorted;
};
struct action_item;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/5] xfs_scrub: start tracking scrub state in scrub_item
2024-07-30 0:18 ` [PATCHSET v30.9 09/23] xfs_scrub: use scrub_item to track check progress Darrick J. Wong
@ 2024-07-30 1:03 ` Darrick J. Wong
2024-07-30 1:03 ` [PATCH 2/5] xfs_scrub: remove enum check_outcome Darrick J. Wong
` (3 subsequent siblings)
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:03 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Start using the scrub_item to track which metadata objects need
checking by adding a new flag to the scrub_item state set.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase1.c | 3 +
scrub/phase2.c | 12 +++--
scrub/phase3.c | 41 +++++-----------
scrub/phase4.c | 16 +++---
scrub/phase5.c | 5 +-
scrub/phase7.c | 5 ++
scrub/scrub.c | 147 +++++++++++++++++++-------------------------------------
scrub/scrub.h | 28 ++++++++---
8 files changed, 108 insertions(+), 149 deletions(-)
diff --git a/scrub/phase1.c b/scrub/phase1.c
index 1e56f9fb1..60a8db572 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -61,7 +61,8 @@ report_to_kernel(
return 0;
scrub_item_init_fs(&sri);
- ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_HEALTHY, &sri);
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_HEALTHY);
+ ret = scrub_item_check(ctx, &sri);
if (ret)
return ret;
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 4d90291ed..79b33dd04 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -75,7 +75,8 @@ scan_ag_metadata(
* First we scrub and fix the AG headers, because we need
* them to work well enough to check the AG btrees.
*/
- ret = scrub_ag_headers(ctx, &sri);
+ scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_AGHEADER);
+ ret = scrub_item_check(ctx, &sri);
if (ret)
goto err;
@@ -85,7 +86,8 @@ scan_ag_metadata(
goto err;
/* Now scrub the AG btrees. */
- ret = scrub_ag_metadata(ctx, &sri);
+ scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_PERAG);
+ ret = scrub_item_check(ctx, &sri);
if (ret)
goto err;
@@ -131,7 +133,8 @@ scan_fs_metadata(
goto out;
scrub_item_init_fs(&sri);
- ret = scrub_meta_type(ctx, type, &sri);
+ scrub_item_schedule(&sri, type);
+ ret = scrub_item_check(ctx, &sri);
if (ret) {
sctl->aborted = true;
goto out;
@@ -189,7 +192,8 @@ phase2_func(
* If errors occur, this function will log them and return nonzero.
*/
scrub_item_init_ag(&sri, 0);
- ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_SB, &sri);
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_SB);
+ ret = scrub_item_check(ctx, &sri);
if (ret)
goto out_wq;
ret = repair_item_completely(ctx, &sri);
diff --git a/scrub/phase3.c b/scrub/phase3.c
index fa2eef4de..09347c977 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -144,7 +144,8 @@ scrub_inode(
fd = scrub_open_handle(handle);
/* Scrub the inode. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_INODE, &sri);
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_INODE);
+ error = scrub_item_check_file(ctx, &sri, fd);
if (error)
goto out;
@@ -153,13 +154,10 @@ scrub_inode(
goto out;
/* Scrub all block mappings. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTD, &sri);
- if (error)
- goto out;
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTA, &sri);
- if (error)
- goto out;
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_BMBTC, &sri);
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_BMBTD);
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_BMBTA);
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_BMBTC);
+ error = scrub_item_check_file(ctx, &sri, fd);
if (error)
goto out;
@@ -177,27 +175,14 @@ scrub_inode(
* content scrubbers. Better to have them return -ENOENT than miss
* some coverage.
*/
- if (S_ISLNK(bstat->bs_mode) || !bstat->bs_mode) {
- /* Check symlink contents. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_SYMLINK,
- &sri);
- if (error)
- goto out;
- }
- if (S_ISDIR(bstat->bs_mode) || !bstat->bs_mode) {
- /* Check the directory entries. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_DIR, &sri);
- if (error)
- goto out;
- }
+ if (S_ISLNK(bstat->bs_mode) || !bstat->bs_mode)
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_SYMLINK);
+ if (S_ISDIR(bstat->bs_mode) || !bstat->bs_mode)
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_DIR);
- /* Check all the extended attributes. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_XATTR, &sri);
- if (error)
- goto out;
-
- /* Check parent pointers. */
- error = scrub_file(ctx, fd, bstat, XFS_SCRUB_TYPE_PARENT, &sri);
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_XATTR);
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_PARENT);
+ error = scrub_item_check_file(ctx, &sri, fd);
if (error)
goto out;
diff --git a/scrub/phase4.c b/scrub/phase4.c
index 230c559f0..3c51b38a5 100644
--- a/scrub/phase4.c
+++ b/scrub/phase4.c
@@ -143,9 +143,7 @@ phase4_func(
* metadata. If repairs fails, we'll come back during phase 7.
*/
scrub_item_init_fs(&sri);
- ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_FSCOUNTERS, &sri);
- if (ret)
- return ret;
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_FSCOUNTERS);
/*
* Repair possibly bad quota counts before starting other repairs,
@@ -157,13 +155,13 @@ phase4_func(
if (ret)
return ret;
- if (fsgeom.sick & XFS_FSOP_GEOM_SICK_QUOTACHECK) {
- ret = scrub_meta_type(ctx, XFS_SCRUB_TYPE_QUOTACHECK, &sri);
- if (ret)
- return ret;
- }
+ if (fsgeom.sick & XFS_FSOP_GEOM_SICK_QUOTACHECK)
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_QUOTACHECK);
- /* Repair counters before starting on the rest. */
+ /* Check and repair counters before starting on the rest. */
+ ret = scrub_item_check(ctx, &sri);
+ if (ret)
+ return ret;
ret = repair_item_corruption(ctx, &sri);
if (ret)
return ret;
diff --git a/scrub/phase5.c b/scrub/phase5.c
index 6c9a518db..0df8c46e9 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -387,7 +387,6 @@ check_fs_label(
struct fs_scan_item {
struct scrub_item sri;
bool *abortedp;
- unsigned int scrub_type;
};
/* Run one full-fs scan scrubber in this thread. */
@@ -412,7 +411,7 @@ fs_scan_worker(
nanosleep(&tv, NULL);
}
- ret = scrub_meta_type(ctx, item->scrub_type, &item->sri);
+ ret = scrub_item_check(ctx, &item->sri);
if (ret) {
str_liberror(ctx, ret, _("checking fs scan metadata"));
*item->abortedp = true;
@@ -450,7 +449,7 @@ queue_fs_scan(
return ret;
}
scrub_item_init_fs(&item->sri);
- item->scrub_type = scrub_type;
+ scrub_item_schedule(&item->sri, scrub_type);
item->abortedp = abortedp;
ret = -workqueue_add(wq, fs_scan_worker, nr, item);
diff --git a/scrub/phase7.c b/scrub/phase7.c
index 02da6b42b..cd4501f72 100644
--- a/scrub/phase7.c
+++ b/scrub/phase7.c
@@ -10,6 +10,8 @@
#include <linux/fsmap.h>
#include "libfrog/paths.h"
#include "libfrog/ptvar.h"
+#include "libfrog/fsgeom.h"
+#include "libfrog/scrub.h"
#include "list.h"
#include "xfs_scrub.h"
#include "common.h"
@@ -118,7 +120,8 @@ phase7_func(
/* Check and fix the summary metadata. */
scrub_item_init_fs(&sri);
- error = scrub_summary_metadata(ctx, &sri);
+ scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_SUMMARY);
+ error = scrub_item_check(ctx, &sri);
if (error)
return error;
error = repair_item_completely(ctx, &sri);
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 5fc549f97..5aa36a964 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -86,9 +86,11 @@ xfs_check_metadata(
bool is_inode)
{
DEFINE_DESCR(dsc, ctx, format_scrub_descr);
+ enum xfrog_scrub_group group;
unsigned int tries = 0;
int error;
+ group = xfrog_scrubbers[meta->sm_type].group;
assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
assert(meta->sm_type < XFS_SCRUB_TYPE_NR);
descr_set(&dsc, meta);
@@ -165,7 +167,7 @@ _("Repairs are required."));
*/
if (is_unoptimized(meta)) {
if (ctx->mode != SCRUB_MODE_REPAIR) {
- if (!is_inode) {
+ if (group != XFROG_SCRUB_GROUP_INODE) {
/* AG or FS metadata, always warn. */
str_info(ctx, descr_render(&dsc),
_("Optimization is possible."));
@@ -223,9 +225,10 @@ _("Optimizations of %s are possible."), _(xfrog_scrubbers[i].descr));
* Returns 0 for success. If errors occur, this function will log them and
* return a positive error code.
*/
-int
+static int
scrub_meta_type(
struct scrub_ctx *ctx,
+ struct xfs_fd *xfdp,
unsigned int type,
struct scrub_item *sri)
{
@@ -243,16 +246,20 @@ scrub_meta_type(
break;
case XFROG_SCRUB_GROUP_FS:
case XFROG_SCRUB_GROUP_SUMMARY:
+ case XFROG_SCRUB_GROUP_ISCAN:
case XFROG_SCRUB_GROUP_NONE:
break;
- default:
- assert(0);
+ case XFROG_SCRUB_GROUP_INODE:
+ meta.sm_ino = sri->sri_ino;
+ meta.sm_gen = sri->sri_gen;
break;
}
/* Check the item. */
- fix = xfs_check_metadata(ctx, &ctx->mnt, &meta, false);
- progress_add(1);
+ fix = xfs_check_metadata(ctx, xfdp, &meta, false);
+
+ if (xfrog_scrubbers[type].group != XFROG_SCRUB_GROUP_INODE)
+ progress_add(1);
switch (fix) {
case CHECK_ABORT:
@@ -269,60 +276,54 @@ scrub_meta_type(
}
}
-/*
- * Scrub all metadata types that are assigned to the given XFROG_SCRUB_GROUP_*,
- * saving corruption reports for later. This should not be used for
- * XFROG_SCRUB_GROUP_INODE or for checking summary metadata.
- */
-static bool
-scrub_group(
- struct scrub_ctx *ctx,
- enum xfrog_scrub_group group,
- struct scrub_item *sri)
+/* Schedule scrub for all metadata of a given group. */
+void
+scrub_item_schedule_group(
+ struct scrub_item *sri,
+ enum xfrog_scrub_group group)
{
- const struct xfrog_scrub_descr *sc;
- unsigned int type;
-
- sc = xfrog_scrubbers;
- for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
- int ret;
+ unsigned int scrub_type;
- if (sc->group != group)
+ foreach_scrub_type(scrub_type) {
+ if (xfrog_scrubbers[scrub_type].group != group)
continue;
-
- ret = scrub_meta_type(ctx, type, sri);
- if (ret)
- return ret;
+ scrub_item_schedule(sri, scrub_type);
}
-
- return 0;
}
-/* Scrub each AG's header blocks. */
+/* Run all the incomplete scans on this scrub principal. */
int
-scrub_ag_headers(
+scrub_item_check_file(
struct scrub_ctx *ctx,
- struct scrub_item *sri)
+ struct scrub_item *sri,
+ int override_fd)
{
- return scrub_group(ctx, XFROG_SCRUB_GROUP_AGHEADER, sri);
-}
+ struct xfs_fd xfd;
+ struct xfs_fd *xfdp = &ctx->mnt;
+ unsigned int scrub_type;
+ int error;
-/* Scrub each AG's metadata btrees. */
-int
-scrub_ag_metadata(
- struct scrub_ctx *ctx,
- struct scrub_item *sri)
-{
- return scrub_group(ctx, XFROG_SCRUB_GROUP_PERAG, sri);
-}
+ /*
+ * If the caller passed us a file descriptor for a scrub, use it
+ * instead of scrub-by-handle because this enables the kernel to skip
+ * costly inode btree lookups.
+ */
+ if (override_fd >= 0) {
+ memcpy(&xfd, xfdp, sizeof(xfd));
+ xfd.fd = override_fd;
+ xfdp = &xfd;
+ }
-/* Scrub all FS summary metadata. */
-int
-scrub_summary_metadata(
- struct scrub_ctx *ctx,
- struct scrub_item *sri)
-{
- return scrub_group(ctx, XFROG_SCRUB_GROUP_SUMMARY, sri);
+ foreach_scrub_type(scrub_type) {
+ if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK))
+ continue;
+
+ error = scrub_meta_type(ctx, xfdp, scrub_type, sri);
+ if (error)
+ break;
+ }
+
+ return error;
}
/* How many items do we have to check? */
@@ -374,54 +375,6 @@ scrub_estimate_iscan_work(
return estimate;
}
-/*
- * Scrub file metadata of some sort. If errors occur, this function will log
- * them and return nonzero.
- */
-int
-scrub_file(
- struct scrub_ctx *ctx,
- int fd,
- const struct xfs_bulkstat *bstat,
- unsigned int type,
- struct scrub_item *sri)
-{
- struct xfs_scrub_metadata meta = {0};
- struct xfs_fd xfd;
- struct xfs_fd *xfdp = &ctx->mnt;
- enum check_outcome fix;
-
- assert(type < XFS_SCRUB_TYPE_NR);
- assert(xfrog_scrubbers[type].group == XFROG_SCRUB_GROUP_INODE);
-
- meta.sm_type = type;
- meta.sm_ino = bstat->bs_ino;
- meta.sm_gen = bstat->bs_gen;
-
- /*
- * If the caller passed us a file descriptor for a scrub, use it
- * instead of scrub-by-handle because this enables the kernel to skip
- * costly inode btree lookups.
- */
- if (fd >= 0) {
- memcpy(&xfd, xfdp, sizeof(xfd));
- xfd.fd = fd;
- xfdp = &xfd;
- }
-
- /* Scrub the piece of metadata. */
- fix = xfs_check_metadata(ctx, xfdp, &meta, true);
- if (fix == CHECK_ABORT)
- return ECANCELED;
- if (fix == CHECK_DONE) {
- scrub_item_clean_state(sri, type);
- return 0;
- }
-
- scrub_item_save_state(sri, type, meta.sm_flags);
- return 0;
-}
-
/* Dump a scrub item for debugging purposes. */
void
scrub_item_dump(
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 3ae0bfd29..1ac0d8aed 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -6,6 +6,8 @@
#ifndef XFS_SCRUB_SCRUB_H_
#define XFS_SCRUB_SCRUB_H_
+enum xfrog_scrub_group;
+
/* Online scrub and repair. */
enum check_outcome {
CHECK_DONE, /* no further processing needed */
@@ -33,6 +35,9 @@ enum check_outcome {
#define SCRUB_ITEM_XFAIL (XFS_SCRUB_OFLAG_XFAIL) /* (1 << 3) */
#define SCRUB_ITEM_XCORRUPT (XFS_SCRUB_OFLAG_XCORRUPT) /* (1 << 4) */
+/* This scrub type needs to be checked. */
+#define SCRUB_ITEM_NEEDSCHECK (1 << 5)
+
/* All of the state flags that we need to prioritize repair work. */
#define SCRUB_ITEM_REPAIR_ANY (SCRUB_ITEM_CORRUPT | \
SCRUB_ITEM_PREEN | \
@@ -96,13 +101,24 @@ scrub_item_init_file(struct scrub_item *sri, const struct xfs_bulkstat *bstat)
void scrub_item_dump(struct scrub_item *sri, unsigned int group_mask,
const char *tag);
+static inline void
+scrub_item_schedule(struct scrub_item *sri, unsigned int scrub_type)
+{
+ sri->sri_state[scrub_type] = SCRUB_ITEM_NEEDSCHECK;
+}
+
+void scrub_item_schedule_group(struct scrub_item *sri,
+ enum xfrog_scrub_group group);
+int scrub_item_check_file(struct scrub_ctx *ctx, struct scrub_item *sri,
+ int override_fd);
+
+static inline int
+scrub_item_check(struct scrub_ctx *ctx, struct scrub_item *sri)
+{
+ return scrub_item_check_file(ctx, sri, -1);
+}
+
void scrub_report_preen_triggers(struct scrub_ctx *ctx);
-int scrub_ag_headers(struct scrub_ctx *ctx, struct scrub_item *sri);
-int scrub_ag_metadata(struct scrub_ctx *ctx, struct scrub_item *sri);
-int scrub_iscan_metadata(struct scrub_ctx *ctx, struct scrub_item *sri);
-int scrub_summary_metadata(struct scrub_ctx *ctx, struct scrub_item *sri);
-int scrub_meta_type(struct scrub_ctx *ctx, unsigned int type,
- struct scrub_item *sri);
bool can_scrub_fs_metadata(struct scrub_ctx *ctx);
bool can_scrub_inode(struct scrub_ctx *ctx);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/5] xfs_scrub: remove enum check_outcome
2024-07-30 0:18 ` [PATCHSET v30.9 09/23] xfs_scrub: use scrub_item to track check progress Darrick J. Wong
2024-07-30 1:03 ` [PATCH 1/5] xfs_scrub: start tracking scrub state in scrub_item Darrick J. Wong
@ 2024-07-30 1:03 ` Darrick J. Wong
2024-07-30 1:04 ` [PATCH 3/5] xfs_scrub: refactor scrub_meta_type out of existence Darrick J. Wong
` (2 subsequent siblings)
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:03 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Get rid of this enumeration, and just do what we will directly.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 56 ++++++++++++++++++++++----------------------------
scrub/scrub.c | 63 +++++++++++++++++++++++++++++---------------------------
scrub/scrub.h | 8 -------
3 files changed, 58 insertions(+), 69 deletions(-)
diff --git a/scrub/repair.c b/scrub/repair.c
index a3a8fb311..f888441aa 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -46,7 +46,7 @@ static const unsigned int repair_deps[XFS_SCRUB_TYPE_NR] = {
#undef DEP
/* Repair some metadata. */
-static enum check_outcome
+static int
xfs_repair_metadata(
struct scrub_ctx *ctx,
struct xfs_fd *xfdp,
@@ -88,7 +88,7 @@ xfs_repair_metadata(
}
if (!is_corrupt(&meta) && repair_only)
- return CHECK_RETRY;
+ return 0;
memcpy(&oldm, &meta, sizeof(oldm));
oldm.sm_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
@@ -112,12 +112,12 @@ xfs_repair_metadata(
if (debug || verbose)
str_info(ctx, descr_render(&dsc),
_("Filesystem is busy, deferring repair."));
- return CHECK_RETRY;
+ return 0;
case ESHUTDOWN:
/* Filesystem is already shut down, abort. */
str_error(ctx, descr_render(&dsc),
_("Filesystem is shut down, aborting."));
- return CHECK_ABORT;
+ return ECANCELED;
case ENOTTY:
case EOPNOTSUPP:
/*
@@ -129,7 +129,7 @@ _("Filesystem is shut down, aborting."));
if (is_unoptimized(&oldm) ||
debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) {
scrub_item_clean_state(sri, scrub_type);
- return CHECK_DONE;
+ return 0;
}
/*
* If we're in no-complain mode, requeue the check for
@@ -140,30 +140,30 @@ _("Filesystem is shut down, aborting."));
* again to see if another repair fixed it.
*/
if (!(repair_flags & XRM_FINAL_WARNING))
- return CHECK_RETRY;
+ return 0;
fallthrough;
case EINVAL:
/* Kernel doesn't know how to repair this? */
str_corrupt(ctx, descr_render(&dsc),
_("Don't know how to fix; offline repair required."));
scrub_item_clean_state(sri, scrub_type);
- return CHECK_DONE;
+ return 0;
case EROFS:
/* Read-only filesystem, can't fix. */
if (verbose || debug || needs_repair(&oldm))
str_error(ctx, descr_render(&dsc),
_("Read-only filesystem; cannot make changes."));
- return CHECK_ABORT;
+ return ECANCELED;
case ENOENT:
/* Metadata not present, just skip it. */
scrub_item_clean_state(sri, scrub_type);
- return CHECK_DONE;
+ return 0;
case ENOMEM:
case ENOSPC:
/* Don't care if preen fails due to low resources. */
if (is_unoptimized(&oldm) && !needs_repair(&oldm)) {
scrub_item_clean_state(sri, scrub_type);
- return CHECK_DONE;
+ return 0;
}
fallthrough;
default:
@@ -175,10 +175,10 @@ _("Read-only filesystem; cannot make changes."));
* trying to repair it, and bail out.
*/
if (!(repair_flags & XRM_FINAL_WARNING))
- return CHECK_RETRY;
+ return 0;
str_liberror(ctx, error, descr_render(&dsc));
scrub_item_clean_state(sri, scrub_type);
- return CHECK_DONE;
+ return 0;
}
/*
@@ -201,7 +201,7 @@ _("Read-only filesystem; cannot make changes."));
* log the error loudly and don't try again.
*/
if (!(repair_flags & XRM_FINAL_WARNING))
- return CHECK_RETRY;
+ return 0;
str_corrupt(ctx, descr_render(&dsc),
_("Repair unsuccessful; offline repair required."));
} else if (xref_failed(&meta)) {
@@ -219,7 +219,7 @@ _("Repair unsuccessful; offline repair required."));
if (verbose)
str_info(ctx, descr_render(&dsc),
_("Seems correct but cross-referencing failed; will keep checking."));
- return CHECK_RETRY;
+ return 0;
}
} else if (meta.sm_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) {
if (verbose)
@@ -242,7 +242,7 @@ _("Repair unsuccessful; offline repair required."));
}
scrub_item_clean_state(sri, scrub_type);
- return CHECK_DONE;
+ return 0;
}
/*
@@ -543,6 +543,7 @@ repair_item_class(
struct xfs_fd xfd;
struct xfs_fd *xfdp = &ctx->mnt;
unsigned int scrub_type;
+ int error = 0;
if (ctx->mode < SCRUB_MODE_REPAIR)
return 0;
@@ -559,8 +560,6 @@ repair_item_class(
}
foreach_scrub_type(scrub_type) {
- enum check_outcome fix;
-
if (scrub_excessive_errors(ctx))
return ECANCELED;
@@ -576,22 +575,17 @@ repair_item_class(
!repair_item_dependencies_ok(sri, scrub_type))
continue;
- fix = xfs_repair_metadata(ctx, xfdp, scrub_type, sri, flags);
- switch (fix) {
- case CHECK_DONE:
- if (!(flags & XRM_NOPROGRESS))
- progress_add(1);
- continue;
- case CHECK_ABORT:
- return ECANCELED;
- case CHECK_RETRY:
- continue;
- case CHECK_REPAIR:
- abort();
- }
+ error = xfs_repair_metadata(ctx, xfdp, scrub_type, sri, flags);
+ if (error)
+ break;
+
+ /* Maybe update progress if we fixed the problem. */
+ if (!(flags & XRM_NOPROGRESS) &&
+ !(sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY))
+ progress_add(1);
}
- return 0;
+ return error;
}
/*
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 5aa36a964..2c47542ee 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -78,12 +78,12 @@ scrub_warn_incomplete_scrub(
}
/* Do a read-only check of some metadata. */
-static enum check_outcome
+static int
xfs_check_metadata(
struct scrub_ctx *ctx,
struct xfs_fd *xfdp,
struct xfs_scrub_metadata *meta,
- bool is_inode)
+ struct scrub_item *sri)
{
DEFINE_DESCR(dsc, ctx, format_scrub_descr);
enum xfrog_scrub_group group;
@@ -106,17 +106,18 @@ xfs_check_metadata(
break;
case ENOENT:
/* Metadata not present, just skip it. */
- return CHECK_DONE;
+ scrub_item_clean_state(sri, meta->sm_type);
+ return 0;
case ESHUTDOWN:
/* FS already crashed, give up. */
str_error(ctx, descr_render(&dsc),
_("Filesystem is shut down, aborting."));
- return CHECK_ABORT;
+ return ECANCELED;
case EIO:
case ENOMEM:
/* Abort on I/O errors or insufficient memory. */
str_liberror(ctx, error, descr_render(&dsc));
- return CHECK_ABORT;
+ return ECANCELED;
case EDEADLOCK:
case EBUSY:
case EFSBADCRC:
@@ -124,13 +125,16 @@ _("Filesystem is shut down, aborting."));
/*
* The first two should never escape the kernel,
* and the other two should be reported via sm_flags.
+ * Log it and move on.
*/
str_liberror(ctx, error, _("Kernel bug"));
- return CHECK_DONE;
+ scrub_item_clean_state(sri, meta->sm_type);
+ return 0;
default:
- /* Operational error. */
+ /* Operational error. Log it and move on. */
str_liberror(ctx, error, descr_render(&dsc));
- return CHECK_DONE;
+ scrub_item_clean_state(sri, meta->sm_type);
+ return 0;
}
/*
@@ -153,12 +157,16 @@ _("Filesystem is shut down, aborting."));
*/
if (is_corrupt(meta) || xref_disagrees(meta)) {
if (ctx->mode < SCRUB_MODE_REPAIR) {
+ /* Dry-run mode, so log an error and forget it. */
str_corrupt(ctx, descr_render(&dsc),
_("Repairs are required."));
- return CHECK_DONE;
+ scrub_item_clean_state(sri, meta->sm_type);
+ return 0;
}
- return CHECK_REPAIR;
+ /* Schedule repairs. */
+ scrub_item_save_state(sri, meta->sm_type, meta->sm_flags);
+ return 0;
}
/*
@@ -167,6 +175,7 @@ _("Repairs are required."));
*/
if (is_unoptimized(meta)) {
if (ctx->mode != SCRUB_MODE_REPAIR) {
+ /* Dry-run mode, so log an error and forget it. */
if (group != XFROG_SCRUB_GROUP_INODE) {
/* AG or FS metadata, always warn. */
str_info(ctx, descr_render(&dsc),
@@ -178,10 +187,13 @@ _("Optimization is possible."));
ctx->preen_triggers[meta->sm_type] = true;
pthread_mutex_unlock(&ctx->lock);
}
- return CHECK_DONE;
+ scrub_item_clean_state(sri, meta->sm_type);
+ return 0;
}
- return CHECK_REPAIR;
+ /* Schedule optimizations. */
+ scrub_item_save_state(sri, meta->sm_type, meta->sm_flags);
+ return 0;
}
/*
@@ -191,11 +203,14 @@ _("Optimization is possible."));
* re-examine the object as repairs progress to see if the kernel will
* deem it completely consistent at some point.
*/
- if (xref_failed(meta) && ctx->mode == SCRUB_MODE_REPAIR)
- return CHECK_REPAIR;
+ if (xref_failed(meta) && ctx->mode == SCRUB_MODE_REPAIR) {
+ scrub_item_save_state(sri, meta->sm_type, meta->sm_flags);
+ return 0;
+ }
/* Everything is ok. */
- return CHECK_DONE;
+ scrub_item_clean_state(sri, meta->sm_type);
+ return 0;
}
/* Bulk-notify user about things that could be optimized. */
@@ -235,7 +250,7 @@ scrub_meta_type(
struct xfs_scrub_metadata meta = {
.sm_type = type,
};
- enum check_outcome fix;
+ int error;
background_sleep();
@@ -256,24 +271,12 @@ scrub_meta_type(
}
/* Check the item. */
- fix = xfs_check_metadata(ctx, xfdp, &meta, false);
+ error = xfs_check_metadata(ctx, xfdp, &meta, sri);
if (xfrog_scrubbers[type].group != XFROG_SCRUB_GROUP_INODE)
progress_add(1);
- switch (fix) {
- case CHECK_ABORT:
- return ECANCELED;
- case CHECK_REPAIR:
- scrub_item_save_state(sri, type, meta.sm_flags);
- return 0;
- case CHECK_DONE:
- scrub_item_clean_state(sri, type);
- return 0;
- default:
- /* CHECK_RETRY should never happen. */
- abort();
- }
+ return error;
}
/* Schedule scrub for all metadata of a given group. */
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 1ac0d8aed..24fb24449 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -8,14 +8,6 @@
enum xfrog_scrub_group;
-/* Online scrub and repair. */
-enum check_outcome {
- CHECK_DONE, /* no further processing needed */
- CHECK_REPAIR, /* schedule this for repairs */
- CHECK_ABORT, /* end program */
- CHECK_RETRY, /* repair failed, try again later */
-};
-
/*
* This flag boosts the repair priority of a scrub item when a dependent scrub
* item is scheduled for repair. Use a separate flag to preserve the
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/5] xfs_scrub: refactor scrub_meta_type out of existence
2024-07-30 0:18 ` [PATCHSET v30.9 09/23] xfs_scrub: use scrub_item to track check progress Darrick J. Wong
2024-07-30 1:03 ` [PATCH 1/5] xfs_scrub: start tracking scrub state in scrub_item Darrick J. Wong
2024-07-30 1:03 ` [PATCH 2/5] xfs_scrub: remove enum check_outcome Darrick J. Wong
@ 2024-07-30 1:04 ` Darrick J. Wong
2024-07-30 1:04 ` [PATCH 4/5] xfs_scrub: hoist repair retry loop to repair_item_class Darrick J. Wong
2024-07-30 1:04 ` [PATCH 5/5] xfs_scrub: hoist scrub retry loop to scrub_item_check_file Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:04 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Remove this helper function since it's trivial now.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/scrub.c | 124 ++++++++++++++++++++++++---------------------------------
1 file changed, 53 insertions(+), 71 deletions(-)
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 2c47542ee..5f0cacbde 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -82,31 +82,51 @@ static int
xfs_check_metadata(
struct scrub_ctx *ctx,
struct xfs_fd *xfdp,
- struct xfs_scrub_metadata *meta,
+ unsigned int scrub_type,
struct scrub_item *sri)
{
DEFINE_DESCR(dsc, ctx, format_scrub_descr);
+ struct xfs_scrub_metadata meta = { };
enum xfrog_scrub_group group;
unsigned int tries = 0;
int error;
- group = xfrog_scrubbers[meta->sm_type].group;
+ background_sleep();
+
+ group = xfrog_scrubbers[scrub_type].group;
+ meta.sm_type = scrub_type;
+ switch (group) {
+ case XFROG_SCRUB_GROUP_AGHEADER:
+ case XFROG_SCRUB_GROUP_PERAG:
+ meta.sm_agno = sri->sri_agno;
+ break;
+ case XFROG_SCRUB_GROUP_FS:
+ case XFROG_SCRUB_GROUP_SUMMARY:
+ case XFROG_SCRUB_GROUP_ISCAN:
+ case XFROG_SCRUB_GROUP_NONE:
+ break;
+ case XFROG_SCRUB_GROUP_INODE:
+ meta.sm_ino = sri->sri_ino;
+ meta.sm_gen = sri->sri_gen;
+ break;
+ }
+
assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
- assert(meta->sm_type < XFS_SCRUB_TYPE_NR);
- descr_set(&dsc, meta);
+ assert(scrub_type < XFS_SCRUB_TYPE_NR);
+ descr_set(&dsc, &meta);
- dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta->sm_flags);
+ dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta.sm_flags);
retry:
- error = -xfrog_scrub_metadata(xfdp, meta);
+ error = -xfrog_scrub_metadata(xfdp, &meta);
if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !error)
- meta->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+ meta.sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
switch (error) {
case 0:
/* No operational errors encountered. */
break;
case ENOENT:
/* Metadata not present, just skip it. */
- scrub_item_clean_state(sri, meta->sm_type);
+ scrub_item_clean_state(sri, scrub_type);
return 0;
case ESHUTDOWN:
/* FS already crashed, give up. */
@@ -128,12 +148,12 @@ _("Filesystem is shut down, aborting."));
* Log it and move on.
*/
str_liberror(ctx, error, _("Kernel bug"));
- scrub_item_clean_state(sri, meta->sm_type);
+ scrub_item_clean_state(sri, scrub_type);
return 0;
default:
/* Operational error. Log it and move on. */
str_liberror(ctx, error, descr_render(&dsc));
- scrub_item_clean_state(sri, meta->sm_type);
+ scrub_item_clean_state(sri, scrub_type);
return 0;
}
@@ -143,29 +163,29 @@ _("Filesystem is shut down, aborting."));
* we'll try the scan again, just in case the fs was busy.
* Only retry so many times.
*/
- if (want_retry(meta) && tries < 10) {
+ if (want_retry(&meta) && tries < 10) {
tries++;
goto retry;
}
/* Complain about incomplete or suspicious metadata. */
- scrub_warn_incomplete_scrub(ctx, &dsc, meta);
+ scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
/*
* If we need repairs or there were discrepancies, schedule a
* repair if desired, otherwise complain.
*/
- if (is_corrupt(meta) || xref_disagrees(meta)) {
+ if (is_corrupt(&meta) || xref_disagrees(&meta)) {
if (ctx->mode < SCRUB_MODE_REPAIR) {
/* Dry-run mode, so log an error and forget it. */
str_corrupt(ctx, descr_render(&dsc),
_("Repairs are required."));
- scrub_item_clean_state(sri, meta->sm_type);
+ scrub_item_clean_state(sri, scrub_type);
return 0;
}
/* Schedule repairs. */
- scrub_item_save_state(sri, meta->sm_type, meta->sm_flags);
+ scrub_item_save_state(sri, scrub_type, meta.sm_flags);
return 0;
}
@@ -173,26 +193,26 @@ _("Repairs are required."));
* If we could optimize, schedule a repair if desired,
* otherwise complain.
*/
- if (is_unoptimized(meta)) {
+ if (is_unoptimized(&meta)) {
if (ctx->mode != SCRUB_MODE_REPAIR) {
/* Dry-run mode, so log an error and forget it. */
if (group != XFROG_SCRUB_GROUP_INODE) {
/* AG or FS metadata, always warn. */
str_info(ctx, descr_render(&dsc),
_("Optimization is possible."));
- } else if (!ctx->preen_triggers[meta->sm_type]) {
+ } else if (!ctx->preen_triggers[scrub_type]) {
/* File metadata, only warn once per type. */
pthread_mutex_lock(&ctx->lock);
- if (!ctx->preen_triggers[meta->sm_type])
- ctx->preen_triggers[meta->sm_type] = true;
+ if (!ctx->preen_triggers[scrub_type])
+ ctx->preen_triggers[scrub_type] = true;
pthread_mutex_unlock(&ctx->lock);
}
- scrub_item_clean_state(sri, meta->sm_type);
+ scrub_item_clean_state(sri, scrub_type);
return 0;
}
/* Schedule optimizations. */
- scrub_item_save_state(sri, meta->sm_type, meta->sm_flags);
+ scrub_item_save_state(sri, scrub_type, meta.sm_flags);
return 0;
}
@@ -203,13 +223,13 @@ _("Optimization is possible."));
* re-examine the object as repairs progress to see if the kernel will
* deem it completely consistent at some point.
*/
- if (xref_failed(meta) && ctx->mode == SCRUB_MODE_REPAIR) {
- scrub_item_save_state(sri, meta->sm_type, meta->sm_flags);
+ if (xref_failed(&meta) && ctx->mode == SCRUB_MODE_REPAIR) {
+ scrub_item_save_state(sri, scrub_type, meta.sm_flags);
return 0;
}
/* Everything is ok. */
- scrub_item_clean_state(sri, meta->sm_type);
+ scrub_item_clean_state(sri, scrub_type);
return 0;
}
@@ -233,52 +253,6 @@ _("Optimizations of %s are possible."), _(xfrog_scrubbers[i].descr));
}
}
-/*
- * Scrub a single XFS_SCRUB_TYPE_*, saving corruption reports for later.
- * Do not call this function to repair file metadata.
- *
- * Returns 0 for success. If errors occur, this function will log them and
- * return a positive error code.
- */
-static int
-scrub_meta_type(
- struct scrub_ctx *ctx,
- struct xfs_fd *xfdp,
- unsigned int type,
- struct scrub_item *sri)
-{
- struct xfs_scrub_metadata meta = {
- .sm_type = type,
- };
- int error;
-
- background_sleep();
-
- switch (xfrog_scrubbers[type].group) {
- case XFROG_SCRUB_GROUP_AGHEADER:
- case XFROG_SCRUB_GROUP_PERAG:
- meta.sm_agno = sri->sri_agno;
- break;
- case XFROG_SCRUB_GROUP_FS:
- case XFROG_SCRUB_GROUP_SUMMARY:
- case XFROG_SCRUB_GROUP_ISCAN:
- case XFROG_SCRUB_GROUP_NONE:
- break;
- case XFROG_SCRUB_GROUP_INODE:
- meta.sm_ino = sri->sri_ino;
- meta.sm_gen = sri->sri_gen;
- break;
- }
-
- /* Check the item. */
- error = xfs_check_metadata(ctx, xfdp, &meta, sri);
-
- if (xfrog_scrubbers[type].group != XFROG_SCRUB_GROUP_INODE)
- progress_add(1);
-
- return error;
-}
-
/* Schedule scrub for all metadata of a given group. */
void
scrub_item_schedule_group(
@@ -321,7 +295,15 @@ scrub_item_check_file(
if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK))
continue;
- error = scrub_meta_type(ctx, xfdp, scrub_type, sri);
+ error = xfs_check_metadata(ctx, xfdp, scrub_type, sri);
+
+ /*
+ * Progress is counted by the inode for inode metadata; for
+ * everything else, it's counted for each scrub call.
+ */
+ if (sri->sri_ino == -1ULL)
+ progress_add(1);
+
if (error)
break;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/5] xfs_scrub: hoist repair retry loop to repair_item_class
2024-07-30 0:18 ` [PATCHSET v30.9 09/23] xfs_scrub: use scrub_item to track check progress Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:04 ` [PATCH 3/5] xfs_scrub: refactor scrub_meta_type out of existence Darrick J. Wong
@ 2024-07-30 1:04 ` Darrick J. Wong
2024-07-30 1:04 ` [PATCH 5/5] xfs_scrub: hoist scrub retry loop to scrub_item_check_file Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:04 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
For metadata repair calls, move the ioctl retry and freeze permission
tracking into scrub_item. This enables us to move the repair retry loop
out of xfs_repair_metadata and into its caller to remove a long
backwards jump, and gets us closer to vectorizing scrub calls.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 21 ++++++++++++---------
scrub/scrub.c | 32 ++++++++++++++++++++++++++++++--
scrub/scrub.h | 6 ++++++
scrub/scrub_private.h | 14 ++++++++++++++
4 files changed, 62 insertions(+), 11 deletions(-)
diff --git a/scrub/repair.c b/scrub/repair.c
index f888441aa..c427e6e95 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -58,7 +58,6 @@ xfs_repair_metadata(
struct xfs_scrub_metadata oldm;
DEFINE_DESCR(dsc, ctx, format_scrub_descr);
bool repair_only;
- unsigned int tries = 0;
int error;
/*
@@ -100,7 +99,6 @@ xfs_repair_metadata(
str_info(ctx, descr_render(&dsc),
_("Attempting optimization."));
-retry:
error = -xfrog_scrub_metadata(xfdp, &meta);
switch (error) {
case 0:
@@ -187,10 +185,8 @@ _("Read-only filesystem; cannot make changes."));
* the repair again, just in case the fs was busy. Only retry so many
* times.
*/
- if (want_retry(&meta) && tries < 10) {
- tries++;
- goto retry;
- }
+ if (want_retry(&meta) && scrub_item_schedule_retry(sri, scrub_type))
+ return 0;
if (repair_flags & XRM_FINAL_WARNING)
scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
@@ -541,6 +537,7 @@ repair_item_class(
unsigned int flags)
{
struct xfs_fd xfd;
+ struct scrub_item old_sri;
struct xfs_fd *xfdp = &ctx->mnt;
unsigned int scrub_type;
int error = 0;
@@ -575,9 +572,15 @@ repair_item_class(
!repair_item_dependencies_ok(sri, scrub_type))
continue;
- error = xfs_repair_metadata(ctx, xfdp, scrub_type, sri, flags);
- if (error)
- break;
+ sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES;
+ do {
+ memcpy(&old_sri, sri, sizeof(old_sri));
+ error = xfs_repair_metadata(ctx, xfdp, scrub_type, sri,
+ flags);
+ if (error)
+ return error;
+ } while (scrub_item_call_kernel_again(sri, scrub_type,
+ repair_mask, &old_sri));
/* Maybe update progress if we fixed the problem. */
if (!(flags & XRM_NOPROGRESS) &&
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 5f0cacbde..8c6bf845f 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -268,6 +268,34 @@ scrub_item_schedule_group(
}
}
+/* Decide if we call the kernel again to finish scrub/repair activity. */
+bool
+scrub_item_call_kernel_again(
+ struct scrub_item *sri,
+ unsigned int scrub_type,
+ uint8_t work_mask,
+ const struct scrub_item *old)
+{
+ uint8_t statex;
+
+ /* If there's nothing to do, we're done. */
+ if (!(sri->sri_state[scrub_type] & work_mask))
+ return false;
+
+ /*
+ * We are willing to go again if the last call had any effect on the
+ * state of the scrub item that the caller cares about, if the freeze
+ * flag got set, or if the kernel asked us to try again...
+ */
+ statex = sri->sri_state[scrub_type] ^ old->sri_state[scrub_type];
+ if (statex & work_mask)
+ return true;
+ if (sri->sri_tries[scrub_type] != old->sri_tries[scrub_type])
+ return true;
+
+ return false;
+}
+
/* Run all the incomplete scans on this scrub principal. */
int
scrub_item_check_file(
@@ -383,9 +411,9 @@ scrub_item_dump(
unsigned int g = 1U << xfrog_scrubbers[i].group;
if (g & group_mask)
- printf("[%u]: type '%s' state 0x%x\n", i,
+ printf("[%u]: type '%s' state 0x%x tries %u\n", i,
xfrog_scrubbers[i].name,
- sri->sri_state[i]);
+ sri->sri_state[i], sri->sri_tries[i]);
}
fflush(stdout);
}
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 24fb24449..246c923f4 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -45,6 +45,9 @@ enum xfrog_scrub_group;
SCRUB_ITEM_XFAIL | \
SCRUB_ITEM_XCORRUPT)
+/* Maximum number of times we'll retry a scrub ioctl call. */
+#define SCRUB_ITEM_MAX_RETRIES 10
+
struct scrub_item {
/*
* Information we need to call the scrub and repair ioctls. Per-AG
@@ -58,6 +61,9 @@ struct scrub_item {
/* Scrub item state flags, one for each XFS_SCRUB_TYPE. */
__u8 sri_state[XFS_SCRUB_TYPE_NR];
+
+ /* Track scrub and repair call retries for each scrub type. */
+ __u8 sri_tries[XFS_SCRUB_TYPE_NR];
};
#define foreach_scrub_type(loopvar) \
diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h
index 53372e1f3..234b30ef2 100644
--- a/scrub/scrub_private.h
+++ b/scrub/scrub_private.h
@@ -89,4 +89,18 @@ scrub_item_type_boosted(
return sri->sri_state[scrub_type] & SCRUB_ITEM_BOOST_REPAIR;
}
+/* Decide if we want to retry this operation and update bookkeeping if yes. */
+static inline bool
+scrub_item_schedule_retry(struct scrub_item *sri, unsigned int scrub_type)
+{
+ if (sri->sri_tries[scrub_type] == 0)
+ return false;
+ sri->sri_tries[scrub_type]--;
+ return true;
+}
+
+bool scrub_item_call_kernel_again(struct scrub_item *sri,
+ unsigned int scrub_type, uint8_t work_mask,
+ const struct scrub_item *old);
+
#endif /* XFS_SCRUB_SCRUB_PRIVATE_H_ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/5] xfs_scrub: hoist scrub retry loop to scrub_item_check_file
2024-07-30 0:18 ` [PATCHSET v30.9 09/23] xfs_scrub: use scrub_item to track check progress Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:04 ` [PATCH 4/5] xfs_scrub: hoist repair retry loop to repair_item_class Darrick J. Wong
@ 2024-07-30 1:04 ` Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:04 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
For metadata check calls, use the ioctl retry and freeze permission
tracking in scrub_item that we created in the last patch. This enables
us to move the check retry loop out of xfs_scrub_metadata and into its
caller to remove a long backwards jump, and gets us closer to
vectorizing scrub calls.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/scrub.c | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 8c6bf845f..69dfb1eb8 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -88,7 +88,6 @@ xfs_check_metadata(
DEFINE_DESCR(dsc, ctx, format_scrub_descr);
struct xfs_scrub_metadata meta = { };
enum xfrog_scrub_group group;
- unsigned int tries = 0;
int error;
background_sleep();
@@ -116,7 +115,7 @@ xfs_check_metadata(
descr_set(&dsc, &meta);
dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta.sm_flags);
-retry:
+
error = -xfrog_scrub_metadata(xfdp, &meta);
if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !error)
meta.sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
@@ -163,10 +162,8 @@ _("Filesystem is shut down, aborting."));
* we'll try the scan again, just in case the fs was busy.
* Only retry so many times.
*/
- if (want_retry(&meta) && tries < 10) {
- tries++;
- goto retry;
- }
+ if (want_retry(&meta) && scrub_item_schedule_retry(sri, scrub_type))
+ return 0;
/* Complain about incomplete or suspicious metadata. */
scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
@@ -304,6 +301,7 @@ scrub_item_check_file(
int override_fd)
{
struct xfs_fd xfd;
+ struct scrub_item old_sri;
struct xfs_fd *xfdp = &ctx->mnt;
unsigned int scrub_type;
int error;
@@ -323,7 +321,14 @@ scrub_item_check_file(
if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK))
continue;
- error = xfs_check_metadata(ctx, xfdp, scrub_type, sri);
+ sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES;
+ do {
+ memcpy(&old_sri, sri, sizeof(old_sri));
+ error = xfs_check_metadata(ctx, xfdp, scrub_type, sri);
+ if (error)
+ return error;
+ } while (scrub_item_call_kernel_again(sri, scrub_type,
+ SCRUB_ITEM_NEEDSCHECK, &old_sri));
/*
* Progress is counted by the inode for inode metadata; for
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/4] libfrog: enhance ptvar to support initializer functions
2024-07-30 0:19 ` [PATCHSET v30.9 10/23] xfs_scrub: improve scheduling of repair items Darrick J. Wong
@ 2024-07-30 1:05 ` Darrick J. Wong
2024-07-30 1:05 ` [PATCH 2/4] xfs_scrub: improve thread scheduling repair items during phase 4 Darrick J. Wong
` (2 subsequent siblings)
3 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:05 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Modify the per-thread variable code to support passing in an initializer
function that will set up each thread's variable space when it is
claimed.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libfrog/ptvar.c | 9 ++++++++-
libfrog/ptvar.h | 4 +++-
scrub/counter.c | 2 +-
scrub/descr.c | 2 +-
scrub/phase7.c | 2 +-
scrub/read_verify.c | 2 +-
6 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/libfrog/ptvar.c b/libfrog/ptvar.c
index 7ac8c5418..9d5ae6bc8 100644
--- a/libfrog/ptvar.c
+++ b/libfrog/ptvar.c
@@ -26,6 +26,7 @@
struct ptvar {
pthread_key_t key;
pthread_mutex_t lock;
+ ptvar_init_fn init_fn;
size_t nr_used;
size_t nr_counters;
size_t data_size;
@@ -38,6 +39,7 @@ int
ptvar_alloc(
size_t nr,
size_t size,
+ ptvar_init_fn init_fn,
struct ptvar **pptv)
{
struct ptvar *ptv;
@@ -58,6 +60,7 @@ ptvar_alloc(
ptv->data_size = size;
ptv->nr_counters = nr;
ptv->nr_used = 0;
+ ptv->init_fn = init_fn;
memset(ptv->data, 0, nr * size);
ret = -pthread_mutex_init(&ptv->lock, NULL);
if (ret)
@@ -98,11 +101,15 @@ ptvar_get(
if (!p) {
pthread_mutex_lock(&ptv->lock);
assert(ptv->nr_used < ptv->nr_counters);
- p = &ptv->data[(ptv->nr_used++) * ptv->data_size];
+ p = &ptv->data[ptv->nr_used * ptv->data_size];
ret = -pthread_setspecific(ptv->key, p);
if (ret)
goto out_unlock;
+ ptv->nr_used++;
pthread_mutex_unlock(&ptv->lock);
+
+ if (ptv->init_fn)
+ ptv->init_fn(p);
}
*retp = 0;
return p;
diff --git a/libfrog/ptvar.h b/libfrog/ptvar.h
index b7d02d626..e4a181ffe 100644
--- a/libfrog/ptvar.h
+++ b/libfrog/ptvar.h
@@ -8,7 +8,9 @@
struct ptvar;
-int ptvar_alloc(size_t nr, size_t size, struct ptvar **pptv);
+typedef void (*ptvar_init_fn)(void *data);
+int ptvar_alloc(size_t nr, size_t size, ptvar_init_fn init_fn,
+ struct ptvar **pptv);
void ptvar_free(struct ptvar *ptv);
void *ptvar_get(struct ptvar *ptv, int *ret);
diff --git a/scrub/counter.c b/scrub/counter.c
index 2ee357f3a..c903454c0 100644
--- a/scrub/counter.c
+++ b/scrub/counter.c
@@ -38,7 +38,7 @@ ptcounter_alloc(
p = malloc(sizeof(struct ptcounter));
if (!p)
return errno;
- ret = -ptvar_alloc(nr, sizeof(uint64_t), &p->var);
+ ret = -ptvar_alloc(nr, sizeof(uint64_t), NULL, &p->var);
if (ret) {
free(p);
return ret;
diff --git a/scrub/descr.c b/scrub/descr.c
index 77d5378ec..88ca5d95a 100644
--- a/scrub/descr.c
+++ b/scrub/descr.c
@@ -89,7 +89,7 @@ descr_init_phase(
int ret;
assert(descr_ptvar == NULL);
- ret = -ptvar_alloc(nr_threads, DESCR_BUFSZ, &descr_ptvar);
+ ret = -ptvar_alloc(nr_threads, DESCR_BUFSZ, NULL, &descr_ptvar);
if (ret)
str_liberror(ctx, ret, _("creating description buffer"));
diff --git a/scrub/phase7.c b/scrub/phase7.c
index cd4501f72..cce5ede00 100644
--- a/scrub/phase7.c
+++ b/scrub/phase7.c
@@ -136,7 +136,7 @@ phase7_func(
}
error = -ptvar_alloc(scrub_nproc(ctx), sizeof(struct summary_counts),
- &ptvar);
+ NULL, &ptvar);
if (error) {
str_liberror(ctx, error, _("setting up block counter"));
return error;
diff --git a/scrub/read_verify.c b/scrub/read_verify.c
index 29d793954..52348274b 100644
--- a/scrub/read_verify.c
+++ b/scrub/read_verify.c
@@ -120,7 +120,7 @@ read_verify_pool_alloc(
rvp->disk = disk;
rvp->ioerr_fn = ioerr_fn;
ret = -ptvar_alloc(submitter_threads, sizeof(struct read_verify),
- &rvp->rvstate);
+ NULL, &rvp->rvstate);
if (ret)
goto out_counter;
ret = -workqueue_create(&rvp->wq, (struct xfs_mount *)rvp,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/4] xfs_scrub: improve thread scheduling repair items during phase 4
2024-07-30 0:19 ` [PATCHSET v30.9 10/23] xfs_scrub: improve scheduling of repair items Darrick J. Wong
2024-07-30 1:05 ` [PATCH 1/4] libfrog: enhance ptvar to support initializer functions Darrick J. Wong
@ 2024-07-30 1:05 ` Darrick J. Wong
2024-07-30 1:05 ` [PATCH 3/4] xfs_scrub: recheck entire metadata objects after corruption repairs Darrick J. Wong
2024-07-30 1:05 ` [PATCH 4/4] xfs_scrub: try to repair space metadata before file metadata Darrick J. Wong
3 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:05 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
As it stands, xfs_scrub doesn't do a good job of scheduling repair items
during phase 4. The repair lists are sharded by AG, and one repair
worker is started for each per-AG repair list. Consequently, if one AG
requires considerably more work than the others (e.g. inodes are not
spread evenly among the AGs) then phase 4 can stall waiting for that one
worker thread when there's still plenty of CPU power available.
While our initial assumptions were that repairs would be vanishingly
scarce, the reality is that "repairs" can be triggered for optimizations
like gaps in the xattr structures, or clearing the inode reflink flag on
inodes that no longer share data. In real world testing scenarios, the
lack of balance leads to complaints about excessive runtime of
xfs_scrub.
To fix these balance problems, we replace the per-AG repair item lists
in the scrub context with a single repair item list. Phase 4 will be
redesigned as follows:
The repair worker will grab a repair item from the main list, try to
repair it, record whether the repair attempt made any progress, and
requeue the item if it was not fully fixed. A separate repair scheduler
function starts the repair workers, and waits for them all to complete.
Requeued repairs are merged back into the main repair list. If we made
any forward progress, we'll start another round of repairs with the
repair workers. Phase 4 retains the behavior that if the pool stops
making forward progress, it will try all the repairs one last time,
serially.
To facilitate this new design, phase 2 will queue repairs of space
metadata items directly to the main list. Phase 3's worker threads will
queue repair items to per-thread lists and splice those lists into the
main list at the end.
On a filesystem crafted to put all the inodes in a single AG, this
restores xfs_scrub's ability to parallelize repairs. There seems to be
a slight performance hit for the evenly-spread case, but avoiding a
performance cliff due to an unbalanced fs is more important here.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase1.c | 8 +-
scrub/phase2.c | 23 +++++
scrub/phase3.c | 106 ++++++++++++++++--------
scrub/phase4.c | 230 ++++++++++++++++++++++++++++++++++++++---------------
scrub/repair.c | 135 +++++++++++++++++--------------
scrub/repair.h | 37 +++++++--
scrub/xfs_scrub.h | 2
7 files changed, 367 insertions(+), 174 deletions(-)
diff --git a/scrub/phase1.c b/scrub/phase1.c
index 60a8db572..78769a57b 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -89,7 +89,8 @@ scrub_cleanup(
if (error)
return error;
- action_lists_free(&ctx->action_lists);
+ action_list_free(&ctx->action_list);
+
if (ctx->fshandle)
free_handle(ctx->fshandle, ctx->fshandle_len);
if (ctx->rtdev)
@@ -185,10 +186,9 @@ _("Not an XFS filesystem."));
return error;
}
- error = action_lists_alloc(ctx->mnt.fsgeom.agcount,
- &ctx->action_lists);
+ error = action_list_alloc(&ctx->action_list);
if (error) {
- str_liberror(ctx, error, _("allocating action lists"));
+ str_liberror(ctx, error, _("allocating repair list"));
return error;
}
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 79b33dd04..5803d8c64 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -50,6 +50,25 @@ warn_repair_difficulties(
str_info(ctx, descr, _("Filesystem might not be repairable."));
}
+/* Add a scrub item that needs more work to fs metadata repair list. */
+static int
+defer_fs_repair(
+ struct scrub_ctx *ctx,
+ const struct scrub_item *sri)
+{
+ struct action_item *aitem = NULL;
+ int error;
+
+ error = repair_item_to_action_item(ctx, sri, &aitem);
+ if (error || !aitem)
+ return error;
+
+ pthread_mutex_lock(&ctx->lock);
+ action_list_add(ctx->action_list, aitem);
+ pthread_mutex_unlock(&ctx->lock);
+ return 0;
+}
+
/* Scrub each AG's metadata btrees. */
static void
scan_ag_metadata(
@@ -108,7 +127,7 @@ scan_ag_metadata(
goto err;
/* Everything else gets fixed during phase 4. */
- ret = repair_item_defer(ctx, &sri);
+ ret = defer_fs_repair(ctx, &sri);
if (ret)
goto err;
return;
@@ -144,7 +163,7 @@ scan_fs_metadata(
difficulty = repair_item_difficulty(&sri);
warn_repair_difficulties(ctx, difficulty, xfrog_scrubbers[type].descr);
- ret = repair_item_defer(ctx, &sri);
+ ret = defer_fs_repair(ctx, &sri);
if (ret) {
sctl->aborted = true;
goto out;
diff --git a/scrub/phase3.c b/scrub/phase3.c
index 09347c977..1a71d4ace 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -10,6 +10,7 @@
#include "list.h"
#include "libfrog/paths.h"
#include "libfrog/workqueue.h"
+#include "libfrog/ptvar.h"
#include "xfs_scrub.h"
#include "common.h"
#include "counter.h"
@@ -26,8 +27,8 @@ struct scrub_inode_ctx {
/* Number of inodes scanned. */
struct ptcounter *icount;
- /* per-AG locks to protect the repair lists */
- pthread_mutex_t *locks;
+ /* Per-thread lists of file repair items. */
+ struct ptvar *repair_ptlists;
/* Set to true to abort all threads. */
bool aborted;
@@ -51,28 +52,28 @@ report_close_error(
str_errno(ctx, descr);
}
-/*
- * Defer all the repairs until phase 4, being careful about locking since the
- * inode scrub threads are not per-AG.
- */
+/* Defer all the repairs until phase 4. */
static int
defer_inode_repair(
struct scrub_inode_ctx *ictx,
- const struct xfs_bulkstat *bstat,
- struct scrub_item *sri)
+ const struct scrub_item *sri)
{
+ struct action_list *alist;
struct action_item *aitem = NULL;
- xfs_agnumber_t agno;
int ret;
ret = repair_item_to_action_item(ictx->ctx, sri, &aitem);
if (ret || !aitem)
return ret;
- agno = cvt_ino_to_agno(&ictx->ctx->mnt, bstat->bs_ino);
- pthread_mutex_lock(&ictx->locks[agno]);
- action_list_add(&ictx->ctx->action_lists[agno], aitem);
- pthread_mutex_unlock(&ictx->locks[agno]);
+ alist = ptvar_get(ictx->repair_ptlists, &ret);
+ if (ret) {
+ str_liberror(ictx->ctx, ret,
+ _("getting per-thread inode repair list"));
+ return ret;
+ }
+
+ action_list_add(alist, aitem);
return 0;
}
@@ -81,8 +82,7 @@ static int
try_inode_repair(
struct scrub_inode_ctx *ictx,
struct scrub_item *sri,
- int fd,
- const struct xfs_bulkstat *bstat)
+ int fd)
{
/*
* If at the start of phase 3 we already had ag/rt metadata repairs
@@ -149,7 +149,7 @@ scrub_inode(
if (error)
goto out;
- error = try_inode_repair(ictx, &sri, fd, bstat);
+ error = try_inode_repair(ictx, &sri, fd);
if (error)
goto out;
@@ -161,7 +161,7 @@ scrub_inode(
if (error)
goto out;
- error = try_inode_repair(ictx, &sri, fd, bstat);
+ error = try_inode_repair(ictx, &sri, fd);
if (error)
goto out;
@@ -187,7 +187,7 @@ scrub_inode(
goto out;
/* Try to repair the file while it's open. */
- error = try_inode_repair(ictx, &sri, fd, bstat);
+ error = try_inode_repair(ictx, &sri, fd);
if (error)
goto out;
@@ -204,7 +204,7 @@ scrub_inode(
progress_add(1);
if (!error && !ictx->aborted)
- error = defer_inode_repair(ictx, bstat, &sri);
+ error = defer_inode_repair(ictx, &sri);
if (fd >= 0) {
int err2;
@@ -221,6 +221,33 @@ scrub_inode(
return error;
}
+/*
+ * Collect all the inode repairs in the file repair list. No need for locks
+ * here, since we're single-threaded.
+ */
+static int
+collect_repairs(
+ struct ptvar *ptv,
+ void *data,
+ void *foreach_arg)
+{
+ struct scrub_ctx *ctx = foreach_arg;
+ struct action_list *alist = data;
+
+ action_list_merge(ctx->action_list, alist);
+ return 0;
+}
+
+/* Initialize this per-thread file repair item list. */
+static void
+action_ptlist_init(
+ void *priv)
+{
+ struct action_list *alist = priv;
+
+ action_list_init(alist);
+}
+
/* Verify all the inodes in a filesystem. */
int
phase3_func(
@@ -231,17 +258,18 @@ phase3_func(
xfs_agnumber_t agno;
int err;
+ err = -ptvar_alloc(scrub_nproc(ctx), sizeof(struct action_list),
+ action_ptlist_init, &ictx.repair_ptlists);
+ if (err) {
+ str_liberror(ctx, err,
+ _("creating per-thread file repair item lists"));
+ return err;
+ }
+
err = ptcounter_alloc(scrub_nproc(ctx), &ictx.icount);
if (err) {
str_liberror(ctx, err, _("creating scanned inode counter"));
- return err;
- }
-
- ictx.locks = calloc(ctx->mnt.fsgeom.agcount, sizeof(pthread_mutex_t));
- if (!ictx.locks) {
- str_errno(ctx, _("creating per-AG repair list locks"));
- err = ENOMEM;
- goto out_ptcounter;
+ goto out_ptvar;
}
/*
@@ -250,9 +278,7 @@ phase3_func(
* to repair the space metadata.
*/
for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
- pthread_mutex_init(&ictx.locks[agno], NULL);
-
- if (!action_list_empty(&ctx->action_lists[agno]))
+ if (!action_list_empty(ctx->action_list))
ictx.always_defer_repairs = true;
}
@@ -260,22 +286,30 @@ phase3_func(
if (!err && ictx.aborted)
err = ECANCELED;
if (err)
- goto out_locks;
+ goto out_ptcounter;
+
+ /*
+ * Combine all of the file repair items into the main repair list.
+ * We don't need locks here since we're the only thread running now.
+ */
+ err = -ptvar_foreach(ictx.repair_ptlists, collect_repairs, ctx);
+ if (err) {
+ str_liberror(ctx, err, _("collecting inode repair lists"));
+ goto out_ptcounter;
+ }
scrub_report_preen_triggers(ctx);
err = ptcounter_value(ictx.icount, &val);
if (err) {
str_liberror(ctx, err, _("summing scanned inode counter"));
- goto out_locks;
+ goto out_ptcounter;
}
ctx->inodes_checked = val;
-out_locks:
- for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++)
- pthread_mutex_destroy(&ictx.locks[agno]);
- free(ictx.locks);
out_ptcounter:
ptcounter_free(ictx.icount);
+out_ptvar:
+ ptvar_free(ictx.repair_ptlists);
return err;
}
diff --git a/scrub/phase4.c b/scrub/phase4.c
index 3c51b38a5..564ccb827 100644
--- a/scrub/phase4.c
+++ b/scrub/phase4.c
@@ -17,57 +17,170 @@
#include "scrub.h"
#include "repair.h"
#include "vfs.h"
+#include "atomic.h"
/* Phase 4: Repair filesystem. */
-/* Fix all the problems in our per-AG list. */
+struct repair_list_schedule {
+ struct action_list *repair_list;
+
+ /* Action items that we could not resolve and want to try again. */
+ struct action_list requeue_list;
+
+ pthread_mutex_t lock;
+
+ /* Workers use this to signal the scheduler when all work is done. */
+ pthread_cond_t done;
+
+ /* Number of workers that are still running. */
+ unsigned int workers;
+
+ /* Or should we all abort? */
+ bool aborted;
+
+ /* Did we make any progress this round? */
+ bool made_progress;
+};
+
+/* Try to repair as many things on our list as we can. */
static void
-repair_ag(
+repair_list_worker(
struct workqueue *wq,
xfs_agnumber_t agno,
void *priv)
{
+ struct repair_list_schedule *rls = priv;
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
- bool *aborted = priv;
- struct action_list *alist;
- unsigned long long unfixed;
- unsigned long long new_unfixed;
- unsigned int flags = 0;
- int ret;
-
- alist = &ctx->action_lists[agno];
- unfixed = action_list_length(alist);
-
- /* Repair anything broken until we fail to make progress. */
- do {
- ret = action_list_process(ctx, alist, flags);
+
+ pthread_mutex_lock(&rls->lock);
+ while (!rls->aborted) {
+ struct action_item *aitem;
+ enum tryrepair_outcome outcome;
+ int ret;
+
+ aitem = action_list_pop(rls->repair_list);
+ if (!aitem)
+ break;
+
+ pthread_mutex_unlock(&rls->lock);
+ ret = action_item_try_repair(ctx, aitem, &outcome);
+ pthread_mutex_lock(&rls->lock);
+
if (ret) {
- *aborted = true;
- return;
+ rls->aborted = true;
+ free(aitem);
+ break;
}
- new_unfixed = action_list_length(alist);
- if (new_unfixed == unfixed)
+
+ switch (outcome) {
+ case TR_REQUEUE:
+ /*
+ * Partial progress. Make a note of that and requeue
+ * this item for the next round.
+ */
+ rls->made_progress = true;
+ action_list_add(&rls->requeue_list, aitem);
+ break;
+ case TR_NOPROGRESS:
+ /*
+ * No progress. Requeue this item for a later round,
+ * which could happen if something else makes progress.
+ */
+ action_list_add(&rls->requeue_list, aitem);
break;
- unfixed = new_unfixed;
- if (*aborted)
- return;
- } while (unfixed > 0);
-
- /* Try once more, but this time complain if we can't fix things. */
- flags |= XRM_FINAL_WARNING;
- ret = action_list_process(ctx, alist, flags);
- if (ret)
- *aborted = true;
+ case TR_REPAIRED:
+ /*
+ * All repairs for this item completed. Free the item,
+ * and remember that progress was made.
+ */
+ rls->made_progress = true;
+ free(aitem);
+ break;
+ }
+ }
+
+ rls->workers--;
+ if (rls->workers == 0)
+ pthread_cond_broadcast(&rls->done);
+ pthread_mutex_unlock(&rls->lock);
+}
+
+/*
+ * Schedule repair list workers. Returns 1 if we made progress, 0 if we
+ * did not, or -1 if we need to abort everything.
+ */
+static int
+repair_list_schedule(
+ struct scrub_ctx *ctx,
+ struct workqueue *wq,
+ struct action_list *repair_list)
+{
+ struct repair_list_schedule rls = {
+ .lock = PTHREAD_MUTEX_INITIALIZER,
+ .done = PTHREAD_COND_INITIALIZER,
+ .repair_list = repair_list,
+ };
+ unsigned int i;
+ unsigned int nr_workers = scrub_nproc(ctx);
+ bool made_any_progress = false;
+ int ret = 0;
+
+ if (action_list_empty(repair_list))
+ return 0;
+
+ action_list_init(&rls.requeue_list);
+
+ /*
+ * Use the workers to run through the entire repair list once. Requeue
+ * anything that did not make progress, and keep trying as long as the
+ * workers made any kind of progress.
+ */
+ do {
+ rls.made_progress = false;
+
+ /* Start all the worker threads. */
+ for (i = 0; i < nr_workers; i++) {
+ pthread_mutex_lock(&rls.lock);
+ rls.workers++;
+ pthread_mutex_unlock(&rls.lock);
+
+ ret = -workqueue_add(wq, repair_list_worker, 0, &rls);
+ if (ret) {
+ str_liberror(ctx, ret,
+ _("queueing repair list worker"));
+ pthread_mutex_lock(&rls.lock);
+ rls.workers--;
+ pthread_mutex_unlock(&rls.lock);
+ break;
+ }
+ }
+
+ /* Wait for all worker functions to return. */
+ pthread_mutex_lock(&rls.lock);
+ while (rls.workers > 0)
+ pthread_cond_wait(&rls.done, &rls.lock);
+ pthread_mutex_unlock(&rls.lock);
+
+ action_list_merge(repair_list, &rls.requeue_list);
+
+ if (ret || rls.aborted)
+ return -1;
+ if (rls.made_progress)
+ made_any_progress = true;
+ } while (rls.made_progress && !action_list_empty(repair_list));
+
+ if (made_any_progress)
+ return 1;
+ return 0;
}
-/* Process all the action items. */
+/* Process both repair lists. */
static int
repair_everything(
struct scrub_ctx *ctx)
{
struct workqueue wq;
- xfs_agnumber_t agno;
- bool aborted = false;
+ int fixed_anything;
int ret;
ret = -workqueue_create(&wq, (struct xfs_mount *)ctx,
@@ -76,41 +189,32 @@ repair_everything(
str_liberror(ctx, ret, _("creating repair workqueue"));
return ret;
}
- for (agno = 0; !aborted && agno < ctx->mnt.fsgeom.agcount; agno++) {
- if (action_list_length(&ctx->action_lists[agno]) == 0)
- continue;
- ret = -workqueue_add(&wq, repair_ag, agno, &aborted);
- if (ret) {
- str_liberror(ctx, ret, _("queueing repair work"));
+ /*
+ * Try to fix everything on the space metadata repair list and then the
+ * file repair list until we stop making progress. These repairs can
+ * be threaded, if the user desires.
+ */
+ do {
+ fixed_anything = 0;
+
+ ret = repair_list_schedule(ctx, &wq, ctx->action_list);
+ if (ret < 0)
break;
- }
- }
+ if (ret == 1)
+ fixed_anything++;
+ } while (fixed_anything > 0);
ret = -workqueue_terminate(&wq);
if (ret)
str_liberror(ctx, ret, _("finishing repair work"));
workqueue_destroy(&wq);
- if (aborted)
- return ECANCELED;
+ if (ret < 0)
+ return ret;
- return 0;
-}
-
-/* Decide if we have any repair work to do. */
-static inline bool
-have_action_items(
- struct scrub_ctx *ctx)
-{
- xfs_agnumber_t agno;
-
- for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
- if (action_list_length(&ctx->action_lists[agno]) > 0)
- return true;
- }
-
- return false;
+ /* Repair everything serially. Last chance to fix things. */
+ return action_list_process(ctx, ctx->action_list, XRM_FINAL_WARNING);
}
/* Trim the unused areas of the filesystem if the caller asked us to. */
@@ -132,7 +236,7 @@ phase4_func(
struct scrub_item sri;
int ret;
- if (!have_action_items(ctx))
+ if (action_list_empty(ctx->action_list))
goto maybe_trim;
/*
@@ -190,12 +294,12 @@ phase4_estimate(
unsigned int *nr_threads,
int *rshift)
{
- xfs_agnumber_t agno;
- unsigned long long need_fixing = 0;
+ unsigned long long need_fixing;
- for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++)
- need_fixing += action_list_length(&ctx->action_lists[agno]);
+ /* Everything on the repair list plus FSTRIM. */
+ need_fixing = action_list_length(ctx->action_list);
need_fixing++;
+
*items = need_fixing;
*nr_threads = scrub_nproc(ctx) + 1;
*rshift = 0;
diff --git a/scrub/repair.c b/scrub/repair.c
index c427e6e95..eba936e1f 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -396,58 +396,41 @@ repair_item_difficulty(
return ret;
}
-/*
- * Allocate a certain number of repair lists for the scrub context. Returns
- * zero or a positive error number.
- */
+/* Create a new repair action list. */
int
-action_lists_alloc(
- size_t nr,
- struct action_list **listsp)
+action_list_alloc(
+ struct action_list **listp)
{
- struct action_list *lists;
- xfs_agnumber_t agno;
+ struct action_list *alist;
- lists = calloc(nr, sizeof(struct action_list));
- if (!lists)
+ alist = malloc(sizeof(struct action_list));
+ if (!alist)
return errno;
- for (agno = 0; agno < nr; agno++)
- action_list_init(&lists[agno]);
- *listsp = lists;
-
+ action_list_init(alist);
+ *listp = alist;
return 0;
}
-/* Discard repair list contents. */
+/* Free the repair lists. */
void
-action_list_discard(
- struct action_list *alist)
+action_list_free(
+ struct action_list **listp)
{
+ struct action_list *alist = *listp;
struct action_item *aitem;
struct action_item *n;
+ if (!(*listp))
+ return;
+
list_for_each_entry_safe(aitem, n, &alist->list, list) {
list_del(&aitem->list);
free(aitem);
}
-}
-/* Free the repair lists. */
-void
-action_lists_free(
- struct action_list **listsp)
-{
- free(*listsp);
- *listsp = NULL;
-}
-
-/* Initialize repair list */
-void
-action_list_init(
- struct action_list *alist)
-{
- INIT_LIST_HEAD(&alist->list);
+ free(alist);
+ *listp = NULL;
}
/* Number of pending repairs in this list. */
@@ -464,7 +447,23 @@ action_list_length(
return ret;
}
-/* Add to the list of repairs. */
+/* Remove the first action item from the action list. */
+struct action_item *
+action_list_pop(
+ struct action_list *alist)
+{
+ struct action_item *aitem;
+
+ aitem = list_first_entry_or_null(&alist->list, struct action_item,
+ list);
+ if (!aitem)
+ return NULL;
+
+ list_del_init(&aitem->list);
+ return aitem;
+}
+
+/* Add an action item to the end of a list. */
void
action_list_add(
struct action_list *alist,
@@ -473,6 +472,46 @@ action_list_add(
list_add_tail(&aitem->list, &alist->list);
}
+/*
+ * Try to repair a filesystem object and let the caller know what it should do
+ * with the action item. The caller must be able to requeue action items, so
+ * we don't complain if repairs are not totally successful.
+ */
+int
+action_item_try_repair(
+ struct scrub_ctx *ctx,
+ struct action_item *aitem,
+ enum tryrepair_outcome *outcome)
+{
+ struct scrub_item *sri = &aitem->sri;
+ unsigned int before, after;
+ int ret;
+
+ before = repair_item_count_needsrepair(sri);
+
+ ret = repair_item(ctx, sri, 0);
+ if (ret)
+ return ret;
+
+ after = repair_item_count_needsrepair(sri);
+ if (after > 0) {
+ /*
+ * The kernel did not complete all of the repairs requested.
+ * If it made some progress we'll requeue; otherwise, let the
+ * caller know that nothing got fixed.
+ */
+ if (before != after)
+ *outcome = TR_REQUEUE;
+ else
+ *outcome = TR_NOPROGRESS;
+ return 0;
+ }
+
+ /* Repairs complete. */
+ *outcome = TR_REPAIRED;
+ return 0;
+}
+
/* Repair everything on this list. */
int
action_list_process(
@@ -676,29 +715,3 @@ repair_item_to_action_item(
*aitemp = aitem;
return 0;
}
-
-/* Defer all the repairs until phase 4. */
-int
-repair_item_defer(
- struct scrub_ctx *ctx,
- const struct scrub_item *sri)
-{
- struct action_item *aitem = NULL;
- unsigned int agno;
- int error;
-
- error = repair_item_to_action_item(ctx, sri, &aitem);
- if (error || !aitem)
- return error;
-
- if (sri->sri_agno != -1U)
- agno = sri->sri_agno;
- else if (sri->sri_ino != -1ULL && sri->sri_gen != -1U)
- agno = cvt_ino_to_agno(&ctx->mnt, sri->sri_ino);
- else
- agno = 0;
- ASSERT(agno < ctx->mnt.fsgeom.agcount);
-
- action_list_add(&ctx->action_lists[agno], aitem);
- return 0;
-}
diff --git a/scrub/repair.h b/scrub/repair.h
index a38cdd5e6..a685e9037 100644
--- a/scrub/repair.h
+++ b/scrub/repair.h
@@ -12,19 +12,43 @@ struct action_list {
struct action_item;
-int action_lists_alloc(size_t nr, struct action_list **listsp);
-void action_lists_free(struct action_list **listsp);
+int action_list_alloc(struct action_list **listp);
+void action_list_free(struct action_list **listp);
+static inline void action_list_init(struct action_list *alist)
+{
+ INIT_LIST_HEAD(&alist->list);
+}
-void action_list_init(struct action_list *alist);
+unsigned long long action_list_length(struct action_list *alist);
+
+/* Move all the items of @src to the tail of @dst, and reinitialize @src. */
+static inline void
+action_list_merge(
+ struct action_list *dst,
+ struct action_list *src)
+{
+ list_splice_tail_init(&src->list, &dst->list);
+}
+
+struct action_item *action_list_pop(struct action_list *alist);
+void action_list_add(struct action_list *alist, struct action_item *aitem);
static inline bool action_list_empty(const struct action_list *alist)
{
return list_empty(&alist->list);
}
-unsigned long long action_list_length(struct action_list *alist);
-void action_list_add(struct action_list *dest, struct action_item *item);
-void action_list_discard(struct action_list *alist);
+enum tryrepair_outcome {
+ /* No progress was made on repairs at all. */
+ TR_NOPROGRESS = 0,
+ /* Some progress was made on repairs; try again soon. */
+ TR_REQUEUE,
+ /* Repairs completely successful. */
+ TR_REPAIRED,
+};
+
+int action_item_try_repair(struct scrub_ctx *ctx, struct action_item *aitem,
+ enum tryrepair_outcome *outcome);
void repair_item_mustfix(struct scrub_item *sri, struct scrub_item *fix_now);
@@ -56,7 +80,6 @@ int repair_item(struct scrub_ctx *ctx, struct scrub_item *sri,
unsigned int repair_flags);
int repair_item_to_action_item(struct scrub_ctx *ctx,
const struct scrub_item *sri, struct action_item **aitemp);
-int repair_item_defer(struct scrub_ctx *ctx, const struct scrub_item *sri);
static inline unsigned int
repair_item_count_needsrepair(
diff --git a/scrub/xfs_scrub.h b/scrub/xfs_scrub.h
index 1151ee9ff..a339c4d63 100644
--- a/scrub/xfs_scrub.h
+++ b/scrub/xfs_scrub.h
@@ -72,7 +72,7 @@ struct scrub_ctx {
/* Mutable scrub state; use lock. */
pthread_mutex_t lock;
- struct action_list *action_lists;
+ struct action_list *action_list;
unsigned long long max_errors;
unsigned long long runtime_errors;
unsigned long long corruptions_found;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/4] xfs_scrub: recheck entire metadata objects after corruption repairs
2024-07-30 0:19 ` [PATCHSET v30.9 10/23] xfs_scrub: improve scheduling of repair items Darrick J. Wong
2024-07-30 1:05 ` [PATCH 1/4] libfrog: enhance ptvar to support initializer functions Darrick J. Wong
2024-07-30 1:05 ` [PATCH 2/4] xfs_scrub: improve thread scheduling repair items during phase 4 Darrick J. Wong
@ 2024-07-30 1:05 ` Darrick J. Wong
2024-07-30 1:05 ` [PATCH 4/4] xfs_scrub: try to repair space metadata before file metadata Darrick J. Wong
3 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:05 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
When we've finished making repairs to some domain of filesystem metadata
(file, AG, etc.) to correct an inconsistency, we should recheck all the
other metadata types within that domain to make sure that we neither
made things worse nor introduced more cross-referencing problems. If we
did, requeue the item to make the repairs. If the only changes we made
were optimizations, don't bother.
The XFS_SCRUB_TYPE_ values are getting close to the max for a u32, so
I chose u64 for sri_selected.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 37 +++++++++++++++++++++++++++++++++++++
scrub/scrub.c | 5 +++--
scrub/scrub.h | 10 ++++++++++
scrub/scrub_private.h | 2 ++
4 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/scrub/repair.c b/scrub/repair.c
index eba936e1f..19f5c9052 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -485,8 +485,10 @@ action_item_try_repair(
{
struct scrub_item *sri = &aitem->sri;
unsigned int before, after;
+ unsigned int scrub_type;
int ret;
+ BUILD_BUG_ON(sizeof(sri->sri_selected) * NBBY < XFS_SCRUB_TYPE_NR);
before = repair_item_count_needsrepair(sri);
ret = repair_item(ctx, sri, 0);
@@ -507,6 +509,41 @@ action_item_try_repair(
return 0;
}
+ /*
+ * Nothing in this fs object was marked inconsistent. This means we
+ * were merely optimizing metadata and there is no revalidation work to
+ * be done.
+ */
+ if (!sri->sri_inconsistent) {
+ *outcome = TR_REPAIRED;
+ return 0;
+ }
+
+ /*
+ * We fixed inconsistent metadata, so reschedule the entire object for
+ * immediate revalidation to see if anything else went wrong.
+ */
+ foreach_scrub_type(scrub_type)
+ if (sri->sri_selected & (1ULL << scrub_type))
+ sri->sri_state[scrub_type] = SCRUB_ITEM_NEEDSCHECK;
+ sri->sri_inconsistent = false;
+ sri->sri_revalidate = true;
+
+ ret = scrub_item_check(ctx, sri);
+ if (ret)
+ return ret;
+
+ after = repair_item_count_needsrepair(sri);
+ if (after > 0) {
+ /*
+ * Uhoh, we found something else broken. Tell the caller that
+ * this item needs to be queued for more repairs.
+ */
+ sri->sri_revalidate = false;
+ *outcome = TR_REQUEUE;
+ return 0;
+ }
+
/* Repairs complete. */
*outcome = TR_REPAIRED;
return 0;
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 69dfb1eb8..2b6b6274e 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -117,11 +117,12 @@ xfs_check_metadata(
dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta.sm_flags);
error = -xfrog_scrub_metadata(xfdp, &meta);
- if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !error)
- meta.sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
switch (error) {
case 0:
/* No operational errors encountered. */
+ if (!sri->sri_revalidate &&
+ debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
+ meta.sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
break;
case ENOENT:
/* Metadata not present, just skip it. */
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 246c923f4..90578108a 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -59,11 +59,20 @@ struct scrub_item {
__u32 sri_gen;
__u32 sri_agno;
+ /* Bitmask of scrub types that were scheduled here. */
+ __u64 sri_selected;
+
/* Scrub item state flags, one for each XFS_SCRUB_TYPE. */
__u8 sri_state[XFS_SCRUB_TYPE_NR];
/* Track scrub and repair call retries for each scrub type. */
__u8 sri_tries[XFS_SCRUB_TYPE_NR];
+
+ /* Were there any corruption repairs needed? */
+ bool sri_inconsistent:1;
+
+ /* Are we revalidating after repairs? */
+ bool sri_revalidate:1;
};
#define foreach_scrub_type(loopvar) \
@@ -103,6 +112,7 @@ static inline void
scrub_item_schedule(struct scrub_item *sri, unsigned int scrub_type)
{
sri->sri_state[scrub_type] = SCRUB_ITEM_NEEDSCHECK;
+ sri->sri_selected |= (1ULL << scrub_type);
}
void scrub_item_schedule_group(struct scrub_item *sri,
diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h
index 234b30ef2..bcfabda16 100644
--- a/scrub/scrub_private.h
+++ b/scrub/scrub_private.h
@@ -71,6 +71,8 @@ scrub_item_save_state(
unsigned int scrub_flags)
{
sri->sri_state[scrub_type] = scrub_flags & SCRUB_ITEM_REPAIR_ANY;
+ if (scrub_flags & SCRUB_ITEM_NEEDSREPAIR)
+ sri->sri_inconsistent = true;
}
static inline void
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/4] xfs_scrub: try to repair space metadata before file metadata
2024-07-30 0:19 ` [PATCHSET v30.9 10/23] xfs_scrub: improve scheduling of repair items Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:05 ` [PATCH 3/4] xfs_scrub: recheck entire metadata objects after corruption repairs Darrick J. Wong
@ 2024-07-30 1:05 ` Darrick J. Wong
3 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:05 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Phase 4 (metadata repairs) of xfs_scrub has suffered a mild race
condition since the beginning of its existence. Repair functions for
higher level metadata such as directories build the new directory blocks
in an unlinked temporary file and use atomic extent swapping to commit
the corrected directory contents into the existing directory. Atomic
extent swapping requires consistent filesystem space metadata, but phase
4 has never enforced correctness dependencies between space and file
metadata repairs.
Before the previous patch eliminated the per-AG repair lists, this error
was not often hit in testing scenarios because the allocator generally
succeeds in placing file data blocks in the same AG as the inode. With
pool threads now able to pop file repairs from the repair list before
space repairs complete, this error became much more obvious.
Fortunately, the new phase 4 design makes it easy to try to enforce the
consistency requirements of higher level file metadata repairs. Split
the repair list into one for space metadata and another for file
metadata. Phase 4 will now try to fix the space metadata until it stops
making progress on that, and only then will it try to fix file metadata.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase1.c | 13 ++++++++++---
scrub/phase2.c | 2 +-
scrub/phase3.c | 4 ++--
scrub/phase4.c | 22 +++++++++++++++++-----
scrub/xfs_scrub.h | 3 ++-
5 files changed, 32 insertions(+), 12 deletions(-)
diff --git a/scrub/phase1.c b/scrub/phase1.c
index 78769a57b..1b3f6e8eb 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -89,7 +89,8 @@ scrub_cleanup(
if (error)
return error;
- action_list_free(&ctx->action_list);
+ action_list_free(&ctx->file_repair_list);
+ action_list_free(&ctx->fs_repair_list);
if (ctx->fshandle)
free_handle(ctx->fshandle, ctx->fshandle_len);
@@ -186,9 +187,15 @@ _("Not an XFS filesystem."));
return error;
}
- error = action_list_alloc(&ctx->action_list);
+ error = action_list_alloc(&ctx->fs_repair_list);
if (error) {
- str_liberror(ctx, error, _("allocating repair list"));
+ str_liberror(ctx, error, _("allocating fs repair list"));
+ return error;
+ }
+
+ error = action_list_alloc(&ctx->file_repair_list);
+ if (error) {
+ str_liberror(ctx, error, _("allocating file repair list"));
return error;
}
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 5803d8c64..57c6d0ef2 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -64,7 +64,7 @@ defer_fs_repair(
return error;
pthread_mutex_lock(&ctx->lock);
- action_list_add(ctx->action_list, aitem);
+ action_list_add(ctx->fs_repair_list, aitem);
pthread_mutex_unlock(&ctx->lock);
return 0;
}
diff --git a/scrub/phase3.c b/scrub/phase3.c
index 1a71d4ace..98e5c5a1f 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -234,7 +234,7 @@ collect_repairs(
struct scrub_ctx *ctx = foreach_arg;
struct action_list *alist = data;
- action_list_merge(ctx->action_list, alist);
+ action_list_merge(ctx->file_repair_list, alist);
return 0;
}
@@ -278,7 +278,7 @@ phase3_func(
* to repair the space metadata.
*/
for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
- if (!action_list_empty(ctx->action_list))
+ if (!action_list_empty(ctx->fs_repair_list))
ictx.always_defer_repairs = true;
}
diff --git a/scrub/phase4.c b/scrub/phase4.c
index 564ccb827..9080d3881 100644
--- a/scrub/phase4.c
+++ b/scrub/phase4.c
@@ -198,7 +198,13 @@ repair_everything(
do {
fixed_anything = 0;
- ret = repair_list_schedule(ctx, &wq, ctx->action_list);
+ ret = repair_list_schedule(ctx, &wq, ctx->fs_repair_list);
+ if (ret < 0)
+ break;
+ if (ret == 1)
+ fixed_anything++;
+
+ ret = repair_list_schedule(ctx, &wq, ctx->file_repair_list);
if (ret < 0)
break;
if (ret == 1)
@@ -213,8 +219,12 @@ repair_everything(
if (ret < 0)
return ret;
- /* Repair everything serially. Last chance to fix things. */
- return action_list_process(ctx, ctx->action_list, XRM_FINAL_WARNING);
+ /*
+ * Combine both repair lists and repair everything serially. This is
+ * the last chance to fix things.
+ */
+ action_list_merge(ctx->fs_repair_list, ctx->file_repair_list);
+ return action_list_process(ctx, ctx->fs_repair_list, XRM_FINAL_WARNING);
}
/* Trim the unused areas of the filesystem if the caller asked us to. */
@@ -236,7 +246,8 @@ phase4_func(
struct scrub_item sri;
int ret;
- if (action_list_empty(ctx->action_list))
+ if (action_list_empty(ctx->fs_repair_list) &&
+ action_list_empty(ctx->file_repair_list))
goto maybe_trim;
/*
@@ -297,7 +308,8 @@ phase4_estimate(
unsigned long long need_fixing;
/* Everything on the repair list plus FSTRIM. */
- need_fixing = action_list_length(ctx->action_list);
+ need_fixing = action_list_length(ctx->fs_repair_list) +
+ action_list_length(ctx->file_repair_list);
need_fixing++;
*items = need_fixing;
diff --git a/scrub/xfs_scrub.h b/scrub/xfs_scrub.h
index a339c4d63..ed86d0093 100644
--- a/scrub/xfs_scrub.h
+++ b/scrub/xfs_scrub.h
@@ -72,7 +72,8 @@ struct scrub_ctx {
/* Mutable scrub state; use lock. */
pthread_mutex_t lock;
- struct action_list *action_list;
+ struct action_list *fs_repair_list;
+ struct action_list *file_repair_list;
unsigned long long max_errors;
unsigned long long runtime_errors;
unsigned long long corruptions_found;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 01/13] xfs_scrub: use proper UChar string iterators
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
@ 2024-07-30 1:06 ` Darrick J. Wong
2024-07-30 1:06 ` [PATCH 02/13] xfs_scrub: hoist code that removes ignorable characters Darrick J. Wong
` (11 subsequent siblings)
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:06 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
For code that wants to examine a UChar string, use libicu's string
iterators to walk UChar strings, instead of the open-coded U16_NEXT*
macros that perform no typechecking.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index dd3016435..02a1b94ef 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -330,13 +330,12 @@ name_entry_examine(
struct name_entry *entry,
unsigned int *badflags)
{
+ UCharIterator uiter;
UChar32 uchr;
- int32_t i;
uint8_t mask = 0;
- for (i = 0; i < entry->normstrlen;) {
- U16_NEXT_UNSAFE(entry->normstr, i, uchr);
-
+ uiter_setString(&uiter, entry->normstr, entry->normstrlen);
+ while ((uchr = uiter_next32(&uiter)) != U_SENTINEL) {
/* zero width character sequences */
switch (uchr) {
case 0x200B: /* zero width space */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 02/13] xfs_scrub: hoist code that removes ignorable characters
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
2024-07-30 1:06 ` [PATCH 01/13] xfs_scrub: use proper UChar string iterators Darrick J. Wong
@ 2024-07-30 1:06 ` Darrick J. Wong
2024-07-30 1:06 ` [PATCH 03/13] xfs_scrub: add a couple of omitted invisible code points Darrick J. Wong
` (10 subsequent siblings)
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:06 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Hoist the loop that removes "ignorable" code points from the skeleton
string into a separate function and give the UChar cursors names that
are easier to understand. Convert the code to use the safe versions of
the U16_ accessor functions.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 39 ++++++++++++++++++++++++++-------------
1 file changed, 26 insertions(+), 13 deletions(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 02a1b94ef..96e20114c 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -145,6 +145,31 @@ is_utf8_locale(void)
return answer;
}
+/*
+ * Remove control/formatting characters from this string and return its new
+ * length. UChar32 is required for U16_NEXT, despite the name.
+ */
+static int32_t
+remove_ignorable(
+ UChar *ustr,
+ int32_t ustrlen)
+{
+ UChar32 uchr;
+ int32_t src, dest;
+
+ for (src = 0, dest = 0; src < ustrlen; dest = src) {
+ U16_NEXT(ustr, src, ustrlen, uchr);
+ if (!u_isIDIgnorable(uchr))
+ continue;
+ memmove(&ustr[dest], &ustr[src],
+ (ustrlen - src + 1) * sizeof(UChar));
+ ustrlen -= (src - dest);
+ src = dest;
+ }
+
+ return dest;
+}
+
/*
* Generate normalized form and skeleton of the name. If this fails, just
* forget everything and return false; this is an advisory checker.
@@ -160,9 +185,6 @@ name_entry_compute_checknames(
int32_t normstrlen;
int32_t unistrlen;
int32_t skelstrlen;
- UChar32 uchr;
- int32_t i, j;
-
UErrorCode uerr = U_ZERO_ERROR;
/* Convert bytestr to unistr for normalization */
@@ -206,16 +228,7 @@ name_entry_compute_checknames(
if (U_FAILURE(uerr))
goto out_skelstr;
- /* Remove control/formatting characters from skeleton. */
- for (i = 0, j = 0; i < skelstrlen; j = i) {
- U16_NEXT_UNSAFE(skelstr, i, uchr);
- if (!u_isIDIgnorable(uchr))
- continue;
- memmove(&skelstr[j], &skelstr[i],
- (skelstrlen - i + 1) * sizeof(UChar));
- skelstrlen -= (i - j);
- i = j;
- }
+ skelstrlen = remove_ignorable(skelstr, skelstrlen);
entry->skelstr = skelstr;
entry->skelstrlen = skelstrlen;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 03/13] xfs_scrub: add a couple of omitted invisible code points
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
2024-07-30 1:06 ` [PATCH 01/13] xfs_scrub: use proper UChar string iterators Darrick J. Wong
2024-07-30 1:06 ` [PATCH 02/13] xfs_scrub: hoist code that removes ignorable characters Darrick J. Wong
@ 2024-07-30 1:06 ` Darrick J. Wong
2024-07-30 1:06 ` [PATCH 04/13] xfs_scrub: avoid potential UAF after freeing a duplicate name entry Darrick J. Wong
` (9 subsequent siblings)
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:06 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
I missed a few non-rendering code points in the "zero width"
classification code. Add them now, and sort the list. Finding them is
an annoyingly manual process because there are various code points that
are not supposed to affect the rendering of a string of text but are not
explicitly named as such. There are other code points that, when
surrounded by code points from the same chart, actually /do/ affect the
rendering.
IOWs, the only way to figure this out is to grep the likely code points
and then go figure out how each of them render by reading the Unicode
spec or trying it.
$ wget https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
$ grep -E '(separator|zero width|invisible|joiner|application)' -i UnicodeData.txt
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 96e20114c..edc32d55c 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -351,15 +351,19 @@ name_entry_examine(
while ((uchr = uiter_next32(&uiter)) != U_SENTINEL) {
/* zero width character sequences */
switch (uchr) {
+ case 0x034F: /* combining grapheme joiner */
case 0x200B: /* zero width space */
case 0x200C: /* zero width non-joiner */
case 0x200D: /* zero width joiner */
- case 0xFEFF: /* zero width non breaking space */
+ case 0x2028: /* line separator */
+ case 0x2029: /* paragraph separator */
case 0x2060: /* word joiner */
case 0x2061: /* function application */
case 0x2062: /* invisible times (multiply) */
case 0x2063: /* invisible separator (comma) */
case 0x2064: /* invisible plus (addition) */
+ case 0x2D7F: /* tifinagh consonant joiner */
+ case 0xFEFF: /* zero width non breaking space */
*badflags |= UNICRASH_ZERO_WIDTH;
break;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 04/13] xfs_scrub: avoid potential UAF after freeing a duplicate name entry
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:06 ` [PATCH 03/13] xfs_scrub: add a couple of omitted invisible code points Darrick J. Wong
@ 2024-07-30 1:06 ` Darrick J. Wong
2024-07-30 1:07 ` [PATCH 05/13] xfs_scrub: guard against libicu returning negative buffer lengths Darrick J. Wong
` (8 subsequent siblings)
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:06 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Change the function declaration of unicrash_add to set the caller's
@new_entry to NULL if we detect an updated name entry and do not wish to
continue processing. This avoids a theoretical UAF if the unicrash_add
caller were to accidentally continue using the pointer.
This isn't an /actual/ UAF because the function formerly set @badflags
to zero, but let's be a little defensive.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index edc32d55c..4517e2bce 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -628,10 +628,11 @@ _("Unicode name \"%s\" in %s could be confused with \"%s\"."),
static void
unicrash_add(
struct unicrash *uc,
- struct name_entry *new_entry,
+ struct name_entry **new_entryp,
unsigned int *badflags,
struct name_entry **existing_entry)
{
+ struct name_entry *new_entry = *new_entryp;
struct name_entry *entry;
size_t bucket;
xfs_dahash_t hash;
@@ -654,7 +655,7 @@ unicrash_add(
entry->ino = new_entry->ino;
uc->buckets[bucket] = new_entry->next;
name_entry_free(new_entry);
- *badflags = 0;
+ *new_entryp = NULL;
return;
}
@@ -697,8 +698,8 @@ __unicrash_check_name(
return 0;
name_entry_examine(new_entry, &badflags);
- unicrash_add(uc, new_entry, &badflags, &dup_entry);
- if (badflags)
+ unicrash_add(uc, &new_entry, &badflags, &dup_entry);
+ if (new_entry && badflags)
unicrash_complain(uc, dsc, namedescr, new_entry, badflags,
dup_entry);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 05/13] xfs_scrub: guard against libicu returning negative buffer lengths
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:06 ` [PATCH 04/13] xfs_scrub: avoid potential UAF after freeing a duplicate name entry Darrick J. Wong
@ 2024-07-30 1:07 ` Darrick J. Wong
2024-07-30 1:07 ` [PATCH 06/13] xfs_scrub: hoist non-rendering character predicate Darrick J. Wong
` (7 subsequent siblings)
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:07 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
The libicu functions u_strFromUTF8, unorm2_normalize, and
uspoof_getSkeleton return int32_t values. Guard against negative return
values, even though the library itself never does this.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 4517e2bce..456caec27 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -189,7 +189,7 @@ name_entry_compute_checknames(
/* Convert bytestr to unistr for normalization */
u_strFromUTF8(NULL, 0, &unistrlen, entry->name, entry->namelen, &uerr);
- if (uerr != U_BUFFER_OVERFLOW_ERROR)
+ if (uerr != U_BUFFER_OVERFLOW_ERROR || unistrlen < 0)
return false;
uerr = U_ZERO_ERROR;
unistr = calloc(unistrlen + 1, sizeof(UChar));
@@ -203,7 +203,7 @@ name_entry_compute_checknames(
/* Normalize the string. */
normstrlen = unorm2_normalize(uc->normalizer, unistr, unistrlen, NULL,
0, &uerr);
- if (uerr != U_BUFFER_OVERFLOW_ERROR)
+ if (uerr != U_BUFFER_OVERFLOW_ERROR || normstrlen < 0)
goto out_unistr;
uerr = U_ZERO_ERROR;
normstr = calloc(normstrlen + 1, sizeof(UChar));
@@ -217,7 +217,7 @@ name_entry_compute_checknames(
/* Compute skeleton. */
skelstrlen = uspoof_getSkeleton(uc->spoof, 0, unistr, unistrlen, NULL,
0, &uerr);
- if (uerr != U_BUFFER_OVERFLOW_ERROR)
+ if (uerr != U_BUFFER_OVERFLOW_ERROR || skelstrlen < 0)
goto out_normstr;
uerr = U_ZERO_ERROR;
skelstr = calloc(skelstrlen + 1, sizeof(UChar));
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 06/13] xfs_scrub: hoist non-rendering character predicate
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 1:07 ` [PATCH 05/13] xfs_scrub: guard against libicu returning negative buffer lengths Darrick J. Wong
@ 2024-07-30 1:07 ` Darrick J. Wong
2024-07-30 1:07 ` [PATCH 07/13] xfs_scrub: store bad flags with the name entry Darrick J. Wong
` (6 subsequent siblings)
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:07 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Hoist this predicate code into its own function; we're going to use it
elsewhere later on. While we're at it, document how we generated this
list in the first place.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 49 ++++++++++++++++++++++++++++++++-----------------
1 file changed, 32 insertions(+), 17 deletions(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 456caec27..1a86b5f8c 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -170,6 +170,36 @@ remove_ignorable(
return dest;
}
+/*
+ * Certain unicode codepoints are formatting hints that are not themselves
+ * supposed to be rendered by a display system. These codepoints can be
+ * encoded in file names to try to confuse users.
+ *
+ * Download https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt and
+ * $ grep -E '(zero width|invisible|joiner|application)' -i UnicodeData.txt
+ */
+static inline bool is_nonrendering(UChar32 uchr)
+{
+ switch (uchr) {
+ case 0x034F: /* combining grapheme joiner */
+ case 0x200B: /* zero width space */
+ case 0x200C: /* zero width non-joiner */
+ case 0x200D: /* zero width joiner */
+ case 0x2028: /* line separator */
+ case 0x2029: /* paragraph separator */
+ case 0x2060: /* word joiner */
+ case 0x2061: /* function application */
+ case 0x2062: /* invisible times (multiply) */
+ case 0x2063: /* invisible separator (comma) */
+ case 0x2064: /* invisible plus (addition) */
+ case 0x2D7F: /* tifinagh consonant joiner */
+ case 0xFEFF: /* zero width non breaking space */
+ return true;
+ }
+
+ return false;
+}
+
/*
* Generate normalized form and skeleton of the name. If this fails, just
* forget everything and return false; this is an advisory checker.
@@ -349,24 +379,9 @@ name_entry_examine(
uiter_setString(&uiter, entry->normstr, entry->normstrlen);
while ((uchr = uiter_next32(&uiter)) != U_SENTINEL) {
- /* zero width character sequences */
- switch (uchr) {
- case 0x034F: /* combining grapheme joiner */
- case 0x200B: /* zero width space */
- case 0x200C: /* zero width non-joiner */
- case 0x200D: /* zero width joiner */
- case 0x2028: /* line separator */
- case 0x2029: /* paragraph separator */
- case 0x2060: /* word joiner */
- case 0x2061: /* function application */
- case 0x2062: /* invisible times (multiply) */
- case 0x2063: /* invisible separator (comma) */
- case 0x2064: /* invisible plus (addition) */
- case 0x2D7F: /* tifinagh consonant joiner */
- case 0xFEFF: /* zero width non breaking space */
+ /* characters are invisible */
+ if (is_nonrendering(uchr))
*badflags |= UNICRASH_ZERO_WIDTH;
- break;
- }
/* control characters */
if (u_iscntrl(uchr))
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 07/13] xfs_scrub: store bad flags with the name entry
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
` (5 preceding siblings ...)
2024-07-30 1:07 ` [PATCH 06/13] xfs_scrub: hoist non-rendering character predicate Darrick J. Wong
@ 2024-07-30 1:07 ` Darrick J. Wong
2024-07-30 1:07 ` [PATCH 08/13] xfs_scrub: rename UNICRASH_ZERO_WIDTH to UNICRASH_INVISIBLE Darrick J. Wong
` (5 subsequent siblings)
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:07 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
When scrub is checking unicode names, there are certain properties of
the directory/attribute/label name itself that it can complain about.
Store these in struct name_entry so that the confusable names detector
can pick this up later.
This restructuring enables a subsequent patch to detect suspicious
sequences in the NFC normalized form of the name without needing to hang
on to that NFC form until the end of processing. IOWs, it's a memory
usage optimization.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 122 ++++++++++++++++++++++++++++--------------------------
1 file changed, 64 insertions(+), 58 deletions(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 1a86b5f8c..e98f850ab 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -69,6 +69,9 @@ struct name_entry {
xfs_ino_t ino;
+ /* Everything that we don't like about this name. */
+ unsigned int badflags;
+
/* Raw dirent name */
size_t namelen;
char name[0];
@@ -276,6 +279,55 @@ name_entry_compute_checknames(
return false;
}
+/*
+ * Check a name for suspicious elements that have appeared in filename
+ * spoofing attacks. This includes names that mixed directions or contain
+ * direction overrides control characters, both of which have appeared in
+ * filename spoofing attacks.
+ */
+static unsigned int
+name_entry_examine(
+ const struct name_entry *entry)
+{
+ UCharIterator uiter;
+ UChar32 uchr;
+ uint8_t mask = 0;
+ unsigned int ret = 0;
+
+ uiter_setString(&uiter, entry->normstr, entry->normstrlen);
+ while ((uchr = uiter_next32(&uiter)) != U_SENTINEL) {
+ /* characters are invisible */
+ if (is_nonrendering(uchr))
+ ret |= UNICRASH_ZERO_WIDTH;
+
+ /* control characters */
+ if (u_iscntrl(uchr))
+ ret |= UNICRASH_CONTROL_CHAR;
+
+ switch (u_charDirection(uchr)) {
+ case U_LEFT_TO_RIGHT:
+ mask |= 0x01;
+ break;
+ case U_RIGHT_TO_LEFT:
+ mask |= 0x02;
+ break;
+ case U_RIGHT_TO_LEFT_OVERRIDE:
+ ret |= UNICRASH_BIDI_OVERRIDE;
+ break;
+ case U_LEFT_TO_RIGHT_OVERRIDE:
+ ret |= UNICRASH_BIDI_OVERRIDE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* mixing left-to-right and right-to-left chars */
+ if (mask == 0x3)
+ ret |= UNICRASH_BIDI_MIXED;
+ return ret;
+}
+
/* Create a new name entry, returns false if we could not succeed. */
static bool
name_entry_create(
@@ -301,6 +353,7 @@ name_entry_create(
if (!name_entry_compute_checknames(uc, new_entry))
goto out;
+ new_entry->badflags = name_entry_examine(new_entry);
*entry = new_entry;
return true;
@@ -362,54 +415,6 @@ name_entry_hash(
}
}
-/*
- * Check a name for suspicious elements that have appeared in filename
- * spoofing attacks. This includes names that mixed directions or contain
- * direction overrides control characters, both of which have appeared in
- * filename spoofing attacks.
- */
-static void
-name_entry_examine(
- struct name_entry *entry,
- unsigned int *badflags)
-{
- UCharIterator uiter;
- UChar32 uchr;
- uint8_t mask = 0;
-
- uiter_setString(&uiter, entry->normstr, entry->normstrlen);
- while ((uchr = uiter_next32(&uiter)) != U_SENTINEL) {
- /* characters are invisible */
- if (is_nonrendering(uchr))
- *badflags |= UNICRASH_ZERO_WIDTH;
-
- /* control characters */
- if (u_iscntrl(uchr))
- *badflags |= UNICRASH_CONTROL_CHAR;
-
- switch (u_charDirection(uchr)) {
- case U_LEFT_TO_RIGHT:
- mask |= 0x01;
- break;
- case U_RIGHT_TO_LEFT:
- mask |= 0x02;
- break;
- case U_RIGHT_TO_LEFT_OVERRIDE:
- *badflags |= UNICRASH_BIDI_OVERRIDE;
- break;
- case U_LEFT_TO_RIGHT_OVERRIDE:
- *badflags |= UNICRASH_BIDI_OVERRIDE;
- break;
- default:
- break;
- }
- }
-
- /* mixing left-to-right and right-to-left chars */
- if (mask == 0x3)
- *badflags |= UNICRASH_BIDI_MIXED;
-}
-
/* Initialize the collision detector. */
static int
unicrash_init(
@@ -640,17 +645,17 @@ _("Unicode name \"%s\" in %s could be confused with \"%s\"."),
* must be skeletonized according to Unicode TR39 to detect names that
* could be visually confused with each other.
*/
-static void
+static unsigned int
unicrash_add(
struct unicrash *uc,
struct name_entry **new_entryp,
- unsigned int *badflags,
struct name_entry **existing_entry)
{
struct name_entry *new_entry = *new_entryp;
struct name_entry *entry;
size_t bucket;
xfs_dahash_t hash;
+ unsigned int badflags = new_entry->badflags;
/* Store name in hashtable. */
hash = name_entry_hash(new_entry);
@@ -671,28 +676,30 @@ unicrash_add(
uc->buckets[bucket] = new_entry->next;
name_entry_free(new_entry);
*new_entryp = NULL;
- return;
+ return 0;
}
/* Same normalization? */
if (new_entry->normstrlen == entry->normstrlen &&
!u_strcmp(new_entry->normstr, entry->normstr) &&
(uc->compare_ino ? entry->ino != new_entry->ino : true)) {
- *badflags |= UNICRASH_NOT_UNIQUE;
+ badflags |= UNICRASH_NOT_UNIQUE;
*existing_entry = entry;
- return;
+ break;
}
/* Confusable? */
if (new_entry->skelstrlen == entry->skelstrlen &&
!u_strcmp(new_entry->skelstr, entry->skelstr) &&
(uc->compare_ino ? entry->ino != new_entry->ino : true)) {
- *badflags |= UNICRASH_CONFUSABLE;
+ badflags |= UNICRASH_CONFUSABLE;
*existing_entry = entry;
- return;
+ break;
}
entry = entry->next;
}
+
+ return badflags;
}
/* Check a name for unicode normalization problems or collisions. */
@@ -706,14 +713,13 @@ __unicrash_check_name(
{
struct name_entry *dup_entry = NULL;
struct name_entry *new_entry = NULL;
- unsigned int badflags = 0;
+ unsigned int badflags;
/* If we can't create entry data, just skip it. */
if (!name_entry_create(uc, name, ino, &new_entry))
return 0;
- name_entry_examine(new_entry, &badflags);
- unicrash_add(uc, &new_entry, &badflags, &dup_entry);
+ badflags = unicrash_add(uc, &new_entry, &dup_entry);
if (new_entry && badflags)
unicrash_complain(uc, dsc, namedescr, new_entry, badflags,
dup_entry);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 08/13] xfs_scrub: rename UNICRASH_ZERO_WIDTH to UNICRASH_INVISIBLE
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
` (6 preceding siblings ...)
2024-07-30 1:07 ` [PATCH 07/13] xfs_scrub: store bad flags with the name entry Darrick J. Wong
@ 2024-07-30 1:07 ` Darrick J. Wong
2024-07-30 1:08 ` [PATCH 09/13] xfs_scrub: type-coerce the UNICRASH_* flags Darrick J. Wong
` (4 subsequent siblings)
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:07 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
"Zero width" doesn't fully describe what the flag represents -- it gets
set for any codepoint that doesn't render. Rename it accordingly.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index e98f850ab..5447d94f0 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -109,7 +109,7 @@ struct unicrash {
#define UNICRASH_CONTROL_CHAR (1 << 3)
/* Invisible characters. Only a problem if we have collisions. */
-#define UNICRASH_ZERO_WIDTH (1 << 4)
+#define UNICRASH_INVISIBLE (1 << 4)
/* Multiple names resolve to the same skeleton string. */
#define UNICRASH_CONFUSABLE (1 << 5)
@@ -298,7 +298,7 @@ name_entry_examine(
while ((uchr = uiter_next32(&uiter)) != U_SENTINEL) {
/* characters are invisible */
if (is_nonrendering(uchr))
- ret |= UNICRASH_ZERO_WIDTH;
+ ret |= UNICRASH_INVISIBLE;
/* control characters */
if (u_iscntrl(uchr))
@@ -582,7 +582,7 @@ _("Unicode name \"%s\" in %s renders identically to \"%s\"."),
* confused with another name as a result, we should complain.
* "moo<zerowidthspace>cow" and "moocow" are misleading.
*/
- if ((badflags & UNICRASH_ZERO_WIDTH) &&
+ if ((badflags & UNICRASH_INVISIBLE) &&
(badflags & UNICRASH_CONFUSABLE)) {
str_warn(uc->ctx, descr_render(dsc),
_("Unicode name \"%s\" in %s could be confused with '%s' due to invisible characters."),
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 09/13] xfs_scrub: type-coerce the UNICRASH_* flags
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
` (7 preceding siblings ...)
2024-07-30 1:07 ` [PATCH 08/13] xfs_scrub: rename UNICRASH_ZERO_WIDTH to UNICRASH_INVISIBLE Darrick J. Wong
@ 2024-07-30 1:08 ` Darrick J. Wong
2024-07-30 1:08 ` [PATCH 10/13] xfs_scrub: reduce size of struct name_entry Darrick J. Wong
` (3 subsequent siblings)
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:08 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Promote this type to something that we can type-check.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 30 ++++++++++++++++++------------
1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 5447d94f0..63694c39a 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -4,6 +4,7 @@
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#include "xfs.h"
+#include "xfs_arch.h"
#include <stdint.h>
#include <stdlib.h>
#include <dirent.h>
@@ -56,6 +57,8 @@
* In other words, skel = remove_invisible(nfd(remap_confusables(nfd(name)))).
*/
+typedef unsigned int __bitwise badname_t;
+
struct name_entry {
struct name_entry *next;
@@ -70,7 +73,7 @@ struct name_entry {
xfs_ino_t ino;
/* Everything that we don't like about this name. */
- unsigned int badflags;
+ badname_t badflags;
/* Raw dirent name */
size_t namelen;
@@ -93,26 +96,29 @@ struct unicrash {
/* Things to complain about in Unicode naming. */
+/* Everything is ok */
+#define UNICRASH_OK ((__force badname_t)0)
+
/*
* Multiple names resolve to the same normalized string and therefore render
* identically.
*/
-#define UNICRASH_NOT_UNIQUE (1 << 0)
+#define UNICRASH_NOT_UNIQUE ((__force badname_t)(1U << 0))
/* Name contains directional overrides. */
-#define UNICRASH_BIDI_OVERRIDE (1 << 1)
+#define UNICRASH_BIDI_OVERRIDE ((__force badname_t)(1U << 1))
/* Name mixes left-to-right and right-to-left characters. */
-#define UNICRASH_BIDI_MIXED (1 << 2)
+#define UNICRASH_BIDI_MIXED ((__force badname_t)(1U << 2))
/* Control characters in name. */
-#define UNICRASH_CONTROL_CHAR (1 << 3)
+#define UNICRASH_CONTROL_CHAR ((__force badname_t)(1U << 3))
/* Invisible characters. Only a problem if we have collisions. */
-#define UNICRASH_INVISIBLE (1 << 4)
+#define UNICRASH_INVISIBLE ((__force badname_t)(1U << 4))
/* Multiple names resolve to the same skeleton string. */
-#define UNICRASH_CONFUSABLE (1 << 5)
+#define UNICRASH_CONFUSABLE ((__force badname_t)(1U << 5))
/*
* We only care about validating utf8 collisions if the underlying
@@ -542,7 +548,7 @@ unicrash_complain(
struct descr *dsc,
const char *what,
struct name_entry *entry,
- unsigned int badflags,
+ badname_t badflags,
struct name_entry *dup_entry)
{
char *bad1 = NULL;
@@ -645,7 +651,7 @@ _("Unicode name \"%s\" in %s could be confused with \"%s\"."),
* must be skeletonized according to Unicode TR39 to detect names that
* could be visually confused with each other.
*/
-static unsigned int
+static badname_t
unicrash_add(
struct unicrash *uc,
struct name_entry **new_entryp,
@@ -655,7 +661,7 @@ unicrash_add(
struct name_entry *entry;
size_t bucket;
xfs_dahash_t hash;
- unsigned int badflags = new_entry->badflags;
+ badname_t badflags = new_entry->badflags;
/* Store name in hashtable. */
hash = name_entry_hash(new_entry);
@@ -713,14 +719,14 @@ __unicrash_check_name(
{
struct name_entry *dup_entry = NULL;
struct name_entry *new_entry = NULL;
- unsigned int badflags;
+ badname_t badflags;
/* If we can't create entry data, just skip it. */
if (!name_entry_create(uc, name, ino, &new_entry))
return 0;
badflags = unicrash_add(uc, &new_entry, &dup_entry);
- if (new_entry && badflags)
+ if (new_entry && badflags != UNICRASH_OK)
unicrash_complain(uc, dsc, namedescr, new_entry, badflags,
dup_entry);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 10/13] xfs_scrub: reduce size of struct name_entry
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
` (8 preceding siblings ...)
2024-07-30 1:08 ` [PATCH 09/13] xfs_scrub: type-coerce the UNICRASH_* flags Darrick J. Wong
@ 2024-07-30 1:08 ` Darrick J. Wong
2024-07-30 1:08 ` [PATCH 11/13] xfs_scrub: rename struct unicrash.normalizer Darrick J. Wong
` (2 subsequent siblings)
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:08 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
libicu doesn't support processing strings longer than 2GB in length, and
we never feed the unicrash code a name longer than about 300 bytes.
Rearrange the structure to reduce the head structure size from 56 bytes
to 44 bytes.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 63694c39a..74c0fe1f9 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -57,18 +57,20 @@
* In other words, skel = remove_invisible(nfd(remap_confusables(nfd(name)))).
*/
-typedef unsigned int __bitwise badname_t;
+typedef uint16_t __bitwise badname_t;
struct name_entry {
struct name_entry *next;
/* NFKC normalized name */
UChar *normstr;
- size_t normstrlen;
/* Unicode skeletonized name */
UChar *skelstr;
- size_t skelstrlen;
+
+ /* Lengths for normstr and skelstr */
+ int32_t normstrlen;
+ int32_t skelstrlen;
xfs_ino_t ino;
@@ -76,7 +78,7 @@ struct name_entry {
badname_t badflags;
/* Raw dirent name */
- size_t namelen;
+ uint16_t namelen;
char name[0];
};
#define NAME_ENTRY_SZ(nl) (sizeof(struct name_entry) + 1 + \
@@ -345,6 +347,12 @@ name_entry_create(
struct name_entry *new_entry;
size_t namelen = strlen(name);
+ /* should never happen */
+ if (namelen > UINT16_MAX) {
+ ASSERT(namelen <= UINT16_MAX);
+ return false;
+ }
+
/* Create new entry */
new_entry = calloc(NAME_ENTRY_SZ(namelen), 1);
if (!new_entry)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 11/13] xfs_scrub: rename struct unicrash.normalizer
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
` (9 preceding siblings ...)
2024-07-30 1:08 ` [PATCH 10/13] xfs_scrub: reduce size of struct name_entry Darrick J. Wong
@ 2024-07-30 1:08 ` Darrick J. Wong
2024-07-30 1:08 ` [PATCH 12/13] xfs_scrub: report deceptive file extensions Darrick J. Wong
2024-07-30 1:09 ` [PATCH 13/13] xfs_scrub: dump unicode points Darrick J. Wong
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:08 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
We're about to introduce a second normalizer, so change the name of the
existing one to reflect the algorithm that you'll get if you use it.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 74c0fe1f9..9cde9afff 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -87,7 +87,7 @@ struct name_entry {
struct unicrash {
struct scrub_ctx *ctx;
USpoofChecker *spoof;
- const UNormalizer2 *normalizer;
+ const UNormalizer2 *nfkc;
bool compare_ino;
bool is_only_root_writeable;
size_t nr_buckets;
@@ -242,7 +242,7 @@ name_entry_compute_checknames(
goto out_unistr;
/* Normalize the string. */
- normstrlen = unorm2_normalize(uc->normalizer, unistr, unistrlen, NULL,
+ normstrlen = unorm2_normalize(uc->nfkc, unistr, unistrlen, NULL,
0, &uerr);
if (uerr != U_BUFFER_OVERFLOW_ERROR || normstrlen < 0)
goto out_unistr;
@@ -250,7 +250,7 @@ name_entry_compute_checknames(
normstr = calloc(normstrlen + 1, sizeof(UChar));
if (!normstr)
goto out_unistr;
- unorm2_normalize(uc->normalizer, unistr, unistrlen, normstr, normstrlen,
+ unorm2_normalize(uc->nfkc, unistr, unistrlen, normstr, normstrlen,
&uerr);
if (U_FAILURE(uerr))
goto out_normstr;
@@ -457,7 +457,7 @@ unicrash_init(
p->ctx = ctx;
p->nr_buckets = nr_buckets;
p->compare_ino = compare_ino;
- p->normalizer = unorm2_getNFKCInstance(&uerr);
+ p->nfkc = unorm2_getNFKCInstance(&uerr);
if (U_FAILURE(uerr))
goto out_free;
p->spoof = uspoof_open(&uerr);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 12/13] xfs_scrub: report deceptive file extensions
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
` (10 preceding siblings ...)
2024-07-30 1:08 ` [PATCH 11/13] xfs_scrub: rename struct unicrash.normalizer Darrick J. Wong
@ 2024-07-30 1:08 ` Darrick J. Wong
2024-07-30 1:09 ` [PATCH 13/13] xfs_scrub: dump unicode points Darrick J. Wong
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:08 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Earlier this year, ESET revealed that Linux users had been tricked into
opening executables containing malware payloads. The trickery came in
the form of a malicious zip file containing a filename with the string
"job offer․pdf". Note that the filename does *not* denote a real pdf
file, since the last four codepoints in the file name are "ONE DOT
LEADER", p, d, and f. Not period (ok, FULL STOP), p, d, f like you'd
normally expect.
Teach xfs_scrub to look for codepoints that could be confused with a
period followed by alphanumerics.
Link: https://www.welivesecurity.com/2023/04/20/linux-malware-strengthens-links-lazarus-3cx-supply-chain-attack/
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 214 insertions(+), 1 deletion(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 9cde9afff..8a896f33c 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -88,6 +88,7 @@ struct unicrash {
struct scrub_ctx *ctx;
USpoofChecker *spoof;
const UNormalizer2 *nfkc;
+ const UNormalizer2 *nfc;
bool compare_ino;
bool is_only_root_writeable;
size_t nr_buckets;
@@ -122,6 +123,12 @@ struct unicrash {
/* Multiple names resolve to the same skeleton string. */
#define UNICRASH_CONFUSABLE ((__force badname_t)(1U << 5))
+/* Possible phony file extension. */
+#define UNICRASH_PHONY_EXTENSION ((__force badname_t)(1U << 6))
+
+/* FULL STOP (aka period), 0x2E */
+#define UCHAR_PERIOD ((UChar32)'.')
+
/*
* We only care about validating utf8 collisions if the underlying
* system configuration says we're using utf8. If the language
@@ -211,6 +218,193 @@ static inline bool is_nonrendering(UChar32 uchr)
return false;
}
+/*
+ * Decide if this unicode codepoint looks similar enough to a period (".")
+ * to fool users into thinking that any subsequent alphanumeric sequence is
+ * the file extension. Most of the fullstop characters do not do this.
+ *
+ * $ grep -i 'full stop' UnicodeData.txt
+ */
+static inline bool is_fullstop_lookalike(UChar32 uchr)
+{
+ switch (uchr) {
+ case 0x0701: /* syriac supralinear full stop */
+ case 0x0702: /* syriac sublinear full stop */
+ case 0x2024: /* one dot leader */
+ case 0xA4F8: /* lisu letter tone mya ti */
+ case 0xFE52: /* small full stop */
+ case 0xFF61: /* haflwidth ideographic full stop */
+ case 0xFF0E: /* fullwidth full stop */
+ return true;
+ }
+
+ return false;
+}
+
+/* How many UChar do we need to fit a full UChar32 codepoint? */
+#define UCHAR_PER_UCHAR32 2
+
+/* Format this UChar32 into a UChar buffer. */
+static inline int32_t
+uchar32_to_uchar(
+ UChar32 uchr,
+ UChar *buf)
+{
+ int32_t i = 0;
+ bool err = false;
+
+ U16_APPEND(buf, i, UCHAR_PER_UCHAR32, uchr, err);
+ if (err)
+ return 0;
+ return i;
+}
+
+/* Extract a single UChar32 code point from this UChar string. */
+static inline UChar32
+uchar_to_uchar32(
+ UChar *buf,
+ int32_t buflen)
+{
+ UChar32 ret;
+ int32_t i = 0;
+
+ U16_NEXT(buf, i, buflen, ret);
+ return ret;
+}
+
+/*
+ * For characters that are not themselves a full stop (0x2E), let's see if the
+ * compatibility normalization (NFKC) will turn it into a full stop. If so,
+ * then this could be the start of a phony file extension.
+ */
+static bool
+is_period_lookalike(
+ struct unicrash *uc,
+ UChar32 uchr)
+{
+ UChar uchrstr[UCHAR_PER_UCHAR32];
+ UChar nfkcstr[UCHAR_PER_UCHAR32];
+ int32_t uchrstrlen, nfkcstrlen;
+ UChar32 nfkc_uchr;
+ UErrorCode uerr = U_ZERO_ERROR;
+
+ if (uchr == UCHAR_PERIOD)
+ return false;
+
+ uchrstrlen = uchar32_to_uchar(uchr, uchrstr);
+ if (!uchrstrlen)
+ return false;
+
+ /*
+ * Normalize the UChar string to NFKC form, which does all the
+ * compatibility transformations.
+ */
+ nfkcstrlen = unorm2_normalize(uc->nfkc, uchrstr, uchrstrlen, NULL,
+ 0, &uerr);
+ if (uerr == U_BUFFER_OVERFLOW_ERROR)
+ return false;
+
+ uerr = U_ZERO_ERROR;
+ unorm2_normalize(uc->nfkc, uchrstr, uchrstrlen, nfkcstr, nfkcstrlen,
+ &uerr);
+ if (U_FAILURE(uerr))
+ return false;
+
+ nfkc_uchr = uchar_to_uchar32(nfkcstr, nfkcstrlen);
+ return nfkc_uchr == UCHAR_PERIOD;
+}
+
+/*
+ * Detect directory entry names that contain deceptive sequences that look like
+ * file extensions but are not. This we define as a sequence that begins with
+ * a code point that renders like a period ("full stop" in unicode parlance)
+ * but is not actually a period, followed by any number of alphanumeric code
+ * points or a period, all the way to the end.
+ *
+ * The 3cx attack used a zip file containing an executable file named "job
+ * offer․pdf". Note that the dot mark in the extension is /not/ a period but
+ * the Unicode codepoint "leader dot". The file was also marked executable
+ * inside the zip file, which meant that naïve file explorers could inflate
+ * the file and restore the execute bit. If a user double-clicked on the file,
+ * the binary would open a decoy pdf while infecting the system.
+ *
+ * For this check, we need to normalize with canonical (and not compatibility)
+ * decomposition, because compatibility mode will turn certain code points
+ * (e.g. one dot leader, 0x2024) into actual periods (0x2e). The NFC
+ * composition is not needed after this, so we save some memory by keeping this
+ * a separate function from name_entry_examine.
+ */
+static badname_t
+name_entry_phony_extension(
+ struct unicrash *uc,
+ const UChar *unistr,
+ int32_t unistrlen)
+{
+ UCharIterator uiter;
+ UChar *nfcstr;
+ int32_t nfcstrlen;
+ UChar32 uchr;
+ bool maybe_phony_extension = false;
+ badname_t ret = UNICRASH_OK;
+ UErrorCode uerr = U_ZERO_ERROR;
+
+ /* Normalize with NFC. */
+ nfcstrlen = unorm2_normalize(uc->nfc, unistr, unistrlen, NULL,
+ 0, &uerr);
+ if (uerr != U_BUFFER_OVERFLOW_ERROR || nfcstrlen < 0)
+ return ret;
+ uerr = U_ZERO_ERROR;
+ nfcstr = calloc(nfcstrlen + 1, sizeof(UChar));
+ if (!nfcstr)
+ return ret;
+ unorm2_normalize(uc->nfc, unistr, unistrlen, nfcstr, nfcstrlen,
+ &uerr);
+ if (U_FAILURE(uerr))
+ goto out_nfcstr;
+
+ /* Examine the NFC normalized string... */
+ uiter_setString(&uiter, nfcstr, nfcstrlen);
+ while ((uchr = uiter_next32(&uiter)) != U_SENTINEL) {
+ /*
+ * If this *looks* like, but is not, a full stop (0x2E), this
+ * could be the start of a phony file extension.
+ */
+ if (is_period_lookalike(uc, uchr)) {
+ maybe_phony_extension = true;
+ continue;
+ }
+
+ if (is_fullstop_lookalike(uchr)) {
+ /*
+ * The normalizer above should catch most of these
+ * codepoints that look like periods, but record the
+ * ones known to have been used in attacks.
+ */
+ maybe_phony_extension = true;
+ } else if (uchr == UCHAR_PERIOD) {
+ /*
+ * Due to the propensity of file explorers to obscure
+ * file extensions in the name of "user friendliness",
+ * this classifier ignores periods.
+ */
+ } else {
+ /*
+ * File extensions (as far as the author knows) tend
+ * only to use ascii alphanumerics.
+ */
+ if (maybe_phony_extension &&
+ !u_isalnum(uchr) && !is_nonrendering(uchr))
+ maybe_phony_extension = false;
+ }
+ }
+ if (maybe_phony_extension)
+ ret |= UNICRASH_PHONY_EXTENSION;
+
+out_nfcstr:
+ free(nfcstr);
+ return ret;
+}
+
/*
* Generate normalized form and skeleton of the name. If this fails, just
* forget everything and return false; this is an advisory checker.
@@ -271,6 +465,11 @@ name_entry_compute_checknames(
skelstrlen = remove_ignorable(skelstr, skelstrlen);
+ /* Check for deceptive file extensions in directory entry names. */
+ if (entry->ino)
+ entry->badflags |= name_entry_phony_extension(uc, unistr,
+ unistrlen);
+
entry->skelstr = skelstr;
entry->skelstrlen = skelstrlen;
entry->normstr = normstr;
@@ -367,7 +566,7 @@ name_entry_create(
if (!name_entry_compute_checknames(uc, new_entry))
goto out;
- new_entry->badflags = name_entry_examine(new_entry);
+ new_entry->badflags |= name_entry_examine(new_entry);
*entry = new_entry;
return true;
@@ -458,6 +657,9 @@ unicrash_init(
p->nr_buckets = nr_buckets;
p->compare_ino = compare_ino;
p->nfkc = unorm2_getNFKCInstance(&uerr);
+ if (U_FAILURE(uerr))
+ goto out_free;
+ p->nfc = unorm2_getNFCInstance(&uerr);
if (U_FAILURE(uerr))
goto out_free;
p->spoof = uspoof_open(&uerr);
@@ -604,6 +806,17 @@ _("Unicode name \"%s\" in %s could be confused with '%s' due to invisible charac
goto out;
}
+ /*
+ * Fake looking file extensions have tricked Linux users into thinking
+ * that an executable is actually a pdf. See Lazarus 3cx attack.
+ */
+ if (badflags & UNICRASH_PHONY_EXTENSION) {
+ str_warn(uc->ctx, descr_render(dsc),
+_("Unicode name \"%s\" in %s contains a possibly deceptive file extension."),
+ bad1, what);
+ goto out;
+ }
+
/*
* Unfiltered control characters can mess up your terminal and render
* invisibly in filechooser UIs.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 13/13] xfs_scrub: dump unicode points
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
` (11 preceding siblings ...)
2024-07-30 1:08 ` [PATCH 12/13] xfs_scrub: report deceptive file extensions Darrick J. Wong
@ 2024-07-30 1:09 ` Darrick J. Wong
12 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:09 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add some debug functions to make it easier to query unicode character
properties.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/unicrash.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 57 insertions(+), 2 deletions(-)
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 8a896f33c..143060b56 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -5,6 +5,7 @@
*/
#include "xfs.h"
#include "xfs_arch.h"
+#include "list.h"
#include <stdint.h>
#include <stdlib.h>
#include <dirent.h>
@@ -1003,14 +1004,68 @@ unicrash_check_fs_label(
label, 0);
}
+/* Dump a unicode code point and its properties. */
+static inline void dump_uchar32(UChar32 c)
+{
+ UChar uchrstr[UCHAR_PER_UCHAR32];
+ const char *descr;
+ char buf[16];
+ int32_t uchrstrlen, buflen;
+ UProperty p;
+ UErrorCode uerr = U_ZERO_ERROR;
+
+ printf("Unicode point 0x%x:", c);
+
+ /* Convert UChar32 to UTF8 representation. */
+ uchrstrlen = uchar32_to_uchar(c, uchrstr);
+ if (!uchrstrlen)
+ return;
+
+ u_strToUTF8(buf, sizeof(buf), &buflen, uchrstr, uchrstrlen, &uerr);
+ if (!U_FAILURE(uerr) && buflen > 0) {
+ int32_t i;
+
+ printf(" \"");
+ for (i = 0; i < buflen; i++)
+ printf("\\x%02x", buf[i]);
+ printf("\"");
+ }
+ printf("\n");
+
+ for (p = 0; p < UCHAR_BINARY_LIMIT; p++) {
+ int has;
+
+ descr = u_getPropertyName(p, U_LONG_PROPERTY_NAME);
+ if (!descr)
+ descr = u_getPropertyName(p, U_SHORT_PROPERTY_NAME);
+
+ has = u_hasBinaryProperty(c, p) ? 1 : 0;
+ if (descr) {
+ printf(" %s(%u) = %d\n", descr, p, has);
+ } else {
+ printf(" ?(%u) = %d\n", p, has);
+ }
+ }
+}
+
/* Load libicu and initialize it. */
bool
unicrash_load(void)
{
- UErrorCode uerr = U_ZERO_ERROR;
+ char *dbgstr;
+ UChar32 uchr;
+ UErrorCode uerr = U_ZERO_ERROR;
u_init(&uerr);
- return U_FAILURE(uerr);
+ if (U_FAILURE(uerr))
+ return true;
+
+ dbgstr = getenv("XFS_SCRUB_DUMP_CHAR");
+ if (dbgstr) {
+ uchr = strtol(dbgstr, NULL, 0);
+ dump_uchar32(uchr);
+ }
+ return false;
}
/* Unload libicu once we're done with it. */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/7] xfs_scrub: move FITRIM to phase 8
2024-07-30 0:19 ` [PATCHSET v30.9 12/23] xfs_scrub: move fstrim to a separate phase Darrick J. Wong
@ 2024-07-30 1:09 ` Darrick J. Wong
2024-07-30 1:09 ` [PATCH 2/7] xfs_scrub: ignore phase 8 if the user disabled fstrim Darrick J. Wong
` (5 subsequent siblings)
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:09 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Issuing discards against the filesystem should be the *last* thing that
xfs_scrub does, after everything else has been checked, repaired, and
found to be clean. If we can't satisfy all those conditions, we have no
business telling the storage to discard itself.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/Makefile | 1 +
scrub/phase4.c | 30 ++----------------------
scrub/phase8.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++
scrub/xfs_scrub.h | 3 ++
4 files changed, 73 insertions(+), 27 deletions(-)
create mode 100644 scrub/phase8.c
diff --git a/scrub/Makefile b/scrub/Makefile
index c11c2b5fe..8ccc67d01 100644
--- a/scrub/Makefile
+++ b/scrub/Makefile
@@ -68,6 +68,7 @@ phase4.c \
phase5.c \
phase6.c \
phase7.c \
+phase8.c \
progress.c \
read_verify.c \
repair.c \
diff --git a/scrub/phase4.c b/scrub/phase4.c
index 9080d3881..451101811 100644
--- a/scrub/phase4.c
+++ b/scrub/phase4.c
@@ -227,16 +227,6 @@ repair_everything(
return action_list_process(ctx, ctx->fs_repair_list, XRM_FINAL_WARNING);
}
-/* Trim the unused areas of the filesystem if the caller asked us to. */
-static void
-trim_filesystem(
- struct scrub_ctx *ctx)
-{
- if (want_fstrim)
- fstrim(ctx);
- progress_add(1);
-}
-
/* Fix everything that needs fixing. */
int
phase4_func(
@@ -248,7 +238,7 @@ phase4_func(
if (action_list_empty(ctx->fs_repair_list) &&
action_list_empty(ctx->file_repair_list))
- goto maybe_trim;
+ return 0;
/*
* Check the resource usage counters early. Normally we do this during
@@ -281,20 +271,7 @@ phase4_func(
if (ret)
return ret;
- ret = repair_everything(ctx);
- if (ret)
- return ret;
-
- /*
- * If errors remain on the filesystem, do not trim anything. We don't
- * have any threads running, so it's ok to skip the ctx lock here.
- */
- if (ctx->corruptions_found || ctx->unfixable_errors != 0)
- return 0;
-
-maybe_trim:
- trim_filesystem(ctx);
- return 0;
+ return repair_everything(ctx);
}
/* Estimate how much work we're going to do. */
@@ -307,10 +284,9 @@ phase4_estimate(
{
unsigned long long need_fixing;
- /* Everything on the repair list plus FSTRIM. */
+ /* Everything on the repair lis. */
need_fixing = action_list_length(ctx->fs_repair_list) +
action_list_length(ctx->file_repair_list);
- need_fixing++;
*items = need_fixing;
*nr_threads = scrub_nproc(ctx) + 1;
diff --git a/scrub/phase8.c b/scrub/phase8.c
new file mode 100644
index 000000000..07726b5b8
--- /dev/null
+++ b/scrub/phase8.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2018-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "xfs.h"
+#include <stdint.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#include "list.h"
+#include "libfrog/paths.h"
+#include "libfrog/workqueue.h"
+#include "xfs_scrub.h"
+#include "common.h"
+#include "progress.h"
+#include "scrub.h"
+#include "repair.h"
+#include "vfs.h"
+#include "atomic.h"
+
+/* Phase 8: Trim filesystem. */
+
+/* Trim the unused areas of the filesystem if the caller asked us to. */
+static void
+trim_filesystem(
+ struct scrub_ctx *ctx)
+{
+ fstrim(ctx);
+ progress_add(1);
+}
+
+/* Trim the filesystem, if desired. */
+int
+phase8_func(
+ struct scrub_ctx *ctx)
+{
+ if (action_list_empty(ctx->fs_repair_list) &&
+ action_list_empty(ctx->file_repair_list))
+ goto maybe_trim;
+
+ /*
+ * If errors remain on the filesystem, do not trim anything. We don't
+ * have any threads running, so it's ok to skip the ctx lock here.
+ */
+ if (ctx->corruptions_found || ctx->unfixable_errors != 0)
+ return 0;
+
+maybe_trim:
+ trim_filesystem(ctx);
+ return 0;
+}
+
+/* Estimate how much work we're going to do. */
+int
+phase8_estimate(
+ struct scrub_ctx *ctx,
+ uint64_t *items,
+ unsigned int *nr_threads,
+ int *rshift)
+{
+ *items = 1;
+ *nr_threads = 1;
+ *rshift = 0;
+ return 0;
+}
diff --git a/scrub/xfs_scrub.h b/scrub/xfs_scrub.h
index ed86d0093..6272a3687 100644
--- a/scrub/xfs_scrub.h
+++ b/scrub/xfs_scrub.h
@@ -98,6 +98,7 @@ int phase4_func(struct scrub_ctx *ctx);
int phase5_func(struct scrub_ctx *ctx);
int phase6_func(struct scrub_ctx *ctx);
int phase7_func(struct scrub_ctx *ctx);
+int phase8_func(struct scrub_ctx *ctx);
/* Progress estimator functions */
unsigned int scrub_estimate_ag_work(struct scrub_ctx *ctx);
@@ -112,5 +113,7 @@ int phase5_estimate(struct scrub_ctx *ctx, uint64_t *items,
unsigned int *nr_threads, int *rshift);
int phase6_estimate(struct scrub_ctx *ctx, uint64_t *items,
unsigned int *nr_threads, int *rshift);
+int phase8_estimate(struct scrub_ctx *ctx, uint64_t *items,
+ unsigned int *nr_threads, int *rshift);
#endif /* XFS_SCRUB_XFS_SCRUB_H_ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/7] xfs_scrub: ignore phase 8 if the user disabled fstrim
2024-07-30 0:19 ` [PATCHSET v30.9 12/23] xfs_scrub: move fstrim to a separate phase Darrick J. Wong
2024-07-30 1:09 ` [PATCH 1/7] xfs_scrub: move FITRIM to phase 8 Darrick J. Wong
@ 2024-07-30 1:09 ` Darrick J. Wong
2024-07-30 1:09 ` [PATCH 3/7] xfs_scrub: collapse trim_filesystem Darrick J. Wong
` (4 subsequent siblings)
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:09 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If the user told us to skip trimming the filesystem, don't run the phase
at all.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/xfs_scrub.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/scrub/xfs_scrub.c b/scrub/xfs_scrub.c
index e49538ca1..edf58d07b 100644
--- a/scrub/xfs_scrub.c
+++ b/scrub/xfs_scrub.c
@@ -249,6 +249,7 @@ struct phase_rusage {
/* Operations for each phase. */
#define DATASCAN_DUMMY_FN ((void *)1)
#define REPAIR_DUMMY_FN ((void *)2)
+#define FSTRIM_DUMMY_FN ((void *)3)
struct phase_ops {
char *descr;
int (*fn)(struct scrub_ctx *ctx);
@@ -429,6 +430,11 @@ run_scrub_phases(
.fn = phase7_func,
.must_run = true,
},
+ {
+ .descr = _("Trim filesystem storage."),
+ .fn = FSTRIM_DUMMY_FN,
+ .estimate_work = phase8_estimate,
+ },
{
NULL
},
@@ -449,6 +455,8 @@ run_scrub_phases(
/* Turn on certain phases if user said to. */
if (sp->fn == DATASCAN_DUMMY_FN && scrub_data) {
sp->fn = phase6_func;
+ } else if (sp->fn == FSTRIM_DUMMY_FN && want_fstrim) {
+ sp->fn = phase8_func;
} else if (sp->fn == REPAIR_DUMMY_FN &&
ctx->mode == SCRUB_MODE_REPAIR) {
sp->descr = _("Repair filesystem.");
@@ -458,7 +466,8 @@ run_scrub_phases(
/* Skip certain phases unless they're turned on. */
if (sp->fn == REPAIR_DUMMY_FN ||
- sp->fn == DATASCAN_DUMMY_FN)
+ sp->fn == DATASCAN_DUMMY_FN ||
+ sp->fn == FSTRIM_DUMMY_FN)
continue;
/* Allow debug users to force a particular phase. */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/7] xfs_scrub: collapse trim_filesystem
2024-07-30 0:19 ` [PATCHSET v30.9 12/23] xfs_scrub: move fstrim to a separate phase Darrick J. Wong
2024-07-30 1:09 ` [PATCH 1/7] xfs_scrub: move FITRIM to phase 8 Darrick J. Wong
2024-07-30 1:09 ` [PATCH 2/7] xfs_scrub: ignore phase 8 if the user disabled fstrim Darrick J. Wong
@ 2024-07-30 1:09 ` Darrick J. Wong
2024-07-30 1:10 ` [PATCH 4/7] xfs_scrub: fix the work estimation for phase 8 Darrick J. Wong
` (3 subsequent siblings)
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:09 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Collapse this two-line helper into the main function since it's trivial.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase8.c | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/scrub/phase8.c b/scrub/phase8.c
index 07726b5b8..e577260a9 100644
--- a/scrub/phase8.c
+++ b/scrub/phase8.c
@@ -21,15 +21,6 @@
/* Phase 8: Trim filesystem. */
-/* Trim the unused areas of the filesystem if the caller asked us to. */
-static void
-trim_filesystem(
- struct scrub_ctx *ctx)
-{
- fstrim(ctx);
- progress_add(1);
-}
-
/* Trim the filesystem, if desired. */
int
phase8_func(
@@ -47,7 +38,8 @@ phase8_func(
return 0;
maybe_trim:
- trim_filesystem(ctx);
+ fstrim(ctx);
+ progress_add(1);
return 0;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/7] xfs_scrub: fix the work estimation for phase 8
2024-07-30 0:19 ` [PATCHSET v30.9 12/23] xfs_scrub: move fstrim to a separate phase Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:09 ` [PATCH 3/7] xfs_scrub: collapse trim_filesystem Darrick J. Wong
@ 2024-07-30 1:10 ` Darrick J. Wong
2024-07-30 1:10 ` [PATCH 5/7] xfs_scrub: report FITRIM errors properly Darrick J. Wong
` (2 subsequent siblings)
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:10 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If there are latent errors on the filesystem, we aren't going to do any
work during phase 8 and it makes no sense to add that into the work
estimate for the progress bar.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase8.c | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/scrub/phase8.c b/scrub/phase8.c
index e577260a9..dfe62e8d9 100644
--- a/scrub/phase8.c
+++ b/scrub/phase8.c
@@ -21,23 +21,35 @@
/* Phase 8: Trim filesystem. */
-/* Trim the filesystem, if desired. */
-int
-phase8_func(
+static inline bool
+fstrim_ok(
struct scrub_ctx *ctx)
{
- if (action_list_empty(ctx->fs_repair_list) &&
- action_list_empty(ctx->file_repair_list))
- goto maybe_trim;
-
/*
* If errors remain on the filesystem, do not trim anything. We don't
* have any threads running, so it's ok to skip the ctx lock here.
*/
- if (ctx->corruptions_found || ctx->unfixable_errors != 0)
+ if (!action_list_empty(ctx->fs_repair_list))
+ return false;
+ if (!action_list_empty(ctx->file_repair_list))
+ return false;
+
+ if (ctx->corruptions_found != 0)
+ return false;
+ if (ctx->unfixable_errors != 0)
+ return false;
+
+ return true;
+}
+
+/* Trim the filesystem, if desired. */
+int
+phase8_func(
+ struct scrub_ctx *ctx)
+{
+ if (!fstrim_ok(ctx))
return 0;
-maybe_trim:
fstrim(ctx);
progress_add(1);
return 0;
@@ -51,7 +63,11 @@ phase8_estimate(
unsigned int *nr_threads,
int *rshift)
{
- *items = 1;
+ *items = 0;
+
+ if (fstrim_ok(ctx))
+ *items = 1;
+
*nr_threads = 1;
*rshift = 0;
return 0;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/7] xfs_scrub: report FITRIM errors properly
2024-07-30 0:19 ` [PATCHSET v30.9 12/23] xfs_scrub: move fstrim to a separate phase Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:10 ` [PATCH 4/7] xfs_scrub: fix the work estimation for phase 8 Darrick J. Wong
@ 2024-07-30 1:10 ` Darrick J. Wong
2024-07-30 1:10 ` [PATCH 6/7] xfs_scrub: don't call FITRIM after runtime errors Darrick J. Wong
2024-07-30 1:11 ` [PATCH 7/7] xfs_scrub: improve responsiveness while trimming the filesystem Darrick J. Wong
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:10 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Move the error reporting for the FITRIM ioctl out of vfs.c and into
phase8.c. This makes it so that IO errors encountered during trim are
counted as runtime errors instead of being dropped silently.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase8.c | 12 +++++++++++-
scrub/vfs.c | 12 +++++++-----
scrub/vfs.h | 2 +-
3 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/scrub/phase8.c b/scrub/phase8.c
index dfe62e8d9..288800a76 100644
--- a/scrub/phase8.c
+++ b/scrub/phase8.c
@@ -47,10 +47,20 @@ int
phase8_func(
struct scrub_ctx *ctx)
{
+ int error;
+
if (!fstrim_ok(ctx))
return 0;
- fstrim(ctx);
+ error = fstrim(ctx);
+ if (error == EOPNOTSUPP)
+ return 0;
+
+ if (error) {
+ str_liberror(ctx, error, _("fstrim"));
+ return error;
+ }
+
progress_add(1);
return 0;
}
diff --git a/scrub/vfs.c b/scrub/vfs.c
index 9e459d624..bcfd4f42c 100644
--- a/scrub/vfs.c
+++ b/scrub/vfs.c
@@ -296,15 +296,17 @@ struct fstrim_range {
#endif
/* Call FITRIM to trim all the unused space in a filesystem. */
-void
+int
fstrim(
struct scrub_ctx *ctx)
{
struct fstrim_range range = {0};
- int error;
range.len = ULLONG_MAX;
- error = ioctl(ctx->mnt.fd, FITRIM, &range);
- if (error && errno != EOPNOTSUPP && errno != ENOTTY)
- perror(_("fstrim"));
+ if (ioctl(ctx->mnt.fd, FITRIM, &range) == 0)
+ return 0;
+ if (errno == EOPNOTSUPP || errno == ENOTTY)
+ return EOPNOTSUPP;
+
+ return errno;
}
diff --git a/scrub/vfs.h b/scrub/vfs.h
index 1ac41e5aa..a8a4d72e2 100644
--- a/scrub/vfs.h
+++ b/scrub/vfs.h
@@ -24,6 +24,6 @@ typedef int (*scan_fs_tree_dirent_fn)(struct scrub_ctx *, const char *,
int scan_fs_tree(struct scrub_ctx *ctx, scan_fs_tree_dir_fn dir_fn,
scan_fs_tree_dirent_fn dirent_fn, void *arg);
-void fstrim(struct scrub_ctx *ctx);
+int fstrim(struct scrub_ctx *ctx);
#endif /* XFS_SCRUB_VFS_H_ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 6/7] xfs_scrub: don't call FITRIM after runtime errors
2024-07-30 0:19 ` [PATCHSET v30.9 12/23] xfs_scrub: move fstrim to a separate phase Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 1:10 ` [PATCH 5/7] xfs_scrub: report FITRIM errors properly Darrick J. Wong
@ 2024-07-30 1:10 ` Darrick J. Wong
2024-07-30 1:11 ` [PATCH 7/7] xfs_scrub: improve responsiveness while trimming the filesystem Darrick J. Wong
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:10 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Don't call FITRIM if there have been runtime errors -- we don't want to
touch anything after any kind of unfixable problem.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase8.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/scrub/phase8.c b/scrub/phase8.c
index 288800a76..75400c968 100644
--- a/scrub/phase8.c
+++ b/scrub/phase8.c
@@ -39,6 +39,9 @@ fstrim_ok(
if (ctx->unfixable_errors != 0)
return false;
+ if (ctx->runtime_errors != 0)
+ return false;
+
return true;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 7/7] xfs_scrub: improve responsiveness while trimming the filesystem
2024-07-30 0:19 ` [PATCHSET v30.9 12/23] xfs_scrub: move fstrim to a separate phase Darrick J. Wong
` (5 preceding siblings ...)
2024-07-30 1:10 ` [PATCH 6/7] xfs_scrub: don't call FITRIM after runtime errors Darrick J. Wong
@ 2024-07-30 1:11 ` Darrick J. Wong
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:11 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
On a 10TB filesystem where the free space in each AG is heavily
fragmented, I noticed some very high runtimes on a FITRIM call for the
entire filesystem. xfs_scrub likes to report progress information on
each phase of the scrub, which means that a strace for the entire
filesystem:
ioctl(3, FITRIM, {start=0x0, len=10995116277760, minlen=0}) = 0 <686.209839>
shows that scrub is uncommunicative for the entire duration. We can't
report any progress for the duration of the call, and the program is not
responsive to signals. Reducing the size of the FITRIM requests to a
single AG at a time produces lower times for each individual call, but
even this isn't quite acceptable, because the time between progress
reports are still very high:
Strace for the first 4x 1TB AGs looks like (2):
ioctl(3, FITRIM, {start=0x0, len=1099511627776, minlen=0}) = 0 <68.352033>
ioctl(3, FITRIM, {start=0x10000000000, len=1099511627776, minlen=0}) = 0 <68.760323>
ioctl(3, FITRIM, {start=0x20000000000, len=1099511627776, minlen=0}) = 0 <67.235226>
ioctl(3, FITRIM, {start=0x30000000000, len=1099511627776, minlen=0}) = 0 <69.465744>
I then had the idea to limit the length parameter of each call to a
smallish amount (~11GB) so that we could report progress relatively
quickly, but much to my surprise, each FITRIM call still took ~68
seconds!
Unfortunately, the by-length fstrim implementation handles this poorly
because it walks the entire free space by length index (cntbt), which is
a very inefficient way to walk a subset of an AG when the free space is
fragmented.
To fix that, I created a second implementation in the kernel that will
walk the bnobt and perform the trims in block number order. This
algorithm constrains the amount of btree scanning to something
resembling the range passed in, which reduces the amount of time it
takes to respond to a signal.
Therefore, break up the FITRIM calls so they don't scan more than 11GB
of space at a time. Break the calls up by AG so that each call only has
to take one AGF per call, because each AG that we traverse causes a log
force.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase8.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++----------
scrub/vfs.c | 10 ++++-
scrub/vfs.h | 2 +
3 files changed, 91 insertions(+), 23 deletions(-)
diff --git a/scrub/phase8.c b/scrub/phase8.c
index 75400c968..e35bf11bf 100644
--- a/scrub/phase8.c
+++ b/scrub/phase8.c
@@ -45,27 +45,90 @@ fstrim_ok(
return true;
}
+/*
+ * Limit the amount of fstrim scanning that we let the kernel do in a single
+ * call so that we can implement decent progress reporting and CPU resource
+ * control. Pick a prime number of gigabytes for interest.
+ */
+#define FSTRIM_MAX_BYTES (11ULL << 30)
+
+/* Trim a certain range of the filesystem. */
+static int
+fstrim_fsblocks(
+ struct scrub_ctx *ctx,
+ uint64_t start_fsb,
+ uint64_t fsbcount)
+{
+ uint64_t start = cvt_off_fsb_to_b(&ctx->mnt, start_fsb);
+ uint64_t len = cvt_off_fsb_to_b(&ctx->mnt, fsbcount);
+ int error;
+
+ while (len > 0) {
+ uint64_t run;
+
+ run = min(len, FSTRIM_MAX_BYTES);
+
+ error = fstrim(ctx, start, run);
+ if (error == EOPNOTSUPP) {
+ /* Pretend we finished all the work. */
+ progress_add(len);
+ return 0;
+ }
+ if (error) {
+ char descr[DESCR_BUFSZ];
+
+ snprintf(descr, sizeof(descr) - 1,
+ _("fstrim start 0x%llx run 0x%llx"),
+ (unsigned long long)start,
+ (unsigned long long)run);
+ str_liberror(ctx, error, descr);
+ return error;
+ }
+
+ progress_add(run);
+ len -= run;
+ start += run;
+ }
+
+ return 0;
+}
+
+/* Trim each AG on the data device. */
+static int
+fstrim_datadev(
+ struct scrub_ctx *ctx)
+{
+ struct xfs_fsop_geom *geo = &ctx->mnt.fsgeom;
+ uint64_t fsbno;
+ int error;
+
+ for (fsbno = 0; fsbno < geo->datablocks; fsbno += geo->agblocks) {
+ uint64_t fsbcount;
+
+ /*
+ * Make sure that trim calls do not cross AG boundaries so that
+ * the kernel only performs one log force (and takes one AGF
+ * lock) per call.
+ */
+ progress_add(geo->blocksize);
+ fsbcount = min(geo->datablocks - fsbno, geo->agblocks);
+ error = fstrim_fsblocks(ctx, fsbno, fsbcount);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
/* Trim the filesystem, if desired. */
int
phase8_func(
struct scrub_ctx *ctx)
{
- int error;
-
if (!fstrim_ok(ctx))
return 0;
- error = fstrim(ctx);
- if (error == EOPNOTSUPP)
- return 0;
-
- if (error) {
- str_liberror(ctx, error, _("fstrim"));
- return error;
- }
-
- progress_add(1);
- return 0;
+ return fstrim_datadev(ctx);
}
/* Estimate how much work we're going to do. */
@@ -76,12 +139,13 @@ phase8_estimate(
unsigned int *nr_threads,
int *rshift)
{
- *items = 0;
-
- if (fstrim_ok(ctx))
- *items = 1;
-
+ if (fstrim_ok(ctx)) {
+ *items = cvt_off_fsb_to_b(&ctx->mnt,
+ ctx->mnt.fsgeom.datablocks);
+ } else {
+ *items = 0;
+ }
*nr_threads = 1;
- *rshift = 0;
+ *rshift = 30; /* GiB */
return 0;
}
diff --git a/scrub/vfs.c b/scrub/vfs.c
index bcfd4f42c..cc958ba94 100644
--- a/scrub/vfs.c
+++ b/scrub/vfs.c
@@ -298,11 +298,15 @@ struct fstrim_range {
/* Call FITRIM to trim all the unused space in a filesystem. */
int
fstrim(
- struct scrub_ctx *ctx)
+ struct scrub_ctx *ctx,
+ uint64_t start,
+ uint64_t len)
{
- struct fstrim_range range = {0};
+ struct fstrim_range range = {
+ .start = start,
+ .len = len,
+ };
- range.len = ULLONG_MAX;
if (ioctl(ctx->mnt.fd, FITRIM, &range) == 0)
return 0;
if (errno == EOPNOTSUPP || errno == ENOTTY)
diff --git a/scrub/vfs.h b/scrub/vfs.h
index a8a4d72e2..1af8d80d1 100644
--- a/scrub/vfs.h
+++ b/scrub/vfs.h
@@ -24,6 +24,6 @@ typedef int (*scan_fs_tree_dirent_fn)(struct scrub_ctx *, const char *,
int scan_fs_tree(struct scrub_ctx *ctx, scan_fs_tree_dir_fn dir_fn,
scan_fs_tree_dirent_fn dirent_fn, void *arg);
-int fstrim(struct scrub_ctx *ctx);
+int fstrim(struct scrub_ctx *ctx, uint64_t start, uint64_t len);
#endif /* XFS_SCRUB_VFS_H_ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/7] libfrog: hoist free space histogram code
2024-07-30 0:19 ` [PATCHSET v30.9 13/23] xfs_scrub: use free space histograms to reduce fstrim runtime Darrick J. Wong
@ 2024-07-30 1:11 ` Darrick J. Wong
2024-07-30 1:11 ` [PATCH 2/7] libfrog: print wider columns for free space histogram Darrick J. Wong
` (5 subsequent siblings)
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:11 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Combine the two free space histograms in xfs_db and xfs_spaceman into a
single implementation.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/freesp.c | 89 ++++++++------------------------
libfrog/Makefile | 2 +
libfrog/histogram.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++
libfrog/histogram.h | 63 ++++++++++++++++++++++
spaceman/freesp.c | 99 ++++++++++++-----------------------
5 files changed, 264 insertions(+), 132 deletions(-)
create mode 100644 libfrog/histogram.c
create mode 100644 libfrog/histogram.h
diff --git a/db/freesp.c b/db/freesp.c
index 883741e66..43520481d 100644
--- a/db/freesp.c
+++ b/db/freesp.c
@@ -12,14 +12,7 @@
#include "output.h"
#include "init.h"
#include "malloc.h"
-
-typedef struct histent
-{
- int low;
- int high;
- long long count;
- long long blocks;
-} histent_t;
+#include "libfrog/histogram.h"
static void addhistent(int h);
static void addtohist(xfs_agnumber_t agno, xfs_agblock_t agbno,
@@ -46,13 +39,10 @@ static int alignment;
static int countflag;
static int dumpflag;
static int equalsize;
-static histent_t *hist;
-static int histcount;
+static struct histogram freesp_hist;
static int multsize;
static int seen1;
static int summaryflag;
-static long long totblocks;
-static long long totexts;
static const cmdinfo_t freesp_cmd =
{ "freesp", NULL, freesp_f, 0, -1, 0,
@@ -93,18 +83,20 @@ freesp_f(
if (inaglist(agno))
scan_ag(agno);
}
- if (histcount)
+ if (hist_buckets(&freesp_hist))
printhist();
if (summaryflag) {
- dbprintf(_("total free extents %lld\n"), totexts);
- dbprintf(_("total free blocks %lld\n"), totblocks);
- dbprintf(_("average free extent size %g\n"),
- (double)totblocks / (double)totexts);
+ struct histogram_strings hstr = {
+ .sum = _("total free blocks"),
+ .observations = _("total free extents"),
+ .averages = _("average free extent size"),
+ };
+
+ hist_summarize(&freesp_hist, &hstr);
}
if (aglist)
xfree(aglist);
- if (hist)
- xfree(hist);
+ hist_free(&freesp_hist);
return 0;
}
@@ -132,10 +124,9 @@ init(
int speced = 0;
agcount = countflag = dumpflag = equalsize = multsize = optind = 0;
- histcount = seen1 = summaryflag = 0;
- totblocks = totexts = 0;
+ seen1 = summaryflag = 0;
aglist = NULL;
- hist = NULL;
+
while ((c = getopt(argc, argv, "A:a:bcde:h:m:s")) != EOF) {
switch (c) {
case 'A':
@@ -163,7 +154,7 @@ init(
speced = 1;
break;
case 'h':
- if (speced && !histcount)
+ if (speced && hist_buckets(&freesp_hist) == 0)
return usage();
addhistent(atoi(optarg));
speced = 1;
@@ -339,14 +330,7 @@ static void
addhistent(
int h)
{
- hist = xrealloc(hist, (histcount + 1) * sizeof(*hist));
- if (h == 0)
- h = 1;
- hist[histcount].low = h;
- hist[histcount].count = hist[histcount].blocks = 0;
- histcount++;
- if (h == 1)
- seen1 = 1;
+ hist_add_bucket(&freesp_hist, h);
}
static void
@@ -355,30 +339,12 @@ addtohist(
xfs_agblock_t agbno,
xfs_extlen_t len)
{
- int i;
-
if (alignment && (XFS_AGB_TO_FSB(mp,agno,agbno) % alignment))
return;
if (dumpflag)
dbprintf("%8d %8d %8d\n", agno, agbno, len);
- totexts++;
- totblocks += len;
- for (i = 0; i < histcount; i++) {
- if (hist[i].high >= len) {
- hist[i].count++;
- hist[i].blocks += len;
- break;
- }
- }
-}
-
-static int
-hcmp(
- const void *a,
- const void *b)
-{
- return ((histent_t *)a)->low - ((histent_t *)b)->low;
+ hist_add(&freesp_hist, len);
}
static void
@@ -387,6 +353,7 @@ histinit(
{
int i;
+ hist_init(&freesp_hist);
if (equalsize) {
for (i = 1; i < maxlen; i += equalsize)
addhistent(i);
@@ -396,27 +363,17 @@ histinit(
} else {
if (!seen1)
addhistent(1);
- qsort(hist, histcount, sizeof(*hist), hcmp);
- }
- for (i = 0; i < histcount; i++) {
- if (i < histcount - 1)
- hist[i].high = hist[i + 1].low - 1;
- else
- hist[i].high = maxlen;
}
+ hist_prepare(&freesp_hist, maxlen);
}
static void
printhist(void)
{
- int i;
+ struct histogram_strings hstr = {
+ .sum = _("blocks"),
+ .observations = _("extents"),
+ };
- dbprintf("%7s %7s %7s %7s %6s\n",
- _("from"), _("to"), _("extents"), _("blocks"), _("pct"));
- for (i = 0; i < histcount; i++) {
- if (hist[i].count)
- dbprintf("%7d %7d %7lld %7lld %6.2f\n", hist[i].low,
- hist[i].high, hist[i].count, hist[i].blocks,
- hist[i].blocks * 100.0 / totblocks);
- }
+ hist_print(&freesp_hist, &hstr);
}
diff --git a/libfrog/Makefile b/libfrog/Makefile
index 53e3c3492..acfa228bc 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -20,6 +20,7 @@ convert.c \
crc32.c \
file_exchange.c \
fsgeom.c \
+histogram.c \
list_sort.c \
linux.c \
logging.c \
@@ -45,6 +46,7 @@ dahashselftest.h \
div64.h \
file_exchange.h \
fsgeom.h \
+histogram.h \
logging.h \
paths.h \
projects.h \
diff --git a/libfrog/histogram.c b/libfrog/histogram.c
new file mode 100644
index 000000000..c2f344a88
--- /dev/null
+++ b/libfrog/histogram.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2012 Red Hat, Inc.
+ * Copyright (c) 2017-2024 Oracle.
+ * All Rights Reserved.
+ */
+#include "xfs.h"
+#include <stdlib.h>
+#include <string.h>
+#include "platform_defs.h"
+#include "libfrog/histogram.h"
+
+/* Create a new bucket with the given low value. */
+int
+hist_add_bucket(
+ struct histogram *hs,
+ long long bucket_low)
+{
+ struct histbucket *buckets;
+
+ if (hs->nr_buckets == INT_MAX)
+ return EFBIG;
+
+ buckets = realloc(hs->buckets,
+ (hs->nr_buckets + 1) * sizeof(struct histbucket));
+ if (!buckets)
+ return errno;
+
+ hs->buckets = buckets;
+ hs->buckets[hs->nr_buckets].low = bucket_low;
+ hs->buckets[hs->nr_buckets].nr_obs = 0;
+ hs->buckets[hs->nr_buckets].sum = 0;
+ hs->nr_buckets++;
+ return 0;
+}
+
+/* Add an observation to the histogram. */
+void
+hist_add(
+ struct histogram *hs,
+ long long len)
+{
+ unsigned int i;
+
+ hs->tot_obs++;
+ hs->tot_sum += len;
+ for (i = 0; i < hs->nr_buckets; i++) {
+ if (hs->buckets[i].high >= len) {
+ hs->buckets[i].nr_obs++;
+ hs->buckets[i].sum += len;
+ break;
+ }
+ }
+}
+
+static int
+histbucket_cmp(
+ const void *a,
+ const void *b)
+{
+ const struct histbucket *ha = a;
+ const struct histbucket *hb = b;
+
+ if (ha->low < hb->low)
+ return -1;
+ if (ha->low > hb->low)
+ return 1;
+ return 0;
+}
+
+/* Prepare a histogram for bucket configuration. */
+void
+hist_init(
+ struct histogram *hs)
+{
+ memset(hs, 0, sizeof(struct histogram));
+}
+
+/* Prepare a histogram to receive data observations. */
+void
+hist_prepare(
+ struct histogram *hs,
+ long long maxlen)
+{
+ unsigned int i;
+
+ qsort(hs->buckets, hs->nr_buckets, sizeof(struct histbucket),
+ histbucket_cmp);
+
+ for (i = 0; i < hs->nr_buckets - 1; i++)
+ hs->buckets[i].high = hs->buckets[i + 1].low - 1;
+ hs->buckets[hs->nr_buckets - 1].high = maxlen;
+}
+
+/* Free all data associated with a histogram. */
+void
+hist_free(
+ struct histogram *hs)
+{
+ free(hs->buckets);
+ memset(hs, 0, sizeof(struct histogram));
+}
+
+/* Dump a histogram to stdout. */
+void
+hist_print(
+ const struct histogram *hs,
+ const struct histogram_strings *hstr)
+{
+ unsigned int obs_w = strlen(hstr->observations);
+ unsigned int sum_w = strlen(hstr->sum);
+ unsigned int i;
+
+ printf("%7s %7s %*s %*s %6s\n",
+ _("from"), _("to"),
+ obs_w, hstr->observations,
+ sum_w, hstr->sum,
+ _("pct"));
+
+ for (i = 0; i < hs->nr_buckets; i++) {
+ if (hs->buckets[i].nr_obs == 0)
+ continue;
+
+ printf("%7lld %7lld %*lld %*lld %6.2f\n",
+ hs->buckets[i].low, hs->buckets[i].high,
+ obs_w, hs->buckets[i].nr_obs,
+ sum_w, hs->buckets[i].sum,
+ hs->buckets[i].sum * 100.0 / hs->tot_sum);
+ }
+}
+
+/* Summarize the contents of the histogram. */
+void
+hist_summarize(
+ const struct histogram *hs,
+ const struct histogram_strings *hstr)
+{
+ printf("%s %lld\n", hstr->observations, hs->tot_obs);
+ printf("%s %lld\n", hstr->sum, hs->tot_sum);
+ printf("%s %g\n", hstr->averages,
+ (double)hs->tot_sum / (double)hs->tot_obs);
+}
diff --git a/libfrog/histogram.h b/libfrog/histogram.h
new file mode 100644
index 000000000..68afdeb29
--- /dev/null
+++ b/libfrog/histogram.h
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2012 Red Hat, Inc.
+ * Copyright (c) 2017-2024 Oracle.
+ * All Rights Reserved.
+ */
+#ifndef __LIBFROG_HISTOGRAM_H__
+#define __LIBFROG_HISTOGRAM_H__
+
+struct histbucket {
+ /* Low and high size of this bucket */
+ long long low;
+ long long high;
+
+ /* Count of observations recorded */
+ long long nr_obs;
+
+ /* Sum of values recorded */
+ long long sum;
+};
+
+struct histogram {
+ /* Sum of all values recorded */
+ long long tot_sum;
+
+ /* Count of all observations recorded */
+ long long tot_obs;
+
+ struct histbucket *buckets;
+
+ /* Number of buckets */
+ unsigned int nr_buckets;
+};
+
+int hist_add_bucket(struct histogram *hs, long long bucket_low);
+void hist_add(struct histogram *hs, long long value);
+void hist_init(struct histogram *hs);
+void hist_prepare(struct histogram *hs, long long maxvalue);
+void hist_free(struct histogram *hs);
+
+struct histogram_strings {
+ /* What does each sum represent? ("free blocks") */
+ const char *sum;
+
+ /* What does each observation represent? ("free extents") */
+ const char *observations;
+
+ /* What does sum / observation represent? ("average extent length") */
+ const char *averages;
+};
+
+void hist_print(const struct histogram *hs,
+ const struct histogram_strings *hstr);
+void hist_summarize(const struct histogram *hs,
+ const struct histogram_strings *hstr);
+
+static inline unsigned int hist_buckets(const struct histogram *hs)
+{
+ return hs->nr_buckets;
+}
+
+#endif /* __LIBFROG_HISTOGRAM_H__ */
diff --git a/spaceman/freesp.c b/spaceman/freesp.c
index f5177cb4e..dfbec52a7 100644
--- a/spaceman/freesp.c
+++ b/spaceman/freesp.c
@@ -15,76 +15,52 @@
#include "libfrog/paths.h"
#include "space.h"
#include "input.h"
-
-struct histent
-{
- long long low;
- long long high;
- long long count;
- long long blocks;
-};
+#include "libfrog/histogram.h"
static int agcount;
static xfs_agnumber_t *aglist;
-static struct histent *hist;
+static struct histogram freesp_hist;
static int dumpflag;
static long long equalsize;
static long long multsize;
-static int histcount;
static int seen1;
static int summaryflag;
static int gflag;
static bool rtflag;
-static long long totblocks;
-static long long totexts;
static cmdinfo_t freesp_cmd;
-static void
+static inline void
addhistent(
long long h)
{
- if (histcount == INT_MAX) {
+ int error;
+
+ error = hist_add_bucket(&freesp_hist, h);
+ if (error == EFBIG) {
printf(_("Too many histogram buckets.\n"));
return;
}
- hist = realloc(hist, (histcount + 1) * sizeof(*hist));
+ if (error) {
+ printf("%s\n", strerror(error));
+ return;
+ }
+
if (h == 0)
h = 1;
- hist[histcount].low = h;
- hist[histcount].count = hist[histcount].blocks = 0;
- histcount++;
if (h == 1)
seen1 = 1;
}
-static void
+static inline void
addtohist(
xfs_agnumber_t agno,
xfs_agblock_t agbno,
off_t len)
{
- long i;
-
if (dumpflag)
printf("%8d %8d %8"PRId64"\n", agno, agbno, len);
- totexts++;
- totblocks += len;
- for (i = 0; i < histcount; i++) {
- if (hist[i].high >= len) {
- hist[i].count++;
- hist[i].blocks += len;
- break;
- }
- }
-}
-
-static int
-hcmp(
- const void *a,
- const void *b)
-{
- return ((struct histent *)a)->low - ((struct histent *)b)->low;
+ hist_add(&freesp_hist, len);
}
static void
@@ -93,6 +69,7 @@ histinit(
{
long long i;
+ hist_init(&freesp_hist);
if (equalsize) {
for (i = 1; i < maxlen; i += equalsize)
addhistent(i);
@@ -102,29 +79,19 @@ histinit(
} else {
if (!seen1)
addhistent(1);
- qsort(hist, histcount, sizeof(*hist), hcmp);
- }
- for (i = 0; i < histcount; i++) {
- if (i < histcount - 1)
- hist[i].high = hist[i + 1].low - 1;
- else
- hist[i].high = maxlen;
}
+ hist_prepare(&freesp_hist, maxlen);
}
-static void
+static inline void
printhist(void)
{
- int i;
+ struct histogram_strings hstr = {
+ .sum = _("blocks"),
+ .observations = _("extents"),
+ };
- printf("%7s %7s %7s %7s %6s\n",
- _("from"), _("to"), _("extents"), _("blocks"), _("pct"));
- for (i = 0; i < histcount; i++) {
- if (hist[i].count)
- printf("%7lld %7lld %7lld %7lld %6.2f\n", hist[i].low,
- hist[i].high, hist[i].count, hist[i].blocks,
- hist[i].blocks * 100.0 / totblocks);
- }
+ hist_print(&freesp_hist, &hstr);
}
static int
@@ -255,10 +222,8 @@ init(
int speced = 0; /* only one of -b -e -h or -m */
agcount = dumpflag = equalsize = multsize = optind = gflag = 0;
- histcount = seen1 = summaryflag = 0;
- totblocks = totexts = 0;
+ seen1 = summaryflag = 0;
aglist = NULL;
- hist = NULL;
rtflag = false;
while ((c = getopt(argc, argv, "a:bde:gh:m:rs")) != EOF) {
@@ -287,7 +252,7 @@ init(
gflag++;
break;
case 'h':
- if (speced && !histcount)
+ if (speced && hist_buckets(&freesp_hist) == 0)
goto many_spec;
/* addhistent increments histcount */
x = cvt_s64(optarg, 0);
@@ -345,18 +310,20 @@ freesp_f(
if (inaglist(agno))
scan_ag(agno);
}
- if (histcount && !gflag)
+ if (hist_buckets(&freesp_hist) > 0 && !gflag)
printhist();
if (summaryflag) {
- printf(_("total free extents %lld\n"), totexts);
- printf(_("total free blocks %lld\n"), totblocks);
- printf(_("average free extent size %g\n"),
- (double)totblocks / (double)totexts);
+ struct histogram_strings hstr = {
+ .sum = _("total free blocks"),
+ .observations = _("total free extents"),
+ .averages = _("average free extent size"),
+ };
+
+ hist_summarize(&freesp_hist, &hstr);
}
if (aglist)
free(aglist);
- if (hist)
- free(hist);
+ hist_free(&freesp_hist);
return 0;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/7] libfrog: print wider columns for free space histogram
2024-07-30 0:19 ` [PATCHSET v30.9 13/23] xfs_scrub: use free space histograms to reduce fstrim runtime Darrick J. Wong
2024-07-30 1:11 ` [PATCH 1/7] libfrog: hoist free space histogram code Darrick J. Wong
@ 2024-07-30 1:11 ` Darrick J. Wong
2024-07-30 1:11 ` [PATCH 3/7] libfrog: print cdf of free space buckets Darrick J. Wong
` (4 subsequent siblings)
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:11 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
The values reported here can reach very large values, so compute the
column width dynamically.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libfrog/histogram.c | 29 +++++++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/libfrog/histogram.c b/libfrog/histogram.c
index c2f344a88..7cee6b350 100644
--- a/libfrog/histogram.c
+++ b/libfrog/histogram.c
@@ -110,10 +110,30 @@ hist_print(
{
unsigned int obs_w = strlen(hstr->observations);
unsigned int sum_w = strlen(hstr->sum);
+ unsigned int from_w = 7, to_w = 7;
unsigned int i;
- printf("%7s %7s %*s %*s %6s\n",
- _("from"), _("to"),
+ for (i = 0; i < hs->nr_buckets; i++) {
+ char buf[256];
+
+ if (hs->buckets[i].nr_obs == 0)
+ continue;
+
+ snprintf(buf, sizeof(buf) - 1, "%lld", hs->buckets[i].low);
+ from_w = max(from_w, strlen(buf));
+
+ snprintf(buf, sizeof(buf) - 1, "%lld", hs->buckets[i].high);
+ to_w = max(to_w, strlen(buf));
+
+ snprintf(buf, sizeof(buf) - 1, "%lld", hs->buckets[i].nr_obs);
+ obs_w = max(obs_w, strlen(buf));
+
+ snprintf(buf, sizeof(buf) - 1, "%lld", hs->buckets[i].sum);
+ sum_w = max(sum_w, strlen(buf));
+ }
+
+ printf("%*s %*s %*s %*s %6s\n",
+ from_w, _("from"), to_w, _("to"),
obs_w, hstr->observations,
sum_w, hstr->sum,
_("pct"));
@@ -122,8 +142,9 @@ hist_print(
if (hs->buckets[i].nr_obs == 0)
continue;
- printf("%7lld %7lld %*lld %*lld %6.2f\n",
- hs->buckets[i].low, hs->buckets[i].high,
+ printf("%*lld %*lld %*lld %*lld %6.2f\n",
+ from_w, hs->buckets[i].low,
+ to_w, hs->buckets[i].high,
obs_w, hs->buckets[i].nr_obs,
sum_w, hs->buckets[i].sum,
hs->buckets[i].sum * 100.0 / hs->tot_sum);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/7] libfrog: print cdf of free space buckets
2024-07-30 0:19 ` [PATCHSET v30.9 13/23] xfs_scrub: use free space histograms to reduce fstrim runtime Darrick J. Wong
2024-07-30 1:11 ` [PATCH 1/7] libfrog: hoist free space histogram code Darrick J. Wong
2024-07-30 1:11 ` [PATCH 2/7] libfrog: print wider columns for free space histogram Darrick J. Wong
@ 2024-07-30 1:11 ` Darrick J. Wong
2024-07-30 1:12 ` [PATCH 4/7] xfs_scrub: don't close stdout when closing the progress bar Darrick J. Wong
` (3 subsequent siblings)
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:11 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Print the cumulative distribution function of the free space buckets in
reverse order.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libfrog/histogram.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++---
libfrog/histogram.h | 8 +++++
2 files changed, 80 insertions(+), 4 deletions(-)
diff --git a/libfrog/histogram.c b/libfrog/histogram.c
index 7cee6b350..59d46363c 100644
--- a/libfrog/histogram.c
+++ b/libfrog/histogram.c
@@ -102,17 +102,81 @@ hist_free(
memset(hs, 0, sizeof(struct histogram));
}
+/*
+ * Compute the CDF of the histogram in decreasing order of value.
+ *
+ * For a free space histogram, callers can determine how much free space is not
+ * in the long tail of small extents, e.g. 98% of the free space extents are
+ * larger than 31 blocks.
+ */
+static struct histogram_cdf *
+hist_cdf(
+ const struct histogram *hs)
+{
+ struct histogram_cdf *cdf;
+ int i = hs->nr_buckets - 1;
+
+ ASSERT(hs->nr_buckets < INT_MAX);
+
+ cdf = malloc(sizeof(struct histogram_cdf));
+ if (!cdf)
+ return NULL;
+ cdf->histogram = hs;
+
+ if (hs->nr_buckets == 0) {
+ cdf->buckets = NULL;
+ return cdf;
+ }
+
+ cdf->buckets = calloc(hs->nr_buckets, sizeof(struct histbucket));
+ if (!cdf->buckets) {
+ free(cdf);
+ return NULL;
+ }
+
+ cdf->buckets[i].nr_obs = hs->buckets[i].nr_obs;
+ cdf->buckets[i].sum = hs->buckets[i].sum;
+ i--;
+
+ while (i >= 0) {
+ cdf->buckets[i].nr_obs = hs->buckets[i].nr_obs +
+ cdf->buckets[i + 1].nr_obs;
+
+ cdf->buckets[i].sum = hs->buckets[i].sum +
+ cdf->buckets[i + 1].sum;
+ i--;
+ }
+
+ return cdf;
+}
+
+/* Free all data associated with a histogram cdf. */
+static void
+histcdf_free(
+ struct histogram_cdf *cdf)
+{
+ free(cdf->buckets);
+ free(cdf);
+}
+
/* Dump a histogram to stdout. */
void
hist_print(
const struct histogram *hs,
const struct histogram_strings *hstr)
{
+ struct histogram_cdf *cdf;
unsigned int obs_w = strlen(hstr->observations);
unsigned int sum_w = strlen(hstr->sum);
unsigned int from_w = 7, to_w = 7;
unsigned int i;
+ cdf = hist_cdf(hs);
+ if (!cdf) {
+ perror(_("histogram cdf"));
+ return;
+ }
+
for (i = 0; i < hs->nr_buckets; i++) {
char buf[256];
@@ -132,23 +196,27 @@ hist_print(
sum_w = max(sum_w, strlen(buf));
}
- printf("%*s %*s %*s %*s %6s\n",
+ printf("%*s %*s %*s %*s %6s %6s %6s\n",
from_w, _("from"), to_w, _("to"),
obs_w, hstr->observations,
sum_w, hstr->sum,
- _("pct"));
+ _("pct"), _("blkcdf"), _("extcdf"));
for (i = 0; i < hs->nr_buckets; i++) {
if (hs->buckets[i].nr_obs == 0)
continue;
- printf("%*lld %*lld %*lld %*lld %6.2f\n",
+ printf("%*lld %*lld %*lld %*lld %6.2f %6.2f %6.2f\n",
from_w, hs->buckets[i].low,
to_w, hs->buckets[i].high,
obs_w, hs->buckets[i].nr_obs,
sum_w, hs->buckets[i].sum,
- hs->buckets[i].sum * 100.0 / hs->tot_sum);
+ hs->buckets[i].sum * 100.0 / hs->tot_sum,
+ cdf->buckets[i].sum * 100.0 / hs->tot_sum,
+ cdf->buckets[i].nr_obs * 100.0 / hs->tot_obs);
}
+
+ histcdf_free(cdf);
}
/* Summarize the contents of the histogram. */
diff --git a/libfrog/histogram.h b/libfrog/histogram.h
index 68afdeb29..0c534f65d 100644
--- a/libfrog/histogram.h
+++ b/libfrog/histogram.h
@@ -33,6 +33,14 @@ struct histogram {
unsigned int nr_buckets;
};
+struct histogram_cdf {
+ /* histogram from which this cdf was computed */
+ const struct histogram *histogram;
+
+ /* distribution information */
+ struct histbucket *buckets;
+};
+
int hist_add_bucket(struct histogram *hs, long long bucket_low);
void hist_add(struct histogram *hs, long long value);
void hist_init(struct histogram *hs);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/7] xfs_scrub: don't close stdout when closing the progress bar
2024-07-30 0:19 ` [PATCHSET v30.9 13/23] xfs_scrub: use free space histograms to reduce fstrim runtime Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:11 ` [PATCH 3/7] libfrog: print cdf of free space buckets Darrick J. Wong
@ 2024-07-30 1:12 ` Darrick J. Wong
2024-07-30 1:12 ` [PATCH 5/7] xfs_scrub: remove pointless spacemap.c arguments Darrick J. Wong
` (2 subsequent siblings)
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:12 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
When we're tearing down the progress bar file stream, check that it's
not an alias of stdout before closing it.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/xfs_scrub.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scrub/xfs_scrub.c b/scrub/xfs_scrub.c
index edf58d07b..adf9d13e5 100644
--- a/scrub/xfs_scrub.c
+++ b/scrub/xfs_scrub.c
@@ -878,7 +878,7 @@ main(
if (ctx.runtime_errors)
ret |= SCRUB_RET_OPERROR;
phase_end(&all_pi, 0);
- if (progress_fp)
+ if (progress_fp && fileno(progress_fp) != 1)
fclose(progress_fp);
out_unicrash:
unicrash_unload();
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/7] xfs_scrub: remove pointless spacemap.c arguments
2024-07-30 0:19 ` [PATCHSET v30.9 13/23] xfs_scrub: use free space histograms to reduce fstrim runtime Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:12 ` [PATCH 4/7] xfs_scrub: don't close stdout when closing the progress bar Darrick J. Wong
@ 2024-07-30 1:12 ` Darrick J. Wong
2024-07-30 1:12 ` [PATCH 6/7] xfs_scrub: collect free space histograms during phase 7 Darrick J. Wong
2024-07-30 1:12 ` [PATCH 7/7] xfs_scrub: tune fstrim minlen parameter based on free space histograms Darrick J. Wong
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:12 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Remove unused parameters from the full-device spacemap scan functions.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/spacemap.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/scrub/spacemap.c b/scrub/spacemap.c
index 9cefe074c..e35756db2 100644
--- a/scrub/spacemap.c
+++ b/scrub/spacemap.c
@@ -132,7 +132,6 @@ scan_ag_rmaps(
static void
scan_dev_rmaps(
struct scrub_ctx *ctx,
- int idx,
dev_t dev,
struct scan_blocks *sbx)
{
@@ -170,7 +169,7 @@ scan_rt_rmaps(
{
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
- scan_dev_rmaps(ctx, agno, ctx->fsinfo.fs_rtdev, arg);
+ scan_dev_rmaps(ctx, ctx->fsinfo.fs_rtdev, arg);
}
/* Iterate all the reverse mappings of the log device. */
@@ -182,7 +181,7 @@ scan_log_rmaps(
{
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
- scan_dev_rmaps(ctx, agno, ctx->fsinfo.fs_logdev, arg);
+ scan_dev_rmaps(ctx, ctx->fsinfo.fs_logdev, arg);
}
/*
@@ -210,8 +209,7 @@ scrub_scan_all_spacemaps(
return ret;
}
if (ctx->fsinfo.fs_rt) {
- ret = -workqueue_add(&wq, scan_rt_rmaps,
- ctx->mnt.fsgeom.agcount + 1, &sbx);
+ ret = -workqueue_add(&wq, scan_rt_rmaps, 0, &sbx);
if (ret) {
sbx.aborted = true;
str_liberror(ctx, ret, _("queueing rtdev fsmap work"));
@@ -219,8 +217,7 @@ scrub_scan_all_spacemaps(
}
}
if (ctx->fsinfo.fs_log) {
- ret = -workqueue_add(&wq, scan_log_rmaps,
- ctx->mnt.fsgeom.agcount + 2, &sbx);
+ ret = -workqueue_add(&wq, scan_log_rmaps, 0, &sbx);
if (ret) {
sbx.aborted = true;
str_liberror(ctx, ret, _("queueing logdev fsmap work"));
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 6/7] xfs_scrub: collect free space histograms during phase 7
2024-07-30 0:19 ` [PATCHSET v30.9 13/23] xfs_scrub: use free space histograms to reduce fstrim runtime Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 1:12 ` [PATCH 5/7] xfs_scrub: remove pointless spacemap.c arguments Darrick J. Wong
@ 2024-07-30 1:12 ` Darrick J. Wong
2024-07-30 1:12 ` [PATCH 7/7] xfs_scrub: tune fstrim minlen parameter based on free space histograms Darrick J. Wong
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:12 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Collect a histogram of free space observed during phase 7. We'll put
this information to use in the next patch.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libfrog/histogram.c | 38 ++++++++++++++++++++++++++++++++++++++
libfrog/histogram.h | 3 +++
scrub/phase7.c | 47 +++++++++++++++++++++++++++++++++++++++++++++--
scrub/xfs_scrub.c | 5 +++++
scrub/xfs_scrub.h | 4 ++++
5 files changed, 95 insertions(+), 2 deletions(-)
diff --git a/libfrog/histogram.c b/libfrog/histogram.c
index 59d46363c..543c8a636 100644
--- a/libfrog/histogram.c
+++ b/libfrog/histogram.c
@@ -230,3 +230,41 @@ hist_summarize(
printf("%s %g\n", hstr->averages,
(double)hs->tot_sum / (double)hs->tot_obs);
}
+
+/* Copy the contents of src to dest. */
+void
+hist_import(
+ struct histogram *dest,
+ const struct histogram *src)
+{
+ unsigned int i;
+
+ ASSERT(dest->nr_buckets == src->nr_buckets);
+
+ dest->tot_sum += src->tot_sum;
+ dest->tot_obs += src->tot_obs;
+
+ for (i = 0; i < dest->nr_buckets; i++) {
+ ASSERT(dest->buckets[i].low == src->buckets[i].low);
+ ASSERT(dest->buckets[i].high == src->buckets[i].high);
+
+ dest->buckets[i].nr_obs += src->buckets[i].nr_obs;
+ dest->buckets[i].sum += src->buckets[i].sum;
+ }
+}
+
+/*
+ * Move the contents of src to dest and reinitialize src. dst must not
+ * contain any observations or buckets.
+ */
+void
+hist_move(
+ struct histogram *dest,
+ struct histogram *src)
+{
+ ASSERT(dest->nr_buckets == 0);
+ ASSERT(dest->tot_obs == 0);
+
+ memcpy(dest, src, sizeof(struct histogram));
+ hist_init(src);
+}
diff --git a/libfrog/histogram.h b/libfrog/histogram.h
index 0c534f65d..002ad78ca 100644
--- a/libfrog/histogram.h
+++ b/libfrog/histogram.h
@@ -68,4 +68,7 @@ static inline unsigned int hist_buckets(const struct histogram *hs)
return hs->nr_buckets;
}
+void hist_import(struct histogram *dest, const struct histogram *src);
+void hist_move(struct histogram *dest, struct histogram *src);
+
#endif /* __LIBFROG_HISTOGRAM_H__ */
diff --git a/scrub/phase7.c b/scrub/phase7.c
index cce5ede00..475d8f157 100644
--- a/scrub/phase7.c
+++ b/scrub/phase7.c
@@ -12,6 +12,7 @@
#include "libfrog/ptvar.h"
#include "libfrog/fsgeom.h"
#include "libfrog/scrub.h"
+#include "libfrog/histogram.h"
#include "list.h"
#include "xfs_scrub.h"
#include "common.h"
@@ -27,8 +28,36 @@ struct summary_counts {
unsigned long long rbytes; /* rt dev bytes */
unsigned long long next_phys; /* next phys bytes we see? */
unsigned long long agbytes; /* freespace bytes */
+
+ /* Free space histogram, in fsb */
+ struct histogram datadev_hist;
};
+/*
+ * Initialize a free space histogram. Unsharded realtime volumes can be up to
+ * 2^52 blocks long, so we allocate enough buckets to handle that.
+ */
+static inline void
+init_freesp_hist(
+ struct histogram *hs)
+{
+ unsigned int i;
+
+ hist_init(hs);
+ for (i = 0; i < 53; i++)
+ hist_add_bucket(hs, 1ULL << i);
+ hist_prepare(hs, 1ULL << 53);
+}
+
+static void
+summary_count_init(
+ void *data)
+{
+ struct summary_counts *counts = data;
+
+ init_freesp_hist(&counts->datadev_hist);
+}
+
/* Record block usage. */
static int
count_block_summary(
@@ -48,8 +77,14 @@ count_block_summary(
if (fsmap->fmr_device == ctx->fsinfo.fs_logdev)
return 0;
if ((fsmap->fmr_flags & FMR_OF_SPECIAL_OWNER) &&
- fsmap->fmr_owner == XFS_FMR_OWN_FREE)
+ fsmap->fmr_owner == XFS_FMR_OWN_FREE) {
+ uint64_t blocks;
+
+ blocks = cvt_b_to_off_fsbt(&ctx->mnt, fsmap->fmr_length);
+ if (fsmap->fmr_device == ctx->fsinfo.fs_datadev)
+ hist_add(&counts->datadev_hist, blocks);
return 0;
+ }
len = fsmap->fmr_length;
@@ -87,6 +122,9 @@ add_summaries(
total->dbytes += item->dbytes;
total->rbytes += item->rbytes;
total->agbytes += item->agbytes;
+
+ hist_import(&total->datadev_hist, &item->datadev_hist);
+ hist_free(&item->datadev_hist);
return 0;
}
@@ -118,6 +156,8 @@ phase7_func(
int ip;
int error;
+ summary_count_init(&totalcount);
+
/* Check and fix the summary metadata. */
scrub_item_init_fs(&sri);
scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_SUMMARY);
@@ -136,7 +176,7 @@ phase7_func(
}
error = -ptvar_alloc(scrub_nproc(ctx), sizeof(struct summary_counts),
- NULL, &ptvar);
+ summary_count_init, &ptvar);
if (error) {
str_liberror(ctx, error, _("setting up block counter"));
return error;
@@ -153,6 +193,9 @@ phase7_func(
}
ptvar_free(ptvar);
+ /* Preserve free space histograms for phase 8. */
+ hist_move(&ctx->datadev_hist, &totalcount.datadev_hist);
+
/* Scan the whole fs. */
error = scrub_count_all_inodes(ctx, &counted_inodes);
if (error) {
diff --git a/scrub/xfs_scrub.c b/scrub/xfs_scrub.c
index adf9d13e5..2894f6148 100644
--- a/scrub/xfs_scrub.c
+++ b/scrub/xfs_scrub.c
@@ -18,6 +18,7 @@
#include "descr.h"
#include "unicrash.h"
#include "progress.h"
+#include "libfrog/histogram.h"
/*
* XFS Online Metadata Scrub (and Repair)
@@ -669,6 +670,8 @@ main(
int ret = SCRUB_RET_SUCCESS;
int error;
+ hist_init(&ctx.datadev_hist);
+
fprintf(stdout, "EXPERIMENTAL xfs_scrub program in use! Use at your own risk!\n");
fflush(stdout);
@@ -883,6 +886,8 @@ main(
out_unicrash:
unicrash_unload();
+ hist_free(&ctx.datadev_hist);
+
/*
* If we're being run as a service, the return code must fit the LSB
* init script action error guidelines, which is to say that we
diff --git a/scrub/xfs_scrub.h b/scrub/xfs_scrub.h
index 6272a3687..1a28f0cc8 100644
--- a/scrub/xfs_scrub.h
+++ b/scrub/xfs_scrub.h
@@ -7,6 +7,7 @@
#define XFS_SCRUB_XFS_SCRUB_H_
#include "libfrog/fsgeom.h"
+#include "libfrog/histogram.h"
extern char *progname;
@@ -86,6 +87,9 @@ struct scrub_ctx {
unsigned long long preens;
bool scrub_setup_succeeded;
bool preen_triggers[XFS_SCRUB_TYPE_NR];
+
+ /* Free space histograms, in fsb */
+ struct histogram datadev_hist;
};
/* Phase helper functions */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 7/7] xfs_scrub: tune fstrim minlen parameter based on free space histograms
2024-07-30 0:19 ` [PATCHSET v30.9 13/23] xfs_scrub: use free space histograms to reduce fstrim runtime Darrick J. Wong
` (5 preceding siblings ...)
2024-07-30 1:12 ` [PATCH 6/7] xfs_scrub: collect free space histograms during phase 7 Darrick J. Wong
@ 2024-07-30 1:12 ` Darrick J. Wong
6 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:12 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Currently, phase 8 runs very slowly on filesystems with a lot of small
free space extents. To reduce the amount of time spent on fstrim
activities during phase 8, we want to balance estimated runtime against
completeness of the trim. In short, the goal is to reduce runtime by
avoiding small trim requests.
At the start of phase 8, a CDF is computed in decreasing order of extent
length from the histogram buckets created during the fsmap scan in phase
7. A point corresponding to the fstrim percentage target is chosen from
the CDF and mapped back to a histogram bucket, and free space extents
smaller than that amount are ommitted from fstrim.
On my aging /home filesystem, the free space histogram reported by
xfs_spaceman looks like this:
from to extents blocks pct blkcdf extcdf
1 1 121953 121953 0.04 100.00 100.00
2 3 124741 299694 0.09 99.96 81.16
4 7 113492 593763 0.18 99.87 61.89
8 15 109215 1179524 0.36 99.69 44.36
16 31 76972 1695455 0.52 99.33 27.48
32 63 48655 2219667 0.68 98.82 15.59
64 127 31398 2876898 0.88 98.14 8.08
128 255 8014 1447920 0.44 97.27 3.23
256 511 4142 1501758 0.46 96.82 1.99
512 1023 2433 1768732 0.54 96.37 1.35
1024 2047 1795 2648460 0.81 95.83 0.97
2048 4095 1429 4206103 1.28 95.02 0.69
4096 8191 1045 6162111 1.88 93.74 0.47
8192 16383 791 9242745 2.81 91.87 0.31
16384 32767 473 10883977 3.31 89.06 0.19
32768 65535 272 12385566 3.77 85.74 0.12
65536 131071 192 18098739 5.51 81.98 0.07
131072 262143 108 20675199 6.29 76.47 0.04
262144 524287 80 29061285 8.84 70.18 0.03
524288 1048575 39 29002829 8.83 61.33 0.02
1048576 2097151 25 36824985 11.21 52.51 0.01
2097152 4194303 32 101727192 30.95 41.30 0.01
4194304 8388607 7 34007410 10.35 10.35 0.00
From this table, we see that free space extents that are 16 blocks or
longer constitute 99.3% of the free space in the filesystem but only
27.5% of the extents. If we set the fstrim minlen parameter to 16
blocks, that means that we can trim over 99% of the space in one third
of the time it would take to trim everything.
Add a new -o fstrim_pct= option to xfs_scrub just in case there are
users out there who want a different percentage. For example, accepting
a 95% trim would net us a speed increase of nearly two orders of
magnitude, ignoring system call overhead. Setting it to 100% will trim
everything, just like fstrim(8).
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libfrog/histogram.c | 4 +-
libfrog/histogram.h | 3 ++
man/man8/xfs_scrub.8 | 16 +++++++++
scrub/phase8.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++---
scrub/vfs.c | 4 ++
scrub/vfs.h | 2 +
scrub/xfs_scrub.c | 38 ++++++++++++++++++++-
scrub/xfs_scrub.h | 12 +++++++
8 files changed, 160 insertions(+), 10 deletions(-)
diff --git a/libfrog/histogram.c b/libfrog/histogram.c
index 543c8a636..f6a749e3c 100644
--- a/libfrog/histogram.c
+++ b/libfrog/histogram.c
@@ -109,7 +109,7 @@ hist_free(
* in the long tail of small extents, e.g. 98% of the free space extents are
* larger than 31 blocks.
*/
-static struct histogram_cdf *
+struct histogram_cdf *
hist_cdf(
const struct histogram *hs)
{
@@ -151,7 +151,7 @@ hist_cdf(
}
/* Free all data associated with a histogram cdf. */
-static void
+void
histcdf_free(
struct histogram_cdf *cdf)
{
diff --git a/libfrog/histogram.h b/libfrog/histogram.h
index 002ad78ca..9a0fedeff 100644
--- a/libfrog/histogram.h
+++ b/libfrog/histogram.h
@@ -68,6 +68,9 @@ static inline unsigned int hist_buckets(const struct histogram *hs)
return hs->nr_buckets;
}
+struct histogram_cdf *hist_cdf(const struct histogram *hs);
+void histcdf_free(struct histogram_cdf *cdf);
+
void hist_import(struct histogram *dest, const struct histogram *src);
void hist_move(struct histogram *dest, struct histogram *src);
diff --git a/man/man8/xfs_scrub.8 b/man/man8/xfs_scrub.8
index 404baba69..b9f253e1b 100644
--- a/man/man8/xfs_scrub.8
+++ b/man/man8/xfs_scrub.8
@@ -100,6 +100,22 @@ The
supported are:
.RS 1.0i
.TP
+.BI fstrim_pct= percentage
+To constrain the amount of time spent on fstrim activities during phase 8,
+this program tries to balance estimated runtime against completeness of the
+trim.
+In short, the program avoids small trim requests to save time.
+
+During phase 7, a log-scale histogram of free space extents is constructed.
+At the start of phase 8, a CDF is computed in decreasing order of extent
+length from the histogram buckets.
+A point corresponding to the fstrim percentage target is chosen from the CDF
+and mapped back to a histogram bucket.
+Free space extents at least as long as the bucket size are trimmed.
+Smaller extents are ignored.
+
+By default, the percentage threshold is 99%.
+.TP
.BI iwarn
Treat informational messages as warnings.
This will result in a nonzero return code, and a higher logging level.
diff --git a/scrub/phase8.c b/scrub/phase8.c
index e35bf11bf..1c88460c3 100644
--- a/scrub/phase8.c
+++ b/scrub/phase8.c
@@ -11,6 +11,7 @@
#include "list.h"
#include "libfrog/paths.h"
#include "libfrog/workqueue.h"
+#include "libfrog/histogram.h"
#include "xfs_scrub.h"
#include "common.h"
#include "progress.h"
@@ -57,10 +58,12 @@ static int
fstrim_fsblocks(
struct scrub_ctx *ctx,
uint64_t start_fsb,
- uint64_t fsbcount)
+ uint64_t fsbcount,
+ uint64_t minlen_fsb)
{
uint64_t start = cvt_off_fsb_to_b(&ctx->mnt, start_fsb);
uint64_t len = cvt_off_fsb_to_b(&ctx->mnt, fsbcount);
+ uint64_t minlen = cvt_off_fsb_to_b(&ctx->mnt, minlen_fsb);
int error;
while (len > 0) {
@@ -68,7 +71,7 @@ fstrim_fsblocks(
run = min(len, FSTRIM_MAX_BYTES);
- error = fstrim(ctx, start, run);
+ error = fstrim(ctx, start, run, minlen);
if (error == EOPNOTSUPP) {
/* Pretend we finished all the work. */
progress_add(len);
@@ -78,9 +81,10 @@ fstrim_fsblocks(
char descr[DESCR_BUFSZ];
snprintf(descr, sizeof(descr) - 1,
- _("fstrim start 0x%llx run 0x%llx"),
+ _("fstrim start 0x%llx run 0x%llx minlen 0x%llx"),
(unsigned long long)start,
- (unsigned long long)run);
+ (unsigned long long)run,
+ (unsigned long long)minlen);
str_liberror(ctx, error, descr);
return error;
}
@@ -93,6 +97,80 @@ fstrim_fsblocks(
return 0;
}
+/*
+ * Return the smallest minlen that still enables us to discard the specified
+ * number of free blocks. Returns 0 if something goes wrong, which means no
+ * minlen threshold for discard.
+ */
+static uint64_t
+minlen_for_threshold(
+ const struct histogram *hs,
+ uint64_t blk_threshold)
+{
+ struct histogram_cdf *cdf;
+ unsigned int i;
+ uint64_t ret = 0;
+
+ /* Insufficient samples to make a meaningful histogram */
+ if (hs->tot_obs < hs->nr_buckets * 10)
+ return 0;
+
+ cdf = hist_cdf(hs);
+ if (!cdf)
+ return 0;
+
+ for (i = 1; i < hs->nr_buckets; i++) {
+ if (cdf->buckets[i].sum < blk_threshold) {
+ ret = hs->buckets[i - 1].low;
+ break;
+ }
+ }
+
+ histcdf_free(cdf);
+ return ret;
+}
+
+/* Compute a suitable minlen parameter for fstrim. */
+static uint64_t
+fstrim_compute_minlen(
+ const struct scrub_ctx *ctx,
+ const struct histogram *freesp_hist)
+{
+ uint64_t ret;
+ double blk_threshold = 0;
+ unsigned int ag_max_usable;
+
+ /*
+ * The kernel will reject a minlen that's larger than m_ag_max_usable.
+ * We can't calculate or query that value directly, so we guesstimate
+ * that it's 95% of the AG size.
+ */
+ ag_max_usable = ctx->mnt.fsgeom.agblocks * 95 / 100;
+
+ if (debug > 1) {
+ struct histogram_strings hstr = {
+ .sum = _("free space blocks"),
+ .observations = _("free space extents"),
+ };
+
+ hist_print(freesp_hist, &hstr);
+ }
+
+ ret = minlen_for_threshold(freesp_hist,
+ freesp_hist->tot_sum * ctx->fstrim_block_pct);
+
+ if (debug > 1)
+ printf(_("fstrim minlen %lld threshold %lld ag_max_usable %u\n"),
+ (unsigned long long)ret,
+ (unsigned long long)blk_threshold,
+ ag_max_usable);
+ if (ret > ag_max_usable)
+ ret = ag_max_usable;
+ if (ret == 1)
+ ret = 0;
+ return ret;
+}
+
/* Trim each AG on the data device. */
static int
fstrim_datadev(
@@ -100,8 +178,11 @@ fstrim_datadev(
{
struct xfs_fsop_geom *geo = &ctx->mnt.fsgeom;
uint64_t fsbno;
+ uint64_t minlen_fsb;
int error;
+ minlen_fsb = fstrim_compute_minlen(ctx, &ctx->datadev_hist);
+
for (fsbno = 0; fsbno < geo->datablocks; fsbno += geo->agblocks) {
uint64_t fsbcount;
@@ -112,7 +193,7 @@ fstrim_datadev(
*/
progress_add(geo->blocksize);
fsbcount = min(geo->datablocks - fsbno, geo->agblocks);
- error = fstrim_fsblocks(ctx, fsbno, fsbcount);
+ error = fstrim_fsblocks(ctx, fsbno, fsbcount, minlen_fsb);
if (error)
return error;
}
diff --git a/scrub/vfs.c b/scrub/vfs.c
index cc958ba94..22c19485a 100644
--- a/scrub/vfs.c
+++ b/scrub/vfs.c
@@ -300,11 +300,13 @@ int
fstrim(
struct scrub_ctx *ctx,
uint64_t start,
- uint64_t len)
+ uint64_t len,
+ uint64_t minlen)
{
struct fstrim_range range = {
.start = start,
.len = len,
+ .minlen = minlen,
};
if (ioctl(ctx->mnt.fd, FITRIM, &range) == 0)
diff --git a/scrub/vfs.h b/scrub/vfs.h
index 1af8d80d1..f0cfd53c2 100644
--- a/scrub/vfs.h
+++ b/scrub/vfs.h
@@ -24,6 +24,6 @@ typedef int (*scan_fs_tree_dirent_fn)(struct scrub_ctx *, const char *,
int scan_fs_tree(struct scrub_ctx *ctx, scan_fs_tree_dir_fn dir_fn,
scan_fs_tree_dirent_fn dirent_fn, void *arg);
-int fstrim(struct scrub_ctx *ctx, uint64_t start, uint64_t len);
+int fstrim(struct scrub_ctx *ctx, uint64_t start, uint64_t len, uint64_t minlen);
#endif /* XFS_SCRUB_VFS_H_ */
diff --git a/scrub/xfs_scrub.c b/scrub/xfs_scrub.c
index 2894f6148..296d814ec 100644
--- a/scrub/xfs_scrub.c
+++ b/scrub/xfs_scrub.c
@@ -622,11 +622,13 @@ report_outcome(
*/
enum o_opt_nums {
IWARN = 0,
+ FSTRIM_PCT,
O_MAX_OPTS,
};
static char *o_opts[] = {
[IWARN] = "iwarn",
+ [FSTRIM_PCT] = "fstrim_pct",
[O_MAX_OPTS] = NULL,
};
@@ -635,8 +637,11 @@ parse_o_opts(
struct scrub_ctx *ctx,
char *p)
{
+ double dval;
+
while (*p != '\0') {
char *val;
+ char *endp;
switch (getsubopt(&p, o_opts, &val)) {
case IWARN:
@@ -647,6 +652,35 @@ parse_o_opts(
}
info_is_warning = true;
break;
+ case FSTRIM_PCT:
+ if (!val) {
+ fprintf(stderr,
+ _("-o fstrim_pct requires a parameter\n"));
+ usage();
+ }
+
+ errno = 0;
+ dval = strtod(val, &endp);
+
+ if (*endp) {
+ fprintf(stderr,
+ _("-o fstrim_pct must be a floating point number\n"));
+ usage();
+ }
+ if (errno) {
+ fprintf(stderr,
+ _("-o fstrim_pct: %s\n"),
+ strerror(errno));
+ usage();
+ }
+ if (dval <= 0 || dval > 100) {
+ fprintf(stderr,
+ _("-o fstrim_pct must be larger than 0 and less than 100\n"));
+ usage();
+ }
+
+ ctx->fstrim_block_pct = dval / 100.0;
+ break;
default:
usage();
break;
@@ -659,7 +693,9 @@ main(
int argc,
char **argv)
{
- struct scrub_ctx ctx = {0};
+ struct scrub_ctx ctx = {
+ .fstrim_block_pct = FSTRIM_BLOCK_PCT_DEFAULT,
+ };
struct phase_rusage all_pi;
char *mtab = NULL;
FILE *progress_fp = NULL;
diff --git a/scrub/xfs_scrub.h b/scrub/xfs_scrub.h
index 1a28f0cc8..7d48f4bad 100644
--- a/scrub/xfs_scrub.h
+++ b/scrub/xfs_scrub.h
@@ -90,8 +90,20 @@ struct scrub_ctx {
/* Free space histograms, in fsb */
struct histogram datadev_hist;
+
+ /*
+ * Pick the largest value for fstrim minlen such that we trim at least
+ * this much space per volume.
+ */
+ double fstrim_block_pct;
};
+/*
+ * Trim only enough free space extents (in order of decreasing length) to
+ * ensure that this percentage of the free space is trimmed.
+ */
+#define FSTRIM_BLOCK_PCT_DEFAULT (99.0 / 100.0)
+
/* Phase helper functions */
void xfs_shutdown_fs(struct scrub_ctx *ctx);
int scrub_cleanup(struct scrub_ctx *ctx);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/6] xfs_scrub: allow auxiliary pathnames for sandboxing
2024-07-30 0:20 ` [PATCHSET v30.9 14/23] xfs_scrub: tighten security of systemd services Darrick J. Wong
@ 2024-07-30 1:13 ` Darrick J. Wong
2024-07-30 1:13 ` [PATCH 2/6] xfs_scrub.service: reduce background CPU usage to less than one core if possible Darrick J. Wong
` (4 subsequent siblings)
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:13 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
In the next patch, we'll tighten up the security on the xfs_scrub
service so that it can't escape. However, sandboxing the service
involves making the host filesystem as inaccessible as possible, with
the filesystem to scrub bind mounted onto a known location within the
sandbox. Hence we need one path for reporting and a new -M argument to
tell scrub what it should actually be trying to open.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man8/xfs_scrub.8 | 9 ++++++++-
scrub/phase1.c | 4 ++--
scrub/vfs.c | 2 +-
scrub/xfs_scrub.c | 11 ++++++++---
scrub/xfs_scrub.h | 5 ++++-
5 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/man/man8/xfs_scrub.8 b/man/man8/xfs_scrub.8
index b9f253e1b..615401127 100644
--- a/man/man8/xfs_scrub.8
+++ b/man/man8/xfs_scrub.8
@@ -4,7 +4,7 @@ xfs_scrub \- check and repair the contents of a mounted XFS filesystem
.SH SYNOPSIS
.B xfs_scrub
[
-.B \-abCemnTvx
+.B \-abCeMmnTvx
]
.I mount-point
.br
@@ -79,6 +79,13 @@ behavior.
.B \-k
Do not call TRIM on the free space.
.TP
+.BI \-M " real-mount-point"
+Open the this path for issuing scrub system calls to the kernel.
+The positional
+.I mount-point
+parameter will be used for displaying informational messages and logging.
+This parameter exists to enable process sandboxing for service mode.
+.TP
.BI \-m " file"
Search this file for mounted filesystems instead of /etc/mtab.
.TP
diff --git a/scrub/phase1.c b/scrub/phase1.c
index 1b3f6e8eb..516d929d6 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -146,7 +146,7 @@ phase1_func(
* CAP_SYS_ADMIN, which we probably need to do anything fancy
* with the (XFS driver) kernel.
*/
- error = -xfd_open(&ctx->mnt, ctx->mntpoint,
+ error = -xfd_open(&ctx->mnt, ctx->actual_mntpoint,
O_RDONLY | O_NOATIME | O_DIRECTORY);
if (error) {
if (error == EPERM)
@@ -199,7 +199,7 @@ _("Not an XFS filesystem."));
return error;
}
- error = path_to_fshandle(ctx->mntpoint, &ctx->fshandle,
+ error = path_to_fshandle(ctx->actual_mntpoint, &ctx->fshandle,
&ctx->fshandle_len);
if (error) {
str_errno(ctx, _("getting fshandle"));
diff --git a/scrub/vfs.c b/scrub/vfs.c
index 22c19485a..fca9a4cf3 100644
--- a/scrub/vfs.c
+++ b/scrub/vfs.c
@@ -249,7 +249,7 @@ scan_fs_tree(
goto out_cond;
}
- ret = queue_subdir(ctx, &sft, &wq, ctx->mntpoint, true);
+ ret = queue_subdir(ctx, &sft, &wq, ctx->actual_mntpoint, true);
if (ret) {
str_liberror(ctx, ret, _("queueing directory scan"));
goto out_wq;
diff --git a/scrub/xfs_scrub.c b/scrub/xfs_scrub.c
index 296d814ec..d7cef115d 100644
--- a/scrub/xfs_scrub.c
+++ b/scrub/xfs_scrub.c
@@ -725,7 +725,7 @@ main(
pthread_mutex_init(&ctx.lock, NULL);
ctx.mode = SCRUB_MODE_REPAIR;
ctx.error_action = ERRORS_CONTINUE;
- while ((c = getopt(argc, argv, "a:bC:de:km:no:TvxV")) != EOF) {
+ while ((c = getopt(argc, argv, "a:bC:de:kM:m:no:TvxV")) != EOF) {
switch (c) {
case 'a':
ctx.max_errors = cvt_u64(optarg, 10);
@@ -769,6 +769,9 @@ main(
case 'k':
want_fstrim = false;
break;
+ case 'M':
+ ctx.actual_mntpoint = optarg;
+ break;
case 'm':
mtab = optarg;
break;
@@ -823,6 +826,8 @@ main(
usage();
ctx.mntpoint = argv[optind];
+ if (!ctx.actual_mntpoint)
+ ctx.actual_mntpoint = ctx.mntpoint;
stdout_isatty = isatty(STDOUT_FILENO);
stderr_isatty = isatty(STDERR_FILENO);
@@ -840,7 +845,7 @@ main(
return SCRUB_RET_OPERROR;
/* Find the mount record for the passed-in argument. */
- if (stat(argv[optind], &ctx.mnt_sb) < 0) {
+ if (stat(ctx.actual_mntpoint, &ctx.mnt_sb) < 0) {
fprintf(stderr,
_("%s: could not stat: %s: %s\n"),
progname, argv[optind], strerror(errno));
@@ -863,7 +868,7 @@ main(
}
fs_table_initialise(0, NULL, 0, NULL);
- fsp = fs_table_lookup_mount(ctx.mntpoint);
+ fsp = fs_table_lookup_mount(ctx.actual_mntpoint);
if (!fsp) {
fprintf(stderr, _("%s: Not a XFS mount point.\n"),
ctx.mntpoint);
diff --git a/scrub/xfs_scrub.h b/scrub/xfs_scrub.h
index 7d48f4bad..b0aa9fcc6 100644
--- a/scrub/xfs_scrub.h
+++ b/scrub/xfs_scrub.h
@@ -38,9 +38,12 @@ enum error_action {
struct scrub_ctx {
/* Immutable scrub state. */
- /* Strings we need for presentation */
+ /* Mountpoint we use for presentation */
char *mntpoint;
+ /* Actual VFS path to the filesystem */
+ char *actual_mntpoint;
+
/* Mountpoint info */
struct stat mnt_sb;
struct statvfs mnt_sv;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/6] xfs_scrub.service: reduce background CPU usage to less than one core if possible
2024-07-30 0:20 ` [PATCHSET v30.9 14/23] xfs_scrub: tighten security of systemd services Darrick J. Wong
2024-07-30 1:13 ` [PATCH 1/6] xfs_scrub: allow auxiliary pathnames for sandboxing Darrick J. Wong
@ 2024-07-30 1:13 ` Darrick J. Wong
2024-07-30 1:13 ` [PATCH 3/6] xfs_scrub: use dynamic users when running as a systemd service Darrick J. Wong
` (3 subsequent siblings)
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:13 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Currently, the xfs_scrub background service is configured to use -b,
which means that the program runs completely serially. However, even
using all of one CPU core with idle priority may be enough to cause
thermal throttling and unwanted fan noise on smaller systems (e.g.
laptops) with fast IO systems.
Let's try to avoid this (at least on systemd) by using cgroups to limit
the program's usage to slghtly more than half of one CPU and lowering
the nice priority in the scheduler. What we /really/ want is to run
steadily on an efficiency core, but there doesn't seem to be a means to
ask the scheduler not to ramp up the CPU frequency for a particular
task.
While we're at it, group the resource limit directives together.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/Makefile | 7 ++++++-
scrub/system-xfs_scrub.slice | 30 ++++++++++++++++++++++++++++++
scrub/xfs_scrub@.service.in | 12 ++++++++++--
scrub/xfs_scrub_all.service.in | 4 ++++
scrub/xfs_scrub_fail@.service.in | 4 ++++
5 files changed, 54 insertions(+), 3 deletions(-)
create mode 100644 scrub/system-xfs_scrub.slice
diff --git a/scrub/Makefile b/scrub/Makefile
index 8ccc67d01..2a257e080 100644
--- a/scrub/Makefile
+++ b/scrub/Makefile
@@ -18,7 +18,12 @@ XFS_SCRUB_FAIL_PROG = xfs_scrub_fail
XFS_SCRUB_ARGS = -b -n
ifeq ($(HAVE_SYSTEMD),yes)
INSTALL_SCRUB += install-systemd
-SYSTEMD_SERVICES = $(scrub_svcname) xfs_scrub_all.service xfs_scrub_all.timer xfs_scrub_fail@.service
+SYSTEMD_SERVICES=\
+ $(scrub_svcname) \
+ xfs_scrub_fail@.service \
+ xfs_scrub_all.service \
+ xfs_scrub_all.timer \
+ system-xfs_scrub.slice
OPTIONAL_TARGETS += $(SYSTEMD_SERVICES)
endif
ifeq ($(HAVE_CROND),yes)
diff --git a/scrub/system-xfs_scrub.slice b/scrub/system-xfs_scrub.slice
new file mode 100644
index 000000000..95cd4f745
--- /dev/null
+++ b/scrub/system-xfs_scrub.slice
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2022-2024 Oracle. All Rights Reserved.
+# Author: Darrick J. Wong <djwong@kernel.org>
+
+[Unit]
+Description=xfs_scrub background service slice
+Before=slices.target
+
+[Slice]
+
+# If the CPU usage cgroup controller is available, don't use more than 60% of a
+# single core for all background processes.
+CPUQuota=60%
+CPUAccounting=true
+
+[Install]
+# As of systemd 249, the systemd cgroupv2 configuration code will drop resource
+# controllers from the root and system.slice cgroups at startup if it doesn't
+# find any direct dependencies that require a given controller. Newly
+# activated units with resource control directives are created under the system
+# slice but do not cause a reconfiguration of the slice's resource controllers.
+# Hence we cannot put CPUQuota= into the xfs_scrub service units directly.
+#
+# For the CPUQuota directive to have any effect, we must therefore create an
+# explicit definition file for the slice that systemd creates to contain the
+# xfs_scrub instance units (e.g. xfs_scrub@.service) and we must configure this
+# slice as a dependency of the system slice to establish the direct dependency
+# relation.
+WantedBy=system.slice
diff --git a/scrub/xfs_scrub@.service.in b/scrub/xfs_scrub@.service.in
index 05e5293ee..855fe4de4 100644
--- a/scrub/xfs_scrub@.service.in
+++ b/scrub/xfs_scrub@.service.in
@@ -18,8 +18,16 @@ PrivateTmp=no
AmbientCapabilities=CAP_SYS_ADMIN CAP_FOWNER CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_SYS_RAWIO
NoNewPrivileges=yes
User=nobody
-IOSchedulingClass=idle
-CPUSchedulingPolicy=idle
Environment=SERVICE_MODE=1
ExecStart=@sbindir@/xfs_scrub @scrub_args@ %f
SyslogIdentifier=%N
+
+# Run scrub with minimal CPU and IO priority so that nothing else will starve.
+IOSchedulingClass=idle
+CPUSchedulingPolicy=idle
+CPUAccounting=true
+Nice=19
+
+# Create the service underneath the scrub background service slice so that we
+# can control resource usage.
+Slice=system-xfs_scrub.slice
diff --git a/scrub/xfs_scrub_all.service.in b/scrub/xfs_scrub_all.service.in
index 347cd6e66..96be90e74 100644
--- a/scrub/xfs_scrub_all.service.in
+++ b/scrub/xfs_scrub_all.service.in
@@ -14,3 +14,7 @@ Type=oneshot
Environment=SERVICE_MODE=1
ExecStart=@sbindir@/xfs_scrub_all
SyslogIdentifier=xfs_scrub_all
+
+# Create the service underneath the scrub background service slice so that we
+# can control resource usage.
+Slice=system-xfs_scrub.slice
diff --git a/scrub/xfs_scrub_fail@.service.in b/scrub/xfs_scrub_fail@.service.in
index 96a2ed5da..32012ec35 100644
--- a/scrub/xfs_scrub_fail@.service.in
+++ b/scrub/xfs_scrub_fail@.service.in
@@ -14,3 +14,7 @@ ExecStart=@pkg_libexec_dir@/xfs_scrub_fail "${EMAIL_ADDR}" %f
User=mail
Group=mail
SupplementaryGroups=systemd-journal
+
+# Create the service underneath the scrub background service slice so that we
+# can control resource usage.
+Slice=system-xfs_scrub.slice
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/6] xfs_scrub: use dynamic users when running as a systemd service
2024-07-30 0:20 ` [PATCHSET v30.9 14/23] xfs_scrub: tighten security of systemd services Darrick J. Wong
2024-07-30 1:13 ` [PATCH 1/6] xfs_scrub: allow auxiliary pathnames for sandboxing Darrick J. Wong
2024-07-30 1:13 ` [PATCH 2/6] xfs_scrub.service: reduce background CPU usage to less than one core if possible Darrick J. Wong
@ 2024-07-30 1:13 ` Darrick J. Wong
2024-07-30 1:13 ` [PATCH 4/6] xfs_scrub: tighten up the security on the background " Darrick J. Wong
` (2 subsequent siblings)
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:13 UTC (permalink / raw)
To: djwong, cem; +Cc: Helle Vaanzinn, Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Five years ago, systemd introduced the DynamicUser directive that
allocates a new unique user/group id, runs a service with those ids, and
deletes them after the service exits. This is a good replacement for
User=nobody, since it eliminates the threat of nobody-services messing
with each other.
Make this transition ahead of all the other security tightenings that
will land in the next few patches, and add credits for the people who
suggested the change and reviewed it.
Link: https://0pointer.net/blog/dynamic-users-with-systemd.html
Suggested-by: Helle Vaanzinn <glitsj16@riseup.net>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
scrub/xfs_scrub@.service.in | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/scrub/xfs_scrub@.service.in b/scrub/xfs_scrub@.service.in
index 855fe4de4..52068add8 100644
--- a/scrub/xfs_scrub@.service.in
+++ b/scrub/xfs_scrub@.service.in
@@ -17,7 +17,6 @@ ProtectHome=read-only
PrivateTmp=no
AmbientCapabilities=CAP_SYS_ADMIN CAP_FOWNER CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_SYS_RAWIO
NoNewPrivileges=yes
-User=nobody
Environment=SERVICE_MODE=1
ExecStart=@sbindir@/xfs_scrub @scrub_args@ %f
SyslogIdentifier=%N
@@ -31,3 +30,6 @@ Nice=19
# Create the service underneath the scrub background service slice so that we
# can control resource usage.
Slice=system-xfs_scrub.slice
+
+# Dynamically create a user that isn't root
+DynamicUser=true
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/6] xfs_scrub: tighten up the security on the background systemd service
2024-07-30 0:20 ` [PATCHSET v30.9 14/23] xfs_scrub: tighten security of systemd services Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:13 ` [PATCH 3/6] xfs_scrub: use dynamic users when running as a systemd service Darrick J. Wong
@ 2024-07-30 1:13 ` Darrick J. Wong
2024-07-30 1:14 ` [PATCH 5/6] xfs_scrub_fail: " Darrick J. Wong
2024-07-30 1:14 ` [PATCH 6/6] xfs_scrub_all: " Darrick J. Wong
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:13 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Currently, xfs_scrub has to run with some elevated privileges. Minimize
the risk of xfs_scrub escaping its service container or contaminating
the rest of the system by using systemd's sandboxing controls to
prohibit as much access as possible.
The directives added by this patch were recommended by the command
'systemd-analyze security xfs_scrub@.service' in systemd 249.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/xfs_scrub@.service.in | 81 +++++++++++++++++++++++++++++++++++++++----
1 file changed, 73 insertions(+), 8 deletions(-)
diff --git a/scrub/xfs_scrub@.service.in b/scrub/xfs_scrub@.service.in
index 52068add8..a8dd9052f 100644
--- a/scrub/xfs_scrub@.service.in
+++ b/scrub/xfs_scrub@.service.in
@@ -8,17 +8,21 @@ Description=Online XFS Metadata Check for %f
OnFailure=xfs_scrub_fail@%i.service
Documentation=man:xfs_scrub(8)
+# Explicitly require the capabilities that this program needs
+ConditionCapability=CAP_SYS_ADMIN
+ConditionCapability=CAP_FOWNER
+ConditionCapability=CAP_DAC_OVERRIDE
+ConditionCapability=CAP_DAC_READ_SEARCH
+ConditionCapability=CAP_SYS_RAWIO
+
+# Must be a mountpoint
+ConditionPathIsMountPoint=%f
+RequiresMountsFor=%f
+
[Service]
Type=oneshot
-PrivateNetwork=true
-ProtectSystem=full
-ProtectHome=read-only
-# Disable private /tmp just in case %f is a path under /tmp.
-PrivateTmp=no
-AmbientCapabilities=CAP_SYS_ADMIN CAP_FOWNER CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_SYS_RAWIO
-NoNewPrivileges=yes
Environment=SERVICE_MODE=1
-ExecStart=@sbindir@/xfs_scrub @scrub_args@ %f
+ExecStart=@sbindir@/xfs_scrub @scrub_args@ -M /tmp/scrub/ %f
SyslogIdentifier=%N
# Run scrub with minimal CPU and IO priority so that nothing else will starve.
@@ -31,5 +35,66 @@ Nice=19
# can control resource usage.
Slice=system-xfs_scrub.slice
+# No realtime CPU scheduling
+RestrictRealtime=true
+
# Dynamically create a user that isn't root
DynamicUser=true
+
+# Make the entire filesystem readonly and /home inaccessible, then bind mount
+# the filesystem we're supposed to be checking into our private /tmp dir.
+# 'norbind' means that we don't bind anything under that original mount.
+ProtectSystem=strict
+ProtectHome=yes
+PrivateTmp=true
+BindPaths=%f:/tmp/scrub:norbind
+
+# Don't let scrub complain about paths in /etc/projects that have been hidden
+# by our sandboxing. scrub doesn't care about project ids anyway.
+InaccessiblePaths=-/etc/projects
+
+# No network access
+PrivateNetwork=true
+ProtectHostname=true
+RestrictAddressFamilies=none
+IPAddressDeny=any
+
+# Don't let the program mess with the kernel configuration at all
+ProtectKernelLogs=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectControlGroups=true
+ProtectProc=invisible
+RestrictNamespaces=true
+
+# Hide everything in /proc, even /proc/mounts
+ProcSubset=pid
+
+# Only allow the default personality Linux
+LockPersonality=true
+
+# No writable memory pages
+MemoryDenyWriteExecute=true
+
+# Don't let our mounts leak out to the host
+PrivateMounts=true
+
+# Restrict system calls to the native arch and only enough to get things going
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+SystemCallFilter=~@privileged
+SystemCallFilter=~@resources
+SystemCallFilter=~@mount
+
+# xfs_scrub needs these privileges to run, and no others
+CapabilityBoundingSet=CAP_SYS_ADMIN CAP_FOWNER CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_SYS_RAWIO
+AmbientCapabilities=CAP_SYS_ADMIN CAP_FOWNER CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_SYS_RAWIO
+NoNewPrivileges=true
+
+# xfs_scrub doesn't create files
+UMask=7777
+
+# No access to hardware /dev files except for block devices
+ProtectClock=true
+DevicePolicy=closed
+DeviceAllow=block-*
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/6] xfs_scrub_fail: tighten up the security on the background systemd service
2024-07-30 0:20 ` [PATCHSET v30.9 14/23] xfs_scrub: tighten security of systemd services Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:13 ` [PATCH 4/6] xfs_scrub: tighten up the security on the background " Darrick J. Wong
@ 2024-07-30 1:14 ` Darrick J. Wong
2024-07-30 1:14 ` [PATCH 6/6] xfs_scrub_all: " Darrick J. Wong
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:14 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Currently, xfs_scrub_fail has to run with enough privileges to access
the journal contents for a given scrub run and to send a report via
email. Minimize the risk of xfs_scrub_fail escaping its service
container or contaminating the rest of the system by using systemd's
sandboxing controls to prohibit as much access as possible.
The directives added by this patch were recommended by the command
'systemd-analyze security xfs_scrub_fail@.service' in systemd 249.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/xfs_scrub_fail@.service.in | 55 ++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/scrub/xfs_scrub_fail@.service.in b/scrub/xfs_scrub_fail@.service.in
index 32012ec35..2c879afd6 100644
--- a/scrub/xfs_scrub_fail@.service.in
+++ b/scrub/xfs_scrub_fail@.service.in
@@ -18,3 +18,58 @@ SupplementaryGroups=systemd-journal
# Create the service underneath the scrub background service slice so that we
# can control resource usage.
Slice=system-xfs_scrub.slice
+
+# No realtime scheduling
+RestrictRealtime=true
+
+# Make the entire filesystem readonly and /home inaccessible.
+ProtectSystem=full
+ProtectHome=yes
+PrivateTmp=true
+RestrictSUIDSGID=true
+
+# Emailing reports requires network access, but not the ability to change the
+# hostname.
+ProtectHostname=true
+
+# Don't let the program mess with the kernel configuration at all
+ProtectKernelLogs=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectControlGroups=true
+ProtectProc=invisible
+RestrictNamespaces=true
+
+# Can't hide /proc because journalctl needs it to find various pieces of log
+# information
+#ProcSubset=pid
+
+# Only allow the default personality Linux
+LockPersonality=true
+
+# No writable memory pages
+MemoryDenyWriteExecute=true
+
+# Don't let our mounts leak out to the host
+PrivateMounts=true
+
+# Restrict system calls to the native arch and only enough to get things going
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+SystemCallFilter=~@privileged
+SystemCallFilter=~@resources
+SystemCallFilter=~@mount
+
+# xfs_scrub needs these privileges to run, and no others
+CapabilityBoundingSet=
+NoNewPrivileges=true
+
+# Failure reporting shouldn't create world-readable files
+UMask=0077
+
+# Clean up any IPC objects when this unit stops
+RemoveIPC=true
+
+# No access to hardware device files
+PrivateDevices=true
+ProtectClock=true
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 6/6] xfs_scrub_all: tighten up the security on the background systemd service
2024-07-30 0:20 ` [PATCHSET v30.9 14/23] xfs_scrub: tighten security of systemd services Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 1:14 ` [PATCH 5/6] xfs_scrub_fail: " Darrick J. Wong
@ 2024-07-30 1:14 ` Darrick J. Wong
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:14 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Currently, xfs_scrub_all has to run with enough privileges to find
mounted XFS filesystems and the device associated with that mount and to
start xfs_scrub@<mountpoint> sub-services. Minimize the risk of
xfs_scrub_all escaping its service container or contaminating the rest
of the system by using systemd's sandboxing controls to prohibit as much
access as possible.
The directives added by this patch were recommended by the command
'systemd-analyze security xfs_scrub_all.service' in systemd 249.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/xfs_scrub_all.service.in | 62 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/scrub/xfs_scrub_all.service.in b/scrub/xfs_scrub_all.service.in
index 96be90e74..478cd8d05 100644
--- a/scrub/xfs_scrub_all.service.in
+++ b/scrub/xfs_scrub_all.service.in
@@ -18,3 +18,65 @@ SyslogIdentifier=xfs_scrub_all
# Create the service underneath the scrub background service slice so that we
# can control resource usage.
Slice=system-xfs_scrub.slice
+
+# Run scrub_all with minimal CPU and IO priority so that nothing will starve.
+IOSchedulingClass=idle
+CPUSchedulingPolicy=idle
+CPUAccounting=true
+Nice=19
+
+# No realtime scheduling
+RestrictRealtime=true
+
+# No special privileges, but we still have to run as root so that we can
+# contact the service manager to start the sub-units.
+CapabilityBoundingSet=
+NoNewPrivileges=true
+RestrictSUIDSGID=true
+
+# Make the entire filesystem readonly. We don't want to hide anything because
+# we need to find all mounted XFS filesystems in the host.
+ProtectSystem=strict
+ProtectHome=read-only
+PrivateTmp=false
+
+# No network access except to the systemd control socket
+PrivateNetwork=true
+ProtectHostname=true
+RestrictAddressFamilies=AF_UNIX
+IPAddressDeny=any
+
+# Don't let the program mess with the kernel configuration at all
+ProtectKernelLogs=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectControlGroups=true
+ProtectProc=invisible
+RestrictNamespaces=true
+
+# Hide everything in /proc, even /proc/mounts
+ProcSubset=pid
+
+# Only allow the default personality Linux
+LockPersonality=true
+
+# No writable memory pages
+MemoryDenyWriteExecute=true
+
+# Don't let our mounts leak out to the host
+PrivateMounts=true
+
+# Restrict system calls to the native arch and only enough to get things going
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+SystemCallFilter=~@privileged
+SystemCallFilter=~@resources
+SystemCallFilter=~@mount
+
+# Media scan stamp file shouldn't be readable by regular users
+UMask=0077
+
+# lsblk ignores mountpoints if it can't find the device files, so we cannot
+# hide them
+#ProtectClock=true
+#PrivateDevices=true
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/6] xfs_scrub_all: only use the xfs_scrub@ systemd services in service mode
2024-07-30 0:20 ` [PATCHSET v30.9 15/23] xfs_scrub_all: automatic media scan service Darrick J. Wong
@ 2024-07-30 1:14 ` Darrick J. Wong
2024-07-30 1:14 ` [PATCH 2/6] xfs_scrub_all: remove journalctl background process Darrick J. Wong
` (4 subsequent siblings)
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:14 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Since the per-mount xfs_scrub@.service definition includes a bunch of
resource usage constraints, we no longer want to use those services if
xfs_scrub_all is being run directly by the sysadmin (aka not in service
mode) on the presumption that sysadmins want answers as quickly as
possible.
Therefore, only try to call the systemd service from xfs_scrub_all if
SERVICE_MODE is set in the environment. If reaching out to systemd
fails and we're in service mode, we still want to run xfs_scrub
directly. Split the makefile variables as necessary so that we only
pass -b to xfs_scrub in service mode.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/Makefile | 5 ++++-
scrub/xfs_scrub@.service.in | 2 +-
scrub/xfs_scrub_all.in | 11 ++++++++---
3 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/scrub/Makefile b/scrub/Makefile
index 2a257e080..2050fe28f 100644
--- a/scrub/Makefile
+++ b/scrub/Makefile
@@ -15,7 +15,8 @@ LTCOMMAND = xfs_scrub
INSTALL_SCRUB = install-scrub
XFS_SCRUB_ALL_PROG = xfs_scrub_all
XFS_SCRUB_FAIL_PROG = xfs_scrub_fail
-XFS_SCRUB_ARGS = -b -n
+XFS_SCRUB_ARGS = -n
+XFS_SCRUB_SERVICE_ARGS = -b
ifeq ($(HAVE_SYSTEMD),yes)
INSTALL_SCRUB += install-systemd
SYSTEMD_SERVICES=\
@@ -113,6 +114,7 @@ xfs_scrub_all: xfs_scrub_all.in $(builddefs)
$(Q)$(SED) -e "s|@sbindir@|$(PKG_SBIN_DIR)|g" \
-e "s|@scrub_svcname@|$(scrub_svcname)|g" \
-e "s|@pkg_version@|$(PKG_VERSION)|g" \
+ -e "s|@scrub_service_args@|$(XFS_SCRUB_SERVICE_ARGS)|g" \
-e "s|@scrub_args@|$(XFS_SCRUB_ARGS)|g" < $< > $@
$(Q)chmod a+x $@
@@ -132,6 +134,7 @@ install: $(INSTALL_SCRUB)
%.service: %.service.in $(builddefs)
@echo " [SED] $@"
$(Q)$(SED) -e "s|@sbindir@|$(PKG_SBIN_DIR)|g" \
+ -e "s|@scrub_service_args@|$(XFS_SCRUB_SERVICE_ARGS)|g" \
-e "s|@scrub_args@|$(XFS_SCRUB_ARGS)|g" \
-e "s|@pkg_libexec_dir@|$(PKG_LIBEXEC_DIR)|g" \
< $< > $@
diff --git a/scrub/xfs_scrub@.service.in b/scrub/xfs_scrub@.service.in
index a8dd9052f..5fa5f3282 100644
--- a/scrub/xfs_scrub@.service.in
+++ b/scrub/xfs_scrub@.service.in
@@ -22,7 +22,7 @@ RequiresMountsFor=%f
[Service]
Type=oneshot
Environment=SERVICE_MODE=1
-ExecStart=@sbindir@/xfs_scrub @scrub_args@ -M /tmp/scrub/ %f
+ExecStart=@sbindir@/xfs_scrub @scrub_service_args@ @scrub_args@ -M /tmp/scrub/ %f
SyslogIdentifier=%N
# Run scrub with minimal CPU and IO priority so that nothing else will starve.
diff --git a/scrub/xfs_scrub_all.in b/scrub/xfs_scrub_all.in
index d0ab27fd3..f27251fa5 100644
--- a/scrub/xfs_scrub_all.in
+++ b/scrub/xfs_scrub_all.in
@@ -162,9 +162,10 @@ def run_scrub(mnt, cond, running_devs, mntdevs, killfuncs):
if terminate:
return
- # Try it the systemd way
+ # Run per-mount systemd xfs_scrub service only if we ourselves
+ # are running as a systemd service.
unitname = path_to_serviceunit(path)
- if unitname is not None:
+ if unitname is not None and 'SERVICE_MODE' in os.environ:
ret = systemctl_start(unitname, killfuncs)
if ret == 0 or ret == 1:
print("Scrubbing %s done, (err=%d)" % (mnt, ret))
@@ -175,8 +176,12 @@ def run_scrub(mnt, cond, running_devs, mntdevs, killfuncs):
if terminate:
return
- # Invoke xfs_scrub manually
+ # Invoke xfs_scrub manually if we're running in the foreground.
+ # We also permit this if we're running as a cronjob where
+ # systemd services are unavailable.
cmd = ['@sbindir@/xfs_scrub']
+ if 'SERVICE_MODE' in os.environ:
+ cmd += '@scrub_service_args@'.split()
cmd += '@scrub_args@'.split()
cmd += [mnt]
ret = run_killable(cmd, None, killfuncs)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/6] xfs_scrub_all: remove journalctl background process
2024-07-30 0:20 ` [PATCHSET v30.9 15/23] xfs_scrub_all: automatic media scan service Darrick J. Wong
2024-07-30 1:14 ` [PATCH 1/6] xfs_scrub_all: only use the xfs_scrub@ systemd services in service mode Darrick J. Wong
@ 2024-07-30 1:14 ` Darrick J. Wong
2024-07-30 1:15 ` [PATCH 3/6] xfs_scrub_all: support metadata+media scans of all filesystems Darrick J. Wong
` (3 subsequent siblings)
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:14 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Now that we only start systemd services if we're running in service
mode, there's no need for the background journalctl process that only
ran if we had started systemd services in non-service mode.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/xfs_scrub_all.in | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/scrub/xfs_scrub_all.in b/scrub/xfs_scrub_all.in
index f27251fa5..fc7a2e637 100644
--- a/scrub/xfs_scrub_all.in
+++ b/scrub/xfs_scrub_all.in
@@ -261,17 +261,6 @@ def main():
fs = find_mounts()
- # Tail the journal if we ourselves aren't a service...
- journalthread = None
- if 'SERVICE_MODE' not in os.environ:
- try:
- cmd=['journalctl', '--no-pager', '-q', '-S', 'now', \
- '-f', '-u', 'xfs_scrub@*', '-o', \
- 'cat']
- journalthread = subprocess.Popen(cmd)
- except:
- pass
-
# Schedule scrub jobs...
running_devs = set()
killfuncs = set()
@@ -308,9 +297,6 @@ def main():
while len(killfuncs) > 0:
wait_for_termination(cond, killfuncs)
- if journalthread is not None:
- journalthread.terminate()
-
# See the service mode comments in xfs_scrub.c for why we do this.
if 'SERVICE_MODE' in os.environ:
time.sleep(2)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/6] xfs_scrub_all: support metadata+media scans of all filesystems
2024-07-30 0:20 ` [PATCHSET v30.9 15/23] xfs_scrub_all: automatic media scan service Darrick J. Wong
2024-07-30 1:14 ` [PATCH 1/6] xfs_scrub_all: only use the xfs_scrub@ systemd services in service mode Darrick J. Wong
2024-07-30 1:14 ` [PATCH 2/6] xfs_scrub_all: remove journalctl background process Darrick J. Wong
@ 2024-07-30 1:15 ` Darrick J. Wong
2024-07-30 1:15 ` [PATCH 4/6] xfs_scrub_all: enable periodic file data scrubs automatically Darrick J. Wong
` (2 subsequent siblings)
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:15 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add the necessary systemd services and control bits so that
xfs_scrub_all can kick off a metadata+media scan of a filesystem.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man8/xfs_scrub_all.8 | 5 +-
scrub/Makefile | 4 +
scrub/xfs_scrub_all.in | 27 +++++++--
scrub/xfs_scrub_fail.in | 13 +++-
scrub/xfs_scrub_fail@.service.in | 2 -
scrub/xfs_scrub_media@.service.in | 100 ++++++++++++++++++++++++++++++++
scrub/xfs_scrub_media_fail@.service.in | 76 ++++++++++++++++++++++++
7 files changed, 214 insertions(+), 13 deletions(-)
create mode 100644 scrub/xfs_scrub_media@.service.in
create mode 100644 scrub/xfs_scrub_media_fail@.service.in
diff --git a/man/man8/xfs_scrub_all.8 b/man/man8/xfs_scrub_all.8
index 74548802e..86a9b3ece 100644
--- a/man/man8/xfs_scrub_all.8
+++ b/man/man8/xfs_scrub_all.8
@@ -4,7 +4,7 @@ xfs_scrub_all \- scrub all mounted XFS filesystems
.SH SYNOPSIS
.B xfs_scrub_all
[
-.B \-hV
+.B \-hxV
]
.SH DESCRIPTION
.B xfs_scrub_all
@@ -21,6 +21,9 @@ the same device simultaneously.
.B \-h
Display help.
.TP
+.B \-x
+Read all file data extents to look for disk errors.
+.TP
.B \-V
Prints the version number and exits.
.SH EXIT CODE
diff --git a/scrub/Makefile b/scrub/Makefile
index 2050fe28f..5567ec061 100644
--- a/scrub/Makefile
+++ b/scrub/Makefile
@@ -9,6 +9,7 @@ include $(builddefs)
SCRUB_PREREQS=$(HAVE_GETFSMAP)
scrub_svcname=xfs_scrub@.service
+scrub_media_svcname=xfs_scrub_media@.service
ifeq ($(SCRUB_PREREQS),yes)
LTCOMMAND = xfs_scrub
@@ -22,6 +23,8 @@ INSTALL_SCRUB += install-systemd
SYSTEMD_SERVICES=\
$(scrub_svcname) \
xfs_scrub_fail@.service \
+ $(scrub_media_svcname) \
+ xfs_scrub_media_fail@.service \
xfs_scrub_all.service \
xfs_scrub_all.timer \
system-xfs_scrub.slice
@@ -113,6 +116,7 @@ xfs_scrub_all: xfs_scrub_all.in $(builddefs)
@echo " [SED] $@"
$(Q)$(SED) -e "s|@sbindir@|$(PKG_SBIN_DIR)|g" \
-e "s|@scrub_svcname@|$(scrub_svcname)|g" \
+ -e "s|@scrub_media_svcname@|$(scrub_media_svcname)|g" \
-e "s|@pkg_version@|$(PKG_VERSION)|g" \
-e "s|@scrub_service_args@|$(XFS_SCRUB_SERVICE_ARGS)|g" \
-e "s|@scrub_args@|$(XFS_SCRUB_ARGS)|g" < $< > $@
diff --git a/scrub/xfs_scrub_all.in b/scrub/xfs_scrub_all.in
index fc7a2e637..c1fd10986 100644
--- a/scrub/xfs_scrub_all.in
+++ b/scrub/xfs_scrub_all.in
@@ -19,6 +19,7 @@ from io import TextIOWrapper
retcode = 0
terminate = False
+scrub_media = False
def DEVNULL():
'''Return /dev/null in subprocess writable format.'''
@@ -88,11 +89,15 @@ def run_killable(cmd, stdout, killfuncs):
# systemd doesn't like unit instance names with slashes in them, so it
# replaces them with dashes when it invokes the service. Filesystem paths
# need a special --path argument so that dashes do not get mangled.
-def path_to_serviceunit(path):
+def path_to_serviceunit(path, scrub_media):
'''Convert a pathname into a systemd service unit name.'''
- cmd = ['systemd-escape', '--template', '@scrub_svcname@',
- '--path', path]
+ if scrub_media:
+ svcname = '@scrub_media_svcname@'
+ else:
+ svcname = '@scrub_svcname@'
+ cmd = ['systemd-escape', '--template', svcname, '--path', path]
+
try:
proc = subprocess.Popen(cmd, stdout = subprocess.PIPE)
proc.wait()
@@ -153,7 +158,9 @@ def systemctl_start(unitname, killfuncs):
def run_scrub(mnt, cond, running_devs, mntdevs, killfuncs):
'''Run a scrub process.'''
- global retcode, terminate
+ global retcode
+ global terminate
+ global scrub_media
print("Scrubbing %s..." % mnt)
sys.stdout.flush()
@@ -164,7 +171,7 @@ def run_scrub(mnt, cond, running_devs, mntdevs, killfuncs):
# Run per-mount systemd xfs_scrub service only if we ourselves
# are running as a systemd service.
- unitname = path_to_serviceunit(path)
+ unitname = path_to_serviceunit(path, scrub_media)
if unitname is not None and 'SERVICE_MODE' in os.environ:
ret = systemctl_start(unitname, killfuncs)
if ret == 0 or ret == 1:
@@ -183,6 +190,8 @@ def run_scrub(mnt, cond, running_devs, mntdevs, killfuncs):
if 'SERVICE_MODE' in os.environ:
cmd += '@scrub_service_args@'.split()
cmd += '@scrub_args@'.split()
+ if scrub_media:
+ cmd += '-x'
cmd += [mnt]
ret = run_killable(cmd, None, killfuncs)
if ret >= 0:
@@ -247,18 +256,24 @@ def main():
a = (mnt, cond, running_devs, devs, killfuncs)
thr = threading.Thread(target = run_scrub, args = a)
thr.start()
- global retcode, terminate
+ global retcode
+ global terminate
+ global scrub_media
parser = argparse.ArgumentParser( \
description = "Scrub all mounted XFS filesystems.")
parser.add_argument("-V", help = "Report version and exit.", \
action = "store_true")
+ parser.add_argument("-x", help = "Scrub file data after filesystem metadata.", \
+ action = "store_true")
args = parser.parse_args()
if args.V:
print("xfs_scrub_all version @pkg_version@")
sys.exit(0)
+ scrub_media = args.x
+
fs = find_mounts()
# Schedule scrub jobs...
diff --git a/scrub/xfs_scrub_fail.in b/scrub/xfs_scrub_fail.in
index 9437b0327..e420917f6 100755
--- a/scrub/xfs_scrub_fail.in
+++ b/scrub/xfs_scrub_fail.in
@@ -9,8 +9,11 @@
recipient="$1"
test -z "${recipient}" && exit 0
-mntpoint="$2"
+service="$2"
+test -z "${service}" && exit 0
+mntpoint="$3"
test -z "${mntpoint}" && exit 0
+
hostname="$(hostname -f 2>/dev/null)"
test -z "${hostname}" && hostname="${HOSTNAME}"
@@ -21,16 +24,16 @@ if [ ! -x "${mailer}" ]; then
fi
# Turn the mountpoint into a properly escaped systemd instance name
-scrub_svc="$(systemd-escape --template "@scrub_svcname@" --path "${mntpoint}")"
+scrub_svc="$(systemd-escape --template "${service}@.service" --path "${mntpoint}")"
(cat << ENDL
To: $1
-From: <xfs_scrub@${hostname}>
-Subject: xfs_scrub failure on ${mntpoint}
+From: <${service}@${hostname}>
+Subject: ${service} failure on ${mntpoint}
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset=UTF-8
-So sorry, the automatic xfs_scrub of ${mntpoint} on ${hostname} failed.
+So sorry, the automatic ${service} of ${mntpoint} on ${hostname} failed.
Please do not reply to this mesage.
A log of what happened follows:
diff --git a/scrub/xfs_scrub_fail@.service.in b/scrub/xfs_scrub_fail@.service.in
index 2c879afd6..16077888d 100644
--- a/scrub/xfs_scrub_fail@.service.in
+++ b/scrub/xfs_scrub_fail@.service.in
@@ -10,7 +10,7 @@ Documentation=man:xfs_scrub(8)
[Service]
Type=oneshot
Environment=EMAIL_ADDR=root
-ExecStart=@pkg_libexec_dir@/xfs_scrub_fail "${EMAIL_ADDR}" %f
+ExecStart=@pkg_libexec_dir@/xfs_scrub_fail "${EMAIL_ADDR}" xfs_scrub %f
User=mail
Group=mail
SupplementaryGroups=systemd-journal
diff --git a/scrub/xfs_scrub_media@.service.in b/scrub/xfs_scrub_media@.service.in
new file mode 100644
index 000000000..e670748ce
--- /dev/null
+++ b/scrub/xfs_scrub_media@.service.in
@@ -0,0 +1,100 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2018-2024 Oracle. All Rights Reserved.
+# Author: Darrick J. Wong <djwong@kernel.org>
+
+[Unit]
+Description=Online XFS Metadata and Media Check for %f
+OnFailure=xfs_scrub_media_fail@%i.service
+Documentation=man:xfs_scrub(8)
+
+# Explicitly require the capabilities that this program needs
+ConditionCapability=CAP_SYS_ADMIN
+ConditionCapability=CAP_FOWNER
+ConditionCapability=CAP_DAC_OVERRIDE
+ConditionCapability=CAP_DAC_READ_SEARCH
+ConditionCapability=CAP_SYS_RAWIO
+
+# Must be a mountpoint
+ConditionPathIsMountPoint=%f
+RequiresMountsFor=%f
+
+[Service]
+Type=oneshot
+Environment=SERVICE_MODE=1
+ExecStart=@sbindir@/xfs_scrub @scrub_service_args@ @scrub_args@ -M /tmp/scrub/ -x %f
+SyslogIdentifier=%N
+
+# Run scrub with minimal CPU and IO priority so that nothing else will starve.
+IOSchedulingClass=idle
+CPUSchedulingPolicy=idle
+CPUAccounting=true
+Nice=19
+
+# Create the service underneath the scrub background service slice so that we
+# can control resource usage.
+Slice=system-xfs_scrub.slice
+
+# No realtime CPU scheduling
+RestrictRealtime=true
+
+# Dynamically create a user that isn't root
+DynamicUser=true
+
+# Make the entire filesystem readonly and /home inaccessible, then bind mount
+# the filesystem we're supposed to be checking into our private /tmp dir.
+# 'norbind' means that we don't bind anything under that original mount.
+ProtectSystem=strict
+ProtectHome=yes
+PrivateTmp=true
+BindPaths=%f:/tmp/scrub:norbind
+
+# Don't let scrub complain about paths in /etc/projects that have been hidden
+# by our sandboxing. scrub doesn't care about project ids anyway.
+InaccessiblePaths=-/etc/projects
+
+# No network access
+PrivateNetwork=true
+ProtectHostname=true
+RestrictAddressFamilies=none
+IPAddressDeny=any
+
+# Don't let the program mess with the kernel configuration at all
+ProtectKernelLogs=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectControlGroups=true
+ProtectProc=invisible
+RestrictNamespaces=true
+
+# Hide everything in /proc, even /proc/mounts
+ProcSubset=pid
+
+# Only allow the default personality Linux
+LockPersonality=true
+
+# No writable memory pages
+MemoryDenyWriteExecute=true
+
+# Don't let our mounts leak out to the host
+PrivateMounts=true
+
+# Restrict system calls to the native arch and only enough to get things going
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+SystemCallFilter=~@privileged
+SystemCallFilter=~@resources
+SystemCallFilter=~@mount
+
+# xfs_scrub needs these privileges to run, and no others
+CapabilityBoundingSet=CAP_SYS_ADMIN CAP_FOWNER CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_SYS_RAWIO
+AmbientCapabilities=CAP_SYS_ADMIN CAP_FOWNER CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_SYS_RAWIO
+NoNewPrivileges=true
+
+# xfs_scrub doesn't create files
+UMask=7777
+
+# No access to hardware /dev files except for block devices
+ProtectClock=true
+DevicePolicy=closed
+DeviceAllow=block-*
diff --git a/scrub/xfs_scrub_media_fail@.service.in b/scrub/xfs_scrub_media_fail@.service.in
new file mode 100644
index 000000000..97c0e0907
--- /dev/null
+++ b/scrub/xfs_scrub_media_fail@.service.in
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2018-2024 Oracle. All Rights Reserved.
+# Author: Darrick J. Wong <djwong@kernel.org>
+
+[Unit]
+Description=Online XFS Metadata and Media Check Failure Reporting for %f
+Documentation=man:xfs_scrub(8)
+
+[Service]
+Type=oneshot
+Environment=EMAIL_ADDR=root
+ExecStart=@pkg_libexec_dir@/xfs_scrub_fail "${EMAIL_ADDR}" xfs_scrub_media %f
+User=mail
+Group=mail
+SupplementaryGroups=systemd-journal
+
+# Create the service underneath the scrub background service slice so that we
+# can control resource usage.
+Slice=system-xfs_scrub.slice
+
+# No realtime scheduling
+RestrictRealtime=true
+
+# Make the entire filesystem readonly and /home inaccessible, then bind mount
+# the filesystem we're supposed to be checking into our private /tmp dir.
+ProtectSystem=full
+ProtectHome=yes
+PrivateTmp=true
+RestrictSUIDSGID=true
+
+# Emailing reports requires network access, but not the ability to change the
+# hostname.
+ProtectHostname=true
+
+# Don't let the program mess with the kernel configuration at all
+ProtectKernelLogs=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectControlGroups=true
+ProtectProc=invisible
+RestrictNamespaces=true
+
+# Can't hide /proc because journalctl needs it to find various pieces of log
+# information
+#ProcSubset=pid
+
+# Only allow the default personality Linux
+LockPersonality=true
+
+# No writable memory pages
+MemoryDenyWriteExecute=true
+
+# Don't let our mounts leak out to the host
+PrivateMounts=true
+
+# Restrict system calls to the native arch and only enough to get things going
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+SystemCallFilter=~@privileged
+SystemCallFilter=~@resources
+SystemCallFilter=~@mount
+
+# xfs_scrub needs these privileges to run, and no others
+CapabilityBoundingSet=
+NoNewPrivileges=true
+
+# Failure reporting shouldn't create world-readable files
+UMask=0077
+
+# Clean up any IPC objects when this unit stops
+RemoveIPC=true
+
+# No access to hardware device files
+PrivateDevices=true
+ProtectClock=true
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/6] xfs_scrub_all: enable periodic file data scrubs automatically
2024-07-30 0:20 ` [PATCHSET v30.9 15/23] xfs_scrub_all: automatic media scan service Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:15 ` [PATCH 3/6] xfs_scrub_all: support metadata+media scans of all filesystems Darrick J. Wong
@ 2024-07-30 1:15 ` Darrick J. Wong
2024-07-30 1:15 ` [PATCH 5/6] xfs_scrub_all: trigger automatic media scans once per month Darrick J. Wong
2024-07-30 1:15 ` [PATCH 6/6] xfs_scrub_all: failure reporting for the xfs_scrub_all job Darrick J. Wong
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:15 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Enhance xfs_scrub_all with the ability to initiate a file data scrub
periodically. The user must specify the period, and they may optionally
specify the path to a file that will record the last time the file data
was scrubbed.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
debian/rules | 3 +-
include/builddefs.in | 3 ++
man/man8/Makefile | 7 +++-
man/man8/xfs_scrub_all.8.in | 15 ++++++++
scrub/Makefile | 3 ++
scrub/xfs_scrub_all.in | 76 +++++++++++++++++++++++++++++++++++++++-
scrub/xfs_scrub_all.service.in | 6 ++-
7 files changed, 108 insertions(+), 5 deletions(-)
rename man/man8/{xfs_scrub_all.8 => xfs_scrub_all.8.in} (63%)
diff --git a/debian/rules b/debian/rules
index 0db0ed8e7..69a79fc67 100755
--- a/debian/rules
+++ b/debian/rules
@@ -38,7 +38,8 @@ configure_options = \
--disable-ubsan \
--disable-addrsan \
--disable-threadsan \
- --enable-lto
+ --enable-lto \
+ --localstatedir=/var
options = export DEBUG=-DNDEBUG DISTRIBUTION=debian \
INSTALL_USER=root INSTALL_GROUP=root \
diff --git a/include/builddefs.in b/include/builddefs.in
index 6ac36c149..734bd95ec 100644
--- a/include/builddefs.in
+++ b/include/builddefs.in
@@ -57,6 +57,9 @@ PKG_DOC_DIR = @datadir@/doc/@pkg_name@
PKG_LOCALE_DIR = @datadir@/locale
PKG_DATA_DIR = @datadir@/@pkg_name@
MKFS_CFG_DIR = @datadir@/@pkg_name@/mkfs
+PKG_STATE_DIR = @localstatedir@/lib/@pkg_name@
+
+XFS_SCRUB_ALL_AUTO_MEDIA_SCAN_STAMP=$(PKG_STATE_DIR)/xfs_scrub_all_media.stamp
CC = @cc@
BUILD_CC = @BUILD_CC@
diff --git a/man/man8/Makefile b/man/man8/Makefile
index 272e45aeb..5be76ab72 100644
--- a/man/man8/Makefile
+++ b/man/man8/Makefile
@@ -11,11 +11,12 @@ ifneq ("$(ENABLE_SCRUB)","yes")
MAN_PAGES = $(filter-out xfs_scrub%,$(shell echo *.$(MAN_SECTION)))
else
MAN_PAGES = $(shell echo *.$(MAN_SECTION))
+ MAN_PAGES += xfs_scrub_all.8
endif
MAN_PAGES += mkfs.xfs.8
MAN_DEST = $(PKG_MAN_DIR)/man$(MAN_SECTION)
LSRCFILES = $(MAN_PAGES)
-DIRT = mkfs.xfs.8
+DIRT = mkfs.xfs.8 xfs_scrub_all.8
default : $(MAN_PAGES)
@@ -29,4 +30,8 @@ mkfs.xfs.8: mkfs.xfs.8.in
@echo " [SED] $@"
$(Q)$(SED) -e 's|@mkfs_cfg_dir@|$(MKFS_CFG_DIR)|g' < $^ > $@
+xfs_scrub_all.8: xfs_scrub_all.8.in
+ @echo " [SED] $@"
+ $(Q)$(SED) -e 's|@stampfile@|$(XFS_SCRUB_ALL_AUTO_MEDIA_SCAN_STAMP)|g' < $^ > $@
+
install-dev :
diff --git a/man/man8/xfs_scrub_all.8 b/man/man8/xfs_scrub_all.8.in
similarity index 63%
rename from man/man8/xfs_scrub_all.8
rename to man/man8/xfs_scrub_all.8.in
index 86a9b3ece..0aa87e237 100644
--- a/man/man8/xfs_scrub_all.8
+++ b/man/man8/xfs_scrub_all.8.in
@@ -18,6 +18,21 @@ operations can be run in parallel so long as no two scrubbers access
the same device simultaneously.
.SH OPTIONS
.TP
+.B \--auto-media-scan-interval
+Automatically enable the file data scan (i.e. the
+.B -x
+flag) if it has not been run in the specified interval.
+The interval must be a floating point number with an optional unit suffix.
+Supported unit suffixes are
+.IR y ", " q ", " mo ", " w ", " d ", " h ", " m ", and " s
+for years, 90-day quarters, 30-day months, weeks, days, hours, minutes, and
+seconds, respectively.
+If no units are specified, the default is seconds.
+.TP
+.B \--auto-media-scan-stamp
+Path to a file that will record the last time the media scan was run.
+Defaults to @stampfile@.
+.TP
.B \-h
Display help.
.TP
diff --git a/scrub/Makefile b/scrub/Makefile
index 5567ec061..091a5c94b 100644
--- a/scrub/Makefile
+++ b/scrub/Makefile
@@ -118,6 +118,7 @@ xfs_scrub_all: xfs_scrub_all.in $(builddefs)
-e "s|@scrub_svcname@|$(scrub_svcname)|g" \
-e "s|@scrub_media_svcname@|$(scrub_media_svcname)|g" \
-e "s|@pkg_version@|$(PKG_VERSION)|g" \
+ -e "s|@stampfile@|$(XFS_SCRUB_ALL_AUTO_MEDIA_SCAN_STAMP)|g" \
-e "s|@scrub_service_args@|$(XFS_SCRUB_SERVICE_ARGS)|g" \
-e "s|@scrub_args@|$(XFS_SCRUB_ARGS)|g" < $< > $@
$(Q)chmod a+x $@
@@ -141,6 +142,7 @@ install: $(INSTALL_SCRUB)
-e "s|@scrub_service_args@|$(XFS_SCRUB_SERVICE_ARGS)|g" \
-e "s|@scrub_args@|$(XFS_SCRUB_ARGS)|g" \
-e "s|@pkg_libexec_dir@|$(PKG_LIBEXEC_DIR)|g" \
+ -e "s|@pkg_state_dir@|$(PKG_STATE_DIR)|g" \
< $< > $@
%.cron: %.cron.in $(builddefs)
@@ -161,6 +163,7 @@ install-scrub: default
$(INSTALL) -m 755 -d $(PKG_SBIN_DIR)
$(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_SBIN_DIR)
$(INSTALL) -m 755 $(XFS_SCRUB_ALL_PROG) $(PKG_SBIN_DIR)
+ $(INSTALL) -m 755 -d $(PKG_STATE_DIR)
install-udev: $(UDEV_RULES)
$(INSTALL) -m 755 -d $(UDEV_RULE_DIR)
diff --git a/scrub/xfs_scrub_all.in b/scrub/xfs_scrub_all.in
index c1fd10986..9dd6347fb 100644
--- a/scrub/xfs_scrub_all.in
+++ b/scrub/xfs_scrub_all.in
@@ -16,6 +16,10 @@ import os
import argparse
import signal
from io import TextIOWrapper
+from pathlib import Path
+from datetime import timedelta
+from datetime import datetime
+from datetime import timezone
retcode = 0
terminate = False
@@ -250,6 +254,65 @@ def wait_for_termination(cond, killfuncs):
fn()
return True
+def scan_interval(string):
+ '''Convert a textual scan interval argument into a time delta.'''
+
+ if string.endswith('y'):
+ year = timedelta(seconds = 31556952)
+ return year * float(string[:-1])
+ if string.endswith('q'):
+ return timedelta(days = 90 * float(string[:-1]))
+ if string.endswith('mo'):
+ return timedelta(days = 30 * float(string[:-2]))
+ if string.endswith('w'):
+ return timedelta(weeks = float(string[:-1]))
+ if string.endswith('d'):
+ return timedelta(days = float(string[:-1]))
+ if string.endswith('h'):
+ return timedelta(hours = float(string[:-1]))
+ if string.endswith('m'):
+ return timedelta(minutes = float(string[:-1]))
+ if string.endswith('s'):
+ return timedelta(seconds = float(string[:-1]))
+ return timedelta(seconds = int(string))
+
+def utcnow():
+ '''Create a representation of the time right now, in UTC.'''
+
+ dt = datetime.utcnow()
+ return dt.replace(tzinfo = timezone.utc)
+
+def enable_automatic_media_scan(args):
+ '''Decide if we enable media scanning automatically.'''
+ already_enabled = args.x
+
+ try:
+ interval = scan_interval(args.auto_media_scan_interval)
+ except Exception as e:
+ raise Exception('%s: Invalid media scan interval.' % \
+ args.auto_media_scan_interval)
+
+ p = Path(args.auto_media_scan_stamp)
+ if already_enabled:
+ res = True
+ else:
+ try:
+ last_run = p.stat().st_mtime
+ now = utcnow().timestamp()
+ res = last_run + interval.total_seconds() < now
+ except FileNotFoundError:
+ res = True
+
+ if res:
+ # Truncate the stamp file to update its mtime
+ with p.open('w') as f:
+ pass
+ if not already_enabled:
+ print('Automatically enabling file data scrub.')
+ sys.stdout.flush()
+
+ return res
+
def main():
'''Find mounts, schedule scrub runs.'''
def thr(mnt, devs):
@@ -266,13 +329,24 @@ def main():
action = "store_true")
parser.add_argument("-x", help = "Scrub file data after filesystem metadata.", \
action = "store_true")
+ parser.add_argument("--auto-media-scan-interval", help = "Automatically scrub file data at this interval.", \
+ default = None)
+ parser.add_argument("--auto-media-scan-stamp", help = "Stamp file for automatic file data scrub.", \
+ default = '@stampfile@')
args = parser.parse_args()
if args.V:
print("xfs_scrub_all version @pkg_version@")
sys.exit(0)
- scrub_media = args.x
+ if args.auto_media_scan_interval is not None:
+ try:
+ scrub_media = enable_automatic_media_scan(args)
+ except Exception as e:
+ print(e)
+ sys.exit(16)
+ else:
+ scrub_media = args.x
fs = find_mounts()
diff --git a/scrub/xfs_scrub_all.service.in b/scrub/xfs_scrub_all.service.in
index 478cd8d05..9ecc3af0c 100644
--- a/scrub/xfs_scrub_all.service.in
+++ b/scrub/xfs_scrub_all.service.in
@@ -34,11 +34,13 @@ CapabilityBoundingSet=
NoNewPrivileges=true
RestrictSUIDSGID=true
-# Make the entire filesystem readonly. We don't want to hide anything because
-# we need to find all mounted XFS filesystems in the host.
+# Make the entire filesystem readonly except for the media scan stamp file
+# directory. We don't want to hide anything because we need to find all
+# mounted XFS filesystems in the host.
ProtectSystem=strict
ProtectHome=read-only
PrivateTmp=false
+BindPaths=@pkg_state_dir@
# No network access except to the systemd control socket
PrivateNetwork=true
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/6] xfs_scrub_all: trigger automatic media scans once per month
2024-07-30 0:20 ` [PATCHSET v30.9 15/23] xfs_scrub_all: automatic media scan service Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:15 ` [PATCH 4/6] xfs_scrub_all: enable periodic file data scrubs automatically Darrick J. Wong
@ 2024-07-30 1:15 ` Darrick J. Wong
2024-07-30 1:15 ` [PATCH 6/6] xfs_scrub_all: failure reporting for the xfs_scrub_all job Darrick J. Wong
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:15 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Teach the xfs_scrub_all background service to trigger an automatic scan
of all file data once per month.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/Makefile | 8 +++++++-
scrub/xfs_scrub_all.cron.in | 2 +-
scrub/xfs_scrub_all.service.in | 2 +-
3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/scrub/Makefile b/scrub/Makefile
index 091a5c94b..0e09ed127 100644
--- a/scrub/Makefile
+++ b/scrub/Makefile
@@ -108,6 +108,9 @@ CFILES += unicrash.c
LCFLAGS += -DHAVE_LIBICU $(LIBICU_CFLAGS)
endif
+# Automatically trigger a media scan once per month
+XFS_SCRUB_ALL_AUTO_MEDIA_SCAN_INTERVAL=1mo
+
LDIRT = $(XFS_SCRUB_ALL_PROG) $(XFS_SCRUB_FAIL_PROG) *.service *.cron
default: depend $(LTCOMMAND) $(XFS_SCRUB_ALL_PROG) $(XFS_SCRUB_FAIL_PROG) $(OPTIONAL_TARGETS)
@@ -143,11 +146,14 @@ install: $(INSTALL_SCRUB)
-e "s|@scrub_args@|$(XFS_SCRUB_ARGS)|g" \
-e "s|@pkg_libexec_dir@|$(PKG_LIBEXEC_DIR)|g" \
-e "s|@pkg_state_dir@|$(PKG_STATE_DIR)|g" \
+ -e "s|@media_scan_interval@|$(XFS_SCRUB_ALL_AUTO_MEDIA_SCAN_INTERVAL)|g" \
< $< > $@
%.cron: %.cron.in $(builddefs)
@echo " [SED] $@"
- $(Q)$(SED) -e "s|@sbindir@|$(PKG_SBIN_DIR)|g" < $< > $@
+ $(Q)$(SED) -e "s|@sbindir@|$(PKG_SBIN_DIR)|g" \
+ -e "s|@media_scan_interval@|$(XFS_SCRUB_ALL_AUTO_MEDIA_SCAN_INTERVAL)|g" \
+ < $< > $@
install-systemd: default $(SYSTEMD_SERVICES)
$(INSTALL) -m 755 -d $(SYSTEMD_SYSTEM_UNIT_DIR)
diff --git a/scrub/xfs_scrub_all.cron.in b/scrub/xfs_scrub_all.cron.in
index e08a9daf9..e6633e091 100644
--- a/scrub/xfs_scrub_all.cron.in
+++ b/scrub/xfs_scrub_all.cron.in
@@ -3,4 +3,4 @@
# Copyright (C) 2018-2024 Oracle. All Rights Reserved.
# Author: Darrick J. Wong <djwong@kernel.org>
#
-10 3 * * 0 root test -e /run/systemd/system || @sbindir@/xfs_scrub_all
+10 3 * * 0 root test -e /run/systemd/system || @sbindir@/xfs_scrub_all --auto-media-scan-interval @media_scan_interval@
diff --git a/scrub/xfs_scrub_all.service.in b/scrub/xfs_scrub_all.service.in
index 9ecc3af0c..8ed682989 100644
--- a/scrub/xfs_scrub_all.service.in
+++ b/scrub/xfs_scrub_all.service.in
@@ -12,7 +12,7 @@ After=paths.target multi-user.target network.target network-online.target system
[Service]
Type=oneshot
Environment=SERVICE_MODE=1
-ExecStart=@sbindir@/xfs_scrub_all
+ExecStart=@sbindir@/xfs_scrub_all --auto-media-scan-interval @media_scan_interval@
SyslogIdentifier=xfs_scrub_all
# Create the service underneath the scrub background service slice so that we
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 6/6] xfs_scrub_all: failure reporting for the xfs_scrub_all job
2024-07-30 0:20 ` [PATCHSET v30.9 15/23] xfs_scrub_all: automatic media scan service Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 1:15 ` [PATCH 5/6] xfs_scrub_all: trigger automatic media scans once per month Darrick J. Wong
@ 2024-07-30 1:15 ` Darrick J. Wong
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:15 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create a failure reporting service for when xfs_scrub_all fails. This
shouldn't happen often, but let's report anyways.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/Makefile | 1
scrub/xfs_scrub_all.service.in | 1
scrub/xfs_scrub_all_fail.service.in | 71 +++++++++++++++++++++++++++++++++++
scrub/xfs_scrub_fail.in | 35 ++++++++++++++---
4 files changed, 101 insertions(+), 7 deletions(-)
create mode 100644 scrub/xfs_scrub_all_fail.service.in
diff --git a/scrub/Makefile b/scrub/Makefile
index 0e09ed127..7e6882450 100644
--- a/scrub/Makefile
+++ b/scrub/Makefile
@@ -26,6 +26,7 @@ SYSTEMD_SERVICES=\
$(scrub_media_svcname) \
xfs_scrub_media_fail@.service \
xfs_scrub_all.service \
+ xfs_scrub_all_fail.service \
xfs_scrub_all.timer \
system-xfs_scrub.slice
OPTIONAL_TARGETS += $(SYSTEMD_SERVICES)
diff --git a/scrub/xfs_scrub_all.service.in b/scrub/xfs_scrub_all.service.in
index 8ed682989..b86b787d2 100644
--- a/scrub/xfs_scrub_all.service.in
+++ b/scrub/xfs_scrub_all.service.in
@@ -5,6 +5,7 @@
[Unit]
Description=Online XFS Metadata Check for All Filesystems
+OnFailure=xfs_scrub_all_fail.service
ConditionACPower=true
Documentation=man:xfs_scrub_all(8)
After=paths.target multi-user.target network.target network-online.target systemd-networkd.service NetworkManager.service connman.service
diff --git a/scrub/xfs_scrub_all_fail.service.in b/scrub/xfs_scrub_all_fail.service.in
new file mode 100644
index 000000000..53479db84
--- /dev/null
+++ b/scrub/xfs_scrub_all_fail.service.in
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2018-2024 Oracle. All Rights Reserved.
+# Author: Darrick J. Wong <djwong@kernel.org>
+
+[Unit]
+Description=Online XFS Metadata Check for All Filesystems Failure Reporting
+Documentation=man:xfs_scrub_all(8)
+
+[Service]
+Type=oneshot
+Environment=EMAIL_ADDR=root
+ExecStart=@pkg_libexec_dir@/xfs_scrub_fail "${EMAIL_ADDR}" xfs_scrub_all
+User=mail
+Group=mail
+SupplementaryGroups=systemd-journal
+
+# No realtime scheduling
+RestrictRealtime=true
+
+# Make the entire filesystem readonly and /home inaccessible.
+ProtectSystem=full
+ProtectHome=yes
+PrivateTmp=true
+RestrictSUIDSGID=true
+
+# Emailing reports requires network access, but not the ability to change the
+# hostname.
+ProtectHostname=true
+
+# Don't let the program mess with the kernel configuration at all
+ProtectKernelLogs=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectControlGroups=true
+ProtectProc=invisible
+RestrictNamespaces=true
+
+# Can't hide /proc because journalctl needs it to find various pieces of log
+# information
+#ProcSubset=pid
+
+# Only allow the default personality Linux
+LockPersonality=true
+
+# No writable memory pages
+MemoryDenyWriteExecute=true
+
+# Don't let our mounts leak out to the host
+PrivateMounts=true
+
+# Restrict system calls to the native arch and only enough to get things going
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+SystemCallFilter=~@privileged
+SystemCallFilter=~@resources
+SystemCallFilter=~@mount
+
+# xfs_scrub needs these privileges to run, and no others
+CapabilityBoundingSet=
+NoNewPrivileges=true
+
+# Failure reporting shouldn't create world-readable files
+UMask=0077
+
+# Clean up any IPC objects when this unit stops
+RemoveIPC=true
+
+# No access to hardware device files
+PrivateDevices=true
+ProtectClock=true
diff --git a/scrub/xfs_scrub_fail.in b/scrub/xfs_scrub_fail.in
index e420917f6..089b438f0 100755
--- a/scrub/xfs_scrub_fail.in
+++ b/scrub/xfs_scrub_fail.in
@@ -5,14 +5,13 @@
# Copyright (C) 2018-2024 Oracle. All Rights Reserved.
# Author: Darrick J. Wong <djwong@kernel.org>
-# Email logs of failed xfs_scrub unit runs
+# Email logs of failed xfs_scrub and xfs_scrub_all unit runs
recipient="$1"
test -z "${recipient}" && exit 0
service="$2"
test -z "${service}" && exit 0
mntpoint="$3"
-test -z "${mntpoint}" && exit 0
hostname="$(hostname -f 2>/dev/null)"
test -z "${hostname}" && hostname="${HOSTNAME}"
@@ -23,11 +22,13 @@ if [ ! -x "${mailer}" ]; then
exit 1
fi
-# Turn the mountpoint into a properly escaped systemd instance name
-scrub_svc="$(systemd-escape --template "${service}@.service" --path "${mntpoint}")"
+fail_mail_mntpoint() {
+ local scrub_svc
-(cat << ENDL
-To: $1
+ # Turn the mountpoint into a properly escaped systemd instance name
+ scrub_svc="$(systemd-escape --template "${service}@.service" --path "${mntpoint}")"
+ cat << ENDL
+To: ${recipient}
From: <${service}@${hostname}>
Subject: ${service} failure on ${mntpoint}
Content-Transfer-Encoding: 8bit
@@ -38,5 +39,25 @@ Please do not reply to this mesage.
A log of what happened follows:
ENDL
-systemctl status --full --lines 4294967295 "${scrub_svc}") | "${mailer}" -t -i
+ systemctl status --full --lines 4294967295 "${scrub_svc}"
+}
+
+fail_mail() {
+ cat << ENDL
+To: ${recipient}
+From: <${service}@${hostname}>
+Subject: ${service} failure
+
+So sorry, the automatic ${service} on ${hostname} failed.
+
+A log of what happened follows:
+ENDL
+ systemctl status --full --lines 4294967295 "${service}"
+}
+
+if [ -n "${mntpoint}" ]; then
+ fail_mail_mntpoint | "${mailer}" -t -i
+else
+ fail_mail | "${mailer}" -t -i
+fi
exit "${PIPESTATUS[1]}"
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/5] xfs_scrub_all: encapsulate all the subprocess code in an object
2024-07-30 0:20 ` [PATCHSET v30.9 16/23] xfs_scrub_all: improve systemd handling Darrick J. Wong
@ 2024-07-30 1:16 ` Darrick J. Wong
2024-07-30 1:16 ` [PATCH 2/5] xfs_scrub_all: encapsulate all the systemctl " Darrick J. Wong
` (3 subsequent siblings)
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:16 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Move all the xfs_scrub subprocess handling code to an object so that we
can contain all the details in a single place. This also simplifies the
background state management.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/xfs_scrub_all.in | 68 ++++++++++++++++++++++++++++++++++++++----------
1 file changed, 54 insertions(+), 14 deletions(-)
diff --git a/scrub/xfs_scrub_all.in b/scrub/xfs_scrub_all.in
index 9dd6347fb..25286f57c 100644
--- a/scrub/xfs_scrub_all.in
+++ b/scrub/xfs_scrub_all.in
@@ -78,15 +78,62 @@ def remove_killfunc(killfuncs, fn):
except:
pass
-def run_killable(cmd, stdout, killfuncs):
+class scrub_control(object):
+ '''Control object for xfs_scrub.'''
+ def __init__(self):
+ pass
+
+ def start(self):
+ '''Start scrub and wait for it to complete. Returns -1 if the
+ service was not started, 0 if it succeeded, or 1 if it
+ failed.'''
+ assert False
+
+ def stop(self):
+ '''Stop scrub.'''
+ assert False
+
+class scrub_subprocess(scrub_control):
+ '''Control object for xfs_scrub subprocesses.'''
+ def __init__(self, mnt, scrub_media):
+ cmd = ['@sbindir@/xfs_scrub']
+ if 'SERVICE_MODE' in os.environ:
+ cmd += '@scrub_service_args@'.split()
+ cmd += '@scrub_args@'.split()
+ if scrub_media:
+ cmd += '-x'
+ cmd += [mnt]
+ self.cmdline = cmd
+ self.proc = None
+
+ def start(self):
+ '''Start xfs_scrub and wait for it to complete. Returns -1 if
+ the service was not started, 0 if it succeeded, or 1 if it
+ failed.'''
+ try:
+ self.proc = subprocess.Popen(self.cmdline)
+ self.proc.wait()
+ except:
+ return -1
+
+ proc = self.proc
+ self.proc = None
+ return proc.returncode
+
+ def stop(self):
+ '''Stop xfs_scrub.'''
+ if self.proc is not None:
+ self.proc.terminate()
+
+def run_subprocess(mnt, scrub_media, killfuncs):
'''Run a killable program. Returns program retcode or -1 if we can't
start it.'''
try:
- proc = subprocess.Popen(cmd, stdout = stdout)
- killfuncs.add(proc.terminate)
- proc.wait()
- remove_killfunc(killfuncs, proc.terminate)
- return proc.returncode
+ p = scrub_subprocess(mnt, scrub_media)
+ killfuncs.add(p.stop)
+ ret = p.start()
+ remove_killfunc(killfuncs, p.stop)
+ return ret
except:
return -1
@@ -190,14 +237,7 @@ def run_scrub(mnt, cond, running_devs, mntdevs, killfuncs):
# Invoke xfs_scrub manually if we're running in the foreground.
# We also permit this if we're running as a cronjob where
# systemd services are unavailable.
- cmd = ['@sbindir@/xfs_scrub']
- if 'SERVICE_MODE' in os.environ:
- cmd += '@scrub_service_args@'.split()
- cmd += '@scrub_args@'.split()
- if scrub_media:
- cmd += '-x'
- cmd += [mnt]
- ret = run_killable(cmd, None, killfuncs)
+ ret = run_subprocess(mnt, scrub_media, killfuncs)
if ret >= 0:
print("Scrubbing %s done, (err=%d)" % (mnt, ret))
sys.stdout.flush()
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/5] xfs_scrub_all: encapsulate all the systemctl code in an object
2024-07-30 0:20 ` [PATCHSET v30.9 16/23] xfs_scrub_all: improve systemd handling Darrick J. Wong
2024-07-30 1:16 ` [PATCH 1/5] xfs_scrub_all: encapsulate all the subprocess code in an object Darrick J. Wong
@ 2024-07-30 1:16 ` Darrick J. Wong
2024-07-30 1:16 ` [PATCH 3/5] xfs_scrub_all: add CLI option for easier debugging Darrick J. Wong
` (2 subsequent siblings)
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:16 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Move all the systemd service handling code to an object so that we can
contain all the insanity^Wdetails in a single place. This also makes
the killfuncs handling similar to starting background processes.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/xfs_scrub_all.in | 113 ++++++++++++++++++++++++++----------------------
1 file changed, 61 insertions(+), 52 deletions(-)
diff --git a/scrub/xfs_scrub_all.in b/scrub/xfs_scrub_all.in
index 25286f57c..4130a98e9 100644
--- a/scrub/xfs_scrub_all.in
+++ b/scrub/xfs_scrub_all.in
@@ -149,63 +149,73 @@ def path_to_serviceunit(path, scrub_media):
svcname = '@scrub_svcname@'
cmd = ['systemd-escape', '--template', svcname, '--path', path]
- try:
- proc = subprocess.Popen(cmd, stdout = subprocess.PIPE)
- proc.wait()
- for line in proc.stdout:
- return line.decode(sys.stdout.encoding).strip()
- except:
- return None
+ proc = subprocess.Popen(cmd, stdout = subprocess.PIPE)
+ proc.wait()
+ for line in proc.stdout:
+ return line.decode(sys.stdout.encoding).strip()
-def systemctl_stop(unitname):
- '''Stop a systemd unit.'''
- cmd = ['systemctl', 'stop', unitname]
- x = subprocess.Popen(cmd)
- x.wait()
+class scrub_service(scrub_control):
+ '''Control object for xfs_scrub systemd service.'''
+ def __init__(self, mnt, scrub_media):
+ self.unitname = path_to_serviceunit(mnt, scrub_media)
-def systemctl_start(unitname, killfuncs):
- '''Start a systemd unit and wait for it to complete.'''
- stop_fn = None
- cmd = ['systemctl', 'start', unitname]
- try:
- proc = subprocess.Popen(cmd, stdout = DEVNULL())
- stop_fn = lambda: systemctl_stop(unitname)
- killfuncs.add(stop_fn)
- proc.wait()
- ret = proc.returncode
- except:
- if stop_fn is not None:
- remove_killfunc(killfuncs, stop_fn)
- return -1
+ def wait(self, interval = 1):
+ '''Wait until the service finishes.'''
- if ret != 1:
- remove_killfunc(killfuncs, stop_fn)
- return ret
+ # As of systemd 249, the is-active command returns any of the
+ # following states: active, reloading, inactive, failed,
+ # activating, deactivating, or maintenance. Apparently these
+ # strings are not localized.
+ while True:
+ try:
+ for l in backtick(['systemctl', 'is-active', self.unitname]):
+ if l == 'failed':
+ return 1
+ if l == 'inactive':
+ return 0
+ except:
+ return -1
- # If systemctl-start returns 1, it's possible that the service failed
- # or that dbus/systemd restarted and the client program lost its
- # connection -- according to the systemctl man page, 1 means "unit not
- # failed".
- #
- # Either way, we switch to polling the service status to try to wait
- # for the service to end. As of systemd 249, the is-active command
- # returns any of the following states: active, reloading, inactive,
- # failed, activating, deactivating, or maintenance. Apparently these
- # strings are not localized.
- while True:
+ time.sleep(interval)
+
+ def start(self):
+ '''Start the service and wait for it to complete. Returns -1
+ if the service was not started, 0 if it succeeded, or 1 if it
+ failed.'''
+ cmd = ['systemctl', 'start', self.unitname]
try:
- for l in backtick(['systemctl', 'is-active', unitname]):
- if l == 'failed':
- remove_killfunc(killfuncs, stop_fn)
- return 1
- if l == 'inactive':
- remove_killfunc(killfuncs, stop_fn)
- return 0
+ proc = subprocess.Popen(cmd, stdout = DEVNULL())
+ proc.wait()
+ ret = proc.returncode
except:
- remove_killfunc(killfuncs, stop_fn)
return -1
- time.sleep(1)
+ if ret != 1:
+ return ret
+
+ # If systemctl-start returns 1, it's possible that the service
+ # failed or that dbus/systemd restarted and the client program
+ # lost its connection -- according to the systemctl man page, 1
+ # means "unit not failed".
+ return self.wait()
+
+ def stop(self):
+ '''Stop the service.'''
+ cmd = ['systemctl', 'stop', self.unitname]
+ x = subprocess.Popen(cmd)
+ x.wait()
+
+def run_service(mnt, scrub_media, killfuncs):
+ '''Run scrub as a service.'''
+ try:
+ svc = scrub_service(mnt, scrub_media)
+ except:
+ return -1
+
+ killfuncs.add(svc.stop)
+ retcode = svc.start()
+ remove_killfunc(killfuncs, svc.stop)
+ return retcode
def run_scrub(mnt, cond, running_devs, mntdevs, killfuncs):
'''Run a scrub process.'''
@@ -222,9 +232,8 @@ def run_scrub(mnt, cond, running_devs, mntdevs, killfuncs):
# Run per-mount systemd xfs_scrub service only if we ourselves
# are running as a systemd service.
- unitname = path_to_serviceunit(path, scrub_media)
- if unitname is not None and 'SERVICE_MODE' in os.environ:
- ret = systemctl_start(unitname, killfuncs)
+ if 'SERVICE_MODE' in os.environ:
+ ret = run_service(mnt, scrub_media, killfuncs)
if ret == 0 or ret == 1:
print("Scrubbing %s done, (err=%d)" % (mnt, ret))
sys.stdout.flush()
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/5] xfs_scrub_all: add CLI option for easier debugging
2024-07-30 0:20 ` [PATCHSET v30.9 16/23] xfs_scrub_all: improve systemd handling Darrick J. Wong
2024-07-30 1:16 ` [PATCH 1/5] xfs_scrub_all: encapsulate all the subprocess code in an object Darrick J. Wong
2024-07-30 1:16 ` [PATCH 2/5] xfs_scrub_all: encapsulate all the systemctl " Darrick J. Wong
@ 2024-07-30 1:16 ` Darrick J. Wong
2024-07-30 1:17 ` [PATCH 4/5] xfs_scrub_all: convert systemctl calls to dbus Darrick J. Wong
2024-07-30 1:17 ` [PATCH 5/5] xfs_scrub_all: implement retry and backoff for dbus calls Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:16 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add a new CLI argument to make it easier to figure out what exactly the
program is doing.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/xfs_scrub_all.in | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/scrub/xfs_scrub_all.in b/scrub/xfs_scrub_all.in
index 4130a98e9..8954b4740 100644
--- a/scrub/xfs_scrub_all.in
+++ b/scrub/xfs_scrub_all.in
@@ -24,6 +24,7 @@ from datetime import timezone
retcode = 0
terminate = False
scrub_media = False
+debug = False
def DEVNULL():
'''Return /dev/null in subprocess writable format.'''
@@ -110,6 +111,11 @@ class scrub_subprocess(scrub_control):
'''Start xfs_scrub and wait for it to complete. Returns -1 if
the service was not started, 0 if it succeeded, or 1 if it
failed.'''
+ global debug
+
+ if debug:
+ print('run ', ' '.join(self.cmdline))
+
try:
self.proc = subprocess.Popen(self.cmdline)
self.proc.wait()
@@ -122,6 +128,10 @@ class scrub_subprocess(scrub_control):
def stop(self):
'''Stop xfs_scrub.'''
+ global debug
+
+ if debug:
+ print('kill ', ' '.join(self.cmdline))
if self.proc is not None:
self.proc.terminate()
@@ -182,8 +192,12 @@ class scrub_service(scrub_control):
'''Start the service and wait for it to complete. Returns -1
if the service was not started, 0 if it succeeded, or 1 if it
failed.'''
+ global debug
+
cmd = ['systemctl', 'start', self.unitname]
try:
+ if debug:
+ print(' '.join(cmd))
proc = subprocess.Popen(cmd, stdout = DEVNULL())
proc.wait()
ret = proc.returncode
@@ -201,7 +215,11 @@ class scrub_service(scrub_control):
def stop(self):
'''Stop the service.'''
+ global debug
+
cmd = ['systemctl', 'stop', self.unitname]
+ if debug:
+ print(' '.join(cmd))
x = subprocess.Popen(cmd)
x.wait()
@@ -266,7 +284,8 @@ def run_scrub(mnt, cond, running_devs, mntdevs, killfuncs):
def signal_scrubs(signum, cond):
'''Handle termination signals by killing xfs_scrub children.'''
- global debug, terminate
+ global debug
+ global terminate
if debug:
print('Signal handler called with signal', signum)
@@ -280,7 +299,8 @@ def signal_scrubs(signum, cond):
def wait_for_termination(cond, killfuncs):
'''Wait for a child thread to terminate. Returns True if we should
abort the program, False otherwise.'''
- global debug, terminate
+ global debug
+ global terminate
if debug:
print('waiting for threads to terminate')
@@ -371,9 +391,12 @@ def main():
global retcode
global terminate
global scrub_media
+ global debug
parser = argparse.ArgumentParser( \
description = "Scrub all mounted XFS filesystems.")
+ parser.add_argument("--debug", help = "Enabling debugging messages.", \
+ action = "store_true")
parser.add_argument("-V", help = "Report version and exit.", \
action = "store_true")
parser.add_argument("-x", help = "Scrub file data after filesystem metadata.", \
@@ -388,6 +411,9 @@ def main():
print("xfs_scrub_all version @pkg_version@")
sys.exit(0)
+ if args.debug:
+ debug = True
+
if args.auto_media_scan_interval is not None:
try:
scrub_media = enable_automatic_media_scan(args)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/5] xfs_scrub_all: convert systemctl calls to dbus
2024-07-30 0:20 ` [PATCHSET v30.9 16/23] xfs_scrub_all: improve systemd handling Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:16 ` [PATCH 3/5] xfs_scrub_all: add CLI option for easier debugging Darrick J. Wong
@ 2024-07-30 1:17 ` Darrick J. Wong
2024-07-30 1:17 ` [PATCH 5/5] xfs_scrub_all: implement retry and backoff for dbus calls Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:17 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Convert the systemctl invocations to direct dbus calls, which decouples
us from the CLI in favor of direct API calls. This spares us from some
of the insanity of divining service state from program outputs.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
debian/control | 2 +
scrub/xfs_scrub_all.in | 96 +++++++++++++++++++++++++++++++-----------------
2 files changed, 63 insertions(+), 35 deletions(-)
diff --git a/debian/control b/debian/control
index 344466de0..31773e53a 100644
--- a/debian/control
+++ b/debian/control
@@ -8,7 +8,7 @@ Standards-Version: 4.0.0
Homepage: https://xfs.wiki.kernel.org/
Package: xfsprogs
-Depends: ${shlibs:Depends}, ${misc:Depends}, python3:any
+Depends: ${shlibs:Depends}, ${misc:Depends}, python3-dbus, python3:any
Provides: fsck-backend
Suggests: xfsdump, acl, attr, quota
Breaks: xfsdump (<< 3.0.0)
diff --git a/scrub/xfs_scrub_all.in b/scrub/xfs_scrub_all.in
index 8954b4740..f2e916513 100644
--- a/scrub/xfs_scrub_all.in
+++ b/scrub/xfs_scrub_all.in
@@ -15,6 +15,7 @@ import sys
import os
import argparse
import signal
+import dbus
from io import TextIOWrapper
from pathlib import Path
from datetime import timedelta
@@ -168,25 +169,57 @@ class scrub_service(scrub_control):
'''Control object for xfs_scrub systemd service.'''
def __init__(self, mnt, scrub_media):
self.unitname = path_to_serviceunit(mnt, scrub_media)
+ self.prop = None
+ self.unit = None
+ self.bind()
+
+ def bind(self):
+ '''Bind to the dbus proxy object for this service.'''
+ sysbus = dbus.SystemBus()
+ systemd1 = sysbus.get_object('org.freedesktop.systemd1',
+ '/org/freedesktop/systemd1')
+ manager = dbus.Interface(systemd1,
+ 'org.freedesktop.systemd1.Manager')
+ path = manager.LoadUnit(self.unitname)
+
+ svc_obj = sysbus.get_object('org.freedesktop.systemd1', path)
+ self.prop = dbus.Interface(svc_obj,
+ 'org.freedesktop.DBus.Properties')
+ self.unit = dbus.Interface(svc_obj,
+ 'org.freedesktop.systemd1.Unit')
+
+ def state(self):
+ '''Retrieve the active state for a systemd service. As of
+ systemd 249, this is supposed to be one of the following:
+ "active", "reloading", "inactive", "failed", "activating",
+ or "deactivating". These strings are not localized.'''
+ global debug
+
+ try:
+ return self.prop.Get('org.freedesktop.systemd1.Unit', 'ActiveState')
+ except Exception as e:
+ if debug:
+ print(e, file = sys.stderr)
+ return 'failed'
def wait(self, interval = 1):
'''Wait until the service finishes.'''
+ global debug
- # As of systemd 249, the is-active command returns any of the
- # following states: active, reloading, inactive, failed,
- # activating, deactivating, or maintenance. Apparently these
- # strings are not localized.
- while True:
- try:
- for l in backtick(['systemctl', 'is-active', self.unitname]):
- if l == 'failed':
- return 1
- if l == 'inactive':
- return 0
- except:
- return -1
-
+ # Use a poll/sleep loop to wait for the service to finish.
+ # Avoid adding a dependency on python3 glib, which is required
+ # to use an event loop to receive a dbus signal.
+ s = self.state()
+ while s not in ['failed', 'inactive']:
+ if debug:
+ print('waiting %s %s' % (self.unitname, s))
time.sleep(interval)
+ s = self.state()
+ if debug:
+ print('waited %s %s' % (self.unitname, s))
+ if s == 'failed':
+ return 1
+ return 0
def start(self):
'''Start the service and wait for it to complete. Returns -1
@@ -194,34 +227,29 @@ class scrub_service(scrub_control):
failed.'''
global debug
- cmd = ['systemctl', 'start', self.unitname]
+ if debug:
+ print('starting %s' % self.unitname)
+
try:
- if debug:
- print(' '.join(cmd))
- proc = subprocess.Popen(cmd, stdout = DEVNULL())
- proc.wait()
- ret = proc.returncode
- except:
+ self.unit.Start('replace')
+ return self.wait()
+ except Exception as e:
+ print(e, file = sys.stderr)
return -1
- if ret != 1:
- return ret
-
- # If systemctl-start returns 1, it's possible that the service
- # failed or that dbus/systemd restarted and the client program
- # lost its connection -- according to the systemctl man page, 1
- # means "unit not failed".
- return self.wait()
-
def stop(self):
'''Stop the service.'''
global debug
- cmd = ['systemctl', 'stop', self.unitname]
if debug:
- print(' '.join(cmd))
- x = subprocess.Popen(cmd)
- x.wait()
+ print('stopping %s' % self.unitname)
+
+ try:
+ self.unit.Stop('replace')
+ return self.wait()
+ except Exception as e:
+ print(e, file = sys.stderr)
+ return -1
def run_service(mnt, scrub_media, killfuncs):
'''Run scrub as a service.'''
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/5] xfs_scrub_all: implement retry and backoff for dbus calls
2024-07-30 0:20 ` [PATCHSET v30.9 16/23] xfs_scrub_all: improve systemd handling Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:17 ` [PATCH 4/5] xfs_scrub_all: convert systemctl calls to dbus Darrick J. Wong
@ 2024-07-30 1:17 ` Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:17 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Calls to systemd across dbus are remote procedure calls, which means
that they're subject to transitory connection failures (e.g. systemd
re-exec itself). We don't want to fail at the *first* sign of what
could be temporary trouble, so implement a limited retry with fibonacci
backoff before we resort to invoking xfs_scrub as a subprocess.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/xfs_scrub_all.in | 43 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 40 insertions(+), 3 deletions(-)
diff --git a/scrub/xfs_scrub_all.in b/scrub/xfs_scrub_all.in
index f2e916513..5440e51c0 100644
--- a/scrub/xfs_scrub_all.in
+++ b/scrub/xfs_scrub_all.in
@@ -165,6 +165,22 @@ def path_to_serviceunit(path, scrub_media):
for line in proc.stdout:
return line.decode(sys.stdout.encoding).strip()
+def fibonacci(max_ret):
+ '''Yield fibonacci sequence up to but not including max_ret.'''
+ if max_ret < 1:
+ return
+
+ x = 0
+ y = 1
+ yield 1
+
+ z = x + y
+ while z <= max_ret:
+ yield z
+ x = y
+ y = z
+ z = x + y
+
class scrub_service(scrub_control):
'''Control object for xfs_scrub systemd service.'''
def __init__(self, mnt, scrub_media):
@@ -188,6 +204,25 @@ class scrub_service(scrub_control):
self.unit = dbus.Interface(svc_obj,
'org.freedesktop.systemd1.Unit')
+ def __dbusrun(self, lambda_fn):
+ '''Call the lambda function to execute something on dbus. dbus
+ exceptions result in retries with Fibonacci backoff, and the
+ bindings will be rebuilt every time.'''
+ global debug
+
+ fatal_ex = None
+
+ for i in fibonacci(30):
+ try:
+ return lambda_fn()
+ except dbus.exceptions.DBusException as e:
+ if debug:
+ print(e)
+ fatal_ex = e
+ time.sleep(i)
+ self.bind()
+ raise fatal_ex
+
def state(self):
'''Retrieve the active state for a systemd service. As of
systemd 249, this is supposed to be one of the following:
@@ -195,8 +230,10 @@ class scrub_service(scrub_control):
or "deactivating". These strings are not localized.'''
global debug
+ l = lambda: self.prop.Get('org.freedesktop.systemd1.Unit',
+ 'ActiveState')
try:
- return self.prop.Get('org.freedesktop.systemd1.Unit', 'ActiveState')
+ return self.__dbusrun(l)
except Exception as e:
if debug:
print(e, file = sys.stderr)
@@ -231,7 +268,7 @@ class scrub_service(scrub_control):
print('starting %s' % self.unitname)
try:
- self.unit.Start('replace')
+ self.__dbusrun(lambda: self.unit.Start('replace'))
return self.wait()
except Exception as e:
print(e, file = sys.stderr)
@@ -245,7 +282,7 @@ class scrub_service(scrub_control):
print('stopping %s' % self.unitname)
try:
- self.unit.Stop('replace')
+ self.__dbusrun(lambda: self.unit.Stop('replace'))
return self.wait()
except Exception as e:
print(e, file = sys.stderr)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/6] xfs_scrub_all: fail fast on masked units
2024-07-30 0:20 ` [PATCHSET v13.8 17/23] xfsprogs: improve extended attribute validation Darrick J. Wong
@ 2024-07-30 1:17 ` Darrick J. Wong
2024-07-30 1:17 ` [PATCH 2/6] xfs_scrub: automatic downgrades to dry-run mode in service mode Darrick J. Wong
` (4 subsequent siblings)
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:17 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If xfs_scrub_all tries to start a masked xfs_scrub@ unit, that's a sign
that the system administrator really didn't want us to scrub that
filesystem. Instead of retrying pointlessly, just make a note of the
failure and move on.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/xfs_scrub_all.in | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/scrub/xfs_scrub_all.in b/scrub/xfs_scrub_all.in
index 5440e51c0..5e2e0446a 100644
--- a/scrub/xfs_scrub_all.in
+++ b/scrub/xfs_scrub_all.in
@@ -181,6 +181,10 @@ def fibonacci(max_ret):
y = z
z = x + y
+def was_unit_masked(ex):
+ '''Decide if this dbus exception occurred because we tried to start a masked unit.'''
+ return ex.get_dbus_name() == "org.freedesktop.systemd1.UnitMasked"
+
class scrub_service(scrub_control):
'''Control object for xfs_scrub systemd service.'''
def __init__(self, mnt, scrub_media):
@@ -219,6 +223,12 @@ class scrub_service(scrub_control):
if debug:
print(e)
fatal_ex = e
+
+ # If the unit is masked, there's no point in
+ # retrying any operations on it.
+ if was_unit_masked(e):
+ break
+
time.sleep(i)
self.bind()
raise fatal_ex
@@ -270,6 +280,13 @@ class scrub_service(scrub_control):
try:
self.__dbusrun(lambda: self.unit.Start('replace'))
return self.wait()
+ except dbus.exceptions.DBusException as e:
+ # If the unit was masked, the sysadmin doesn't want us
+ # running it. Pretend that we finished it.
+ if was_unit_masked(e):
+ return 32
+ print(e, file = sys.stderr)
+ return -1
except Exception as e:
print(e, file = sys.stderr)
return -1
@@ -317,6 +334,10 @@ def run_scrub(mnt, cond, running_devs, mntdevs, killfuncs):
# are running as a systemd service.
if 'SERVICE_MODE' in os.environ:
ret = run_service(mnt, scrub_media, killfuncs)
+ if ret == 32:
+ print("Scrubbing %s disabled by administrator, (err=%d)" % (mnt, ret))
+ sys.stdout.flush()
+ return
if ret == 0 or ret == 1:
print("Scrubbing %s done, (err=%d)" % (mnt, ret))
sys.stdout.flush()
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/6] xfs_scrub: automatic downgrades to dry-run mode in service mode
2024-07-30 0:20 ` [PATCHSET v13.8 17/23] xfsprogs: improve extended attribute validation Darrick J. Wong
2024-07-30 1:17 ` [PATCH 1/6] xfs_scrub_all: fail fast on masked units Darrick J. Wong
@ 2024-07-30 1:17 ` Darrick J. Wong
2024-07-30 1:18 ` [PATCH 3/6] xfs_scrub: add an optimization-only mode Darrick J. Wong
` (3 subsequent siblings)
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:17 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
When service mode is enabled, xfs_scrub is being run within the context
of a systemd service. The service description language doesn't have any
particularly good constructs for adding in a '-n' argument if the
filesystem is readonly, which means that xfs_scrub is passed a path, and
needs to switch to dry-run mode on its own if the fs is mounted
readonly or the kernel doesn't support repairs.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase1.c | 13 +++++++++++++
scrub/repair.c | 33 +++++++++++++++++++++++++++++++++
scrub/repair.h | 2 ++
3 files changed, 48 insertions(+)
diff --git a/scrub/phase1.c b/scrub/phase1.c
index 516d929d6..095c04591 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -216,6 +216,19 @@ _("Kernel metadata scrubbing facility is not available."));
return ECANCELED;
}
+ /*
+ * Normally, callers are required to pass -n if the provided path is a
+ * readonly filesystem or the kernel wasn't built with online repair
+ * enabled. However, systemd services are not scripts and cannot
+ * determine either of these conditions programmatically. Change the
+ * behavior to dry-run mode if either condition is detected.
+ */
+ if (repair_want_service_downgrade(ctx)) {
+ str_info(ctx, ctx->mntpoint,
+_("Filesystem cannot be repaired in service mode, downgrading to dry-run mode."));
+ ctx->mode = SCRUB_MODE_DRY_RUN;
+ }
+
/* Do we need kernel-assisted metadata repair? */
if (ctx->mode != SCRUB_MODE_DRY_RUN && !can_repair(ctx)) {
str_error(ctx, ctx->mntpoint,
diff --git a/scrub/repair.c b/scrub/repair.c
index 19f5c9052..2883f98af 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -45,6 +45,39 @@ static const unsigned int repair_deps[XFS_SCRUB_TYPE_NR] = {
};
#undef DEP
+/*
+ * Decide if we want an automatic downgrade to dry-run mode. This is only
+ * for service mode, where we are fed a path and have to figure out if the fs
+ * is repairable or not.
+ */
+bool
+repair_want_service_downgrade(
+ struct scrub_ctx *ctx)
+{
+ struct xfs_scrub_metadata meta = {
+ .sm_type = XFS_SCRUB_TYPE_PROBE,
+ .sm_flags = XFS_SCRUB_IFLAG_REPAIR,
+ };
+ int error;
+
+ if (ctx->mode == SCRUB_MODE_DRY_RUN)
+ return false;
+ if (!is_service)
+ return false;
+ if (debug_tweak_on("XFS_SCRUB_NO_KERNEL"))
+ return false;
+
+ error = -xfrog_scrub_metadata(&ctx->mnt, &meta);
+ switch (error) {
+ case EROFS:
+ case ENOTRECOVERABLE:
+ case EOPNOTSUPP:
+ return true;
+ }
+
+ return false;
+}
+
/* Repair some metadata. */
static int
xfs_repair_metadata(
diff --git a/scrub/repair.h b/scrub/repair.h
index a685e9037..411a379f6 100644
--- a/scrub/repair.h
+++ b/scrub/repair.h
@@ -102,4 +102,6 @@ repair_item_completely(
return repair_item(ctx, sri, XRM_FINAL_WARNING | XRM_NOPROGRESS);
}
+bool repair_want_service_downgrade(struct scrub_ctx *ctx);
+
#endif /* XFS_SCRUB_REPAIR_H_ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/6] xfs_scrub: add an optimization-only mode
2024-07-30 0:20 ` [PATCHSET v13.8 17/23] xfsprogs: improve extended attribute validation Darrick J. Wong
2024-07-30 1:17 ` [PATCH 1/6] xfs_scrub_all: fail fast on masked units Darrick J. Wong
2024-07-30 1:17 ` [PATCH 2/6] xfs_scrub: automatic downgrades to dry-run mode in service mode Darrick J. Wong
@ 2024-07-30 1:18 ` Darrick J. Wong
2024-07-30 1:18 ` [PATCH 4/6] xfs_repair: check free space requirements before allowing upgrades Darrick J. Wong
` (2 subsequent siblings)
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:18 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add a "preen" mode in which we only optimize filesystem metadata.
Repairs will result in early exits.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man8/xfs_scrub.8 | 6 +++++-
scrub/Makefile | 2 +-
scrub/phase4.c | 6 ++++++
scrub/repair.c | 4 +++-
scrub/scrub.c | 4 ++--
scrub/xfs_scrub.c | 21 +++++++++++++++++++--
scrub/xfs_scrub.h | 1 +
7 files changed, 37 insertions(+), 7 deletions(-)
diff --git a/man/man8/xfs_scrub.8 b/man/man8/xfs_scrub.8
index 615401127..1fd122f2a 100644
--- a/man/man8/xfs_scrub.8
+++ b/man/man8/xfs_scrub.8
@@ -4,7 +4,7 @@ xfs_scrub \- check and repair the contents of a mounted XFS filesystem
.SH SYNOPSIS
.B xfs_scrub
[
-.B \-abCeMmnTvx
+.B \-abCeMmnpTvx
]
.I mount-point
.br
@@ -128,6 +128,10 @@ Treat informational messages as warnings.
This will result in a nonzero return code, and a higher logging level.
.RE
.TP
+.B \-p
+Only optimize filesystem metadata.
+If repairs are required, report them and exit.
+.TP
.BI \-T
Print timing and memory usage information for each phase.
.TP
diff --git a/scrub/Makefile b/scrub/Makefile
index 7e6882450..885b43e99 100644
--- a/scrub/Makefile
+++ b/scrub/Makefile
@@ -16,7 +16,7 @@ LTCOMMAND = xfs_scrub
INSTALL_SCRUB = install-scrub
XFS_SCRUB_ALL_PROG = xfs_scrub_all
XFS_SCRUB_FAIL_PROG = xfs_scrub_fail
-XFS_SCRUB_ARGS = -n
+XFS_SCRUB_ARGS = -p
XFS_SCRUB_SERVICE_ARGS = -b
ifeq ($(HAVE_SYSTEMD),yes)
INSTALL_SCRUB += install-systemd
diff --git a/scrub/phase4.c b/scrub/phase4.c
index 451101811..88cb53aea 100644
--- a/scrub/phase4.c
+++ b/scrub/phase4.c
@@ -240,6 +240,12 @@ phase4_func(
action_list_empty(ctx->file_repair_list))
return 0;
+ if (ctx->mode == SCRUB_MODE_PREEN && ctx->corruptions_found) {
+ str_info(ctx, ctx->mntpoint,
+ _("Corruptions found; will not optimize. Re-run without -p.\n"));
+ return 0;
+ }
+
/*
* Check the resource usage counters early. Normally we do this during
* phase 7, but some of the cross-referencing requires fairly accurate
diff --git a/scrub/repair.c b/scrub/repair.c
index 2883f98af..025821072 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -651,7 +651,9 @@ repair_item_class(
unsigned int scrub_type;
int error = 0;
- if (ctx->mode < SCRUB_MODE_REPAIR)
+ if (ctx->mode == SCRUB_MODE_DRY_RUN)
+ return 0;
+ if (ctx->mode == SCRUB_MODE_PREEN && !(repair_mask & SCRUB_ITEM_PREEN))
return 0;
/*
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 2b6b6274e..1b0609e74 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -174,7 +174,7 @@ _("Filesystem is shut down, aborting."));
* repair if desired, otherwise complain.
*/
if (is_corrupt(&meta) || xref_disagrees(&meta)) {
- if (ctx->mode < SCRUB_MODE_REPAIR) {
+ if (ctx->mode != SCRUB_MODE_REPAIR) {
/* Dry-run mode, so log an error and forget it. */
str_corrupt(ctx, descr_render(&dsc),
_("Repairs are required."));
@@ -192,7 +192,7 @@ _("Repairs are required."));
* otherwise complain.
*/
if (is_unoptimized(&meta)) {
- if (ctx->mode != SCRUB_MODE_REPAIR) {
+ if (ctx->mode == SCRUB_MODE_DRY_RUN) {
/* Dry-run mode, so log an error and forget it. */
if (group != XFROG_SCRUB_GROUP_INODE) {
/* AG or FS metadata, always warn. */
diff --git a/scrub/xfs_scrub.c b/scrub/xfs_scrub.c
index d7cef115d..bb316f73e 100644
--- a/scrub/xfs_scrub.c
+++ b/scrub/xfs_scrub.c
@@ -183,6 +183,7 @@ usage(void)
fprintf(stderr, _(" -k Do not FITRIM the free space.\n"));
fprintf(stderr, _(" -m path Path to /etc/mtab.\n"));
fprintf(stderr, _(" -n Dry run. Do not modify anything.\n"));
+ fprintf(stderr, _(" -p Only optimize, do not fix corruptions.\n"));
fprintf(stderr, _(" -T Display timing/usage information.\n"));
fprintf(stderr, _(" -v Verbose output.\n"));
fprintf(stderr, _(" -V Print version.\n"));
@@ -463,6 +464,11 @@ run_scrub_phases(
sp->descr = _("Repair filesystem.");
sp->fn = phase4_func;
sp->must_run = true;
+ } else if (sp->fn == REPAIR_DUMMY_FN &&
+ ctx->mode == SCRUB_MODE_PREEN) {
+ sp->descr = _("Optimize filesystem.");
+ sp->fn = phase4_func;
+ sp->must_run = true;
}
/* Skip certain phases unless they're turned on. */
@@ -601,7 +607,7 @@ report_outcome(
if (ctx->scrub_setup_succeeded && actionable_errors > 0) {
char *msg;
- if (ctx->mode == SCRUB_MODE_DRY_RUN)
+ if (ctx->mode != SCRUB_MODE_REPAIR)
msg = _("%s: Re-run xfs_scrub without -n.\n");
else
msg = _("%s: Unmount and run xfs_repair.\n");
@@ -725,7 +731,7 @@ main(
pthread_mutex_init(&ctx.lock, NULL);
ctx.mode = SCRUB_MODE_REPAIR;
ctx.error_action = ERRORS_CONTINUE;
- while ((c = getopt(argc, argv, "a:bC:de:kM:m:no:TvxV")) != EOF) {
+ while ((c = getopt(argc, argv, "a:bC:de:kM:m:no:pTvxV")) != EOF) {
switch (c) {
case 'a':
ctx.max_errors = cvt_u64(optarg, 10);
@@ -776,11 +782,22 @@ main(
mtab = optarg;
break;
case 'n':
+ if (ctx.mode != SCRUB_MODE_REPAIR) {
+ fprintf(stderr, _("Cannot use -n with -p.\n"));
+ usage();
+ }
ctx.mode = SCRUB_MODE_DRY_RUN;
break;
case 'o':
parse_o_opts(&ctx, optarg);
break;
+ case 'p':
+ if (ctx.mode != SCRUB_MODE_REPAIR) {
+ fprintf(stderr, _("Cannot use -p with -n.\n"));
+ usage();
+ }
+ ctx.mode = SCRUB_MODE_PREEN;
+ break;
case 'T':
display_rusage = true;
break;
diff --git a/scrub/xfs_scrub.h b/scrub/xfs_scrub.h
index b0aa9fcc6..4d9a02892 100644
--- a/scrub/xfs_scrub.h
+++ b/scrub/xfs_scrub.h
@@ -27,6 +27,7 @@ extern bool info_is_warning;
enum scrub_mode {
SCRUB_MODE_DRY_RUN,
+ SCRUB_MODE_PREEN,
SCRUB_MODE_REPAIR,
};
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/6] xfs_repair: check free space requirements before allowing upgrades
2024-07-30 0:20 ` [PATCHSET v13.8 17/23] xfsprogs: improve extended attribute validation Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:18 ` [PATCH 3/6] xfs_scrub: add an optimization-only mode Darrick J. Wong
@ 2024-07-30 1:18 ` Darrick J. Wong
2024-07-30 1:18 ` [PATCH 5/6] xfs_repair: enforce one namespace bit per extended attribute Darrick J. Wong
2024-07-30 1:18 ` [PATCH 6/6] xfs_repair: check for unknown flags in attr entries Darrick J. Wong
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:18 UTC (permalink / raw)
To: djwong, cem
Cc: Chandan Babu R, Dave Chinner, Christoph Hellwig, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Currently, the V5 feature upgrades permitted by xfs_repair do not affect
filesystem space usage, so we haven't needed to verify the geometry.
However, this will change once we start to allow the sysadmin to add new
metadata indexes to existing filesystems. Add all the infrastructure we
need to ensure that there's enough space for metadata space reservations
and per-AG reservations the next time the filesystem will be mounted.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandan.babu@oracle.com>
[david: Recompute transaction reservation values; Exit with error if upgrade fails]
Signed-off-by: Dave Chinner <david@fromorbit.com>
[djwong: Refuse to upgrade if any part of the fs has < 10% free]
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
include/libxfs.h | 1
repair/phase2.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 135 insertions(+)
diff --git a/include/libxfs.h b/include/libxfs.h
index e760a46d8..bb00b71b1 100644
--- a/include/libxfs.h
+++ b/include/libxfs.h
@@ -91,6 +91,7 @@ struct iomap;
#include "libxfs/buf_mem.h"
#include "xfs_btree_mem.h"
#include "xfs_parent.h"
+#include "xfs_ag_resv.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
diff --git a/repair/phase2.c b/repair/phase2.c
index 83f0c539b..3418da523 100644
--- a/repair/phase2.c
+++ b/repair/phase2.c
@@ -249,6 +249,137 @@ install_new_state(
libxfs_trans_init(mp);
}
+#define GIGABYTES(count, blog) ((uint64_t)(count) << (30 - (blog)))
+static inline bool
+check_free_space(
+ struct xfs_mount *mp,
+ unsigned long long avail,
+ unsigned long long total)
+{
+ /* Ok if there's more than 10% free. */
+ if (avail >= total / 10)
+ return true;
+
+ /* Not ok if there's less than 5% free. */
+ if (avail < total / 5)
+ return false;
+
+ /* Let it slide if there's at least 10GB free. */
+ return avail > GIGABYTES(10, mp->m_sb.sb_blocklog);
+}
+
+static void
+check_fs_free_space(
+ struct xfs_mount *mp,
+ const struct check_state *old,
+ struct xfs_sb *new_sb)
+{
+ struct xfs_perag *pag;
+ xfs_agnumber_t agno;
+ int error;
+
+ /* Make sure we have enough space for per-AG reservations. */
+ for_each_perag(mp, agno, pag) {
+ struct xfs_trans *tp;
+ struct xfs_agf *agf;
+ struct xfs_buf *agi_bp, *agf_bp;
+ unsigned int avail, agblocks;
+
+ /* Put back the old super so that we can read AG headers. */
+ restore_old_state(mp, old);
+
+ /*
+ * Create a dummy transaction so that we can load the AGI and
+ * AGF buffers in memory with the old fs geometry and pin them
+ * there while we try to make a per-AG reservation with the new
+ * geometry.
+ */
+ error = -libxfs_trans_alloc_empty(mp, &tp);
+ if (error)
+ do_error(
+ _("Cannot reserve resources for upgrade check, err=%d.\n"),
+ error);
+
+ error = -libxfs_ialloc_read_agi(pag, tp, 0, &agi_bp);
+ if (error)
+ do_error(
+ _("Cannot read AGI %u for upgrade check, err=%d.\n"),
+ pag->pag_agno, error);
+
+ error = -libxfs_alloc_read_agf(pag, tp, 0, &agf_bp);
+ if (error)
+ do_error(
+ _("Cannot read AGF %u for upgrade check, err=%d.\n"),
+ pag->pag_agno, error);
+ agf = agf_bp->b_addr;
+ agblocks = be32_to_cpu(agf->agf_length);
+
+ /*
+ * Install the new superblock and try to make a per-AG space
+ * reservation with the new geometry. We pinned the AG header
+ * buffers to the transaction, so we shouldn't hit any
+ * corruption errors on account of the new geometry.
+ */
+ install_new_state(mp, new_sb);
+
+ error = -libxfs_ag_resv_init(pag, tp);
+ if (error == ENOSPC) {
+ printf(
+ _("Not enough free space would remain in AG %u for metadata.\n"),
+ pag->pag_agno);
+ exit(1);
+ }
+ if (error)
+ do_error(
+ _("Error %d while checking AG %u space reservation.\n"),
+ error, pag->pag_agno);
+
+ /*
+ * Would the post-upgrade filesystem have enough free space in
+ * this AG after making per-AG reservations?
+ */
+ avail = pag->pagf_freeblks + pag->pagf_flcount;
+ avail -= pag->pag_meta_resv.ar_reserved;
+ avail -= pag->pag_rmapbt_resv.ar_asked;
+
+ if (!check_free_space(mp, avail, agblocks)) {
+ printf(
+ _("AG %u will be low on space after upgrade.\n"),
+ pag->pag_agno);
+ exit(1);
+ }
+ libxfs_trans_cancel(tp);
+ }
+
+ /*
+ * Would the post-upgrade filesystem have enough free space on the data
+ * device after making per-AG reservations?
+ */
+ if (!check_free_space(mp, mp->m_sb.sb_fdblocks, mp->m_sb.sb_dblocks)) {
+ printf(_("Filesystem will be low on space after upgrade.\n"));
+ exit(1);
+ }
+
+ /*
+ * Release the per-AG reservations and mark the per-AG structure as
+ * uninitialized so that we don't trip over stale cached counters
+ * after the upgrade/
+ */
+ for_each_perag(mp, agno, pag) {
+ libxfs_ag_resv_free(pag);
+ clear_bit(XFS_AGSTATE_AGF_INIT, &pag->pag_opstate);
+ clear_bit(XFS_AGSTATE_AGI_INIT, &pag->pag_opstate);
+ }
+}
+
+static bool
+need_check_fs_free_space(
+ struct xfs_mount *mp,
+ const struct check_state *old)
+{
+ return false;
+}
+
/*
* Make sure we can actually upgrade this (v5) filesystem without running afoul
* of root inode or log size requirements that would prevent us from mounting
@@ -291,6 +422,9 @@ install_new_geometry(
exit(1);
}
+ if (need_check_fs_free_space(mp, &old))
+ check_fs_free_space(mp, &old, new_sb);
+
/*
* Restore the old state to get everything back to a clean state,
* upgrade the featureset one more time, and recompute the btree max
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/6] xfs_repair: enforce one namespace bit per extended attribute
2024-07-30 0:20 ` [PATCHSET v13.8 17/23] xfsprogs: improve extended attribute validation Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:18 ` [PATCH 4/6] xfs_repair: check free space requirements before allowing upgrades Darrick J. Wong
@ 2024-07-30 1:18 ` Darrick J. Wong
2024-07-30 1:18 ` [PATCH 6/6] xfs_repair: check for unknown flags in attr entries Darrick J. Wong
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:18 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Enforce that all extended attributes have at most one namespace bit.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/libxfs_api_defs.h | 1 +
repair/attr_repair.c | 15 +++++++++++++++
2 files changed, 16 insertions(+)
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index cc670d93a..2d858580a 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -36,6 +36,7 @@
#define xfs_ascii_ci_hashname libxfs_ascii_ci_hashname
+#define xfs_attr_check_namespace libxfs_attr_check_namespace
#define xfs_attr_get libxfs_attr_get
#define xfs_attr_leaf_newentsize libxfs_attr_leaf_newentsize
#define xfs_attr_namecheck libxfs_attr_namecheck
diff --git a/repair/attr_repair.c b/repair/attr_repair.c
index 0f2f7a284..a756a40db 100644
--- a/repair/attr_repair.c
+++ b/repair/attr_repair.c
@@ -291,6 +291,13 @@ process_shortform_attr(
}
}
+ if (!libxfs_attr_check_namespace(currententry->flags)) {
+ do_warn(
+ _("multiple namespaces for shortform attribute %d in inode %" PRIu64 "\n"),
+ i, ino);
+ junkit = 1;
+ }
+
/* namecheck checks for null chars in attr names. */
if (!libxfs_attr_namecheck(currententry->flags,
currententry->nameval,
@@ -641,6 +648,14 @@ process_leaf_attr_block(
break;
}
+ if (!libxfs_attr_check_namespace(entry->flags)) {
+ do_warn(
+ _("multiple namespaces for attribute entry %d in attr block %u, inode %" PRIu64 "\n"),
+ i, da_bno, ino);
+ clearit = 1;
+ break;
+ }
+
if (entry->flags & XFS_ATTR_INCOMPLETE) {
/* we are inconsistent state. get rid of us */
do_warn(
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 6/6] xfs_repair: check for unknown flags in attr entries
2024-07-30 0:20 ` [PATCHSET v13.8 17/23] xfsprogs: improve extended attribute validation Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 1:18 ` [PATCH 5/6] xfs_repair: enforce one namespace bit per extended attribute Darrick J. Wong
@ 2024-07-30 1:18 ` Darrick J. Wong
5 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:18 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Explicitly check for unknown bits being set in the shortform and leaf
attr entries.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
repair/attr_repair.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/repair/attr_repair.c b/repair/attr_repair.c
index a756a40db..37b5852b8 100644
--- a/repair/attr_repair.c
+++ b/repair/attr_repair.c
@@ -291,6 +291,13 @@ process_shortform_attr(
}
}
+ if (currententry->flags & ~XFS_ATTR_ONDISK_MASK) {
+ do_warn(
+ _("unknown flags 0x%x in shortform attribute %d in inode %" PRIu64 "\n"),
+ currententry->flags, i, ino);
+ junkit = 1;
+ }
+
if (!libxfs_attr_check_namespace(currententry->flags)) {
do_warn(
_("multiple namespaces for shortform attribute %d in inode %" PRIu64 "\n"),
@@ -648,6 +655,14 @@ process_leaf_attr_block(
break;
}
+ if (entry->flags & ~XFS_ATTR_ONDISK_MASK) {
+ do_warn(
+ _("unknown flags 0x%x in attribute entry #%d in attr block %u, inode %" PRIu64 "\n"),
+ entry->flags, i, da_bno, ino);
+ clearit = 1;
+ break;
+ }
+
if (!libxfs_attr_check_namespace(entry->flags)) {
do_warn(
_("multiple namespaces for attribute entry %d in attr block %u, inode %" PRIu64 "\n"),
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 01/24] libxfs: create attr log item opcodes and formats for parent pointers
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
@ 2024-07-30 1:19 ` Darrick J. Wong
2024-07-30 1:19 ` [PATCH 02/24] xfs_{db,repair}: implement new attr hash value function Darrick J. Wong
` (22 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:19 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Update xfs_attr_defer_add to use the pptr-specific opcodes if it's
reading or writing parent pointers.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/defer_item.c | 52 +++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 46 insertions(+), 6 deletions(-)
diff --git a/libxfs/defer_item.c b/libxfs/defer_item.c
index 9955e189d..8cdf57eac 100644
--- a/libxfs/defer_item.c
+++ b/libxfs/defer_item.c
@@ -676,21 +676,61 @@ xfs_attr_defer_add(
enum xfs_attr_defer_op op)
{
struct xfs_attr_intent *new;
+ unsigned int log_op = 0;
+ bool is_pptr = args->attr_filter & XFS_ATTR_PARENT;
- new = kmem_cache_zalloc(xfs_attr_intent_cache, GFP_NOFS | __GFP_NOFAIL);
+ if (is_pptr) {
+ ASSERT(xfs_has_parent(args->dp->i_mount));
+ ASSERT((args->attr_filter & ~XFS_ATTR_PARENT) != 0);
+ ASSERT(args->op_flags & XFS_DA_OP_LOGGED);
+ ASSERT(args->valuelen == sizeof(struct xfs_parent_rec));
+ }
+
+ new = kmem_cache_zalloc(xfs_attr_intent_cache,
+ GFP_NOFS | __GFP_NOFAIL);
new->xattri_da_args = args;
+ /* Compute log operation from the higher level op and namespace. */
switch (op) {
case XFS_ATTR_DEFER_SET:
- new->xattri_op_flags = XFS_ATTRI_OP_FLAGS_SET;
- new->xattri_dela_state = xfs_attr_init_add_state(args);
+ if (is_pptr)
+ log_op = XFS_ATTRI_OP_FLAGS_PPTR_SET;
+ else
+ log_op = XFS_ATTRI_OP_FLAGS_SET;
break;
case XFS_ATTR_DEFER_REPLACE:
- new->xattri_op_flags = XFS_ATTRI_OP_FLAGS_REPLACE;
- new->xattri_dela_state = xfs_attr_init_replace_state(args);
+ if (is_pptr)
+ log_op = XFS_ATTRI_OP_FLAGS_PPTR_REPLACE;
+ else
+ log_op = XFS_ATTRI_OP_FLAGS_REPLACE;
break;
case XFS_ATTR_DEFER_REMOVE:
- new->xattri_op_flags = XFS_ATTRI_OP_FLAGS_REMOVE;
+ if (is_pptr)
+ log_op = XFS_ATTRI_OP_FLAGS_PPTR_REMOVE;
+ else
+ log_op = XFS_ATTRI_OP_FLAGS_REMOVE;
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ new->xattri_op_flags = log_op;
+
+ /* Set up initial attr operation state. */
+ switch (log_op) {
+ case XFS_ATTRI_OP_FLAGS_PPTR_SET:
+ case XFS_ATTRI_OP_FLAGS_SET:
+ new->xattri_dela_state = xfs_attr_init_add_state(args);
+ break;
+ case XFS_ATTRI_OP_FLAGS_PPTR_REPLACE:
+ ASSERT(args->new_valuelen == args->valuelen);
+ new->xattri_dela_state = xfs_attr_init_replace_state(args);
+ break;
+ case XFS_ATTRI_OP_FLAGS_REPLACE:
+ new->xattri_dela_state = xfs_attr_init_replace_state(args);
+ break;
+ case XFS_ATTRI_OP_FLAGS_PPTR_REMOVE:
+ case XFS_ATTRI_OP_FLAGS_REMOVE:
new->xattri_dela_state = xfs_attr_init_remove_state(args);
break;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 02/24] xfs_{db,repair}: implement new attr hash value function
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
2024-07-30 1:19 ` [PATCH 01/24] libxfs: create attr log item opcodes and formats for parent pointers Darrick J. Wong
@ 2024-07-30 1:19 ` Darrick J. Wong
2024-07-30 1:19 ` [PATCH 03/24] xfs_logprint: dump new attr log item fields Darrick J. Wong
` (21 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:19 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Port existing utilities to use libxfs_attr_hashname instead of calling
libxfs_da_hashname directly.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/hash.c | 8 ++++----
db/metadump.c | 10 +++++-----
libxfs/libxfs_api_defs.h | 2 ++
repair/attr_repair.c | 18 ++++++++++++------
4 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/db/hash.c b/db/hash.c
index 9b3fdea6c..1500a6032 100644
--- a/db/hash.c
+++ b/db/hash.c
@@ -73,7 +73,7 @@ hash_f(
if (use_dir2_hash)
hashval = libxfs_dir2_hashname(mp, &xname);
else
- hashval = libxfs_da_hashname(xname.name, xname.len);
+ hashval = libxfs_attr_hashname(xname.name, xname.len);
dbprintf("0x%x\n", hashval);
}
@@ -306,7 +306,7 @@ collide_xattrs(
unsigned long i;
int error = 0;
- old_hash = libxfs_da_hashname((uint8_t *)name, namelen);
+ old_hash = libxfs_attr_hashname((uint8_t *)name, namelen);
if (fd >= 0) {
/*
@@ -331,8 +331,8 @@ collide_xattrs(
snprintf(xattrname, MAXNAMELEN + 5, "user.%s", name);
obfuscate_name(old_hash, namelen, (uint8_t *)xattrname + 5,
false);
- ASSERT(old_hash == libxfs_da_hashname((uint8_t *)xattrname + 5,
- namelen));
+ ASSERT(old_hash == libxfs_attr_hashname(
+ (uint8_t *)xattrname + 5, namelen));
if (fd >= 0) {
error = fsetxattr(fd, xattrname, "1", 1, 0);
diff --git a/db/metadump.c b/db/metadump.c
index 9457e02e8..c1bf5d002 100644
--- a/db/metadump.c
+++ b/db/metadump.c
@@ -835,7 +835,7 @@ dirattr_hashname(
return libxfs_dir2_hashname(mp, &xname);
}
- return libxfs_da_hashname(name, namelen);
+ return libxfs_attr_hashname(name, namelen);
}
static void
@@ -982,9 +982,9 @@ obfuscate_path_components(
if (!slash) {
/* last (or single) component */
namelen = strnlen((char *)comp, len);
- hash = libxfs_da_hashname(comp, namelen);
+ hash = dirattr_hashname(true, comp, namelen);
obfuscate_name(hash, namelen, comp, false);
- ASSERT(hash == libxfs_da_hashname(comp, namelen));
+ ASSERT(hash == dirattr_hashname(true, comp, namelen));
break;
}
namelen = slash - (char *)comp;
@@ -994,9 +994,9 @@ obfuscate_path_components(
len--;
continue;
}
- hash = libxfs_da_hashname(comp, namelen);
+ hash = dirattr_hashname(true, comp, namelen);
obfuscate_name(hash, namelen, comp, false);
- ASSERT(hash == libxfs_da_hashname(comp, namelen));
+ ASSERT(hash == dirattr_hashname(true, comp, namelen));
comp += namelen + 1;
len -= namelen + 1;
}
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index 2d858580a..c36a6ac81 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -38,6 +38,8 @@
#define xfs_attr_check_namespace libxfs_attr_check_namespace
#define xfs_attr_get libxfs_attr_get
+#define xfs_attr_hashname libxfs_attr_hashname
+#define xfs_attr_hashval libxfs_attr_hashval
#define xfs_attr_leaf_newentsize libxfs_attr_leaf_newentsize
#define xfs_attr_namecheck libxfs_attr_namecheck
#define xfs_attr_set libxfs_attr_set
diff --git a/repair/attr_repair.c b/repair/attr_repair.c
index 37b5852b8..8321c9b67 100644
--- a/repair/attr_repair.c
+++ b/repair/attr_repair.c
@@ -485,6 +485,7 @@ process_leaf_attr_local(
xfs_ino_t ino)
{
xfs_attr_leaf_name_local_t *local;
+ xfs_dahash_t computed;
local = xfs_attr3_leaf_name_local(leaf, i);
if (local->namelen == 0 ||
@@ -504,9 +505,12 @@ process_leaf_attr_local(
* ordering anyway in case both the name value and the
* hashvalue were wrong but matched. Unlikely, however.
*/
- if (be32_to_cpu(entry->hashval) != libxfs_da_hashname(
- &local->nameval[0], local->namelen) ||
- be32_to_cpu(entry->hashval) < last_hashval) {
+ computed = libxfs_attr_hashval(mp, entry->flags, local->nameval,
+ local->namelen,
+ local->nameval + local->namelen,
+ be16_to_cpu(local->valuelen));
+ if (be32_to_cpu(entry->hashval) != computed ||
+ be32_to_cpu(entry->hashval) < last_hashval) {
do_warn(
_("bad hashvalue for attribute entry %d in attr block %u, inode %" PRIu64 "\n"),
i, da_bno, ino);
@@ -540,15 +544,17 @@ process_leaf_attr_remote(
{
xfs_attr_leaf_name_remote_t *remotep;
char* value;
+ xfs_dahash_t computed;
remotep = xfs_attr3_leaf_name_remote(leaf, i);
+ computed = libxfs_attr_hashval(mp, entry->flags, remotep->name,
+ remotep->namelen, NULL,
+ be32_to_cpu(remotep->valuelen));
if (remotep->namelen == 0 ||
!libxfs_attr_namecheck(entry->flags, remotep->name,
remotep->namelen) ||
- be32_to_cpu(entry->hashval) !=
- libxfs_da_hashname((unsigned char *)&remotep->name[0],
- remotep->namelen) ||
+ be32_to_cpu(entry->hashval) != computed ||
be32_to_cpu(entry->hashval) < last_hashval ||
be32_to_cpu(remotep->valueblk) == 0) {
do_warn(
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 03/24] xfs_logprint: dump new attr log item fields
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
2024-07-30 1:19 ` [PATCH 01/24] libxfs: create attr log item opcodes and formats for parent pointers Darrick J. Wong
2024-07-30 1:19 ` [PATCH 02/24] xfs_{db,repair}: implement new attr hash value function Darrick J. Wong
@ 2024-07-30 1:19 ` Darrick J. Wong
2024-07-30 1:19 ` [PATCH 04/24] man: document the XFS_IOC_GETPARENTS ioctl Darrick J. Wong
` (20 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:19 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Dump the new extended attribute log item fields. This was split out
from the previous patch to make libxfs resyncing easier. This code
needs more cleaning, which we'll do in the next few patches before
moving on to the parent pointer code.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
logprint/log_redo.c | 140 ++++++++++++++++++++++++++++++++++++++++++---------
logprint/logprint.h | 5 +-
2 files changed, 118 insertions(+), 27 deletions(-)
diff --git a/logprint/log_redo.c b/logprint/log_redo.c
index ca6dadd75..1d55164a9 100644
--- a/logprint/log_redo.c
+++ b/logprint/log_redo.c
@@ -674,6 +674,12 @@ xfs_attri_copy_log_format(
return 1;
}
+static inline unsigned int
+xfs_attr_log_item_op(const struct xfs_attri_log_format *attrp)
+{
+ return attrp->alfi_op_flags & XFS_ATTRI_OP_FLAGS_TYPE_MASK;
+}
+
int
xlog_print_trans_attri(
char **ptr,
@@ -683,6 +689,10 @@ xlog_print_trans_attri(
struct xfs_attri_log_format *src_f = NULL;
xlog_op_header_t *head = NULL;
uint dst_len;
+ unsigned int name_len = 0;
+ unsigned int new_name_len = 0;
+ unsigned int value_len = 0;
+ unsigned int new_value_len = 0;
int error = 0;
dst_len = sizeof(struct xfs_attri_log_format);
@@ -705,27 +715,71 @@ xlog_print_trans_attri(
memmove((char*)src_f, *ptr, src_len);
*ptr += src_len;
- printf(_("ATTRI: #regs: %d name_len: %d, value_len: %d id: 0x%llx\n"),
- src_f->alfi_size, src_f->alfi_name_len, src_f->alfi_value_len,
- (unsigned long long)src_f->alfi_id);
+ if (xfs_attr_log_item_op(src_f) == XFS_ATTRI_OP_FLAGS_PPTR_REPLACE) {
+ name_len = src_f->alfi_old_name_len;
+ new_name_len = src_f->alfi_new_name_len;
+ value_len = src_f->alfi_value_len;
+ new_value_len = src_f->alfi_value_len;
+ } else {
+ name_len = src_f->alfi_name_len;
+ value_len = src_f->alfi_value_len;
+ }
+
+ printf(_("ATTRI: #regs: %d f: 0x%x, ino: 0x%llx, igen: 0x%x, attr_filter: 0x%x, name_len: %u, new_name_len: %u, value_len: %u, new_value_len: %u id: 0x%llx\n"),
+ src_f->alfi_size,
+ src_f->alfi_op_flags,
+ (unsigned long long)src_f->alfi_ino,
+ (unsigned int)src_f->alfi_igen,
+ src_f->alfi_attr_filter,
+ name_len,
+ new_name_len,
+ value_len,
+ new_value_len,
+ (unsigned long long)src_f->alfi_id);
+
+ if (name_len > 0) {
+ printf(_("\n"));
+ (*i)++;
+ head = (xlog_op_header_t *)*ptr;
+ xlog_print_op_header(head, *i, ptr);
+ error = xlog_print_trans_attri_name(ptr,
+ be32_to_cpu(head->oh_len), "name");
+ if (error)
+ goto error;
+ }
- if (src_f->alfi_name_len > 0) {
+ if (new_name_len > 0) {
printf(_("\n"));
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
- error = xlog_print_trans_attri_name(ptr, be32_to_cpu(head->oh_len));
+ error = xlog_print_trans_attri_name(ptr,
+ be32_to_cpu(head->oh_len), "newname");
if (error)
goto error;
}
- if (src_f->alfi_value_len > 0) {
+ if (value_len > 0) {
printf(_("\n"));
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
- error = xlog_print_trans_attri_value(ptr, be32_to_cpu(head->oh_len),
- src_f->alfi_value_len);
+ error = xlog_print_trans_attri_value(ptr,
+ be32_to_cpu(head->oh_len), value_len, "value");
+ if (error)
+ goto error;
+ }
+
+ if (new_value_len > 0) {
+ printf(_("\n"));
+ (*i)++;
+ head = (xlog_op_header_t *)*ptr;
+ xlog_print_op_header(head, *i, ptr);
+ error = xlog_print_trans_attri_value(ptr,
+ be32_to_cpu(head->oh_len), new_value_len,
+ "newvalue");
+ if (error)
+ goto error;
}
error:
free(src_f);
@@ -736,31 +790,33 @@ xlog_print_trans_attri(
int
xlog_print_trans_attri_name(
char **ptr,
- uint src_len)
+ uint src_len,
+ const char *tag)
{
- printf(_("ATTRI: name len:%u\n"), src_len);
+ printf(_("ATTRI: %s len:%u\n"), tag, src_len);
print_or_dump(*ptr, src_len);
*ptr += src_len;
return 0;
-} /* xlog_print_trans_attri */
+}
int
xlog_print_trans_attri_value(
char **ptr,
uint src_len,
- int value_len)
+ int value_len,
+ const char *tag)
{
int len = min(value_len, src_len);
- printf(_("ATTRI: value len:%u\n"), value_len);
+ printf(_("ATTRI: %s len:%u\n"), tag, value_len);
print_or_dump(*ptr, len);
*ptr += src_len;
return 0;
-} /* xlog_print_trans_attri_value */
+}
void
xlog_recover_print_attri(
@@ -768,7 +824,10 @@ xlog_recover_print_attri(
{
struct xfs_attri_log_format *f, *src_f = NULL;
uint src_len, dst_len;
-
+ unsigned int name_len = 0;
+ unsigned int new_name_len = 0;
+ unsigned int value_len = 0;
+ unsigned int new_value_len = 0;
int region = 0;
src_f = (struct xfs_attri_log_format *)item->ri_buf[0].i_addr;
@@ -788,24 +847,55 @@ xlog_recover_print_attri(
if (xfs_attri_copy_log_format((char*)src_f, src_len, f))
goto out;
- printf(_("ATTRI: #regs: %d name_len: %d, value_len: %d id: 0x%llx\n"),
- f->alfi_size, f->alfi_name_len, f->alfi_value_len, (unsigned long long)f->alfi_id);
+ if (xfs_attr_log_item_op(f) == XFS_ATTRI_OP_FLAGS_PPTR_REPLACE) {
+ name_len = f->alfi_old_name_len;
+ new_name_len = f->alfi_new_name_len;
+ value_len = f->alfi_value_len;
+ new_value_len = f->alfi_value_len;
+ } else {
+ name_len = f->alfi_name_len;
+ value_len = f->alfi_value_len;
+ }
+
+ printf(_("ATTRI: #regs: %d f: 0x%x, ino: 0x%llx, igen: 0x%x, attr_filter: 0x%x, name_len: %u, new_name_len: %u, value_len: %d, new_value_len: %u id: 0x%llx\n"),
+ f->alfi_size,
+ f->alfi_op_flags,
+ (unsigned long long)f->alfi_ino,
+ (unsigned int)f->alfi_igen,
+ f->alfi_attr_filter,
+ name_len,
+ new_name_len,
+ value_len,
+ new_value_len,
+ (unsigned long long)f->alfi_id);
- if (f->alfi_name_len > 0) {
+ if (name_len > 0) {
region++;
- printf(_("ATTRI: name len:%u\n"), f->alfi_name_len);
+ printf(_("ATTRI: name len:%u\n"), name_len);
print_or_dump((char *)item->ri_buf[region].i_addr,
- f->alfi_name_len);
+ name_len);
}
- if (f->alfi_value_len > 0) {
- int len = f->alfi_value_len;
+ if (new_name_len > 0) {
+ region++;
+ printf(_("ATTRI: newname len:%u\n"), new_name_len);
+ print_or_dump((char *)item->ri_buf[region].i_addr,
+ new_name_len);
+ }
+
+ if (value_len > 0) {
+ int len = min(MAX_ATTR_VAL_PRINT, value_len);
+
+ region++;
+ printf(_("ATTRI: value len:%u\n"), value_len);
+ print_or_dump((char *)item->ri_buf[region].i_addr, len);
+ }
- if (len > MAX_ATTR_VAL_PRINT)
- len = MAX_ATTR_VAL_PRINT;
+ if (new_value_len > 0) {
+ int len = min(MAX_ATTR_VAL_PRINT, new_value_len);
region++;
- printf(_("ATTRI: value len:%u\n"), f->alfi_value_len);
+ printf(_("ATTRI: newvalue len:%u\n"), new_value_len);
print_or_dump((char *)item->ri_buf[region].i_addr, len);
}
diff --git a/logprint/logprint.h b/logprint/logprint.h
index 25c043485..8a997fe11 100644
--- a/logprint/logprint.h
+++ b/logprint/logprint.h
@@ -59,8 +59,9 @@ extern void xlog_recover_print_bud(struct xlog_recover_item *item);
#define MAX_ATTR_VAL_PRINT 128
extern int xlog_print_trans_attri(char **ptr, uint src_len, int *i);
-extern int xlog_print_trans_attri_name(char **ptr, uint src_len);
-extern int xlog_print_trans_attri_value(char **ptr, uint src_len, int value_len);
+int xlog_print_trans_attri_name(char **ptr, uint src_len, const char *tag);
+int xlog_print_trans_attri_value(char **ptr, uint src_len, int value_len,
+ const char *tag);
extern void xlog_recover_print_attri(struct xlog_recover_item *item);
extern int xlog_print_trans_attrd(char **ptr, uint len);
extern void xlog_recover_print_attrd(struct xlog_recover_item *item);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 04/24] man: document the XFS_IOC_GETPARENTS ioctl
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:19 ` [PATCH 03/24] xfs_logprint: dump new attr log item fields Darrick J. Wong
@ 2024-07-30 1:19 ` Darrick J. Wong
2024-07-30 1:20 ` [PATCH 05/24] libfrog: report parent pointers to userspace Darrick J. Wong
` (19 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:19 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Document how this new ioctl works.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man2/ioctl_xfs_getparents.2 | 212 +++++++++++++++++++++++++++++++++++++++
1 file changed, 212 insertions(+)
create mode 100644 man/man2/ioctl_xfs_getparents.2
diff --git a/man/man2/ioctl_xfs_getparents.2 b/man/man2/ioctl_xfs_getparents.2
new file mode 100644
index 000000000..5bb9b96a0
--- /dev/null
+++ b/man/man2/ioctl_xfs_getparents.2
@@ -0,0 +1,212 @@
+.\" Copyright (c) 2019-2024 Oracle. All rights reserved.
+.\"
+.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
+.\" SPDX-License-Identifier: GPL-2.0-or-later
+.\" %%%LICENSE_END
+.TH IOCTL-XFS-GETPARENTS 2 2024-04-09 "XFS"
+.SH NAME
+ioctl_xfs_getparents \- query XFS directory parent information
+.SH SYNOPSIS
+.br
+.B #include <xfs/xfs_fs.h>
+.PP
+.BI "int ioctl(int " fd ", XFS_IOC_GETPARENTS, struct xfs_getparents *" arg );
+.PP
+.BI "int ioctl(int " fd ", XFS_IOC_GETPARENTS_BY_HANDLE, struct xfs_getparents_by_handle *" arg );
+.SH DESCRIPTION
+This command is used to retrieve the directory parent pointers of either the
+currently opened file or a file handle.
+Parent pointers point upwards in the directory tree from a child file towards a
+parent directories.
+Each entry in a parent directory must have a corresponding parent pointer in
+the child.
+
+Calling programs should allocate a large memory buffer and initialize a header
+of the following form:
+.PP
+.in +4n
+.nf
+struct xfs_getparents {
+ struct xfs_attrlist_cursor gp_cursor;
+ __u16 gp_iflags;
+ __u16 gp_oflags;
+ __u32 gp_bufsize;
+ __u64 __pad;
+ __u64 gp_buffer;
+};
+
+struct xfs_getparents {
+ struct xfs_handle gph_handle;
+ struct xfs_getparents gph_request;
+};
+.fi
+.in
+
+.PP
+The field
+.I gp_cursor
+tracks the progress of iterating through the parent pointers.
+Calling programs must initialize this to zero before the first system call
+and must not touch it after that.
+
+.PP
+The field
+.I gp_iflags
+control the behavior of the query operation and provide more information
+about the outcome of the operation.
+There are no input flags currently defined; this field must be zero.
+
+.PP
+The field
+.I gp_oflags
+contains information about the query itself.
+Possibly output flags are:
+.RS 0.4i
+.TP
+.B XFS_GETPARENTS_OFLAG_ROOT
+The file queried was the root directory.
+.TP
+.B XFS_GETPARENTS_OFLAG_DONE
+There are no more parent pointers to query.
+.RE
+
+.PP
+The field
+.I __pad
+must be zero.
+
+.PP
+The field
+.I gp_bufsize
+should be set to the size of the buffer, in bytes.
+
+.PP
+The field
+.I gp_buffer
+should point to an output buffer for the parent pointer records.
+
+Parent pointer records are returned in the following form:
+.PP
+.in +4n
+.nf
+
+struct xfs_getparents_rec {
+ struct xfs_handle gpr_parent;
+ __u16 gpr_reclen;
+ char gpr_name[];
+};
+.fi
+.in
+
+.PP
+The field
+.I gpr_parent
+is a file handle that can be used to open the parent directory.
+
+.PP
+The field
+.I gpr_reclen
+will be set to the number of bytes used by this parent record.
+
+.PP
+The array
+.I gpr_name
+will be set to a NULL-terminated byte sequence representing the filename
+stored in the parent pointer.
+If the name is a zero-length string, the file queried has no parents.
+
+.SH SAMPLE PROGRAM
+Calling programs should allocate a large memory buffer, initialize the head
+structure to zeroes, set gp_bufsize to the size of the buffer, and call the
+ioctl.
+The XFS_GETPARENTS_OFLAG_DONE flag will be set in gp_flags when there are no
+more parent pointers to be read.
+The below code is an example of XFS_IOC_GETPARENTS usage:
+
+.nf
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <xfs/linux.h>
+#include <xfs/xfs.h>
+#include <xfs/xfs_types.h>
+#include <xfs/xfs_fs.h>
+
+int main() {
+ struct xfs_getparents gp = { };
+ struct xfs_getparents_rec *gpr;
+ int error, fd;
+
+ gp.gp_buffer = (uintptr_t)malloc(65536);
+ if (!gp.gp_buffer) {
+ perror("malloc");
+ return 1;
+ }
+ gp->gp_bufsize = 65536;
+
+ fd = open("/mnt/test/foo.txt", O_RDONLY | O_CREAT);
+ if (fd == -1)
+ return errno;
+
+ do {
+ error = ioctl(fd, XFS_IOC_GETPARENTS, gp);
+ if (error)
+ return error;
+
+ for (gpr = xfs_getparents_first_rec(&gp);
+ gpr != NULL;
+ gpr = xfs_getparents_next_rec(&gp, gpr)) {
+ if (gpr->gpr_name[0] == 0)
+ break;
+
+ printf("inode = %llu\\n",
+ gpr->gpr_parent.ha_fid.fid_ino);
+ printf("generation = %u\\n",
+ gpr->gpr_parent.ha_fid.fid_gen);
+ printf("name = \\"%s\\"\\n\\n",
+ gpr->gpr_name);
+ }
+ } while (!(gp.gp_flags & XFS_GETPARENTS_OFLAG_DONE));
+
+ return 0;
+}
+.fi
+
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EFSBADCRC
+Metadata checksum validation failed while performing the query.
+.TP
+.B EFSCORRUPTED
+Metadata corruption was encountered while performing the query.
+.TP
+.B EINVAL
+One or more of the arguments specified is invalid.
+.TP
+.B EMSGSIZE
+The record buffer was not large enough to store even a single record.
+.TP
+.B ENOMEM
+Not enough memory to retrieve parent pointers.
+.TP
+.B EOPNOTSUPP
+Repairs of the requested metadata object are not supported.
+.TP
+.B EROFS
+Filesystem is read-only and a repair was requested.
+.TP
+.B ESHUTDOWN
+Filesystem is shut down due to previous errors.
+.TP
+.B EIO
+An I/O error was encountered while performing the query.
+.SH CONFORMING TO
+This API is specific to XFS filesystem on the Linux kernel.
+.SH SEE ALSO
+.BR ioctl (2)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 05/24] libfrog: report parent pointers to userspace
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:19 ` [PATCH 04/24] man: document the XFS_IOC_GETPARENTS ioctl Darrick J. Wong
@ 2024-07-30 1:20 ` Darrick J. Wong
2024-07-30 1:20 ` [PATCH 06/24] libfrog: add parent pointer support code Darrick J. Wong
` (18 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:20 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Report the presence of parent pointer to userspace.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libfrog/fsgeom.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libfrog/fsgeom.c b/libfrog/fsgeom.c
index 71a8e4bb9..597c38b11 100644
--- a/libfrog/fsgeom.c
+++ b/libfrog/fsgeom.c
@@ -32,6 +32,7 @@ xfs_report_geom(
int inobtcount;
int nrext64;
int exchangerange;
+ int parent;
isint = geo->logstart > 0;
lazycount = geo->flags & XFS_FSOP_GEOM_FLAGS_LAZYSB ? 1 : 0;
@@ -51,6 +52,7 @@ xfs_report_geom(
inobtcount = geo->flags & XFS_FSOP_GEOM_FLAGS_INOBTCNT ? 1 : 0;
nrext64 = geo->flags & XFS_FSOP_GEOM_FLAGS_NREXT64 ? 1 : 0;
exchangerange = geo->flags & XFS_FSOP_GEOM_FLAGS_EXCHANGE_RANGE ? 1 : 0;
+ parent = geo->flags & XFS_FSOP_GEOM_FLAGS_PARENT ? 1 : 0;
printf(_(
"meta-data=%-22s isize=%-6d agcount=%u, agsize=%u blks\n"
@@ -60,7 +62,7 @@ xfs_report_geom(
" =%-22s exchange=%-3u\n"
"data =%-22s bsize=%-6u blocks=%llu, imaxpct=%u\n"
" =%-22s sunit=%-6u swidth=%u blks\n"
-"naming =version %-14u bsize=%-6u ascii-ci=%d, ftype=%d\n"
+"naming =version %-14u bsize=%-6u ascii-ci=%d, ftype=%d, parent=%d\n"
"log =%-22s bsize=%-6d blocks=%u, version=%d\n"
" =%-22s sectsz=%-5u sunit=%d blks, lazy-count=%d\n"
"realtime =%-22s extsz=%-6d blocks=%lld, rtextents=%lld\n"),
@@ -72,7 +74,7 @@ xfs_report_geom(
"", geo->blocksize, (unsigned long long)geo->datablocks,
geo->imaxpct,
"", geo->sunit, geo->swidth,
- dirversion, geo->dirblocksize, cimode, ftype_enabled,
+ dirversion, geo->dirblocksize, cimode, ftype_enabled, parent,
isint ? _("internal log") : logname ? logname : _("external"),
geo->blocksize, geo->logblocks, logversion,
"", geo->logsectsize, geo->logsunit / geo->blocksize, lazycount,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 06/24] libfrog: add parent pointer support code
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 1:20 ` [PATCH 05/24] libfrog: report parent pointers to userspace Darrick J. Wong
@ 2024-07-30 1:20 ` Darrick J. Wong
2024-07-30 1:20 ` [PATCH 07/24] xfs_io: adapt parent command to new parent pointer ioctls Darrick J. Wong
` (17 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:20 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Add some support code to libfrog so that client programs can walk file
descriptors and handles upwards through the directory tree; and obtain a
reasonable file path from a file descriptor/handle. This code will be
used in xfsprogs utilities.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
include/handle.h | 1
libfrog/Makefile | 2
libfrog/getparents.c | 355 ++++++++++++++++++++++++++++++++++++++++++++++++++
libfrog/getparents.h | 42 ++++++
libfrog/paths.c | 168 ++++++++++++++++++++++++
libfrog/paths.h | 25 ++++
libhandle/handle.c | 7 +
7 files changed, 597 insertions(+), 3 deletions(-)
create mode 100644 libfrog/getparents.c
create mode 100644 libfrog/getparents.h
diff --git a/include/handle.h b/include/handle.h
index 34246f385..ba0650051 100644
--- a/include/handle.h
+++ b/include/handle.h
@@ -17,6 +17,7 @@ struct parent;
extern int path_to_handle (char *__path, void **__hanp, size_t *__hlen);
extern int path_to_fshandle (char *__path, void **__fshanp, size_t *__fshlen);
extern int fd_to_handle (int fd, void **hanp, size_t *hlen);
+extern int handle_to_fsfd(void *, char **);
extern int handle_to_fshandle (void *__hanp, size_t __hlen, void **__fshanp,
size_t *__fshlen);
extern void free_handle (void *__hanp, size_t __hlen);
diff --git a/libfrog/Makefile b/libfrog/Makefile
index acfa228bc..0b5b23893 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -20,6 +20,7 @@ convert.c \
crc32.c \
file_exchange.c \
fsgeom.c \
+getparents.c \
histogram.c \
list_sort.c \
linux.c \
@@ -46,6 +47,7 @@ dahashselftest.h \
div64.h \
file_exchange.h \
fsgeom.h \
+getparents.h \
histogram.h \
logging.h \
paths.h \
diff --git a/libfrog/getparents.c b/libfrog/getparents.c
new file mode 100644
index 000000000..9118b0ff3
--- /dev/null
+++ b/libfrog/getparents.c
@@ -0,0 +1,355 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "platform_defs.h"
+#include "xfs.h"
+#include "xfs_arch.h"
+#include "list.h"
+#include "paths.h"
+#include "handle.h"
+#include "libfrog/getparents.h"
+
+/* Allocate a buffer for the xfs_getparent_rec array. */
+static void *
+alloc_records(
+ struct xfs_getparents *gp,
+ size_t bufsize)
+{
+ void *buf;
+
+ if (bufsize >= UINT32_MAX) {
+ errno = ENOMEM;
+ return NULL;
+ } else if (!bufsize) {
+ bufsize = XFS_XATTR_LIST_MAX;
+ }
+
+ buf = malloc(bufsize);
+ if (!buf)
+ return NULL;
+
+ gp->gp_buffer = (uintptr_t)buf;
+ gp->gp_bufsize = bufsize;
+ return buf;
+}
+
+/* Copy a file handle. */
+static inline void
+copy_handle(
+ struct xfs_handle *dest,
+ const struct xfs_handle *src)
+{
+ memcpy(dest, src, sizeof(struct xfs_handle));
+}
+
+/* Initiate a callback for each parent pointer. */
+static int
+walk_parent_records(
+ struct xfs_getparents *gp,
+ walk_parent_fn fn,
+ void *arg)
+{
+ struct xfs_getparents_rec *gpr;
+ int ret;
+
+ if (gp->gp_oflags & XFS_GETPARENTS_OFLAG_ROOT) {
+ struct parent_rec rec = {
+ .p_flags = PARENTREC_FILE_IS_ROOT,
+ };
+
+ return fn(&rec, arg);
+ }
+
+ for (gpr = xfs_getparents_first_rec(gp);
+ gpr != NULL;
+ gpr = xfs_getparents_next_rec(gp, gpr)) {
+ struct parent_rec rec = { };
+
+ if (gpr->gpr_name[0] == 0)
+ break;
+
+ copy_handle(&rec.p_handle, &gpr->gpr_parent);
+ rec.p_name = gpr->gpr_name;
+
+ ret = fn(&rec, arg);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Walk all parent pointers of this fd. Returns 0 or positive errno. */
+int
+fd_walk_parents(
+ int fd,
+ size_t bufsize,
+ walk_parent_fn fn,
+ void *arg)
+{
+ struct xfs_getparents gp = { };
+ void *buf;
+ int ret;
+
+ buf = alloc_records(&gp, bufsize);
+ if (!buf)
+ return errno;
+
+ while ((ret = ioctl(fd, XFS_IOC_GETPARENTS, &gp)) == 0) {
+ ret = walk_parent_records(&gp, fn, arg);
+ if (ret)
+ goto out_buf;
+ if (gp.gp_oflags & XFS_GETPARENTS_OFLAG_DONE)
+ break;
+ }
+ if (ret)
+ ret = errno;
+
+out_buf:
+ free(buf);
+ return ret;
+}
+
+/* Walk all parent pointers of this handle. Returns 0 or positive errno. */
+int
+handle_walk_parents(
+ const void *hanp,
+ size_t hlen,
+ size_t bufsize,
+ walk_parent_fn fn,
+ void *arg)
+{
+ struct xfs_getparents_by_handle gph = { };
+ void *buf;
+ char *mntpt;
+ int fd;
+ int ret;
+
+ if (hlen != sizeof(struct xfs_handle))
+ return EINVAL;
+
+ /*
+ * This function doesn't modify the handle, but we don't want to have
+ * to bump the libhandle major version just to change that.
+ */
+ fd = handle_to_fsfd((void *)hanp, &mntpt);
+ if (fd < 0)
+ return errno;
+
+ buf = alloc_records(&gph.gph_request, bufsize);
+ if (!buf)
+ return errno;
+
+ copy_handle(&gph.gph_handle, hanp);
+ while ((ret = ioctl(fd, XFS_IOC_GETPARENTS_BY_HANDLE, &gph)) == 0) {
+ ret = walk_parent_records(&gph.gph_request, fn, arg);
+ if (ret)
+ goto out_buf;
+ if (gph.gph_request.gp_oflags & XFS_GETPARENTS_OFLAG_DONE)
+ break;
+ }
+ if (ret)
+ ret = errno;
+
+out_buf:
+ free(buf);
+ return ret;
+}
+
+struct walk_ppaths_info {
+ /* Callback */
+ walk_path_fn fn;
+ void *arg;
+
+ /* Mountpoint of this filesystem. */
+ char *mntpt;
+
+ /* Path that we're constructing. */
+ struct path_list *path;
+
+ size_t ioctl_bufsize;
+};
+
+/*
+ * Recursively walk upwards through the directory tree, changing out the path
+ * components as needed. Call the callback when we have a complete path.
+ */
+static int
+find_parent_component(
+ const struct parent_rec *rec,
+ void *arg)
+{
+ struct walk_ppaths_info *wpi = arg;
+ struct path_component *pc;
+ int ret;
+
+ if (rec->p_flags & PARENTREC_FILE_IS_ROOT)
+ return wpi->fn(wpi->mntpt, wpi->path, wpi->arg);
+
+ /*
+ * If we detect a directory tree cycle, give up. We never made any
+ * guarantees about concurrent tree updates.
+ */
+ if (path_will_loop(wpi->path, rec->p_handle.ha_fid.fid_ino))
+ return 0;
+
+ pc = path_component_init(rec->p_name, rec->p_handle.ha_fid.fid_ino);
+ if (!pc)
+ return errno;
+ path_list_add_parent_component(wpi->path, pc);
+
+ ret = handle_walk_parents(&rec->p_handle, sizeof(rec->p_handle),
+ wpi->ioctl_bufsize, find_parent_component, wpi);
+
+ path_list_del_component(wpi->path, pc);
+ path_component_free(pc);
+ return ret;
+}
+
+/*
+ * Call the given function on all known paths from the vfs root to the inode
+ * described in the handle. Returns 0 for success or positive errno.
+ */
+int
+handle_walk_paths(
+ const void *hanp,
+ size_t hlen,
+ size_t ioctl_bufsize,
+ walk_path_fn fn,
+ void *arg)
+{
+ struct walk_ppaths_info wpi = {
+ .ioctl_bufsize = ioctl_bufsize,
+ };
+ int ret;
+
+ /*
+ * This function doesn't modify the handle, but we don't want to have
+ * to bump the libhandle major version just to change that.
+ */
+ ret = handle_to_fsfd((void *)hanp, &wpi.mntpt);
+ if (ret < 0)
+ return errno;
+
+ wpi.path = path_list_init();
+ if (!wpi.path)
+ return errno;
+ wpi.fn = fn;
+ wpi.arg = arg;
+
+ ret = handle_walk_parents(hanp, hlen, ioctl_bufsize,
+ find_parent_component, &wpi);
+
+ path_list_free(wpi.path);
+ return ret;
+}
+
+/*
+ * Call the given function on all known paths from the vfs root to the inode
+ * referred to by the file description. Returns 0 or positive errno.
+ */
+int
+fd_walk_paths(
+ int fd,
+ size_t ioctl_bufsize,
+ walk_path_fn fn,
+ void *arg)
+{
+ void *hanp;
+ size_t hlen;
+ int ret;
+
+ ret = fd_to_handle(fd, &hanp, &hlen);
+ if (ret)
+ return errno;
+
+ ret = handle_walk_paths(hanp, hlen, ioctl_bufsize, fn, arg);
+ free_handle(hanp, hlen);
+ return ret;
+}
+
+struct gather_path_info {
+ char *buf;
+ size_t len;
+ size_t written;
+};
+
+/* Helper that stringifies the first full path that we find. */
+static int
+path_to_string(
+ const char *mntpt,
+ const struct path_list *path,
+ void *arg)
+{
+ struct gather_path_info *gpi = arg;
+ int mntpt_len = strlen(mntpt);
+ int ret;
+
+ /* Trim trailing slashes from the mountpoint */
+ while (mntpt_len > 0 && mntpt[mntpt_len - 1] == '/')
+ mntpt_len--;
+
+ ret = snprintf(gpi->buf, gpi->len, "%.*s", mntpt_len, mntpt);
+ if (ret != mntpt_len)
+ return ENAMETOOLONG;
+ gpi->written += ret;
+
+ ret = path_list_to_string(path, gpi->buf + ret, gpi->len - ret);
+ if (ret < 0)
+ return ENAMETOOLONG;
+
+ gpi->written += ret;
+ return ECANCELED;
+}
+
+/*
+ * Return any eligible path to this file handle. Returns 0 for success or
+ * positive errno.
+ */
+int
+handle_to_path(
+ const void *hanp,
+ size_t hlen,
+ size_t ioctl_bufsize,
+ char *path,
+ size_t pathlen)
+{
+ struct gather_path_info gpi = { .buf = path, .len = pathlen };
+ int ret;
+
+ ret = handle_walk_paths(hanp, hlen, ioctl_bufsize, path_to_string,
+ &gpi);
+ if (ret && ret != ECANCELED)
+ return ret;
+ if (!gpi.written)
+ return ENODATA;
+
+ path[gpi.written] = 0;
+ return 0;
+}
+
+/*
+ * Return any eligible path to this file description. Returns 0 for success
+ * or positive errno.
+ */
+int
+fd_to_path(
+ int fd,
+ size_t ioctl_bufsize,
+ char *path,
+ size_t pathlen)
+{
+ struct gather_path_info gpi = { .buf = path, .len = pathlen };
+ int ret;
+
+ ret = fd_walk_paths(fd, ioctl_bufsize, path_to_string, &gpi);
+ if (ret && ret != ECANCELED)
+ return ret;
+ if (!gpi.written)
+ return ENODATA;
+
+ path[gpi.written] = 0;
+ return 0;
+}
diff --git a/libfrog/getparents.h b/libfrog/getparents.h
new file mode 100644
index 000000000..8098d5942
--- /dev/null
+++ b/libfrog/getparents.h
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __LIBFROG_GETPARENTS_H_
+#define __LIBFROG_GETPARENTS_H_
+
+struct path_list;
+
+struct parent_rec {
+ /* File handle to parent directory */
+ struct xfs_handle p_handle;
+
+ /* Null-terminated directory entry name in the parent */
+ char *p_name;
+
+ /* Flags for this record; see PARENTREC_* below */
+ uint32_t p_flags;
+};
+
+/* This is the root directory. */
+#define PARENTREC_FILE_IS_ROOT (1U << 0)
+
+typedef int (*walk_parent_fn)(const struct parent_rec *rec, void *arg);
+
+int fd_walk_parents(int fd, size_t ioctl_bufsize, walk_parent_fn fn, void *arg);
+int handle_walk_parents(const void *hanp, size_t hanlen, size_t ioctl_bufsize,
+ walk_parent_fn fn, void *arg);
+
+typedef int (*walk_path_fn)(const char *mntpt, const struct path_list *path,
+ void *arg);
+
+int fd_walk_paths(int fd, size_t ioctl_bufsize, walk_path_fn fn, void *arg);
+int handle_walk_paths(const void *hanp, size_t hanlen, size_t ioctl_bufsize,
+ walk_path_fn fn, void *arg);
+
+int fd_to_path(int fd, size_t ioctl_bufsize, char *path, size_t pathlen);
+int handle_to_path(const void *hanp, size_t hlen, size_t ioctl_bufsize,
+ char *path, size_t pathlen);
+
+#endif /* __LIBFROG_GETPARENTS_H_ */
diff --git a/libfrog/paths.c b/libfrog/paths.c
index 320b26dbf..a5dfab48e 100644
--- a/libfrog/paths.c
+++ b/libfrog/paths.c
@@ -16,6 +16,7 @@
#include "input.h"
#include "projects.h"
#include <mntent.h>
+#include "list.h"
#include <limits.h>
extern char *progname;
@@ -560,3 +561,170 @@ fs_table_insert_project_path(
return error;
}
+
+/* Structured path components. */
+
+struct path_list {
+ struct list_head p_head;
+};
+
+struct path_component {
+ struct list_head pc_list;
+ uint64_t pc_ino;
+ char *pc_fname;
+};
+
+/* Initialize a path component with a given name. */
+struct path_component *
+path_component_init(
+ const char *name,
+ uint64_t ino)
+{
+ struct path_component *pc;
+
+ pc = malloc(sizeof(struct path_component));
+ if (!pc)
+ return NULL;
+ INIT_LIST_HEAD(&pc->pc_list);
+ pc->pc_fname = strdup(name);
+ if (!pc->pc_fname) {
+ free(pc);
+ return NULL;
+ }
+ pc->pc_ino = ino;
+ return pc;
+}
+
+/* Free a path component. */
+void
+path_component_free(
+ struct path_component *pc)
+{
+ free(pc->pc_fname);
+ free(pc);
+}
+
+/* Initialize a pathname or returns positive errno. */
+struct path_list *
+path_list_init(void)
+{
+ struct path_list *path;
+
+ path = malloc(sizeof(struct path_list));
+ if (!path)
+ return NULL;
+ INIT_LIST_HEAD(&path->p_head);
+ return path;
+}
+
+/* Empty out a pathname. */
+void
+path_list_free(
+ struct path_list *path)
+{
+ struct path_component *pos;
+ struct path_component *n;
+
+ list_for_each_entry_safe(pos, n, &path->p_head, pc_list) {
+ path_list_del_component(path, pos);
+ path_component_free(pos);
+ }
+ free(path);
+}
+
+/* Add a parent component to a pathname. */
+void
+path_list_add_parent_component(
+ struct path_list *path,
+ struct path_component *pc)
+{
+ list_add(&pc->pc_list, &path->p_head);
+}
+
+/* Add a component to a pathname. */
+void
+path_list_add_component(
+ struct path_list *path,
+ struct path_component *pc)
+{
+ list_add_tail(&pc->pc_list, &path->p_head);
+}
+
+/* Remove a component from a pathname. */
+void
+path_list_del_component(
+ struct path_list *path,
+ struct path_component *pc)
+{
+ list_del_init(&pc->pc_list);
+}
+
+/*
+ * Convert a pathname into a string or returns -1 if the buffer isn't long
+ * enough.
+ */
+ssize_t
+path_list_to_string(
+ const struct path_list *path,
+ char *buf,
+ size_t buflen)
+{
+ struct path_component *pos;
+ char *buf_end = buf + buflen;
+ ssize_t bytes = 0;
+ int ret;
+
+ list_for_each_entry(pos, &path->p_head, pc_list) {
+ if (buf >= buf_end)
+ return -1;
+
+ ret = snprintf(buf, buflen, "/%s", pos->pc_fname);
+ if (ret < 0 || ret >= buflen)
+ return -1;
+
+ bytes += ret;
+ buf += ret;
+ buflen -= ret;
+ }
+ return bytes;
+}
+
+/* Walk each component of a path. */
+int
+path_walk_components(
+ const struct path_list *path,
+ path_walk_fn_t fn,
+ void *arg)
+{
+ struct path_component *pos;
+ int ret;
+
+ list_for_each_entry(pos, &path->p_head, pc_list) {
+ ret = fn(pos->pc_fname, pos->pc_ino, arg);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Will this path contain a loop if we add this inode? */
+bool
+path_will_loop(
+ const struct path_list *path_list,
+ uint64_t ino)
+{
+ struct path_component *pc;
+ unsigned int nr = 0;
+
+ list_for_each_entry(pc, &path_list->p_head, pc_list) {
+ if (pc->pc_ino == ino)
+ return true;
+
+ /* 256 path components should be enough for anyone. */
+ if (++nr > 256)
+ return true;
+ }
+
+ return false;
+}
diff --git a/libfrog/paths.h b/libfrog/paths.h
index f20a2c3ef..306fd3cb8 100644
--- a/libfrog/paths.h
+++ b/libfrog/paths.h
@@ -58,4 +58,29 @@ typedef struct fs_cursor {
extern void fs_cursor_initialise(char *__dir, uint __flags, fs_cursor_t *__cp);
extern fs_path_t *fs_cursor_next_entry(fs_cursor_t *__cp);
+/* Path information. */
+
+struct path_list;
+struct path_component;
+
+struct path_component *path_component_init(const char *name, uint64_t ino);
+void path_component_free(struct path_component *pc);
+
+struct path_list *path_list_init(void);
+void path_list_free(struct path_list *path);
+void path_list_add_parent_component(struct path_list *path,
+ struct path_component *pc);
+void path_list_add_component(struct path_list *path, struct path_component *pc);
+void path_list_del_component(struct path_list *path, struct path_component *pc);
+
+ssize_t path_list_to_string(const struct path_list *path, char *buf,
+ size_t buflen);
+
+typedef int (*path_walk_fn_t)(const char *name, uint64_t ino, void *arg);
+
+int path_walk_components(const struct path_list *path, path_walk_fn_t fn,
+ void *arg);
+
+bool path_will_loop(const struct path_list *path, uint64_t ino);
+
#endif /* __LIBFROG_PATH_H__ */
diff --git a/libhandle/handle.c b/libhandle/handle.c
index 333c21909..1e8fe9ac5 100644
--- a/libhandle/handle.c
+++ b/libhandle/handle.c
@@ -29,7 +29,6 @@ typedef union {
} comarg_t;
static int obj_to_handle(char *, int, unsigned int, comarg_t, void**, size_t*);
-static int handle_to_fsfd(void *, char **);
static char *path_to_fspath(char *path);
@@ -203,8 +202,10 @@ handle_to_fshandle(
return 0;
}
-static int
-handle_to_fsfd(void *hanp, char **path)
+int
+handle_to_fsfd(
+ void *hanp,
+ char **path)
{
struct fdhash *fdhp;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 07/24] xfs_io: adapt parent command to new parent pointer ioctls
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (5 preceding siblings ...)
2024-07-30 1:20 ` [PATCH 06/24] libfrog: add parent pointer support code Darrick J. Wong
@ 2024-07-30 1:20 ` Darrick J. Wong
2024-07-30 1:20 ` [PATCH 08/24] xfs_io: Add i, n and f flags to parent command Darrick J. Wong
` (16 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:20 UTC (permalink / raw)
To: djwong, cem
Cc: Allison Henderson, Christoph Hellwig, linux-xfs, catherine.hoang,
allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
For ages, xfs_io has had a totally useless 'parent' command that enabled
callers to walk the parents or print the directory tree path of an open
file. This code used the ioctl interface presented by SGI's version of
parent pointers that was never merged. Rework the code in here to use
the new ioctl interfaces that we've settled upon. Get rid of the old
parent pointer checking code since xfs_repair/xfs_scrub will take care
of that.
(This originally was in the "xfsprogs: implement the upper half of
parent pointers" megapatch.)
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
io/parent.c | 504 +++++++++++++++--------------------------------------
man/man8/xfs_io.8 | 25 ++-
2 files changed, 161 insertions(+), 368 deletions(-)
diff --git a/io/parent.c b/io/parent.c
index 8f63607ff..927d05d70 100644
--- a/io/parent.c
+++ b/io/parent.c
@@ -7,365 +7,88 @@
#include "command.h"
#include "input.h"
#include "libfrog/paths.h"
-#include "parent.h"
+#include "libfrog/getparents.h"
#include "handle.h"
-#include "jdm.h"
#include "init.h"
#include "io.h"
-#define PARENTBUF_SZ 16384
-#define BSTATBUF_SZ 16384
-
static cmdinfo_t parent_cmd;
-static int verbose_flag;
-static int err_status;
-static __u64 inodes_checked;
static char *mntpt;
-/*
- * check out a parent entry to see if the values seem valid
- */
-static void
-check_parent_entry(struct xfs_bstat *bstatp, parent_t *parent)
-{
- int sts;
- char fullpath[PATH_MAX];
- struct stat statbuf;
- char *str;
-
- sprintf(fullpath, _("%s%s"), mntpt, parent->p_name);
-
- sts = lstat(fullpath, &statbuf);
- if (sts != 0) {
- fprintf(stderr,
- _("inode-path for inode: %llu is incorrect - path \"%s\" non-existent\n"),
- (unsigned long long) bstatp->bs_ino, fullpath);
- if (verbose_flag) {
- fprintf(stderr,
- _("path \"%s\" does not stat for inode: %llu; err = %s\n"),
- fullpath,
- (unsigned long long) bstatp->bs_ino,
- strerror(errno));
- }
- err_status++;
- return;
- } else {
- if (verbose_flag > 1) {
- printf(_("path \"%s\" found\n"), fullpath);
- }
- }
-
- if (statbuf.st_ino != bstatp->bs_ino) {
- fprintf(stderr,
- _("inode-path for inode: %llu is incorrect - wrong inode#\n"),
- (unsigned long long) bstatp->bs_ino);
- if (verbose_flag) {
- fprintf(stderr,
- _("ino mismatch for path \"%s\" %llu vs %llu\n"),
- fullpath,
- (unsigned long long)statbuf.st_ino,
- (unsigned long long)bstatp->bs_ino);
- }
- err_status++;
- return;
- } else if (verbose_flag > 1) {
- printf(_("inode number match: %llu\n"),
- (unsigned long long)statbuf.st_ino);
- }
-
- /* get parent path */
- str = strrchr(fullpath, '/');
- *str = '\0';
- sts = stat(fullpath, &statbuf);
- if (sts != 0) {
- fprintf(stderr,
- _("parent path \"%s\" does not stat: %s\n"),
- fullpath,
- strerror(errno));
- err_status++;
- return;
- } else {
- if (parent->p_ino != statbuf.st_ino) {
- fprintf(stderr,
- _("inode-path for inode: %llu is incorrect - wrong parent inode#\n"),
- (unsigned long long) bstatp->bs_ino);
- if (verbose_flag) {
- fprintf(stderr,
- _("ino mismatch for path \"%s\" %llu vs %llu\n"),
- fullpath,
- (unsigned long long)parent->p_ino,
- (unsigned long long)statbuf.st_ino);
- }
- err_status++;
- return;
- } else {
- if (verbose_flag > 1) {
- printf(_("parent ino match for %llu\n"),
- (unsigned long long) parent->p_ino);
- }
- }
- }
-}
-
-static void
-check_parents(parent_t *parentbuf, size_t *parentbuf_size,
- jdm_fshandle_t *fshandlep, struct xfs_bstat *statp)
-{
- int error, i;
- __u32 count;
- parent_t *entryp;
-
- do {
- error = jdm_parentpaths(fshandlep, statp, parentbuf, *parentbuf_size, &count);
-
- if (error == ERANGE) {
- *parentbuf_size *= 2;
- parentbuf = (parent_t *)realloc(parentbuf, *parentbuf_size);
- } else if (error) {
- fprintf(stderr, _("parentpaths failed for ino %llu: %s\n"),
- (unsigned long long) statp->bs_ino,
- strerror(errno));
- err_status++;
- break;
- }
- } while (error == ERANGE);
-
-
- if (count == 0) {
- /* no links for inode - something wrong here */
- fprintf(stderr, _("inode-path for inode: %llu is missing\n"),
- (unsigned long long) statp->bs_ino);
- err_status++;
- }
-
- entryp = parentbuf;
- for (i = 0; i < count; i++) {
- check_parent_entry(statp, entryp);
- entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
- }
-}
-
-static int
-do_bulkstat(parent_t *parentbuf, size_t *parentbuf_size,
- struct xfs_bstat *bstatbuf, int fsfd, jdm_fshandle_t *fshandlep)
-{
- __s32 buflenout;
- __u64 lastino = 0;
- struct xfs_bstat *p;
- struct xfs_bstat *endp;
- struct xfs_fsop_bulkreq bulkreq;
- struct stat mntstat;
-
- if (stat(mntpt, &mntstat)) {
- fprintf(stderr, _("can't stat mount point \"%s\": %s\n"),
- mntpt, strerror(errno));
- return 1;
- }
-
- bulkreq.lastip = &lastino;
- bulkreq.icount = BSTATBUF_SZ;
- bulkreq.ubuffer = (void *)bstatbuf;
- bulkreq.ocount = &buflenout;
-
- while (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT, &bulkreq) == 0) {
- if (*(bulkreq.ocount) == 0) {
- return 0;
- }
- for (p = bstatbuf, endp = bstatbuf + *bulkreq.ocount; p < endp; p++) {
-
- /* inode being modified, get synced data with iget */
- if ( (!p->bs_nlink || !p->bs_mode) && p->bs_ino != 0 ) {
-
- if (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) < 0) {
- fprintf(stderr,
- _("failed to get bulkstat information for inode %llu\n"),
- (unsigned long long) p->bs_ino);
- continue;
- }
- if (!p->bs_nlink || !p->bs_mode || !p->bs_ino) {
- fprintf(stderr,
- _("failed to get valid bulkstat information for inode %llu\n"),
- (unsigned long long) p->bs_ino);
- continue;
- }
- }
-
- /* skip root */
- if (p->bs_ino == mntstat.st_ino) {
- continue;
- }
-
- if (verbose_flag > 1) {
- printf(_("checking inode %llu\n"),
- (unsigned long long) p->bs_ino);
- }
-
- /* print dotted progress */
- if ((inodes_checked % 100) == 0 && verbose_flag == 1) {
- printf("."); fflush(stdout);
- }
- inodes_checked++;
-
- check_parents(parentbuf, parentbuf_size, fshandlep, p);
- }
-
- }/*while*/
-
- fprintf(stderr, _("syssgi bulkstat failed: %s\n"), strerror(errno));
- return 1;
-}
+struct pptr_args {
+ char *pathbuf;
+};
static int
-parent_check(void)
+pptr_print(
+ const struct parent_rec *rec,
+ void *arg)
{
- int fsfd;
- jdm_fshandle_t *fshandlep;
- parent_t *parentbuf;
- size_t parentbuf_size = PARENTBUF_SZ;
- struct xfs_bstat *bstatbuf;
+ const struct xfs_fid *fid = &rec->p_handle.ha_fid;
- err_status = 0;
- inodes_checked = 0;
-
- sync();
-
- fsfd = file->fd;
-
- fshandlep = jdm_getfshandle(mntpt);
- if (fshandlep == NULL) {
- fprintf(stderr, _("unable to open \"%s\" for jdm: %s\n"),
- mntpt,
- strerror(errno));
- return 1;
+ if (rec->p_flags & PARENTREC_FILE_IS_ROOT) {
+ printf(_("Root directory.\n"));
+ return 0;
}
- /* allocate buffers */
- bstatbuf = (struct xfs_bstat *)calloc(BSTATBUF_SZ, sizeof(struct xfs_bstat));
- parentbuf = (parent_t *)malloc(parentbuf_size);
- if (!bstatbuf || !parentbuf) {
- fprintf(stderr, _("unable to allocate buffers: %s\n"),
- strerror(errno));
- err_status = 1;
- goto out;
- }
-
- if (do_bulkstat(parentbuf, &parentbuf_size, bstatbuf, fsfd, fshandlep) != 0)
- err_status++;
-
- if (err_status > 0)
- fprintf(stderr, _("num errors: %d\n"), err_status);
- else
- printf(_("succeeded checking %llu inodes\n"),
- (unsigned long long) inodes_checked);
-
-out:
- free(bstatbuf);
- free(parentbuf);
- free(fshandlep);
- return err_status;
-}
+ printf(_("p_ino = %llu\n"), (unsigned long long)fid->fid_ino);
+ printf(_("p_gen = %u\n"), (unsigned int)fid->fid_gen);
+ printf(_("p_namelen = %zu\n"), strlen(rec->p_name));
+ printf(_("p_name = \"%s\"\n\n"), rec->p_name);
-static void
-print_parent_entry(parent_t *parent, int fullpath)
-{
- printf(_("p_ino = %llu\n"), (unsigned long long) parent->p_ino);
- printf(_("p_gen = %u\n"), parent->p_gen);
- printf(_("p_reclen = %u\n"), parent->p_reclen);
- if (fullpath)
- printf(_("p_name = \"%s%s\"\n"), mntpt, parent->p_name);
- else
- printf(_("p_name = \"%s\"\n"), parent->p_name);
+ return 0;
}
static int
-parent_list(int fullpath)
+paths_print(
+ const char *mntpt,
+ const struct path_list *path,
+ void *arg)
{
- void *handlep = NULL;
- size_t handlen;
- int error, i;
- int retval = 1;
- __u32 count;
- parent_t *entryp;
- parent_t *parentbuf = NULL;
- char *path = file->name;
- int pb_size = PARENTBUF_SZ;
-
- /* XXXX for linux libhandle version - to set libhandle fsfd cache */
- {
- void *fshandle;
- size_t fshlen;
-
- if (path_to_fshandle(mntpt, &fshandle, &fshlen) != 0) {
- fprintf(stderr, _("%s: failed path_to_fshandle \"%s\": %s\n"),
- progname, path, strerror(errno));
- goto error;
- }
- free_handle(fshandle, fshlen);
- }
-
- if (path_to_handle(path, &handlep, &handlen) != 0) {
- fprintf(stderr, _("%s: path_to_handle failed for \"%s\"\n"), progname, path);
- goto error;
- }
-
- do {
- parentbuf = (parent_t *)realloc(parentbuf, pb_size);
- if (!parentbuf) {
- fprintf(stderr, _("%s: unable to allocate parent buffer: %s\n"),
- progname, strerror(errno));
- goto error;
- }
-
- if (fullpath) {
- error = parentpaths_by_handle(handlep,
- handlen,
- parentbuf,
- pb_size,
- &count);
- } else {
- error = parents_by_handle(handlep,
- handlen,
- parentbuf,
- pb_size,
- &count);
- }
- if (error == ERANGE) {
- pb_size *= 2;
- } else if (error) {
- fprintf(stderr, _("%s: %s call failed for \"%s\": %s\n"),
- progname, fullpath ? "parentpaths" : "parents",
- path, strerror(errno));
- goto error;
- }
- } while (error == ERANGE);
-
- if (count == 0) {
- /* no links for inode - something wrong here */
- fprintf(stderr, _("%s: inode-path is missing\n"), progname);
- goto error;
- }
-
- entryp = parentbuf;
- for (i = 0; i < count; i++) {
- print_parent_entry(entryp, fullpath);
- entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
- }
-
- retval = 0;
-error:
- free(handlep);
- free(parentbuf);
- return retval;
+ struct pptr_args *args = arg;
+ char *buf = args->pathbuf;
+ size_t len = MAXPATHLEN;
+ int mntpt_len = strlen(mntpt);
+ int ret;
+
+ /* Trim trailing slashes from the mountpoint */
+ while (mntpt_len > 0 && mntpt[mntpt_len - 1] == '/')
+ mntpt_len--;
+
+ ret = snprintf(buf, len, "%.*s", mntpt_len, mntpt);
+ if (ret != mntpt_len)
+ return ENAMETOOLONG;
+
+ ret = path_list_to_string(path, buf + ret, len - ret);
+ if (ret < 0)
+ return ENAMETOOLONG;
+
+ printf("%s\n", buf);
+ return 0;
}
static int
-parent_f(int argc, char **argv)
+parent_f(
+ int argc,
+ char **argv)
{
- int c;
- int listpath_flag = 0;
- int check_flag = 0;
- fs_path_t *fs;
- static int tab_init;
+ char pathbuf[MAXPATHLEN + 1];
+ struct pptr_args args = {
+ .pathbuf = pathbuf,
+ };
+ struct xfs_handle handle;
+ void *hanp = NULL;
+ size_t hlen;
+ struct fs_path *fs;
+ char *p;
+ uint64_t ino = 0;
+ uint32_t gen = 0;
+ int c;
+ int listpath_flag = 0;
+ int ret;
+ size_t ioctl_bufsize = 8192;
+ bool single_path = false;
+ static int tab_init;
if (!tab_init) {
tab_init = 1;
@@ -380,46 +103,110 @@ parent_f(int argc, char **argv)
}
mntpt = fs->fs_dir;
- verbose_flag = 0;
-
- while ((c = getopt(argc, argv, "cpv")) != EOF) {
+ while ((c = getopt(argc, argv, "b:pz")) != EOF) {
switch (c) {
- case 'c':
- check_flag = 1;
+ case 'b':
+ errno = 0;
+ ioctl_bufsize = atoi(optarg);
+ if (errno) {
+ perror(optarg);
+ exitcode = 1;
+ return 1;
+ }
break;
case 'p':
listpath_flag = 1;
break;
- case 'v':
- verbose_flag++;
+ case 'z':
+ single_path = true;
break;
default:
return command_usage(&parent_cmd);
}
}
- if (!check_flag && !listpath_flag) /* default case */
- exitcode = parent_list(listpath_flag);
- else {
- if (listpath_flag)
- exitcode = parent_list(listpath_flag);
- if (check_flag)
- exitcode = parent_check();
+ /*
+ * Always initialize the fshandle table because we need it for
+ * the ppaths functions to work.
+ */
+ ret = path_to_fshandle((char *)mntpt, &hanp, &hlen);
+ if (ret) {
+ perror(mntpt);
+ return 0;
}
+ if (optind + 2 == argc) {
+ ino = strtoull(argv[optind], &p, 0);
+ if (*p != '\0' || ino == 0) {
+ fprintf(stderr,
+ _("Bad inode number '%s'.\n"),
+ argv[optind]);
+ return 0;
+ }
+ gen = strtoul(argv[optind + 1], &p, 0);
+ if (*p != '\0') {
+ fprintf(stderr,
+ _("Bad generation number '%s'.\n"),
+ argv[optind + 1]);
+ return 0;
+ }
+
+ memcpy(&handle, hanp, sizeof(handle));
+ handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
+ sizeof(handle.ha_fid.fid_len);
+ handle.ha_fid.fid_pad = 0;
+ handle.ha_fid.fid_ino = ino;
+ handle.ha_fid.fid_gen = gen;
+ } else if (optind != argc) {
+ return command_usage(&parent_cmd);
+ }
+
+ if (single_path) {
+ if (ino)
+ ret = handle_to_path(&handle, sizeof(handle),
+ ioctl_bufsize, pathbuf, MAXPATHLEN);
+ else
+ ret = fd_to_path(file->fd, ioctl_bufsize,
+ pathbuf, MAXPATHLEN);
+ if (!ret)
+ printf("%s\n", pathbuf);
+ } else if (listpath_flag) {
+ if (ino)
+ ret = handle_walk_paths(&handle, sizeof(handle),
+ ioctl_bufsize, paths_print, &args);
+ else
+ ret = fd_walk_paths(file->fd, ioctl_bufsize,
+ paths_print, &args);
+ } else {
+ if (ino)
+ ret = handle_walk_parents(&handle, sizeof(handle),
+ ioctl_bufsize, pptr_print, &args);
+ else
+ ret = fd_walk_parents(file->fd, ioctl_bufsize,
+ pptr_print, &args);
+ }
+
+ if (hanp)
+ free_handle(hanp, hlen);
+ if (ret) {
+ exitcode = 1;
+ fprintf(stderr, "%s: %s\n", file->name, strerror(ret));
+ }
return 0;
}
static void
parent_help(void)
{
- printf(_(
+printf(_(
"\n"
" list the current file's parents and their filenames\n"
"\n"
-" -c -- check the current file's file system for parent consistency\n"
-" -p -- list the current file's parents and their full paths\n"
-" -v -- verbose mode\n"
+" -b -- use this many bytes to hold parent pointer records\n"
+" -p -- list the current file's paths up to the root\n"
+" -z -- print only the first path from the root\n"
+"\n"
+"If ino and gen are supplied, use them instead.\n"
"\n"));
}
@@ -430,11 +217,10 @@ parent_init(void)
parent_cmd.cfunc = parent_f;
parent_cmd.argmin = 0;
parent_cmd.argmax = -1;
- parent_cmd.args = _("[-cpv]");
+ parent_cmd.args = _("[-pz] [-b bufsize] [ino gen]");
parent_cmd.flags = CMD_NOMAP_OK;
- parent_cmd.oneline = _("print or check parent inodes");
+ parent_cmd.oneline = _("print parent inodes");
parent_cmd.help = parent_help;
- if (expert)
- add_command(&parent_cmd);
+ add_command(&parent_cmd);
}
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 2a7c67f7c..b9d544770 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -1004,25 +1004,32 @@ and
options behave as described above, in
.B chproj.
.TP
-.BR parent " [ " \-cpv " ]"
+.BR parent " [ " \-pz " ] [ " \-b " bufsize ] [" " ino gen " "]"
By default this command prints out the parent inode numbers,
inode generation numbers and basenames of all the hardlinks which
point to the inode of the current file.
+
+If the optional
+.B ino
+and
+.B gen
+parameters are provided, they will be used to create a file handle on the same
+filesystem as the open file.
+The parents of the file represented by the handle will be reported instead of
+the open file.
+
.RS 1.0i
.PD 0
.TP 0.4i
+.B \-b
+Use a buffer of this size to receive parent pointer records from the kernel.
+.TP
.B \-p
the output is similar to the default output except pathnames up to
the mount-point are printed out instead of the component name.
.TP
-.B \-c
-the file's filesystem will check all the parent attributes for consistency.
-.TP
-.B \-v
-verbose output will be printed.
-.RE
-.IP
-.B [NOTE: Not currently operational on Linux.]
+.B \-z
+Print only the first path from the root.
.RE
.PD
.TP
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 08/24] xfs_io: Add i, n and f flags to parent command
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (6 preceding siblings ...)
2024-07-30 1:20 ` [PATCH 07/24] xfs_io: adapt parent command to new parent pointer ioctls Darrick J. Wong
@ 2024-07-30 1:20 ` Darrick J. Wong
2024-07-30 1:21 ` [PATCH 09/24] xfs_logprint: decode parent pointers in ATTRI items fully Darrick J. Wong
` (15 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:20 UTC (permalink / raw)
To: djwong, cem
Cc: Allison Henderson, Christoph Hellwig, linux-xfs, catherine.hoang,
allison.henderson
From: Allison Henderson <allison.henderson@oracle.com>
This patch adds the flags i, n, and f to the parent command. These flags add
filtering options that are used by the new parent pointer tests in xfstests, and
help to improve the test run time. The flags are:
-i: Only show parent pointer records containing the given inode
-n: Only show parent pointer records containing the given filename
-f: Print records in short format: ino/gen/namelen/name
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: adapt to new getparents ioctl]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
io/parent.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++--
man/man8/xfs_io.8 | 13 ++++++++++-
2 files changed, 70 insertions(+), 4 deletions(-)
diff --git a/io/parent.c b/io/parent.c
index 927d05d70..8db93d987 100644
--- a/io/parent.c
+++ b/io/parent.c
@@ -17,6 +17,9 @@ static char *mntpt;
struct pptr_args {
char *pathbuf;
+ char *filter_name;
+ uint64_t filter_ino;
+ bool shortformat;
};
static int
@@ -25,12 +28,27 @@ pptr_print(
void *arg)
{
const struct xfs_fid *fid = &rec->p_handle.ha_fid;
+ struct pptr_args *args = arg;
if (rec->p_flags & PARENTREC_FILE_IS_ROOT) {
printf(_("Root directory.\n"));
return 0;
}
+ if (args->filter_ino && fid->fid_ino != args->filter_ino)
+ return 0;
+ if (args->filter_name && strcmp(args->filter_name, rec->p_name))
+ return 0;
+
+ if (args->shortformat) {
+ printf("%llu:%u:%zu:%s\n",
+ (unsigned long long)fid->fid_ino,
+ (unsigned int)fid->fid_gen,
+ strlen(rec->p_name),
+ rec->p_name);
+ return 0;
+ }
+
printf(_("p_ino = %llu\n"), (unsigned long long)fid->fid_ino);
printf(_("p_gen = %u\n"), (unsigned int)fid->fid_gen);
printf(_("p_namelen = %zu\n"), strlen(rec->p_name));
@@ -39,6 +57,21 @@ pptr_print(
return 0;
}
+static int
+filter_path_components(
+ const char *name,
+ uint64_t ino,
+ void *arg)
+{
+ struct pptr_args *args = arg;
+
+ if (args->filter_ino && ino == args->filter_ino)
+ return ECANCELED;
+ if (args->filter_name && !strcmp(args->filter_name, name))
+ return ECANCELED;
+ return 0;
+}
+
static int
paths_print(
const char *mntpt,
@@ -51,6 +84,12 @@ paths_print(
int mntpt_len = strlen(mntpt);
int ret;
+ if (args->filter_ino || args->filter_name) {
+ ret = path_walk_components(path, filter_path_components, args);
+ if (ret != ECANCELED)
+ return 0;
+ }
+
/* Trim trailing slashes from the mountpoint */
while (mntpt_len > 0 && mntpt[mntpt_len - 1] == '/')
mntpt_len--;
@@ -103,7 +142,7 @@ parent_f(
}
mntpt = fs->fs_dir;
- while ((c = getopt(argc, argv, "b:pz")) != EOF) {
+ while ((c = getopt(argc, argv, "b:i:n:psz")) != EOF) {
switch (c) {
case 'b':
errno = 0;
@@ -114,9 +153,24 @@ parent_f(
return 1;
}
break;
+ case 'i':
+ args.filter_ino = strtoull(optarg, &p, 0);
+ if (*p != '\0' || args.filter_ino == 0) {
+ fprintf(stderr, _("Bad inode number '%s'.\n"),
+ optarg);
+ exitcode = 1;
+ return 1;
+ }
+ break;
+ case 'n':
+ args.filter_name = optarg;
+ break;
case 'p':
listpath_flag = 1;
break;
+ case 's':
+ args.shortformat = true;
+ break;
case 'z':
single_path = true;
break;
@@ -203,7 +257,10 @@ printf(_(
" list the current file's parents and their filenames\n"
"\n"
" -b -- use this many bytes to hold parent pointer records\n"
+" -i -- Only show parent pointer records containing the given inode\n"
+" -n -- Only show parent pointer records containing the given filename\n"
" -p -- list the current file's paths up to the root\n"
+" -s -- Print records in short format: ino/gen/namelen/filename\n"
" -z -- print only the first path from the root\n"
"\n"
"If ino and gen are supplied, use them instead.\n"
@@ -217,7 +274,7 @@ parent_init(void)
parent_cmd.cfunc = parent_f;
parent_cmd.argmin = 0;
parent_cmd.argmax = -1;
- parent_cmd.args = _("[-pz] [-b bufsize] [ino gen]");
+ parent_cmd.args = _("[-psz] [-b bufsize] [-i ino] [-n name] [ino gen]");
parent_cmd.flags = CMD_NOMAP_OK;
parent_cmd.oneline = _("print parent inodes");
parent_cmd.help = parent_help;
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index b9d544770..02036e3d0 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -1004,7 +1004,7 @@ and
options behave as described above, in
.B chproj.
.TP
-.BR parent " [ " \-pz " ] [ " \-b " bufsize ] [" " ino gen " "]"
+.BR parent " [ " \-fpz " ] [ " \-b " bufsize ] [ " \-i " ino ] [ " \-n " name ] [" " ino gen " "]"
By default this command prints out the parent inode numbers,
inode generation numbers and basenames of all the hardlinks which
point to the inode of the current file.
@@ -1023,11 +1023,20 @@ the open file.
.TP 0.4i
.B \-b
Use a buffer of this size to receive parent pointer records from the kernel.
-.TP
+.TP 0.4i
+.B \-i
+Only show parent pointer records containing this inode number.
+.TP 0.4i
+.B \-n
+Only show parent pointer records containing this directory entry name.
+.TP 0.4i
.B \-p
the output is similar to the default output except pathnames up to
the mount-point are printed out instead of the component name.
.TP
+.B \-s
+Print records in short format: ino/gen/namelen/name
+.TP
.B \-z
Print only the first path from the root.
.RE
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 09/24] xfs_logprint: decode parent pointers in ATTRI items fully
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (7 preceding siblings ...)
2024-07-30 1:20 ` [PATCH 08/24] xfs_io: Add i, n and f flags to parent command Darrick J. Wong
@ 2024-07-30 1:21 ` Darrick J. Wong
2024-07-30 1:21 ` [PATCH 10/24] xfs_spaceman: report file paths Darrick J. Wong
` (14 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:21 UTC (permalink / raw)
To: djwong, cem
Cc: Allison Henderson, Christoph Hellwig, linux-xfs, catherine.hoang,
allison.henderson
From: Allison Henderson <allison.henderson@oracle.com>
This patch modifies the ATTRI print routines to look for the parent
pointer flag, and decode logged parent pointers fully when dumping log
contents. Between the existing ATTRI: printouts and the new ones
introduced here, we can figure out what was stored in each log iovec,
as well as the higher level parent pointer that was logged.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: adjust to new ondisk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/libxfs_api_defs.h | 3 ++
logprint/log_redo.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 80 insertions(+)
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index c36a6ac81..b7947591d 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -190,6 +190,9 @@
#define xfs_log_sb libxfs_log_sb
#define xfs_mode_to_ftype libxfs_mode_to_ftype
#define xfs_mkdir_space_res libxfs_mkdir_space_res
+#define xfs_parent_add libxfs_parent_add
+#define xfs_parent_finish libxfs_parent_finish
+#define xfs_parent_start libxfs_parent_start
#define xfs_perag_get libxfs_perag_get
#define xfs_perag_hold libxfs_perag_hold
#define xfs_perag_put libxfs_perag_put
diff --git a/logprint/log_redo.c b/logprint/log_redo.c
index 1d55164a9..684e5f4a3 100644
--- a/logprint/log_redo.c
+++ b/logprint/log_redo.c
@@ -674,6 +674,55 @@ xfs_attri_copy_log_format(
return 1;
}
+static void
+dump_pptr(
+ const char *tag,
+ const void *name_ptr,
+ unsigned int name_len,
+ const void *value_ptr,
+ unsigned int value_len)
+{
+ const struct xfs_parent_rec *rec = value_ptr;
+
+ if (value_len < sizeof(struct xfs_parent_rec)) {
+ printf("PPTR: %s CORRUPT\n", tag);
+ return;
+ }
+
+ printf("PPTR: %s attr_namelen %u attr_valuelen %u\n", tag, name_len, value_len);
+ printf("PPTR: %s parent_ino %llu parent_gen %u name '%.*s'\n",
+ tag,
+ (unsigned long long)be64_to_cpu(rec->p_ino),
+ (unsigned int)be32_to_cpu(rec->p_gen),
+ name_len,
+ (char *)name_ptr);
+}
+
+static void
+dump_pptr_update(
+ const void *name_ptr,
+ unsigned int name_len,
+ const void *new_name_ptr,
+ unsigned int new_name_len,
+ const void *value_ptr,
+ unsigned int value_len,
+ const void *new_value_ptr,
+ unsigned int new_value_len)
+{
+ if (new_name_ptr && name_ptr) {
+ dump_pptr("OLDNAME", name_ptr, name_len, value_ptr, value_len);
+ dump_pptr("NEWNAME", new_name_ptr, new_name_len, new_value_ptr,
+ new_value_len);
+ return;
+ }
+
+ if (name_ptr)
+ dump_pptr("NAME", name_ptr, name_len, value_ptr, value_len);
+ if (new_name_ptr)
+ dump_pptr("NEWNAME", new_name_ptr, new_name_len, new_value_ptr,
+ new_value_len);
+}
+
static inline unsigned int
xfs_attr_log_item_op(const struct xfs_attri_log_format *attrp)
{
@@ -688,6 +737,10 @@ xlog_print_trans_attri(
{
struct xfs_attri_log_format *src_f = NULL;
xlog_op_header_t *head = NULL;
+ void *name_ptr = NULL;
+ void *new_name_ptr = NULL;
+ void *value_ptr = NULL;
+ void *new_value_ptr = NULL;
uint dst_len;
unsigned int name_len = 0;
unsigned int new_name_len = 0;
@@ -742,6 +795,7 @@ xlog_print_trans_attri(
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
+ name_ptr = *ptr;
error = xlog_print_trans_attri_name(ptr,
be32_to_cpu(head->oh_len), "name");
if (error)
@@ -753,6 +807,7 @@ xlog_print_trans_attri(
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
+ new_name_ptr = *ptr;
error = xlog_print_trans_attri_name(ptr,
be32_to_cpu(head->oh_len), "newname");
if (error)
@@ -764,6 +819,7 @@ xlog_print_trans_attri(
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
+ value_ptr = *ptr;
error = xlog_print_trans_attri_value(ptr,
be32_to_cpu(head->oh_len), value_len, "value");
if (error)
@@ -775,12 +831,19 @@ xlog_print_trans_attri(
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
+ new_value_ptr = *ptr;
error = xlog_print_trans_attri_value(ptr,
be32_to_cpu(head->oh_len), new_value_len,
"newvalue");
if (error)
goto error;
}
+
+ if (src_f->alfi_attr_filter & XFS_ATTR_PARENT)
+ dump_pptr_update(name_ptr, name_len,
+ new_name_ptr, new_name_len,
+ value_ptr, value_len,
+ new_value_ptr, new_value_len);
error:
free(src_f);
@@ -823,6 +886,10 @@ xlog_recover_print_attri(
struct xlog_recover_item *item)
{
struct xfs_attri_log_format *f, *src_f = NULL;
+ void *name_ptr = NULL;
+ void *new_name_ptr = NULL;
+ void *value_ptr = NULL;
+ void *new_value_ptr = NULL;
uint src_len, dst_len;
unsigned int name_len = 0;
unsigned int new_name_len = 0;
@@ -874,6 +941,7 @@ xlog_recover_print_attri(
printf(_("ATTRI: name len:%u\n"), name_len);
print_or_dump((char *)item->ri_buf[region].i_addr,
name_len);
+ name_ptr = item->ri_buf[region].i_addr;
}
if (new_name_len > 0) {
@@ -881,6 +949,7 @@ xlog_recover_print_attri(
printf(_("ATTRI: newname len:%u\n"), new_name_len);
print_or_dump((char *)item->ri_buf[region].i_addr,
new_name_len);
+ new_name_ptr = item->ri_buf[region].i_addr;
}
if (value_len > 0) {
@@ -889,6 +958,7 @@ xlog_recover_print_attri(
region++;
printf(_("ATTRI: value len:%u\n"), value_len);
print_or_dump((char *)item->ri_buf[region].i_addr, len);
+ value_ptr = item->ri_buf[region].i_addr;
}
if (new_value_len > 0) {
@@ -897,8 +967,15 @@ xlog_recover_print_attri(
region++;
printf(_("ATTRI: newvalue len:%u\n"), new_value_len);
print_or_dump((char *)item->ri_buf[region].i_addr, len);
+ new_value_ptr = item->ri_buf[region].i_addr;
}
+ if (src_f->alfi_attr_filter & XFS_ATTR_PARENT)
+ dump_pptr_update(name_ptr, name_len,
+ new_name_ptr, new_name_len,
+ value_ptr, value_len,
+ new_value_ptr, new_value_len);
+
out:
free(f);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 10/24] xfs_spaceman: report file paths
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (8 preceding siblings ...)
2024-07-30 1:21 ` [PATCH 09/24] xfs_logprint: decode parent pointers in ATTRI items fully Darrick J. Wong
@ 2024-07-30 1:21 ` Darrick J. Wong
2024-07-30 1:21 ` [PATCH 11/24] xfs_scrub: use parent pointers when possible to report file operations Darrick J. Wong
` (13 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:21 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Teach the health command to report file paths when possible.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man8/xfs_spaceman.8 | 7 +++++-
spaceman/Makefile | 16 +++++++++++---
spaceman/file.c | 7 ++++++
spaceman/health.c | 53 ++++++++++++++++++++++++++++++++++++++---------
spaceman/space.h | 3 +++
5 files changed, 71 insertions(+), 15 deletions(-)
diff --git a/man/man8/xfs_spaceman.8 b/man/man8/xfs_spaceman.8
index ece840d73..0d299132a 100644
--- a/man/man8/xfs_spaceman.8
+++ b/man/man8/xfs_spaceman.8
@@ -91,7 +91,7 @@ The output will have the same format that
.BR "xfs_info" "(8)"
prints when querying a filesystem.
.TP
-.BI "health [ \-a agno] [ \-c ] [ \-f ] [ \-i inum ] [ \-q ] [ paths ]"
+.BI "health [ \-a agno] [ \-c ] [ \-f ] [ \-i inum ] [ \-n ] [ \-q ] [ paths ]"
Reports the health of the given group of filesystem metadata.
.RS 1.0i
.PD 0
@@ -111,6 +111,11 @@ Report on the health of metadata that affect the entire filesystem.
.B \-i inum
Report on the health of a specific inode.
.TP
+.B \-n
+When reporting on the health of a file, try to report the full file path,
+if possible.
+This option is disabled by default to minimize runtime.
+.TP
.B \-q
Report only unhealthy metadata.
.TP
diff --git a/spaceman/Makefile b/spaceman/Makefile
index 1f048d54a..358db9edf 100644
--- a/spaceman/Makefile
+++ b/spaceman/Makefile
@@ -6,12 +6,20 @@ TOPDIR = ..
include $(TOPDIR)/include/builddefs
LTCOMMAND = xfs_spaceman
-HFILES = init.h space.h
-CFILES = info.c init.c file.c health.c prealloc.c trim.c
+HFILES = \
+ init.h \
+ space.h
+CFILES = \
+ file.c \
+ health.c \
+ info.c \
+ init.c \
+ prealloc.c \
+ trim.c
LSRCFILES = xfs_info.sh
-LLDLIBS = $(LIBXCMD) $(LIBFROG)
-LTDEPENDENCIES = $(LIBXCMD) $(LIBFROG)
+LLDLIBS = $(LIBHANDLE) $(LIBXCMD) $(LIBFROG)
+LTDEPENDENCIES = $(LIBHANDLE) $(LIBXCMD) $(LIBFROG)
LLDFLAGS = -static
ifeq ($(ENABLE_EDITLINE),yes)
diff --git a/spaceman/file.c b/spaceman/file.c
index eec7ee9f4..850688ace 100644
--- a/spaceman/file.c
+++ b/spaceman/file.c
@@ -14,6 +14,7 @@
#include "libfrog/paths.h"
#include "libfrog/fsgeom.h"
#include "space.h"
+#include "handle.h"
static cmdinfo_t print_cmd;
@@ -106,6 +107,12 @@ addfile(
file->name = filename;
memcpy(&file->xfd, xfd, sizeof(struct xfs_fd));
memcpy(&file->fs_path, fs_path, sizeof(file->fs_path));
+
+ /* Try to capture a fs handle for reporting paths. */
+ file->fshandle = NULL;
+ file->fshandle_len = 0;
+ path_to_fshandle(filename, &file->fshandle, &file->fshandle_len);
+
return 0;
}
diff --git a/spaceman/health.c b/spaceman/health.c
index 88b12c0b0..6722babf5 100644
--- a/spaceman/health.c
+++ b/spaceman/health.c
@@ -13,11 +13,13 @@
#include "libfrog/fsgeom.h"
#include "libfrog/bulkstat.h"
#include "space.h"
+#include "libfrog/getparents.h"
static cmdinfo_t health_cmd;
static unsigned long long reported;
static bool comprehensive;
static bool quiet;
+static bool report_paths;
static bool has_realtime(const struct xfs_fsop_geom *g)
{
@@ -265,6 +267,38 @@ report_file_health(
#define BULKSTAT_NR (128)
+static void
+report_inode(
+ const struct xfs_bulkstat *bs)
+{
+ char descr[PATH_MAX];
+ int ret;
+
+ if (report_paths && file->fshandle &&
+ (file->xfd.fsgeom.flags & XFS_FSOP_GEOM_FLAGS_PARENT)) {
+ struct xfs_handle handle;
+
+ memcpy(&handle.ha_fsid, file->fshandle, sizeof(handle.ha_fsid));
+ handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
+ sizeof(handle.ha_fid.fid_len);
+ handle.ha_fid.fid_pad = 0;
+ handle.ha_fid.fid_ino = bs->bs_ino;
+ handle.ha_fid.fid_gen = bs->bs_gen;
+
+ ret = handle_to_path(&handle, sizeof(struct xfs_handle), 0,
+ descr, sizeof(descr) - 1);
+ if (ret)
+ goto report_inum;
+
+ goto report_status;
+ }
+
+report_inum:
+ snprintf(descr, sizeof(descr) - 1, _("inode %"PRIu64), bs->bs_ino);
+report_status:
+ report_sick(descr, inode_flags, bs->bs_sick, bs->bs_checked);
+}
+
/*
* Report on all files' health for a given @agno. If @agno is NULLAGNUMBER,
* report on all files in the filesystem.
@@ -274,7 +308,6 @@ report_bulkstat_health(
xfs_agnumber_t agno)
{
struct xfs_bulkstat_req *breq;
- char descr[256];
uint32_t i;
int error;
@@ -292,13 +325,8 @@ report_bulkstat_health(
error = -xfrog_bulkstat(&file->xfd, breq);
if (error)
break;
- for (i = 0; i < breq->hdr.ocount; i++) {
- snprintf(descr, sizeof(descr) - 1, _("inode %"PRIu64),
- breq->bulkstat[i].bs_ino);
- report_sick(descr, inode_flags,
- breq->bulkstat[i].bs_sick,
- breq->bulkstat[i].bs_checked);
- }
+ for (i = 0; i < breq->hdr.ocount; i++)
+ report_inode(&breq->bulkstat[i]);
} while (breq->hdr.ocount > 0);
if (error)
@@ -308,7 +336,7 @@ report_bulkstat_health(
return error;
}
-#define OPT_STRING ("a:cfi:q")
+#define OPT_STRING ("a:cfi:nq")
/* Report on health problems in XFS filesystem. */
static int
@@ -323,6 +351,7 @@ health_f(
int ret;
reported = 0;
+ report_paths = false;
if (file->xfd.fsgeom.version != XFS_FSOP_GEOM_VERSION_V5) {
perror("health");
@@ -358,6 +387,9 @@ health_f(
return 1;
}
break;
+ case 'n':
+ report_paths = true;
+ break;
case 'q':
quiet = true;
break;
@@ -445,6 +477,7 @@ health_help(void)
" -c -- Report on the health of all inodes.\n"
" -f -- Report health of the overall filesystem.\n"
" -i inum -- Report health of a given inode number.\n"
+" -n -- Try to report file names.\n"
" -q -- Only report unhealthy metadata.\n"
" paths -- Report health of the given file path.\n"
"\n"));
@@ -456,7 +489,7 @@ static cmdinfo_t health_cmd = {
.cfunc = health_f,
.argmin = 0,
.argmax = -1,
- .args = "[-a agno] [-c] [-f] [-i inum] [-q] [paths]",
+ .args = "[-a agno] [-c] [-f] [-i inum] [-n] [-q] [paths]",
.flags = CMD_FLAG_ONESHOT,
.help = health_help,
};
diff --git a/spaceman/space.h b/spaceman/space.h
index 723209edd..28fa35a30 100644
--- a/spaceman/space.h
+++ b/spaceman/space.h
@@ -10,6 +10,9 @@ struct fileio {
struct xfs_fd xfd; /* XFS runtime support context */
struct fs_path fs_path; /* XFS path information */
char *name; /* file name at time of open */
+
+ void *fshandle;
+ size_t fshandle_len;
};
extern struct fileio *filetable; /* open file table */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 11/24] xfs_scrub: use parent pointers when possible to report file operations
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (9 preceding siblings ...)
2024-07-30 1:21 ` [PATCH 10/24] xfs_spaceman: report file paths Darrick J. Wong
@ 2024-07-30 1:21 ` Darrick J. Wong
2024-07-30 1:21 ` [PATCH 12/24] xfs_scrub: use parent pointers to report lost file data Darrick J. Wong
` (12 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:21 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
If parent pointers are available, use them to supply file paths when
doing things to files, instead of merely printing the inode number.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/common.c | 41 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/scrub/common.c b/scrub/common.c
index aca596487..f86546556 100644
--- a/scrub/common.c
+++ b/scrub/common.c
@@ -9,6 +9,7 @@
#include <syslog.h>
#include "platform_defs.h"
#include "libfrog/paths.h"
+#include "libfrog/getparents.h"
#include "xfs_scrub.h"
#include "common.h"
#include "progress.h"
@@ -405,19 +406,55 @@ scrub_render_ino_descr(
...)
{
va_list args;
+ size_t pathlen = 0;
uint32_t agno;
uint32_t agino;
int ret;
+ if (ctx->mnt.fsgeom.flags & XFS_FSOP_GEOM_FLAGS_PARENT) {
+ struct xfs_handle handle;
+
+ memcpy(&handle.ha_fsid, ctx->fshandle, sizeof(handle.ha_fsid));
+ handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
+ sizeof(handle.ha_fid.fid_len);
+ handle.ha_fid.fid_pad = 0;
+ handle.ha_fid.fid_ino = ino;
+ handle.ha_fid.fid_gen = gen;
+
+ ret = handle_to_path(&handle, sizeof(struct xfs_handle), 4096,
+ buf, buflen);
+ if (ret)
+ goto report_inum;
+
+ /*
+ * Leave at least 16 bytes for the description of what went
+ * wrong. If we can't do that, we'll use the inode number.
+ */
+ pathlen = strlen(buf);
+ if (pathlen >= buflen - 16)
+ goto report_inum;
+
+ if (format) {
+ buf[pathlen] = ' ';
+ buf[pathlen + 1] = 0;
+ pathlen++;
+ }
+
+ goto report_format;
+ }
+
+report_inum:
agno = cvt_ino_to_agno(&ctx->mnt, ino);
agino = cvt_ino_to_agino(&ctx->mnt, ino);
ret = snprintf(buf, buflen, _("inode %"PRIu64" (%"PRIu32"/%"PRIu32")%s"),
ino, agno, agino, format ? " " : "");
if (ret < 0 || ret >= buflen || format == NULL)
return ret;
+ pathlen = ret;
+report_format:
va_start(args, format);
- ret += vsnprintf(buf + ret, buflen - ret, format, args);
+ pathlen += vsnprintf(buf + pathlen, buflen - pathlen, format, args);
va_end(args);
- return ret;
+ return pathlen;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 12/24] xfs_scrub: use parent pointers to report lost file data
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (10 preceding siblings ...)
2024-07-30 1:21 ` [PATCH 11/24] xfs_scrub: use parent pointers when possible to report file operations Darrick J. Wong
@ 2024-07-30 1:21 ` Darrick J. Wong
2024-07-30 1:22 ` [PATCH 13/24] xfs_db: report parent pointers in version command Darrick J. Wong
` (11 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:21 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
If parent pointers are enabled, compute the path to the file while we're
doing the fsmap scan and report that, instead of walking the entire
directory tree to print the paths of the (hopefully few) files that lost
data.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase6.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 63 insertions(+), 12 deletions(-)
diff --git a/scrub/phase6.c b/scrub/phase6.c
index 193d3b4e9..a61853019 100644
--- a/scrub/phase6.c
+++ b/scrub/phase6.c
@@ -22,6 +22,7 @@
#include "spacemap.h"
#include "vfs.h"
#include "common.h"
+#include "libfrog/bulkstat.h"
/*
* Phase 6: Verify data file integrity.
@@ -381,6 +382,24 @@ report_dirent_loss(
return error;
}
+struct ioerr_filerange {
+ uint64_t physical;
+ uint64_t length;
+};
+
+/*
+ * If reverse mapping and parent pointers are enabled, we can map media errors
+ * directly back to a filename and a file position without needing to walk the
+ * directory tree.
+ */
+static inline bool
+can_use_pptrs(
+ const struct scrub_ctx *ctx)
+{
+ return (ctx->mnt.fsgeom.flags & XFS_FSOP_GEOM_FLAGS_PARENT) &&
+ (ctx->mnt.fsgeom.flags & XFS_FSOP_GEOM_FLAGS_RMAPBT);
+}
+
/* Use a fsmap to report metadata lost to a media error. */
static int
report_ioerr_fsmap(
@@ -389,16 +408,18 @@ report_ioerr_fsmap(
void *arg)
{
const char *type;
+ struct xfs_bulkstat bs = { };
char buf[DESCR_BUFSZ];
- uint64_t err_physical = *(uint64_t *)arg;
+ struct ioerr_filerange *fr = arg;
uint64_t err_off;
+ int ret;
/* Don't care about unwritten extents. */
if (map->fmr_flags & FMR_OF_PREALLOC)
return 0;
- if (err_physical > map->fmr_physical)
- err_off = err_physical - map->fmr_physical;
+ if (fr->physical > map->fmr_physical)
+ err_off = fr->physical - map->fmr_physical;
else
err_off = 0;
@@ -421,23 +442,43 @@ report_ioerr_fsmap(
}
}
+ if (can_use_pptrs(ctx)) {
+ ret = -xfrog_bulkstat_single(&ctx->mnt, map->fmr_owner, 0, &bs);
+ if (ret)
+ str_liberror(ctx, ret,
+ _("bulkstat for media error report"));
+ }
+
/* Report extent maps */
if (map->fmr_flags & FMR_OF_EXTENT_MAP) {
bool attr = (map->fmr_flags & FMR_OF_ATTR_FORK);
scrub_render_ino_descr(ctx, buf, DESCR_BUFSZ,
- map->fmr_owner, 0, " %s",
+ map->fmr_owner, bs.bs_gen, " %s",
attr ? _("extended attribute") :
_("file data"));
str_corrupt(ctx, buf, _("media error in extent map"));
}
/*
- * XXX: If we had a getparent() call we could report IO errors
- * efficiently. Until then, we'll have to scan the dir tree
- * to find the bad file's pathname.
+ * If directory parent pointers are available, use that to find the
+ * pathname to a file, and report that path as having lost its
+ * extended attributes, or the precise offset of the lost file data.
*/
+ if (!can_use_pptrs(ctx))
+ return 0;
+ scrub_render_ino_descr(ctx, buf, DESCR_BUFSZ, map->fmr_owner,
+ bs.bs_gen, NULL);
+
+ if (map->fmr_flags & FMR_OF_ATTR_FORK) {
+ str_corrupt(ctx, buf, _("media error in extended attributes"));
+ return 0;
+ }
+
+ str_unfixable_error(ctx, buf,
+ _("media error at data offset %llu length %llu."),
+ err_off, fr->length);
return 0;
}
@@ -452,6 +493,10 @@ report_ioerr(
void *arg)
{
struct fsmap keys[2];
+ struct ioerr_filerange fr = {
+ .physical = start,
+ .length = length,
+ };
struct disk_ioerr_report *dioerr = arg;
dev_t dev;
@@ -467,7 +512,7 @@ report_ioerr(
(keys + 1)->fmr_offset = ULLONG_MAX;
(keys + 1)->fmr_flags = UINT_MAX;
return -scrub_iterate_fsmap(dioerr->ctx, keys, report_ioerr_fsmap,
- &start);
+ &fr);
}
/* Report all the media errors found on a disk. */
@@ -511,10 +556,16 @@ report_all_media_errors(
return ret;
}
- /* Scan the directory tree to get file paths. */
- ret = scan_fs_tree(ctx, report_dir_loss, report_dirent_loss, vs);
- if (ret)
- return ret;
+ /*
+ * Scan the directory tree to get file paths if we didn't already use
+ * directory parent pointers to report the loss.
+ */
+ if (!can_use_pptrs(ctx)) {
+ ret = scan_fs_tree(ctx, report_dir_loss, report_dirent_loss,
+ vs);
+ if (ret)
+ return ret;
+ }
/* Scan for unlinked files. */
return scrub_scan_all_inodes(ctx, report_inode_loss, vs);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 13/24] xfs_db: report parent pointers in version command
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (11 preceding siblings ...)
2024-07-30 1:21 ` [PATCH 12/24] xfs_scrub: use parent pointers to report lost file data Darrick J. Wong
@ 2024-07-30 1:22 ` Darrick J. Wong
2024-07-30 1:22 ` [PATCH 14/24] xfs_db: report parent bit on xattrs Darrick J. Wong
` (10 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:22 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Report the presents of PARENT pointers from the version subcommand.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/sb.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/db/sb.c b/db/sb.c
index c39011634..7836384a1 100644
--- a/db/sb.c
+++ b/db/sb.c
@@ -708,6 +708,8 @@ version_string(
strcat(s, ",NREXT64");
if (xfs_has_exchange_range(mp))
strcat(s, ",EXCHANGE");
+ if (xfs_has_parent(mp))
+ strcat(s, ",PARENT");
return s;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 14/24] xfs_db: report parent bit on xattrs
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (12 preceding siblings ...)
2024-07-30 1:22 ` [PATCH 13/24] xfs_db: report parent pointers in version command Darrick J. Wong
@ 2024-07-30 1:22 ` Darrick J. Wong
2024-07-30 1:22 ` [PATCH 15/24] xfs_db: report parent pointers embedded in xattrs Darrick J. Wong
` (9 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:22 UTC (permalink / raw)
To: djwong, cem
Cc: Allison Henderson, Christoph Hellwig, linux-xfs, catherine.hoang,
allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Display the parent bit on xattr keys
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/attr.c | 3 +++
db/attrshort.c | 3 +++
2 files changed, 6 insertions(+)
diff --git a/db/attr.c b/db/attr.c
index de68d6276..3252f3886 100644
--- a/db/attr.c
+++ b/db/attr.c
@@ -82,6 +82,9 @@ const field_t attr_leaf_entry_flds[] = {
{ "local", FLDT_UINT1,
OI(LEOFF(flags) + bitsz(uint8_t) - XFS_ATTR_LOCAL_BIT - 1), C1, 0,
TYP_NONE },
+ { "parent", FLDT_UINT1,
+ OI(LEOFF(flags) + bitsz(uint8_t) - XFS_ATTR_PARENT_BIT - 1), C1, 0,
+ TYP_NONE },
{ "pad2", FLDT_UINT8X, OI(LEOFF(pad2)), C1, FLD_SKIPALL, TYP_NONE },
{ NULL }
};
diff --git a/db/attrshort.c b/db/attrshort.c
index 7c386d46f..978f58d67 100644
--- a/db/attrshort.c
+++ b/db/attrshort.c
@@ -43,6 +43,9 @@ const field_t attr_sf_entry_flds[] = {
{ "secure", FLDT_UINT1,
OI(EOFF(flags) + bitsz(uint8_t) - XFS_ATTR_SECURE_BIT - 1), C1, 0,
TYP_NONE },
+ { "parent", FLDT_UINT1,
+ OI(EOFF(flags) + bitsz(uint8_t) - XFS_ATTR_PARENT_BIT - 1), C1, 0,
+ TYP_NONE },
{ "name", FLDT_CHARNS, OI(EOFF(nameval)), attr_sf_entry_name_count,
FLD_COUNT, TYP_NONE },
{ "value", FLDT_CHARNS, attr_sf_entry_value_offset,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 15/24] xfs_db: report parent pointers embedded in xattrs
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (13 preceding siblings ...)
2024-07-30 1:22 ` [PATCH 14/24] xfs_db: report parent bit on xattrs Darrick J. Wong
@ 2024-07-30 1:22 ` Darrick J. Wong
2024-07-30 1:23 ` [PATCH 16/24] xfs_db: obfuscate dirent and parent pointer names consistently Darrick J. Wong
` (8 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:22 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Decode the parent pointer inode, generation, and name fields if the
parent pointer passes basic validation checks.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/attr.c | 28 ++++++++++++++++++++++++++++
db/attrshort.c | 24 ++++++++++++++++++++++++
db/field.c | 10 ++++++++++
db/field.h | 3 +++
4 files changed, 65 insertions(+)
diff --git a/db/attr.c b/db/attr.c
index 3252f3886..3b556c43d 100644
--- a/db/attr.c
+++ b/db/attr.c
@@ -33,6 +33,8 @@ static int attr_remote_data_count(void *obj, int startoff);
static int attr3_remote_hdr_count(void *obj, int startoff);
static int attr3_remote_data_count(void *obj, int startoff);
+static int attr_leaf_value_pptr_count(void *obj, int startoff);
+
const field_t attr_hfld[] = {
{ "", FLDT_ATTR, OI(0), C1, 0, TYP_NONE },
{ NULL }
@@ -118,6 +120,8 @@ const field_t attr_leaf_name_flds[] = {
attr_leaf_name_local_count, FLD_COUNT, TYP_NONE },
{ "name", FLDT_CHARNS, OI(LNOFF(nameval)),
attr_leaf_name_local_name_count, FLD_COUNT, TYP_NONE },
+ { "parent_dir", FLDT_PARENT_REC, attr_leaf_name_local_value_offset,
+ attr_leaf_value_pptr_count, FLD_COUNT | FLD_OFFSET, TYP_NONE },
{ "value", FLDT_CHARNS, attr_leaf_name_local_value_offset,
attr_leaf_name_local_value_count, FLD_COUNT|FLD_OFFSET, TYP_NONE },
{ "valueblk", FLDT_UINT32X, OI(LVOFF(valueblk)),
@@ -307,6 +311,8 @@ __attr_leaf_name_local_value_count(
if (!(e->flags & XFS_ATTR_LOCAL))
return 0;
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) == XFS_ATTR_PARENT)
+ return 0;
l = xfs_attr3_leaf_name_local(leaf, i);
return be16_to_cpu(l->valuelen);
@@ -514,6 +520,28 @@ attr3_remote_hdr_count(
return be32_to_cpu(node->rm_magic) == XFS_ATTR3_RMT_MAGIC;
}
+static int
+__leaf_pptr_count(
+ struct xfs_attr_leafblock *leaf,
+ struct xfs_attr_leaf_entry *e,
+ int i)
+{
+ if (!(e->flags & XFS_ATTR_LOCAL))
+ return 0;
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) != XFS_ATTR_PARENT)
+ return 0;
+
+ return 1;
+}
+
+static int
+attr_leaf_value_pptr_count(
+ void *obj,
+ int startoff)
+{
+ return attr_leaf_entry_walk(obj, startoff, __leaf_pptr_count);
+}
+
int
attr_size(
void *obj,
diff --git a/db/attrshort.c b/db/attrshort.c
index 978f58d67..7e5c94ca5 100644
--- a/db/attrshort.c
+++ b/db/attrshort.c
@@ -18,6 +18,8 @@ static int attr_sf_entry_value_offset(void *obj, int startoff, int idx);
static int attr_shortform_list_count(void *obj, int startoff);
static int attr_shortform_list_offset(void *obj, int startoff, int idx);
+static int attr_sf_entry_pptr_count(void *obj, int startoff);
+
const field_t attr_shortform_flds[] = {
{ "hdr", FLDT_ATTR_SF_HDR, OI(0), C1, 0, TYP_NONE },
{ "list", FLDT_ATTR_SF_ENTRY, attr_shortform_list_offset,
@@ -48,6 +50,8 @@ const field_t attr_sf_entry_flds[] = {
TYP_NONE },
{ "name", FLDT_CHARNS, OI(EOFF(nameval)), attr_sf_entry_name_count,
FLD_COUNT, TYP_NONE },
+ { "parent_dir", FLDT_PARENT_REC, attr_sf_entry_value_offset,
+ attr_sf_entry_pptr_count, FLD_COUNT | FLD_OFFSET, TYP_NONE },
{ "value", FLDT_CHARNS, attr_sf_entry_value_offset,
attr_sf_entry_value_count, FLD_COUNT|FLD_OFFSET, TYP_NONE },
{ NULL }
@@ -92,6 +96,10 @@ attr_sf_entry_value_count(
ASSERT(bitoffs(startoff) == 0);
e = (struct xfs_attr_sf_entry *)((char *)obj + byteize(startoff));
+
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) == XFS_ATTR_PARENT)
+ return 0;
+
return e->valuelen;
}
@@ -159,3 +167,19 @@ attrshort_size(
e = xfs_attr_sf_nextentry(e);
return bitize((int)((char *)e - (char *)hdr));
}
+
+static int
+attr_sf_entry_pptr_count(
+ void *obj,
+ int startoff)
+{
+ struct xfs_attr_sf_entry *e;
+
+ ASSERT(bitoffs(startoff) == 0);
+ e = (struct xfs_attr_sf_entry *)((char *)obj + byteize(startoff));
+
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) != XFS_ATTR_PARENT)
+ return 0;
+
+ return 1;
+}
diff --git a/db/field.c b/db/field.c
index a3e47ee81..a61ccc9ef 100644
--- a/db/field.c
+++ b/db/field.c
@@ -24,6 +24,14 @@
#include "dir2sf.h"
#include "symlink.h"
+#define PPOFF(f) bitize(offsetof(struct xfs_parent_rec, f))
+const field_t parent_flds[] = {
+ { "inumber", FLDT_INO, OI(PPOFF(p_ino)), C1, 0, TYP_INODE },
+ { "gen", FLDT_UINT32D, OI(PPOFF(p_gen)), C1, 0, TYP_NONE },
+ { NULL }
+};
+#undef PPOFF
+
const ftattr_t ftattrtab[] = {
{ FLDT_AGBLOCK, "agblock", fp_num, "%u", SI(bitsz(xfs_agblock_t)),
FTARG_DONULL, fa_agblock, NULL },
@@ -384,6 +392,8 @@ const ftattr_t ftattrtab[] = {
{ FLDT_UINT8X, "uint8x", fp_num, "%#x", SI(bitsz(uint8_t)), 0, NULL,
NULL },
{ FLDT_UUID, "uuid", fp_uuid, NULL, SI(bitsz(uuid_t)), 0, NULL, NULL },
+ { FLDT_PARENT_REC, "parent", NULL, (char *)parent_flds,
+ SI(bitsz(struct xfs_parent_rec)), 0, NULL, parent_flds },
{ FLDT_ZZZ, NULL }
};
diff --git a/db/field.h b/db/field.h
index 634742a57..b1bfdbed1 100644
--- a/db/field.h
+++ b/db/field.h
@@ -187,6 +187,9 @@ typedef enum fldt {
FLDT_UINT8O,
FLDT_UINT8X,
FLDT_UUID,
+
+ FLDT_PARENT_REC,
+
FLDT_ZZZ /* mark last entry */
} fldt_t;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 16/24] xfs_db: obfuscate dirent and parent pointer names consistently
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (14 preceding siblings ...)
2024-07-30 1:22 ` [PATCH 15/24] xfs_db: report parent pointers embedded in xattrs Darrick J. Wong
@ 2024-07-30 1:23 ` Darrick J. Wong
2024-07-30 1:23 ` [PATCH 17/24] libxfs: export attr3_leaf_hdr_from_disk via libxfs_api_defs.h Darrick J. Wong
` (7 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:23 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
When someone wants to perform an obfuscated metadump of a filesystem
where parent pointers are enabled, we have to use the *exact* same
obfuscated name for both the directory entry and the parent pointer.
Create a name remapping table so that when we obfuscate a dirent name or
a parent pointer name, we can apply the same obfuscation when we find
the corresponding parent pointer or dirent.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/metadump.c | 310 ++++++++++++++++++++++++++++++++++++++++++++--
libxfs/libxfs_api_defs.h | 1
2 files changed, 299 insertions(+), 12 deletions(-)
diff --git a/db/metadump.c b/db/metadump.c
index c1bf5d002..e95238fb0 100644
--- a/db/metadump.c
+++ b/db/metadump.c
@@ -21,6 +21,14 @@
#include "dir2.h"
#include "obfuscate.h"
+#undef REMAP_DEBUG
+
+#ifdef REMAP_DEBUG
+# define remap_debug printf
+#else
+# define remap_debug(...) ((void)0)
+#endif
+
#define DEFAULT_MAX_EXT_SIZE XFS_MAX_BMBT_EXTLEN
/* copy all metadata structures to/from a file */
@@ -719,6 +727,111 @@ nametable_add(xfs_dahash_t hash, int namelen, unsigned char *name)
return ent;
}
+/*
+ * Obfuscated name remapping table for parent pointer-enabled filesystems.
+ * When this feature is enabled, we have to maintain consistency between the
+ * names that appears in the dirent and the corresponding parent pointer.
+ */
+
+struct remap_ent {
+ struct remap_ent *next;
+ xfs_ino_t dir_ino;
+ xfs_dahash_t namehash;
+ uint8_t namelen;
+
+ uint8_t names[];
+};
+
+static inline uint8_t *remap_ent_before(struct remap_ent *ent)
+{
+ return &ent->names[0];
+}
+
+static inline uint8_t *remap_ent_after(struct remap_ent *ent)
+{
+ return &ent->names[ent->namelen];
+}
+
+#define REMAP_TABLE_SIZE 4096
+
+static struct remap_ent *remaptable[REMAP_TABLE_SIZE];
+
+static void
+remaptable_clear(void)
+{
+ int i;
+ struct remap_ent *ent, *next;
+
+ for (i = 0; i < REMAP_TABLE_SIZE; i++) {
+ ent = remaptable[i];
+
+ while (ent) {
+ next = ent->next;
+ free(ent);
+ ent = next;
+ }
+ }
+}
+
+/* Try to find a remapping table entry. */
+static struct remap_ent *
+remaptable_find(
+ xfs_ino_t dir_ino,
+ xfs_dahash_t namehash,
+ const unsigned char *name,
+ unsigned int namelen)
+{
+ struct remap_ent *ent = remaptable[namehash % REMAP_TABLE_SIZE];
+
+ remap_debug("REMAP FIND: 0x%lx hash 0x%x '%.*s'\n",
+ dir_ino, namehash, namelen, name);
+
+ while (ent) {
+ remap_debug("REMAP ENT: 0x%lx hash 0x%x '%.*s'\n",
+ ent->dir_ino, ent->namehash, ent->namelen,
+ remap_ent_before(ent));
+
+ if (ent->dir_ino == dir_ino &&
+ ent->namehash == namehash &&
+ ent->namelen == namelen &&
+ !memcmp(remap_ent_before(ent), name, namelen))
+ return ent;
+ ent = ent->next;
+ }
+
+ return NULL;
+}
+
+/* Remember the remapping for a particular dirent that we obfuscated. */
+static struct remap_ent *
+remaptable_add(
+ xfs_ino_t dir_ino,
+ xfs_dahash_t namehash,
+ const unsigned char *old_name,
+ unsigned int namelen,
+ const unsigned char *new_name)
+{
+ struct remap_ent *ent;
+
+ ent = malloc(sizeof(struct remap_ent) + (namelen * 2));
+ if (!ent)
+ return NULL;
+
+ ent->dir_ino = dir_ino;
+ ent->namehash = namehash;
+ ent->namelen = namelen;
+ memcpy(remap_ent_before(ent), old_name, namelen);
+ memcpy(remap_ent_after(ent), new_name, namelen);
+ ent->next = remaptable[namehash % REMAP_TABLE_SIZE];
+
+ remaptable[namehash % REMAP_TABLE_SIZE] = ent;
+
+ remap_debug("REMAP ADD: 0x%lx hash 0x%x '%.*s' -> '%.*s'\n",
+ dir_ino, namehash, namelen, old_name, namelen,
+ new_name);
+ return ent;
+}
+
#define ORPHANAGE "lost+found"
#define ORPHANAGE_LEN (sizeof (ORPHANAGE) - 1)
@@ -844,6 +957,7 @@ generate_obfuscated_name(
int namelen,
unsigned char *name)
{
+ unsigned char *orig_name = NULL;
xfs_dahash_t hash;
/*
@@ -865,8 +979,37 @@ generate_obfuscated_name(
name++;
/* Obfuscate the name (if possible) */
-
hash = dirattr_hashname(ino != 0, name, namelen);
+
+ /*
+ * If we're obfuscating a dirent name on a pptrs filesystem, see if we
+ * already processed the parent pointer and use the same name.
+ */
+ if (xfs_has_parent(mp) && ino) {
+ struct remap_ent *remap;
+
+ remap = remaptable_find(metadump.cur_ino, hash, name, namelen);
+ if (remap) {
+ remap_debug("found obfuscated dir 0x%lx '%.*s' -> 0x%lx -> '%.*s' \n",
+ cur_ino, namelen,
+ remap_ent_before(remap), ino, namelen,
+ remap_ent_after(remap));
+ memcpy(name, remap_ent_after(remap), namelen);
+ return;
+ }
+
+ /*
+ * If we haven't procesed this dirent name before, save the
+ * old name for a remap table entry. Obfuscate the name.
+ */
+ orig_name = malloc(namelen);
+ if (!orig_name) {
+ orig_name = name;
+ goto add_remap;
+ }
+ memcpy(orig_name, name, namelen);
+ }
+
obfuscate_name(hash, namelen, name, ino != 0);
ASSERT(hash == dirattr_hashname(ino != 0, name, namelen));
@@ -891,6 +1034,26 @@ generate_obfuscated_name(
"in dir inode %llu\n",
(unsigned long long) ino,
(unsigned long long) metadump.cur_ino);
+
+ /*
+ * We've obfuscated a name in the directory entry. Remember this
+ * remapping for when we come across the parent pointer later.
+ */
+ if (!orig_name)
+ return;
+
+add_remap:
+ remap_debug("obfuscating dir 0x%lx '%.*s' -> 0x%lx -> '%.*s' \n",
+ metadump.cur_ino, namelen, orig_name, ino, namelen,
+ name);
+
+ if (!remaptable_add(metadump.cur_ino, hash, orig_name, namelen, name))
+ print_warning("unable to record remapped dirent name for inode %llu "
+ "in dir inode %llu\n",
+ (unsigned long long) ino,
+ (unsigned long long) metadump.cur_ino);
+ if (orig_name && orig_name != name)
+ free(orig_name);
}
static void
@@ -1026,6 +1189,106 @@ process_sf_symlink(
memset(&buf[len], 0, XFS_DFORK_DSIZE(dip, mp) - len);
}
+/*
+ * Decide if we want to obfuscate this parent pointer. If we do, either find
+ * the obfuscated name that we created when we scanned the corresponding dirent
+ * and replace the name with that; or create a new obfuscated name for later
+ * use.
+ */
+static void
+maybe_obfuscate_pptr(
+ unsigned int attr_flags,
+ uint8_t *name,
+ int namelen,
+ const void *value,
+ int valuelen)
+{
+ unsigned char old_name[MAXNAMELEN];
+ struct remap_ent *remap;
+ xfs_dahash_t hash;
+ xfs_ino_t child_ino = metadump.cur_ino;
+ xfs_ino_t parent_ino;
+ int error;
+
+ if (!metadump.obfuscate)
+ return;
+
+ if (!(attr_flags & XFS_ATTR_PARENT))
+ return;
+
+ /* Do not obfuscate this parent pointer if it fails basic checks. */
+ error = -libxfs_parent_from_attr(mp, attr_flags, name, namelen, value,
+ valuelen, &parent_ino, NULL);
+ if (error)
+ return;
+ memcpy(old_name, name, namelen);
+
+ /*
+ * We don't obfuscate "lost+found" or any orphan files therein. When
+ * the name table is used for extended attributes, the inode number
+ * provided is 0, in which case we don't need to make this check.
+ */
+ metadump.cur_ino = parent_ino;
+ if (in_lost_found(child_ino, namelen, name)) {
+ metadump.cur_ino = child_ino;
+ return;
+ }
+ metadump.cur_ino = child_ino;
+
+ hash = dirattr_hashname(true, name, namelen);
+
+ /*
+ * If we already processed the dirent, use the same name for the parent
+ * pointer.
+ */
+ remap = remaptable_find(parent_ino, hash, name, namelen);
+ if (remap) {
+ remap_debug(
+ "found obfuscated pptr 0x%lx '%.*s' -> 0x%lx -> '%.*s' \n",
+ parent_ino, namelen, remap_ent_before(remap),
+ metadump.cur_ino, namelen,
+ remap_ent_after(remap));
+ memcpy(name, remap_ent_after(remap), namelen);
+ return;
+ }
+
+ /*
+ * Obfuscate the parent pointer name and remember this for later
+ * in case we encounter the dirent and need to reuse the name there.
+ */
+ obfuscate_name(hash, namelen, name, true);
+
+ remap_debug("obfuscated pptr 0x%lx '%.*s' -> 0x%lx -> '%.*s'\n",
+ parent_ino, namelen, old_name, metadump.cur_ino,
+ namelen, name);
+ if (!remaptable_add(parent_ino, hash, old_name, namelen, name))
+ print_warning(
+ "unable to record remapped pptr name for inode %llu in dir inode %llu\n",
+ (unsigned long long) metadump.cur_ino,
+ (unsigned long long) parent_ino);
+}
+
+static inline bool
+want_obfuscate_attr(
+ unsigned int nsp_flags,
+ const void *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen)
+{
+ if (!metadump.obfuscate)
+ return false;
+
+ /*
+ * If we didn't already obfuscate the parent pointer, it's probably
+ * corrupt. Leave it intact for analysis.
+ */
+ if (nsp_flags & XFS_ATTR_PARENT)
+ return false;
+
+ return true;
+}
+
static void
process_sf_attr(
struct xfs_dinode *dip)
@@ -1053,7 +1316,7 @@ process_sf_attr(
for (i = 0; (i < hdr->count) &&
((char *)asfep - (char *)hdr < ino_attr_size); i++) {
-
+ void *name, *value;
int namelen = asfep->namelen;
if (namelen == 0) {
@@ -1070,11 +1333,16 @@ process_sf_attr(
break;
}
- if (metadump.obfuscate) {
- generate_obfuscated_name(0, asfep->namelen,
- &asfep->nameval[0]);
- memset(&asfep->nameval[asfep->namelen], 'v',
- asfep->valuelen);
+ name = &asfep->nameval[0];
+ value = &asfep->nameval[asfep->namelen];
+
+ if (asfep->flags & XFS_ATTR_PARENT) {
+ maybe_obfuscate_pptr(asfep->flags, name, namelen,
+ value, asfep->valuelen);
+ } else if (want_obfuscate_attr(asfep->flags, name, namelen,
+ value, asfep->valuelen)) {
+ generate_obfuscated_name(0, asfep->namelen, name);
+ memset(value, 'v', asfep->valuelen);
}
asfep = (struct xfs_attr_sf_entry *)((char *)asfep +
@@ -1443,6 +1711,9 @@ process_attr_block(
break;
}
if (entry->flags & XFS_ATTR_LOCAL) {
+ void *name, *value;
+ unsigned int valuelen;
+
local = xfs_attr3_leaf_name_local(leaf, i);
if (local->namelen == 0) {
if (metadump.show_warnings)
@@ -1451,11 +1722,21 @@ process_attr_block(
(long long)metadump.cur_ino);
break;
}
- if (metadump.obfuscate) {
+
+ name = &local->nameval[0];
+ value = &local->nameval[local->namelen];
+ valuelen = be16_to_cpu(local->valuelen);
+
+ if (entry->flags & XFS_ATTR_PARENT) {
+ maybe_obfuscate_pptr(entry->flags, name,
+ local->namelen, value,
+ valuelen);
+ } else if (want_obfuscate_attr(entry->flags, name,
+ local->namelen, value,
+ valuelen)) {
generate_obfuscated_name(0, local->namelen,
- &local->nameval[0]);
- memset(&local->nameval[local->namelen], 'v',
- be16_to_cpu(local->valuelen));
+ name);
+ memset(value, 'v', valuelen);
}
/* zero from end of nameval[] to next name start */
nlen = local->namelen;
@@ -1474,7 +1755,11 @@ process_attr_block(
(long long)metadump.cur_ino);
break;
}
- if (metadump.obfuscate) {
+ if (entry->flags & XFS_ATTR_PARENT) {
+ /* do not obfuscate obviously busted pptr */
+ add_remote_vals(be32_to_cpu(remote->valueblk),
+ be32_to_cpu(remote->valuelen));
+ } else if (metadump.obfuscate) {
generate_obfuscated_name(0, remote->namelen,
&remote->name[0]);
add_remote_vals(be32_to_cpu(remote->valueblk),
@@ -3044,5 +3329,6 @@ metadump_f(
metadump.mdops->release();
out:
+ remaptable_clear();
return 0;
}
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index b7947591d..c3dde1511 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -193,6 +193,7 @@
#define xfs_parent_add libxfs_parent_add
#define xfs_parent_finish libxfs_parent_finish
#define xfs_parent_start libxfs_parent_start
+#define xfs_parent_from_attr libxfs_parent_from_attr
#define xfs_perag_get libxfs_perag_get
#define xfs_perag_hold libxfs_perag_hold
#define xfs_perag_put libxfs_perag_put
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 17/24] libxfs: export attr3_leaf_hdr_from_disk via libxfs_api_defs.h
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (15 preceding siblings ...)
2024-07-30 1:23 ` [PATCH 16/24] xfs_db: obfuscate dirent and parent pointer names consistently Darrick J. Wong
@ 2024-07-30 1:23 ` Darrick J. Wong
2024-07-30 1:23 ` [PATCH 18/24] xfs_db: add a parents command to list the parents of a file Darrick J. Wong
` (6 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:23 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Do the xfs -> libxfs switcheroo and cleanups separately so the next
patch doesn't become an even larger mess.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/attr.c | 2 +-
db/metadump.c | 2 +-
libxfs/libxfs_api_defs.h | 5 +++++
repair/attr_repair.c | 6 +++---
4 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/db/attr.c b/db/attr.c
index 3b556c43d..0b1f498e4 100644
--- a/db/attr.c
+++ b/db/attr.c
@@ -248,7 +248,7 @@ attr_leaf_entry_walk(
return 0;
off = byteize(startoff);
- xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
entries = xfs_attr3_leaf_entryp(leaf);
for (i = 0; i < leafhdr.count; i++) {
diff --git a/db/metadump.c b/db/metadump.c
index e95238fb0..424544f9f 100644
--- a/db/metadump.c
+++ b/db/metadump.c
@@ -1680,7 +1680,7 @@ process_attr_block(
}
/* Ok, it's a leaf - get header; accounts for crc & non-crc */
- xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &hdr, leaf);
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &hdr, leaf);
nentries = hdr.count;
if (nentries == 0 ||
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index c3dde1511..5713e5221 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -36,10 +36,13 @@
#define xfs_ascii_ci_hashname libxfs_ascii_ci_hashname
+#define xfs_attr3_leaf_hdr_from_disk libxfs_attr3_leaf_hdr_from_disk
+#define xfs_attr3_leaf_read libxfs_attr3_leaf_read
#define xfs_attr_check_namespace libxfs_attr_check_namespace
#define xfs_attr_get libxfs_attr_get
#define xfs_attr_hashname libxfs_attr_hashname
#define xfs_attr_hashval libxfs_attr_hashval
+#define xfs_attr_is_leaf libxfs_attr_is_leaf
#define xfs_attr_leaf_newentsize libxfs_attr_leaf_newentsize
#define xfs_attr_namecheck libxfs_attr_namecheck
#define xfs_attr_set libxfs_attr_set
@@ -103,6 +106,7 @@
#define xfs_compute_rextslog libxfs_compute_rextslog
#define xfs_create_space_res libxfs_create_space_res
#define xfs_da3_node_hdr_from_disk libxfs_da3_node_hdr_from_disk
+#define xfs_da3_node_read libxfs_da3_node_read
#define xfs_da_get_buf libxfs_da_get_buf
#define xfs_da_hashname libxfs_da_hashname
#define xfs_da_read_buf libxfs_da_read_buf
@@ -177,6 +181,7 @@
#define xfs_inobt_stage_cursor libxfs_inobt_stage_cursor
#define xfs_inode_from_disk libxfs_inode_from_disk
#define xfs_inode_from_disk_ts libxfs_inode_from_disk_ts
+#define xfs_inode_hasattr libxfs_inode_hasattr
#define xfs_inode_to_disk libxfs_inode_to_disk
#define xfs_inode_validate_cowextsize libxfs_inode_validate_cowextsize
#define xfs_inode_validate_extsize libxfs_inode_validate_extsize
diff --git a/repair/attr_repair.c b/repair/attr_repair.c
index 8321c9b67..2e97fd977 100644
--- a/repair/attr_repair.c
+++ b/repair/attr_repair.c
@@ -610,7 +610,7 @@ process_leaf_attr_block(
da_freemap_t *attr_freemap;
struct xfs_attr3_icleaf_hdr leafhdr;
- xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
clearit = usedbs = 0;
firstb = mp->m_sb.sb_blocksize;
stop = xfs_attr3_leaf_hdr_size(leaf);
@@ -849,7 +849,7 @@ process_leaf_attr_level(xfs_mount_t *mp,
}
leaf = bp->b_addr;
- xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
/* check magic number for leaf directory btree block */
if (!(leafhdr.magic == XFS_ATTR_LEAF_MAGIC ||
@@ -1052,7 +1052,7 @@ process_longform_leaf_root(
* check sibling pointers in leaf block or root block 0 before
* we have to release the btree block
*/
- xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, bp->b_addr);
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, bp->b_addr);
if (leafhdr.forw != 0 || leafhdr.back != 0) {
if (!no_modify) {
do_warn(
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 18/24] xfs_db: add a parents command to list the parents of a file
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (16 preceding siblings ...)
2024-07-30 1:23 ` [PATCH 17/24] libxfs: export attr3_leaf_hdr_from_disk via libxfs_api_defs.h Darrick J. Wong
@ 2024-07-30 1:23 ` Darrick J. Wong
2024-07-30 1:23 ` [PATCH 19/24] xfs_db: make attr_set and attr_remove handle parent pointers Darrick J. Wong
` (5 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:23 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Create a command to dump the parents of a file.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/namei.c | 323 +++++++++++++++++++++++++++++++++++++++++++++++++++++
man/man8/xfs_db.8 | 9 +
2 files changed, 332 insertions(+)
diff --git a/db/namei.c b/db/namei.c
index 41ccaa04b..46b4cacb5 100644
--- a/db/namei.c
+++ b/db/namei.c
@@ -596,6 +596,326 @@ static struct cmdinfo ls_cmd = {
.help = ls_help,
};
+static void
+pptr_emit(
+ struct xfs_inode *ip,
+ unsigned int attr_flags,
+ const uint8_t *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ xfs_ino_t parent_ino;
+ uint32_t parent_gen;
+ int error;
+
+ if (!(attr_flags & XFS_ATTR_PARENT))
+ return;
+
+ error = -libxfs_parent_from_attr(mp, attr_flags, name, namelen, value,
+ valuelen, &parent_ino, &parent_gen);
+ if (error)
+ return;
+
+ dbprintf("%18llu:0x%08x %3d %.*s\n", parent_ino, parent_gen, namelen,
+ namelen, name);
+}
+
+static int
+list_sf_pptrs(
+ struct xfs_inode *ip)
+{
+ struct xfs_attr_sf_hdr *hdr = ip->i_af.if_data;
+ struct xfs_attr_sf_entry *sfe;
+ unsigned int i;
+
+ sfe = libxfs_attr_sf_firstentry(hdr);
+ for (i = 0; i < hdr->count; i++) {
+ pptr_emit(ip, sfe->flags, sfe->nameval, sfe->namelen,
+ sfe->nameval + sfe->valuelen, sfe->valuelen);
+
+ sfe = xfs_attr_sf_nextentry(sfe);
+ }
+
+ return 0;
+}
+
+static void
+list_leaf_pptr_entries(
+ struct xfs_inode *ip,
+ struct xfs_buf *bp)
+{
+ struct xfs_attr3_icleaf_hdr ichdr;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_attr_leafblock *leaf = bp->b_addr;
+ struct xfs_attr_leaf_entry *entry;
+ unsigned int i;
+
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf);
+ entry = xfs_attr3_leaf_entryp(leaf);
+
+ for (i = 0; i < ichdr.count; entry++, i++) {
+ struct xfs_attr_leaf_name_local *name_loc;
+
+ /*
+ * Parent pointers cannot be remote values; don't bother
+ * decoding this xattr name.
+ */
+ if (!(entry->flags & XFS_ATTR_LOCAL))
+ continue;
+
+ name_loc = xfs_attr3_leaf_name_local(leaf, i);
+ pptr_emit(ip, entry->flags, name_loc->nameval,
+ name_loc->namelen,
+ name_loc->nameval + name_loc->namelen,
+ be16_to_cpu(name_loc->valuelen));
+ }
+}
+
+static int
+list_leaf_pptrs(
+ struct xfs_inode *ip)
+{
+ struct xfs_buf *leaf_bp;
+ int error;
+
+ error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino, 0, &leaf_bp);
+ if (error)
+ return error;
+
+ list_leaf_pptr_entries(ip, leaf_bp);
+ libxfs_trans_brelse(NULL, leaf_bp);
+ return 0;
+}
+
+static int
+find_leftmost_attr_leaf(
+ struct xfs_inode *ip,
+ struct xfs_buf **leaf_bpp)
+{
+ struct xfs_da3_icnode_hdr nodehdr;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_da_intnode *node;
+ struct xfs_da_node_entry *btree;
+ struct xfs_buf *bp;
+ xfs_dablk_t blkno = 0;
+ unsigned int expected_level = 0;
+ int error;
+
+ for (;;) {
+ uint16_t magic;
+
+ error = -libxfs_da3_node_read(NULL, ip, blkno, &bp,
+ XFS_ATTR_FORK);
+ if (error)
+ return error;
+
+ node = bp->b_addr;
+ magic = be16_to_cpu(node->hdr.info.magic);
+ if (magic == XFS_ATTR_LEAF_MAGIC ||
+ magic == XFS_ATTR3_LEAF_MAGIC)
+ break;
+
+ error = EFSCORRUPTED;
+ if (magic != XFS_DA_NODE_MAGIC &&
+ magic != XFS_DA3_NODE_MAGIC)
+ goto out_buf;
+
+ libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node);
+
+ if (nodehdr.count == 0 || nodehdr.level >= XFS_DA_NODE_MAXDEPTH)
+ goto out_buf;
+
+ /* Check the level from the root node. */
+ if (blkno == 0)
+ expected_level = nodehdr.level - 1;
+ else if (expected_level != nodehdr.level)
+ goto out_buf;
+ else
+ expected_level--;
+
+ /* Find the next level towards the leaves of the dabtree. */
+ btree = nodehdr.btree;
+ blkno = be32_to_cpu(btree->before);
+ libxfs_trans_brelse(NULL, bp);
+ }
+
+ error = EFSCORRUPTED;
+ if (expected_level != 0)
+ goto out_buf;
+
+ *leaf_bpp = bp;
+ return 0;
+
+out_buf:
+ libxfs_trans_brelse(NULL, bp);
+ return error;
+}
+
+static int
+list_node_pptrs(
+ struct xfs_inode *ip)
+{
+ struct xfs_attr3_icleaf_hdr leafhdr;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_attr_leafblock *leaf;
+ struct xfs_buf *leaf_bp;
+ int error;
+
+ error = find_leftmost_attr_leaf(ip, &leaf_bp);
+ if (error)
+ return error;
+
+ for (;;) {
+ list_leaf_pptr_entries(ip, leaf_bp);
+
+ /* Find the right sibling of this leaf block. */
+ leaf = leaf_bp->b_addr;
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
+ if (leafhdr.forw == 0)
+ goto out_leaf;
+
+ libxfs_trans_brelse(NULL, leaf_bp);
+
+ error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino,
+ leafhdr.forw, &leaf_bp);
+ if (error)
+ return error;
+ }
+
+out_leaf:
+ libxfs_trans_brelse(NULL, leaf_bp);
+ return error;
+}
+
+static int
+list_pptrs(
+ struct xfs_inode *ip)
+{
+ int error;
+
+ if (!libxfs_inode_hasattr(ip))
+ return 0;
+
+ if (ip->i_af.if_format == XFS_DINODE_FMT_LOCAL)
+ return list_sf_pptrs(ip);
+
+ /* attr functions require that the attr fork is loaded */
+ error = -libxfs_iread_extents(NULL, ip, XFS_ATTR_FORK);
+ if (error)
+ return error;
+
+ if (libxfs_attr_is_leaf(ip))
+ return list_leaf_pptrs(ip);
+
+ return list_node_pptrs(ip);
+}
+
+/* If the io cursor points to a file, list its parents. */
+static int
+parent_cur(
+ char *tag)
+{
+ struct xfs_inode *ip;
+ int error = 0;
+
+ if (!xfs_has_parent(mp))
+ return 0;
+
+ if (iocur_top->typ != &typtab[TYP_INODE])
+ return ENOTDIR;
+
+ error = -libxfs_iget(mp, NULL, iocur_top->ino, 0, &ip);
+ if (error)
+ return error;
+
+ /* List the parents of a file. */
+ if (tag)
+ dbprintf(_("%s:\n"), tag);
+
+ error = list_pptrs(ip);
+ if (error)
+ goto rele;
+
+rele:
+ libxfs_irele(ip);
+ return error;
+}
+
+static void
+parent_help(void)
+{
+ dbprintf(_(
+"\n"
+" List the parents of the currently selected file.\n"
+"\n"
+" Parent pointers will be listed in the format:\n"
+" inode_number:inode_gen ondisk_namehash:namehash name_length name\n"
+ ));
+}
+
+static int
+parent_f(
+ int argc,
+ char **argv)
+{
+ int c;
+ int error = 0;
+
+ while ((c = getopt(argc, argv, "")) != -1) {
+ switch (c) {
+ default:
+ ls_help();
+ return 0;
+ }
+ }
+
+ if (optind == argc) {
+ error = parent_cur(NULL);
+ if (error) {
+ dbprintf("%s\n", strerror(error));
+ exitcode = 1;
+ }
+
+ return 0;
+ }
+
+ for (c = optind; c < argc; c++) {
+ push_cur();
+
+ error = path_walk(argv[c]);
+ if (error)
+ goto err_cur;
+
+ error = parent_cur(argv[c]);
+ if (error)
+ goto err_cur;
+
+ pop_cur();
+ }
+
+ return 0;
+err_cur:
+ pop_cur();
+ if (error) {
+ dbprintf("%s: %s\n", argv[c], strerror(error));
+ exitcode = 1;
+ }
+ return 0;
+}
+
+static struct cmdinfo parent_cmd = {
+ .name = "parent",
+ .altname = "pptr",
+ .cfunc = parent_f,
+ .argmin = 0,
+ .argmax = -1,
+ .canpush = 0,
+ .args = "[paths...]",
+ .help = parent_help,
+};
+
void
namei_init(void)
{
@@ -604,4 +924,7 @@ namei_init(void)
ls_cmd.oneline = _("list directory contents");
add_command(&ls_cmd);
+
+ parent_cmd.oneline = _("list parent pointers");
+ add_command(&parent_cmd);
}
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index a7f6d55ed..937b17e79 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -943,6 +943,15 @@ See the
.B print
command.
.TP
+.BI "parent [" paths "]..."
+List the parents of a file.
+If a path resolves to a file, the parents of that file will be listed.
+If no paths are supplied and the IO cursor points at an inode, the parents of
+that file will be listed.
+
+The output format is:
+inode number, inode generation, ondisk namehash, namehash, name length, name.
+.TP
.BI "path " dir_path
Walk the directory tree to an inode using the supplied path.
Absolute and relative paths are supported.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 19/24] xfs_db: make attr_set and attr_remove handle parent pointers
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (17 preceding siblings ...)
2024-07-30 1:23 ` [PATCH 18/24] xfs_db: add a parents command to list the parents of a file Darrick J. Wong
@ 2024-07-30 1:23 ` Darrick J. Wong
2024-07-30 1:24 ` [PATCH 20/24] xfs_db: add link and unlink expert commands Darrick J. Wong
` (4 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:23 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Make it so that xfs_db can load up the filesystem (somewhat uselessly)
with parent pointers.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/attrset.c | 202 +++++++++++++++++++++++++++++++++++++---------
libxfs/libxfs_api_defs.h | 1
man/man8/xfs_db.8 | 21 ++++-
3 files changed, 181 insertions(+), 43 deletions(-)
diff --git a/db/attrset.c b/db/attrset.c
index 3b5db7c2a..d9ab79fa7 100644
--- a/db/attrset.c
+++ b/db/attrset.c
@@ -24,11 +24,11 @@ static void attrset_help(void);
static const cmdinfo_t attr_set_cmd =
{ "attr_set", "aset", attr_set_f, 1, -1, 0,
- N_("[-r|-s|-u] [-n] [-R|-C] [-v n] name"),
+ N_("[-r|-s|-u|-p] [-n] [-R|-C] [-v n] name"),
N_("set the named attribute on the current inode"), attrset_help };
static const cmdinfo_t attr_remove_cmd =
{ "attr_remove", "aremove", attr_remove_f, 1, -1, 0,
- N_("[-r|-s|-u] [-n] name"),
+ N_("[-r|-s|-u|-p] [-n] name"),
N_("remove the named attribute from the current inode"), attrset_help };
static void
@@ -44,6 +44,7 @@ attrset_help(void)
" -r -- 'root'\n"
" -u -- 'user' (default)\n"
" -s -- 'secure'\n"
+" -p -- 'parent'\n"
"\n"
" For attr_set, these options further define the type of set operation:\n"
" -C -- 'create' - create attribute, fail if it already exists\n"
@@ -62,6 +63,49 @@ attrset_init(void)
add_command(&attr_remove_cmd);
}
+static unsigned char *
+get_buf_from_file(
+ const char *fname,
+ size_t bufsize,
+ int *namelen)
+{
+ FILE *fp;
+ unsigned char *buf;
+ size_t sz;
+
+ buf = malloc(bufsize + 1);
+ if (!buf) {
+ perror("malloc");
+ return NULL;
+ }
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ perror(fname);
+ goto out_free;
+ }
+
+ sz = fread(buf, sizeof(char), bufsize, fp);
+ if (sz == 0) {
+ printf("%s: Could not read anything from file\n", fname);
+ goto out_fp;
+ }
+
+ fclose(fp);
+
+ *namelen = sz;
+ return buf;
+out_fp:
+ fclose(fp);
+out_free:
+ free(buf);
+ return NULL;
+}
+
+#define LIBXFS_ATTR_NS (LIBXFS_ATTR_SECURE | \
+ LIBXFS_ATTR_ROOT | \
+ LIBXFS_ATTR_PARENT)
+
static int
attr_set_f(
int argc,
@@ -69,6 +113,8 @@ attr_set_f(
{
struct xfs_da_args args = { };
char *sp;
+ char *name_from_file = NULL;
+ char *value_from_file = NULL;
enum xfs_attr_update op = XFS_ATTRUPDATE_UPSERT;
int c;
@@ -81,20 +127,23 @@ attr_set_f(
return 0;
}
- while ((c = getopt(argc, argv, "rusCRnv:")) != EOF) {
+ while ((c = getopt(argc, argv, "ruspCRnN:v:V:")) != EOF) {
switch (c) {
/* namespaces */
case 'r':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
args.attr_filter |= LIBXFS_ATTR_ROOT;
- args.attr_filter &= ~LIBXFS_ATTR_SECURE;
break;
case 'u':
- args.attr_filter &= ~(LIBXFS_ATTR_ROOT |
- LIBXFS_ATTR_SECURE);
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
break;
case 's':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
args.attr_filter |= LIBXFS_ATTR_SECURE;
- args.attr_filter &= ~LIBXFS_ATTR_ROOT;
+ break;
+ case 'p':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
+ args.attr_filter |= XFS_ATTR_PARENT;
break;
/* modifiers */
@@ -105,6 +154,10 @@ attr_set_f(
op = XFS_ATTRUPDATE_REPLACE;
break;
+ case 'N':
+ name_from_file = optarg;
+ break;
+
case 'n':
/*
* We never touch attr2 these days; leave this here to
@@ -114,6 +167,11 @@ attr_set_f(
/* value length */
case 'v':
+ if (value_from_file) {
+ dbprintf(_("already set value file\n"));
+ return 0;
+ }
+
args.valuelen = strtol(optarg, &sp, 0);
if (*sp != '\0' ||
args.valuelen < 0 || args.valuelen > 64 * 1024) {
@@ -122,30 +180,64 @@ attr_set_f(
}
break;
+ case 'V':
+ if (args.valuelen != 0) {
+ dbprintf(_("already set valuelen\n"));
+ return 0;
+ }
+
+ value_from_file = optarg;
+ break;
+
default:
dbprintf(_("bad option for attr_set command\n"));
return 0;
}
}
- if (optind != argc - 1) {
- dbprintf(_("too few options for attr_set (no name given)\n"));
- return 0;
- }
+ if (name_from_file) {
+ int namelen;
- args.name = (const unsigned char *)argv[optind];
- if (!args.name) {
- dbprintf(_("invalid name\n"));
- return 0;
- }
+ if (optind != argc) {
+ dbprintf(_("too many options for attr_set (no name needed)\n"));
+ return 0;
+ }
+
+ args.name = get_buf_from_file(name_from_file, MAXNAMELEN,
+ &namelen);
+ if (!args.name)
+ return 0;
+
+ args.namelen = namelen;
+ } else {
+ if (optind != argc - 1) {
+ dbprintf(_("too few options for attr_set (no name given)\n"));
+ return 0;
+ }
- args.namelen = strlen(argv[optind]);
- if (args.namelen >= MAXNAMELEN) {
- dbprintf(_("name too long\n"));
- return 0;
+ args.name = (const unsigned char *)argv[optind];
+ if (!args.name) {
+ dbprintf(_("invalid name\n"));
+ return 0;
+ }
+
+ args.namelen = strlen(argv[optind]);
+ if (args.namelen >= MAXNAMELEN) {
+ dbprintf(_("name too long\n"));
+ goto out;
+ }
}
- if (args.valuelen) {
+ if (value_from_file) {
+ int valuelen;
+
+ args.value = get_buf_from_file(value_from_file,
+ XFS_XATTR_SIZE_MAX, &valuelen);
+ if (!args.value)
+ goto out;
+
+ args.valuelen = valuelen;
+ } else if (args.valuelen) {
args.value = memalign(getpagesize(), args.valuelen);
if (!args.value) {
dbprintf(_("cannot allocate buffer (%d)\n"),
@@ -175,6 +267,8 @@ attr_set_f(
libxfs_irele(args.dp);
if (args.value)
free(args.value);
+ if (name_from_file)
+ free((void *)args.name);
return 0;
}
@@ -184,6 +278,7 @@ attr_remove_f(
char **argv)
{
struct xfs_da_args args = { };
+ char *name_from_file = NULL;
int c;
if (cur_typ == NULL) {
@@ -195,20 +290,27 @@ attr_remove_f(
return 0;
}
- while ((c = getopt(argc, argv, "rusn")) != EOF) {
+ while ((c = getopt(argc, argv, "ruspnN:")) != EOF) {
switch (c) {
/* namespaces */
case 'r':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
args.attr_filter |= LIBXFS_ATTR_ROOT;
- args.attr_filter &= ~LIBXFS_ATTR_SECURE;
break;
case 'u':
- args.attr_filter &= ~(LIBXFS_ATTR_ROOT |
- LIBXFS_ATTR_SECURE);
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
break;
case 's':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
args.attr_filter |= LIBXFS_ATTR_SECURE;
- args.attr_filter &= ~LIBXFS_ATTR_ROOT;
+ break;
+ case 'p':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
+ args.attr_filter |= XFS_ATTR_PARENT;
+ break;
+
+ case 'N':
+ name_from_file = optarg;
break;
case 'n':
@@ -224,21 +326,37 @@ attr_remove_f(
}
}
- if (optind != argc - 1) {
- dbprintf(_("too few options for attr_remove (no name given)\n"));
- return 0;
- }
-
- args.name = (const unsigned char *)argv[optind];
- if (!args.name) {
- dbprintf(_("invalid name\n"));
- return 0;
- }
-
- args.namelen = strlen(argv[optind]);
- if (args.namelen >= MAXNAMELEN) {
- dbprintf(_("name too long\n"));
- return 0;
+ if (name_from_file) {
+ int namelen;
+
+ if (optind != argc) {
+ dbprintf(_("too many options for attr_set (no name needed)\n"));
+ return 0;
+ }
+
+ args.name = get_buf_from_file(name_from_file, MAXNAMELEN,
+ &namelen);
+ if (!args.name)
+ return 0;
+
+ args.namelen = namelen;
+ } else {
+ if (optind != argc - 1) {
+ dbprintf(_("too few options for attr_remove (no name given)\n"));
+ return 0;
+ }
+
+ args.name = (const unsigned char *)argv[optind];
+ if (!args.name) {
+ dbprintf(_("invalid name\n"));
+ return 0;
+ }
+
+ args.namelen = strlen(argv[optind]);
+ if (args.namelen >= MAXNAMELEN) {
+ dbprintf(_("name too long\n"));
+ return 0;
+ }
}
if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &args.dp)) {
@@ -260,5 +378,7 @@ attr_remove_f(
out:
if (args.dp)
libxfs_irele(args.dp);
+ if (name_from_file)
+ free((void *)args.name);
return 0;
}
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index 5713e5221..bceaab8ba 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -15,6 +15,7 @@
*/
#define LIBXFS_ATTR_ROOT XFS_ATTR_ROOT
#define LIBXFS_ATTR_SECURE XFS_ATTR_SECURE
+#define LIBXFS_ATTR_PARENT XFS_ATTR_PARENT
#define xfs_agfl_size libxfs_agfl_size
#define xfs_agfl_walk libxfs_agfl_walk
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index 937b17e79..a561bdc49 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -184,10 +184,14 @@ Displays the length, free block count, per-AG reservation size, and per-AG
reservation usage for a given AG.
If no argument is given, display information for all AGs.
.TP
-.BI "attr_remove [\-r|\-u|\-s] [\-n] " name
+.BI "attr_remove [\-p|\-r|\-u|\-s] [\-n] [\-N " namefile "|" name "] "
Remove the specified extended attribute from the current file.
.RS 1.0i
.TP 0.4i
+.B \-p
+Sets the attribute in the parent namespace.
+Only one namespace option can be specified.
+.TP
.B \-r
Sets the attribute in the root namespace.
Only one namespace option can be specified.
@@ -200,14 +204,21 @@ Only one namespace option can be specified.
Sets the attribute in the secure namespace.
Only one namespace option can be specified.
.TP
+.B \-N
+Read the name from this file.
+.TP
.B \-n
Do not enable 'noattr2' mode on V4 filesystems.
.RE
.TP
-.BI "attr_set [\-r|\-u|\-s] [\-n] [\-R|\-C] [\-v " namelen "] " name
+.BI "attr_set [\-p\-r|\-u|\-s] [\-n] [\-R|\-C] [\-v " valuelen "|\-V " valuefile "] [\-N " namefile "|" name "] "
Sets an extended attribute on the current file with the given name.
.RS 1.0i
.TP 0.4i
+.B \-p
+Sets the attribute in the parent namespace.
+Only one namespace option can be specified.
+.TP
.B \-r
Sets the attribute in the root namespace.
Only one namespace option can be specified.
@@ -220,6 +231,9 @@ Only one namespace option can be specified.
Sets the attribute in the secure namespace.
Only one namespace option can be specified.
.TP
+.B \-N
+Read the name from this file.
+.TP
.B \-n
Do not enable 'noattr2' mode on V4 filesystems.
.TP
@@ -231,6 +245,9 @@ The command will fail if the attribute does not already exist.
Create the attribute.
The command will fail if the attribute already exists.
.TP
+.B \-V
+Read the value from this file.
+.TP
.B \-v
Set the attribute value to a string of this length containing the letter 'v'.
.RE
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 20/24] xfs_db: add link and unlink expert commands
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (18 preceding siblings ...)
2024-07-30 1:23 ` [PATCH 19/24] xfs_db: make attr_set and attr_remove handle parent pointers Darrick J. Wong
@ 2024-07-30 1:24 ` Darrick J. Wong
2024-07-30 1:24 ` [PATCH 21/24] xfs_db: compute hashes of parent pointers Darrick J. Wong
` (3 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:24 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Create a pair of commands to create and remove directory entries to
support functional testing of directory tree corruption.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/namei.c | 378 ++++++++++++++++++++++++++++++++++++++++++++++
include/xfs_inode.h | 4
libxfs/libxfs_api_defs.h | 8 +
man/man8/xfs_db.8 | 20 ++
4 files changed, 409 insertions(+), 1 deletion(-)
diff --git a/db/namei.c b/db/namei.c
index 46b4cacb5..d57ead4f1 100644
--- a/db/namei.c
+++ b/db/namei.c
@@ -916,6 +916,376 @@ static struct cmdinfo parent_cmd = {
.help = parent_help,
};
+static void
+link_help(void)
+{
+ dbprintf(_(
+"\n"
+" Create a directory entry in the current directory that points to the\n"
+" specified file.\n"
+"\n"
+" Options:\n"
+" -i -- Point to this specific inode number.\n"
+" -p -- Point to the inode given by this path.\n"
+" -t -- Set the file type to this value.\n"
+" name -- Create this directory entry with this name.\n"
+ ));
+}
+
+static int
+create_child(
+ struct xfs_mount *mp,
+ xfs_ino_t parent_ino,
+ const char *name,
+ unsigned int ftype,
+ xfs_ino_t child_ino)
+{
+ struct xfs_name xname = {
+ .name = (const unsigned char *)name,
+ .len = strlen(name),
+ .type = ftype,
+ };
+ struct xfs_parent_args *ppargs = NULL;
+ struct xfs_trans *tp;
+ struct xfs_inode *dp, *ip;
+ unsigned int resblks;
+ bool isdir;
+ int error;
+
+ error = -libxfs_iget(mp, NULL, parent_ino, 0, &dp);
+ if (error)
+ return error;
+
+ if (!S_ISDIR(VFS_I(dp)->i_mode)) {
+ error = -ENOTDIR;
+ goto out_dp;
+ }
+
+ error = -libxfs_iget(mp, NULL, child_ino, 0, &ip);
+ if (error)
+ goto out_dp;
+ isdir = S_ISDIR(VFS_I(ip)->i_mode);
+
+ if (xname.type == XFS_DIR3_FT_UNKNOWN)
+ xname.type = libxfs_mode_to_ftype(VFS_I(ip)->i_mode);
+
+ error = -libxfs_parent_start(mp, &ppargs);
+ if (error)
+ goto out_ip;
+
+ resblks = libxfs_link_space_res(mp, MAXNAMELEN);
+ error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_link, resblks, 0, 0,
+ &tp);
+ if (error)
+ goto out_parent;
+
+ libxfs_trans_ijoin(tp, dp, 0);
+ libxfs_trans_ijoin(tp, ip, 0);
+
+ error = -libxfs_dir_createname(tp, dp, &xname, ip->i_ino, resblks);
+ if (error)
+ goto out_trans;
+
+ /* bump dp's link to ip */
+ libxfs_bumplink(tp, ip);
+
+ /* bump ip's dotdot link to dp */
+ if (isdir)
+ libxfs_bumplink(tp, dp);
+
+ /* Replace the dotdot entry in the child directory. */
+ if (isdir) {
+ error = -libxfs_dir_replace(tp, ip, &xfs_name_dotdot,
+ dp->i_ino, resblks);
+ if (error)
+ goto out_trans;
+ }
+
+ if (ppargs) {
+ error = -libxfs_parent_addname(tp, ppargs, dp, &xname, ip);
+ if (error)
+ goto out_trans;
+ }
+
+ error = -libxfs_trans_commit(tp);
+ goto out_parent;
+
+out_trans:
+ libxfs_trans_cancel(tp);
+out_parent:
+ libxfs_parent_finish(mp, ppargs);
+out_ip:
+ libxfs_irele(ip);
+out_dp:
+ libxfs_irele(dp);
+ return error;
+}
+
+static const char *ftype_map[] = {
+ [XFS_DIR3_FT_REG_FILE] = "reg",
+ [XFS_DIR3_FT_DIR] = "dir",
+ [XFS_DIR3_FT_CHRDEV] = "cdev",
+ [XFS_DIR3_FT_BLKDEV] = "bdev",
+ [XFS_DIR3_FT_FIFO] = "fifo",
+ [XFS_DIR3_FT_SOCK] = "sock",
+ [XFS_DIR3_FT_SYMLINK] = "symlink",
+ [XFS_DIR3_FT_WHT] = "whiteout",
+};
+
+static int
+link_f(
+ int argc,
+ char **argv)
+{
+ xfs_ino_t child_ino = NULLFSINO;
+ int ftype = XFS_DIR3_FT_UNKNOWN;
+ unsigned int i;
+ int c;
+ int error = 0;
+
+ while ((c = getopt(argc, argv, "i:p:t:")) != -1) {
+ switch (c) {
+ case 'i':
+ errno = 0;
+ child_ino = strtoull(optarg, NULL, 0);
+ if (errno == ERANGE) {
+ printf("%s: unknown inode number\n", optarg);
+ exitcode = 1;
+ return 0;
+ }
+ break;
+ case 'p':
+ push_cur();
+ error = path_walk(optarg);
+ if (error) {
+ printf("%s: %s\n", optarg, strerror(error));
+ exitcode = 1;
+ return 0;
+ } else if (iocur_top->typ != &typtab[TYP_INODE]) {
+ printf("%s: does not point to an inode\n",
+ optarg);
+ exitcode = 1;
+ return 0;
+ } else {
+ child_ino = iocur_top->ino;
+ }
+ pop_cur();
+ break;
+ case 't':
+ for (i = 0; i < ARRAY_SIZE(ftype_map); i++) {
+ if (ftype_map[i] &&
+ !strcmp(ftype_map[i], optarg)) {
+ ftype = i;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(ftype_map)) {
+ printf("%s: unknown file type\n", optarg);
+ exitcode = 1;
+ return 0;
+ }
+ break;
+ default:
+ link_help();
+ return 0;
+ }
+ }
+
+ if (child_ino == NULLFSINO) {
+ printf("link: need to specify child via -i or -p\n");
+ exitcode = 1;
+ return 0;
+ }
+
+ if (iocur_top->typ != &typtab[TYP_INODE]) {
+ printf("io cursor does not point to an inode.\n");
+ exitcode = 1;
+ return 0;
+ }
+
+ if (optind + 1 != argc) {
+ printf("link: need directory entry name");
+ exitcode = 1;
+ return 0;
+ }
+
+ error = create_child(mp, iocur_top->ino, argv[optind], ftype,
+ child_ino);
+ if (error) {
+ printf("link failed: %s\n", strerror(error));
+ exitcode = 1;
+ return 0;
+ }
+
+ return 0;
+}
+
+static struct cmdinfo link_cmd = {
+ .name = "link",
+ .cfunc = link_f,
+ .argmin = 0,
+ .argmax = -1,
+ .canpush = 0,
+ .args = "[-i ino] [-p path] [-t ftype] name",
+ .help = link_help,
+};
+
+static void
+unlink_help(void)
+{
+ dbprintf(_(
+"\n"
+" Remove a directory entry from the current directory.\n"
+"\n"
+" Options:\n"
+" name -- Remove the directory entry with this name.\n"
+ ));
+}
+
+static void
+droplink(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip)
+{
+ struct inode *inode = VFS_I(ip);
+
+ libxfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
+
+ if (inode->i_nlink != XFS_NLINK_PINNED)
+ drop_nlink(VFS_I(ip));
+
+ libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+}
+
+static int
+remove_child(
+ struct xfs_mount *mp,
+ xfs_ino_t parent_ino,
+ const char *name)
+{
+ struct xfs_name xname = {
+ .name = (const unsigned char *)name,
+ .len = strlen(name),
+ };
+ struct xfs_parent_args *ppargs;
+ struct xfs_trans *tp;
+ struct xfs_inode *dp, *ip;
+ xfs_ino_t child_ino;
+ unsigned int resblks;
+ int error;
+
+ error = -libxfs_iget(mp, NULL, parent_ino, 0, &dp);
+ if (error)
+ return error;
+
+ if (!S_ISDIR(VFS_I(dp)->i_mode)) {
+ error = -ENOTDIR;
+ goto out_dp;
+ }
+
+ error = -libxfs_dir_lookup(NULL, dp, &xname, &child_ino, NULL);
+ if (error)
+ goto out_dp;
+
+ error = -libxfs_iget(mp, NULL, child_ino, 0, &ip);
+ if (error)
+ goto out_dp;
+
+ error = -libxfs_parent_start(mp, &ppargs);
+ if (error)
+ goto out_ip;
+
+ resblks = libxfs_remove_space_res(mp, MAXNAMELEN);
+ error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove, resblks, 0, 0,
+ &tp);
+ if (error)
+ goto out_parent;
+
+ libxfs_trans_ijoin(tp, dp, 0);
+ libxfs_trans_ijoin(tp, ip, 0);
+
+ if (S_ISDIR(VFS_I(ip)->i_mode)) {
+ /* drop ip's dotdot link to dp */
+ droplink(tp, dp);
+ } else {
+ libxfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
+ }
+
+ /* drop dp's link to ip */
+ droplink(tp, ip);
+
+ error = -libxfs_dir_removename(tp, dp, &xname, ip->i_ino, resblks);
+ if (error)
+ goto out_trans;
+
+ if (ppargs) {
+ error = -libxfs_parent_removename(tp, ppargs, dp, &xname, ip);
+ if (error)
+ goto out_trans;
+ }
+
+ error = -libxfs_trans_commit(tp);
+ goto out_parent;
+
+out_trans:
+ libxfs_trans_cancel(tp);
+out_parent:
+ libxfs_parent_finish(mp, ppargs);
+out_ip:
+ libxfs_irele(ip);
+out_dp:
+ libxfs_irele(dp);
+ return error;
+}
+
+static int
+unlink_f(
+ int argc,
+ char **argv)
+{
+ int c;
+ int error = 0;
+
+ while ((c = getopt(argc, argv, "")) != -1) {
+ switch (c) {
+ default:
+ unlink_help();
+ return 0;
+ }
+ }
+
+ if (iocur_top->typ != &typtab[TYP_INODE]) {
+ printf("io cursor does not point to an inode.\n");
+ exitcode = 1;
+ return 0;
+ }
+
+ if (optind + 1 != argc) {
+ printf("unlink: need directory entry name");
+ exitcode = 1;
+ return 0;
+ }
+
+ error = remove_child(mp, iocur_top->ino, argv[optind]);
+ if (error) {
+ printf("unlink failed: %s\n", strerror(error));
+ exitcode = 1;
+ return 0;
+ }
+
+ return 0;
+}
+
+static struct cmdinfo unlink_cmd = {
+ .name = "unlink",
+ .cfunc = unlink_f,
+ .argmin = 0,
+ .argmax = -1,
+ .canpush = 0,
+ .args = "name",
+ .help = unlink_help,
+};
+
void
namei_init(void)
{
@@ -927,4 +1297,12 @@ namei_init(void)
parent_cmd.oneline = _("list parent pointers");
add_command(&parent_cmd);
+
+ if (expert_mode) {
+ link_cmd.oneline = _("create directory link");
+ add_command(&link_cmd);
+
+ unlink_cmd.oneline = _("remove directory link");
+ add_command(&unlink_cmd);
+ }
}
diff --git a/include/xfs_inode.h b/include/xfs_inode.h
index 45339b426..9bbf37225 100644
--- a/include/xfs_inode.h
+++ b/include/xfs_inode.h
@@ -315,6 +315,10 @@ static inline void inc_nlink(struct inode *inode)
{
inode->i_nlink++;
}
+static inline void drop_nlink(struct inode *inode)
+{
+ inode->i_nlink--;
+}
static inline bool xfs_is_reflink_inode(struct xfs_inode *ip)
{
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index bceaab8ba..b7edaf788 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -147,12 +147,16 @@
#define xfs_dir_init libxfs_dir_init
#define xfs_dir_ino_validate libxfs_dir_ino_validate
#define xfs_dir_lookup libxfs_dir_lookup
+#define xfs_dir_removename libxfs_dir_removename
#define xfs_dir_replace libxfs_dir_replace
#define xfs_dqblk_repair libxfs_dqblk_repair
#define xfs_dquot_from_disk_ts libxfs_dquot_from_disk_ts
#define xfs_dquot_verify libxfs_dquot_verify
+#define xfs_bumplink libxfs_bumplink
+#define xfs_droplink libxfs_droplink
+
#define xfs_finobt_calc_reserves libxfs_finobt_calc_reserves
#define xfs_finobt_init_cursor libxfs_finobt_init_cursor
#define xfs_free_extent libxfs_free_extent
@@ -191,13 +195,15 @@
#define xfs_iread_extents libxfs_iread_extents
#define xfs_irele libxfs_irele
+#define xfs_link_space_res libxfs_link_space_res
#define xfs_log_calc_minimum_size libxfs_log_calc_minimum_size
#define xfs_log_get_max_trans_res libxfs_log_get_max_trans_res
#define xfs_log_sb libxfs_log_sb
#define xfs_mode_to_ftype libxfs_mode_to_ftype
#define xfs_mkdir_space_res libxfs_mkdir_space_res
-#define xfs_parent_add libxfs_parent_add
+#define xfs_parent_addname libxfs_parent_addname
#define xfs_parent_finish libxfs_parent_finish
+#define xfs_parent_removename libxfs_parent_removename
#define xfs_parent_start libxfs_parent_start
#define xfs_parent_from_attr libxfs_parent_from_attr
#define xfs_perag_get libxfs_perag_get
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index a561bdc49..f8db6c36f 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -901,6 +901,21 @@ will result in truncation and a warning will be issued. If no
.I label
is given, the current filesystem label is printed.
.TP
+.BI "link [-i " ino "] [-p " path "] [-t " ftype "] name"
+In the current directory, create a directory entry with the given
+.I name
+pointing to a file.
+The file must be specified either as a directory tree path as given by the
+.I path
+option; or directly as an inode number as given by the
+.I ino
+option.
+The file type in the directory entry will be determined from the mode of the
+child file unless the
+.I ftype
+option is given.
+The file being targetted must not be on the iunlink list.
+.TP
.BI "log [stop | start " filename ]
Start logging output to
.IR filename ,
@@ -1052,6 +1067,11 @@ Print the timestamps in the current locale's date and time format instead of
raw seconds since the Unix epoch.
.RE
.TP
+.BI "unlink name"
+In the current directory, remove a directory entry with the given
+.IR name .
+The file being targetted will not be put on the iunlink list.
+.TP
.BI "uuid [" uuid " | " generate " | " rewrite " | " restore ]
Set the filesystem universally unique identifier (UUID).
The filesystem UUID can be used by
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 21/24] xfs_db: compute hashes of parent pointers
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (19 preceding siblings ...)
2024-07-30 1:24 ` [PATCH 20/24] xfs_db: add link and unlink expert commands Darrick J. Wong
@ 2024-07-30 1:24 ` Darrick J. Wong
2024-07-30 1:24 ` [PATCH 22/24] libxfs: create new files with attr forks if necessary Darrick J. Wong
` (2 subsequent siblings)
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:24 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Enhance the hash command to compute the hashes of parent pointers.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/hash.c | 36 ++++++++++++++++++++++++++++++------
libxfs/libxfs_api_defs.h | 1 +
man/man8/xfs_db.8 | 9 ++++++++-
3 files changed, 39 insertions(+), 7 deletions(-)
diff --git a/db/hash.c b/db/hash.c
index 1500a6032..50f6da0b1 100644
--- a/db/hash.c
+++ b/db/hash.c
@@ -36,26 +36,42 @@ hash_help(void)
" 'hash' prints out the calculated hash value for a string using the\n"
"directory/attribute code hash function.\n"
"\n"
-" Usage: \"hash <string>\"\n"
+" Usage: \"hash [-d|-p parent_ino] <string>\"\n"
"\n"
));
}
+enum hash_what {
+ ATTR,
+ DIRECTORY,
+ PPTR,
+};
+
/* ARGSUSED */
static int
hash_f(
int argc,
char **argv)
{
+ xfs_ino_t p_ino = 0;
xfs_dahash_t hashval;
- bool use_dir2_hash = false;
+ enum hash_what what = ATTR;
int c;
- while ((c = getopt(argc, argv, "d")) != EOF) {
+ while ((c = getopt(argc, argv, "dp:")) != EOF) {
switch (c) {
case 'd':
- use_dir2_hash = true;
+ what = DIRECTORY;
+ break;
+ case 'p':
+ errno = 0;
+ p_ino = strtoull(optarg, NULL, 0);
+ if (errno) {
+ perror(optarg);
+ return 1;
+ }
+ what = PPTR;
break;
default:
exitcode = 1;
@@ -70,10 +86,18 @@ hash_f(
.len = strlen(argv[c]),
};
- if (use_dir2_hash)
+ switch (what) {
+ case DIRECTORY:
hashval = libxfs_dir2_hashname(mp, &xname);
- else
+ break;
+ case PPTR:
+ hashval = libxfs_parent_hashval(mp, xname.name,
+ xname.len, p_ino);
+ break;
+ case ATTR:
hashval = libxfs_attr_hashname(xname.name, xname.len);
+ break;
+ }
dbprintf("0x%x\n", hashval);
}
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index b7edaf788..df83aabdc 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -203,6 +203,7 @@
#define xfs_mkdir_space_res libxfs_mkdir_space_res
#define xfs_parent_addname libxfs_parent_addname
#define xfs_parent_finish libxfs_parent_finish
+#define xfs_parent_hashval libxfs_parent_hashval
#define xfs_parent_removename libxfs_parent_removename
#define xfs_parent_start libxfs_parent_start
#define xfs_parent_from_attr libxfs_parent_from_attr
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index f8db6c36f..9f6fea574 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -822,7 +822,7 @@ Skip write verifiers but perform CRC recalculation; allows invalid data to be
written to disk to test detection of invalid data.
.RE
.TP
-.BI hash [-d]" strings
+.BI hash [-d|-p parent_ino]" strings
Prints the hash value of
.I string
using the hash function of the XFS directory and attribute implementation.
@@ -832,6 +832,13 @@ If the
option is specified, the directory-specific hash function is used.
This only makes a difference on filesystems with ascii case-insensitive
lookups enabled.
+
+If the
+.B \-p
+option is specified, the parent pointer-specific hash function is used.
+The parent directory inumber must be specified as an argument.
+This only makes a difference on filesystems with ascii case-insensitive
+lookups enabled.
.TP
.BI "hashcoll [-a] [-s seed] [-n " nr "] [-p " path "] -i | " names...
Create directory entries or extended attributes names that all have the same
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 22/24] libxfs: create new files with attr forks if necessary
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (20 preceding siblings ...)
2024-07-30 1:24 ` [PATCH 21/24] xfs_db: compute hashes of parent pointers Darrick J. Wong
@ 2024-07-30 1:24 ` Darrick J. Wong
2024-07-30 1:24 ` [PATCH 23/24] mkfs: Add parent pointers during protofile creation Darrick J. Wong
2024-07-30 1:25 ` [PATCH 24/24] mkfs: enable formatting with parent pointers Darrick J. Wong
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:24 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Create new files with attr forks if they're going to have parent
pointers. In the next patch we'll fix mkfs to use the same parent
creation functions as the kernel, so we're going to need this.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/init.c | 4 ++++
libxfs/util.c | 19 ++++++++++++++++++-
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/libxfs/init.c b/libxfs/init.c
index 95de1e6d1..90a539e04 100644
--- a/libxfs/init.c
+++ b/libxfs/init.c
@@ -602,14 +602,18 @@ void
libxfs_compute_all_maxlevels(
struct xfs_mount *mp)
{
+ struct xfs_ino_geometry *igeo = M_IGEO(mp);
+
xfs_alloc_compute_maxlevels(mp);
xfs_bmap_compute_maxlevels(mp, XFS_DATA_FORK);
xfs_bmap_compute_maxlevels(mp, XFS_ATTR_FORK);
+ igeo->attr_fork_offset = xfs_bmap_compute_attr_offset(mp);
xfs_ialloc_setup_geometry(mp);
xfs_rmapbt_compute_maxlevels(mp);
xfs_refcountbt_compute_maxlevels(mp);
xfs_agbtree_compute_maxlevels(mp);
+
}
/*
diff --git a/libxfs/util.c b/libxfs/util.c
index 74eea0fcb..373749457 100644
--- a/libxfs/util.c
+++ b/libxfs/util.c
@@ -274,11 +274,12 @@ libxfs_init_new_inode(
struct fsxattr *fsx,
struct xfs_inode **ipp)
{
+ struct xfs_mount *mp = tp->t_mountp;
struct xfs_inode *ip;
unsigned int flags;
int error;
- error = libxfs_iget(tp->t_mountp, tp, ino, XFS_IGET_CREATE, &ip);
+ error = libxfs_iget(mp, tp, ino, XFS_IGET_CREATE, &ip);
if (error != 0)
return error;
ASSERT(ip != NULL);
@@ -340,6 +341,22 @@ libxfs_init_new_inode(
ASSERT(0);
}
+ /*
+ * If we're going to set a parent pointer on this file, we need to
+ * create an attr fork to receive that parent pointer.
+ */
+ if (pip && xfs_has_parent(mp)) {
+ ip->i_forkoff = xfs_default_attroffset(ip) >> 3;
+ xfs_ifork_init_attr(ip, XFS_DINODE_FMT_EXTENTS, 0);
+
+ if (!xfs_has_attr(mp)) {
+ spin_lock(&mp->m_sb_lock);
+ xfs_add_attr(mp);
+ spin_unlock(&mp->m_sb_lock);
+ xfs_log_sb(tp);
+ }
+ }
+
/*
* Log the new values stuffed into the inode.
*/
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 23/24] mkfs: Add parent pointers during protofile creation
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (21 preceding siblings ...)
2024-07-30 1:24 ` [PATCH 22/24] libxfs: create new files with attr forks if necessary Darrick J. Wong
@ 2024-07-30 1:24 ` Darrick J. Wong
2024-07-30 1:25 ` [PATCH 24/24] mkfs: enable formatting with parent pointers Darrick J. Wong
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:24 UTC (permalink / raw)
To: djwong, cem
Cc: Allison Henderson, Christoph Hellwig, linux-xfs, catherine.hoang,
allison.henderson
From: Allison Henderson <allison.henderson@oracle.com>
Inodes created from protofile parsing will also need to add the
appropriate parent pointers.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: use xfs_parent_add from libxfs instead of open-coding xfs_attr_set]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
mkfs/proto.c | 62 +++++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 48 insertions(+), 14 deletions(-)
diff --git a/mkfs/proto.c b/mkfs/proto.c
index a9a9b704a..8e16eb150 100644
--- a/mkfs/proto.c
+++ b/mkfs/proto.c
@@ -348,11 +348,12 @@ newregfile(
static void
newdirent(
- xfs_mount_t *mp,
- xfs_trans_t *tp,
- xfs_inode_t *pip,
- struct xfs_name *name,
- xfs_ino_t inum)
+ struct xfs_mount *mp,
+ struct xfs_trans *tp,
+ struct xfs_inode *pip,
+ struct xfs_name *name,
+ struct xfs_inode *ip,
+ struct xfs_parent_args *ppargs)
{
int error;
int rsv;
@@ -365,9 +366,15 @@ newdirent(
rsv = XFS_DIRENTER_SPACE_RES(mp, name->len);
- error = -libxfs_dir_createname(tp, pip, name, inum, rsv);
+ error = -libxfs_dir_createname(tp, pip, name, ip->i_ino, rsv);
if (error)
fail(_("directory createname error"), error);
+
+ if (ppargs) {
+ error = -libxfs_parent_addname(tp, ppargs, pip, name, ip);
+ if (error)
+ fail(_("parent addname error"), error);
+ }
}
static void
@@ -384,6 +391,20 @@ newdirectory(
fail(_("directory create error"), error);
}
+static struct xfs_parent_args *
+newpptr(
+ struct xfs_mount *mp)
+{
+ struct xfs_parent_args *ret;
+ int error;
+
+ error = -libxfs_parent_start(mp, &ret);
+ if (error)
+ fail(_("initializing parent pointer"), error);
+
+ return ret;
+}
+
static void
parseproto(
xfs_mount_t *mp,
@@ -418,6 +439,7 @@ parseproto(
struct cred creds;
char *value;
struct xfs_name xname;
+ struct xfs_parent_args *ppargs = NULL;
memset(&creds, 0, sizeof(creds));
mstr = getstr(pp);
@@ -492,6 +514,7 @@ parseproto(
case IF_REGULAR:
buf = newregfile(pp, &len);
tp = getres(mp, XFS_B_TO_FSB(mp, len));
+ ppargs = newpptr(mp);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFREG, 1, 0,
&creds, fsxp, &ip);
if (error)
@@ -501,7 +524,7 @@ parseproto(
free(buf);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_REG_FILE;
- newdirent(mp, tp, pip, &xname, ip->i_ino);
+ newdirent(mp, tp, pip, &xname, ip, ppargs);
break;
case IF_RESERVED: /* pre-allocated space only */
@@ -515,7 +538,7 @@ parseproto(
exit(1);
}
tp = getres(mp, XFS_B_TO_FSB(mp, llen));
-
+ ppargs = newpptr(mp);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFREG, 1, 0,
&creds, fsxp, &ip);
if (error)
@@ -524,17 +547,19 @@ parseproto(
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_REG_FILE;
- newdirent(mp, tp, pip, &xname, ip->i_ino);
+ newdirent(mp, tp, pip, &xname, ip, ppargs);
libxfs_trans_log_inode(tp, ip, flags);
error = -libxfs_trans_commit(tp);
if (error)
fail(_("Space preallocation failed."), error);
+ libxfs_parent_finish(mp, ppargs);
rsvfile(mp, ip, llen);
libxfs_irele(ip);
return;
case IF_BLOCK:
tp = getres(mp, 0);
+ ppargs = newpptr(mp);
majdev = getnum(getstr(pp), 0, 0, false);
mindev = getnum(getstr(pp), 0, 0, false);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFBLK, 1,
@@ -544,12 +569,13 @@ parseproto(
}
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_BLKDEV;
- newdirent(mp, tp, pip, &xname, ip->i_ino);
+ newdirent(mp, tp, pip, &xname, ip, ppargs);
flags |= XFS_ILOG_DEV;
break;
case IF_CHAR:
tp = getres(mp, 0);
+ ppargs = newpptr(mp);
majdev = getnum(getstr(pp), 0, 0, false);
mindev = getnum(getstr(pp), 0, 0, false);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFCHR, 1,
@@ -558,24 +584,26 @@ parseproto(
fail(_("Inode allocation failed"), error);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_CHRDEV;
- newdirent(mp, tp, pip, &xname, ip->i_ino);
+ newdirent(mp, tp, pip, &xname, ip, ppargs);
flags |= XFS_ILOG_DEV;
break;
case IF_FIFO:
tp = getres(mp, 0);
+ ppargs = newpptr(mp);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFIFO, 1, 0,
&creds, fsxp, &ip);
if (error)
fail(_("Inode allocation failed"), error);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_FIFO;
- newdirent(mp, tp, pip, &xname, ip->i_ino);
+ newdirent(mp, tp, pip, &xname, ip, ppargs);
break;
case IF_SYMLINK:
buf = getstr(pp);
len = (int)strlen(buf);
tp = getres(mp, XFS_B_TO_FSB(mp, len));
+ ppargs = newpptr(mp);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFLNK, 1, 0,
&creds, fsxp, &ip);
if (error)
@@ -583,7 +611,7 @@ parseproto(
writesymlink(tp, ip, buf, len);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_SYMLINK;
- newdirent(mp, tp, pip, &xname, ip->i_ino);
+ newdirent(mp, tp, pip, &xname, ip, ppargs);
break;
case IF_DIRECTORY:
tp = getres(mp, 0);
@@ -598,9 +626,10 @@ parseproto(
libxfs_log_sb(tp);
isroot = 1;
} else {
+ ppargs = newpptr(mp);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_DIR;
- newdirent(mp, tp, pip, &xname, ip->i_ino);
+ newdirent(mp, tp, pip, &xname, ip, ppargs);
libxfs_bumplink(tp, pip);
libxfs_trans_log_inode(tp, pip, XFS_ILOG_CORE);
}
@@ -609,6 +638,9 @@ parseproto(
error = -libxfs_trans_commit(tp);
if (error)
fail(_("Directory inode allocation failed."), error);
+
+ libxfs_parent_finish(mp, ppargs);
+
/*
* RT initialization. Do this here to ensure that
* the RT inodes get placed after the root inode.
@@ -636,6 +668,8 @@ parseproto(
fail(_("Error encountered creating file from prototype file"),
error);
}
+
+ libxfs_parent_finish(mp, ppargs);
libxfs_irele(ip);
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 24/24] mkfs: enable formatting with parent pointers
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
` (22 preceding siblings ...)
2024-07-30 1:24 ` [PATCH 23/24] mkfs: Add parent pointers during protofile creation Darrick J. Wong
@ 2024-07-30 1:25 ` Darrick J. Wong
23 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:25 UTC (permalink / raw)
To: djwong, cem
Cc: Allison Henderson, Christoph Hellwig, linux-xfs, catherine.hoang,
allison.henderson
From: Allison Henderson <allison.henderson@oracle.com>
Enable parent pointer support in mkfs via the '-n parent' parameter.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: move the no-V4 filesystem check to join the rest]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
mkfs/lts_4.19.conf | 3 +++
mkfs/lts_5.10.conf | 3 +++
mkfs/lts_5.15.conf | 3 +++
mkfs/lts_5.4.conf | 3 +++
mkfs/lts_6.1.conf | 3 +++
mkfs/lts_6.6.conf | 3 +++
mkfs/xfs_mkfs.c | 45 ++++++++++++++++++++++++++++++++++++++++++---
7 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/mkfs/lts_4.19.conf b/mkfs/lts_4.19.conf
index 92e8eba6b..9fa1f9378 100644
--- a/mkfs/lts_4.19.conf
+++ b/mkfs/lts_4.19.conf
@@ -13,3 +13,6 @@ rmapbt=0
sparse=1
nrext64=0
exchange=0
+
+[naming]
+parent=0
diff --git a/mkfs/lts_5.10.conf b/mkfs/lts_5.10.conf
index 34e7662cd..d64bcdf8c 100644
--- a/mkfs/lts_5.10.conf
+++ b/mkfs/lts_5.10.conf
@@ -13,3 +13,6 @@ rmapbt=0
sparse=1
nrext64=0
exchange=0
+
+[naming]
+parent=0
diff --git a/mkfs/lts_5.15.conf b/mkfs/lts_5.15.conf
index a36a5c2b7..775fd9ab9 100644
--- a/mkfs/lts_5.15.conf
+++ b/mkfs/lts_5.15.conf
@@ -13,3 +13,6 @@ rmapbt=0
sparse=1
nrext64=0
exchange=0
+
+[naming]
+parent=0
diff --git a/mkfs/lts_5.4.conf b/mkfs/lts_5.4.conf
index 4204d5b8f..6f43a6c6d 100644
--- a/mkfs/lts_5.4.conf
+++ b/mkfs/lts_5.4.conf
@@ -13,3 +13,6 @@ rmapbt=0
sparse=1
nrext64=0
exchange=0
+
+[naming]
+parent=0
diff --git a/mkfs/lts_6.1.conf b/mkfs/lts_6.1.conf
index 9a90def8f..a78a4f9e3 100644
--- a/mkfs/lts_6.1.conf
+++ b/mkfs/lts_6.1.conf
@@ -13,3 +13,6 @@ rmapbt=0
sparse=1
nrext64=0
exchange=0
+
+[naming]
+parent=0
diff --git a/mkfs/lts_6.6.conf b/mkfs/lts_6.6.conf
index 3f7fb6519..91a25bd81 100644
--- a/mkfs/lts_6.6.conf
+++ b/mkfs/lts_6.6.conf
@@ -13,3 +13,6 @@ rmapbt=1
sparse=1
nrext64=1
exchange=0
+
+[naming]
+parent=0
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 991ecbdd0..394a35771 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -114,6 +114,7 @@ enum {
N_SIZE = 0,
N_VERSION,
N_FTYPE,
+ N_PARENT,
N_MAX_OPTS,
};
@@ -656,6 +657,7 @@ static struct opt_params nopts = {
[N_SIZE] = "size",
[N_VERSION] = "version",
[N_FTYPE] = "ftype",
+ [N_PARENT] = "parent",
[N_MAX_OPTS] = NULL,
},
.subopt_params = {
@@ -679,6 +681,14 @@ static struct opt_params nopts = {
.maxval = 1,
.defaultval = 1,
},
+ { .index = N_PARENT,
+ .conflicts = { { NULL, LAST_CONFLICT } },
+ .minval = 0,
+ .maxval = 1,
+ .defaultval = 1,
+ },
+
+
},
};
@@ -1040,7 +1050,7 @@ usage( void )
sunit=value|su=num,sectsize=num,lazy-count=0|1,\n\
concurrency=num]\n\
/* label */ [-L label (maximum 12 characters)]\n\
-/* naming */ [-n size=num,version=2|ci,ftype=0|1]\n\
+/* naming */ [-n size=num,version=2|ci,ftype=0|1,parent=0|1]]\n\
/* no-op info only */ [-N]\n\
/* prototype file */ [-p fname]\n\
/* quiet */ [-q]\n\
@@ -1878,6 +1888,9 @@ naming_opts_parser(
case N_FTYPE:
cli->sb_feat.dirftype = getnum(value, opts, subopt);
break;
+ case N_PARENT:
+ cli->sb_feat.parent_pointers = getnum(value, &nopts, N_PARENT);
+ break;
default:
return -EINVAL;
}
@@ -2385,6 +2398,14 @@ _("exchange-range not supported without CRC support\n"));
usage();
}
cli->sb_feat.exchrange = false;
+
+ if (cli->sb_feat.parent_pointers &&
+ cli_opt_set(&nopts, N_PARENT)) {
+ fprintf(stderr,
+_("parent pointers not supported without CRC support\n"));
+ usage();
+ }
+ cli->sb_feat.parent_pointers = false;
}
if (!cli->sb_feat.finobt) {
@@ -2419,6 +2440,17 @@ _("cowextsize not supported without reflink support\n"));
usage();
}
+ /*
+ * Turn on exchange-range if parent pointers are enabled and the caller
+ * did not provide an explicit exchange-range parameter so that users
+ * can take advantage of online repair. It's not required for correct
+ * operation, but it costs us nothing to enable it.
+ */
+ if (cli->sb_feat.parent_pointers && !cli->sb_feat.exchrange &&
+ !cli_opt_set(&iopts, I_EXCHANGE)) {
+ cli->sb_feat.exchrange = true;
+ }
+
/*
* Copy features across to config structure now.
*/
@@ -3458,8 +3490,6 @@ sb_set_features(
sbp->sb_features2 |= XFS_SB_VERSION2_LAZYSBCOUNTBIT;
if (fp->projid32bit)
sbp->sb_features2 |= XFS_SB_VERSION2_PROJID32BIT;
- if (fp->parent_pointers)
- sbp->sb_features2 |= XFS_SB_VERSION2_PARENTBIT;
if (fp->crcs_enabled)
sbp->sb_features2 |= XFS_SB_VERSION2_CRCBIT;
if (fp->attr_version == 2)
@@ -3520,6 +3550,15 @@ sb_set_features(
sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_NREXT64;
if (fp->exchrange)
sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_EXCHRANGE;
+ if (fp->parent_pointers) {
+ sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_PARENT;
+ /*
+ * Set ATTRBIT even if mkfs doesn't write out a single parent
+ * pointer so that the kernel doesn't have to do that for us
+ * with a synchronous write to the primary super at runtime.
+ */
+ sbp->sb_versionnum |= XFS_SB_VERSION_ATTRBIT;
+ }
}
/*
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/2] xfs: create a blob array data structure
2024-07-30 0:21 ` [PATCHSET v13.8 19/23] xfsprogs: scrubbing for " Darrick J. Wong
@ 2024-07-30 1:25 ` Darrick J. Wong
2024-07-30 1:25 ` [PATCH 2/2] man2: update ioctl_xfs_scrub_metadata.2 for parent pointers Darrick J. Wong
1 sibling, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:25 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Create a simple 'blob array' data structure for storage of arbitrarily
sized metadata objects that will be used to reconstruct metadata. For
the intended usage (temporarily storing extended attribute names and
values) we only have to support storing objects and retrieving them.
Use the xfile abstraction to store the attribute information in memory
that can be swapped out.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/Makefile | 2 +
libxfs/xfblob.c | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfblob.h | 24 +++++++++
libxfs/xfile.c | 11 ++++
libxfs/xfile.h | 1
5 files changed, 185 insertions(+)
create mode 100644 libxfs/xfblob.c
create mode 100644 libxfs/xfblob.h
diff --git a/libxfs/Makefile b/libxfs/Makefile
index 9fb53d9cc..4e8f9a135 100644
--- a/libxfs/Makefile
+++ b/libxfs/Makefile
@@ -28,6 +28,7 @@ HFILES = \
linux-err.h \
topology.h \
buf_mem.h \
+ xfblob.h \
xfile.h \
xfs_ag_resv.h \
xfs_alloc.h \
@@ -73,6 +74,7 @@ CFILES = buf_mem.c \
topology.c \
trans.c \
util.c \
+ xfblob.c \
xfile.c \
xfs_ag.c \
xfs_ag_resv.c \
diff --git a/libxfs/xfblob.c b/libxfs/xfblob.c
new file mode 100644
index 000000000..7d8caaa4c
--- /dev/null
+++ b/libxfs/xfblob.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "libxfs_priv.h"
+#include "libxfs.h"
+#include "libxfs/xfile.h"
+#include "libxfs/xfblob.h"
+
+/*
+ * XFS Blob Storage
+ * ================
+ * Stores and retrieves blobs using an xfile. Objects are appended to the file
+ * and the offset is returned as a magic cookie for retrieval.
+ */
+
+#define XB_KEY_MAGIC 0xABAADDAD
+struct xb_key {
+ uint32_t xb_magic; /* XB_KEY_MAGIC */
+ uint32_t xb_size; /* size of the blob, in bytes */
+ loff_t xb_offset; /* byte offset of this key */
+ /* blob comes after here */
+} __packed;
+
+/* Initialize a blob storage object. */
+int
+xfblob_create(
+ const char *description,
+ struct xfblob **blobp)
+{
+ struct xfblob *blob;
+ struct xfile *xfile;
+ int error;
+
+ error = xfile_create(description, 0, &xfile);
+ if (error)
+ return error;
+
+ blob = malloc(sizeof(struct xfblob));
+ if (!blob) {
+ error = -ENOMEM;
+ goto out_xfile;
+ }
+
+ blob->xfile = xfile;
+ blob->last_offset = PAGE_SIZE;
+
+ *blobp = blob;
+ return 0;
+
+out_xfile:
+ xfile_destroy(xfile);
+ return error;
+}
+
+/* Destroy a blob storage object. */
+void
+xfblob_destroy(
+ struct xfblob *blob)
+{
+ xfile_destroy(blob->xfile);
+ kfree(blob);
+}
+
+/* Retrieve a blob. */
+int
+xfblob_load(
+ struct xfblob *blob,
+ xfblob_cookie cookie,
+ void *ptr,
+ uint32_t size)
+{
+ struct xb_key key;
+ int error;
+
+ error = xfile_load(blob->xfile, &key, sizeof(key), cookie);
+ if (error)
+ return error;
+
+ if (key.xb_magic != XB_KEY_MAGIC || key.xb_offset != cookie) {
+ ASSERT(0);
+ return -ENODATA;
+ }
+ if (size < key.xb_size) {
+ ASSERT(0);
+ return -EFBIG;
+ }
+
+ return xfile_load(blob->xfile, ptr, key.xb_size,
+ cookie + sizeof(key));
+}
+
+/* Store a blob. */
+int
+xfblob_store(
+ struct xfblob *blob,
+ xfblob_cookie *cookie,
+ const void *ptr,
+ uint32_t size)
+{
+ struct xb_key key = {
+ .xb_offset = blob->last_offset,
+ .xb_magic = XB_KEY_MAGIC,
+ .xb_size = size,
+ };
+ loff_t pos = blob->last_offset;
+ int error;
+
+ error = xfile_store(blob->xfile, &key, sizeof(key), pos);
+ if (error)
+ return error;
+
+ pos += sizeof(key);
+ error = xfile_store(blob->xfile, ptr, size, pos);
+ if (error)
+ goto out_err;
+
+ *cookie = blob->last_offset;
+ blob->last_offset += sizeof(key) + size;
+ return 0;
+out_err:
+ xfile_discard(blob->xfile, blob->last_offset, sizeof(key));
+ return error;
+}
+
+/* Free a blob. */
+int
+xfblob_free(
+ struct xfblob *blob,
+ xfblob_cookie cookie)
+{
+ struct xb_key key;
+ int error;
+
+ error = xfile_load(blob->xfile, &key, sizeof(key), cookie);
+ if (error)
+ return error;
+
+ if (key.xb_magic != XB_KEY_MAGIC || key.xb_offset != cookie) {
+ ASSERT(0);
+ return -ENODATA;
+ }
+
+ xfile_discard(blob->xfile, cookie, sizeof(key) + key.xb_size);
+ return 0;
+}
diff --git a/libxfs/xfblob.h b/libxfs/xfblob.h
new file mode 100644
index 000000000..28bf4ab28
--- /dev/null
+++ b/libxfs/xfblob.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2022-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __XFS_SCRUB_XFBLOB_H__
+#define __XFS_SCRUB_XFBLOB_H__
+
+struct xfblob {
+ struct xfile *xfile;
+ loff_t last_offset;
+};
+
+typedef loff_t xfblob_cookie;
+
+int xfblob_create(const char *descr, struct xfblob **blobp);
+void xfblob_destroy(struct xfblob *blob);
+int xfblob_load(struct xfblob *blob, xfblob_cookie cookie, void *ptr,
+ uint32_t size);
+int xfblob_store(struct xfblob *blob, xfblob_cookie *cookie, const void *ptr,
+ uint32_t size);
+int xfblob_free(struct xfblob *blob, xfblob_cookie cookie);
+
+#endif /* __XFS_SCRUB_XFBLOB_H__ */
diff --git a/libxfs/xfile.c b/libxfs/xfile.c
index 6e0fa809a..b4908b49b 100644
--- a/libxfs/xfile.c
+++ b/libxfs/xfile.c
@@ -391,3 +391,14 @@ xfile_bytes(
return (unsigned long long)statbuf.st_blocks << 9;
}
+
+/* Discard pages backing a range of the xfile. */
+void
+xfile_discard(
+ struct xfile *xf,
+ loff_t pos,
+ unsigned long long count)
+{
+ fallocate(xf->fcb->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+ pos, count);
+}
diff --git a/libxfs/xfile.h b/libxfs/xfile.h
index 180a42bbb..7ee8b90cd 100644
--- a/libxfs/xfile.h
+++ b/libxfs/xfile.h
@@ -30,5 +30,6 @@ ssize_t xfile_load(struct xfile *xf, void *buf, size_t count, loff_t pos);
ssize_t xfile_store(struct xfile *xf, const void *buf, size_t count, loff_t pos);
unsigned long long xfile_bytes(struct xfile *xf);
+void xfile_discard(struct xfile *xf, loff_t pos, unsigned long long count);
#endif /* __LIBXFS_XFILE_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/2] man2: update ioctl_xfs_scrub_metadata.2 for parent pointers
2024-07-30 0:21 ` [PATCHSET v13.8 19/23] xfsprogs: scrubbing for " Darrick J. Wong
2024-07-30 1:25 ` [PATCH 1/2] xfs: create a blob array data structure Darrick J. Wong
@ 2024-07-30 1:25 ` Darrick J. Wong
1 sibling, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:25 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Update the man page for the scrub ioctl to reflect the new scrubbing
abilities when parent pointers are enabled.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man2/ioctl_xfs_scrub_metadata.2 | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/man/man2/ioctl_xfs_scrub_metadata.2 b/man/man2/ioctl_xfs_scrub_metadata.2
index 9963f1913..75ae52bb5 100644
--- a/man/man2/ioctl_xfs_scrub_metadata.2
+++ b/man/man2/ioctl_xfs_scrub_metadata.2
@@ -109,12 +109,11 @@ must be zero.
.nf
.B XFS_SCRUB_TYPE_BMBTD
.B XFS_SCRUB_TYPE_BMBTA
+.fi
+.TP
.B XFS_SCRUB_TYPE_BMBTC
-.fi
-.TP
-.B XFS_SCRUB_TYPE_PARENT
Examine a given inode's data block map, extended attribute block map,
-copy on write block map, or parent inode pointer.
+or copy on write block map.
Inode records are examined for obviously incorrect values and
discrepancies with the three block map types.
The block maps are checked for obviously wrong values and
@@ -133,9 +132,22 @@ The inode to examine can be specified in the same manner as
.TP
.B XFS_SCRUB_TYPE_DIR
Examine the entries in a given directory for invalid data or dangling pointers.
+If the filesystem supports directory parent pointers, each entry will be
+checked to confirm that the child file has a matching parent pointer.
The directory to examine can be specified in the same manner as
.BR XFS_SCRUB_TYPE_INODE "."
+.TP
+.B XFS_SCRUB_TYPE_PARENT
+For filesystems that support directory parent pointers, this scrubber
+examines all the parent pointers attached to a file and confirms that the
+parent directory has an entry matching the parent pointer.
+For filesystems that do not support directory parent pointers, this scrubber
+checks that a subdirectory's dotdot entry points to a directory with an entry
+that points back to the subdirectory.
+The inode to examine can be specified in the same manner as
+.BR XFS_SCRUB_TYPE_INODE "."
+
.TP
.B XFS_SCRUB_TYPE_SYMLINK
Examine the target of a symbolic link for obvious pathname problems.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 01/12] xfs_db: remove some boilerplate from xfs_attr_set
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
@ 2024-07-30 1:25 ` Darrick J. Wong
2024-07-30 1:26 ` [PATCH 02/12] xfs_db: actually report errors from libxfs_attr_set Darrick J. Wong
` (10 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:25 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
In preparation for online/offline repair wanting to use xfs_attr_set,
move some of the boilerplate out of this function into the callers.
Repair can initialize the da_args completely, and the userspace flag
handling/twisting goes away once we move it to xfs_attr_change.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/attrset.c | 18 ++++++++++++++++--
libxfs/libxfs_api_defs.h | 1 +
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/db/attrset.c b/db/attrset.c
index d9ab79fa7..008662571 100644
--- a/db/attrset.c
+++ b/db/attrset.c
@@ -111,7 +111,11 @@ attr_set_f(
int argc,
char **argv)
{
- struct xfs_da_args args = { };
+ struct xfs_da_args args = {
+ .geo = mp->m_attr_geo,
+ .whichfork = XFS_ATTR_FORK,
+ .op_flags = XFS_DA_OP_OKNOENT,
+ };
char *sp;
char *name_from_file = NULL;
char *value_from_file = NULL;
@@ -253,6 +257,9 @@ attr_set_f(
goto out;
}
+ args.owner = iocur_top->ino;
+ libxfs_attr_sethash(&args);
+
if (libxfs_attr_set(&args, op, false)) {
dbprintf(_("failed to set attr %s on inode %llu\n"),
args.name, (unsigned long long)iocur_top->ino);
@@ -277,7 +284,11 @@ attr_remove_f(
int argc,
char **argv)
{
- struct xfs_da_args args = { };
+ struct xfs_da_args args = {
+ .geo = mp->m_attr_geo,
+ .whichfork = XFS_ATTR_FORK,
+ .op_flags = XFS_DA_OP_OKNOENT,
+ };
char *name_from_file = NULL;
int c;
@@ -365,6 +376,9 @@ attr_remove_f(
goto out;
}
+ args.owner = iocur_top->ino;
+ libxfs_attr_sethash(&args);
+
if (libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE, false)) {
dbprintf(_("failed to remove attr %s from inode %llu\n"),
(unsigned char *)args.name,
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index df83aabdc..bf1d3c9d3 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -47,6 +47,7 @@
#define xfs_attr_leaf_newentsize libxfs_attr_leaf_newentsize
#define xfs_attr_namecheck libxfs_attr_namecheck
#define xfs_attr_set libxfs_attr_set
+#define xfs_attr_sethash libxfs_attr_sethash
#define xfs_attr_sf_firstentry libxfs_attr_sf_firstentry
#define xfs_attr_shortform_verify libxfs_attr_shortform_verify
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 02/12] xfs_db: actually report errors from libxfs_attr_set
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
2024-07-30 1:25 ` [PATCH 01/12] xfs_db: remove some boilerplate from xfs_attr_set Darrick J. Wong
@ 2024-07-30 1:26 ` Darrick J. Wong
2024-07-30 1:26 ` [PATCH 03/12] xfs_repair: junk parent pointer attributes when filesystem doesn't support them Darrick J. Wong
` (9 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:26 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Actually tell the user what went wrong when setting or removing xattrs.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
db/attrset.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/db/attrset.c b/db/attrset.c
index 008662571..81d530055 100644
--- a/db/attrset.c
+++ b/db/attrset.c
@@ -121,6 +121,7 @@ attr_set_f(
char *value_from_file = NULL;
enum xfs_attr_update op = XFS_ATTRUPDATE_UPSERT;
int c;
+ int error;
if (cur_typ == NULL) {
dbprintf(_("no current type\n"));
@@ -260,9 +261,11 @@ attr_set_f(
args.owner = iocur_top->ino;
libxfs_attr_sethash(&args);
- if (libxfs_attr_set(&args, op, false)) {
- dbprintf(_("failed to set attr %s on inode %llu\n"),
- args.name, (unsigned long long)iocur_top->ino);
+ error = -libxfs_attr_set(&args, op, false);
+ if (error) {
+ dbprintf(_("failed to set attr %s on inode %llu: %s\n"),
+ args.name, (unsigned long long)iocur_top->ino,
+ strerror(error));
goto out;
}
@@ -291,6 +294,7 @@ attr_remove_f(
};
char *name_from_file = NULL;
int c;
+ int error;
if (cur_typ == NULL) {
dbprintf(_("no current type\n"));
@@ -379,10 +383,12 @@ attr_remove_f(
args.owner = iocur_top->ino;
libxfs_attr_sethash(&args);
- if (libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE, false)) {
- dbprintf(_("failed to remove attr %s from inode %llu\n"),
+ error = -libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE, false);
+ if (error) {
+ dbprintf(_("failed to remove attr %s from inode %llu: %s\n"),
(unsigned char *)args.name,
- (unsigned long long)iocur_top->ino);
+ (unsigned long long)iocur_top->ino,
+ strerror(error));
goto out;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 03/12] xfs_repair: junk parent pointer attributes when filesystem doesn't support them
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
2024-07-30 1:25 ` [PATCH 01/12] xfs_db: remove some boilerplate from xfs_attr_set Darrick J. Wong
2024-07-30 1:26 ` [PATCH 02/12] xfs_db: actually report errors from libxfs_attr_set Darrick J. Wong
@ 2024-07-30 1:26 ` Darrick J. Wong
2024-07-30 1:26 ` [PATCH 04/12] xfs_repair: add parent pointers when messing with /lost+found Darrick J. Wong
` (8 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:26 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Drop a parent pointer xattr if the filesystem doesn't support parent
pointers.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
repair/attr_repair.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/repair/attr_repair.c b/repair/attr_repair.c
index 2e97fd977..50159b9a5 100644
--- a/repair/attr_repair.c
+++ b/repair/attr_repair.c
@@ -327,6 +327,13 @@ process_shortform_attr(
NULL, currententry->namelen,
currententry->valuelen);
+ if ((currententry->flags & XFS_ATTR_PARENT) &&
+ !xfs_has_parent(mp)) {
+ do_warn(
+ _("parent pointer found on filesystem that doesn't support parent pointers\n"));
+ junkit |= 1;
+ }
+
remainingspace = remainingspace -
xfs_attr_sf_entsize(currententry);
@@ -527,6 +534,15 @@ process_leaf_attr_local(
return -1;
}
}
+
+ if ((entry->flags & XFS_ATTR_PARENT) && !xfs_has_parent(mp)) {
+ do_warn(
+ _("parent pointer found in attribute entry %d in attr block %u, inode %"
+ PRIu64 " on filesystem that doesn't support parent pointers\n"),
+ i, da_bno, ino);
+ return -1;
+ }
+
return xfs_attr_leaf_entsize_local(local->namelen,
be16_to_cpu(local->valuelen));
}
@@ -562,6 +578,20 @@ process_leaf_attr_remote(
return -1;
}
+ if (entry->flags & XFS_ATTR_PARENT) {
+ if (!xfs_has_parent(mp))
+ do_warn(
+ _("parent pointer found in attribute entry %d in attr block %u, inode %"
+ PRIu64 " on filesystem that doesn't support parent pointers\n"),
+ i, da_bno, ino);
+ else
+ do_warn(
+ _("parent pointer found in attribute entry %d in attr block %u, inode %"
+ PRIu64 " with bogus remote value\n"),
+ i, da_bno, ino);
+ return -1;
+ }
+
value = malloc(be32_to_cpu(remotep->valuelen));
if (value == NULL) {
do_warn(
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 04/12] xfs_repair: add parent pointers when messing with /lost+found
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:26 ` [PATCH 03/12] xfs_repair: junk parent pointer attributes when filesystem doesn't support them Darrick J. Wong
@ 2024-07-30 1:26 ` Darrick J. Wong
2024-07-30 1:26 ` [PATCH 05/12] xfs_repair: junk duplicate hashtab entries when processing sf dirents Darrick J. Wong
` (7 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:26 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Make sure that the /lost+found gets created with parent pointers, and
that lost children being put in there get new parent pointers.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/libxfs_api_defs.h | 2 +
repair/phase6.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 78 insertions(+)
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index bf1d3c9d3..d3611e05b 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -52,6 +52,7 @@
#define xfs_attr_shortform_verify libxfs_attr_shortform_verify
#define __xfs_bmap_add_free __libxfs_bmap_add_free
+#define xfs_bmap_add_attrfork libxfs_bmap_add_attrfork
#define xfs_bmap_validate_extent libxfs_bmap_validate_extent
#define xfs_bmapi_read libxfs_bmapi_read
#define xfs_bmapi_remap libxfs_bmapi_remap
@@ -205,6 +206,7 @@
#define xfs_parent_addname libxfs_parent_addname
#define xfs_parent_finish libxfs_parent_finish
#define xfs_parent_hashval libxfs_parent_hashval
+#define xfs_parent_lookup libxfs_parent_lookup
#define xfs_parent_removename libxfs_parent_removename
#define xfs_parent_start libxfs_parent_start
#define xfs_parent_from_attr libxfs_parent_from_attr
diff --git a/repair/phase6.c b/repair/phase6.c
index 47dd9de27..791f7d36f 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -903,6 +903,12 @@ mk_orphanage(xfs_mount_t *mp)
const int mode = 0755;
int nres;
struct xfs_name xname;
+ struct xfs_parent_args *ppargs = NULL;
+
+ i = -libxfs_parent_start(mp, &ppargs);
+ if (i)
+ do_error(_("%d - couldn't allocate parent pointer for %s\n"),
+ i, ORPHANAGE);
/*
* check for an existing lost+found first, if it exists, return
@@ -994,6 +1000,14 @@ mk_orphanage(xfs_mount_t *mp)
_("can't make %s, createname error %d\n"),
ORPHANAGE, error);
+ if (ppargs) {
+ error = -libxfs_parent_addname(tp, ppargs, pip, &xname, ip);
+ if (error)
+ do_error(
+ _("can't make %s, parent addname error %d\n"),
+ ORPHANAGE, error);
+ }
+
/*
* bump up the link count in the root directory to account
* for .. in the new directory, and update the irec copy of the
@@ -1016,10 +1030,52 @@ mk_orphanage(xfs_mount_t *mp)
libxfs_irele(ip);
out_pip:
libxfs_irele(pip);
+ libxfs_parent_finish(mp, ppargs);
return(ino);
}
+/*
+ * Add a parent pointer back to the orphanage for any file we're moving into
+ * the orphanage, being careful not to trip over any existing parent pointer.
+ * You never know when the orphanage might get corrupted.
+ */
+static void
+add_orphan_pptr(
+ struct xfs_trans *tp,
+ struct xfs_inode *orphanage_ip,
+ const struct xfs_name *xname,
+ struct xfs_inode *ip,
+ struct xfs_parent_args *ppargs)
+{
+ struct xfs_parent_rec pptr = { };
+ struct xfs_da_args scratch;
+ int error;
+
+ xfs_inode_to_parent_rec(&pptr, orphanage_ip);
+ error = -libxfs_parent_lookup(tp, ip, xname, &pptr, &scratch);
+ if (!error)
+ return;
+ if (error != ENOATTR)
+ do_log(
+ _("cannot look up parent pointer for '%.*s', err %d\n"),
+ xname->len, xname->name, error);
+
+ if (!xfs_inode_has_attr_fork(ip)) {
+ error = -libxfs_bmap_add_attrfork(tp, ip,
+ sizeof(struct xfs_attr_sf_hdr), true);
+ if (error)
+ do_error(_("can't add attr fork to inode 0x%llx\n"),
+ (unsigned long long)ip->i_ino);
+ }
+
+ error = -libxfs_parent_addname(tp, ppargs, orphanage_ip, xname, ip);
+ if (error)
+ do_error(
+ _("can't add parent pointer for '%.*s', error %d\n"),
+ xname->len, xname->name, error);
+}
+
/*
* move a file to the orphange.
*/
@@ -1040,6 +1096,13 @@ mv_orphanage(
ino_tree_node_t *irec;
int ino_offset = 0;
struct xfs_name xname;
+ struct xfs_parent_args *ppargs;
+
+ err = -libxfs_parent_start(mp, &ppargs);
+ if (err)
+ do_error(
+ _("%d - couldn't allocate parent pointer for lost inode\n"),
+ err);
xname.name = fname;
xname.len = snprintf((char *)fname, sizeof(fname), "%llu",
@@ -1091,6 +1154,10 @@ mv_orphanage(
do_error(
_("name create failed in %s (%d)\n"), ORPHANAGE, err);
+ if (ppargs)
+ add_orphan_pptr(tp, orphanage_ip, &xname,
+ ino_p, ppargs);
+
if (irec)
add_inode_ref(irec, ino_offset);
else
@@ -1125,6 +1192,10 @@ mv_orphanage(
do_error(
_("name create failed in %s (%d)\n"), ORPHANAGE, err);
+ if (ppargs)
+ add_orphan_pptr(tp, orphanage_ip, &xname,
+ ino_p, ppargs);
+
if (irec)
add_inode_ref(irec, ino_offset);
else
@@ -1173,6 +1244,10 @@ mv_orphanage(
_("name create failed in %s (%d)\n"), ORPHANAGE, err);
ASSERT(err == 0);
+ if (ppargs)
+ add_orphan_pptr(tp, orphanage_ip, &xname, ino_p,
+ ppargs);
+
set_nlink(VFS_I(ino_p), 1);
libxfs_trans_log_inode(tp, ino_p, XFS_ILOG_CORE);
err = -libxfs_trans_commit(tp);
@@ -1182,6 +1257,7 @@ mv_orphanage(
}
libxfs_irele(ino_p);
libxfs_irele(orphanage_ip);
+ libxfs_parent_finish(mp, ppargs);
}
static int
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 05/12] xfs_repair: junk duplicate hashtab entries when processing sf dirents
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:26 ` [PATCH 04/12] xfs_repair: add parent pointers when messing with /lost+found Darrick J. Wong
@ 2024-07-30 1:26 ` Darrick J. Wong
2024-07-30 1:27 ` [PATCH 06/12] xfs_repair: build a parent pointer index Darrick J. Wong
` (6 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:26 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
dir_hash_add() adds the passed-in dirent to the directory hashtab even
if there's already a duplicate. Therefore, if we detect a duplicate or
a garbage entry while processing the a shortform directory's entries, we
need to junk the newly added entry, just like we do when processing
directory data blocks.
This will become particularly relevant in the next patch, where we
generate a master index of parent pointers from the non-junked hashtab
entries of each directory that phase6 scans.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
repair/phase6.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/repair/phase6.c b/repair/phase6.c
index 791f7d36f..9d41aad78 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -2562,6 +2562,7 @@ shortform_dir2_entry_check(
struct xfs_dir2_sf_entry *next_sfep;
struct xfs_ifork *ifp;
struct ino_tree_node *irec;
+ xfs_dir2_dataptr_t diroffset;
int max_size;
int ino_offset;
int i;
@@ -2739,8 +2740,9 @@ shortform_dir2_entry_check(
/*
* check for duplicate names in directory.
*/
- dup_inum = dir_hash_add(mp, hashtab, (xfs_dir2_dataptr_t)
- (sfep - xfs_dir2_sf_firstentry(sfp)),
+ diroffset = xfs_dir2_byte_to_dataptr(
+ xfs_dir2_sf_get_offset(sfep));
+ dup_inum = dir_hash_add(mp, hashtab, diroffset,
lino, sfep->namelen, sfep->name,
libxfs_dir2_sf_get_ftype(mp, sfep));
if (dup_inum != NULLFSINO) {
@@ -2775,6 +2777,7 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " already points to ino %" PR
next_sfep = shortform_dir2_junk(mp, sfp, sfep,
lino, &max_size, &i,
&bytes_deleted, ino_dirty);
+ dir_hash_junkit(hashtab, diroffset);
continue;
} else if (parent == ino) {
add_inode_reached(irec, ino_offset);
@@ -2799,6 +2802,7 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " already points to ino %" PR
next_sfep = shortform_dir2_junk(mp, sfp, sfep,
lino, &max_size, &i,
&bytes_deleted, ino_dirty);
+ dir_hash_junkit(hashtab, diroffset);
continue;
}
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 06/12] xfs_repair: build a parent pointer index
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 1:26 ` [PATCH 05/12] xfs_repair: junk duplicate hashtab entries when processing sf dirents Darrick J. Wong
@ 2024-07-30 1:27 ` Darrick J. Wong
2024-07-30 1:27 ` [PATCH 07/12] xfs_repair: move the global dirent name store to a separate object Darrick J. Wong
` (5 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:27 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
When we're walking directories during phase 6, build an index of parent
pointers that we expect to find.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
repair/Makefile | 2 +
repair/phase6.c | 35 +++++++++
repair/pptr.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
repair/pptr.h | 15 ++++
4 files changed, 267 insertions(+)
create mode 100644 repair/pptr.c
create mode 100644 repair/pptr.h
diff --git a/repair/Makefile b/repair/Makefile
index d94858878..b0acc8c46 100644
--- a/repair/Makefile
+++ b/repair/Makefile
@@ -24,6 +24,7 @@ HFILES = \
err_protos.h \
globals.h \
incore.h \
+ pptr.h \
prefetch.h \
progress.h \
protos.h \
@@ -63,6 +64,7 @@ CFILES = \
phase5.c \
phase6.c \
phase7.c \
+ pptr.c \
prefetch.c \
progress.c \
quotacheck.c \
diff --git a/repair/phase6.c b/repair/phase6.c
index 9d41aad78..fe56feb6e 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -18,6 +18,7 @@
#include "dinode.h"
#include "progress.h"
#include "versions.h"
+#include "repair/pptr.h"
static struct cred zerocr;
static struct fsxattr zerofsx;
@@ -999,6 +1000,7 @@ mk_orphanage(xfs_mount_t *mp)
do_error(
_("can't make %s, createname error %d\n"),
ORPHANAGE, error);
+ add_parent_ptr(ip->i_ino, (unsigned char *)ORPHANAGE, pip, false);
if (ppargs) {
error = -libxfs_parent_addname(tp, ppargs, pip, &xname, ip);
@@ -1255,6 +1257,10 @@ mv_orphanage(
do_error(
_("orphanage name create failed (%d)\n"), err);
}
+
+ if (xfs_has_parent(mp))
+ add_parent_ptr(ino_p->i_ino, xname.name, orphanage_ip, false);
+
libxfs_irele(ino_p);
libxfs_irele(orphanage_ip);
libxfs_parent_finish(mp, ppargs);
@@ -2894,6 +2900,30 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " already points to ino %" PR
}
}
+static void
+dir_hash_add_parent_ptrs(
+ struct xfs_inode *dp,
+ struct dir_hash_tab *hashtab)
+{
+ struct dir_hash_ent *p;
+
+ if (!xfs_has_parent(dp->i_mount))
+ return;
+
+ for (p = hashtab->first; p; p = p->nextbyorder) {
+ if (p->junkit)
+ continue;
+ if (p->name.name[0] == '/')
+ continue;
+ if (p->name.name[0] == '.' &&
+ (p->name.len == 1 ||
+ (p->name.len == 2 && p->name.name[1] == '.')))
+ continue;
+
+ add_parent_ptr(p->inum, p->name.name, dp, dotdot_update);
+ }
+}
+
/*
* processes all reachable inodes in directories
*/
@@ -3020,6 +3050,7 @@ _("error %d fixing shortform directory %llu\n"),
default:
break;
}
+ dir_hash_add_parent_ptrs(ip, hashtab);
dir_hash_done(hashtab);
/*
@@ -3311,6 +3342,8 @@ phase6(xfs_mount_t *mp)
ino_tree_node_t *irec;
int i;
+ parent_ptr_init(mp);
+
memset(&zerocr, 0, sizeof(struct cred));
memset(&zerofsx, 0, sizeof(struct fsxattr));
orphanage_ino = 0;
@@ -3411,4 +3444,6 @@ _(" - resetting contents of realtime bitmap and summary inodes\n"));
irec = next_ino_rec(irec);
}
}
+
+ parent_ptr_free(mp);
}
diff --git a/repair/pptr.c b/repair/pptr.c
new file mode 100644
index 000000000..193daa0a5
--- /dev/null
+++ b/repair/pptr.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "libxfs.h"
+#include "libxfs/xfile.h"
+#include "libxfs/xfblob.h"
+#include "libfrog/platform.h"
+#include "repair/err_protos.h"
+#include "repair/slab.h"
+#include "repair/pptr.h"
+
+#undef PPTR_DEBUG
+
+#ifdef PPTR_DEBUG
+# define dbg_printf(f, a...) do {printf(f, ## a); fflush(stdout); } while (0)
+#else
+# define dbg_printf(f, a...)
+#endif
+
+/*
+ * Parent Pointer Validation
+ * =========================
+ *
+ * Phase 6 validates the connectivity of the directory tree after validating
+ * that all the space metadata are correct, and confirming all the inodes that
+ * we intend to keep. The first part of phase 6 walks the directories of the
+ * filesystem to ensure that every file that isn't the root directory has a
+ * parent. Unconnected files are attached to the orphanage. Filesystems with
+ * the directory parent pointer feature enabled must also ensure that for every
+ * directory entry that points to a child file, that child has a matching
+ * parent pointer.
+ *
+ * There are many ways that we could check the parent pointers, but the means
+ * that we have chosen is to build a per-AG master index of all parent pointers
+ * of all inodes stored in that AG, and use that as the basis for comparison.
+ * This consumes a lot of memory, but performing both a forward scan to check
+ * dirent -> parent pointer and a backwards scan of parent pointer -> dirent
+ * takes longer than the simple method presented here. Userspace adds the
+ * additional twist that inodes are not cached (and there are no ILOCKs), which
+ * makes that approach even less attractive.
+ *
+ * During the directory walk at the start of phase 6, we transform each child
+ * directory entry found into its parent pointer equivalent. In other words,
+ * the forward information:
+ *
+ * (dir_ino, name, child_ino)
+ *
+ * becomes this backwards information:
+ *
+ * (child_agino*, dir_ino*, dir_gen, name*)
+ *
+ * Key fields are starred.
+ *
+ * This tuple is recorded in the per-AG master parent pointer index. Note
+ * that names are stored separately in an xfblob data structure so that the
+ * rest of the information can be sorted and processed as fixed-size records;
+ * the incore parent pointer record contains a pointer to the xfblob data.
+ */
+
+struct ag_pptr {
+ /* parent directory handle */
+ xfs_ino_t parent_ino;
+ uint32_t parent_gen;
+
+ /* dirent name length */
+ unsigned short namelen;
+
+ /* AG_PPTR_* flags */
+ unsigned short flags;
+
+ /* cookie for the actual dirent name */
+ xfblob_cookie name_cookie;
+
+ /* agino of the child file */
+ xfs_agino_t child_agino;
+
+ /* hash of the dirent name */
+ xfs_dahash_t namehash;
+};
+
+/* This might be a duplicate due to dotdot reprocessing */
+#define AG_PPTR_POSSIBLE_DUP (1U << 0)
+
+struct ag_pptrs {
+ /* Lock to protect pptr_recs during the dirent scan. */
+ pthread_mutex_t lock;
+
+ /* Parent pointer records for files in this AG. */
+ struct xfs_slab *pptr_recs;
+};
+
+/* Global names storage file. */
+static struct xfblob *names;
+static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct ag_pptrs *fs_pptrs;
+
+void
+parent_ptr_free(
+ struct xfs_mount *mp)
+{
+ xfs_agnumber_t agno;
+
+ if (!xfs_has_parent(mp))
+ return;
+
+ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+ free_slab(&fs_pptrs[agno].pptr_recs);
+ pthread_mutex_destroy(&fs_pptrs[agno].lock);
+ }
+ free(fs_pptrs);
+ fs_pptrs = NULL;
+
+ xfblob_destroy(names);
+}
+
+void
+parent_ptr_init(
+ struct xfs_mount *mp)
+{
+ char *descr;
+ xfs_agnumber_t agno;
+ int error;
+
+ if (!xfs_has_parent(mp))
+ return;
+
+ descr = kasprintf(GFP_KERNEL, "xfs_repair (%s): parent pointer names",
+ mp->m_fsname);
+ error = -xfblob_create(descr, &names);
+ kfree(descr);
+ if (error)
+ do_error(_("init parent pointer names failed: %s\n"),
+ strerror(error));
+
+ fs_pptrs = calloc(mp->m_sb.sb_agcount, sizeof(struct ag_pptrs));
+ if (!fs_pptrs)
+ do_error(
+ _("init parent pointer per-AG record array failed: %s\n"),
+ strerror(errno));
+
+ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+ error = pthread_mutex_init(&fs_pptrs[agno].lock, NULL);
+ if (error)
+ do_error(
+ _("init agno %u parent pointer lock failed: %s\n"),
+ agno, strerror(error));
+
+ error = -init_slab(&fs_pptrs[agno].pptr_recs,
+ sizeof(struct ag_pptr));
+ if (error)
+ do_error(
+ _("init agno %u parent pointer recs failed: %s\n"),
+ agno, strerror(error));
+ }
+}
+
+/* Remember that @dp has a dirent (@fname, @ino). */
+void
+add_parent_ptr(
+ xfs_ino_t ino,
+ const unsigned char *fname,
+ struct xfs_inode *dp,
+ bool possible_dup)
+{
+ struct xfs_mount *mp = dp->i_mount;
+ struct xfs_name dname = {
+ .name = fname,
+ .len = strlen((char *)fname),
+ };
+ struct ag_pptr ag_pptr = {
+ .child_agino = XFS_INO_TO_AGINO(mp, ino),
+ .parent_ino = dp->i_ino,
+ .parent_gen = VFS_I(dp)->i_generation,
+ .namelen = dname.len,
+ };
+ struct ag_pptrs *ag_pptrs;
+ xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, ino);
+ int error;
+
+ if (!xfs_has_parent(mp))
+ return;
+
+ if (possible_dup)
+ ag_pptr.flags |= AG_PPTR_POSSIBLE_DUP;
+
+ ag_pptr.namehash = libxfs_dir2_hashname(mp, &dname);
+
+ pthread_mutex_lock(&names_mutex);
+ error = -xfblob_store(names, &ag_pptr.name_cookie, fname,
+ ag_pptr.namelen);
+ pthread_mutex_unlock(&names_mutex);
+ if (error)
+ do_error(_("storing name '%s' failed: %s\n"),
+ fname, strerror(error));
+
+ ag_pptrs = &fs_pptrs[agno];
+ pthread_mutex_lock(&ag_pptrs->lock);
+ error = -slab_add(ag_pptrs->pptr_recs, &ag_pptr);
+ pthread_mutex_unlock(&ag_pptrs->lock);
+ if (error)
+ do_error(_("storing name '%s' key failed: %s\n"),
+ fname, strerror(error));
+
+ dbg_printf(
+ _("%s: dp %llu gen 0x%x fname '%s' namehash 0x%x ino %llu namecookie 0x%llx\n"),
+ __func__,
+ (unsigned long long)dp->i_ino,
+ VFS_I(dp)->i_generation,
+ fname,
+ ag_pptr.namehash,
+ (unsigned long long)ino,
+ (unsigned long long)ag_pptr.name_cookie);
+}
diff --git a/repair/pptr.h b/repair/pptr.h
new file mode 100644
index 000000000..752237942
--- /dev/null
+++ b/repair/pptr.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __REPAIR_PPTR_H__
+#define __REPAIR_PPTR_H__
+
+void parent_ptr_free(struct xfs_mount *mp);
+void parent_ptr_init(struct xfs_mount *mp);
+
+void add_parent_ptr(xfs_ino_t ino, const unsigned char *fname,
+ struct xfs_inode *dp, bool possible_dup);
+
+#endif /* __REPAIR_PPTR_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 07/12] xfs_repair: move the global dirent name store to a separate object
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
` (5 preceding siblings ...)
2024-07-30 1:27 ` [PATCH 06/12] xfs_repair: build a parent pointer index Darrick J. Wong
@ 2024-07-30 1:27 ` Darrick J. Wong
2024-07-30 1:27 ` [PATCH 08/12] xfs_repair: deduplicate strings stored in string blob Darrick J. Wong
` (4 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:27 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Abstract the main parent pointer dirent names xfblob object into a
separate data structure to hide implementation details.
The goals here are (a) reduce memory usage when we can by deduplicating
dirent names that exist in multiple directories; and (b) provide a
unique id for each name in the system so that sorting incore parent
pointer records can be done in a stable manner. Fast stable sorting of
records is required for the dirent <-> pptr matching algorithm.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
repair/Makefile | 2 +
repair/pptr.c | 11 ++++---
repair/strblobs.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++
repair/strblobs.h | 19 +++++++++++++
4 files changed, 106 insertions(+), 5 deletions(-)
create mode 100644 repair/strblobs.c
create mode 100644 repair/strblobs.h
diff --git a/repair/Makefile b/repair/Makefile
index b0acc8c46..a36a95e35 100644
--- a/repair/Makefile
+++ b/repair/Makefile
@@ -35,6 +35,7 @@ HFILES = \
rt.h \
scan.h \
slab.h \
+ strblobs.h \
threads.h \
versions.h
@@ -75,6 +76,7 @@ CFILES = \
sb.c \
scan.c \
slab.c \
+ strblobs.c \
threads.c \
versions.c \
xfs_repair.c
diff --git a/repair/pptr.c b/repair/pptr.c
index 193daa0a5..9f219b398 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -10,6 +10,7 @@
#include "repair/err_protos.h"
#include "repair/slab.h"
#include "repair/pptr.h"
+#include "repair/strblobs.h"
#undef PPTR_DEBUG
@@ -56,7 +57,7 @@
* This tuple is recorded in the per-AG master parent pointer index. Note
* that names are stored separately in an xfblob data structure so that the
* rest of the information can be sorted and processed as fixed-size records;
- * the incore parent pointer record contains a pointer to the xfblob data.
+ * the incore parent pointer record contains a pointer to the strblob data.
*/
struct ag_pptr {
@@ -92,7 +93,7 @@ struct ag_pptrs {
};
/* Global names storage file. */
-static struct xfblob *names;
+static struct strblobs *nameblobs;
static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct ag_pptrs *fs_pptrs;
@@ -112,7 +113,7 @@ parent_ptr_free(
free(fs_pptrs);
fs_pptrs = NULL;
- xfblob_destroy(names);
+ strblobs_destroy(&nameblobs);
}
void
@@ -128,7 +129,7 @@ parent_ptr_init(
descr = kasprintf(GFP_KERNEL, "xfs_repair (%s): parent pointer names",
mp->m_fsname);
- error = -xfblob_create(descr, &names);
+ error = strblobs_init(descr, &nameblobs);
kfree(descr);
if (error)
do_error(_("init parent pointer names failed: %s\n"),
@@ -188,7 +189,7 @@ add_parent_ptr(
ag_pptr.namehash = libxfs_dir2_hashname(mp, &dname);
pthread_mutex_lock(&names_mutex);
- error = -xfblob_store(names, &ag_pptr.name_cookie, fname,
+ error = strblobs_store(nameblobs, &ag_pptr.name_cookie, fname,
ag_pptr.namelen);
pthread_mutex_unlock(&names_mutex);
if (error)
diff --git a/repair/strblobs.c b/repair/strblobs.c
new file mode 100644
index 000000000..45d2559c7
--- /dev/null
+++ b/repair/strblobs.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "libxfs.h"
+#include "libxfs/xfile.h"
+#include "libxfs/xfblob.h"
+#include "repair/strblobs.h"
+
+/*
+ * String Blob Structure
+ * =====================
+ *
+ * This data structure wraps the storage of strings with explicit length in an
+ * xfblob structure.
+ */
+struct strblobs {
+ struct xfblob *strings;
+};
+
+/* Initialize a string blob structure. */
+int
+strblobs_init(
+ const char *descr,
+ struct strblobs **sblobs)
+{
+ struct strblobs *sb;
+ int error;
+
+ sb = malloc(sizeof(struct strblobs));
+ if (!sb)
+ return ENOMEM;
+
+ error = -xfblob_create(descr, &sb->strings);
+ if (error)
+ goto out_free;
+
+ *sblobs = sb;
+ return 0;
+
+out_free:
+ free(sb);
+ return error;
+}
+
+/* Deconstruct a string blob structure. */
+void
+strblobs_destroy(
+ struct strblobs **sblobs)
+{
+ struct strblobs *sb = *sblobs;
+
+ xfblob_destroy(sb->strings);
+ free(sb);
+ *sblobs = NULL;
+}
+
+/* Store a string and return a cookie for its retrieval. */
+int
+strblobs_store(
+ struct strblobs *sblobs,
+ xfblob_cookie *str_cookie,
+ const unsigned char *str,
+ unsigned int str_len)
+{
+ return -xfblob_store(sblobs->strings, str_cookie, str, str_len);
+}
+
+/* Retrieve a previously stored string. */
+int
+strblobs_load(
+ struct strblobs *sblobs,
+ xfblob_cookie str_cookie,
+ unsigned char *str,
+ unsigned int str_len)
+{
+ return -xfblob_load(sblobs->strings, str_cookie, str, str_len);
+}
diff --git a/repair/strblobs.h b/repair/strblobs.h
new file mode 100644
index 000000000..27e98eee2
--- /dev/null
+++ b/repair/strblobs.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __REPAIR_STRBLOBS_H__
+#define __REPAIR_STRBLOBS_H__
+
+struct strblobs;
+
+int strblobs_init(const char *descr, struct strblobs **sblobs);
+void strblobs_destroy(struct strblobs **sblobs);
+
+int strblobs_store(struct strblobs *sblobs, xfblob_cookie *str_cookie,
+ const unsigned char *str, unsigned int str_len);
+int strblobs_load(struct strblobs *sblobs, xfblob_cookie str_cookie,
+ unsigned char *str, unsigned int str_len);
+
+#endif /* __REPAIR_STRBLOBS_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 08/12] xfs_repair: deduplicate strings stored in string blob
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
` (6 preceding siblings ...)
2024-07-30 1:27 ` [PATCH 07/12] xfs_repair: move the global dirent name store to a separate object Darrick J. Wong
@ 2024-07-30 1:27 ` Darrick J. Wong
2024-07-30 1:27 ` [PATCH 09/12] xfs_repair: check parent pointers Darrick J. Wong
` (3 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:27 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Reduce the memory requirements of the string blob structure by
deduplicating the strings stored within.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
repair/pptr.c | 13 ++++-
repair/strblobs.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++--
repair/strblobs.h | 9 +++
3 files changed, 153 insertions(+), 9 deletions(-)
diff --git a/repair/pptr.c b/repair/pptr.c
index 9f219b398..f8db57b2c 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -50,7 +50,7 @@
*
* becomes this backwards information:
*
- * (child_agino*, dir_ino*, dir_gen, name*)
+ * (child_agino*, dir_ino*, dir_gen, name_cookie*)
*
* Key fields are starred.
*
@@ -58,6 +58,10 @@
* that names are stored separately in an xfblob data structure so that the
* rest of the information can be sorted and processed as fixed-size records;
* the incore parent pointer record contains a pointer to the strblob data.
+ * Because string blobs are deduplicated, there's a 1:1 mapping of name cookies
+ * to strings, which means that we can use the name cookie as a comparison key
+ * instead of loading the full dentry name every time we want to perform a
+ * comparison.
*/
struct ag_pptr {
@@ -121,15 +125,18 @@ parent_ptr_init(
struct xfs_mount *mp)
{
char *descr;
+ uint64_t iused;
xfs_agnumber_t agno;
int error;
if (!xfs_has_parent(mp))
return;
+ /* One hash bucket per inode, up to about 8M of memory on 64-bit. */
+ iused = min(mp->m_sb.sb_icount - mp->m_sb.sb_ifree, 1048573);
descr = kasprintf(GFP_KERNEL, "xfs_repair (%s): parent pointer names",
mp->m_fsname);
- error = strblobs_init(descr, &nameblobs);
+ error = strblobs_init(descr, iused, &nameblobs);
kfree(descr);
if (error)
do_error(_("init parent pointer names failed: %s\n"),
@@ -190,7 +197,7 @@ add_parent_ptr(
pthread_mutex_lock(&names_mutex);
error = strblobs_store(nameblobs, &ag_pptr.name_cookie, fname,
- ag_pptr.namelen);
+ ag_pptr.namelen, ag_pptr.namehash);
pthread_mutex_unlock(&names_mutex);
if (error)
do_error(_("storing name '%s' failed: %s\n"),
diff --git a/repair/strblobs.c b/repair/strblobs.c
index 45d2559c7..3cd678dbb 100644
--- a/repair/strblobs.c
+++ b/repair/strblobs.c
@@ -13,22 +13,42 @@
* =====================
*
* This data structure wraps the storage of strings with explicit length in an
- * xfblob structure.
+ * xfblob structure. It stores a hashtable of string checksums to provide
+ * fast(ish) lookups of existing strings to enable deduplication of the strings
+ * contained within.
*/
+struct strblob_hashent {
+ struct strblob_hashent *next;
+
+ xfblob_cookie str_cookie;
+ unsigned int str_len;
+ xfs_dahash_t str_hash;
+};
+
struct strblobs {
struct xfblob *strings;
+ unsigned int nr_buckets;
+
+ struct strblob_hashent *buckets[];
};
+static inline size_t strblobs_sizeof(unsigned int nr_buckets)
+{
+ return sizeof(struct strblobs) +
+ (nr_buckets * sizeof(struct strblobs_hashent *));
+}
+
/* Initialize a string blob structure. */
int
strblobs_init(
const char *descr,
+ unsigned int hash_buckets,
struct strblobs **sblobs)
{
struct strblobs *sb;
int error;
- sb = malloc(sizeof(struct strblobs));
+ sb = calloc(strblobs_sizeof(hash_buckets), 1);
if (!sb)
return ENOMEM;
@@ -36,6 +56,7 @@ strblobs_init(
if (error)
goto out_free;
+ sb->nr_buckets = hash_buckets;
*sblobs = sb;
return 0;
@@ -50,21 +71,132 @@ strblobs_destroy(
struct strblobs **sblobs)
{
struct strblobs *sb = *sblobs;
+ struct strblob_hashent *ent, *ent_next;
+ unsigned int bucket;
+
+ for (bucket = 0; bucket < sb->nr_buckets; bucket++) {
+ ent = sb->buckets[bucket];
+ while (ent != NULL) {
+ ent_next = ent->next;
+ free(ent);
+ ent = ent_next;
+ }
+ }
xfblob_destroy(sb->strings);
free(sb);
*sblobs = NULL;
}
+/*
+ * Search the string hashtable for a matching entry. Sets sets the cookie and
+ * returns 0 if one is found; ENOENT if there is no match; or a positive errno.
+ */
+static int
+__strblobs_lookup(
+ struct strblobs *sblobs,
+ xfblob_cookie *str_cookie,
+ const unsigned char *str,
+ unsigned int str_len,
+ xfs_dahash_t str_hash)
+{
+ struct strblob_hashent *ent;
+ unsigned char *buf = NULL;
+ unsigned int bucket;
+ int error;
+
+ bucket = str_hash % sblobs->nr_buckets;
+ ent = sblobs->buckets[bucket];
+
+ for (ent = sblobs->buckets[bucket]; ent != NULL; ent = ent->next) {
+ if (ent->str_len != str_len || ent->str_hash != str_hash)
+ continue;
+
+ if (!buf) {
+ buf = malloc(str_len);
+ if (!buf)
+ return ENOMEM;
+ }
+
+ error = strblobs_load(sblobs, ent->str_cookie, buf, str_len);
+ if (error)
+ goto out;
+
+ if (memcmp(str, buf, str_len))
+ continue;
+
+ *str_cookie = ent->str_cookie;
+ goto out;
+ }
+ error = ENOENT;
+
+out:
+ free(buf);
+ return error;
+}
+
+/*
+ * Search the string hashtable for a matching entry. Sets sets the cookie and
+ * returns 0 if one is found; ENOENT if there is no match; or a positive errno.
+ */
+int
+strblobs_lookup(
+ struct strblobs *sblobs,
+ xfblob_cookie *str_cookie,
+ const unsigned char *str,
+ unsigned int str_len,
+ xfs_dahash_t str_hash)
+{
+ return __strblobs_lookup(sblobs, str_cookie, str, str_len, str_hash);
+}
+
+/* Remember a string in the hashtable. */
+static int
+strblobs_hash(
+ struct strblobs *sblobs,
+ xfblob_cookie str_cookie,
+ const unsigned char *str,
+ unsigned int str_len,
+ xfs_dahash_t str_hash)
+{
+ struct strblob_hashent *ent;
+ unsigned int bucket;
+
+ bucket = str_hash % sblobs->nr_buckets;
+
+ ent = malloc(sizeof(struct strblob_hashent));
+ if (!ent)
+ return ENOMEM;
+
+ ent->str_cookie = str_cookie;
+ ent->str_len = str_len;
+ ent->str_hash = str_hash;
+ ent->next = sblobs->buckets[bucket];
+
+ sblobs->buckets[bucket] = ent;
+ return 0;
+}
+
/* Store a string and return a cookie for its retrieval. */
int
strblobs_store(
struct strblobs *sblobs,
xfblob_cookie *str_cookie,
const unsigned char *str,
- unsigned int str_len)
+ unsigned int str_len,
+ xfs_dahash_t str_hash)
{
- return -xfblob_store(sblobs->strings, str_cookie, str, str_len);
+ int error;
+
+ error = __strblobs_lookup(sblobs, str_cookie, str, str_len, str_hash);
+ if (error != ENOENT)
+ return error;
+
+ error = -xfblob_store(sblobs->strings, str_cookie, str, str_len);
+ if (error)
+ return error;
+
+ return strblobs_hash(sblobs, *str_cookie, str, str_len, str_hash);
}
/* Retrieve a previously stored string. */
diff --git a/repair/strblobs.h b/repair/strblobs.h
index 27e98eee2..40cd6d8e9 100644
--- a/repair/strblobs.h
+++ b/repair/strblobs.h
@@ -8,12 +8,17 @@
struct strblobs;
-int strblobs_init(const char *descr, struct strblobs **sblobs);
+int strblobs_init(const char *descr, unsigned int hash_buckets,
+ struct strblobs **sblobs);
void strblobs_destroy(struct strblobs **sblobs);
int strblobs_store(struct strblobs *sblobs, xfblob_cookie *str_cookie,
- const unsigned char *str, unsigned int str_len);
+ const unsigned char *str, unsigned int str_len,
+ xfs_dahash_t hash);
int strblobs_load(struct strblobs *sblobs, xfblob_cookie str_cookie,
unsigned char *str, unsigned int str_len);
+int strblobs_lookup(struct strblobs *sblobs, xfblob_cookie *str_cookie,
+ const unsigned char *str, unsigned int str_len,
+ xfs_dahash_t hash);
#endif /* __REPAIR_STRBLOBS_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 09/12] xfs_repair: check parent pointers
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
` (7 preceding siblings ...)
2024-07-30 1:27 ` [PATCH 08/12] xfs_repair: deduplicate strings stored in string blob Darrick J. Wong
@ 2024-07-30 1:27 ` Darrick J. Wong
2024-07-30 1:28 ` [PATCH 10/12] xfs_repair: dump garbage parent pointer attributes Darrick J. Wong
` (2 subsequent siblings)
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:27 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Use the parent pointer index that we constructed in the previous patch
to check that each file's parent pointer records exactly match the
directory entries that we recorded while walking directory entries.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/xfblob.c | 9 +
libxfs/xfblob.h | 2
repair/Makefile | 2
repair/listxattr.c | 271 +++++++++++++++++
repair/listxattr.h | 15 +
repair/phase6.c | 2
repair/pptr.c | 846 ++++++++++++++++++++++++++++++++++++++++++++++++++++
repair/pptr.h | 2
8 files changed, 1149 insertions(+)
create mode 100644 repair/listxattr.c
create mode 100644 repair/listxattr.h
diff --git a/libxfs/xfblob.c b/libxfs/xfblob.c
index 7d8caaa4c..00f8ed5e5 100644
--- a/libxfs/xfblob.c
+++ b/libxfs/xfblob.c
@@ -145,3 +145,12 @@ xfblob_free(
xfile_discard(blob->xfile, cookie, sizeof(key) + key.xb_size);
return 0;
}
+
+/* Drop all the blobs. */
+void
+xfblob_truncate(
+ struct xfblob *blob)
+{
+ xfile_discard(blob->xfile, PAGE_SIZE, blob->last_offset - PAGE_SIZE);
+ blob->last_offset = PAGE_SIZE;
+}
diff --git a/libxfs/xfblob.h b/libxfs/xfblob.h
index 28bf4ab28..1939202e1 100644
--- a/libxfs/xfblob.h
+++ b/libxfs/xfblob.h
@@ -21,4 +21,6 @@ int xfblob_store(struct xfblob *blob, xfblob_cookie *cookie, const void *ptr,
uint32_t size);
int xfblob_free(struct xfblob *blob, xfblob_cookie cookie);
+void xfblob_truncate(struct xfblob *blob);
+
#endif /* __XFS_SCRUB_XFBLOB_H__ */
diff --git a/repair/Makefile b/repair/Makefile
index a36a95e35..e7445d53e 100644
--- a/repair/Makefile
+++ b/repair/Makefile
@@ -24,6 +24,7 @@ HFILES = \
err_protos.h \
globals.h \
incore.h \
+ listxattr.h \
pptr.h \
prefetch.h \
progress.h \
@@ -58,6 +59,7 @@ CFILES = \
incore_ext.c \
incore_ino.c \
init.c \
+ listxattr.c \
phase1.c \
phase2.c \
phase3.c \
diff --git a/repair/listxattr.c b/repair/listxattr.c
new file mode 100644
index 000000000..2af77b7b2
--- /dev/null
+++ b/repair/listxattr.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "libxfs.h"
+#include "libxlog.h"
+#include "libfrog/bitmap.h"
+#include "repair/listxattr.h"
+
+/* Call a function for every entry in a shortform xattr structure. */
+STATIC int
+xattr_walk_sf(
+ struct xfs_inode *ip,
+ xattr_walk_fn attr_fn,
+ void *priv)
+{
+ struct xfs_attr_sf_hdr *hdr = ip->i_af.if_data;
+ struct xfs_attr_sf_entry *sfe;
+ unsigned int i;
+ int error;
+
+ sfe = libxfs_attr_sf_firstentry(hdr);
+ for (i = 0; i < hdr->count; i++) {
+ error = attr_fn(ip, sfe->flags, sfe->nameval, sfe->namelen,
+ &sfe->nameval[sfe->namelen], sfe->valuelen,
+ priv);
+ if (error)
+ return error;
+
+ sfe = xfs_attr_sf_nextentry(sfe);
+ }
+
+ return 0;
+}
+
+/* Call a function for every entry in this xattr leaf block. */
+STATIC int
+xattr_walk_leaf_entries(
+ struct xfs_inode *ip,
+ xattr_walk_fn attr_fn,
+ struct xfs_buf *bp,
+ void *priv)
+{
+ struct xfs_attr3_icleaf_hdr ichdr;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_attr_leafblock *leaf = bp->b_addr;
+ struct xfs_attr_leaf_entry *entry;
+ unsigned int i;
+ int error;
+
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf);
+ entry = xfs_attr3_leaf_entryp(leaf);
+
+ for (i = 0; i < ichdr.count; entry++, i++) {
+ void *value;
+ unsigned char *name;
+ unsigned int namelen, valuelen;
+
+ if (entry->flags & XFS_ATTR_LOCAL) {
+ struct xfs_attr_leaf_name_local *name_loc;
+
+ name_loc = xfs_attr3_leaf_name_local(leaf, i);
+ name = name_loc->nameval;
+ namelen = name_loc->namelen;
+ value = &name_loc->nameval[name_loc->namelen];
+ valuelen = be16_to_cpu(name_loc->valuelen);
+ } else {
+ struct xfs_attr_leaf_name_remote *name_rmt;
+
+ name_rmt = xfs_attr3_leaf_name_remote(leaf, i);
+ name = name_rmt->name;
+ namelen = name_rmt->namelen;
+ value = NULL;
+ valuelen = be32_to_cpu(name_rmt->valuelen);
+ }
+
+ error = attr_fn(ip, entry->flags, name, namelen, value,
+ valuelen, priv);
+ if (error)
+ return error;
+
+ }
+
+ return 0;
+}
+
+/*
+ * Call a function for every entry in a leaf-format xattr structure. Avoid
+ * memory allocations for the loop detector since there's only one block.
+ */
+STATIC int
+xattr_walk_leaf(
+ struct xfs_inode *ip,
+ xattr_walk_fn attr_fn,
+ void *priv)
+{
+ struct xfs_buf *leaf_bp;
+ int error;
+
+ error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino, 0, &leaf_bp);
+ if (error)
+ return error;
+
+ error = xattr_walk_leaf_entries(ip, attr_fn, leaf_bp, priv);
+ libxfs_trans_brelse(NULL, leaf_bp);
+ return error;
+}
+
+/* Find the leftmost leaf in the xattr dabtree. */
+STATIC int
+xattr_walk_find_leftmost_leaf(
+ struct xfs_inode *ip,
+ struct bitmap *seen_blocks,
+ struct xfs_buf **leaf_bpp)
+{
+ struct xfs_da3_icnode_hdr nodehdr;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_da_intnode *node;
+ struct xfs_da_node_entry *btree;
+ struct xfs_buf *bp;
+ //xfs_failaddr_t fa;
+ xfs_dablk_t blkno = 0;
+ unsigned int expected_level = 0;
+ int error;
+
+ for (;;) {
+ uint16_t magic;
+
+ error = -libxfs_da3_node_read(NULL, ip, blkno, &bp,
+ XFS_ATTR_FORK);
+ if (error)
+ return error;
+
+ node = bp->b_addr;
+ magic = be16_to_cpu(node->hdr.info.magic);
+ if (magic == XFS_ATTR_LEAF_MAGIC ||
+ magic == XFS_ATTR3_LEAF_MAGIC)
+ break;
+
+ error = EFSCORRUPTED;
+ if (magic != XFS_DA_NODE_MAGIC &&
+ magic != XFS_DA3_NODE_MAGIC)
+ goto out_buf;
+
+ libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node);
+
+ if (nodehdr.count == 0 || nodehdr.level >= XFS_DA_NODE_MAXDEPTH)
+ goto out_buf;
+
+ /* Check the level from the root node. */
+ if (blkno == 0)
+ expected_level = nodehdr.level - 1;
+ else if (expected_level != nodehdr.level)
+ goto out_buf;
+ else
+ expected_level--;
+
+ /* Remember that we've seen this node. */
+ error = -bitmap_set(seen_blocks, blkno, 1);
+ if (error)
+ goto out_buf;
+
+ /* Find the next level towards the leaves of the dabtree. */
+ btree = nodehdr.btree;
+ blkno = be32_to_cpu(btree->before);
+ libxfs_trans_brelse(NULL, bp);
+
+ /* Make sure we haven't seen this new block already. */
+ if (bitmap_test(seen_blocks, blkno, 1))
+ return EFSCORRUPTED;
+ }
+
+ error = EFSCORRUPTED;
+ if (expected_level != 0)
+ goto out_buf;
+
+ /* Remember that we've seen this leaf. */
+ error = -bitmap_set(seen_blocks, blkno, 1);
+ if (error)
+ goto out_buf;
+
+ *leaf_bpp = bp;
+ return 0;
+
+out_buf:
+ libxfs_trans_brelse(NULL, bp);
+ return error;
+}
+
+/* Call a function for every entry in a node-format xattr structure. */
+STATIC int
+xattr_walk_node(
+ struct xfs_inode *ip,
+ xattr_walk_fn attr_fn,
+ void *priv)
+{
+ struct xfs_attr3_icleaf_hdr leafhdr;
+ struct bitmap *seen_blocks;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_attr_leafblock *leaf;
+ struct xfs_buf *leaf_bp;
+ int error;
+
+ bitmap_alloc(&seen_blocks);
+
+ error = xattr_walk_find_leftmost_leaf(ip, seen_blocks, &leaf_bp);
+ if (error)
+ goto out_bitmap;
+
+ for (;;) {
+ error = xattr_walk_leaf_entries(ip, attr_fn, leaf_bp,
+ priv);
+ if (error)
+ goto out_leaf;
+
+ /* Find the right sibling of this leaf block. */
+ leaf = leaf_bp->b_addr;
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
+ if (leafhdr.forw == 0)
+ goto out_leaf;
+
+ libxfs_trans_brelse(NULL, leaf_bp);
+
+ /* Make sure we haven't seen this new leaf already. */
+ if (bitmap_test(seen_blocks, leafhdr.forw, 1))
+ goto out_bitmap;
+
+ error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino,
+ leafhdr.forw, &leaf_bp);
+ if (error)
+ goto out_bitmap;
+
+ /* Remember that we've seen this new leaf. */
+ error = -bitmap_set(seen_blocks, leafhdr.forw, 1);
+ if (error)
+ goto out_leaf;
+ }
+
+out_leaf:
+ libxfs_trans_brelse(NULL, leaf_bp);
+out_bitmap:
+ bitmap_free(&seen_blocks);
+ return error;
+}
+
+/* Call a function for every extended attribute in a file. */
+int
+xattr_walk(
+ struct xfs_inode *ip,
+ xattr_walk_fn attr_fn,
+ void *priv)
+{
+ int error;
+
+ if (!libxfs_inode_hasattr(ip))
+ return 0;
+
+ if (ip->i_af.if_format == XFS_DINODE_FMT_LOCAL)
+ return xattr_walk_sf(ip, attr_fn, priv);
+
+ /* attr functions require that the attr fork is loaded */
+ error = -libxfs_iread_extents(NULL, ip, XFS_ATTR_FORK);
+ if (error)
+ return error;
+
+ if (libxfs_attr_is_leaf(ip))
+ return xattr_walk_leaf(ip, attr_fn, priv);
+
+ return xattr_walk_node(ip, attr_fn, priv);
+}
diff --git a/repair/listxattr.h b/repair/listxattr.h
new file mode 100644
index 000000000..2d26fce0f
--- /dev/null
+++ b/repair/listxattr.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2022-2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __REPAIR_LISTXATTR_H__
+#define __REPAIR_LISTXATTR_H__
+
+typedef int (*xattr_walk_fn)(struct xfs_inode *ip, unsigned int attr_flags,
+ const unsigned char *name, unsigned int namelen,
+ const void *value, unsigned int valuelen, void *priv);
+
+int xattr_walk(struct xfs_inode *ip, xattr_walk_fn attr_fn, void *priv);
+
+#endif /* __REPAIR_LISTXATTR_H__ */
diff --git a/repair/phase6.c b/repair/phase6.c
index fe56feb6e..ad067ba0a 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -3445,5 +3445,7 @@ _(" - resetting contents of realtime bitmap and summary inodes\n"));
}
}
+ /* Check and repair directory parent pointers, if enabled. */
+ check_parent_ptrs(mp);
parent_ptr_free(mp);
}
diff --git a/repair/pptr.c b/repair/pptr.c
index f8db57b2c..bd9bb6f8b 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -7,8 +7,13 @@
#include "libxfs/xfile.h"
#include "libxfs/xfblob.h"
#include "libfrog/platform.h"
+#include "libfrog/workqueue.h"
+#include "repair/globals.h"
#include "repair/err_protos.h"
#include "repair/slab.h"
+#include "repair/listxattr.h"
+#include "repair/threads.h"
+#include "repair/incore.h"
#include "repair/pptr.h"
#include "repair/strblobs.h"
@@ -62,6 +67,65 @@
* to strings, which means that we can use the name cookie as a comparison key
* instead of loading the full dentry name every time we want to perform a
* comparison.
+ *
+ * Once we've finished with the forward scan, we get to work on the backwards
+ * scan. Each AG is processed independently. First, we sort the per-AG master
+ * records in order of child_agino, dir_ino, and name_cookie. Each inode in
+ * the AG is then processed in numerical order.
+ *
+ * The first thing that happens to the file is that we read all the extended
+ * attributes to look for parent pointers. Attributes that claim to be parent
+ * pointers but are obviously garbage are thrown away. The rest of the ondisk
+ * parent pointers for that file are stored in memory like this:
+ *
+ * (dir_ino*, dir_gen, name_cookie*)
+ *
+ * After loading the ondisk parent pointer name, we search the strblobs
+ * structure to see if it has already recorded the name. If so, this value is
+ * used as the name cookie. If the name has not yet been recorded, we flag the
+ * incore record for later deletion.
+ *
+ * When we've concluded the xattr scan, the per-file records are sorted in
+ * order of dir_ino and name_cookie.
+ *
+ * There are three possibilities here:
+ *
+ * A. The first record in the per-AG master index is an exact match for the
+ * first record in the per-file index. Everything is consistent, and we can
+ * proceed with the lockstep scan detailed below.
+ *
+ * B. The per-AG master index cursor points to a higher inode number than the
+ * first inode we are scanning. Delete the ondisk parent pointers
+ * corresponding to the per-file records until condition (B) is no longer true.
+ *
+ * C. The per-AG master index cursor instead points to a lower inode number
+ * than the one we are scanning. This means that there exists a directory
+ * entry pointing at an inode that is free. We supposedly already settled
+ * which inodes are free and which aren't, which means in-memory information is
+ * inconsistent. Abort.
+ *
+ * Otherwise, we are ready to check the file parent pointers against the
+ * master. If the ondisk directory metadata are all consistent, this recordset
+ * should correspond exactly to the subset of the master records with a
+ * child_agino matching the file that we're scanning. We should be able to
+ * walk both sets in lockstep, and find one of the following outcomes:
+ *
+ * 1) The master index cursor is ahead of the ondisk index cursor. This means
+ * that the inode has parent pointers that were not found during the dirent
+ * scan. These should be deleted.
+ *
+ * 2) The ondisk index gets ahead of the master index. This means that the
+ * dirent scan found parent pointers that are not attached to the inode.
+ * These should be added.
+ *
+ * 3) The parent_gen or (dirent) name are not consistent. Update the parent
+ * pointer to the values that we found during the dirent scan.
+ *
+ * 4) Everything matches. Move on to the next parent pointer.
+ *
+ * The current implementation does not try to rebuild directories from parent
+ * pointer information, as this requires a lengthy scan of the filesystem for
+ * each broken directory.
*/
struct ag_pptr {
@@ -88,6 +152,24 @@ struct ag_pptr {
/* This might be a duplicate due to dotdot reprocessing */
#define AG_PPTR_POSSIBLE_DUP (1U << 0)
+struct file_pptr {
+ /* parent directory handle */
+ xfs_ino_t parent_ino;
+ uint32_t parent_gen;
+
+ /* Is the name stored in the global nameblobs structure? */
+ unsigned int name_in_nameblobs;
+
+ /* hash of the dirent name */
+ xfs_dahash_t namehash;
+
+ /* parent pointer name length */
+ unsigned int namelen;
+
+ /* cookie for the file dirent name */
+ xfblob_cookie name_cookie;
+};
+
struct ag_pptrs {
/* Lock to protect pptr_recs during the dirent scan. */
pthread_mutex_t lock;
@@ -96,11 +178,99 @@ struct ag_pptrs {
struct xfs_slab *pptr_recs;
};
+struct file_scan {
+ struct ag_pptrs *ag_pptrs;
+
+ /* cursor for comparing ag_pptrs.pptr_recs against file_pptrs_recs */
+ struct xfs_slab_cursor *ag_pptr_recs_cur;
+
+ /* xfs_parent_rec records for a file that we're checking */
+ struct xfs_slab *file_pptr_recs;
+
+ /* cursor for comparing file_pptr_recs against pptrs_recs */
+ struct xfs_slab_cursor *file_pptr_recs_cur;
+
+ /* names associated with file_pptr_recs */
+ struct xfblob *file_pptr_names;
+
+ /* Number of parent pointers recorded for this file. */
+ unsigned int nr_file_pptrs;
+
+ /* Does this file have garbage xattrs with ATTR_PARENT set? */
+ bool have_garbage;
+};
+
/* Global names storage file. */
static struct strblobs *nameblobs;
static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct ag_pptrs *fs_pptrs;
+static int
+cmp_ag_pptr(
+ const void *a,
+ const void *b)
+{
+ const struct ag_pptr *pa = a;
+ const struct ag_pptr *pb = b;
+
+ if (pa->child_agino < pb->child_agino)
+ return -1;
+ if (pa->child_agino > pb->child_agino)
+ return 1;
+
+ if (pa->parent_ino < pb->parent_ino)
+ return -1;
+ if (pa->parent_ino > pb->parent_ino)
+ return 1;
+
+ if (pa->namehash < pb->namehash)
+ return -1;
+ if (pa->namehash > pb->namehash)
+ return 1;
+
+ if (pa->name_cookie < pb->name_cookie)
+ return -1;
+ if (pa->name_cookie > pb->name_cookie)
+ return 1;
+
+ return 0;
+}
+
+static int
+cmp_file_pptr(
+ const void *a,
+ const void *b)
+{
+ const struct file_pptr *pa = a;
+ const struct file_pptr *pb = b;
+
+ if (pa->parent_ino < pb->parent_ino)
+ return -1;
+ if (pa->parent_ino > pb->parent_ino)
+ return 1;
+
+ /*
+ * Push the parent pointer names that we didn't find in the dirent scan
+ * towards the end of the list so that we delete them as excess.
+ */
+ if (!pa->name_in_nameblobs && pb->name_in_nameblobs)
+ return 1;
+ if (pa->name_in_nameblobs && !pb->name_in_nameblobs)
+ return -1;
+
+ if (pa->namehash < pb->namehash)
+ return -1;
+ if (pa->namehash > pb->namehash)
+ return 1;
+
+ if (pa->name_cookie < pb->name_cookie)
+ return -1;
+ if (pa->name_cookie > pb->name_cookie)
+ return 1;
+
+ return 0;
+}
+
void
parent_ptr_free(
struct xfs_mount *mp)
@@ -221,3 +391,679 @@ add_parent_ptr(
(unsigned long long)ino,
(unsigned long long)ag_pptr.name_cookie);
}
+
+/* Schedule this ATTR_PARENT extended attribute for deletion. */
+static void
+record_garbage_xattr(
+ struct xfs_inode *ip,
+ struct file_scan *fscan,
+ unsigned int attr_filter,
+ const unsigned char *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen)
+{
+ if (no_modify) {
+ if (!fscan->have_garbage)
+ do_warn(
+ _("would delete garbage parent pointer extended attributes in ino %llu\n"),
+ (unsigned long long)ip->i_ino);
+ fscan->have_garbage = true;
+ return;
+ }
+
+ if (fscan->have_garbage)
+ return;
+ fscan->have_garbage = true;
+
+ do_warn(
+ _("deleting garbage parent pointer extended attributes in ino %llu\n"),
+ (unsigned long long)ip->i_ino);
+ /* XXX do the work */
+}
+
+/*
+ * Store this file parent pointer's name in the file scan namelist unless it's
+ * already in the global list.
+ */
+static int
+store_file_pptr_name(
+ struct file_scan *fscan,
+ struct file_pptr *file_pptr,
+ const struct xfs_name *xname)
+{
+ int error;
+
+ error = strblobs_lookup(nameblobs, &file_pptr->name_cookie,
+ xname->name, xname->len, file_pptr->namehash);
+ if (!error) {
+ file_pptr->name_in_nameblobs = true;
+ return 0;
+ }
+ if (error != ENOENT)
+ return error;
+
+ file_pptr->name_in_nameblobs = false;
+ return -xfblob_store(fscan->file_pptr_names, &file_pptr->name_cookie,
+ xname->name, xname->len);
+}
+
+/* Decide if this is a directory parent pointer and stash it if so. */
+static int
+examine_xattr(
+ struct xfs_inode *ip,
+ unsigned int attr_flags,
+ const unsigned char *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen,
+ void *priv)
+{
+ struct file_pptr file_pptr = {
+ .namelen = namelen,
+ };
+ struct xfs_name xname = {
+ .name = name,
+ .len = namelen,
+ };
+ struct xfs_mount *mp = ip->i_mount;
+ struct file_scan *fscan = priv;
+ int error;
+
+ if (!(attr_flags & XFS_ATTR_PARENT))
+ return 0;
+
+ error = -libxfs_parent_from_attr(mp, attr_flags, name, namelen, value,
+ valuelen, &file_pptr.parent_ino, &file_pptr.parent_gen);
+ if (error)
+ goto corrupt;
+
+ file_pptr.namehash = libxfs_dir2_hashname(mp, &xname);
+
+ error = store_file_pptr_name(fscan, &file_pptr, &xname);
+ if (error)
+ do_error(
+ _("storing ino %llu parent pointer '%.*s' failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ namelen,
+ (const char *)name,
+ strerror(error));
+
+ error = -slab_add(fscan->file_pptr_recs, &file_pptr);
+ if (error)
+ do_error(_("storing ino %llu parent pointer rec failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+
+ dbg_printf(
+ _("%s: dp %llu gen 0x%x fname '%.*s' namelen %u namehash 0x%x ino %llu namecookie 0x%llx global? %d\n"),
+ __func__,
+ (unsigned long long)file_pptr.parent_ino,
+ file_pptr.parent_gen,
+ namelen,
+ (const char *)name,
+ namelen,
+ file_pptr.namehash,
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr.name_cookie,
+ file_pptr.name_in_nameblobs);
+
+ fscan->nr_file_pptrs++;
+ return 0;
+corrupt:
+ record_garbage_xattr(ip, fscan, attr_flags, name, namelen, value,
+ valuelen);
+ return 0;
+}
+
+/* Load a file parent pointer name from wherever we stored it. */
+static int
+load_file_pptr_name(
+ struct file_scan *fscan,
+ const struct file_pptr *file_pptr,
+ unsigned char *name)
+{
+ if (file_pptr->name_in_nameblobs)
+ return strblobs_load(nameblobs, file_pptr->name_cookie,
+ name, file_pptr->namelen);
+
+ return -xfblob_load(fscan->file_pptr_names, file_pptr->name_cookie,
+ name, file_pptr->namelen);
+}
+
+/* Remove all pptrs from @ip. */
+static void
+clear_all_pptrs(
+ struct xfs_inode *ip)
+{
+ if (no_modify) {
+ do_warn(_("would delete unlinked ino %llu parent pointers\n"),
+ (unsigned long long)ip->i_ino);
+ return;
+ }
+
+ do_warn(_("deleting unlinked ino %llu parent pointers\n"),
+ (unsigned long long)ip->i_ino);
+ /* XXX actually do the work */
+}
+
+/* Add @ag_pptr to @ip. */
+static void
+add_missing_parent_ptr(
+ struct xfs_inode *ip,
+ struct file_scan *fscan,
+ const struct ag_pptr *ag_pptr)
+{
+ unsigned char name[MAXNAMELEN];
+ int error;
+
+ error = strblobs_load(nameblobs, ag_pptr->name_cookie, name,
+ ag_pptr->namelen);
+ if (error)
+ do_error(
+ _("loading missing name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ (unsigned long long)ag_pptr->name_cookie,
+ strerror(error));
+
+ if (no_modify) {
+ do_warn(
+ _("would add missing ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name);
+ return;
+ } else {
+ do_warn(
+ _("adding missing ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name);
+ }
+
+ /* XXX actually do the work */
+}
+
+/* Remove @file_pptr from @ip. */
+static void
+remove_incorrect_parent_ptr(
+ struct xfs_inode *ip,
+ struct file_scan *fscan,
+ const struct file_pptr *file_pptr)
+{
+ unsigned char name[MAXNAMELEN] = { };
+ int error;
+
+ error = load_file_pptr_name(fscan, file_pptr, name);
+ if (error)
+ do_error(
+ _("loading incorrect name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ (unsigned long long)file_pptr->name_cookie,
+ strerror(error));
+
+ if (no_modify) {
+ do_warn(
+ _("would remove bad ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ name);
+ return;
+ }
+
+ do_warn(
+ _("removing bad ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ name);
+
+ /* XXX actually do the work */
+}
+
+/*
+ * We found parent pointers that point to the same inode and directory offset.
+ * Make sure they have the same generation number and dirent name.
+ */
+static void
+compare_parent_ptrs(
+ struct xfs_inode *ip,
+ struct file_scan *fscan,
+ const struct ag_pptr *ag_pptr,
+ const struct file_pptr *file_pptr)
+{
+ unsigned char name1[MAXNAMELEN] = { };
+ unsigned char name2[MAXNAMELEN] = { };
+ int error;
+
+ error = strblobs_load(nameblobs, ag_pptr->name_cookie, name1,
+ ag_pptr->namelen);
+ if (error)
+ do_error(
+ _("loading master-list name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx namelen %u) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ (unsigned long long)ag_pptr->name_cookie,
+ ag_pptr->namelen,
+ strerror(error));
+
+ error = load_file_pptr_name(fscan, file_pptr, name2);
+ if (error)
+ do_error(
+ _("loading file-list name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx namelen %u) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ (unsigned long long)file_pptr->name_cookie,
+ ag_pptr->namelen,
+ strerror(error));
+
+ if (ag_pptr->parent_gen != file_pptr->parent_gen)
+ goto reset;
+ if (ag_pptr->namelen != file_pptr->namelen)
+ goto reset;
+ if (ag_pptr->namehash != file_pptr->namehash)
+ goto reset;
+ if (memcmp(name1, name2, ag_pptr->namelen))
+ goto reset;
+
+ return;
+
+reset:
+ if (no_modify) {
+ do_warn(
+ _("would update ino %llu parent pointer (ino %llu gen 0x%x name '%.*s') -> (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ name2,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name1);
+ return;
+ }
+
+ do_warn(
+ _("updating ino %llu parent pointer (ino %llu gen 0x%x name '%.*s') -> (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ name2,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name1);
+
+ /* XXX do the work */
+}
+
+static int
+cmp_file_to_ag_pptr(
+ const struct file_pptr *fp,
+ const struct ag_pptr *ap)
+{
+ /*
+ * We finished iterating all the pptrs attached to the file before we
+ * ran out of pptrs that we found in the directory scan. Return 1 so
+ * the caller adds the pptr from the dir scan.
+ */
+ if (!fp)
+ return 1;
+
+ if (fp->parent_ino > ap->parent_ino)
+ return 1;
+ if (fp->parent_ino < ap->parent_ino)
+ return -1;
+
+ if (fp->namehash < ap->namehash)
+ return -1;
+ if (fp->namehash > ap->namehash)
+ return 1;
+
+ /*
+ * If this parent pointer wasn't found in the dirent scan, we know it
+ * should be removed.
+ */
+ if (!fp->name_in_nameblobs)
+ return -1;
+
+ if (fp->name_cookie < ap->name_cookie)
+ return -1;
+ if (fp->name_cookie > ap->name_cookie)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * If this parent pointer that we got via directory scan thinks it might be a
+ * duplicate, compare it to the previous pptr found by the directory scan. If
+ * they are the same, then we got duplicate entries on account of dotdot
+ * reprocessing and we can ignore this one.
+ */
+static inline bool
+crosscheck_want_skip_dup(
+ const struct ag_pptr *ag_pptr,
+ const struct ag_pptr *prev_ag_pptr)
+{
+ if (!(ag_pptr->flags & AG_PPTR_POSSIBLE_DUP) || !prev_ag_pptr)
+ return false;
+
+ if (ag_pptr->parent_ino == prev_ag_pptr->parent_ino &&
+ ag_pptr->parent_gen == prev_ag_pptr->parent_gen &&
+ ag_pptr->namelen == prev_ag_pptr->namelen &&
+ ag_pptr->name_cookie == prev_ag_pptr->name_cookie &&
+ ag_pptr->child_agino == prev_ag_pptr->child_agino)
+ return true;
+
+ return false;
+}
+
+/*
+ * Make sure that the parent pointers we observed match the ones ondisk.
+ *
+ * Earlier, we generated a master list of parent pointers for files in this AG
+ * based on what we saw during the directory walk at the start of phase 6.
+ * Now that we've read in all of this file's parent pointers, make sure the
+ * lists match.
+ */
+static void
+crosscheck_file_parent_ptrs(
+ struct xfs_inode *ip,
+ struct file_scan *fscan)
+{
+ struct ag_pptr *ag_pptr, *prev_ag_pptr = NULL;
+ struct file_pptr *file_pptr;
+ struct xfs_mount *mp = ip->i_mount;
+ xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
+ xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
+ int error;
+
+ ag_pptr = peek_slab_cursor(fscan->ag_pptr_recs_cur);
+
+ if (!ag_pptr || ag_pptr->child_agino > agino) {
+ /*
+ * The cursor for the master pptr list has gone beyond this
+ * file that we're scanning. Evidently it has no parents at
+ * all, so we better not have found any pptrs attached to the
+ * file.
+ */
+ if (fscan->nr_file_pptrs > 0)
+ clear_all_pptrs(ip);
+
+ return;
+ }
+
+ if (ag_pptr->child_agino < agino) {
+ /*
+ * The cursor for the master pptr list is behind the file that
+ * we're scanning. This suggests that the incore inode tree
+ * doesn't know about a file that is mentioned by a dirent.
+ * At this point the inode liveness is supposed to be settled,
+ * which means our incore information is inconsistent.
+ */
+ do_error(
+ _("found dirent referring to ino %llu even though inobt scan moved on to ino %llu?!\n"),
+ (unsigned long long)XFS_AGINO_TO_INO(mp, agno,
+ ag_pptr->child_agino),
+ (unsigned long long)ip->i_ino);
+ /* does not return */
+ }
+
+ /*
+ * The master pptr list cursor is pointing to the inode that we want
+ * to check. Sort the pptr records that we recorded from the ondisk
+ * pptrs for this file, then set up for the comparison.
+ */
+ qsort_slab(fscan->file_pptr_recs, cmp_file_pptr);
+
+ error = -init_slab_cursor(fscan->file_pptr_recs, cmp_file_pptr,
+ &fscan->file_pptr_recs_cur);
+ if (error)
+ do_error(_("init ino %llu parent pointer cursor failed: %s\n"),
+ (unsigned long long)ip->i_ino, strerror(error));
+
+ do {
+ int cmp_result;
+
+ if (crosscheck_want_skip_dup(ag_pptr, prev_ag_pptr)) {
+ /*
+ * This master parent pointer thinks it's a duplicate
+ * and it matches the previous master parent pointer.
+ * We don't want to add duplicate parent pointers, so
+ * advance the master pptr cursor and loop again.
+ */
+ dbg_printf(
+ _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (skip_dup)\n"),
+ __func__,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->name_cookie);
+ prev_ag_pptr = ag_pptr;
+ advance_slab_cursor(fscan->ag_pptr_recs_cur);
+ ag_pptr = peek_slab_cursor(fscan->ag_pptr_recs_cur);
+ continue;
+ }
+ prev_ag_pptr = ag_pptr;
+
+ file_pptr = peek_slab_cursor(fscan->file_pptr_recs_cur);
+
+ dbg_printf(
+ _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (master)\n"),
+ __func__,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->name_cookie);
+
+ if (file_pptr) {
+ dbg_printf(
+ _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (file)\n"),
+ __func__,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->name_cookie);
+ } else {
+ dbg_printf(
+ _("%s: ran out of parent pointers for ino %llu (file)\n"),
+ __func__,
+ (unsigned long long)ip->i_ino);
+ }
+
+ cmp_result = cmp_file_to_ag_pptr(file_pptr, ag_pptr);
+ if (cmp_result > 0) {
+ /*
+ * The master pptr list knows about pptrs that are not
+ * in the ondisk metadata. Add the missing pptr and
+ * advance only the master pptr cursor.
+ */
+ add_missing_parent_ptr(ip, fscan, ag_pptr);
+ advance_slab_cursor(fscan->ag_pptr_recs_cur);
+ } else if (cmp_result < 0) {
+ /*
+ * The ondisk pptrs mention a link that is not in the
+ * master list. Delete the extra pptr and advance only
+ * the file pptr cursor.
+ */
+ remove_incorrect_parent_ptr(ip, fscan, file_pptr);
+ advance_slab_cursor(fscan->file_pptr_recs_cur);
+ } else {
+ /*
+ * Exact match, make sure the parent_gen and dirent
+ * name parts of the parent pointer match. Move both
+ * cursors forward.
+ */
+ compare_parent_ptrs(ip, fscan, ag_pptr, file_pptr);
+ advance_slab_cursor(fscan->ag_pptr_recs_cur);
+ advance_slab_cursor(fscan->file_pptr_recs_cur);
+ }
+
+ ag_pptr = peek_slab_cursor(fscan->ag_pptr_recs_cur);
+ } while (ag_pptr && ag_pptr->child_agino == agino);
+
+ while ((file_pptr = pop_slab_cursor(fscan->file_pptr_recs_cur))) {
+ dbg_printf(
+ _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (excess)\n"),
+ __func__,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->name_cookie);
+
+ /*
+ * The master pptr list does not have any more pptrs for this
+ * file, but we still have unprocessed ondisk pptrs. Delete
+ * all these ondisk pptrs.
+ */
+ remove_incorrect_parent_ptr(ip, fscan, file_pptr);
+ }
+}
+
+/* Ensure this file's parent pointers match what we found in the dirent scan. */
+static void
+check_file_parent_ptrs(
+ struct xfs_inode *ip,
+ struct file_scan *fscan)
+{
+ int error;
+
+ error = -init_slab(&fscan->file_pptr_recs, sizeof(struct file_pptr));
+ if (error)
+ do_error(_("init file parent pointer recs failed: %s\n"),
+ strerror(error));
+
+ fscan->have_garbage = false;
+ fscan->nr_file_pptrs = 0;
+
+ error = xattr_walk(ip, examine_xattr, fscan);
+ if (error && !no_modify)
+ do_error(_("ino %llu parent pointer scan failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+ if (error) {
+ do_warn(_("ino %llu parent pointer scan failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+ goto out_free;
+ }
+
+ crosscheck_file_parent_ptrs(ip, fscan);
+
+out_free:
+ free_slab(&fscan->file_pptr_recs);
+ xfblob_truncate(fscan->file_pptr_names);
+}
+
+/* Check all the parent pointers of files in this AG. */
+static void
+check_ag_parent_ptrs(
+ struct workqueue *wq,
+ uint32_t agno,
+ void *arg)
+{
+ struct xfs_mount *mp = wq->wq_ctx;
+ struct file_scan fscan = {
+ .ag_pptrs = &fs_pptrs[agno],
+ };
+ struct ag_pptrs *ag_pptrs = &fs_pptrs[agno];
+ struct ino_tree_node *irec;
+ char *descr;
+ int error;
+
+ qsort_slab(ag_pptrs->pptr_recs, cmp_ag_pptr);
+
+ error = -init_slab_cursor(ag_pptrs->pptr_recs, cmp_ag_pptr,
+ &fscan.ag_pptr_recs_cur);
+ if (error)
+ do_error(
+ _("init agno %u parent pointer slab cursor failed: %s\n"),
+ agno, strerror(error));
+
+ descr = kasprintf(GFP_KERNEL,
+ "xfs_repair (%s): file parent pointer names",
+ mp->m_fsname);
+ error = -xfblob_create(descr, &fscan.file_pptr_names);
+ kfree(descr);
+ if (error)
+ do_error(
+ _("init agno %u file parent pointer names failed: %s\n"),
+ agno, strerror(error));
+
+ for (irec = findfirst_inode_rec(agno);
+ irec != NULL;
+ irec = next_ino_rec(irec)) {
+ unsigned int ino_offset;
+
+ for (ino_offset = 0;
+ ino_offset < XFS_INODES_PER_CHUNK;
+ ino_offset++) {
+ struct xfs_inode *ip;
+ xfs_ino_t ino;
+
+ if (is_inode_free(irec, ino_offset))
+ continue;
+
+ ino = XFS_AGINO_TO_INO(mp, agno,
+ irec->ino_startnum + ino_offset);
+ error = -libxfs_iget(mp, NULL, ino, 0, &ip);
+ if (error && !no_modify)
+ do_error(
+ _("loading ino %llu for parent pointer check failed: %s\n"),
+ (unsigned long long)ino,
+ strerror(error));
+ if (error) {
+ do_warn(
+ _("loading ino %llu for parent pointer check failed: %s\n"),
+ (unsigned long long)ino,
+ strerror(error));
+ continue;
+ }
+
+ check_file_parent_ptrs(ip, &fscan);
+ libxfs_irele(ip);
+ }
+ }
+
+ xfblob_destroy(fscan.file_pptr_names);
+ free_slab_cursor(&fscan.ag_pptr_recs_cur);
+}
+
+/* Check all the parent pointers of all files in this filesystem. */
+void
+check_parent_ptrs(
+ struct xfs_mount *mp)
+{
+ struct workqueue wq;
+ xfs_agnumber_t agno;
+
+ if (!xfs_has_parent(mp))
+ return;
+
+ create_work_queue(&wq, mp, ag_stride);
+
+ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++)
+ queue_work(&wq, check_ag_parent_ptrs, agno, NULL);
+
+ destroy_work_queue(&wq);
+}
diff --git a/repair/pptr.h b/repair/pptr.h
index 752237942..65acff963 100644
--- a/repair/pptr.h
+++ b/repair/pptr.h
@@ -12,4 +12,6 @@ void parent_ptr_init(struct xfs_mount *mp);
void add_parent_ptr(xfs_ino_t ino, const unsigned char *fname,
struct xfs_inode *dp, bool possible_dup);
+void check_parent_ptrs(struct xfs_mount *mp);
+
#endif /* __REPAIR_PPTR_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 10/12] xfs_repair: dump garbage parent pointer attributes
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
` (8 preceding siblings ...)
2024-07-30 1:27 ` [PATCH 09/12] xfs_repair: check parent pointers Darrick J. Wong
@ 2024-07-30 1:28 ` Darrick J. Wong
2024-07-30 1:28 ` [PATCH 11/12] xfs_repair: update ondisk parent pointer records Darrick J. Wong
2024-07-30 1:28 ` [PATCH 12/12] xfs_repair: wipe ondisk parent pointers when there are none Darrick J. Wong
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:28 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Delete xattrs that have ATTR_PARENT set but are so garbage that they
clearly aren't parent pointers.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/libxfs_api_defs.h | 1
repair/pptr.c | 149 +++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 148 insertions(+), 2 deletions(-)
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index d3611e05b..e12f0a40b 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -46,6 +46,7 @@
#define xfs_attr_is_leaf libxfs_attr_is_leaf
#define xfs_attr_leaf_newentsize libxfs_attr_leaf_newentsize
#define xfs_attr_namecheck libxfs_attr_namecheck
+#define xfs_attr_removename libxfs_attr_removename
#define xfs_attr_set libxfs_attr_set
#define xfs_attr_sethash libxfs_attr_sethash
#define xfs_attr_sf_firstentry libxfs_attr_sf_firstentry
diff --git a/repair/pptr.c b/repair/pptr.c
index bd9bb6f8b..61466009d 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -198,6 +198,29 @@ struct file_scan {
/* Does this file have garbage xattrs with ATTR_PARENT set? */
bool have_garbage;
+
+ /* xattrs that we have to remove from this file */
+ struct xfs_slab *garbage_xattr_recs;
+
+ /* attr names associated with garbage_xattr_recs */
+ struct xfblob *garbage_xattr_names;
+};
+
+struct garbage_xattr {
+ /* xfs_da_args.attr_filter for the attribute being removed */
+ unsigned int attr_filter;
+
+ /* attribute name length */
+ unsigned int attrnamelen;
+
+ /* attribute value length */
+ unsigned int attrvaluelen;
+
+ /* cookie for the attribute name */
+ xfblob_cookie attrname_cookie;
+
+ /* cookie for the attribute value */
+ xfblob_cookie attrvalue_cookie;
};
/* Global names storage file. */
@@ -392,6 +415,82 @@ add_parent_ptr(
(unsigned long long)ag_pptr.name_cookie);
}
+/* Remove garbage extended attributes that have ATTR_PARENT set. */
+static void
+remove_garbage_xattrs(
+ struct xfs_inode *ip,
+ struct file_scan *fscan)
+{
+ struct xfs_slab_cursor *cur;
+ struct garbage_xattr *ga;
+ void *buf = NULL;
+ size_t bufsize = 0;
+ int error;
+
+ error = -init_slab_cursor(fscan->garbage_xattr_recs, NULL, &cur);
+ if (error)
+ do_error(_("init garbage xattr cursor failed: %s\n"),
+ strerror(error));
+
+ while ((ga = pop_slab_cursor(cur)) != NULL) {
+ struct xfs_da_args args = {
+ .dp = ip,
+ .attr_filter = ga->attr_filter,
+ .namelen = ga->attrnamelen,
+ .valuelen = ga->attrvaluelen,
+ .owner = ip->i_ino,
+ .geo = ip->i_mount->m_attr_geo,
+ .whichfork = XFS_ATTR_FORK,
+ .op_flags = XFS_DA_OP_OKNOENT | XFS_DA_OP_LOGGED,
+ };
+ size_t desired = ga->attrnamelen + ga->attrvaluelen;
+
+ if (desired > bufsize) {
+ free(buf);
+ buf = malloc(desired);
+ if (!buf)
+ do_error(
+ _("allocating %zu bytes to remove ino %llu garbage xattr failed: %s\n"),
+ desired,
+ (unsigned long long)ip->i_ino,
+ strerror(errno));
+ bufsize = desired;
+ }
+
+ args.name = buf;
+ args.value = buf + ga->attrnamelen;
+
+ error = -xfblob_load(fscan->garbage_xattr_names,
+ ga->attrname_cookie, buf, ga->attrnamelen);
+ if (error)
+ do_error(
+ _("loading garbage xattr name failed: %s\n"),
+ strerror(error));
+
+ error = -xfblob_load(fscan->garbage_xattr_names,
+ ga->attrvalue_cookie, args.value,
+ ga->attrvaluelen);
+ if (error)
+ do_error(
+ _("loading garbage xattr value failed: %s\n"),
+ strerror(error));
+
+ libxfs_attr_sethash(&args);
+ error = -libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE, true);
+ if (error)
+ do_error(
+ _("removing ino %llu garbage xattr failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+ }
+
+ free(buf);
+ free_slab_cursor(&cur);
+ free_slab(&fscan->garbage_xattr_recs);
+ xfblob_destroy(fscan->garbage_xattr_names);
+ fscan->garbage_xattr_names = NULL;
+}
+
/* Schedule this ATTR_PARENT extended attribute for deletion. */
static void
record_garbage_xattr(
@@ -403,6 +502,15 @@ record_garbage_xattr(
const void *value,
unsigned int valuelen)
{
+ struct garbage_xattr garbage_xattr = {
+ .attr_filter = attr_filter,
+ .attrnamelen = namelen,
+ .attrvaluelen = valuelen,
+ };
+ struct xfs_mount *mp = ip->i_mount;
+ char *descr;
+ int error;
+
if (no_modify) {
if (!fscan->have_garbage)
do_warn(
@@ -413,13 +521,47 @@ record_garbage_xattr(
}
if (fscan->have_garbage)
- return;
+ goto stuffit;
fscan->have_garbage = true;
do_warn(
_("deleting garbage parent pointer extended attributes in ino %llu\n"),
(unsigned long long)ip->i_ino);
- /* XXX do the work */
+
+ error = -init_slab(&fscan->garbage_xattr_recs,
+ sizeof(struct garbage_xattr));
+ if (error)
+ do_error(_("init garbage xattr recs failed: %s\n"),
+ strerror(error));
+
+ descr = kasprintf(GFP_KERNEL, "xfs_repair (%s): garbage xattr names",
+ mp->m_fsname);
+ error = -xfblob_create(descr, &fscan->garbage_xattr_names);
+ kfree(descr);
+ if (error)
+ do_error("init garbage xattr names failed: %s\n",
+ strerror(error));
+
+stuffit:
+ error = -xfblob_store(fscan->garbage_xattr_names,
+ &garbage_xattr.attrname_cookie, name, namelen);
+ if (error)
+ do_error(_("storing ino %llu garbage xattr failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+
+ error = -xfblob_store(fscan->garbage_xattr_names,
+ &garbage_xattr.attrvalue_cookie, value, valuelen);
+ if (error)
+ do_error(_("storing ino %llu garbage xattr failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+
+ error = -slab_add(fscan->garbage_xattr_recs, &garbage_xattr);
+ if (error)
+ do_error(_("storing ino %llu garbage xattr rec failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
}
/*
@@ -968,6 +1110,9 @@ check_file_parent_ptrs(
goto out_free;
}
+ if (!no_modify && fscan->have_garbage)
+ remove_garbage_xattrs(ip, fscan);
+
crosscheck_file_parent_ptrs(ip, fscan);
out_free:
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 11/12] xfs_repair: update ondisk parent pointer records
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
` (9 preceding siblings ...)
2024-07-30 1:28 ` [PATCH 10/12] xfs_repair: dump garbage parent pointer attributes Darrick J. Wong
@ 2024-07-30 1:28 ` Darrick J. Wong
2024-07-30 1:28 ` [PATCH 12/12] xfs_repair: wipe ondisk parent pointers when there are none Darrick J. Wong
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:28 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Update the ondisk parent pointer records as necessary.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libxfs/libxfs_api_defs.h | 2 +
repair/pptr.c | 88 ++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 87 insertions(+), 3 deletions(-)
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index e12f0a40b..df316727b 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -209,8 +209,10 @@
#define xfs_parent_hashval libxfs_parent_hashval
#define xfs_parent_lookup libxfs_parent_lookup
#define xfs_parent_removename libxfs_parent_removename
+#define xfs_parent_set libxfs_parent_set
#define xfs_parent_start libxfs_parent_start
#define xfs_parent_from_attr libxfs_parent_from_attr
+#define xfs_parent_unset libxfs_parent_unset
#define xfs_perag_get libxfs_perag_get
#define xfs_perag_hold libxfs_perag_hold
#define xfs_perag_put libxfs_perag_put
diff --git a/repair/pptr.c b/repair/pptr.c
index 61466009d..94d6d8346 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -673,6 +673,44 @@ load_file_pptr_name(
name, file_pptr->namelen);
}
+/* Add an on disk parent pointer to a file. */
+static int
+add_file_pptr(
+ struct xfs_inode *ip,
+ const struct ag_pptr *ag_pptr,
+ const unsigned char *name)
+{
+ struct xfs_name xname = {
+ .name = name,
+ .len = ag_pptr->namelen,
+ };
+ struct xfs_parent_rec pptr_rec = { };
+ struct xfs_da_args scratch;
+
+ xfs_parent_rec_init(&pptr_rec, ag_pptr->parent_ino,
+ ag_pptr->parent_gen);
+ return -libxfs_parent_set(ip, ip->i_ino, &xname, &pptr_rec, &scratch);
+}
+
+/* Remove an on disk parent pointer from a file. */
+static int
+remove_file_pptr(
+ struct xfs_inode *ip,
+ const struct file_pptr *file_pptr,
+ const unsigned char *name)
+{
+ struct xfs_name xname = {
+ .name = name,
+ .len = file_pptr->namelen,
+ };
+ struct xfs_parent_rec pptr_rec = { };
+ struct xfs_da_args scratch;
+
+ xfs_parent_rec_init(&pptr_rec, file_pptr->parent_ino,
+ file_pptr->parent_gen);
+ return -libxfs_parent_unset(ip, ip->i_ino, &xname, &pptr_rec, &scratch);
+}
+
/* Remove all pptrs from @ip. */
static void
clear_all_pptrs(
@@ -729,7 +767,16 @@ add_missing_parent_ptr(
name);
}
- /* XXX actually do the work */
+ error = add_file_pptr(ip, ag_pptr, name);
+ if (error)
+ do_error(
+ _("adding ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name,
+ strerror(error));
}
/* Remove @file_pptr from @ip. */
@@ -771,7 +818,16 @@ remove_incorrect_parent_ptr(
file_pptr->namelen,
name);
- /* XXX actually do the work */
+ error = remove_file_pptr(ip, file_pptr, name);
+ if (error)
+ do_error(
+ _("removing ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ name,
+ strerror(error));
}
/*
@@ -851,7 +907,33 @@ compare_parent_ptrs(
ag_pptr->namelen,
name1);
- /* XXX do the work */
+ /* Remove the parent pointer that we don't want. */
+ error = remove_file_pptr(ip, file_pptr, name2);
+ if (error)
+ do_error(
+_("erasing ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ name2,
+ strerror(error));
+
+ /*
+ * Add the parent pointer that we do want. It's possible that this
+ * parent pointer already exists but we haven't gotten that far in the
+ * scan, so we'll keep going on EEXIST.
+ */
+ error = add_file_pptr(ip, ag_pptr, name1);
+ if (error && error != EEXIST)
+ do_error(
+ _("updating ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name1,
+ strerror(error));
}
static int
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 12/12] xfs_repair: wipe ondisk parent pointers when there are none
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
` (10 preceding siblings ...)
2024-07-30 1:28 ` [PATCH 11/12] xfs_repair: update ondisk parent pointer records Darrick J. Wong
@ 2024-07-30 1:28 ` Darrick J. Wong
11 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:28 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, linux-xfs, catherine.hoang, allison.henderson
From: Darrick J. Wong <djwong@kernel.org>
Erase all the parent pointers when there aren't any found by the
directory entry scan.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
repair/pptr.c | 41 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 38 insertions(+), 3 deletions(-)
diff --git a/repair/pptr.c b/repair/pptr.c
index 94d6d8346..8ec6a51d2 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -714,8 +714,13 @@ remove_file_pptr(
/* Remove all pptrs from @ip. */
static void
clear_all_pptrs(
- struct xfs_inode *ip)
+ struct xfs_inode *ip,
+ struct file_scan *fscan)
{
+ struct xfs_slab_cursor *cur;
+ struct file_pptr *file_pptr;
+ int error;
+
if (no_modify) {
do_warn(_("would delete unlinked ino %llu parent pointers\n"),
(unsigned long long)ip->i_ino);
@@ -724,7 +729,37 @@ clear_all_pptrs(
do_warn(_("deleting unlinked ino %llu parent pointers\n"),
(unsigned long long)ip->i_ino);
- /* XXX actually do the work */
+
+ error = -init_slab_cursor(fscan->file_pptr_recs, NULL, &cur);
+ if (error)
+ do_error(_("init ino %llu pptr cursor failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+
+ while ((file_pptr = pop_slab_cursor(cur)) != NULL) {
+ unsigned char name[MAXNAMELEN];
+
+ error = load_file_pptr_name(fscan, file_pptr, name);
+ if (error)
+ do_error(
+ _("loading incorrect name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ (unsigned long long)file_pptr->name_cookie,
+ strerror(error));
+
+ error = remove_file_pptr(ip, file_pptr, name);
+ if (error)
+ do_error(
+ _("wiping ino %llu pptr (ino %llu gen 0x%x) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ strerror(error));
+ }
+
+ free_slab_cursor(&cur);
}
/* Add @ag_pptr to @ip. */
@@ -1028,7 +1063,7 @@ crosscheck_file_parent_ptrs(
* file.
*/
if (fscan->nr_file_pptrs > 0)
- clear_all_pptrs(ip);
+ clear_all_pptrs(ip, fscan);
return;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/5] libfrog: add directory tree structure scrubber to scrub library
2024-07-30 0:21 ` [PATCHSET v13.8 21/23] xfsprogs: detect and correct directory tree problems Darrick J. Wong
@ 2024-07-30 1:29 ` Darrick J. Wong
2024-07-30 1:29 ` [PATCH 2/5] xfs_spaceman: report directory tree corruption in the health information Darrick J. Wong
` (3 subsequent siblings)
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:29 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Make it so that scrub clients can detect corruptions within the
directory tree structure itself. Update the documentation for the scrub
ioctl to mention this new functionality.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libfrog/scrub.c | 5 +++++
man/man2/ioctl_xfs_scrub_metadata.2 | 14 ++++++++++++++
2 files changed, 19 insertions(+)
diff --git a/libfrog/scrub.c b/libfrog/scrub.c
index baaa4b4d9..a2146e228 100644
--- a/libfrog/scrub.c
+++ b/libfrog/scrub.c
@@ -149,6 +149,11 @@ const struct xfrog_scrub_descr xfrog_scrubbers[XFS_SCRUB_TYPE_NR] = {
.descr = "retained health records",
.group = XFROG_SCRUB_GROUP_NONE,
},
+ [XFS_SCRUB_TYPE_DIRTREE] = {
+ .name = "dirtree",
+ .descr = "directory tree structure",
+ .group = XFROG_SCRUB_GROUP_INODE,
+ },
};
#undef DEP
diff --git a/man/man2/ioctl_xfs_scrub_metadata.2 b/man/man2/ioctl_xfs_scrub_metadata.2
index 75ae52bb5..44aa139b2 100644
--- a/man/man2/ioctl_xfs_scrub_metadata.2
+++ b/man/man2/ioctl_xfs_scrub_metadata.2
@@ -148,6 +148,20 @@ that points back to the subdirectory.
The inode to examine can be specified in the same manner as
.BR XFS_SCRUB_TYPE_INODE "."
+.TP
+.B XFS_SCRUB_TYPE_DIRTREE
+This scrubber looks for problems in the directory tree structure such as loops
+and directories accessible through more than one path.
+Problems are detected by walking parent pointers upwards towards the root.
+Loops are detected by comparing the parent directory at each step against the
+directories already examined.
+Directories with multiple paths are detected by counting the parent pointers
+attached to a directory.
+Non-directories do not have links pointing away from the directory tree root
+and can be skipped.
+The directory to examine can be specified in the same manner as
+.BR XFS_SCRUB_TYPE_INODE "."
+
.TP
.B XFS_SCRUB_TYPE_SYMLINK
Examine the target of a symbolic link for obvious pathname problems.
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 2/5] xfs_spaceman: report directory tree corruption in the health information
2024-07-30 0:21 ` [PATCHSET v13.8 21/23] xfsprogs: detect and correct directory tree problems Darrick J. Wong
2024-07-30 1:29 ` [PATCH 1/5] libfrog: add directory tree structure scrubber to scrub library Darrick J. Wong
@ 2024-07-30 1:29 ` Darrick J. Wong
2024-07-30 1:29 ` [PATCH 3/5] xfs_scrub: fix erroring out of check_inode_names Darrick J. Wong
` (2 subsequent siblings)
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:29 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Report directories that are the source of corruption in the directory
tree. While we're at it, add the documentation updates for the new
reporting flags and scrub type.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man2/ioctl_xfs_bulkstat.2 | 3 +++
man/man2/ioctl_xfs_fsbulkstat.2 | 3 +++
spaceman/health.c | 4 ++++
3 files changed, 10 insertions(+)
diff --git a/man/man2/ioctl_xfs_bulkstat.2 b/man/man2/ioctl_xfs_bulkstat.2
index 3203ca0c5..b6d51aa43 100644
--- a/man/man2/ioctl_xfs_bulkstat.2
+++ b/man/man2/ioctl_xfs_bulkstat.2
@@ -326,6 +326,9 @@ Symbolic link target.
.TP
.B XFS_BS_SICK_PARENT
Parent pointers.
+.TP
+.B XFS_BS_SICK_DIRTREE
+Directory is the source of corruption in the directory tree.
.RE
.SH ERRORS
Error codes can be one of, but are not limited to, the following:
diff --git a/man/man2/ioctl_xfs_fsbulkstat.2 b/man/man2/ioctl_xfs_fsbulkstat.2
index 3f059942a..cd38d2fd6 100644
--- a/man/man2/ioctl_xfs_fsbulkstat.2
+++ b/man/man2/ioctl_xfs_fsbulkstat.2
@@ -239,6 +239,9 @@ Symbolic link target.
.TP
.B XFS_BS_SICK_PARENT
Parent pointers.
+.TP
+.B XFS_BS_SICK_DIRTREE
+Directory is the source of corruption in the directory tree.
.RE
.SH RETURN VALUE
On error, \-1 is returned, and
diff --git a/spaceman/health.c b/spaceman/health.c
index 6722babf5..d88a7f6c6 100644
--- a/spaceman/health.c
+++ b/spaceman/health.c
@@ -165,6 +165,10 @@ static const struct flag_map inode_flags[] = {
.mask = XFS_BS_SICK_PARENT,
.descr = "parent pointers",
},
+ {
+ .mask = XFS_BS_SICK_DIRTREE,
+ .descr = "directory tree structure",
+ },
{0},
};
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 3/5] xfs_scrub: fix erroring out of check_inode_names
2024-07-30 0:21 ` [PATCHSET v13.8 21/23] xfsprogs: detect and correct directory tree problems Darrick J. Wong
2024-07-30 1:29 ` [PATCH 1/5] libfrog: add directory tree structure scrubber to scrub library Darrick J. Wong
2024-07-30 1:29 ` [PATCH 2/5] xfs_spaceman: report directory tree corruption in the health information Darrick J. Wong
@ 2024-07-30 1:29 ` Darrick J. Wong
2024-07-30 1:29 ` [PATCH 4/5] xfs_scrub: detect and repair directory tree corruptions Darrick J. Wong
2024-07-30 1:30 ` [PATCH 5/5] xfs_scrub: defer phase5 file scans if dirloop fails Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:29 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
The early exit logic in this function is a bit suboptimal -- we don't
need to close the @fd if we haven't even opened it, and since all errors
are fatal, we don't need to bump the progress counter. The logic in
this function is about to get more involved due to the addition of the
directory tree structure checker, so clean up these warts.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase5.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/scrub/phase5.c b/scrub/phase5.c
index 0df8c46e9..b37196277 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -279,7 +279,7 @@ check_inode_names(
if (bstat->bs_xflags & FS_XFLAG_HASATTR) {
error = check_xattr_names(ctx, &dsc, handle, bstat);
if (error)
- goto out;
+ goto err;
}
/*
@@ -295,16 +295,16 @@ check_inode_names(
if (error == ESTALE)
return ESTALE;
str_errno(ctx, descr_render(&dsc));
- goto out;
+ goto err;
}
error = check_dirent_names(ctx, &dsc, &fd, bstat);
if (error)
- goto out;
+ goto err_fd;
}
-out:
progress_add(1);
+err_fd:
if (fd >= 0) {
err2 = close(fd);
if (err2)
@@ -312,7 +312,7 @@ check_inode_names(
if (!error && err2)
error = err2;
}
-
+err:
if (error)
*aborted = true;
if (!error && *aborted)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 4/5] xfs_scrub: detect and repair directory tree corruptions
2024-07-30 0:21 ` [PATCHSET v13.8 21/23] xfsprogs: detect and correct directory tree problems Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:29 ` [PATCH 3/5] xfs_scrub: fix erroring out of check_inode_names Darrick J. Wong
@ 2024-07-30 1:29 ` Darrick J. Wong
2024-07-30 1:30 ` [PATCH 5/5] xfs_scrub: defer phase5 file scans if dirloop fails Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:29 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Now that we have online fsck for directory tree structure problems, we
need to find a place to call it. The scanner requires that parent
pointers are enabled, that directory link counts are correct, and that
every directory entry has a corresponding parent pointer. Therefore, we
can only run it after phase 4 fixes every file, and phase 5 resets the
link counts.
In other words, we call it as part of the phase 5 file scan that we do
to warn about weird looking file names. This has the added benefit that
opening the directory by handle is less likely to fail if there are
loops in the directory structure. For now, only plumb in enough to try
to fix directory tree problems right away; the next patch will make
phase 5 retry the dirloop scanner until the problems are fixed or we
stop making forward progress.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase5.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/scrub/phase5.c b/scrub/phase5.c
index b37196277..6c8dee66e 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -252,6 +252,47 @@ render_ino_from_handle(
bstat->bs_gen, NULL);
}
+/*
+ * Check the directory structure for problems that could cause open_by_handle
+ * not to work. Returns 0 for no problems; EADDRNOTAVAIL if the there are
+ * problems that would prevent name checking.
+ */
+static int
+check_dir_connection(
+ struct scrub_ctx *ctx,
+ struct descr *dsc,
+ const struct xfs_bulkstat *bstat)
+{
+ struct scrub_item sri = { };
+ int error;
+
+ /* The dirtree scrubber only works when parent pointers are enabled */
+ if (!(ctx->mnt.fsgeom.flags & XFS_FSOP_GEOM_FLAGS_PARENT))
+ return 0;
+
+ scrub_item_init_file(&sri, bstat);
+ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_DIRTREE);
+
+ error = scrub_item_check_file(ctx, &sri, -1);
+ if (error) {
+ str_liberror(ctx, error, _("checking directory loops"));
+ return error;
+ }
+
+ error = repair_file_corruption(ctx, &sri, -1);
+ if (error) {
+ str_liberror(ctx, error, _("repairing directory loops"));
+ return error;
+ }
+
+ /* No directory tree problems? Clear this inode if it was deferred. */
+ if (repair_item_count_needsrepair(&sri) == 0)
+ return 0;
+
+ str_corrupt(ctx, descr_render(dsc), _("directory loop uncorrected!"));
+ return EADDRNOTAVAIL;
+}
+
/*
* Verify the connectivity of the directory tree.
* We know that the kernel's open-by-handle function will try to reconnect
@@ -275,6 +316,20 @@ check_inode_names(
descr_set(&dsc, bstat);
background_sleep();
+ /*
+ * Try to fix directory loops before we have problems opening files by
+ * handle.
+ */
+ if (S_ISDIR(bstat->bs_mode)) {
+ error = check_dir_connection(ctx, &dsc, bstat);
+ if (error == EADDRNOTAVAIL) {
+ error = 0;
+ goto out;
+ }
+ if (error)
+ goto err;
+ }
+
/* Warn about naming problems in xattrs. */
if (bstat->bs_xflags & FS_XFLAG_HASATTR) {
error = check_xattr_names(ctx, &dsc, handle, bstat);
@@ -315,6 +370,7 @@ check_inode_names(
err:
if (error)
*aborted = true;
+out:
if (!error && *aborted)
error = ECANCELED;
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 5/5] xfs_scrub: defer phase5 file scans if dirloop fails
2024-07-30 0:21 ` [PATCHSET v13.8 21/23] xfsprogs: detect and correct directory tree problems Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:29 ` [PATCH 4/5] xfs_scrub: detect and repair directory tree corruptions Darrick J. Wong
@ 2024-07-30 1:30 ` Darrick J. Wong
4 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:30 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If we cannot fix dirloop problems during the initial phase 5 inode scan,
defer them until later.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase5.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
scrub/repair.c | 13 +++
scrub/repair.h | 2 +
3 files changed, 216 insertions(+), 14 deletions(-)
diff --git a/scrub/phase5.c b/scrub/phase5.c
index 6c8dee66e..f6c295c64 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -18,6 +18,8 @@
#include "libfrog/workqueue.h"
#include "libfrog/fsgeom.h"
#include "libfrog/scrub.h"
+#include "libfrog/bitmap.h"
+#include "libfrog/bulkstat.h"
#include "xfs_scrub.h"
#include "common.h"
#include "inodes.h"
@@ -29,6 +31,36 @@
/* Phase 5: Full inode scans and check directory connectivity. */
+struct ncheck_state {
+ struct scrub_ctx *ctx;
+
+ /* Have we aborted this scan? */
+ bool aborted;
+
+ /* Is this the last time we're going to process deferred inodes? */
+ bool last_call;
+
+ /* Did we fix at least one thing while walking @cur->deferred? */
+ bool fixed_something;
+
+ /* Lock for this structure */
+ pthread_mutex_t lock;
+
+ /*
+ * Inodes that are involved with directory tree structure corruptions
+ * are marked here. This will be NULL until the first corruption is
+ * noted.
+ */
+ struct bitmap *new_deferred;
+
+ /*
+ * Inodes that we're reprocessing due to earlier directory tree
+ * structure corruption problems are marked here. This will be NULL
+ * during the first (parallel) inode scan.
+ */
+ struct bitmap *cur_deferred;
+};
+
/*
* Warn about problematic bytes in a directory/attribute name. That means
* terminal control characters and escape sequences, since that could be used
@@ -252,6 +284,26 @@ render_ino_from_handle(
bstat->bs_gen, NULL);
}
+/* Defer this inode until later. */
+static inline int
+defer_inode(
+ struct ncheck_state *ncs,
+ uint64_t ino)
+{
+ int error;
+
+ pthread_mutex_lock(&ncs->lock);
+ if (!ncs->new_deferred) {
+ error = -bitmap_alloc(&ncs->new_deferred);
+ if (error)
+ goto unlock;
+ }
+ error = -bitmap_set(ncs->new_deferred, ino, 1);
+unlock:
+ pthread_mutex_unlock(&ncs->lock);
+ return error;
+}
+
/*
* Check the directory structure for problems that could cause open_by_handle
* not to work. Returns 0 for no problems; EADDRNOTAVAIL if the there are
@@ -260,7 +312,7 @@ render_ino_from_handle(
static int
check_dir_connection(
struct scrub_ctx *ctx,
- struct descr *dsc,
+ struct ncheck_state *ncs,
const struct xfs_bulkstat *bstat)
{
struct scrub_item sri = { };
@@ -279,17 +331,31 @@ check_dir_connection(
return error;
}
- error = repair_file_corruption(ctx, &sri, -1);
+ if (ncs->last_call)
+ error = repair_file_corruption_now(ctx, &sri, -1);
+ else
+ error = repair_file_corruption(ctx, &sri, -1);
if (error) {
str_liberror(ctx, error, _("repairing directory loops"));
return error;
}
/* No directory tree problems? Clear this inode if it was deferred. */
- if (repair_item_count_needsrepair(&sri) == 0)
+ if (repair_item_count_needsrepair(&sri) == 0) {
+ if (ncs->cur_deferred)
+ ncs->fixed_something = true;
return 0;
+ }
+
+ /* Don't defer anything during last call. */
+ if (ncs->last_call)
+ return 0;
+
+ /* Directory tree structure problems exist; do not check names yet. */
+ error = defer_inode(ncs, bstat->bs_ino);
+ if (error)
+ return error;
- str_corrupt(ctx, descr_render(dsc), _("directory loop uncorrected!"));
return EADDRNOTAVAIL;
}
@@ -308,7 +374,7 @@ check_inode_names(
void *arg)
{
DEFINE_DESCR(dsc, ctx, render_ino_from_handle);
- bool *aborted = arg;
+ struct ncheck_state *ncs = arg;
int fd = -1;
int error = 0;
int err2;
@@ -321,7 +387,7 @@ check_inode_names(
* handle.
*/
if (S_ISDIR(bstat->bs_mode)) {
- error = check_dir_connection(ctx, &dsc, bstat);
+ error = check_dir_connection(ctx, ncs, bstat);
if (error == EADDRNOTAVAIL) {
error = 0;
goto out;
@@ -369,14 +435,120 @@ check_inode_names(
}
err:
if (error)
- *aborted = true;
+ ncs->aborted = true;
out:
- if (!error && *aborted)
+ if (!error && ncs->aborted)
error = ECANCELED;
return error;
}
+/* Try to check_inode_names on a specific inode. */
+static int
+retry_deferred_inode(
+ struct ncheck_state *ncs,
+ struct xfs_handle *handle,
+ uint64_t ino)
+{
+ struct xfs_bulkstat bstat;
+ struct scrub_ctx *ctx = ncs->ctx;
+ unsigned int flags = 0;
+ int error;
+
+ error = -xfrog_bulkstat_single(&ctx->mnt, ino, flags, &bstat);
+ if (error == ENOENT) {
+ /* Directory is gone, mark it clear. */
+ ncs->fixed_something = true;
+ return 0;
+ }
+ if (error)
+ return error;
+
+ handle->ha_fid.fid_ino = bstat.bs_ino;
+ handle->ha_fid.fid_gen = bstat.bs_gen;
+
+ return check_inode_names(ncs->ctx, handle, &bstat, ncs);
+}
+
+/* Try to check_inode_names on a range of inodes from the bitmap. */
+static int
+retry_deferred_inode_range(
+ uint64_t ino,
+ uint64_t len,
+ void *arg)
+{
+ struct xfs_handle handle = { };
+ struct ncheck_state *ncs = arg;
+ struct scrub_ctx *ctx = ncs->ctx;
+ uint64_t i;
+ int error;
+
+ memcpy(&handle.ha_fsid, ctx->fshandle, sizeof(handle.ha_fsid));
+ handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
+ sizeof(handle.ha_fid.fid_len);
+ handle.ha_fid.fid_pad = 0;
+
+ for (i = 0; i < len; i++) {
+ error = retry_deferred_inode(ncs, &handle, ino + i);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+/*
+ * Try to check_inode_names on inodes that were deferred due to directory tree
+ * problems until we stop making progress.
+ */
+static int
+retry_deferred_inodes(
+ struct scrub_ctx *ctx,
+ struct ncheck_state *ncs)
+{
+ int error;
+
+ if (!ncs->new_deferred)
+ return 0;
+
+ /*
+ * Try to repair things until we stop making forward progress or we
+ * don't observe any new corruptions. During the loop, we do not
+ * complain about the corruptions that do not get fixed.
+ */
+ do {
+ ncs->cur_deferred = ncs->new_deferred;
+ ncs->new_deferred = NULL;
+ ncs->fixed_something = false;
+
+ error = -bitmap_iterate(ncs->cur_deferred,
+ retry_deferred_inode_range, ncs);
+ if (error)
+ return error;
+
+ bitmap_free(&ncs->cur_deferred);
+ } while (ncs->fixed_something && ncs->new_deferred);
+
+ /*
+ * Try one last time to fix things, and complain about any problems
+ * that remain.
+ */
+ if (!ncs->new_deferred)
+ return 0;
+
+ ncs->cur_deferred = ncs->new_deferred;
+ ncs->new_deferred = NULL;
+ ncs->last_call = true;
+
+ error = -bitmap_iterate(ncs->cur_deferred,
+ retry_deferred_inode_range, ncs);
+ if (error)
+ return error;
+
+ bitmap_free(&ncs->cur_deferred);
+ return 0;
+}
+
#ifndef FS_IOC_GETFSLABEL
# define FSLABEL_MAX 256
# define FS_IOC_GETFSLABEL _IOR(0x94, 49, char[FSLABEL_MAX])
@@ -568,9 +740,10 @@ int
phase5_func(
struct scrub_ctx *ctx)
{
- bool aborted = false;
+ struct ncheck_state ncs = { .ctx = ctx };
int ret;
+
/*
* Check and fix anything that requires a full filesystem scan. We do
* this after we've checked all inodes and repaired anything that could
@@ -590,14 +763,28 @@ _("Filesystem has errors, skipping connectivity checks."));
if (ret)
return ret;
- ret = scrub_scan_all_inodes(ctx, check_inode_names, &aborted);
+ pthread_mutex_init(&ncs.lock, NULL);
+
+ ret = scrub_scan_all_inodes(ctx, check_inode_names, &ncs);
if (ret)
- return ret;
- if (aborted)
- return ECANCELED;
+ goto out_lock;
+ if (ncs.aborted) {
+ ret = ECANCELED;
+ goto out_lock;
+ }
+
+ ret = retry_deferred_inodes(ctx, &ncs);
+ if (ret)
+ goto out_lock;
scrub_report_preen_triggers(ctx);
- return 0;
+out_lock:
+ pthread_mutex_destroy(&ncs.lock);
+ if (ncs.new_deferred)
+ bitmap_free(&ncs.new_deferred);
+ if (ncs.cur_deferred)
+ bitmap_free(&ncs.cur_deferred);
+ return ret;
}
/* Estimate how much work we're going to do. */
diff --git a/scrub/repair.c b/scrub/repair.c
index 025821072..4fed86134 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -732,6 +732,19 @@ repair_file_corruption(
XRM_REPAIR_ONLY | XRM_NOPROGRESS);
}
+/* Repair all parts of this file or complain if we cannot. */
+int
+repair_file_corruption_now(
+ struct scrub_ctx *ctx,
+ struct scrub_item *sri,
+ int override_fd)
+{
+ repair_item_boost_priorities(sri);
+
+ return repair_item_class(ctx, sri, override_fd, SCRUB_ITEM_CORRUPT,
+ XRM_REPAIR_ONLY | XRM_NOPROGRESS | XRM_FINAL_WARNING);
+}
+
/*
* Repair everything in this filesystem object that needs it. This includes
* cross-referencing and preening.
diff --git a/scrub/repair.h b/scrub/repair.h
index 411a379f6..ec4aa381a 100644
--- a/scrub/repair.h
+++ b/scrub/repair.h
@@ -76,6 +76,8 @@ int action_list_process(struct scrub_ctx *ctx, struct action_list *alist,
int repair_item_corruption(struct scrub_ctx *ctx, struct scrub_item *sri);
int repair_file_corruption(struct scrub_ctx *ctx, struct scrub_item *sri,
int override_fd);
+int repair_file_corruption_now(struct scrub_ctx *ctx, struct scrub_item *sri,
+ int override_fd);
int repair_item(struct scrub_ctx *ctx, struct scrub_item *sri,
unsigned int repair_flags);
int repair_item_to_action_item(struct scrub_ctx *ctx,
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 01/10] man: document vectored scrub mode
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
@ 2024-07-30 1:30 ` Darrick J. Wong
2024-07-30 1:30 ` [PATCH 02/10] libfrog: support vectored scrub Darrick J. Wong
` (8 subsequent siblings)
9 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:30 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add a manpage to document XFS_IOC_SCRUBV_METADATA. From the kernel
patch:
Introduce a variant on XFS_SCRUB_METADATA that allows for a vectored
mode. The caller specifies the principal metadata object that they want
to scrub (allocation group, inode, etc.) once, followed by an array of
scrub types they want called on that object. The kernel runs the scrub
operations and writes the output flags and errno code to the
corresponding array element.
A new pseudo scrub type BARRIER is introduced to force the kernel to
return to userspace if any corruptions have been found when scrubbing
the previous scrub types in the array. This enables userspace to
schedule, for example, the sequence:
1. data fork
2. barrier
3. directory
If the data fork scrub is clean, then the kernel will perform the
directory scrub. If not, the barrier in 2 will exit back to userspace.
The alternative would have been an interface where userspace passes a
pointer to an empty buffer, and the kernel formats that with
xfs_scrub_vecs that tell userspace what it scrubbed and what the outcome
was. With that the kernel would have to communicate that the buffer
needed to have been at least X size, even though for our cases
XFS_SCRUB_TYPE_NR + 2 would always be enough.
Compared to that, this design keeps all the dependency policy and
ordering logic in userspace where it already resides instead of
duplicating it in the kernel. The downside of that is that it needs the
barrier logic.
When running fstests in "rebuild all metadata after each test" mode, I
observed a 10% reduction in runtime due to fewer transitions across the
system call boundary.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
man/man2/ioctl_xfs_scrubv_metadata.2 | 171 ++++++++++++++++++++++++++++++++++
1 file changed, 171 insertions(+)
create mode 100644 man/man2/ioctl_xfs_scrubv_metadata.2
diff --git a/man/man2/ioctl_xfs_scrubv_metadata.2 b/man/man2/ioctl_xfs_scrubv_metadata.2
new file mode 100644
index 000000000..532916756
--- /dev/null
+++ b/man/man2/ioctl_xfs_scrubv_metadata.2
@@ -0,0 +1,171 @@
+.\" Copyright (c) 2023-2024 Oracle. All rights reserved.
+.\"
+.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
+.\" SPDX-License-Identifier: GPL-2.0-or-later
+.\" %%%LICENSE_END
+.TH IOCTL-XFS-SCRUBV-METADATA 2 2024-05-21 "XFS"
+.SH NAME
+ioctl_xfs_scrubv_metadata \- check a lot of XFS filesystem metadata
+.SH SYNOPSIS
+.br
+.B #include <xfs/xfs_fs.h>
+.PP
+.BI "int ioctl(int " dest_fd ", XFS_IOC_SCRUBV_METADATA, struct xfs_scrub_vec_head *" arg );
+.SH DESCRIPTION
+This XFS ioctl asks the kernel driver to examine several pieces of filesystem
+metadata for errors or suboptimal metadata.
+Multiple scrub types can be invoked to target a single filesystem object.
+See
+.BR ioctl_xfs_scrub_metadata (2)
+for a discussion of metadata validation, and documentation of the various
+.B XFS_SCRUB_TYPE
+and
+.B XFS_SCRUB_FLAGS
+values referenced below.
+
+The types and location of the metadata to scrub are conveyed as a vector with
+a header of the following form:
+.PP
+.in +4n
+.nf
+
+struct xfs_scrub_vec_head {
+ __u64 svh_ino;
+ __u32 svh_gen;
+ __u32 svh_agno;
+ __u32 svh_flags;
+ __u16 svh_rest_us;
+ __u16 svh_nr;
+ __u64 svh_reserved;
+ __u64 svh_vectors;
+};
+.fi
+.in
+.PP
+The field
+.IR svh_ino ,
+.IR svh_gen ,
+and
+.IR svh_agno
+correspond to the
+.IR sm_ino ,
+.IR sm_gen ,
+and
+.IR sm_agno
+fields of the regular scrub ioctl.
+Exactly one filesystem object can be specified in a single call.
+The kernel will proceed with each vector in
+.I svh_vectors
+until progress is no longer possible.
+
+The field
+.I svh_rest_us
+specifies an amount of time to pause between each scrub invocation to give
+the system a chance to process other requests.
+
+The field
+.I svh_nr
+specifies the number of vectors in the
+.I svh_vectors
+array.
+
+The field
+.I svh_vectors
+is a pointer to an array of
+.B struct xfs_scrub_vec
+structures.
+
+.PP
+The field
+.I svh_reserved
+must be zero.
+
+Each vector has the following form:
+.PP
+.in +4n
+.nf
+
+struct xfs_scrub_vec {
+ __u32 sv_type;
+ __u32 sv_flags;
+ __s32 sv_ret;
+ __u32 sv_reserved;
+};
+.fi
+.in
+
+.PP
+The fields
+.I sv_type
+and
+.I sv_flags
+indicate the type of metadata to check and the behavioral changes that
+userspace will permit of the kernel.
+The
+.I sv_flags
+field will be updated upon completion of the scrub call.
+See the documentation of
+.B XFS_SCRUB_TYPE_*
+and
+.B XFS_SCRUB_[IO]FLAG_*
+values in
+.BR ioctl_xfs_scrub_metadata (2)
+for a detailed description of their purpose.
+
+.PP
+If a vector's
+.I sv_type
+field is set to the value
+.BR XFS_SCRUB_TYPE_BARRIER ,
+the kernel will stop processing vectors and return to userspace if a scrubber
+flags corruption by setting one of the
+.B XFS_SCRUB_OFLAG_*
+values in
+.I sv_flags
+or
+returns an operation error in
+.IR sv_ret .
+Otherwise, the kernel returns only after processing all vectors.
+
+The
+.I sv_ret
+field is set to the return value of the scrub function.
+See the RETURN VALUE
+section of the
+.BR ioctl_xfs_scrub_metadata (2)
+manual page for more information.
+
+The
+.B sv_reserved
+field must be zero.
+
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EINVAL
+One or more of the arguments specified is invalid.
+.TP
+.B EINTR
+The operation was interrupted.
+.TP
+.B ENOMEM
+There was not sufficient memory to perform the scrub or repair operation.
+.TP
+.B EFAULT
+A memory fault was encountered while reading or writing the vector.
+.SH CONFORMING TO
+This API is specific to XFS filesystem on the Linux kernel.
+.SH NOTES
+These operations may block other filesystem operations for a long time.
+A calling process can stop the operation by being sent a fatal
+signal, but non-fatal signals are blocked.
+.SH SEE ALSO
+.BR ioctl (2)
+.BR ioctl_xfs_scrub_metadata (2)
+.BR xfs_scrub (8)
+.BR xfs_repair (8)
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 02/10] libfrog: support vectored scrub
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
2024-07-30 1:30 ` [PATCH 01/10] man: document vectored scrub mode Darrick J. Wong
@ 2024-07-30 1:30 ` Darrick J. Wong
2024-07-30 1:30 ` [PATCH 03/10] xfs_io: " Darrick J. Wong
` (7 subsequent siblings)
9 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:30 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Enhance libfrog to support performing vectored metadata scrub.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
libfrog/fsgeom.h | 6 ++
libfrog/scrub.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
libfrog/scrub.h | 35 ++++++++++++++
3 files changed, 178 insertions(+)
diff --git a/libfrog/fsgeom.h b/libfrog/fsgeom.h
index f327dc7d7..df2ca2a40 100644
--- a/libfrog/fsgeom.h
+++ b/libfrog/fsgeom.h
@@ -50,6 +50,12 @@ struct xfs_fd {
/* Only use v5 bulkstat/inumbers ioctls. */
#define XFROG_FLAG_BULKSTAT_FORCE_V5 (1 << 1)
+/* Only use the older one-at-a-time scrub ioctl. */
+#define XFROG_FLAG_SCRUB_FORCE_SINGLE (1 << 2)
+
+/* Only use the vectored scrub ioctl. */
+#define XFROG_FLAG_SCRUB_FORCE_VECTOR (1 << 3)
+
/* Static initializers */
#define XFS_FD_INIT(_fd) { .fd = (_fd), }
#define XFS_FD_INIT_EMPTY XFS_FD_INIT(-1)
diff --git a/libfrog/scrub.c b/libfrog/scrub.c
index a2146e228..e233c0f9c 100644
--- a/libfrog/scrub.c
+++ b/libfrog/scrub.c
@@ -171,3 +171,140 @@ xfrog_scrub_metadata(
return 0;
}
+
+/* Decide if there have been any scrub failures up to this point. */
+static inline int
+xfrog_scrubv_check_barrier(
+ const struct xfs_scrub_vec *vectors,
+ const struct xfs_scrub_vec *stop_vec)
+{
+ const struct xfs_scrub_vec *v;
+ __u32 failmask;
+
+ failmask = stop_vec->sv_flags & XFS_SCRUB_FLAGS_OUT;
+
+ for (v = vectors; v < stop_vec; v++) {
+ if (v->sv_type == XFS_SCRUB_TYPE_BARRIER)
+ continue;
+
+ /*
+ * Runtime errors count as a previous failure, except the ones
+ * used to ask userspace to retry.
+ */
+ switch (v->sv_ret) {
+ case -EBUSY:
+ case -ENOENT:
+ case -EUSERS:
+ case 0:
+ break;
+ default:
+ return -ECANCELED;
+ }
+
+ /*
+ * If any of the out-flags on the scrub vector match the mask
+ * that was set on the barrier vector, that's a previous fail.
+ */
+ if (v->sv_flags & failmask)
+ return -ECANCELED;
+ }
+
+ return 0;
+}
+
+static int
+xfrog_scrubv_fallback(
+ struct xfs_fd *xfd,
+ struct xfrog_scrubv *scrubv)
+{
+ struct xfs_scrub_vec *vectors = scrubv->vectors;
+ struct xfs_scrub_vec *v;
+ unsigned int i;
+
+ if (scrubv->head.svh_flags & ~XFS_SCRUB_VEC_FLAGS_ALL)
+ return -EINVAL;
+
+ foreach_xfrog_scrubv_vec(scrubv, i, v) {
+ if (v->sv_reserved)
+ return -EINVAL;
+
+ if (v->sv_type == XFS_SCRUB_TYPE_BARRIER &&
+ (v->sv_flags & ~XFS_SCRUB_FLAGS_OUT))
+ return -EINVAL;
+ }
+
+ /* Run all the scrubbers. */
+ foreach_xfrog_scrubv_vec(scrubv, i, v) {
+ struct xfs_scrub_metadata sm = {
+ .sm_type = v->sv_type,
+ .sm_flags = v->sv_flags,
+ .sm_ino = scrubv->head.svh_ino,
+ .sm_gen = scrubv->head.svh_gen,
+ .sm_agno = scrubv->head.svh_agno,
+ };
+ struct timespec tv;
+
+ if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) {
+ v->sv_ret = xfrog_scrubv_check_barrier(vectors, v);
+ if (v->sv_ret)
+ break;
+ continue;
+ }
+
+ v->sv_ret = xfrog_scrub_metadata(xfd, &sm);
+ v->sv_flags = sm.sm_flags;
+
+ if (scrubv->head.svh_rest_us) {
+ tv.tv_sec = 0;
+ tv.tv_nsec = scrubv->head.svh_rest_us * 1000;
+ nanosleep(&tv, NULL);
+ }
+ }
+
+ return 0;
+}
+
+/* Invoke the vectored scrub ioctl. */
+static int
+xfrog_scrubv_call(
+ struct xfs_fd *xfd,
+ struct xfs_scrub_vec_head *vhead)
+{
+ int ret;
+
+ ret = ioctl(xfd->fd, XFS_IOC_SCRUBV_METADATA, vhead);
+ if (ret)
+ return -errno;
+
+ return 0;
+}
+
+/* Invoke the vectored scrub ioctl. Returns zero or negative error code. */
+int
+xfrog_scrubv_metadata(
+ struct xfs_fd *xfd,
+ struct xfrog_scrubv *scrubv)
+{
+ int error = 0;
+
+ if (scrubv->head.svh_nr > XFROG_SCRUBV_MAX_VECTORS)
+ return -EINVAL;
+
+ if (xfd->flags & XFROG_FLAG_SCRUB_FORCE_SINGLE)
+ goto try_single;
+
+ error = xfrog_scrubv_call(xfd, &scrubv->head);
+ if (error == 0 || (xfd->flags & XFROG_FLAG_SCRUB_FORCE_VECTOR))
+ return error;
+
+ /* If the vectored scrub ioctl wasn't found, force single mode. */
+ switch (error) {
+ case -EOPNOTSUPP:
+ case -ENOTTY:
+ xfd->flags |= XFROG_FLAG_SCRUB_FORCE_SINGLE;
+ break;
+ }
+
+try_single:
+ return xfrog_scrubv_fallback(xfd, scrubv);
+}
diff --git a/libfrog/scrub.h b/libfrog/scrub.h
index 27230c62f..b564c0d7b 100644
--- a/libfrog/scrub.h
+++ b/libfrog/scrub.h
@@ -28,4 +28,39 @@ extern const struct xfrog_scrub_descr xfrog_scrubbers[XFS_SCRUB_TYPE_NR];
int xfrog_scrub_metadata(struct xfs_fd *xfd, struct xfs_scrub_metadata *meta);
+/*
+ * Allow enough space to call all scrub types with a barrier between each.
+ * This is overkill for every caller in xfsprogs.
+ */
+#define XFROG_SCRUBV_MAX_VECTORS (XFS_SCRUB_TYPE_NR * 2)
+
+struct xfrog_scrubv {
+ struct xfs_scrub_vec_head head;
+ struct xfs_scrub_vec vectors[XFROG_SCRUBV_MAX_VECTORS];
+};
+
+/* Initialize a scrubv structure; callers must have zeroed @scrubv. */
+static inline void
+xfrog_scrubv_init(struct xfrog_scrubv *scrubv)
+{
+ scrubv->head.svh_vectors = (uintptr_t)scrubv->vectors;
+}
+
+/* Return the next free vector from the scrubv structure. */
+static inline struct xfs_scrub_vec *
+xfrog_scrubv_next_vector(struct xfrog_scrubv *scrubv)
+{
+ if (scrubv->head.svh_nr >= XFROG_SCRUBV_MAX_VECTORS)
+ return NULL;
+
+ return &scrubv->vectors[scrubv->head.svh_nr++];
+}
+
+#define foreach_xfrog_scrubv_vec(scrubv, i, vec) \
+ for ((i) = 0, (vec) = (scrubv)->vectors; \
+ (i) < (scrubv)->head.svh_nr; \
+ (i)++, (vec)++)
+
+int xfrog_scrubv_metadata(struct xfs_fd *xfd, struct xfrog_scrubv *scrubv);
+
#endif /* __LIBFROG_SCRUB_H__ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 03/10] xfs_io: support vectored scrub
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
2024-07-30 1:30 ` [PATCH 01/10] man: document vectored scrub mode Darrick J. Wong
2024-07-30 1:30 ` [PATCH 02/10] libfrog: support vectored scrub Darrick J. Wong
@ 2024-07-30 1:30 ` Darrick J. Wong
2024-07-30 1:31 ` [PATCH 04/10] xfs_scrub: split the scrub epilogue code into a separate function Darrick J. Wong
` (6 subsequent siblings)
9 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:30 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create a new scrubv command to xfs_io to support the vectored scrub
ioctl.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
io/scrub.c | 368 +++++++++++++++++++++++++++++++++++++++++++++++------
man/man8/xfs_io.8 | 51 +++++++
2 files changed, 379 insertions(+), 40 deletions(-)
diff --git a/io/scrub.c b/io/scrub.c
index a77cd872f..e03c1d0ea 100644
--- a/io/scrub.c
+++ b/io/scrub.c
@@ -12,10 +12,13 @@
#include "libfrog/paths.h"
#include "libfrog/fsgeom.h"
#include "libfrog/scrub.h"
+#include "libfrog/logging.h"
#include "io.h"
+#include "list.h"
static struct cmdinfo scrub_cmd;
static struct cmdinfo repair_cmd;
+static const struct cmdinfo scrubv_cmd;
static void
scrub_help(void)
@@ -197,31 +200,38 @@ parse_args(
return 0;
}
-static int
-scrub_f(
- int argc,
- char **argv)
+static void
+report_scrub_outcome(
+ uint32_t flags)
{
- struct xfs_scrub_metadata meta;
- int error;
-
- error = parse_args(argc, argv, &scrub_cmd, &meta);
- if (error)
- return error;
-
- error = ioctl(file->fd, XFS_IOC_SCRUB_METADATA, &meta);
- if (error)
- perror("scrub");
- if (meta.sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ if (flags & XFS_SCRUB_OFLAG_CORRUPT)
printf(_("Corruption detected.\n"));
- if (meta.sm_flags & XFS_SCRUB_OFLAG_PREEN)
+ if (flags & XFS_SCRUB_OFLAG_PREEN)
printf(_("Optimization possible.\n"));
- if (meta.sm_flags & XFS_SCRUB_OFLAG_XFAIL)
+ if (flags & XFS_SCRUB_OFLAG_XFAIL)
printf(_("Cross-referencing failed.\n"));
- if (meta.sm_flags & XFS_SCRUB_OFLAG_XCORRUPT)
+ if (flags & XFS_SCRUB_OFLAG_XCORRUPT)
printf(_("Corruption detected during cross-referencing.\n"));
- if (meta.sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE)
+ if (flags & XFS_SCRUB_OFLAG_INCOMPLETE)
printf(_("Scan was not complete.\n"));
+}
+
+static int
+scrub_f(
+ int argc,
+ char **argv)
+{
+ struct xfs_scrub_metadata meta;
+ int error;
+
+ error = parse_args(argc, argv, &scrub_cmd, &meta);
+ if (error)
+ return error;
+
+ error = ioctl(file->fd, XFS_IOC_SCRUB_METADATA, &meta);
+ if (error)
+ perror("scrub");
+ report_scrub_outcome(meta.sm_flags);
return 0;
}
@@ -239,6 +249,7 @@ scrub_init(void)
scrub_cmd.help = scrub_help;
add_command(&scrub_cmd);
+ add_command(&scrubv_cmd);
}
static void
@@ -267,34 +278,41 @@ repair_help(void)
printf("\n");
}
-static int
-repair_f(
- int argc,
- char **argv)
+static void
+report_repair_outcome(
+ uint32_t flags)
{
- struct xfs_scrub_metadata meta;
- int error;
-
- error = parse_args(argc, argv, &repair_cmd, &meta);
- if (error)
- return error;
- meta.sm_flags |= XFS_SCRUB_IFLAG_REPAIR;
-
- error = ioctl(file->fd, XFS_IOC_SCRUB_METADATA, &meta);
- if (error)
- perror("repair");
- if (meta.sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ if (flags & XFS_SCRUB_OFLAG_CORRUPT)
printf(_("Corruption remains.\n"));
- if (meta.sm_flags & XFS_SCRUB_OFLAG_PREEN)
+ if (flags & XFS_SCRUB_OFLAG_PREEN)
printf(_("Optimization possible.\n"));
- if (meta.sm_flags & XFS_SCRUB_OFLAG_XFAIL)
+ if (flags & XFS_SCRUB_OFLAG_XFAIL)
printf(_("Cross-referencing failed.\n"));
- if (meta.sm_flags & XFS_SCRUB_OFLAG_XCORRUPT)
+ if (flags & XFS_SCRUB_OFLAG_XCORRUPT)
printf(_("Corruption still detected during cross-referencing.\n"));
- if (meta.sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE)
+ if (flags & XFS_SCRUB_OFLAG_INCOMPLETE)
printf(_("Repair was not complete.\n"));
- if (meta.sm_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED)
+ if (flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED)
printf(_("Metadata did not need repair or optimization.\n"));
+}
+
+static int
+repair_f(
+ int argc,
+ char **argv)
+{
+ struct xfs_scrub_metadata meta;
+ int error;
+
+ error = parse_args(argc, argv, &repair_cmd, &meta);
+ if (error)
+ return error;
+ meta.sm_flags |= XFS_SCRUB_IFLAG_REPAIR;
+
+ error = ioctl(file->fd, XFS_IOC_SCRUB_METADATA, &meta);
+ if (error)
+ perror("repair");
+ report_repair_outcome(meta.sm_flags);
return 0;
}
@@ -315,3 +333,273 @@ repair_init(void)
add_command(&repair_cmd);
}
+
+static void
+scrubv_help(void)
+{
+ printf(_(
+"\n"
+" Scrubs pieces of XFS filesystem metadata. The first argument is the group\n"
+" of metadata to examine. If the group is 'ag', the second parameter should\n"
+" be the AG number. If the group is 'inode', the second and third parameters\n"
+" should be the inode number and generation number to act upon; if these are\n"
+" omitted, the scrub is performed on the open file. If the group is 'fs',\n"
+" 'summary', or 'probe', there are no other parameters.\n"
+"\n"
+" Flags are -d for debug, and -r to allow repairs.\n"
+" -b NN will insert a scrub barrier after every NN scrubs, and -m sets the\n"
+" desired corruption mask in all barriers. -w pauses for some microseconds\n"
+" after each scrub call.\n"
+"\n"
+" Example:\n"
+" 'scrubv ag 3' - scrub all metadata in AG 3.\n"
+" 'scrubv ag 3 -b 2 -m 0x4' - scrub all metadata in AG 3, and use barriers\n"
+" every third scrub to exit early if there are optimizations.\n"
+" 'scrubv fs' - scrub all non-AG non-file metadata.\n"
+" 'scrubv inode' - scrub all metadata for the open file.\n"
+" 'scrubv inode 128 13525' - scrub all metadata for inode 128 gen 13525.\n"
+" 'scrubv probe' - check for presence of online scrub.\n"
+" 'scrubv summary' - scrub all summary metadata.\n"));
+}
+
+/* Fill out the scrub vectors for a group of scrubber (ag, ino, fs, summary) */
+static void
+scrubv_fill_group(
+ struct xfrog_scrubv *scrubv,
+ int barrier_interval,
+ __u32 barrier_mask,
+ enum xfrog_scrub_group group)
+{
+ const struct xfrog_scrub_descr *d;
+ struct xfs_scrub_vec *v;
+ unsigned int i;
+
+ for (i = 0, d = xfrog_scrubbers; i < XFS_SCRUB_TYPE_NR; i++, d++) {
+ if (d->group != group)
+ continue;
+
+ v = xfrog_scrubv_next_vector(scrubv);
+ v->sv_type = i;
+
+ if (barrier_interval &&
+ scrubv->head.svh_nr % (barrier_interval + 1) == 0) {
+ v = xfrog_scrubv_next_vector(scrubv);
+ v->sv_flags = barrier_mask;
+ v->sv_type = XFS_SCRUB_TYPE_BARRIER;
+ }
+ }
+}
+
+static int
+scrubv_f(
+ int argc,
+ char **argv)
+{
+ struct xfrog_scrubv scrubv = { };
+ struct xfs_fd xfd = XFS_FD_INIT(file->fd);
+ struct xfs_scrub_vec *v;
+ uint32_t flags = 0;
+ __u32 barrier_mask = XFS_SCRUB_OFLAG_CORRUPT;
+ enum xfrog_scrub_group group;
+ bool debug = false;
+ int version = -1;
+ int barrier_interval = 0;
+ int rest_us = 0;
+ int c;
+ int error;
+
+ xfrog_scrubv_init(&scrubv);
+
+ while ((c = getopt(argc, argv, "b:dm:rv:w:")) != EOF) {
+ switch (c) {
+ case 'b':
+ barrier_interval = atoi(optarg);
+ if (barrier_interval < 0) {
+ fprintf(stderr,
+ _("Negative barrier interval makes no sense.\n"));
+ exitcode = 1;
+ return command_usage(&scrubv_cmd);
+ }
+ break;
+ case 'd':
+ debug = true;
+ break;
+ case 'm':
+ barrier_mask = strtoul(optarg, NULL, 0);
+ break;
+ case 'r':
+ flags |= XFS_SCRUB_IFLAG_REPAIR;
+ break;
+ case 'v':
+ if (!strcmp("single", optarg)) {
+ version = 0;
+ } else if (!strcmp("vector", optarg)) {
+ version = 1;
+ } else {
+ fprintf(stderr,
+ _("API version must be 'single' or 'vector'.\n"));
+ exitcode = 1;
+ return command_usage(&scrubv_cmd);
+ }
+ break;
+ case 'w':
+ rest_us = atoi(optarg);
+ if (rest_us < 0) {
+ fprintf(stderr,
+ _("Rest time must be positive.\n"));
+ exitcode = 1;
+ return command_usage(&scrubv_cmd);
+ }
+ break;
+ default:
+ exitcode = 1;
+ return command_usage(&scrubv_cmd);
+ }
+ }
+ if (optind > argc - 1) {
+ fprintf(stderr,
+ _("Must have at least one positional argument.\n"));
+ exitcode = 1;
+ return command_usage(&scrubv_cmd);
+ }
+
+ if ((flags & XFS_SCRUB_IFLAG_REPAIR) && !expert) {
+ printf(_("Repair flag requires expert mode.\n"));
+ return 1;
+ }
+
+ scrubv.head.svh_rest_us = rest_us;
+ foreach_xfrog_scrubv_vec(&scrubv, c, v)
+ v->sv_flags = flags;
+
+ /* Extract group and domain information from cmdline. */
+ if (!strcmp(argv[optind], "probe"))
+ group = XFROG_SCRUB_GROUP_NONE;
+ else if (!strcmp(argv[optind], "agheader"))
+ group = XFROG_SCRUB_GROUP_AGHEADER;
+ else if (!strcmp(argv[optind], "ag"))
+ group = XFROG_SCRUB_GROUP_PERAG;
+ else if (!strcmp(argv[optind], "fs"))
+ group = XFROG_SCRUB_GROUP_FS;
+ else if (!strcmp(argv[optind], "inode"))
+ group = XFROG_SCRUB_GROUP_INODE;
+ else if (!strcmp(argv[optind], "iscan"))
+ group = XFROG_SCRUB_GROUP_ISCAN;
+ else if (!strcmp(argv[optind], "summary"))
+ group = XFROG_SCRUB_GROUP_SUMMARY;
+ else {
+ printf(_("Unknown group '%s'.\n"), argv[optind]);
+ exitcode = 1;
+ return command_usage(&scrubv_cmd);
+ }
+ optind++;
+
+ switch (group) {
+ case XFROG_SCRUB_GROUP_INODE:
+ if (!parse_inode(argc, argv, optind, &scrubv.head.svh_ino,
+ &scrubv.head.svh_gen)) {
+ exitcode = 1;
+ return command_usage(&scrubv_cmd);
+ }
+ break;
+ case XFROG_SCRUB_GROUP_AGHEADER:
+ case XFROG_SCRUB_GROUP_PERAG:
+ if (!parse_agno(argc, argv, optind, &scrubv.head.svh_agno)) {
+ exitcode = 1;
+ return command_usage(&scrubv_cmd);
+ }
+ break;
+ case XFROG_SCRUB_GROUP_FS:
+ case XFROG_SCRUB_GROUP_SUMMARY:
+ case XFROG_SCRUB_GROUP_ISCAN:
+ case XFROG_SCRUB_GROUP_NONE:
+ if (!parse_none(argc, optind)) {
+ exitcode = 1;
+ return command_usage(&scrubv_cmd);
+ }
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ scrubv_fill_group(&scrubv, barrier_interval, barrier_mask, group);
+ assert(scrubv.head.svh_nr <= XFROG_SCRUBV_MAX_VECTORS);
+
+ error = -xfd_prepare_geometry(&xfd);
+ if (error) {
+ xfrog_perror(error, "xfd_prepare_geometry");
+ exitcode = 1;
+ return 0;
+ }
+
+ switch (version) {
+ case 0:
+ xfd.flags |= XFROG_FLAG_SCRUB_FORCE_SINGLE;
+ break;
+ case 1:
+ xfd.flags |= XFROG_FLAG_SCRUB_FORCE_VECTOR;
+ break;
+ default:
+ break;
+ }
+
+ error = -xfrog_scrubv_metadata(&xfd, &scrubv);
+ if (error) {
+ xfrog_perror(error, "xfrog_scrub_many");
+ exitcode = 1;
+ return 0;
+ }
+
+ /* Dump what happened. */
+ if (debug) {
+ foreach_xfrog_scrubv_vec(&scrubv, c, v) {
+ const char *type;
+
+ if (v->sv_type == XFS_SCRUB_TYPE_BARRIER)
+ type = _("barrier");
+ else
+ type = _(xfrog_scrubbers[v->sv_type].descr);
+ printf(_("[%02u] %-25s: flags 0x%x ret %d\n"), c, type,
+ v->sv_flags, v->sv_ret);
+ }
+ }
+
+ /* Figure out what happened. */
+ foreach_xfrog_scrubv_vec(&scrubv, c, v) {
+ /* Report barrier failures. */
+ if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) {
+ if (v->sv_ret) {
+ printf(_("barrier: FAILED\n"));
+ break;
+ }
+ continue;
+ }
+
+ printf("%s: ", _(xfrog_scrubbers[v->sv_type].descr));
+ switch (v->sv_ret) {
+ case 0:
+ break;
+ default:
+ printf("%s\n", strerror(-v->sv_ret));
+ continue;
+ }
+ if (!(v->sv_flags & XFS_SCRUB_FLAGS_OUT))
+ printf(_("OK.\n"));
+ else if (v->sv_flags & XFS_SCRUB_IFLAG_REPAIR)
+ report_repair_outcome(v->sv_flags);
+ else
+ report_scrub_outcome(v->sv_flags);
+ }
+
+ return 0;
+}
+
+static const struct cmdinfo scrubv_cmd = {
+ .name = "scrubv",
+ .cfunc = scrubv_f,
+ .argmin = 1,
+ .argmax = -1,
+ .flags = CMD_NOMAP_OK,
+ .oneline = N_("vectored metadata scrub"),
+ .help = scrubv_help,
+};
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 02036e3d0..657bdaec4 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -1392,6 +1392,57 @@ inode number and generation number are specified.
.RE
.PD
.TP
+.BI "scrubv [ \-b NN ] [ \-d ] [ \-m oflags ] [ \-r ] [ \-v NN ] [ \-w ms ] " group " [ " agnumber " | " "ino" " " "gen" " ]"
+Scrub a bunch of internal XFS filesystem metadata.
+The
+.BI group
+parameter specifies which group of metadata to scrub.
+Valid groups are
+.IR ag ", " agheader ", " inode ", " iscan ", " fs ", " probe ", " rtgroup ", or " summary .
+
+For
+.BR ag " and " agheader
+metadata, one AG number must be specified.
+For
+.B inode
+metadata, the scrub is applied to the open file unless the
+inode number and generation number are specified.
+For
+.B rtgroup
+metadata, one rt group number must be specified.
+
+.RS 1.0i
+.PD 0
+.TP
+.BI "\-b " NN
+Inject scrub barriers into the vector stream at the given interval.
+Barriers abort vector processing if any previous scrub function found
+corruption.
+.TP
+.BI \-d
+Enables debug mode.
+.TP
+.BI \-m
+Fail scrub barriers if any of these scrub output flags are seen.
+.TP
+.BI \-r
+Repair metadata if corruptions are found.
+This option requires expert mode.
+.TP
+.BI "\-v " NN
+Force a particular API version.
+.B single
+selects XFS_SCRUB_METADATA (one-by-one).
+.B vector
+selects XFS_SCRUBV_METADATA (vectored).
+If no option is specified, vector mode will be used, with a fallback to single
+mode if the kernel doesn't recognize the vector mode ioctl.
+.TP
+.BI "\-w " us
+Wait the given number of microseconds between each scrub function.
+.RE
+.PD
+.TP
.BI "repair " type " [ " agnumber " | " "ino" " " "gen" " ]"
Repair internal XFS filesystem metadata. The
.BI type
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 04/10] xfs_scrub: split the scrub epilogue code into a separate function
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
` (2 preceding siblings ...)
2024-07-30 1:30 ` [PATCH 03/10] xfs_io: " Darrick J. Wong
@ 2024-07-30 1:31 ` Darrick J. Wong
2024-07-30 1:31 ` [PATCH 05/10] xfs_scrub: split the repair " Darrick J. Wong
` (5 subsequent siblings)
9 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:31 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Move all the code that updates the internal state in response to a scrub
ioctl() call completion into a separate function. This will help with
vectorizing scrub calls later on.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/scrub.c | 52 ++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 38 insertions(+), 14 deletions(-)
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 1b0609e74..c4b4367e4 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -22,6 +22,10 @@
#include "descr.h"
#include "scrub_private.h"
+static int scrub_epilogue(struct scrub_ctx *ctx, struct descr *dsc,
+ struct scrub_item *sri, struct xfs_scrub_metadata *meta,
+ int error);
+
/* Online scrub and repair wrappers. */
/* Format a scrub description. */
@@ -117,12 +121,32 @@ xfs_check_metadata(
dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta.sm_flags);
error = -xfrog_scrub_metadata(xfdp, &meta);
+ return scrub_epilogue(ctx, &dsc, sri, &meta, error);
+}
+
+/*
+ * Update all internal state after a scrub ioctl call.
+ * Returns 0 for success, or ECANCELED to abort the program.
+ */
+static int
+scrub_epilogue(
+ struct scrub_ctx *ctx,
+ struct descr *dsc,
+ struct scrub_item *sri,
+ struct xfs_scrub_metadata *meta,
+ int error)
+{
+ unsigned int scrub_type = meta->sm_type;
+ enum xfrog_scrub_group group;
+
+ group = xfrog_scrubbers[scrub_type].group;
+
switch (error) {
case 0:
/* No operational errors encountered. */
if (!sri->sri_revalidate &&
debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
- meta.sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+ meta->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
break;
case ENOENT:
/* Metadata not present, just skip it. */
@@ -130,13 +154,13 @@ xfs_check_metadata(
return 0;
case ESHUTDOWN:
/* FS already crashed, give up. */
- str_error(ctx, descr_render(&dsc),
+ str_error(ctx, descr_render(dsc),
_("Filesystem is shut down, aborting."));
return ECANCELED;
case EIO:
case ENOMEM:
/* Abort on I/O errors or insufficient memory. */
- str_liberror(ctx, error, descr_render(&dsc));
+ str_liberror(ctx, error, descr_render(dsc));
return ECANCELED;
case EDEADLOCK:
case EBUSY:
@@ -152,7 +176,7 @@ _("Filesystem is shut down, aborting."));
return 0;
default:
/* Operational error. Log it and move on. */
- str_liberror(ctx, error, descr_render(&dsc));
+ str_liberror(ctx, error, descr_render(dsc));
scrub_item_clean_state(sri, scrub_type);
return 0;
}
@@ -163,27 +187,27 @@ _("Filesystem is shut down, aborting."));
* we'll try the scan again, just in case the fs was busy.
* Only retry so many times.
*/
- if (want_retry(&meta) && scrub_item_schedule_retry(sri, scrub_type))
+ if (want_retry(meta) && scrub_item_schedule_retry(sri, scrub_type))
return 0;
/* Complain about incomplete or suspicious metadata. */
- scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
+ scrub_warn_incomplete_scrub(ctx, dsc, meta);
/*
* If we need repairs or there were discrepancies, schedule a
* repair if desired, otherwise complain.
*/
- if (is_corrupt(&meta) || xref_disagrees(&meta)) {
+ if (is_corrupt(meta) || xref_disagrees(meta)) {
if (ctx->mode != SCRUB_MODE_REPAIR) {
/* Dry-run mode, so log an error and forget it. */
- str_corrupt(ctx, descr_render(&dsc),
+ str_corrupt(ctx, descr_render(dsc),
_("Repairs are required."));
scrub_item_clean_state(sri, scrub_type);
return 0;
}
/* Schedule repairs. */
- scrub_item_save_state(sri, scrub_type, meta.sm_flags);
+ scrub_item_save_state(sri, scrub_type, meta->sm_flags);
return 0;
}
@@ -191,12 +215,12 @@ _("Repairs are required."));
* If we could optimize, schedule a repair if desired,
* otherwise complain.
*/
- if (is_unoptimized(&meta)) {
+ if (is_unoptimized(meta)) {
if (ctx->mode == SCRUB_MODE_DRY_RUN) {
/* Dry-run mode, so log an error and forget it. */
if (group != XFROG_SCRUB_GROUP_INODE) {
/* AG or FS metadata, always warn. */
- str_info(ctx, descr_render(&dsc),
+ str_info(ctx, descr_render(dsc),
_("Optimization is possible."));
} else if (!ctx->preen_triggers[scrub_type]) {
/* File metadata, only warn once per type. */
@@ -210,7 +234,7 @@ _("Optimization is possible."));
}
/* Schedule optimizations. */
- scrub_item_save_state(sri, scrub_type, meta.sm_flags);
+ scrub_item_save_state(sri, scrub_type, meta->sm_flags);
return 0;
}
@@ -221,8 +245,8 @@ _("Optimization is possible."));
* re-examine the object as repairs progress to see if the kernel will
* deem it completely consistent at some point.
*/
- if (xref_failed(&meta) && ctx->mode == SCRUB_MODE_REPAIR) {
- scrub_item_save_state(sri, scrub_type, meta.sm_flags);
+ if (xref_failed(meta) && ctx->mode == SCRUB_MODE_REPAIR) {
+ scrub_item_save_state(sri, scrub_type, meta->sm_flags);
return 0;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 05/10] xfs_scrub: split the repair epilogue code into a separate function
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
` (3 preceding siblings ...)
2024-07-30 1:31 ` [PATCH 04/10] xfs_scrub: split the scrub epilogue code into a separate function Darrick J. Wong
@ 2024-07-30 1:31 ` Darrick J. Wong
2024-07-30 1:31 ` [PATCH 06/10] xfs_scrub: convert scrub and repair epilogues to use xfs_scrub_vec Darrick J. Wong
` (4 subsequent siblings)
9 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:31 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Move all the code that updates the internal state in response to a
repair ioctl() call completion into a separate function. This will help
with vectorizing repair calls later on.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 69 +++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 45 insertions(+), 24 deletions(-)
diff --git a/scrub/repair.c b/scrub/repair.c
index 4fed86134..0b99e3351 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -20,6 +20,11 @@
#include "descr.h"
#include "scrub_private.h"
+static int repair_epilogue(struct scrub_ctx *ctx, struct descr *dsc,
+ struct scrub_item *sri, unsigned int repair_flags,
+ struct xfs_scrub_metadata *oldm,
+ struct xfs_scrub_metadata *meta, int error);
+
/* General repair routines. */
/*
@@ -133,6 +138,22 @@ xfs_repair_metadata(
_("Attempting optimization."));
error = -xfrog_scrub_metadata(xfdp, &meta);
+ return repair_epilogue(ctx, &dsc, sri, repair_flags, &oldm, &meta,
+ error);
+}
+
+static int
+repair_epilogue(
+ struct scrub_ctx *ctx,
+ struct descr *dsc,
+ struct scrub_item *sri,
+ unsigned int repair_flags,
+ struct xfs_scrub_metadata *oldm,
+ struct xfs_scrub_metadata *meta,
+ int error)
+{
+ unsigned int scrub_type = meta->sm_type;
+
switch (error) {
case 0:
/* No operational errors encountered. */
@@ -141,12 +162,12 @@ xfs_repair_metadata(
case EBUSY:
/* Filesystem is busy, try again later. */
if (debug || verbose)
- str_info(ctx, descr_render(&dsc),
+ str_info(ctx, descr_render(dsc),
_("Filesystem is busy, deferring repair."));
return 0;
case ESHUTDOWN:
/* Filesystem is already shut down, abort. */
- str_error(ctx, descr_render(&dsc),
+ str_error(ctx, descr_render(dsc),
_("Filesystem is shut down, aborting."));
return ECANCELED;
case ENOTTY:
@@ -157,7 +178,7 @@ _("Filesystem is shut down, aborting."));
* how to perform the repair, don't requeue the request. Mark
* it done and move on.
*/
- if (is_unoptimized(&oldm) ||
+ if (is_unoptimized(oldm) ||
debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) {
scrub_item_clean_state(sri, scrub_type);
return 0;
@@ -175,14 +196,14 @@ _("Filesystem is shut down, aborting."));
fallthrough;
case EINVAL:
/* Kernel doesn't know how to repair this? */
- str_corrupt(ctx, descr_render(&dsc),
+ str_corrupt(ctx, descr_render(dsc),
_("Don't know how to fix; offline repair required."));
scrub_item_clean_state(sri, scrub_type);
return 0;
case EROFS:
/* Read-only filesystem, can't fix. */
- if (verbose || debug || needs_repair(&oldm))
- str_error(ctx, descr_render(&dsc),
+ if (verbose || debug || needs_repair(oldm))
+ str_error(ctx, descr_render(dsc),
_("Read-only filesystem; cannot make changes."));
return ECANCELED;
case ENOENT:
@@ -192,7 +213,7 @@ _("Read-only filesystem; cannot make changes."));
case ENOMEM:
case ENOSPC:
/* Don't care if preen fails due to low resources. */
- if (is_unoptimized(&oldm) && !needs_repair(&oldm)) {
+ if (is_unoptimized(oldm) && !needs_repair(oldm)) {
scrub_item_clean_state(sri, scrub_type);
return 0;
}
@@ -207,7 +228,7 @@ _("Read-only filesystem; cannot make changes."));
*/
if (!(repair_flags & XRM_FINAL_WARNING))
return 0;
- str_liberror(ctx, error, descr_render(&dsc));
+ str_liberror(ctx, error, descr_render(dsc));
scrub_item_clean_state(sri, scrub_type);
return 0;
}
@@ -218,12 +239,12 @@ _("Read-only filesystem; cannot make changes."));
* the repair again, just in case the fs was busy. Only retry so many
* times.
*/
- if (want_retry(&meta) && scrub_item_schedule_retry(sri, scrub_type))
+ if (want_retry(meta) && scrub_item_schedule_retry(sri, scrub_type))
return 0;
if (repair_flags & XRM_FINAL_WARNING)
- scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
- if (needs_repair(&meta) || is_incomplete(&meta)) {
+ scrub_warn_incomplete_scrub(ctx, dsc, meta);
+ if (needs_repair(meta) || is_incomplete(meta)) {
/*
* Still broken; if we've been told not to complain then we
* just requeue this and try again later. Otherwise we
@@ -231,9 +252,9 @@ _("Read-only filesystem; cannot make changes."));
*/
if (!(repair_flags & XRM_FINAL_WARNING))
return 0;
- str_corrupt(ctx, descr_render(&dsc),
+ str_corrupt(ctx, descr_render(dsc),
_("Repair unsuccessful; offline repair required."));
- } else if (xref_failed(&meta)) {
+ } else if (xref_failed(meta)) {
/*
* This metadata object itself looks ok, but we still noticed
* inconsistencies when comparing it with the other filesystem
@@ -242,31 +263,31 @@ _("Repair unsuccessful; offline repair required."));
* reverify the cross-referencing as repairs progress.
*/
if (repair_flags & XRM_FINAL_WARNING) {
- str_info(ctx, descr_render(&dsc),
+ str_info(ctx, descr_render(dsc),
_("Seems correct but cross-referencing failed; offline repair recommended."));
} else {
if (verbose)
- str_info(ctx, descr_render(&dsc),
+ str_info(ctx, descr_render(dsc),
_("Seems correct but cross-referencing failed; will keep checking."));
return 0;
}
- } else if (meta.sm_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) {
+ } else if (meta->sm_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) {
if (verbose)
- str_info(ctx, descr_render(&dsc),
+ str_info(ctx, descr_render(dsc),
_("No modification needed."));
} else {
/* Clean operation, no corruption detected. */
- if (is_corrupt(&oldm))
- record_repair(ctx, descr_render(&dsc),
+ if (is_corrupt(oldm))
+ record_repair(ctx, descr_render(dsc),
_("Repairs successful."));
- else if (xref_disagrees(&oldm))
- record_repair(ctx, descr_render(&dsc),
+ else if (xref_disagrees(oldm))
+ record_repair(ctx, descr_render(dsc),
_("Repairs successful after discrepancy in cross-referencing."));
- else if (xref_failed(&oldm))
- record_repair(ctx, descr_render(&dsc),
+ else if (xref_failed(oldm))
+ record_repair(ctx, descr_render(dsc),
_("Repairs successful after cross-referencing failure."));
else
- record_preen(ctx, descr_render(&dsc),
+ record_preen(ctx, descr_render(dsc),
_("Optimization successful."));
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 06/10] xfs_scrub: convert scrub and repair epilogues to use xfs_scrub_vec
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
` (4 preceding siblings ...)
2024-07-30 1:31 ` [PATCH 05/10] xfs_scrub: split the repair " Darrick J. Wong
@ 2024-07-30 1:31 ` Darrick J. Wong
2024-07-30 1:31 ` [PATCH 07/10] xfs_scrub: vectorize scrub calls Darrick J. Wong
` (3 subsequent siblings)
9 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:31 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Convert the scrub and repair epilogue code to pass around xfs_scrub_vecs
as we prepare for vectorized operation.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 35 ++++++++++++++++++-----------------
scrub/scrub.c | 27 ++++++++++++++-------------
scrub/scrub_private.h | 34 +++++++++++++++++-----------------
3 files changed, 49 insertions(+), 47 deletions(-)
diff --git a/scrub/repair.c b/scrub/repair.c
index 0b99e3351..7a710a159 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -22,8 +22,8 @@
static int repair_epilogue(struct scrub_ctx *ctx, struct descr *dsc,
struct scrub_item *sri, unsigned int repair_flags,
- struct xfs_scrub_metadata *oldm,
- struct xfs_scrub_metadata *meta, int error);
+ const struct xfs_scrub_vec *oldm,
+ const struct xfs_scrub_vec *meta);
/* General repair routines. */
@@ -93,10 +93,9 @@ xfs_repair_metadata(
unsigned int repair_flags)
{
struct xfs_scrub_metadata meta = { 0 };
- struct xfs_scrub_metadata oldm;
+ struct xfs_scrub_vec oldm, vec;
DEFINE_DESCR(dsc, ctx, format_scrub_descr);
bool repair_only;
- int error;
/*
* If the caller boosted the priority of this scrub type on behalf of a
@@ -124,22 +123,24 @@ xfs_repair_metadata(
break;
}
- if (!is_corrupt(&meta) && repair_only)
+ vec.sv_type = scrub_type;
+ vec.sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
+ memcpy(&oldm, &vec, sizeof(struct xfs_scrub_vec));
+ if (!is_corrupt(&vec) && repair_only)
return 0;
- memcpy(&oldm, &meta, sizeof(oldm));
- oldm.sm_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
- descr_set(&dsc, &oldm);
+ descr_set(&dsc, &meta);
- if (needs_repair(&oldm))
+ if (needs_repair(&vec))
str_info(ctx, descr_render(&dsc), _("Attempting repair."));
else if (debug || verbose)
str_info(ctx, descr_render(&dsc),
_("Attempting optimization."));
- error = -xfrog_scrub_metadata(xfdp, &meta);
- return repair_epilogue(ctx, &dsc, sri, repair_flags, &oldm, &meta,
- error);
+ vec.sv_ret = xfrog_scrub_metadata(xfdp, &meta);
+ vec.sv_flags = meta.sm_flags;
+
+ return repair_epilogue(ctx, &dsc, sri, repair_flags, &oldm, &vec);
}
static int
@@ -148,11 +149,11 @@ repair_epilogue(
struct descr *dsc,
struct scrub_item *sri,
unsigned int repair_flags,
- struct xfs_scrub_metadata *oldm,
- struct xfs_scrub_metadata *meta,
- int error)
+ const struct xfs_scrub_vec *oldm,
+ const struct xfs_scrub_vec *meta)
{
- unsigned int scrub_type = meta->sm_type;
+ unsigned int scrub_type = meta->sv_type;
+ int error = -meta->sv_ret;
switch (error) {
case 0:
@@ -271,7 +272,7 @@ _("Repair unsuccessful; offline repair required."));
_("Seems correct but cross-referencing failed; will keep checking."));
return 0;
}
- } else if (meta->sm_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) {
+ } else if (meta->sv_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) {
if (verbose)
str_info(ctx, descr_render(dsc),
_("No modification needed."));
diff --git a/scrub/scrub.c b/scrub/scrub.c
index c4b4367e4..2fb229355 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -23,8 +23,7 @@
#include "scrub_private.h"
static int scrub_epilogue(struct scrub_ctx *ctx, struct descr *dsc,
- struct scrub_item *sri, struct xfs_scrub_metadata *meta,
- int error);
+ struct scrub_item *sri, struct xfs_scrub_vec *vec);
/* Online scrub and repair wrappers. */
@@ -62,7 +61,7 @@ void
scrub_warn_incomplete_scrub(
struct scrub_ctx *ctx,
struct descr *dsc,
- struct xfs_scrub_metadata *meta)
+ const struct xfs_scrub_vec *meta)
{
if (is_incomplete(meta))
str_info(ctx, descr_render(dsc), _("Check incomplete."));
@@ -91,8 +90,8 @@ xfs_check_metadata(
{
DEFINE_DESCR(dsc, ctx, format_scrub_descr);
struct xfs_scrub_metadata meta = { };
+ struct xfs_scrub_vec vec;
enum xfrog_scrub_group group;
- int error;
background_sleep();
@@ -120,8 +119,10 @@ xfs_check_metadata(
dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta.sm_flags);
- error = -xfrog_scrub_metadata(xfdp, &meta);
- return scrub_epilogue(ctx, &dsc, sri, &meta, error);
+ vec.sv_ret = xfrog_scrub_metadata(xfdp, &meta);
+ vec.sv_type = scrub_type;
+ vec.sv_flags = meta.sm_flags;
+ return scrub_epilogue(ctx, &dsc, sri, &vec);
}
/*
@@ -133,11 +134,11 @@ scrub_epilogue(
struct scrub_ctx *ctx,
struct descr *dsc,
struct scrub_item *sri,
- struct xfs_scrub_metadata *meta,
- int error)
+ struct xfs_scrub_vec *meta)
{
- unsigned int scrub_type = meta->sm_type;
+ unsigned int scrub_type = meta->sv_type;
enum xfrog_scrub_group group;
+ int error = -meta->sv_ret;
group = xfrog_scrubbers[scrub_type].group;
@@ -146,7 +147,7 @@ scrub_epilogue(
/* No operational errors encountered. */
if (!sri->sri_revalidate &&
debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
- meta->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+ meta->sv_flags |= XFS_SCRUB_OFLAG_CORRUPT;
break;
case ENOENT:
/* Metadata not present, just skip it. */
@@ -207,7 +208,7 @@ _("Repairs are required."));
}
/* Schedule repairs. */
- scrub_item_save_state(sri, scrub_type, meta->sm_flags);
+ scrub_item_save_state(sri, scrub_type, meta->sv_flags);
return 0;
}
@@ -234,7 +235,7 @@ _("Optimization is possible."));
}
/* Schedule optimizations. */
- scrub_item_save_state(sri, scrub_type, meta->sm_flags);
+ scrub_item_save_state(sri, scrub_type, meta->sv_flags);
return 0;
}
@@ -246,7 +247,7 @@ _("Optimization is possible."));
* deem it completely consistent at some point.
*/
if (xref_failed(meta) && ctx->mode == SCRUB_MODE_REPAIR) {
- scrub_item_save_state(sri, scrub_type, meta->sm_flags);
+ scrub_item_save_state(sri, scrub_type, meta->sv_flags);
return 0;
}
diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h
index bcfabda16..98a9238f2 100644
--- a/scrub/scrub_private.h
+++ b/scrub/scrub_private.h
@@ -13,40 +13,40 @@ int format_scrub_descr(struct scrub_ctx *ctx, char *buf, size_t buflen,
/* Predicates for scrub flag state. */
-static inline bool is_corrupt(struct xfs_scrub_metadata *sm)
+static inline bool is_corrupt(const struct xfs_scrub_vec *sv)
{
- return sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT;
+ return sv->sv_flags & XFS_SCRUB_OFLAG_CORRUPT;
}
-static inline bool is_unoptimized(struct xfs_scrub_metadata *sm)
+static inline bool is_unoptimized(const struct xfs_scrub_vec *sv)
{
- return sm->sm_flags & XFS_SCRUB_OFLAG_PREEN;
+ return sv->sv_flags & XFS_SCRUB_OFLAG_PREEN;
}
-static inline bool xref_failed(struct xfs_scrub_metadata *sm)
+static inline bool xref_failed(const struct xfs_scrub_vec *sv)
{
- return sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL;
+ return sv->sv_flags & XFS_SCRUB_OFLAG_XFAIL;
}
-static inline bool xref_disagrees(struct xfs_scrub_metadata *sm)
+static inline bool xref_disagrees(const struct xfs_scrub_vec *sv)
{
- return sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT;
+ return sv->sv_flags & XFS_SCRUB_OFLAG_XCORRUPT;
}
-static inline bool is_incomplete(struct xfs_scrub_metadata *sm)
+static inline bool is_incomplete(const struct xfs_scrub_vec *sv)
{
- return sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE;
+ return sv->sv_flags & XFS_SCRUB_OFLAG_INCOMPLETE;
}
-static inline bool is_suspicious(struct xfs_scrub_metadata *sm)
+static inline bool is_suspicious(const struct xfs_scrub_vec *sv)
{
- return sm->sm_flags & XFS_SCRUB_OFLAG_WARNING;
+ return sv->sv_flags & XFS_SCRUB_OFLAG_WARNING;
}
/* Should we fix it? */
-static inline bool needs_repair(struct xfs_scrub_metadata *sm)
+static inline bool needs_repair(const struct xfs_scrub_vec *sv)
{
- return is_corrupt(sm) || xref_disagrees(sm);
+ return is_corrupt(sv) || xref_disagrees(sv);
}
/*
@@ -54,13 +54,13 @@ static inline bool needs_repair(struct xfs_scrub_metadata *sm)
* scan/repair; or if there were cross-referencing problems but the object was
* not obviously corrupt.
*/
-static inline bool want_retry(struct xfs_scrub_metadata *sm)
+static inline bool want_retry(const struct xfs_scrub_vec *sv)
{
- return is_incomplete(sm) || (xref_disagrees(sm) && !is_corrupt(sm));
+ return is_incomplete(sv) || (xref_disagrees(sv) && !is_corrupt(sv));
}
void scrub_warn_incomplete_scrub(struct scrub_ctx *ctx, struct descr *dsc,
- struct xfs_scrub_metadata *meta);
+ const struct xfs_scrub_vec *meta);
/* Scrub item functions */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 07/10] xfs_scrub: vectorize scrub calls
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
` (5 preceding siblings ...)
2024-07-30 1:31 ` [PATCH 06/10] xfs_scrub: convert scrub and repair epilogues to use xfs_scrub_vec Darrick J. Wong
@ 2024-07-30 1:31 ` Darrick J. Wong
2024-07-30 1:32 ` [PATCH 08/10] xfs_scrub: vectorize repair calls Darrick J. Wong
` (2 subsequent siblings)
9 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:31 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Use the new vectorized kernel scrub calls to reduce the overhead of
checking metadata.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase1.c | 2
scrub/scrub.c | 277 ++++++++++++++++++++++++++++++++++++-------------
scrub/scrub.h | 2
scrub/scrub_private.h | 16 +++
scrub/xfs_scrub.c | 1
5 files changed, 225 insertions(+), 73 deletions(-)
diff --git a/scrub/phase1.c b/scrub/phase1.c
index 095c04591..091b59e57 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -216,6 +216,8 @@ _("Kernel metadata scrubbing facility is not available."));
return ECANCELED;
}
+ check_scrubv(ctx);
+
/*
* Normally, callers are required to pass -n if the provided path is a
* readonly filesystem or the kernel wasn't built with online repair
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 2fb229355..0c77f9472 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -22,11 +22,48 @@
#include "descr.h"
#include "scrub_private.h"
-static int scrub_epilogue(struct scrub_ctx *ctx, struct descr *dsc,
- struct scrub_item *sri, struct xfs_scrub_vec *vec);
-
/* Online scrub and repair wrappers. */
+/* Describe the current state of a vectored scrub. */
+int
+format_scrubv_descr(
+ struct scrub_ctx *ctx,
+ char *buf,
+ size_t buflen,
+ void *where)
+{
+ struct scrubv_descr *vdesc = where;
+ struct xfrog_scrubv *scrubv = vdesc->scrubv;
+ struct xfs_scrub_vec_head *vhead = &scrubv->head;
+ const struct xfrog_scrub_descr *sc;
+ unsigned int scrub_type;
+
+ if (vdesc->idx >= 0)
+ scrub_type = scrubv->vectors[vdesc->idx].sv_type;
+ else if (scrubv->head.svh_nr > 0)
+ scrub_type = scrubv->vectors[scrubv->head.svh_nr - 1].sv_type;
+ else
+ scrub_type = XFS_SCRUB_TYPE_PROBE;
+ sc = &xfrog_scrubbers[scrub_type];
+
+ switch (sc->group) {
+ case XFROG_SCRUB_GROUP_AGHEADER:
+ case XFROG_SCRUB_GROUP_PERAG:
+ return snprintf(buf, buflen, _("AG %u %s"), vhead->svh_agno,
+ _(sc->descr));
+ case XFROG_SCRUB_GROUP_INODE:
+ return scrub_render_ino_descr(ctx, buf, buflen,
+ vhead->svh_ino, vhead->svh_gen, "%s",
+ _(sc->descr));
+ case XFROG_SCRUB_GROUP_FS:
+ case XFROG_SCRUB_GROUP_SUMMARY:
+ case XFROG_SCRUB_GROUP_ISCAN:
+ case XFROG_SCRUB_GROUP_NONE:
+ return snprintf(buf, buflen, _("%s"), _(sc->descr));
+ }
+ return -1;
+}
+
/* Format a scrub description. */
int
format_scrub_descr(
@@ -80,51 +117,6 @@ scrub_warn_incomplete_scrub(
_("Cross-referencing failed."));
}
-/* Do a read-only check of some metadata. */
-static int
-xfs_check_metadata(
- struct scrub_ctx *ctx,
- struct xfs_fd *xfdp,
- unsigned int scrub_type,
- struct scrub_item *sri)
-{
- DEFINE_DESCR(dsc, ctx, format_scrub_descr);
- struct xfs_scrub_metadata meta = { };
- struct xfs_scrub_vec vec;
- enum xfrog_scrub_group group;
-
- background_sleep();
-
- group = xfrog_scrubbers[scrub_type].group;
- meta.sm_type = scrub_type;
- switch (group) {
- case XFROG_SCRUB_GROUP_AGHEADER:
- case XFROG_SCRUB_GROUP_PERAG:
- meta.sm_agno = sri->sri_agno;
- break;
- case XFROG_SCRUB_GROUP_FS:
- case XFROG_SCRUB_GROUP_SUMMARY:
- case XFROG_SCRUB_GROUP_ISCAN:
- case XFROG_SCRUB_GROUP_NONE:
- break;
- case XFROG_SCRUB_GROUP_INODE:
- meta.sm_ino = sri->sri_ino;
- meta.sm_gen = sri->sri_gen;
- break;
- }
-
- assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
- assert(scrub_type < XFS_SCRUB_TYPE_NR);
- descr_set(&dsc, &meta);
-
- dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta.sm_flags);
-
- vec.sv_ret = xfrog_scrub_metadata(xfdp, &meta);
- vec.sv_type = scrub_type;
- vec.sv_flags = meta.sm_flags;
- return scrub_epilogue(ctx, &dsc, sri, &vec);
-}
-
/*
* Update all internal state after a scrub ioctl call.
* Returns 0 for success, or ECANCELED to abort the program.
@@ -256,6 +248,87 @@ _("Optimization is possible."));
return 0;
}
+/* Fill out the scrub vector header from a scrub item. */
+void
+xfrog_scrubv_from_item(
+ struct xfrog_scrubv *scrubv,
+ const struct scrub_item *sri)
+{
+ xfrog_scrubv_init(scrubv);
+
+ if (bg_mode > 1)
+ scrubv->head.svh_rest_us = bg_mode - 1;
+ if (sri->sri_agno != -1)
+ scrubv->head.svh_agno = sri->sri_agno;
+ if (sri->sri_ino != -1ULL) {
+ scrubv->head.svh_ino = sri->sri_ino;
+ scrubv->head.svh_gen = sri->sri_gen;
+ }
+}
+
+/* Add a scrubber to the scrub vector. */
+void
+xfrog_scrubv_add_item(
+ struct xfrog_scrubv *scrubv,
+ const struct scrub_item *sri,
+ unsigned int scrub_type)
+{
+ struct xfs_scrub_vec *v;
+
+ v = xfrog_scrubv_next_vector(scrubv);
+ v->sv_type = scrub_type;
+}
+
+/* Do a read-only check of some metadata. */
+static int
+scrub_call_kernel(
+ struct scrub_ctx *ctx,
+ struct xfs_fd *xfdp,
+ struct scrub_item *sri)
+{
+ DEFINE_DESCR(dsc, ctx, format_scrubv_descr);
+ struct xfrog_scrubv scrubv = { };
+ struct scrubv_descr vdesc = SCRUBV_DESCR(&scrubv);
+ struct xfs_scrub_vec *v;
+ unsigned int scrub_type;
+ int error;
+
+ assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
+
+ xfrog_scrubv_from_item(&scrubv, sri);
+ descr_set(&dsc, &vdesc);
+
+ foreach_scrub_type(scrub_type) {
+ if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK))
+ continue;
+ xfrog_scrubv_add_item(&scrubv, sri, scrub_type);
+
+ dbg_printf("check %s flags %xh tries %u\n", descr_render(&dsc),
+ sri->sri_state[scrub_type],
+ sri->sri_tries[scrub_type]);
+ }
+
+ error = -xfrog_scrubv_metadata(xfdp, &scrubv);
+ if (error)
+ return error;
+
+ foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, v) {
+ error = scrub_epilogue(ctx, &dsc, sri, v);
+ if (error)
+ return error;
+
+ /*
+ * Progress is counted by the inode for inode metadata; for
+ * everything else, it's counted for each scrub call.
+ */
+ if (!(sri->sri_state[v->sv_type] & SCRUB_ITEM_NEEDSCHECK) &&
+ sri->sri_ino == -1ULL)
+ progress_add(1);
+ }
+
+ return 0;
+}
+
/* Bulk-notify user about things that could be optimized. */
void
scrub_report_preen_triggers(
@@ -291,6 +364,37 @@ scrub_item_schedule_group(
}
}
+/* Decide if we call the kernel again to finish scrub/repair activity. */
+static inline bool
+scrub_item_call_kernel_again_future(
+ struct scrub_item *sri,
+ uint8_t work_mask,
+ const struct scrub_item *old)
+{
+ unsigned int scrub_type;
+ unsigned int nr = 0;
+
+ /* If there's nothing to do, we're done. */
+ foreach_scrub_type(scrub_type) {
+ if (sri->sri_state[scrub_type] & work_mask)
+ nr++;
+ }
+ if (!nr)
+ return false;
+
+ foreach_scrub_type(scrub_type) {
+ uint8_t statex = sri->sri_state[scrub_type] ^
+ old->sri_state[scrub_type];
+
+ if (statex & work_mask)
+ return true;
+ if (sri->sri_tries[scrub_type] != old->sri_tries[scrub_type])
+ return true;
+ }
+
+ return false;
+}
+
/* Decide if we call the kernel again to finish scrub/repair activity. */
bool
scrub_item_call_kernel_again(
@@ -319,6 +423,29 @@ scrub_item_call_kernel_again(
return false;
}
+/*
+ * For each scrub item whose state matches the state_flags, set up the item
+ * state for a kernel call. Returns true if any work was scheduled.
+ */
+bool
+scrub_item_schedule_work(
+ struct scrub_item *sri,
+ uint8_t state_flags)
+{
+ unsigned int scrub_type;
+ unsigned int nr = 0;
+
+ foreach_scrub_type(scrub_type) {
+ if (!(sri->sri_state[scrub_type] & state_flags))
+ continue;
+
+ sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES;
+ nr++;
+ }
+
+ return nr > 0;
+}
+
/* Run all the incomplete scans on this scrub principal. */
int
scrub_item_check_file(
@@ -329,8 +456,10 @@ scrub_item_check_file(
struct xfs_fd xfd;
struct scrub_item old_sri;
struct xfs_fd *xfdp = &ctx->mnt;
- unsigned int scrub_type;
- int error;
+ int error = 0;
+
+ if (!scrub_item_schedule_work(sri, SCRUB_ITEM_NEEDSCHECK))
+ return 0;
/*
* If the caller passed us a file descriptor for a scrub, use it
@@ -343,31 +472,15 @@ scrub_item_check_file(
xfdp = &xfd;
}
- foreach_scrub_type(scrub_type) {
- if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK))
- continue;
-
- sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES;
- do {
- memcpy(&old_sri, sri, sizeof(old_sri));
- error = xfs_check_metadata(ctx, xfdp, scrub_type, sri);
- if (error)
- return error;
- } while (scrub_item_call_kernel_again(sri, scrub_type,
- SCRUB_ITEM_NEEDSCHECK, &old_sri));
-
- /*
- * Progress is counted by the inode for inode metadata; for
- * everything else, it's counted for each scrub call.
- */
- if (sri->sri_ino == -1ULL)
- progress_add(1);
-
+ do {
+ memcpy(&old_sri, sri, sizeof(old_sri));
+ error = scrub_call_kernel(ctx, xfdp, sri);
if (error)
- break;
- }
+ return error;
+ } while (scrub_item_call_kernel_again_future(sri, SCRUB_ITEM_NEEDSCHECK,
+ &old_sri));
- return error;
+ return 0;
}
/* How many items do we have to check? */
@@ -562,3 +675,21 @@ can_force_rebuild(
return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE,
XFS_SCRUB_IFLAG_REPAIR | XFS_SCRUB_IFLAG_FORCE_REBUILD);
}
+
+void
+check_scrubv(
+ struct scrub_ctx *ctx)
+{
+ struct xfrog_scrubv scrubv = { };
+
+ xfrog_scrubv_init(&scrubv);
+
+ if (debug_tweak_on("XFS_SCRUB_FORCE_SINGLE"))
+ ctx->mnt.flags |= XFROG_FLAG_SCRUB_FORCE_SINGLE;
+
+ /*
+ * We set the fallback flag if calling the kernel with a zero-length
+ * vector doesn't work.
+ */
+ xfrog_scrubv_metadata(&ctx->mnt, &scrubv);
+}
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 90578108a..183b89379 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -138,6 +138,8 @@ bool can_scrub_parent(struct scrub_ctx *ctx);
bool can_repair(struct scrub_ctx *ctx);
bool can_force_rebuild(struct scrub_ctx *ctx);
+void check_scrubv(struct scrub_ctx *ctx);
+
int scrub_file(struct scrub_ctx *ctx, int fd, const struct xfs_bulkstat *bstat,
unsigned int type, struct scrub_item *sri);
diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h
index 98a9238f2..bf53ee5af 100644
--- a/scrub/scrub_private.h
+++ b/scrub/scrub_private.h
@@ -8,9 +8,24 @@
/* Shared code between scrub.c and repair.c. */
+void xfrog_scrubv_from_item(struct xfrog_scrubv *scrubv,
+ const struct scrub_item *sri);
+void xfrog_scrubv_add_item(struct xfrog_scrubv *scrubv,
+ const struct scrub_item *sri, unsigned int scrub_type);
+
int format_scrub_descr(struct scrub_ctx *ctx, char *buf, size_t buflen,
void *where);
+struct scrubv_descr {
+ struct xfrog_scrubv *scrubv;
+ int idx;
+};
+
+#define SCRUBV_DESCR(sv) { .scrubv = (sv), .idx = -1 }
+
+int format_scrubv_descr(struct scrub_ctx *ctx, char *buf, size_t buflen,
+ void *where);
+
/* Predicates for scrub flag state. */
static inline bool is_corrupt(const struct xfs_scrub_vec *sv)
@@ -104,5 +119,6 @@ scrub_item_schedule_retry(struct scrub_item *sri, unsigned int scrub_type)
bool scrub_item_call_kernel_again(struct scrub_item *sri,
unsigned int scrub_type, uint8_t work_mask,
const struct scrub_item *old);
+bool scrub_item_schedule_work(struct scrub_item *sri, uint8_t state_flags);
#endif /* XFS_SCRUB_SCRUB_PRIVATE_H_ */
diff --git a/scrub/xfs_scrub.c b/scrub/xfs_scrub.c
index bb316f73e..f5b58de12 100644
--- a/scrub/xfs_scrub.c
+++ b/scrub/xfs_scrub.c
@@ -115,6 +115,7 @@
* XFS_SCRUB_THREADS -- start exactly this number of threads
* XFS_SCRUB_DISK_ERROR_INTERVAL-- simulate a disk error every this many bytes
* XFS_SCRUB_DISK_VERIFY_SKIP -- pretend disk verify read calls succeeded
+ * XFS_SCRUB_FORCE_SINGLE -- fall back to ioctl-per-item scrubbing
*
* Available even in non-debug mode:
* SERVICE_MODE -- compress all error codes to 1 for LSB
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 08/10] xfs_scrub: vectorize repair calls
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
` (6 preceding siblings ...)
2024-07-30 1:31 ` [PATCH 07/10] xfs_scrub: vectorize scrub calls Darrick J. Wong
@ 2024-07-30 1:32 ` Darrick J. Wong
2024-07-30 1:32 ` [PATCH 09/10] xfs_scrub: use scrub barriers to reduce kernel calls Darrick J. Wong
2024-07-30 1:32 ` [PATCH 10/10] xfs_scrub: try spot repairs of metadata items to make scrub progress Darrick J. Wong
9 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:32 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Use the new vectorized scrub kernel calls to reduce the overhead of
performing repairs.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/repair.c | 268 +++++++++++++++++++++++++++----------------------
scrub/scrub.c | 77 +++-----------
scrub/scrub_private.h | 9 +-
3 files changed, 166 insertions(+), 188 deletions(-)
diff --git a/scrub/repair.c b/scrub/repair.c
index 7a710a159..6a5fd40fd 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -20,11 +20,6 @@
#include "descr.h"
#include "scrub_private.h"
-static int repair_epilogue(struct scrub_ctx *ctx, struct descr *dsc,
- struct scrub_item *sri, unsigned int repair_flags,
- const struct xfs_scrub_vec *oldm,
- const struct xfs_scrub_vec *meta);
-
/* General repair routines. */
/*
@@ -83,64 +78,14 @@ repair_want_service_downgrade(
return false;
}
-/* Repair some metadata. */
-static int
-xfs_repair_metadata(
- struct scrub_ctx *ctx,
- struct xfs_fd *xfdp,
- unsigned int scrub_type,
- struct scrub_item *sri,
- unsigned int repair_flags)
+static inline void
+restore_oldvec(
+ struct xfs_scrub_vec *oldvec,
+ const struct scrub_item *sri,
+ unsigned int scrub_type)
{
- struct xfs_scrub_metadata meta = { 0 };
- struct xfs_scrub_vec oldm, vec;
- DEFINE_DESCR(dsc, ctx, format_scrub_descr);
- bool repair_only;
-
- /*
- * If the caller boosted the priority of this scrub type on behalf of a
- * higher level repair by setting IFLAG_REPAIR, turn off REPAIR_ONLY.
- */
- repair_only = (repair_flags & XRM_REPAIR_ONLY) &&
- scrub_item_type_boosted(sri, scrub_type);
-
- assert(scrub_type < XFS_SCRUB_TYPE_NR);
- assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
- meta.sm_type = scrub_type;
- meta.sm_flags = XFS_SCRUB_IFLAG_REPAIR;
- if (use_force_rebuild)
- meta.sm_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
- switch (xfrog_scrubbers[scrub_type].group) {
- case XFROG_SCRUB_GROUP_AGHEADER:
- case XFROG_SCRUB_GROUP_PERAG:
- meta.sm_agno = sri->sri_agno;
- break;
- case XFROG_SCRUB_GROUP_INODE:
- meta.sm_ino = sri->sri_ino;
- meta.sm_gen = sri->sri_gen;
- break;
- default:
- break;
- }
-
- vec.sv_type = scrub_type;
- vec.sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
- memcpy(&oldm, &vec, sizeof(struct xfs_scrub_vec));
- if (!is_corrupt(&vec) && repair_only)
- return 0;
-
- descr_set(&dsc, &meta);
-
- if (needs_repair(&vec))
- str_info(ctx, descr_render(&dsc), _("Attempting repair."));
- else if (debug || verbose)
- str_info(ctx, descr_render(&dsc),
- _("Attempting optimization."));
-
- vec.sv_ret = xfrog_scrub_metadata(xfdp, &meta);
- vec.sv_flags = meta.sm_flags;
-
- return repair_epilogue(ctx, &dsc, sri, repair_flags, &oldm, &vec);
+ oldvec->sv_type = scrub_type;
+ oldvec->sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
}
static int
@@ -149,12 +94,15 @@ repair_epilogue(
struct descr *dsc,
struct scrub_item *sri,
unsigned int repair_flags,
- const struct xfs_scrub_vec *oldm,
const struct xfs_scrub_vec *meta)
{
+ struct xfs_scrub_vec oldv;
+ struct xfs_scrub_vec *oldm = &oldv;
unsigned int scrub_type = meta->sv_type;
int error = -meta->sv_ret;
+ restore_oldvec(oldm, sri, meta->sv_type);
+
switch (error) {
case 0:
/* No operational errors encountered. */
@@ -296,6 +244,133 @@ _("Repair unsuccessful; offline repair required."));
return 0;
}
+/* Decide if the dependent scrub types of the given scrub type are ok. */
+static bool
+repair_item_dependencies_ok(
+ const struct scrub_item *sri,
+ unsigned int scrub_type)
+{
+ unsigned int dep_mask = repair_deps[scrub_type];
+ unsigned int b;
+
+ for (b = 0; dep_mask && b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
+ if (!(dep_mask & 1))
+ continue;
+ /*
+ * If this lower level object also needs repair, we can't fix
+ * the higher level item.
+ */
+ if (sri->sri_state[b] & SCRUB_ITEM_NEEDSREPAIR)
+ return false;
+ }
+
+ return true;
+}
+
+/* Decide if we want to repair a particular type of metadata. */
+static bool
+can_repair_now(
+ const struct scrub_item *sri,
+ unsigned int scrub_type,
+ __u32 repair_mask,
+ unsigned int repair_flags)
+{
+ struct xfs_scrub_vec oldvec;
+ bool repair_only;
+
+ /* Do we even need to repair this thing? */
+ if (!(sri->sri_state[scrub_type] & repair_mask))
+ return false;
+
+ restore_oldvec(&oldvec, sri, scrub_type);
+
+ /*
+ * If the caller boosted the priority of this scrub type on behalf of a
+ * higher level repair by setting IFLAG_REPAIR, ignore REPAIR_ONLY.
+ */
+ repair_only = (repair_flags & XRM_REPAIR_ONLY) &&
+ !(sri->sri_state[scrub_type] & SCRUB_ITEM_BOOST_REPAIR);
+ if (!is_corrupt(&oldvec) && repair_only)
+ return false;
+
+ /*
+ * Don't try to repair higher level items if their lower-level
+ * dependencies haven't been verified, unless this is our last chance
+ * to fix things without complaint.
+ */
+ if (!(repair_flags & XRM_FINAL_WARNING) &&
+ !repair_item_dependencies_ok(sri, scrub_type))
+ return false;
+
+ return true;
+}
+
+/*
+ * Repair some metadata.
+ *
+ * Returns 0 for success (or repair item deferral), or ECANCELED to abort the
+ * program.
+ */
+static int
+repair_call_kernel(
+ struct scrub_ctx *ctx,
+ struct xfs_fd *xfdp,
+ struct scrub_item *sri,
+ __u32 repair_mask,
+ unsigned int repair_flags)
+{
+ DEFINE_DESCR(dsc, ctx, format_scrubv_descr);
+ struct xfrog_scrubv scrubv = { };
+ struct scrubv_descr vdesc = SCRUBV_DESCR(&scrubv);
+ struct xfs_scrub_vec *v;
+ unsigned int scrub_type;
+ int error;
+
+ assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
+
+ xfrog_scrubv_from_item(&scrubv, sri);
+ descr_set(&dsc, &vdesc);
+
+ foreach_scrub_type(scrub_type) {
+ if (scrub_excessive_errors(ctx))
+ return ECANCELED;
+
+ if (!can_repair_now(sri, scrub_type, repair_mask,
+ repair_flags))
+ continue;
+
+ xfrog_scrubv_add_item(&scrubv, sri, scrub_type, true);
+
+ if (sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSREPAIR)
+ str_info(ctx, descr_render(&dsc),
+ _("Attempting repair."));
+ else if (debug || verbose)
+ str_info(ctx, descr_render(&dsc),
+ _("Attempting optimization."));
+
+ dbg_printf("repair %s flags %xh tries %u\n", descr_render(&dsc),
+ sri->sri_state[scrub_type],
+ sri->sri_tries[scrub_type]);
+ }
+
+ error = -xfrog_scrubv_metadata(xfdp, &scrubv);
+ if (error)
+ return error;
+
+ foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, v) {
+ error = repair_epilogue(ctx, &dsc, sri, repair_flags, v);
+ if (error)
+ return error;
+
+ /* Maybe update progress if we fixed the problem. */
+ if (!(repair_flags & XRM_NOPROGRESS) &&
+ !(sri->sri_state[v->sv_type] & SCRUB_ITEM_REPAIR_ANY))
+ progress_add(1);
+ }
+
+ return 0;
+}
+
/*
* Prioritize action items in order of how long we can wait.
*
@@ -632,29 +707,6 @@ action_list_process(
return ret;
}
-/* Decide if the dependent scrub types of the given scrub type are ok. */
-static bool
-repair_item_dependencies_ok(
- const struct scrub_item *sri,
- unsigned int scrub_type)
-{
- unsigned int dep_mask = repair_deps[scrub_type];
- unsigned int b;
-
- for (b = 0; dep_mask && b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
- if (!(dep_mask & 1))
- continue;
- /*
- * If this lower level object also needs repair, we can't fix
- * the higher level item.
- */
- if (sri->sri_state[b] & SCRUB_ITEM_NEEDSREPAIR)
- return false;
- }
-
- return true;
-}
-
/*
* For a given filesystem object, perform all repairs of a given class
* (corrupt, xcorrupt, xfail, preen) if the repair item says it's needed.
@@ -670,13 +722,14 @@ repair_item_class(
struct xfs_fd xfd;
struct scrub_item old_sri;
struct xfs_fd *xfdp = &ctx->mnt;
- unsigned int scrub_type;
int error = 0;
if (ctx->mode == SCRUB_MODE_DRY_RUN)
return 0;
if (ctx->mode == SCRUB_MODE_PREEN && !(repair_mask & SCRUB_ITEM_PREEN))
return 0;
+ if (!scrub_item_schedule_work(sri, repair_mask))
+ return 0;
/*
* If the caller passed us a file descriptor for a scrub, use it
@@ -689,39 +742,14 @@ repair_item_class(
xfdp = &xfd;
}
- foreach_scrub_type(scrub_type) {
- if (scrub_excessive_errors(ctx))
- return ECANCELED;
-
- if (!(sri->sri_state[scrub_type] & repair_mask))
- continue;
-
- /*
- * Don't try to repair higher level items if their lower-level
- * dependencies haven't been verified, unless this is our last
- * chance to fix things without complaint.
- */
- if (!(flags & XRM_FINAL_WARNING) &&
- !repair_item_dependencies_ok(sri, scrub_type))
- continue;
-
- sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES;
- do {
- memcpy(&old_sri, sri, sizeof(old_sri));
- error = xfs_repair_metadata(ctx, xfdp, scrub_type, sri,
- flags);
- if (error)
- return error;
- } while (scrub_item_call_kernel_again(sri, scrub_type,
- repair_mask, &old_sri));
-
- /* Maybe update progress if we fixed the problem. */
- if (!(flags & XRM_NOPROGRESS) &&
- !(sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY))
- progress_add(1);
- }
-
- return error;
+ do {
+ memcpy(&old_sri, sri, sizeof(struct scrub_item));
+ error = repair_call_kernel(ctx, xfdp, sri, repair_mask, flags);
+ if (error)
+ return error;
+ } while (scrub_item_call_kernel_again(sri, repair_mask, &old_sri));
+
+ return 0;
}
/*
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 0c77f9472..d582dafbb 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -64,35 +64,6 @@ format_scrubv_descr(
return -1;
}
-/* Format a scrub description. */
-int
-format_scrub_descr(
- struct scrub_ctx *ctx,
- char *buf,
- size_t buflen,
- void *where)
-{
- struct xfs_scrub_metadata *meta = where;
- const struct xfrog_scrub_descr *sc = &xfrog_scrubbers[meta->sm_type];
-
- switch (sc->group) {
- case XFROG_SCRUB_GROUP_AGHEADER:
- case XFROG_SCRUB_GROUP_PERAG:
- return snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
- _(sc->descr));
- case XFROG_SCRUB_GROUP_INODE:
- return scrub_render_ino_descr(ctx, buf, buflen,
- meta->sm_ino, meta->sm_gen, "%s",
- _(sc->descr));
- case XFROG_SCRUB_GROUP_FS:
- case XFROG_SCRUB_GROUP_SUMMARY:
- case XFROG_SCRUB_GROUP_ISCAN:
- case XFROG_SCRUB_GROUP_NONE:
- return snprintf(buf, buflen, _("%s"), _(sc->descr));
- }
- return -1;
-}
-
/* Warn about strange circumstances after scrub. */
void
scrub_warn_incomplete_scrub(
@@ -271,12 +242,17 @@ void
xfrog_scrubv_add_item(
struct xfrog_scrubv *scrubv,
const struct scrub_item *sri,
- unsigned int scrub_type)
+ unsigned int scrub_type,
+ bool want_repair)
{
struct xfs_scrub_vec *v;
v = xfrog_scrubv_next_vector(scrubv);
v->sv_type = scrub_type;
+ if (want_repair)
+ v->sv_flags |= XFS_SCRUB_IFLAG_REPAIR;
+ if (want_repair && use_force_rebuild)
+ v->sv_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
}
/* Do a read-only check of some metadata. */
@@ -301,7 +277,7 @@ scrub_call_kernel(
foreach_scrub_type(scrub_type) {
if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK))
continue;
- xfrog_scrubv_add_item(&scrubv, sri, scrub_type);
+ xfrog_scrubv_add_item(&scrubv, sri, scrub_type, false);
dbg_printf("check %s flags %xh tries %u\n", descr_render(&dsc),
sri->sri_state[scrub_type],
@@ -365,8 +341,8 @@ scrub_item_schedule_group(
}
/* Decide if we call the kernel again to finish scrub/repair activity. */
-static inline bool
-scrub_item_call_kernel_again_future(
+bool
+scrub_item_call_kernel_again(
struct scrub_item *sri,
uint8_t work_mask,
const struct scrub_item *old)
@@ -382,6 +358,11 @@ scrub_item_call_kernel_again_future(
if (!nr)
return false;
+ /*
+ * We are willing to go again if the last call had any effect on the
+ * state of the scrub item that the caller cares about or if the kernel
+ * asked us to try again.
+ */
foreach_scrub_type(scrub_type) {
uint8_t statex = sri->sri_state[scrub_type] ^
old->sri_state[scrub_type];
@@ -395,34 +376,6 @@ scrub_item_call_kernel_again_future(
return false;
}
-/* Decide if we call the kernel again to finish scrub/repair activity. */
-bool
-scrub_item_call_kernel_again(
- struct scrub_item *sri,
- unsigned int scrub_type,
- uint8_t work_mask,
- const struct scrub_item *old)
-{
- uint8_t statex;
-
- /* If there's nothing to do, we're done. */
- if (!(sri->sri_state[scrub_type] & work_mask))
- return false;
-
- /*
- * We are willing to go again if the last call had any effect on the
- * state of the scrub item that the caller cares about, if the freeze
- * flag got set, or if the kernel asked us to try again...
- */
- statex = sri->sri_state[scrub_type] ^ old->sri_state[scrub_type];
- if (statex & work_mask)
- return true;
- if (sri->sri_tries[scrub_type] != old->sri_tries[scrub_type])
- return true;
-
- return false;
-}
-
/*
* For each scrub item whose state matches the state_flags, set up the item
* state for a kernel call. Returns true if any work was scheduled.
@@ -477,7 +430,7 @@ scrub_item_check_file(
error = scrub_call_kernel(ctx, xfdp, sri);
if (error)
return error;
- } while (scrub_item_call_kernel_again_future(sri, SCRUB_ITEM_NEEDSCHECK,
+ } while (scrub_item_call_kernel_again(sri, SCRUB_ITEM_NEEDSCHECK,
&old_sri));
return 0;
diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h
index bf53ee5af..d9de18ce1 100644
--- a/scrub/scrub_private.h
+++ b/scrub/scrub_private.h
@@ -11,10 +11,8 @@
void xfrog_scrubv_from_item(struct xfrog_scrubv *scrubv,
const struct scrub_item *sri);
void xfrog_scrubv_add_item(struct xfrog_scrubv *scrubv,
- const struct scrub_item *sri, unsigned int scrub_type);
-
-int format_scrub_descr(struct scrub_ctx *ctx, char *buf, size_t buflen,
- void *where);
+ const struct scrub_item *sri, unsigned int scrub_type,
+ bool want_repair);
struct scrubv_descr {
struct xfrog_scrubv *scrubv;
@@ -116,8 +114,7 @@ scrub_item_schedule_retry(struct scrub_item *sri, unsigned int scrub_type)
return true;
}
-bool scrub_item_call_kernel_again(struct scrub_item *sri,
- unsigned int scrub_type, uint8_t work_mask,
+bool scrub_item_call_kernel_again(struct scrub_item *sri, uint8_t work_mask,
const struct scrub_item *old);
bool scrub_item_schedule_work(struct scrub_item *sri, uint8_t state_flags);
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 09/10] xfs_scrub: use scrub barriers to reduce kernel calls
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
` (7 preceding siblings ...)
2024-07-30 1:32 ` [PATCH 08/10] xfs_scrub: vectorize repair calls Darrick J. Wong
@ 2024-07-30 1:32 ` Darrick J. Wong
2024-07-30 1:32 ` [PATCH 10/10] xfs_scrub: try spot repairs of metadata items to make scrub progress Darrick J. Wong
9 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:32 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Use scrub barriers so that we can submit a single scrub request for a
bunch of things, and have the kernel stop midway through if it finds
anything broken.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase2.c | 15 ++--------
scrub/phase3.c | 17 +----------
scrub/repair.c | 32 +++++++++++++++++++-
scrub/scrub.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++-
scrub/scrub.h | 17 +++++++++++
scrub/scrub_private.h | 4 ++-
6 files changed, 130 insertions(+), 32 deletions(-)
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 57c6d0ef2..d435da071 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -91,21 +91,12 @@ scan_ag_metadata(
snprintf(descr, DESCR_BUFSZ, _("AG %u"), agno);
/*
- * First we scrub and fix the AG headers, because we need
- * them to work well enough to check the AG btrees.
+ * First we scrub and fix the AG headers, because we need them to work
+ * well enough to check the AG btrees. Then scrub the AG btrees.
*/
scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_AGHEADER);
- ret = scrub_item_check(ctx, &sri);
- if (ret)
- goto err;
-
- /* Repair header damage. */
- ret = repair_item_corruption(ctx, &sri);
- if (ret)
- goto err;
-
- /* Now scrub the AG btrees. */
scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_PERAG);
+
ret = scrub_item_check(ctx, &sri);
if (ret)
goto err;
diff --git a/scrub/phase3.c b/scrub/phase3.c
index 98e5c5a1f..09a1ea452 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -145,25 +145,11 @@ scrub_inode(
/* Scrub the inode. */
scrub_item_schedule(&sri, XFS_SCRUB_TYPE_INODE);
- error = scrub_item_check_file(ctx, &sri, fd);
- if (error)
- goto out;
-
- error = try_inode_repair(ictx, &sri, fd);
- if (error)
- goto out;
/* Scrub all block mappings. */
scrub_item_schedule(&sri, XFS_SCRUB_TYPE_BMBTD);
scrub_item_schedule(&sri, XFS_SCRUB_TYPE_BMBTA);
scrub_item_schedule(&sri, XFS_SCRUB_TYPE_BMBTC);
- error = scrub_item_check_file(ctx, &sri, fd);
- if (error)
- goto out;
-
- error = try_inode_repair(ictx, &sri, fd);
- if (error)
- goto out;
/*
* Check file data contents, e.g. symlink and directory entries.
@@ -182,11 +168,12 @@ scrub_inode(
scrub_item_schedule(&sri, XFS_SCRUB_TYPE_XATTR);
scrub_item_schedule(&sri, XFS_SCRUB_TYPE_PARENT);
+
+ /* Try to check and repair the file while it's open. */
error = scrub_item_check_file(ctx, &sri, fd);
if (error)
goto out;
- /* Try to repair the file while it's open. */
error = try_inode_repair(ictx, &sri, fd);
if (error)
goto out;
diff --git a/scrub/repair.c b/scrub/repair.c
index 6a5fd40fd..8a28f6b13 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -324,6 +324,7 @@ repair_call_kernel(
struct scrubv_descr vdesc = SCRUBV_DESCR(&scrubv);
struct xfs_scrub_vec *v;
unsigned int scrub_type;
+ bool need_barrier = false;
int error;
assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
@@ -339,6 +340,11 @@ repair_call_kernel(
repair_flags))
continue;
+ if (need_barrier) {
+ xfrog_scrubv_add_barrier(&scrubv);
+ need_barrier = false;
+ }
+
xfrog_scrubv_add_item(&scrubv, sri, scrub_type, true);
if (sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSREPAIR)
@@ -351,6 +357,17 @@ repair_call_kernel(
dbg_printf("repair %s flags %xh tries %u\n", descr_render(&dsc),
sri->sri_state[scrub_type],
sri->sri_tries[scrub_type]);
+
+ /*
+ * One of the other scrub types depends on this one. Set us up
+ * to add a repair barrier if we decide to schedule a repair
+ * after this one. If the UNFIXED flag is set, that means this
+ * is our last chance to fix things, so we skip the barriers
+ * just let everything run.
+ */
+ if (!(repair_flags & XRM_FINAL_WARNING) &&
+ (sri->sri_state[scrub_type] & SCRUB_ITEM_BARRIER))
+ need_barrier = true;
}
error = -xfrog_scrubv_metadata(xfdp, &scrubv);
@@ -358,6 +375,16 @@ repair_call_kernel(
return error;
foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, v) {
+ /* Deal with barriers separately. */
+ if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) {
+ /* -ECANCELED means the kernel stopped here. */
+ if (v->sv_ret == -ECANCELED)
+ return 0;
+ if (v->sv_ret)
+ return -v->sv_ret;
+ continue;
+ }
+
error = repair_epilogue(ctx, &dsc, sri, repair_flags, v);
if (error)
return error;
@@ -446,7 +473,8 @@ repair_item_boost_priorities(
* bits are left untouched to force a rescan in phase 4.
*/
#define MUSTFIX_STATES (SCRUB_ITEM_CORRUPT | \
- SCRUB_ITEM_BOOST_REPAIR)
+ SCRUB_ITEM_BOOST_REPAIR | \
+ SCRUB_ITEM_BARRIER)
/*
* Figure out which AG metadata must be fixed before we can move on
* to the inode scan.
@@ -728,7 +756,7 @@ repair_item_class(
return 0;
if (ctx->mode == SCRUB_MODE_PREEN && !(repair_mask & SCRUB_ITEM_PREEN))
return 0;
- if (!scrub_item_schedule_work(sri, repair_mask))
+ if (!scrub_item_schedule_work(sri, repair_mask, repair_deps))
return 0;
/*
diff --git a/scrub/scrub.c b/scrub/scrub.c
index d582dafbb..44c404989 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -24,6 +24,35 @@
/* Online scrub and repair wrappers. */
+/*
+ * Bitmap showing the correctness dependencies between scrub types for scrubs.
+ * Dependencies cannot cross scrub groups.
+ */
+#define DEP(x) (1U << (x))
+static const unsigned int scrub_deps[XFS_SCRUB_TYPE_NR] = {
+ [XFS_SCRUB_TYPE_AGF] = DEP(XFS_SCRUB_TYPE_SB),
+ [XFS_SCRUB_TYPE_AGFL] = DEP(XFS_SCRUB_TYPE_SB) |
+ DEP(XFS_SCRUB_TYPE_AGF),
+ [XFS_SCRUB_TYPE_AGI] = DEP(XFS_SCRUB_TYPE_SB),
+ [XFS_SCRUB_TYPE_BNOBT] = DEP(XFS_SCRUB_TYPE_AGF),
+ [XFS_SCRUB_TYPE_CNTBT] = DEP(XFS_SCRUB_TYPE_AGF),
+ [XFS_SCRUB_TYPE_INOBT] = DEP(XFS_SCRUB_TYPE_AGI),
+ [XFS_SCRUB_TYPE_FINOBT] = DEP(XFS_SCRUB_TYPE_AGI),
+ [XFS_SCRUB_TYPE_RMAPBT] = DEP(XFS_SCRUB_TYPE_AGF),
+ [XFS_SCRUB_TYPE_REFCNTBT] = DEP(XFS_SCRUB_TYPE_AGF),
+ [XFS_SCRUB_TYPE_BMBTD] = DEP(XFS_SCRUB_TYPE_INODE),
+ [XFS_SCRUB_TYPE_BMBTA] = DEP(XFS_SCRUB_TYPE_INODE),
+ [XFS_SCRUB_TYPE_BMBTC] = DEP(XFS_SCRUB_TYPE_INODE),
+ [XFS_SCRUB_TYPE_DIR] = DEP(XFS_SCRUB_TYPE_BMBTD),
+ [XFS_SCRUB_TYPE_XATTR] = DEP(XFS_SCRUB_TYPE_BMBTA),
+ [XFS_SCRUB_TYPE_SYMLINK] = DEP(XFS_SCRUB_TYPE_BMBTD),
+ [XFS_SCRUB_TYPE_PARENT] = DEP(XFS_SCRUB_TYPE_BMBTD),
+ [XFS_SCRUB_TYPE_QUOTACHECK] = DEP(XFS_SCRUB_TYPE_UQUOTA) |
+ DEP(XFS_SCRUB_TYPE_GQUOTA) |
+ DEP(XFS_SCRUB_TYPE_PQUOTA),
+};
+#undef DEP
+
/* Describe the current state of a vectored scrub. */
int
format_scrubv_descr(
@@ -255,6 +284,20 @@ xfrog_scrubv_add_item(
v->sv_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
}
+/* Add a barrier to the scrub vector. */
+void
+xfrog_scrubv_add_barrier(
+ struct xfrog_scrubv *scrubv)
+{
+ struct xfs_scrub_vec *v;
+
+ v = xfrog_scrubv_next_vector(scrubv);
+
+ v->sv_type = XFS_SCRUB_TYPE_BARRIER;
+ v->sv_flags = XFS_SCRUB_OFLAG_CORRUPT | XFS_SCRUB_OFLAG_XFAIL |
+ XFS_SCRUB_OFLAG_XCORRUPT | XFS_SCRUB_OFLAG_INCOMPLETE;
+}
+
/* Do a read-only check of some metadata. */
static int
scrub_call_kernel(
@@ -267,6 +310,7 @@ scrub_call_kernel(
struct scrubv_descr vdesc = SCRUBV_DESCR(&scrubv);
struct xfs_scrub_vec *v;
unsigned int scrub_type;
+ bool need_barrier = false;
int error;
assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
@@ -277,8 +321,17 @@ scrub_call_kernel(
foreach_scrub_type(scrub_type) {
if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK))
continue;
+
+ if (need_barrier) {
+ xfrog_scrubv_add_barrier(&scrubv);
+ need_barrier = false;
+ }
+
xfrog_scrubv_add_item(&scrubv, sri, scrub_type, false);
+ if (sri->sri_state[scrub_type] & SCRUB_ITEM_BARRIER)
+ need_barrier = true;
+
dbg_printf("check %s flags %xh tries %u\n", descr_render(&dsc),
sri->sri_state[scrub_type],
sri->sri_tries[scrub_type]);
@@ -289,6 +342,16 @@ scrub_call_kernel(
return error;
foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, v) {
+ /* Deal with barriers separately. */
+ if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) {
+ /* -ECANCELED means the kernel stopped here. */
+ if (v->sv_ret == -ECANCELED)
+ return 0;
+ if (v->sv_ret)
+ return -v->sv_ret;
+ continue;
+ }
+
error = scrub_epilogue(ctx, &dsc, sri, v);
if (error)
return error;
@@ -383,15 +446,25 @@ scrub_item_call_kernel_again(
bool
scrub_item_schedule_work(
struct scrub_item *sri,
- uint8_t state_flags)
+ uint8_t state_flags,
+ const unsigned int *schedule_deps)
{
unsigned int scrub_type;
unsigned int nr = 0;
foreach_scrub_type(scrub_type) {
+ unsigned int j;
+
+ sri->sri_state[scrub_type] &= ~SCRUB_ITEM_BARRIER;
+
if (!(sri->sri_state[scrub_type] & state_flags))
continue;
+ foreach_scrub_type(j) {
+ if (schedule_deps[scrub_type] & (1U << j))
+ sri->sri_state[j] |= SCRUB_ITEM_BARRIER;
+ }
+
sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES;
nr++;
}
@@ -411,7 +484,7 @@ scrub_item_check_file(
struct xfs_fd *xfdp = &ctx->mnt;
int error = 0;
- if (!scrub_item_schedule_work(sri, SCRUB_ITEM_NEEDSCHECK))
+ if (!scrub_item_schedule_work(sri, SCRUB_ITEM_NEEDSCHECK, scrub_deps))
return 0;
/*
diff --git a/scrub/scrub.h b/scrub/scrub.h
index 183b89379..c3eed1b26 100644
--- a/scrub/scrub.h
+++ b/scrub/scrub.h
@@ -30,6 +30,9 @@ enum xfrog_scrub_group;
/* This scrub type needs to be checked. */
#define SCRUB_ITEM_NEEDSCHECK (1 << 5)
+/* Scrub barrier. */
+#define SCRUB_ITEM_BARRIER (1 << 6)
+
/* All of the state flags that we need to prioritize repair work. */
#define SCRUB_ITEM_REPAIR_ANY (SCRUB_ITEM_CORRUPT | \
SCRUB_ITEM_PREEN | \
@@ -126,6 +129,20 @@ scrub_item_check(struct scrub_ctx *ctx, struct scrub_item *sri)
return scrub_item_check_file(ctx, sri, -1);
}
+/* Count the number of metadata objects still needing a scrub. */
+static inline unsigned int
+scrub_item_count_needscheck(
+ const struct scrub_item *sri)
+{
+ unsigned int ret = 0;
+ unsigned int i;
+
+ foreach_scrub_type(i)
+ if (sri->sri_state[i] & SCRUB_ITEM_NEEDSCHECK)
+ ret++;
+ return ret;
+}
+
void scrub_report_preen_triggers(struct scrub_ctx *ctx);
bool can_scrub_fs_metadata(struct scrub_ctx *ctx);
diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h
index d9de18ce1..c5e0a7c08 100644
--- a/scrub/scrub_private.h
+++ b/scrub/scrub_private.h
@@ -13,6 +13,7 @@ void xfrog_scrubv_from_item(struct xfrog_scrubv *scrubv,
void xfrog_scrubv_add_item(struct xfrog_scrubv *scrubv,
const struct scrub_item *sri, unsigned int scrub_type,
bool want_repair);
+void xfrog_scrubv_add_barrier(struct xfrog_scrubv *scrubv);
struct scrubv_descr {
struct xfrog_scrubv *scrubv;
@@ -116,6 +117,7 @@ scrub_item_schedule_retry(struct scrub_item *sri, unsigned int scrub_type)
bool scrub_item_call_kernel_again(struct scrub_item *sri, uint8_t work_mask,
const struct scrub_item *old);
-bool scrub_item_schedule_work(struct scrub_item *sri, uint8_t state_flags);
+bool scrub_item_schedule_work(struct scrub_item *sri, uint8_t state_flags,
+ const unsigned int *schedule_deps);
#endif /* XFS_SCRUB_SCRUB_PRIVATE_H_ */
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 10/10] xfs_scrub: try spot repairs of metadata items to make scrub progress
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
` (8 preceding siblings ...)
2024-07-30 1:32 ` [PATCH 09/10] xfs_scrub: use scrub barriers to reduce kernel calls Darrick J. Wong
@ 2024-07-30 1:32 ` Darrick J. Wong
9 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:32 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Now that we've enabled scrub dependency barriers, it's possible that a
scrub_item_check call will return with some of the scrub items still in
NEEDSCHECK state. If, for example, scrub type B depends on scrub type
A being clean and A is not clean, B will still be in NEEDSCHECK state.
In order to make as much scanning progress as possible during phase 2
and phase 3, allow ourselves to try some spot repairs in the hopes that
it will enable us to make progress towards at least scanning the whole
metadata item. If we can't make any forward progress, we'll queue the
scrub item for repair in phase 4, which means that anything still in in
NEEDSCHECK state becomes CORRUPT state. (At worst, the NEEDSCHECK item
will actually be clean by phase 4, and xfs_scrub will report that it
didn't need any work after all.)
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
scrub/phase2.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
scrub/phase3.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++-
scrub/repair.c | 15 +++++++++++
3 files changed, 163 insertions(+), 1 deletion(-)
diff --git a/scrub/phase2.c b/scrub/phase2.c
index d435da071..c24d13735 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -69,6 +69,53 @@ defer_fs_repair(
return 0;
}
+/*
+ * If we couldn't check all the scheduled metadata items, try performing spot
+ * repairs until we check everything or stop making forward progress.
+ */
+static int
+repair_and_scrub_loop(
+ struct scrub_ctx *ctx,
+ struct scrub_item *sri,
+ const char *descr,
+ bool *defer)
+{
+ unsigned int to_check;
+ int ret;
+
+ *defer = false;
+ if (ctx->mode != SCRUB_MODE_REPAIR)
+ return 0;
+
+ to_check = scrub_item_count_needscheck(sri);
+ while (to_check > 0) {
+ unsigned int nr;
+
+ ret = repair_item_corruption(ctx, sri);
+ if (ret)
+ return ret;
+
+ ret = scrub_item_check(ctx, sri);
+ if (ret)
+ return ret;
+
+ nr = scrub_item_count_needscheck(sri);
+ if (nr == to_check) {
+ /*
+ * We cannot make forward scanning progress with this
+ * metadata, so defer the rest until phase 4.
+ */
+ str_info(ctx, descr,
+ _("Unable to make forward checking progress; will try again in phase 4."));
+ *defer = true;
+ return 0;
+ }
+ to_check = nr;
+ }
+
+ return 0;
+}
+
/* Scrub each AG's metadata btrees. */
static void
scan_ag_metadata(
@@ -82,6 +129,7 @@ scan_ag_metadata(
struct scan_ctl *sctl = arg;
char descr[DESCR_BUFSZ];
unsigned int difficulty;
+ bool defer_repairs;
int ret;
if (sctl->aborted)
@@ -97,10 +145,22 @@ scan_ag_metadata(
scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_AGHEADER);
scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_PERAG);
+ /*
+ * Try to check all of the AG metadata items that we just scheduled.
+ * If we return with some types still needing a check, try repairing
+ * any damaged metadata that we've found so far, and try again. Abort
+ * if we stop making forward progress.
+ */
ret = scrub_item_check(ctx, &sri);
if (ret)
goto err;
+ ret = repair_and_scrub_loop(ctx, &sri, descr, &defer_repairs);
+ if (ret)
+ goto err;
+ if (defer_repairs)
+ goto defer;
+
/*
* Figure out if we need to perform early fixing. The only
* reason we need to do this is if the inobt is broken, which
@@ -117,6 +177,7 @@ scan_ag_metadata(
if (ret)
goto err;
+defer:
/* Everything else gets fixed during phase 4. */
ret = defer_fs_repair(ctx, &sri);
if (ret)
@@ -137,11 +198,18 @@ scan_fs_metadata(
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
struct scan_ctl *sctl = arg;
unsigned int difficulty;
+ bool defer_repairs;
int ret;
if (sctl->aborted)
goto out;
+ /*
+ * Try to check all of the metadata files that we just scheduled. If
+ * we return with some types still needing a check, try repairing any
+ * damaged metadata that we've found so far, and try again. Abort if
+ * we stop making forward progress.
+ */
scrub_item_init_fs(&sri);
scrub_item_schedule(&sri, type);
ret = scrub_item_check(ctx, &sri);
@@ -150,10 +218,20 @@ scan_fs_metadata(
goto out;
}
+ ret = repair_and_scrub_loop(ctx, &sri, xfrog_scrubbers[type].descr,
+ &defer_repairs);
+ if (ret) {
+ sctl->aborted = true;
+ goto out;
+ }
+ if (defer_repairs)
+ goto defer;
+
/* Complain about metadata corruptions that might not be fixable. */
difficulty = repair_item_difficulty(&sri);
warn_repair_difficulties(ctx, difficulty, xfrog_scrubbers[type].descr);
+defer:
ret = defer_fs_repair(ctx, &sri);
if (ret) {
sctl->aborted = true;
diff --git a/scrub/phase3.c b/scrub/phase3.c
index 09a1ea452..046a42c1d 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -99,6 +99,58 @@ try_inode_repair(
return repair_file_corruption(ictx->ctx, sri, fd);
}
+/*
+ * If we couldn't check all the scheduled file metadata items, try performing
+ * spot repairs until we check everything or stop making forward progress.
+ */
+static int
+repair_and_scrub_inode_loop(
+ struct scrub_ctx *ctx,
+ struct xfs_bulkstat *bstat,
+ int fd,
+ struct scrub_item *sri,
+ bool *defer)
+{
+ unsigned int to_check;
+ int error;
+
+ *defer = false;
+ if (ctx->mode != SCRUB_MODE_REPAIR)
+ return 0;
+
+ to_check = scrub_item_count_needscheck(sri);
+ while (to_check > 0) {
+ unsigned int nr;
+
+ error = repair_file_corruption(ctx, sri, fd);
+ if (error)
+ return error;
+
+ error = scrub_item_check_file(ctx, sri, fd);
+ if (error)
+ return error;
+
+ nr = scrub_item_count_needscheck(sri);
+ if (nr == to_check) {
+ char descr[DESCR_BUFSZ];
+
+ /*
+ * We cannot make forward scanning progress with this
+ * inode, so defer the rest until phase 4.
+ */
+ scrub_render_ino_descr(ctx, descr, DESCR_BUFSZ,
+ bstat->bs_ino, bstat->bs_gen, NULL);
+ str_info(ctx, descr,
+ _("Unable to make forward checking progress; will try again in phase 4."));
+ *defer = true;
+ return 0;
+ }
+ to_check = nr;
+ }
+
+ return 0;
+}
+
/* Verify the contents, xattrs, and extent maps of an inode. */
static int
scrub_inode(
@@ -169,11 +221,28 @@ scrub_inode(
scrub_item_schedule(&sri, XFS_SCRUB_TYPE_XATTR);
scrub_item_schedule(&sri, XFS_SCRUB_TYPE_PARENT);
- /* Try to check and repair the file while it's open. */
+ /*
+ * Try to check all of the metadata items that we just scheduled. If
+ * we return with some types still needing a check and the space
+ * metadata isn't also in need of repairs, try repairing any damaged
+ * file metadata that we've found so far, and try checking the file
+ * again. Worst case, defer the repairs and the checks to phase 4 if
+ * we can't make any progress on anything.
+ */
error = scrub_item_check_file(ctx, &sri, fd);
if (error)
goto out;
+ if (!ictx->always_defer_repairs) {
+ bool defer_repairs;
+
+ error = repair_and_scrub_inode_loop(ctx, bstat, fd, &sri,
+ &defer_repairs);
+ if (error || defer_repairs)
+ goto out;
+ }
+
+ /* Try to repair the file while it's open. */
error = try_inode_repair(ictx, &sri, fd);
if (error)
goto out;
diff --git a/scrub/repair.c b/scrub/repair.c
index 8a28f6b13..e594e704f 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -860,6 +860,7 @@ repair_item_to_action_item(
struct action_item **aitemp)
{
struct action_item *aitem;
+ unsigned int scrub_type;
if (repair_item_count_needsrepair(sri) == 0)
return 0;
@@ -875,6 +876,20 @@ repair_item_to_action_item(
INIT_LIST_HEAD(&aitem->list);
memcpy(&aitem->sri, sri, sizeof(struct scrub_item));
+ /*
+ * If the scrub item indicates that there is unchecked metadata, assume
+ * that the scrub type checker depends on something that couldn't be
+ * fixed. Mark that type as corrupt so that phase 4 will try it again.
+ */
+ foreach_scrub_type(scrub_type) {
+ __u8 *state = aitem->sri.sri_state;
+
+ if (state[scrub_type] & SCRUB_ITEM_NEEDSCHECK) {
+ state[scrub_type] &= ~SCRUB_ITEM_NEEDSCHECK;
+ state[scrub_type] |= SCRUB_ITEM_CORRUPT;
+ }
+ }
+
*aitemp = aitem;
return 0;
}
^ permalink raw reply related [flat|nested] 296+ messages in thread
* [PATCH 1/1] xfs_repair: allow symlinks with short remote targets
2024-07-30 0:22 ` [PATCHSET v30.9 23/23] xfs_repair: fixes for kernel 6.10 Darrick J. Wong
@ 2024-07-30 1:32 ` Darrick J. Wong
0 siblings, 0 replies; 296+ messages in thread
From: Darrick J. Wong @ 2024-07-30 1:32 UTC (permalink / raw)
To: djwong, cem; +Cc: Christoph Hellwig, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Symbolic links can have extended attributes. If the attr fork consumes
enough space in the inode record, a shortform symlink can become a
remote symlink. However, if we delete those extended attributes, the
target is not moved back into the inode core.
IOWs, we can end up with a symlink inode that looks like this:
core.magic = 0x494e
core.mode = 0120777
core.version = 3
core.format = 2 (extents)
core.nlinkv2 = 1
core.nextents = 1
core.size = 297
core.nblocks = 1
core.naextents = 0
core.forkoff = 0
core.aformat = 2 (extents)
u3.bmx[0] = [startoff,startblock,blockcount,extentflag]
0:[0,12,1,0]
This is a symbolic link with a 297-byte target stored in a disk block,
which is to say this is a symlink with a remote target. The forkoff is
0, which is to say that there's 512 - 176 == 336 bytes in the inode core
to store the data fork.
Prior to kernel commit 1eb70f54c445f, the kernel was ok with this
arrangement, but the change to symlink validation in that patch now
produces corruption errors on filesystems written by older kernels that
are not otherwise inconsistent. Those changes were inspired by reports
of illegal memory accesses, which I think were a result of making data
fork access decisions based on symlink di_size and not on di_format.
Unfortunately, for a very long time xfs_repair has flagged these inodes
as being corrupt, even though the kernel has historically been willing
to read and write symlinks with these properties. Resolve the conflict
by adjusting the xfs_repair corruption tests to allow extents format.
This change matches the kernel patch "xfs: allow symlinks with short
remote targets".
While we're at it, fix a lurking bad symlink fork access.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
repair/dinode.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/repair/dinode.c b/repair/dinode.c
index 168cbf484..e36de9bf1 100644
--- a/repair/dinode.c
+++ b/repair/dinode.c
@@ -1036,7 +1036,8 @@ process_symlink_extlist(
int max_blocks;
if (be64_to_cpu(dino->di_size) <= XFS_DFORK_DSIZE(dino, mp)) {
- if (dino->di_format == XFS_DINODE_FMT_LOCAL)
+ if (dino->di_format == XFS_DINODE_FMT_LOCAL ||
+ dino->di_format == XFS_DINODE_FMT_EXTENTS)
return 0;
do_warn(
_("mismatch between format (%d) and size (%" PRId64 ") in symlink ino %" PRIu64 "\n"),
@@ -1368,7 +1369,7 @@ process_symlink(
* get symlink contents into data area
*/
symlink = &data[0];
- if (be64_to_cpu(dino->di_size) <= XFS_DFORK_DSIZE(dino, mp)) {
+ if (dino->di_format == XFS_DINODE_FMT_LOCAL) {
/*
* local symlink, just copy the symlink out of the
* inode into the data area
^ permalink raw reply related [flat|nested] 296+ messages in thread
end of thread, other threads:[~2024-07-30 1:32 UTC | newest]
Thread overview: 296+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-30 0:10 [PATCHBOMB v2] xfsprogs: catch us up to 6.10 Darrick J. Wong
2024-07-30 0:16 ` [PATCHSET 01/23] libxfs: fixes for 6.9 Darrick J. Wong
2024-07-30 0:22 ` [PATCH 1/5] [PATCH v3] Remove support for split-/usr installs Darrick J. Wong
2024-07-30 0:22 ` [PATCH 2/5] repair: btree blocks are never on the RT subvolume Darrick J. Wong
2024-07-30 0:23 ` [PATCH 3/5] xfile: fix missing error unlock in xfile_fcb_find Darrick J. Wong
2024-07-30 0:23 ` [PATCH 4/5] xfs_repair: don't leak the rootdir inode when orphanage already exists Darrick J. Wong
2024-07-30 0:23 ` [PATCH 5/5] xfs_repair: don't crash on -vv Darrick J. Wong
2024-07-30 0:16 ` [PATCHSET 02/23] libxfs: sync with 6.10 Darrick J. Wong
2024-07-30 0:24 ` [PATCH 001/115] xfs: pass xfs_buf lookup flags to xfs_*read_agi Darrick J. Wong
2024-07-30 0:24 ` [PATCH 002/115] xfs: constify xfs_bmap_is_written_extent Darrick J. Wong
2024-07-30 0:24 ` [PATCH 003/115] xfs: introduce new file range exchange ioctl Darrick J. Wong
2024-07-30 0:24 ` [PATCH 004/115] xfs: create a incompat flag for atomic file mapping exchanges Darrick J. Wong
2024-07-30 0:25 ` [PATCH 005/115] xfs: introduce a file mapping exchange log intent item Darrick J. Wong
2024-07-30 0:25 ` [PATCH 006/115] xfs: create deferred log items for file mapping exchanges Darrick J. Wong
2024-07-30 0:25 ` [PATCH 007/115] xfs: add error injection to test file mapping exchange recovery Darrick J. Wong
2024-07-30 0:25 ` [PATCH 008/115] xfs: condense extended attributes after a mapping exchange operation Darrick J. Wong
2024-07-30 0:26 ` [PATCH 009/115] xfs: condense directories " Darrick J. Wong
2024-07-30 0:26 ` [PATCH 010/115] xfs: condense symbolic links " Darrick J. Wong
2024-07-30 0:26 ` [PATCH 011/115] xfs: make file range exchange support realtime files Darrick J. Wong
2024-07-30 0:26 ` [PATCH 012/115] xfs: capture inode generation numbers in the ondisk exchmaps log item Darrick J. Wong
2024-07-30 0:27 ` [PATCH 013/115] xfs: enable logged file mapping exchange feature Darrick J. Wong
2024-07-30 0:27 ` [PATCH 014/115] xfs: add an explicit owner field to xfs_da_args Darrick J. Wong
2024-07-30 0:27 ` [PATCH 015/115] xfs: use the xfs_da_args owner field to set new dir/attr block owner Darrick J. Wong
2024-07-30 0:27 ` [PATCH 016/115] xfs: validate attr leaf buffer owners Darrick J. Wong
2024-07-30 0:28 ` [PATCH 017/115] xfs: validate attr remote value " Darrick J. Wong
2024-07-30 0:28 ` [PATCH 018/115] xfs: validate dabtree node " Darrick J. Wong
2024-07-30 0:28 ` [PATCH 019/115] xfs: validate directory leaf " Darrick J. Wong
2024-07-30 0:28 ` [PATCH 020/115] xfs: validate explicit directory data " Darrick J. Wong
2024-07-30 0:29 ` [PATCH 021/115] xfs: validate explicit directory block " Darrick J. Wong
2024-07-30 0:29 ` [PATCH 022/115] xfs: validate explicit directory free block owners Darrick J. Wong
2024-07-30 0:29 ` [PATCH 023/115] xfs: use atomic extent swapping to fix user file fork data Darrick J. Wong
2024-07-30 0:30 ` [PATCH 024/115] xfs: repair extended attributes Darrick J. Wong
2024-07-30 0:30 ` [PATCH 025/115] xfs: expose xfs_bmap_local_to_extents for online repair Darrick J. Wong
2024-07-30 0:30 ` [PATCH 026/115] xfs: pass the owner to xfs_symlink_write_target Darrick J. Wong
2024-07-30 0:30 ` [PATCH 027/115] xfs: check unused nlink fields in the ondisk inode Darrick J. Wong
2024-07-30 0:31 ` [PATCH 028/115] xfs: try to avoid allocating from sick inode clusters Darrick J. Wong
2024-07-30 0:31 ` [PATCH 029/115] xfs: pin inodes that would otherwise overflow link count Darrick J. Wong
2024-07-30 0:31 ` [PATCH 030/115] xfs: Increase XFS_DEFER_OPS_NR_INODES to 5 Darrick J. Wong
2024-07-30 0:31 ` [PATCH 031/115] xfs: make XFS_TRANS_LOWMODE match the other XFS_TRANS_ definitions Darrick J. Wong
2024-07-30 0:32 ` [PATCH 032/115] xfs: refactor realtime inode locking Darrick J. Wong
2024-07-30 0:32 ` [PATCH 033/115] xfs: free RT extents after updating the bmap btree Darrick J. Wong
2024-07-30 0:32 ` [PATCH 034/115] xfs: move RT inode locking out of __xfs_bunmapi Darrick J. Wong
2024-07-30 0:32 ` [PATCH 035/115] xfs: split xfs_mod_freecounter Darrick J. Wong
2024-07-30 0:33 ` [PATCH 036/115] xfs: reinstate RT support in xfs_bmapi_reserve_delalloc Darrick J. Wong
2024-07-30 0:33 ` [PATCH 037/115] xfs: cleanup fdblock/frextent accounting in xfs_bmap_del_extent_delay Darrick J. Wong
2024-07-30 0:33 ` [PATCH 038/115] xfs: support RT inodes in xfs_mod_delalloc Darrick J. Wong
2024-07-30 0:33 ` [PATCH 039/115] xfs: rework splitting of indirect block reservations Darrick J. Wong
2024-07-30 0:34 ` [PATCH 040/115] xfs: stop the steal (of data blocks for RT indirect blocks) Darrick J. Wong
2024-07-30 0:34 ` [PATCH 041/115] xfs: remove XFS_DA_OP_REMOVE Darrick J. Wong
2024-07-30 0:34 ` [PATCH 042/115] xfs: remove XFS_DA_OP_NOTIME Darrick J. Wong
2024-07-30 0:34 ` [PATCH 043/115] xfs: remove xfs_da_args.attr_flags Darrick J. Wong
2024-07-30 0:35 ` [PATCH 044/115] xfs: make attr removal an explicit operation Darrick J. Wong
2024-07-30 0:35 ` [PATCH 045/115] xfs: rearrange xfs_da_args a bit to use less space Darrick J. Wong
2024-07-30 0:35 ` [PATCH 046/115] xfs: attr fork iext must be loaded before calling xfs_attr_is_leaf Darrick J. Wong
2024-07-30 0:36 ` [PATCH 047/115] xfs: fix missing check for invalid attr flags Darrick J. Wong
2024-07-30 0:36 ` [PATCH 048/115] xfs: restructure xfs_attr_complete_op a bit Darrick J. Wong
2024-07-30 0:36 ` [PATCH 049/115] xfs: use helpers to extract xattr op from opflags Darrick J. Wong
2024-07-30 0:36 ` [PATCH 050/115] xfs: enforce one namespace per attribute Darrick J. Wong
2024-07-30 0:37 ` [PATCH 051/115] xfs: rearrange xfs_attr_match parameters Darrick J. Wong
2024-07-30 0:37 ` [PATCH 052/115] xfs: check the flags earlier in xfs_attr_match Darrick J. Wong
2024-07-30 0:37 ` [PATCH 053/115] xfs: move xfs_attr_defer_add to xfs_attr_item.c Darrick J. Wong
2024-07-30 0:37 ` [PATCH 054/115] xfs: create a separate hashname function for extended attributes Darrick J. Wong
2024-07-30 0:38 ` [PATCH 055/115] xfs: add parent pointer support to attribute code Darrick J. Wong
2024-07-30 0:38 ` [PATCH 056/115] xfs: define parent pointer ondisk extended attribute format Darrick J. Wong
2024-07-30 0:38 ` [PATCH 057/115] xfs: allow xattr matching on name and value for parent pointers Darrick J. Wong
2024-07-30 0:38 ` [PATCH 058/115] xfs: create attr log item opcodes and formats " Darrick J. Wong
2024-07-30 0:39 ` [PATCH 059/115] xfs: record inode generation in xattr update log intent items Darrick J. Wong
2024-07-30 0:39 ` [PATCH 060/115] xfs: add parent pointer validator functions Darrick J. Wong
2024-07-30 0:39 ` [PATCH 061/115] xfs: extend transaction reservations for parent attributes Darrick J. Wong
2024-07-30 0:39 ` [PATCH 062/115] xfs: create a hashname function for parent pointers Darrick J. Wong
2024-07-30 0:40 ` [PATCH 063/115] xfs: parent pointer attribute creation Darrick J. Wong
2024-07-30 0:40 ` [PATCH 064/115] xfs: add parent attributes to link Darrick J. Wong
2024-07-30 0:40 ` [PATCH 065/115] xfs: add parent attributes to symlink Darrick J. Wong
2024-07-30 0:40 ` [PATCH 066/115] xfs: remove parent pointers in unlink Darrick J. Wong
2024-07-30 0:41 ` [PATCH 067/115] xfs: Add parent pointers to rename Darrick J. Wong
2024-07-30 0:41 ` [PATCH 068/115] xfs: don't return XFS_ATTR_PARENT attributes via listxattr Darrick J. Wong
2024-07-30 0:41 ` [PATCH 069/115] xfs: pass the attr value to put_listent when possible Darrick J. Wong
2024-07-30 0:42 ` [PATCH 070/115] xfs: split out handle management helpers a bit Darrick J. Wong
2024-07-30 0:42 ` [PATCH 071/115] xfs: add parent pointer ioctls Darrick J. Wong
2024-07-30 0:42 ` [PATCH 072/115] xfs: don't remove the attr fork when parent pointers are enabled Darrick J. Wong
2024-07-30 0:42 ` [PATCH 073/115] xfs: add a incompat feature bit for parent pointers Darrick J. Wong
2024-07-30 0:43 ` [PATCH 074/115] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res Darrick J. Wong
2024-07-30 0:43 ` [PATCH 075/115] xfs: drop compatibility minimum log size computations for reflink Darrick J. Wong
2024-07-30 0:43 ` [PATCH 076/115] xfs: enable parent pointers Darrick J. Wong
2024-07-30 0:43 ` [PATCH 077/115] xfs: check dirents have " Darrick J. Wong
2024-07-30 0:44 ` [PATCH 078/115] xfs: remove some boilerplate from xfs_attr_set Darrick J. Wong
2024-07-30 0:44 ` [PATCH 079/115] xfs: make the reserved block permission flag explicit in xfs_attr_set Darrick J. Wong
2024-07-30 0:44 ` [PATCH 080/115] xfs: add raw parent pointer apis to support repair Darrick J. Wong
2024-07-30 0:44 ` [PATCH 081/115] xfs: remove pointless unlocked assertion Darrick J. Wong
2024-07-30 0:45 ` [PATCH 082/115] xfs: split xfs_bmap_add_attrfork into two pieces Darrick J. Wong
2024-07-30 0:45 ` [PATCH 083/115] xfs: actually rebuild the parent pointer xattrs Darrick J. Wong
2024-07-30 0:45 ` [PATCH 084/115] xfs: teach online scrub to find directory tree structure problems Darrick J. Wong
2024-07-30 0:45 ` [PATCH 085/115] xfs: report directory tree corruption in the health information Darrick J. Wong
2024-07-30 0:46 ` [PATCH 086/115] xfs: introduce vectored scrub mode Darrick J. Wong
2024-07-30 0:46 ` [PATCH 087/115] xfs: factor out a xfs_dir_lookup_args helper Darrick J. Wong
2024-07-30 0:46 ` [PATCH 088/115] xfs: factor out a xfs_dir_createname_args helper Darrick J. Wong
2024-07-30 0:46 ` [PATCH 089/115] xfs: factor out a xfs_dir_removename_args helper Darrick J. Wong
2024-07-30 0:47 ` [PATCH 090/115] xfs: factor out a xfs_dir_replace_args helper Darrick J. Wong
2024-07-30 0:47 ` [PATCH 091/115] xfs: refactor dir format helpers Darrick J. Wong
2024-07-30 0:47 ` [PATCH 092/115] xfs: make the seq argument to xfs_bmapi_convert_delalloc() optional Darrick J. Wong
2024-07-30 0:48 ` [PATCH 093/115] xfs: make xfs_bmapi_convert_delalloc() to allocate the target offset Darrick J. Wong
2024-07-30 0:48 ` [PATCH 094/115] xfs: fix error returns from xfs_bmapi_write Darrick J. Wong
2024-07-30 0:48 ` [PATCH 095/115] xfs: remove the unusued tmp_logflags variable in xfs_bmapi_allocate Darrick J. Wong
2024-07-30 0:48 ` [PATCH 096/115] xfs: lift a xfs_valid_startblock into xfs_bmapi_allocate Darrick J. Wong
2024-07-30 0:49 ` [PATCH 097/115] xfs: don't open code XFS_FILBLKS_MIN in xfs_bmapi_write Darrick J. Wong
2024-07-30 0:49 ` [PATCH 098/115] xfs: pass the actual offset and len to allocate to xfs_bmapi_allocate Darrick J. Wong
2024-07-30 0:49 ` [PATCH 099/115] xfs: remove the xfs_iext_peek_prev_extent call in xfs_bmapi_allocate Darrick J. Wong
2024-07-30 0:49 ` [PATCH 100/115] xfs: fix xfs_bmap_add_extent_delay_real for partial conversions Darrick J. Wong
2024-07-30 0:50 ` [PATCH 101/115] xfs: do not allocate the entire delalloc extent in xfs_bmapi_write Darrick J. Wong
2024-07-30 0:50 ` [PATCH 102/115] xfs: use unsigned ints for non-negative quantities in xfs_attr_remote.c Darrick J. Wong
2024-07-30 0:50 ` [PATCH 103/115] xfs: turn XFS_ATTR3_RMT_BUF_SPACE into a function Darrick J. Wong
2024-07-30 0:50 ` [PATCH 104/115] xfs: create a helper to compute the blockcount of a max sized remote value Darrick J. Wong
2024-07-30 0:51 ` [PATCH 105/115] xfs: minor cleanups of xfs_attr3_rmt_blocks Darrick J. Wong
2024-07-30 0:51 ` [PATCH 106/115] xfs: xfs_quota_unreserve_blkres can't fail Darrick J. Wong
2024-07-30 0:51 ` [PATCH 107/115] xfs: simplify iext overflow checking and upgrade Darrick J. Wong
2024-07-30 0:51 ` [PATCH 108/115] xfs: Stop using __maybe_unused in xfs_alloc.c Darrick J. Wong
2024-07-30 0:52 ` [PATCH 109/115] xfs: fix xfs_init_attr_trans not handling explicit operation codes Darrick J. Wong
2024-07-30 0:52 ` [PATCH 110/115] xfs: allow symlinks with short remote targets Darrick J. Wong
2024-07-30 0:52 ` [PATCH 111/115] xfs: Add cond_resched to block unmap range and reflink remap path Darrick J. Wong
2024-07-30 0:52 ` [PATCH 112/115] xfs: make sure sb_fdblocks is non-negative Darrick J. Wong
2024-07-30 0:53 ` [PATCH 113/115] xfs: restrict when we try to align cow fork delalloc to cowextsz hints Darrick J. Wong
2024-07-30 0:53 ` [PATCH 114/115] xfs: allow unlinked symlinks and dirs with zero size Darrick J. Wong
2024-07-30 0:53 ` [PATCH 115/115] xfs: fix direction in XFS_IOC_EXCHANGE_RANGE Darrick J. Wong
2024-07-30 0:17 ` [PATCHSET v30.9 03/23] xfsprogs: atomic file updates Darrick J. Wong
2024-07-30 0:54 ` [PATCH 01/12] man: document the exchange-range ioctl Darrick J. Wong
2024-07-30 0:54 ` [PATCH 02/12] man: document XFS_FSOP_GEOM_FLAGS_EXCHRANGE Darrick J. Wong
2024-07-30 0:54 ` [PATCH 03/12] libhandle: add support for bulkstat v5 Darrick J. Wong
2024-07-30 0:54 ` [PATCH 04/12] libfrog: add support for exchange range ioctl family Darrick J. Wong
2024-07-30 0:55 ` [PATCH 05/12] xfs_db: advertise exchange-range in the version command Darrick J. Wong
2024-07-30 0:55 ` [PATCH 06/12] xfs_logprint: support dumping exchmaps log items Darrick J. Wong
2024-07-30 0:55 ` [PATCH 07/12] xfs_fsr: convert to bulkstat v5 ioctls Darrick J. Wong
2024-07-30 0:55 ` [PATCH 08/12] xfs_fsr: skip the xattr/forkoff levering with the newer swapext implementations Darrick J. Wong
2024-07-30 0:56 ` [PATCH 09/12] xfs_io: create exchangerange command to test file range exchange ioctl Darrick J. Wong
2024-07-30 0:56 ` [PATCH 10/12] libfrog: advertise exchange-range support Darrick J. Wong
2024-07-30 0:56 ` [PATCH 11/12] xfs_repair: add exchange-range to file systems Darrick J. Wong
2024-07-30 0:56 ` [PATCH 12/12] mkfs: add a formatting option for exchange-range Darrick J. Wong
2024-07-30 0:17 ` [PATCHSET v30.9 04/23] xfsprogs: set and validate dir/attr block owners Darrick J. Wong
2024-07-30 0:57 ` [PATCH 1/1] xfs_{db,repair}: add an explicit owner field to xfs_da_args Darrick J. Wong
2024-07-30 0:17 ` [PATCHSET v30.9 05/23] xfsprogs: inode-related repair fixes Darrick J. Wong
2024-07-30 0:57 ` [PATCH 1/2] libxfs: port the bumplink function from the kernel Darrick J. Wong
2024-07-30 0:57 ` [PATCH 2/2] mkfs/repair: pin inodes that would otherwise overflow link count Darrick J. Wong
2024-07-30 0:18 ` [PATCHSET v30.9 06/23] xfs_scrub: fixes to the repair code Darrick J. Wong
2024-07-30 0:57 ` [PATCH 1/5] xfs_scrub: remove ALP_* flags namespace Darrick J. Wong
2024-07-30 0:58 ` [PATCH 2/5] xfs_scrub: move repair functions to repair.c Darrick J. Wong
2024-07-30 0:58 ` [PATCH 3/5] xfs_scrub: log when a repair was unnecessary Darrick J. Wong
2024-07-30 0:58 ` [PATCH 4/5] xfs_scrub: require primary superblock repairs to complete before proceeding Darrick J. Wong
2024-07-30 0:58 ` [PATCH 5/5] xfs_scrub: actually try to fix summary counters ahead of repairs Darrick J. Wong
2024-07-30 0:18 ` [PATCHSET v30.9 07/23] xfs_scrub: improve warnings about difficult repairs Darrick J. Wong
2024-07-30 0:59 ` [PATCH 1/8] xfs_scrub: fix missing scrub coverage for broken inodes Darrick J. Wong
2024-07-30 0:59 ` [PATCH 2/8] xfs_scrub: collapse trivial superblock scrub helpers Darrick J. Wong
2024-07-30 0:59 ` [PATCH 3/8] xfs_scrub: get rid of trivial fs metadata scanner helpers Darrick J. Wong
2024-07-30 1:00 ` [PATCH 4/8] xfs_scrub: split up the mustfix repairs and difficulty assessment functions Darrick J. Wong
2024-07-30 1:00 ` [PATCH 5/8] xfs_scrub: add missing repair types to the mustfix and difficulty assessment Darrick J. Wong
2024-07-30 1:00 ` [PATCH 6/8] xfs_scrub: any inconsistency in metadata should trigger difficulty warnings Darrick J. Wong
2024-07-30 1:00 ` [PATCH 7/8] xfs_scrub: warn about difficult repairs to rt and quota metadata Darrick J. Wong
2024-07-30 1:01 ` [PATCH 8/8] xfs_scrub: enable users to bump information messages to warnings Darrick J. Wong
2024-07-30 0:18 ` [PATCHSET v30.9 08/23] xfs_scrub: track data dependencies for repairs Darrick J. Wong
2024-07-30 1:01 ` [PATCH 1/9] xfs_scrub: track repair items by principal, not by individual repairs Darrick J. Wong
2024-07-30 1:01 ` [PATCH 2/9] xfs_scrub: use repair_item to direct repair activities Darrick J. Wong
2024-07-30 1:01 ` [PATCH 3/9] xfs_scrub: remove action lists from phaseX code Darrick J. Wong
2024-07-30 1:02 ` [PATCH 4/9] xfs_scrub: remove scrub_metadata_file Darrick J. Wong
2024-07-30 1:02 ` [PATCH 5/9] xfs_scrub: boost the repair priority of dependencies of damaged items Darrick J. Wong
2024-07-30 1:02 ` [PATCH 6/9] xfs_scrub: clean up repair_item_difficulty a little Darrick J. Wong
2024-07-30 1:02 ` [PATCH 7/9] xfs_scrub: check dependencies of a scrub type before repairing Darrick J. Wong
2024-07-30 1:03 ` [PATCH 8/9] xfs_scrub: retry incomplete repairs Darrick J. Wong
2024-07-30 1:03 ` [PATCH 9/9] xfs_scrub: remove unused action_list fields Darrick J. Wong
2024-07-30 0:18 ` [PATCHSET v30.9 09/23] xfs_scrub: use scrub_item to track check progress Darrick J. Wong
2024-07-30 1:03 ` [PATCH 1/5] xfs_scrub: start tracking scrub state in scrub_item Darrick J. Wong
2024-07-30 1:03 ` [PATCH 2/5] xfs_scrub: remove enum check_outcome Darrick J. Wong
2024-07-30 1:04 ` [PATCH 3/5] xfs_scrub: refactor scrub_meta_type out of existence Darrick J. Wong
2024-07-30 1:04 ` [PATCH 4/5] xfs_scrub: hoist repair retry loop to repair_item_class Darrick J. Wong
2024-07-30 1:04 ` [PATCH 5/5] xfs_scrub: hoist scrub retry loop to scrub_item_check_file Darrick J. Wong
2024-07-30 0:19 ` [PATCHSET v30.9 10/23] xfs_scrub: improve scheduling of repair items Darrick J. Wong
2024-07-30 1:05 ` [PATCH 1/4] libfrog: enhance ptvar to support initializer functions Darrick J. Wong
2024-07-30 1:05 ` [PATCH 2/4] xfs_scrub: improve thread scheduling repair items during phase 4 Darrick J. Wong
2024-07-30 1:05 ` [PATCH 3/4] xfs_scrub: recheck entire metadata objects after corruption repairs Darrick J. Wong
2024-07-30 1:05 ` [PATCH 4/4] xfs_scrub: try to repair space metadata before file metadata Darrick J. Wong
2024-07-30 0:19 ` [PATCHSET v30.9 11/23] xfs_scrub: detect deceptive filename extensions Darrick J. Wong
2024-07-30 1:06 ` [PATCH 01/13] xfs_scrub: use proper UChar string iterators Darrick J. Wong
2024-07-30 1:06 ` [PATCH 02/13] xfs_scrub: hoist code that removes ignorable characters Darrick J. Wong
2024-07-30 1:06 ` [PATCH 03/13] xfs_scrub: add a couple of omitted invisible code points Darrick J. Wong
2024-07-30 1:06 ` [PATCH 04/13] xfs_scrub: avoid potential UAF after freeing a duplicate name entry Darrick J. Wong
2024-07-30 1:07 ` [PATCH 05/13] xfs_scrub: guard against libicu returning negative buffer lengths Darrick J. Wong
2024-07-30 1:07 ` [PATCH 06/13] xfs_scrub: hoist non-rendering character predicate Darrick J. Wong
2024-07-30 1:07 ` [PATCH 07/13] xfs_scrub: store bad flags with the name entry Darrick J. Wong
2024-07-30 1:07 ` [PATCH 08/13] xfs_scrub: rename UNICRASH_ZERO_WIDTH to UNICRASH_INVISIBLE Darrick J. Wong
2024-07-30 1:08 ` [PATCH 09/13] xfs_scrub: type-coerce the UNICRASH_* flags Darrick J. Wong
2024-07-30 1:08 ` [PATCH 10/13] xfs_scrub: reduce size of struct name_entry Darrick J. Wong
2024-07-30 1:08 ` [PATCH 11/13] xfs_scrub: rename struct unicrash.normalizer Darrick J. Wong
2024-07-30 1:08 ` [PATCH 12/13] xfs_scrub: report deceptive file extensions Darrick J. Wong
2024-07-30 1:09 ` [PATCH 13/13] xfs_scrub: dump unicode points Darrick J. Wong
2024-07-30 0:19 ` [PATCHSET v30.9 12/23] xfs_scrub: move fstrim to a separate phase Darrick J. Wong
2024-07-30 1:09 ` [PATCH 1/7] xfs_scrub: move FITRIM to phase 8 Darrick J. Wong
2024-07-30 1:09 ` [PATCH 2/7] xfs_scrub: ignore phase 8 if the user disabled fstrim Darrick J. Wong
2024-07-30 1:09 ` [PATCH 3/7] xfs_scrub: collapse trim_filesystem Darrick J. Wong
2024-07-30 1:10 ` [PATCH 4/7] xfs_scrub: fix the work estimation for phase 8 Darrick J. Wong
2024-07-30 1:10 ` [PATCH 5/7] xfs_scrub: report FITRIM errors properly Darrick J. Wong
2024-07-30 1:10 ` [PATCH 6/7] xfs_scrub: don't call FITRIM after runtime errors Darrick J. Wong
2024-07-30 1:11 ` [PATCH 7/7] xfs_scrub: improve responsiveness while trimming the filesystem Darrick J. Wong
2024-07-30 0:19 ` [PATCHSET v30.9 13/23] xfs_scrub: use free space histograms to reduce fstrim runtime Darrick J. Wong
2024-07-30 1:11 ` [PATCH 1/7] libfrog: hoist free space histogram code Darrick J. Wong
2024-07-30 1:11 ` [PATCH 2/7] libfrog: print wider columns for free space histogram Darrick J. Wong
2024-07-30 1:11 ` [PATCH 3/7] libfrog: print cdf of free space buckets Darrick J. Wong
2024-07-30 1:12 ` [PATCH 4/7] xfs_scrub: don't close stdout when closing the progress bar Darrick J. Wong
2024-07-30 1:12 ` [PATCH 5/7] xfs_scrub: remove pointless spacemap.c arguments Darrick J. Wong
2024-07-30 1:12 ` [PATCH 6/7] xfs_scrub: collect free space histograms during phase 7 Darrick J. Wong
2024-07-30 1:12 ` [PATCH 7/7] xfs_scrub: tune fstrim minlen parameter based on free space histograms Darrick J. Wong
2024-07-30 0:20 ` [PATCHSET v30.9 14/23] xfs_scrub: tighten security of systemd services Darrick J. Wong
2024-07-30 1:13 ` [PATCH 1/6] xfs_scrub: allow auxiliary pathnames for sandboxing Darrick J. Wong
2024-07-30 1:13 ` [PATCH 2/6] xfs_scrub.service: reduce background CPU usage to less than one core if possible Darrick J. Wong
2024-07-30 1:13 ` [PATCH 3/6] xfs_scrub: use dynamic users when running as a systemd service Darrick J. Wong
2024-07-30 1:13 ` [PATCH 4/6] xfs_scrub: tighten up the security on the background " Darrick J. Wong
2024-07-30 1:14 ` [PATCH 5/6] xfs_scrub_fail: " Darrick J. Wong
2024-07-30 1:14 ` [PATCH 6/6] xfs_scrub_all: " Darrick J. Wong
2024-07-30 0:20 ` [PATCHSET v30.9 15/23] xfs_scrub_all: automatic media scan service Darrick J. Wong
2024-07-30 1:14 ` [PATCH 1/6] xfs_scrub_all: only use the xfs_scrub@ systemd services in service mode Darrick J. Wong
2024-07-30 1:14 ` [PATCH 2/6] xfs_scrub_all: remove journalctl background process Darrick J. Wong
2024-07-30 1:15 ` [PATCH 3/6] xfs_scrub_all: support metadata+media scans of all filesystems Darrick J. Wong
2024-07-30 1:15 ` [PATCH 4/6] xfs_scrub_all: enable periodic file data scrubs automatically Darrick J. Wong
2024-07-30 1:15 ` [PATCH 5/6] xfs_scrub_all: trigger automatic media scans once per month Darrick J. Wong
2024-07-30 1:15 ` [PATCH 6/6] xfs_scrub_all: failure reporting for the xfs_scrub_all job Darrick J. Wong
2024-07-30 0:20 ` [PATCHSET v30.9 16/23] xfs_scrub_all: improve systemd handling Darrick J. Wong
2024-07-30 1:16 ` [PATCH 1/5] xfs_scrub_all: encapsulate all the subprocess code in an object Darrick J. Wong
2024-07-30 1:16 ` [PATCH 2/5] xfs_scrub_all: encapsulate all the systemctl " Darrick J. Wong
2024-07-30 1:16 ` [PATCH 3/5] xfs_scrub_all: add CLI option for easier debugging Darrick J. Wong
2024-07-30 1:17 ` [PATCH 4/5] xfs_scrub_all: convert systemctl calls to dbus Darrick J. Wong
2024-07-30 1:17 ` [PATCH 5/5] xfs_scrub_all: implement retry and backoff for dbus calls Darrick J. Wong
2024-07-30 0:20 ` [PATCHSET v13.8 17/23] xfsprogs: improve extended attribute validation Darrick J. Wong
2024-07-30 1:17 ` [PATCH 1/6] xfs_scrub_all: fail fast on masked units Darrick J. Wong
2024-07-30 1:17 ` [PATCH 2/6] xfs_scrub: automatic downgrades to dry-run mode in service mode Darrick J. Wong
2024-07-30 1:18 ` [PATCH 3/6] xfs_scrub: add an optimization-only mode Darrick J. Wong
2024-07-30 1:18 ` [PATCH 4/6] xfs_repair: check free space requirements before allowing upgrades Darrick J. Wong
2024-07-30 1:18 ` [PATCH 5/6] xfs_repair: enforce one namespace bit per extended attribute Darrick J. Wong
2024-07-30 1:18 ` [PATCH 6/6] xfs_repair: check for unknown flags in attr entries Darrick J. Wong
2024-07-30 0:21 ` [PATCHSET v13.8 18/23] xfsprogs: Parent Pointers Darrick J. Wong
2024-07-30 1:19 ` [PATCH 01/24] libxfs: create attr log item opcodes and formats for parent pointers Darrick J. Wong
2024-07-30 1:19 ` [PATCH 02/24] xfs_{db,repair}: implement new attr hash value function Darrick J. Wong
2024-07-30 1:19 ` [PATCH 03/24] xfs_logprint: dump new attr log item fields Darrick J. Wong
2024-07-30 1:19 ` [PATCH 04/24] man: document the XFS_IOC_GETPARENTS ioctl Darrick J. Wong
2024-07-30 1:20 ` [PATCH 05/24] libfrog: report parent pointers to userspace Darrick J. Wong
2024-07-30 1:20 ` [PATCH 06/24] libfrog: add parent pointer support code Darrick J. Wong
2024-07-30 1:20 ` [PATCH 07/24] xfs_io: adapt parent command to new parent pointer ioctls Darrick J. Wong
2024-07-30 1:20 ` [PATCH 08/24] xfs_io: Add i, n and f flags to parent command Darrick J. Wong
2024-07-30 1:21 ` [PATCH 09/24] xfs_logprint: decode parent pointers in ATTRI items fully Darrick J. Wong
2024-07-30 1:21 ` [PATCH 10/24] xfs_spaceman: report file paths Darrick J. Wong
2024-07-30 1:21 ` [PATCH 11/24] xfs_scrub: use parent pointers when possible to report file operations Darrick J. Wong
2024-07-30 1:21 ` [PATCH 12/24] xfs_scrub: use parent pointers to report lost file data Darrick J. Wong
2024-07-30 1:22 ` [PATCH 13/24] xfs_db: report parent pointers in version command Darrick J. Wong
2024-07-30 1:22 ` [PATCH 14/24] xfs_db: report parent bit on xattrs Darrick J. Wong
2024-07-30 1:22 ` [PATCH 15/24] xfs_db: report parent pointers embedded in xattrs Darrick J. Wong
2024-07-30 1:23 ` [PATCH 16/24] xfs_db: obfuscate dirent and parent pointer names consistently Darrick J. Wong
2024-07-30 1:23 ` [PATCH 17/24] libxfs: export attr3_leaf_hdr_from_disk via libxfs_api_defs.h Darrick J. Wong
2024-07-30 1:23 ` [PATCH 18/24] xfs_db: add a parents command to list the parents of a file Darrick J. Wong
2024-07-30 1:23 ` [PATCH 19/24] xfs_db: make attr_set and attr_remove handle parent pointers Darrick J. Wong
2024-07-30 1:24 ` [PATCH 20/24] xfs_db: add link and unlink expert commands Darrick J. Wong
2024-07-30 1:24 ` [PATCH 21/24] xfs_db: compute hashes of parent pointers Darrick J. Wong
2024-07-30 1:24 ` [PATCH 22/24] libxfs: create new files with attr forks if necessary Darrick J. Wong
2024-07-30 1:24 ` [PATCH 23/24] mkfs: Add parent pointers during protofile creation Darrick J. Wong
2024-07-30 1:25 ` [PATCH 24/24] mkfs: enable formatting with parent pointers Darrick J. Wong
2024-07-30 0:21 ` [PATCHSET v13.8 19/23] xfsprogs: scrubbing for " Darrick J. Wong
2024-07-30 1:25 ` [PATCH 1/2] xfs: create a blob array data structure Darrick J. Wong
2024-07-30 1:25 ` [PATCH 2/2] man2: update ioctl_xfs_scrub_metadata.2 for parent pointers Darrick J. Wong
2024-07-30 0:21 ` [PATCHSET v13.8 20/23] xfsprogs: offline repair " Darrick J. Wong
2024-07-30 1:25 ` [PATCH 01/12] xfs_db: remove some boilerplate from xfs_attr_set Darrick J. Wong
2024-07-30 1:26 ` [PATCH 02/12] xfs_db: actually report errors from libxfs_attr_set Darrick J. Wong
2024-07-30 1:26 ` [PATCH 03/12] xfs_repair: junk parent pointer attributes when filesystem doesn't support them Darrick J. Wong
2024-07-30 1:26 ` [PATCH 04/12] xfs_repair: add parent pointers when messing with /lost+found Darrick J. Wong
2024-07-30 1:26 ` [PATCH 05/12] xfs_repair: junk duplicate hashtab entries when processing sf dirents Darrick J. Wong
2024-07-30 1:27 ` [PATCH 06/12] xfs_repair: build a parent pointer index Darrick J. Wong
2024-07-30 1:27 ` [PATCH 07/12] xfs_repair: move the global dirent name store to a separate object Darrick J. Wong
2024-07-30 1:27 ` [PATCH 08/12] xfs_repair: deduplicate strings stored in string blob Darrick J. Wong
2024-07-30 1:27 ` [PATCH 09/12] xfs_repair: check parent pointers Darrick J. Wong
2024-07-30 1:28 ` [PATCH 10/12] xfs_repair: dump garbage parent pointer attributes Darrick J. Wong
2024-07-30 1:28 ` [PATCH 11/12] xfs_repair: update ondisk parent pointer records Darrick J. Wong
2024-07-30 1:28 ` [PATCH 12/12] xfs_repair: wipe ondisk parent pointers when there are none Darrick J. Wong
2024-07-30 0:21 ` [PATCHSET v13.8 21/23] xfsprogs: detect and correct directory tree problems Darrick J. Wong
2024-07-30 1:29 ` [PATCH 1/5] libfrog: add directory tree structure scrubber to scrub library Darrick J. Wong
2024-07-30 1:29 ` [PATCH 2/5] xfs_spaceman: report directory tree corruption in the health information Darrick J. Wong
2024-07-30 1:29 ` [PATCH 3/5] xfs_scrub: fix erroring out of check_inode_names Darrick J. Wong
2024-07-30 1:29 ` [PATCH 4/5] xfs_scrub: detect and repair directory tree corruptions Darrick J. Wong
2024-07-30 1:30 ` [PATCH 5/5] xfs_scrub: defer phase5 file scans if dirloop fails Darrick J. Wong
2024-07-30 0:22 ` [PATCHSET v30.9 22/23] xfs_scrub: vectorize kernel calls Darrick J. Wong
2024-07-30 1:30 ` [PATCH 01/10] man: document vectored scrub mode Darrick J. Wong
2024-07-30 1:30 ` [PATCH 02/10] libfrog: support vectored scrub Darrick J. Wong
2024-07-30 1:30 ` [PATCH 03/10] xfs_io: " Darrick J. Wong
2024-07-30 1:31 ` [PATCH 04/10] xfs_scrub: split the scrub epilogue code into a separate function Darrick J. Wong
2024-07-30 1:31 ` [PATCH 05/10] xfs_scrub: split the repair " Darrick J. Wong
2024-07-30 1:31 ` [PATCH 06/10] xfs_scrub: convert scrub and repair epilogues to use xfs_scrub_vec Darrick J. Wong
2024-07-30 1:31 ` [PATCH 07/10] xfs_scrub: vectorize scrub calls Darrick J. Wong
2024-07-30 1:32 ` [PATCH 08/10] xfs_scrub: vectorize repair calls Darrick J. Wong
2024-07-30 1:32 ` [PATCH 09/10] xfs_scrub: use scrub barriers to reduce kernel calls Darrick J. Wong
2024-07-30 1:32 ` [PATCH 10/10] xfs_scrub: try spot repairs of metadata items to make scrub progress Darrick J. Wong
2024-07-30 0:22 ` [PATCHSET v30.9 23/23] xfs_repair: fixes for kernel 6.10 Darrick J. Wong
2024-07-30 1:32 ` [PATCH 1/1] xfs_repair: allow symlinks with short remote targets Darrick J. Wong
-- strict thread matches above, loose matches on Subject: below --
2024-07-02 0:52 [PATCHSET v13.7 11/16] xfsprogs: Parent Pointers Darrick J. Wong
2024-07-02 1:10 ` [PATCH 01/24] libxfs: create attr log item opcodes and formats for parent pointers Darrick J. Wong
2024-07-02 6:24 ` Christoph Hellwig
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).