[PATCH 14/13] xfs: make XFS_IOC_COMMIT_RANGE freshness data opaque

Darrick J. Wong
2024-02-27T17:46:49Z

From: Darrick J. Wong <djwong@kernel.org>

To head off bikeshedding about the fields in xfs_commit_range, let's
make it an opaque u64 array and require the userspace program to call
a third ioctl to sample the freshness data for us. If we ever converge
on a definition for i_version then we can use that; for now we'll just
use mtime/ctime like the old swapext ioctl.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_fs.h | 13 +++--------
fs/xfs/xfs_exchrange.c | 15 ++++++++++++
fs/xfs/xfs_exchrange.h | 1 +
fs/xfs/xfs_ioctl.c | 58 +++++++++++++++++++++++++++++++++++++++++++-----
4 files changed, 72 insertions(+), 15 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 01b3553adfc55..4019a78ee3ea5 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -860,14 +860,8 @@ struct xfs_commit_range {

__u64 flags; /* see XFS_EXCHRANGE_* below */

- /* file2 metadata for freshness checks */
- __u64 file2_ino; /* inode number */
- __s64 file2_mtime; /* modification time */
- __s64 file2_ctime; /* change time */
- __s32 file2_mtime_nsec; /* mod time, nsec */
- __s32 file2_ctime_nsec; /* change time, nsec */
-
- __u64 pad; /* must be zeroes */
+ /* opaque file2 metadata for freshness checks */
+ __u64 file2_freshness[5];
};

/*
@@ -973,7 +967,8 @@ struct xfs_commit_range {
#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_exch_range)
-#define XFS_IOC_COMMIT_RANGE _IOWR('X', 129, struct xfs_commit_range)
+#define XFS_IOC_START_COMMIT _IOWR('X', 130, struct xfs_commit_range)
+#define XFS_IOC_COMMIT_RANGE _IOWR('X', 131, struct xfs_commit_range)
/* XFS_IOC_GETFSUUID ---------- deprecated 140 */

diff --git a/fs/xfs/xfs_exchrange.c b/fs/xfs/xfs_exchrange.c
index e55ae06f1a32c..dae855515c3c4 100644
--- a/fs/xfs/xfs_exchrange.c
+++ b/fs/xfs/xfs_exchrange.c
@@ -863,3 +863,18 @@ xfs_exchange_range(
fsnotify_modify(fxr->file2);
return 0;
}
+
+/* Sample freshness data from fxr->file2 for a commit range operation. */
+void
+xfs_exchrange_freshness(
+ struct xfs_exchrange *fxr)
+{
+ struct inode *inode2 = file_inode(fxr->file2);
+ struct xfs_inode *ip2 = XFS_I(inode2);
+
+ xfs_ilock(ip2, XFS_IOLOCK_SHARED | XFS_MMAPLOCK_SHARED | XFS_ILOCK_SHARED);
+ fxr->file2_ino = ip2->i_ino;
+ fxr->file2_ctime = inode_get_ctime(inode2);
+ fxr->file2_mtime = inode_get_mtime(inode2);
+ xfs_iunlock(ip2, XFS_IOLOCK_SHARED | XFS_MMAPLOCK_SHARED | XFS_ILOCK_SHARED);
+}
diff --git a/fs/xfs/xfs_exchrange.h b/fs/xfs/xfs_exchrange.h
index 2dd9ab7d76828..942283a7f75f5 100644
--- a/fs/xfs/xfs_exchrange.h
+++ b/fs/xfs/xfs_exchrange.h
@@ -36,6 +36,7 @@ struct xfs_exchrange {
struct timespec64 file2_ctime;
};

+void xfs_exchrange_freshness(struct xfs_exchrange *fxr);
int xfs_exchange_range(struct xfs_exchrange *fxr);

/* XFS-specific parts of file exchanges */
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index ee26ac2028da1..1940da72a1da7 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -2402,6 +2402,47 @@ xfs_ioc_exchange_range(
return error;
}

