From: Darrick J. Wong Date: Thu, 5 Mar 2026 18:47:54 +0000 (-0800) Subject: xfs_healer: use statmount to find moved filesystems even faster X-Git-Tag: v7.0.0~38 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=327f964dd6f07acdac88a41271cdaabbdbc15673;p=xfsprogs-dev.git xfs_healer: use statmount to find moved filesystems even faster As noted in the previous patch, it's possible that a mounted filesystem can move mountpoints between the time of the initial mount (at which point xfs_healer starts) and when it actually wants to start a repair. The previous patch fixed that problem by using getmntent to walk /proc/self/mounts to see if it finds a mount with the same "source" name, aka data device. However, this is really slow if there are a lot of filesystems because we end up wading through a lot of irrelevant information. However, statmount() can help us here because as of Linux 7.0 we can open the passed-in path at startup, call statmount() on it to retrieve the mnt_id, and then call it again later with that same mnt_id to find the mountpoint. Luckily xfs_healthmon didn't get merged until 7.0 so it's more or less guaranteed to be there if XFS_IOC_HEALTH_MONITOR succeeds. Obviously if this doesn't work, we can fall back to the slow walk. This statmount code enables xfs_healer to find a filesystem that has had its mountpoint moved to a different place in the directory tree without the use of bind mounts and without needing to walk the entire mount list: # mount -t tmpfs urk /mnt # mount --make-rprivate /mnt # mkdir -p /mnt/a /mnt/b # mount /dev/sda /mnt/a # mount --move /mnt/a /mnt/b The key here is that the struct mount object is moved, and no new ones are created. Therefore, the original mnt_id is still usable. Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig --- diff --git a/healer/weakhandle.c b/healer/weakhandle.c index 5df52075..358c553f 100644 --- a/healer/weakhandle.c +++ b/healer/weakhandle.c @@ -14,6 +14,7 @@ #include "libfrog/getparents.h" #include "libfrog/paths.h" #include "libfrog/systemd.h" +#include "libfrog/statmount.h" #include "xfs_healer.h" struct weakhandle { @@ -23,6 +24,9 @@ struct weakhandle { /* Shared reference to the getmntent fsname for reconnecting */ const char *fsname; + /* Mount id for faster reconnecting */ + uint64_t mnt_id; + /* handle to root dir */ void *hanp; size_t hlen; @@ -33,6 +37,7 @@ int weakhandle_alloc( int fd, const char *mountpoint, + uint64_t mnt_id, const char *fsname, struct weakhandle **whp) { @@ -51,6 +56,7 @@ weakhandle_alloc( return -1; wh->mntpoint = mountpoint; + wh->mnt_id = mnt_id; wh->fsname = fsname; ret = fd_to_handle(fd, &wh->hanp, &wh->hlen); @@ -112,6 +118,9 @@ weakhandle_reopen( struct weakhandle *wh, int *fd) { + const size_t smbuf_size = + libfrog_statmount_sizeof(PATH_MAX); + struct statmount *smbuf = alloca(smbuf_size); FILE *mtab; struct mntent *mnt; int ret; @@ -121,6 +130,21 @@ weakhandle_reopen( if (!ret) return 0; + /* + * The original mountpoint didn't work, which means the mount might + * have been moved. Look up the mountpoint for the mount id that we + * captured earlier, which is a quick lookup if there are many mounts. + * Note that @ret is nonzero here. + */ + ret = libfrog_statmount(wh->mnt_id, DEFAULT_MOUNTNS_FD, + STATMOUNT_MNT_POINT, smbuf, smbuf_size); + if (ret || !(smbuf->mask & STATMOUNT_MNT_POINT)) + goto fallback; + ret = weakhandle_reopen_from(wh, smbuf->str + smbuf->mnt_point, fd); + if (!ret) + return 0; + +fallback: /* * That didn't work, so now walk /proc/mounts to find a mount with the * same fsname (aka xfs data device path) as when we started. diff --git a/healer/xfs_healer.c b/healer/xfs_healer.c index d885eb0a..36e52a71 100644 --- a/healer/xfs_healer.c +++ b/healer/xfs_healer.c @@ -15,6 +15,7 @@ #include "libfrog/workqueue.h" #include "libfrog/systemd.h" #include "libfrog/fsproperties.h" +#include "libfrog/statmount.h" #include "xfs_healer.h" /* Program name; needed for libfrog error reports. */ @@ -169,11 +170,45 @@ try_capture_fsinfo( { struct mntent *mnt; FILE *mtp; - char rpath[PATH_MAX], rmnt_dir[PATH_MAX]; + const size_t smbuf_size = + libfrog_statmount_sizeof(PATH_MAX + 128); + struct statmount *smbuf = alloca(smbuf_size); + char *rmnt_dir = smbuf->str; + char rpath[PATH_MAX]; + int ret; if (!realpath(ctx->mntpoint, rpath)) return -1; + /* + * In Linux 7.0 we can do statmount on an open file, which means that + * we can capture the mnt_id, mount point, and fsname, which can help + * us find a mount --move'd elsewhere in the directory tree. + */ + ret = libfrog_fstatmount(ctx->mnt.fd, STATMOUNT_MNT_POINT, smbuf, + smbuf_size); + if (ret || !(smbuf->mask & STATMOUNT_MNT_POINT)) + goto fallback; + if (strcmp(rpath, smbuf->str + smbuf->mnt_point)) + goto fallback; + + ret = libfrog_fstatmount(ctx->mnt.fd, + STATMOUNT_SB_SOURCE | STATMOUNT_MNT_BASIC, + smbuf, smbuf_size); + if (ret || !(smbuf->mask & STATMOUNT_SB_SOURCE)) + goto fallback; + + ctx->fsname = strdup(smbuf->str + smbuf->sb_source); + if (!ctx->fsname) + return -1; + ctx->mnt_id = smbuf->mnt_id; + return 0; + +fallback: + /* + * If statmount isn't available for whatever reason, fall back to + * walking the mount table via getmntent. + */ mtp = setmntent(_PATH_PROC_MOUNTS, "r"); if (mtp == NULL) return -1; @@ -350,7 +385,7 @@ setup_monitor( * paths for logging. */ if (ctx->want_repair || healer_has_parent(ctx)) { - ret = weakhandle_alloc(ctx->mnt.fd, ctx->mntpoint, + ret = weakhandle_alloc(ctx->mnt.fd, ctx->mntpoint, ctx->mnt_id, ctx->fsname, &ctx->wh); if (ret) { fprintf(stderr, "%s: %s: %s\n", ctx->mntpoint, diff --git a/healer/xfs_healer.h b/healer/xfs_healer.h index e1370323..96e146f2 100644 --- a/healer/xfs_healer.h +++ b/healer/xfs_healer.h @@ -39,6 +39,9 @@ struct healer_ctx { /* Shared reference to the getmntent fsname for reconnecting */ const char *fsname; + /* Mount id for faster reconnecting */ + uint64_t mnt_id; + /* weak file handle so we can reattach to filesystem */ struct weakhandle *wh; @@ -75,8 +78,8 @@ bool healer_can_repair(struct healer_ctx *ctx); void run_full_repair(struct healer_ctx *ctx); /* weakhandle.c */ -int weakhandle_alloc(int fd, const char *mountpoint, const char *fsname, - struct weakhandle **whp); +int weakhandle_alloc(int fd, const char *mountpoint, uint64_t mnt_id, + const char *fsname, struct weakhandle **whp); int weakhandle_reopen(struct weakhandle *wh, int *fd); void weakhandle_free(struct weakhandle **whp); int weakhandle_getpath_for(struct weakhandle *wh, uint64_t ino, uint32_t gen,