+/* Opaque freshness blob for XFS_IOC_COMMIT_RANGE */
+struct xfs_commit_range_fresh {
+ __u64 file2_ino; /* inode number */
+ __s64 file2_mtime; /* modification time */
+ __s64 file2_ctime; /* change time */
+ __s32 file2_mtime_nsec; /* mod time, nsec */
+ __s32 file2_ctime_nsec; /* change time, nsec */
+ __u64 pad; /* zero */
+};
+
+static long
+xfs_ioc_start_commit(
+ struct file *file,
+ struct xfs_commit_range __user *argp)
+{
+ struct xfs_exchrange fxr = {
+ .file2 = file,
+ };
+ struct xfs_commit_range args;
+ struct xfs_commit_range_fresh *kern_f;
+ struct xfs_commit_range_fresh __user *user_f;
+
+ BUILD_BUG_ON(sizeof(struct xfs_commit_range_fresh) !=
+ sizeof(args.file2_freshness));
+
+ xfs_exchrange_freshness(&fxr);
+
+ kern_f = (struct xfs_commit_range_fresh *)&args.file2_freshness;
+ kern_f->file2_ino = fxr.file2_ino;
+ kern_f->file2_mtime = fxr.file2_mtime.tv_sec;
+ kern_f->file2_mtime_nsec = fxr.file2_mtime.tv_nsec;
+ kern_f->file2_ctime = fxr.file2_ctime.tv_sec;
+ kern_f->file2_ctime_nsec = fxr.file2_ctime.tv_nsec;
+
+ user_f = (struct xfs_commit_range_fresh *)&argp->file2_freshness;
+ if (copy_to_user(user_f, kern_f, sizeof(*kern_f)))
+ return -EFAULT;
+
+ return 0;
+}
+
static long
xfs_ioc_commit_range(
struct file *file,
@@ -2411,12 +2452,15 @@ xfs_ioc_commit_range(
.file2 = file,
};
struct xfs_commit_range args;
+ struct xfs_commit_range_fresh *kern_f;
struct fd file1;
int error;

+ kern_f = (struct xfs_commit_range_fresh *)&args.file2_freshness;
+
if (copy_from_user(&args, argp, sizeof(args)))
return -EFAULT;
- if (memchr_inv(&args.pad, 0, sizeof(args.pad)))
+ if (memchr_inv(&kern_f->pad, 0, sizeof(kern_f->pad)))
return -EINVAL;
if (args.flags & ~XFS_EXCHRANGE_ALL_FLAGS)
return -EINVAL;
@@ -2425,11 +2469,11 @@ xfs_ioc_commit_range(
fxr.file2_offset = args.file2_offset;
fxr.length = args.length;
fxr.flags = args.flags | __XFS_EXCHRANGE_CHECK_FRESH2;
- fxr.file2_ino = args.file2_ino;
- fxr.file2_mtime.tv_sec = args.file2_mtime;
- fxr.file2_mtime.tv_nsec = args.file2_mtime_nsec;
- fxr.file2_ctime.tv_sec = args.file2_ctime;
- fxr.file2_ctime.tv_nsec = args.file2_ctime_nsec;
+ fxr.file2_ino = kern_f->file2_ino;
+ fxr.file2_mtime.tv_sec = kern_f->file2_mtime;
+ fxr.file2_mtime.tv_nsec = kern_f->file2_mtime_nsec;
+ fxr.file2_ctime.tv_sec = kern_f->file2_ctime;
+ fxr.file2_ctime.tv_nsec = kern_f->file2_ctime_nsec;

file1 = fdget(args.file1_fd);
if (!file1.file)
@@ -2782,6 +2826,8 @@ xfs_file_ioctl(

case XFS_IOC_EXCHANGE_RANGE:
return xfs_ioc_exchange_range(filp, arg);
+ case XFS_IOC_START_COMMIT:
+ return xfs_ioc_start_commit(filp, arg);
case XFS_IOC_COMMIT_RANGE:
return xfs_ioc_commit_range(filp, arg);