* [PATCH v8 00/11] fs: multigrain timestamp redux
@ 2024-09-14 17:07 Jeff Layton
2024-09-14 17:07 ` [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper Jeff Layton
` (11 more replies)
0 siblings, 12 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
This is a fairly small update to the v7 set. It seems to pass all of my
testing. Again, most of the changes are in the first two patches, but
there are some differences in the patch that adds percpu counters as
well.
Since the report of a performance regression came just before the merge
window, it looks like we're going to have to wait for yet another
release, so consider this version v6.13 material.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
Changes in v8:
- drop the cookie handling from the new timekeeper interfaces
- add back the floor_swaps percpu counter
- comment updates and minor cleanups
- Link to v7: https://lore.kernel.org/r/20240913-mgtime-v7-0-92d4020e3b00@kernel.org
Changes in v7:
- move the floor value handling into timekeeper for better performance
- Link to v6: https://lore.kernel.org/r/20240715-mgtime-v6-0-48e5d34bd2ba@kernel.org
Changes in v6:
- Normalize timespec64 in inode_set_ctime_to_ts
- use DEFINE_PER_CPU counters for better vfs consistency
- skip ctime cmpxchg if the result means nothing will change
- add trace_ctime_xchg_skip to track skipped ctime updates
- use __print_flags in ctime_ns_xchg tracepoint
- Link to v5: https://lore.kernel.org/r/20240711-mgtime-v5-0-37bb5b465feb@kernel.org
Changes in v5:
- refetch coarse time in coarse_ctime if not returning floor
- timestamp_truncate before swapping new ctime value into place
- track floor value as atomic64_t
- cleanups to Documentation file
- Link to v4: https://lore.kernel.org/r/20240708-mgtime-v4-0-a0f3c6fb57f3@kernel.org
Changes in v4:
- reordered tracepoint fields for better packing
- rework percpu counters again to also count fine grained timestamps
- switch to try_cmpxchg for better efficiency
- Link to v3: https://lore.kernel.org/r/20240705-mgtime-v3-0-85b2daa9b335@kernel.org
Changes in v3:
- Drop the conversion of i_ctime fields to ktime_t, and use an unused bit
of the i_ctime_nsec field as QUERIED flag.
- Better tracepoints for tracking floor and ctime updates
- Reworked percpu counters to be more useful
- Track floor as monotonic value, which eliminates clock-jump problem
Changes in v2:
- Added Documentation file
- Link to v1: https://lore.kernel.org/r/20240626-mgtime-v1-0-a189352d0f8f@kernel.org
---
Jeff Layton (11):
timekeeping: move multigrain timestamp floor handling into timekeeper
fs: add infrastructure for multigrain timestamps
fs: have setattr_copy handle multigrain timestamps appropriately
fs: handle delegated timestamps in setattr_copy_mgtime
fs: tracepoints around multigrain timestamp events
fs: add percpu counters for significant multigrain timestamp events
Documentation: add a new file documenting multigrain timestamps
xfs: switch to multigrain timestamps
ext4: switch to multigrain timestamps
btrfs: convert to multigrain timestamps
tmpfs: add support for multigrain timestamps
Documentation/filesystems/index.rst | 1 +
Documentation/filesystems/multigrain-ts.rst | 121 ++++++++++++
fs/attr.c | 60 +++++-
fs/btrfs/file.c | 25 +--
fs/btrfs/super.c | 3 +-
fs/ext4/super.c | 2 +-
fs/inode.c | 278 +++++++++++++++++++++++++---
fs/stat.c | 42 ++++-
fs/xfs/libxfs/xfs_trans_inode.c | 6 +-
fs/xfs/xfs_iops.c | 10 +-
fs/xfs/xfs_super.c | 2 +-
include/linux/fs.h | 36 +++-
include/linux/timekeeping.h | 5 +
include/trace/events/timestamp.h | 124 +++++++++++++
kernel/time/timekeeping.c | 83 +++++++++
kernel/time/timekeeping_debug.c | 12 ++
kernel/time/timekeeping_internal.h | 3 +
mm/shmem.c | 2 +-
18 files changed, 742 insertions(+), 73 deletions(-)
---
base-commit: da3ea35007d0af457a0afc87e84fddaebc4e0b63
change-id: 20240913-mgtime-20c98bcda88e
Best regards,
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
@ 2024-09-14 17:07 ` Jeff Layton
2024-09-14 20:10 ` John Stultz
` (2 more replies)
2024-09-14 17:07 ` [PATCH v8 02/11] fs: add infrastructure for multigrain timestamps Jeff Layton
` (10 subsequent siblings)
11 siblings, 3 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
For multigrain timestamps, we must keep track of the latest timestamp
that has ever been handed out, and never hand out a coarse time below
that value.
Add a static singleton atomic64_t into timekeeper.c that we can use to
keep track of the latest fine-grained time ever handed out. This is
tracked as a monotonic ktime_t value to ensure that it isn't affected by
clock jumps.
Add two new public interfaces:
- ktime_get_coarse_real_ts64_mg() fills a timespec64 with the later of the
coarse-grained clock and the floor time
- ktime_get_real_ts64_mg() gets the fine-grained clock value, and tries
to swap it into the floor. A timespec64 is filled with the result.
Since the floor is global, we take great pains to avoid updating it
unless it's absolutely necessary. If we do the cmpxchg and find that the
value has been updated since we fetched it, then we discard the
fine-grained time that was fetched in favor of the recent update.
To maximize the window of this occurring when multiple tasks are racing
to update the floor, ktime_get_coarse_real_ts64_mg returns a cookie
value that represents the state of the floor tracking word, and
ktime_get_real_ts64_mg accepts a cookie value that it uses as the "old"
value when calling cmpxchg().
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
include/linux/timekeeping.h | 4 +++
kernel/time/timekeeping.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 86 insertions(+)
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
index fc12a9ba2c88..7aa85246c183 100644
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -45,6 +45,10 @@ extern void ktime_get_real_ts64(struct timespec64 *tv);
extern void ktime_get_coarse_ts64(struct timespec64 *ts);
extern void ktime_get_coarse_real_ts64(struct timespec64 *ts);
+/* Multigrain timestamp interfaces */
+extern void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts);
+extern void ktime_get_real_ts64_mg(struct timespec64 *ts);
+
void getboottime64(struct timespec64 *ts);
/*
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 5391e4167d60..16937242b904 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -114,6 +114,13 @@ static struct tk_fast tk_fast_raw ____cacheline_aligned = {
.base[1] = FAST_TK_INIT,
};
+/*
+ * This represents the latest fine-grained time that we have handed out as a
+ * timestamp on the system. Tracked as a monotonic ktime_t, and converted to the
+ * realtime clock on an as-needed basis.
+ */
+static __cacheline_aligned_in_smp atomic64_t mg_floor;
+
static inline void tk_normalize_xtime(struct timekeeper *tk)
{
while (tk->tkr_mono.xtime_nsec >= ((u64)NSEC_PER_SEC << tk->tkr_mono.shift)) {
@@ -2394,6 +2401,81 @@ void ktime_get_coarse_real_ts64(struct timespec64 *ts)
}
EXPORT_SYMBOL(ktime_get_coarse_real_ts64);
+/**
+ * ktime_get_coarse_real_ts64_mg - get later of coarse grained time or floor
+ * @ts: timespec64 to be filled
+ *
+ * Adjust floor to realtime and compare it to the coarse time. Fill
+ * @ts with the latest one. Note that this is a filesystem-specific
+ * interface and should be avoided outside of that context.
+ */
+void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts)
+{
+ struct timekeeper *tk = &tk_core.timekeeper;
+ u64 floor = atomic64_read(&mg_floor);
+ ktime_t f_real, offset, coarse;
+ unsigned int seq;
+
+ WARN_ON(timekeeping_suspended);
+
+ do {
+ seq = read_seqcount_begin(&tk_core.seq);
+ *ts = tk_xtime(tk);
+ offset = *offsets[TK_OFFS_REAL];
+ } while (read_seqcount_retry(&tk_core.seq, seq));
+
+ coarse = timespec64_to_ktime(*ts);
+ f_real = ktime_add(floor, offset);
+ if (ktime_after(f_real, coarse))
+ *ts = ktime_to_timespec64(f_real);
+}
+EXPORT_SYMBOL_GPL(ktime_get_coarse_real_ts64_mg);
+
+/**
+ * ktime_get_real_ts64_mg - attempt to update floor value and return result
+ * @ts: pointer to the timespec to be set
+ *
+ * Get a current monotonic fine-grained time value and attempt to swap
+ * it into the floor. @ts will be filled with the resulting floor value,
+ * regardless of the outcome of the swap. Note that this is a filesystem
+ * specific interface and should be avoided outside of that context.
+ */
+void ktime_get_real_ts64_mg(struct timespec64 *ts, u64 cookie)
+{
+ struct timekeeper *tk = &tk_core.timekeeper;
+ ktime_t old = atomic64_read(&mg_floor);
+ ktime_t offset, mono;
+ unsigned int seq;
+ u64 nsecs;
+
+ WARN_ON(timekeeping_suspended);
+
+ do {
+ seq = read_seqcount_begin(&tk_core.seq);
+
+ ts->tv_sec = tk->xtime_sec;
+ mono = tk->tkr_mono.base;
+ nsecs = timekeeping_get_ns(&tk->tkr_mono);
+ offset = *offsets[TK_OFFS_REAL];
+ } while (read_seqcount_retry(&tk_core.seq, seq));
+
+ mono = ktime_add_ns(mono, nsecs);
+
+ if (atomic64_try_cmpxchg(&mg_floor, &old, mono)) {
+ ts->tv_nsec = 0;
+ timespec64_add_ns(ts, nsecs);
+ } else {
+ /*
+ * Something has changed mg_floor since "old" was
+ * fetched. "old" has now been updated with the
+ * current value of mg_floor, so use that to return
+ * the current coarse floor value.
+ */
+ *ts = ktime_to_timespec64(ktime_add(old, offset));
+ }
+}
+EXPORT_SYMBOL_GPL(ktime_get_real_ts64_mg);
+
void ktime_get_coarse_ts64(struct timespec64 *ts)
{
struct timekeeper *tk = &tk_core.timekeeper;
--
2.46.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v8 02/11] fs: add infrastructure for multigrain timestamps
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
2024-09-14 17:07 ` [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper Jeff Layton
@ 2024-09-14 17:07 ` Jeff Layton
2024-09-14 17:07 ` [PATCH v8 03/11] fs: have setattr_copy handle multigrain timestamps appropriately Jeff Layton
` (9 subsequent siblings)
11 siblings, 0 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
The VFS has always used coarse-grained timestamps when updating the
ctime and mtime after a change. This has the benefit of allowing
filesystems to optimize away a lot metadata updates, down to around 1
per jiffy, even when a file is under heavy writes.
Unfortunately, this has always been an issue when we're exporting via
NFSv3, which relies on timestamps to validate caches. A lot of changes
can happen in a jiffy, so timestamps aren't sufficient to help the
client decide when to invalidate the cache. Even with NFSv4, a lot of
exported filesystems don't properly support a change attribute and are
subject to the same problems with timestamp granularity. Other
applications have similar issues with timestamps (e.g backup
applications).
If we were to always use fine-grained timestamps, that would improve the
situation, but that becomes rather expensive, as the underlying
filesystem would have to log a lot more metadata updates.
What we need is a way to only use fine-grained timestamps when they are
being actively queried. Use the (unused) top bit in inode->i_ctime_nsec
as a flag that indicates whether the current timestamps have been
queried via stat() or the like. When it's set, we allow the kernel to
use a fine-grained timestamp iff it's necessary to make the ctime show
a different value.
This solves the problem of being able to distinguish the timestamp
between updates, but introduces a new problem: it's now possible for a
file being changed to get a fine-grained timestamp. A file that is
altered just a bit later can then get a coarse-grained one that appears
older than the earlier fine-grained time. This violates timestamp
ordering guarantees.
To remedy this, keep a global monotonic atomic64_t value that acts as a
timestamp floor. When we go to stamp a file, we first get the latter of
the current floor value and the current coarse-grained time. If the
inode ctime hasn't been queried then we just attempt to stamp it with
that value.
If it has been queried, then first see whether the current coarse time
is later than the existing ctime. If it is, then we accept that value.
If it isn't, then we get a fine-grained timestamp.
Filesystems can opt into this by setting the FS_MGTIME fstype flag.
Others should be unaffected (other than being subject to the same floor
value as multigrain filesystems).
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/inode.c | 137 +++++++++++++++++++++++++++++++++++++++++++----------
fs/stat.c | 39 ++++++++++++++-
include/linux/fs.h | 34 +++++++++----
3 files changed, 175 insertions(+), 35 deletions(-)
diff --git a/fs/inode.c b/fs/inode.c
index 10c4619faeef..232b474218e6 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2172,19 +2172,58 @@ int file_remove_privs(struct file *file)
}
EXPORT_SYMBOL(file_remove_privs);
+/**
+ * current_time - Return FS time (possibly fine-grained)
+ * @inode: inode.
+ *
+ * Return the current time truncated to the time granularity supported by
+ * the fs, as suitable for a ctime/mtime change. If the ctime is flagged
+ * as having been QUERIED, get a fine-grained timestamp, but don't update
+ * the floor.
+ *
+ * For a multigrain inode, this is effectively an estimate of the timestamp
+ * that a file would receive. An actual update must go through
+ * inode_set_ctime_current().
+ */
+struct timespec64 current_time(struct inode *inode)
+{
+ struct timespec64 now;
+ u32 cns;
+
+ ktime_get_coarse_real_ts64_mg(&now);
+
+ if (!is_mgtime(inode))
+ goto out;
+
+ /* If nothing has queried it, then coarse time is fine */
+ cns = smp_load_acquire(&inode->i_ctime_nsec);
+ if (cns & I_CTIME_QUERIED) {
+ /*
+ * If there is no apparent change, then get a fine-grained
+ * timestamp.
+ */
+ if (now.tv_nsec == (cns & ~I_CTIME_QUERIED))
+ ktime_get_real_ts64(&now);
+ }
+out:
+ return timestamp_truncate(now, inode);
+}
+EXPORT_SYMBOL(current_time);
+
static int inode_needs_update_time(struct inode *inode)
{
+ struct timespec64 now, ts;
int sync_it = 0;
- struct timespec64 now = current_time(inode);
- struct timespec64 ts;
/* First try to exhaust all avenues to not sync */
if (IS_NOCMTIME(inode))
return 0;
+ now = current_time(inode);
+
ts = inode_get_mtime(inode);
if (!timespec64_equal(&ts, &now))
- sync_it = S_MTIME;
+ sync_it |= S_MTIME;
ts = inode_get_ctime(inode);
if (!timespec64_equal(&ts, &now))
@@ -2562,6 +2601,15 @@ void inode_nohighmem(struct inode *inode)
}
EXPORT_SYMBOL(inode_nohighmem);
+struct timespec64 inode_set_ctime_to_ts(struct inode *inode, struct timespec64 ts)
+{
+ set_normalized_timespec64(&ts, ts.tv_sec, ts.tv_nsec);
+ inode->i_ctime_sec = ts.tv_sec;
+ inode->i_ctime_nsec = ts.tv_nsec;
+ return ts;
+}
+EXPORT_SYMBOL(inode_set_ctime_to_ts);
+
/**
* timestamp_truncate - Truncate timespec to a granularity
* @t: Timespec
@@ -2594,36 +2642,75 @@ struct timespec64 timestamp_truncate(struct timespec64 t, struct inode *inode)
EXPORT_SYMBOL(timestamp_truncate);
/**
- * current_time - Return FS time
- * @inode: inode.
+ * inode_set_ctime_current - set the ctime to current_time
+ * @inode: inode
*
- * Return the current time truncated to the time granularity supported by
- * the fs.
+ * Set the inode's ctime to the current value for the inode. Returns the
+ * current value that was assigned. If this is not a multigrain inode, then we
+ * set it to the later of the coarse time and floor value.
*
- * Note that inode and inode->sb cannot be NULL.
- * Otherwise, the function warns and returns time without truncation.
+ * If it is multigrain, then we first see if the coarse-grained timestamp is
+ * distinct from what we have. If so, then we'll just use that. If we have to
+ * get a fine-grained timestamp, then do so, and try to swap it into the floor.
+ * We accept the new floor value regardless of the outcome of the cmpxchg.
+ * After that, we try to swap the new value into i_ctime_nsec. Again, we take
+ * the resulting ctime, regardless of the outcome of the swap.
*/
-struct timespec64 current_time(struct inode *inode)
+struct timespec64 inode_set_ctime_current(struct inode *inode)
{
struct timespec64 now;
+ u32 cns, cur;
- ktime_get_coarse_real_ts64(&now);
- return timestamp_truncate(now, inode);
-}
-EXPORT_SYMBOL(current_time);
+ ktime_get_coarse_real_ts64_mg(&now);
+ now = timestamp_truncate(now, inode);
-/**
- * inode_set_ctime_current - set the ctime to current_time
- * @inode: inode
- *
- * Set the inode->i_ctime to the current value for the inode. Returns
- * the current value that was assigned to i_ctime.
- */
-struct timespec64 inode_set_ctime_current(struct inode *inode)
-{
- struct timespec64 now = current_time(inode);
+ /* Just return that if this is not a multigrain fs */
+ if (!is_mgtime(inode)) {
+ inode_set_ctime_to_ts(inode, now);
+ goto out;
+ }
- inode_set_ctime_to_ts(inode, now);
+ /*
+ * We only need a fine-grained time if someone has queried it,
+ * and the current coarse grained time isn't later than what's
+ * already there.
+ */
+ cns = smp_load_acquire(&inode->i_ctime_nsec);
+ if (cns & I_CTIME_QUERIED) {
+ struct timespec64 ctime = { .tv_sec = inode->i_ctime_sec,
+ .tv_nsec = cns & ~I_CTIME_QUERIED };
+
+ if (timespec64_compare(&now, &ctime) <= 0) {
+ ktime_get_real_ts64_mg(&now);
+ now = timestamp_truncate(now, inode);
+ }
+ }
+
+ /* No need to cmpxchg if it's exactly the same */
+ if (cns == now.tv_nsec && inode->i_ctime_sec == now.tv_sec)
+ goto out;
+ cur = cns;
+retry:
+ /* Try to swap the nsec value into place. */
+ if (try_cmpxchg(&inode->i_ctime_nsec, &cur, now.tv_nsec)) {
+ /* If swap occurred, then we're (mostly) done */
+ inode->i_ctime_sec = now.tv_sec;
+ } else {
+ /*
+ * Was the change due to someone marking the old ctime QUERIED?
+ * If so then retry the swap. This can only happen once since
+ * the only way to clear I_CTIME_QUERIED is to stamp the inode
+ * with a new ctime.
+ */
+ if (!(cns & I_CTIME_QUERIED) && (cns | I_CTIME_QUERIED) == cur) {
+ cns = cur;
+ goto retry;
+ }
+ /* Otherwise, keep the existing ctime */
+ now.tv_sec = inode->i_ctime_sec;
+ now.tv_nsec = cur & ~I_CTIME_QUERIED;
+ }
+out:
return now;
}
EXPORT_SYMBOL(inode_set_ctime_current);
diff --git a/fs/stat.c b/fs/stat.c
index 89ce1be56310..a449626fd460 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -26,6 +26,35 @@
#include "internal.h"
#include "mount.h"
+/**
+ * fill_mg_cmtime - Fill in the mtime and ctime and flag ctime as QUERIED
+ * @stat: where to store the resulting values
+ * @request_mask: STATX_* values requested
+ * @inode: inode from which to grab the c/mtime
+ *
+ * Given @inode, grab the ctime and mtime out if it and store the result
+ * in @stat. When fetching the value, flag it as QUERIED (if not already)
+ * so the next write will record a distinct timestamp.
+ */
+void fill_mg_cmtime(struct kstat *stat, u32 request_mask, struct inode *inode)
+{
+ atomic_t *pcn = (atomic_t *)&inode->i_ctime_nsec;
+
+ /* If neither time was requested, then don't report them */
+ if (!(request_mask & (STATX_CTIME|STATX_MTIME))) {
+ stat->result_mask &= ~(STATX_CTIME|STATX_MTIME);
+ return;
+ }
+
+ stat->mtime = inode_get_mtime(inode);
+ stat->ctime.tv_sec = inode->i_ctime_sec;
+ stat->ctime.tv_nsec = (u32)atomic_read(pcn);
+ if (!(stat->ctime.tv_nsec & I_CTIME_QUERIED))
+ stat->ctime.tv_nsec = ((u32)atomic_fetch_or(I_CTIME_QUERIED, pcn));
+ stat->ctime.tv_nsec &= ~I_CTIME_QUERIED;
+}
+EXPORT_SYMBOL(fill_mg_cmtime);
+
/**
* generic_fillattr - Fill in the basic attributes from the inode struct
* @idmap: idmap of the mount the inode was found from
@@ -58,8 +87,14 @@ void generic_fillattr(struct mnt_idmap *idmap, u32 request_mask,
stat->rdev = inode->i_rdev;
stat->size = i_size_read(inode);
stat->atime = inode_get_atime(inode);
- stat->mtime = inode_get_mtime(inode);
- stat->ctime = inode_get_ctime(inode);
+
+ if (is_mgtime(inode)) {
+ fill_mg_cmtime(stat, request_mask, inode);
+ } else {
+ stat->ctime = inode_get_ctime(inode);
+ stat->mtime = inode_get_mtime(inode);
+ }
+
stat->blksize = i_blocksize(inode);
stat->blocks = inode->i_blocks;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 6ca11e241a24..eff688e75f2f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1613,6 +1613,17 @@ static inline struct timespec64 inode_set_mtime(struct inode *inode,
return inode_set_mtime_to_ts(inode, ts);
}
+/*
+ * Multigrain timestamps
+ *
+ * Conditionally use fine-grained ctime and mtime timestamps when there
+ * are users actively observing them via getattr. The primary use-case
+ * for this is NFS clients that use the ctime to distinguish between
+ * different states of the file, and that are often fooled by multiple
+ * operations that occur in the same coarse-grained timer tick.
+ */
+#define I_CTIME_QUERIED ((u32)BIT(31))
+
static inline time64_t inode_get_ctime_sec(const struct inode *inode)
{
return inode->i_ctime_sec;
@@ -1620,7 +1631,7 @@ static inline time64_t inode_get_ctime_sec(const struct inode *inode)
static inline long inode_get_ctime_nsec(const struct inode *inode)
{
- return inode->i_ctime_nsec;
+ return inode->i_ctime_nsec & ~I_CTIME_QUERIED;
}
static inline struct timespec64 inode_get_ctime(const struct inode *inode)
@@ -1631,13 +1642,7 @@ static inline struct timespec64 inode_get_ctime(const struct inode *inode)
return ts;
}
-static inline struct timespec64 inode_set_ctime_to_ts(struct inode *inode,
- struct timespec64 ts)
-{
- inode->i_ctime_sec = ts.tv_sec;
- inode->i_ctime_nsec = ts.tv_nsec;
- return ts;
-}
+struct timespec64 inode_set_ctime_to_ts(struct inode *inode, struct timespec64 ts);
/**
* inode_set_ctime - set the ctime in the inode
@@ -2500,6 +2505,7 @@ struct file_system_type {
#define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */
#define FS_DISALLOW_NOTIFY_PERM 16 /* Disable fanotify permission events */
#define FS_ALLOW_IDMAP 32 /* FS has been updated to handle vfs idmappings. */
+#define FS_MGTIME 64 /* FS uses multigrain timestamps */
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
int (*init_fs_context)(struct fs_context *);
const struct fs_parameter_spec *parameters;
@@ -2523,6 +2529,17 @@ struct file_system_type {
#define MODULE_ALIAS_FS(NAME) MODULE_ALIAS("fs-" NAME)
+/**
+ * is_mgtime: is this inode using multigrain timestamps
+ * @inode: inode to test for multigrain timestamps
+ *
+ * Return true if the inode uses multigrain timestamps, false otherwise.
+ */
+static inline bool is_mgtime(const struct inode *inode)
+{
+ return inode->i_sb->s_type->fs_flags & FS_MGTIME;
+}
+
extern struct dentry *mount_bdev(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data,
int (*fill_super)(struct super_block *, void *, int));
@@ -3262,6 +3279,7 @@ extern void page_put_link(void *);
extern int page_symlink(struct inode *inode, const char *symname, int len);
extern const struct inode_operations page_symlink_inode_operations;
extern void kfree_link(void *);
+void fill_mg_cmtime(struct kstat *stat, u32 request_mask, struct inode *inode);
void generic_fillattr(struct mnt_idmap *, u32, struct inode *, struct kstat *);
void generic_fill_statx_attr(struct inode *inode, struct kstat *stat);
void generic_fill_statx_atomic_writes(struct kstat *stat,
--
2.46.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v8 03/11] fs: have setattr_copy handle multigrain timestamps appropriately
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
2024-09-14 17:07 ` [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper Jeff Layton
2024-09-14 17:07 ` [PATCH v8 02/11] fs: add infrastructure for multigrain timestamps Jeff Layton
@ 2024-09-14 17:07 ` Jeff Layton
2024-09-14 17:07 ` [PATCH v8 04/11] fs: handle delegated timestamps in setattr_copy_mgtime Jeff Layton
` (8 subsequent siblings)
11 siblings, 0 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
The setattr codepath is still using coarse-grained timestamps, even on
multigrain filesystems. To fix this, we need to fetch the timestamp for
ctime updates later, at the point where the assignment occurs in
setattr_copy.
On a multigrain inode, ignore the ia_ctime in the attrs, and always
update the ctime to the current clock value. Update the atime and mtime
with the same value (if needed) unless they are being set to other
specific values, a'la utimes().
Note that we don't want to do this universally however, as some
filesystems (e.g. most networked fs) want to do an explicit update
elsewhere before updating the local inode.
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/attr.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 46 insertions(+), 6 deletions(-)
diff --git a/fs/attr.c b/fs/attr.c
index c04d19b58f12..3bcbc45708a3 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -271,6 +271,42 @@ int inode_newsize_ok(const struct inode *inode, loff_t offset)
}
EXPORT_SYMBOL(inode_newsize_ok);
+/**
+ * setattr_copy_mgtime - update timestamps for mgtime inodes
+ * @inode: inode timestamps to be updated
+ * @attr: attrs for the update
+ *
+ * With multigrain timestamps, we need to take more care to prevent races
+ * when updating the ctime. Always update the ctime to the very latest
+ * using the standard mechanism, and use that to populate the atime and
+ * mtime appropriately (unless we're setting those to specific values).
+ */
+static void setattr_copy_mgtime(struct inode *inode, const struct iattr *attr)
+{
+ unsigned int ia_valid = attr->ia_valid;
+ struct timespec64 now;
+
+ /*
+ * If the ctime isn't being updated then nothing else should be
+ * either.
+ */
+ if (!(ia_valid & ATTR_CTIME)) {
+ WARN_ON_ONCE(ia_valid & (ATTR_ATIME|ATTR_MTIME));
+ return;
+ }
+
+ now = inode_set_ctime_current(inode);
+ if (ia_valid & ATTR_ATIME_SET)
+ inode_set_atime_to_ts(inode, attr->ia_atime);
+ else if (ia_valid & ATTR_ATIME)
+ inode_set_atime_to_ts(inode, now);
+
+ if (ia_valid & ATTR_MTIME_SET)
+ inode_set_mtime_to_ts(inode, attr->ia_mtime);
+ else if (ia_valid & ATTR_MTIME)
+ inode_set_mtime_to_ts(inode, now);
+}
+
/**
* setattr_copy - copy simple metadata updates into the generic inode
* @idmap: idmap of the mount the inode was found from
@@ -303,12 +339,6 @@ void setattr_copy(struct mnt_idmap *idmap, struct inode *inode,
i_uid_update(idmap, attr, inode);
i_gid_update(idmap, attr, inode);
- if (ia_valid & ATTR_ATIME)
- inode_set_atime_to_ts(inode, attr->ia_atime);
- if (ia_valid & ATTR_MTIME)
- inode_set_mtime_to_ts(inode, attr->ia_mtime);
- if (ia_valid & ATTR_CTIME)
- inode_set_ctime_to_ts(inode, attr->ia_ctime);
if (ia_valid & ATTR_MODE) {
umode_t mode = attr->ia_mode;
if (!in_group_or_capable(idmap, inode,
@@ -316,6 +346,16 @@ void setattr_copy(struct mnt_idmap *idmap, struct inode *inode,
mode &= ~S_ISGID;
inode->i_mode = mode;
}
+
+ if (is_mgtime(inode))
+ return setattr_copy_mgtime(inode, attr);
+
+ if (ia_valid & ATTR_ATIME)
+ inode_set_atime_to_ts(inode, attr->ia_atime);
+ if (ia_valid & ATTR_MTIME)
+ inode_set_mtime_to_ts(inode, attr->ia_mtime);
+ if (ia_valid & ATTR_CTIME)
+ inode_set_ctime_to_ts(inode, attr->ia_ctime);
}
EXPORT_SYMBOL(setattr_copy);
--
2.46.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v8 04/11] fs: handle delegated timestamps in setattr_copy_mgtime
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
` (2 preceding siblings ...)
2024-09-14 17:07 ` [PATCH v8 03/11] fs: have setattr_copy handle multigrain timestamps appropriately Jeff Layton
@ 2024-09-14 17:07 ` Jeff Layton
2024-09-14 17:07 ` [PATCH v8 05/11] fs: tracepoints around multigrain timestamp events Jeff Layton
` (7 subsequent siblings)
11 siblings, 0 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
When updating the ctime on an inode for a SETATTR with a multigrain
filesystem, we usually want to take the latest time we can get for the
ctime. The exception to this rule is when there is a nfsd write
delegation and the server is proxying timestamps from the client.
When nfsd gets a CB_GETATTR response, we want to update the timestamp
value in the inode to the values that the client is tracking. The client
doesn't send a ctime value (since that's always determined by the
exported filesystem), but it can send a mtime value. In the case where
it does, then we may need to update the ctime to a value commensurate
with that instead of the current time.
If ATTR_DELEG is set, then use ia_ctime value instead of setting the
timestamp to the current time.
With the addition of delegated timestamps we can also receive a request
to update only the atime, but we may not need to set the ctime. Trust
the ATTR_CTIME flag in the update and only update the ctime when it's
set.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/attr.c | 28 +++++++++++++--------
fs/inode.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/fs.h | 2 ++
3 files changed, 92 insertions(+), 10 deletions(-)
diff --git a/fs/attr.c b/fs/attr.c
index 3bcbc45708a3..392eb62aa609 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -286,16 +286,20 @@ static void setattr_copy_mgtime(struct inode *inode, const struct iattr *attr)
unsigned int ia_valid = attr->ia_valid;
struct timespec64 now;
- /*
- * If the ctime isn't being updated then nothing else should be
- * either.
- */
- if (!(ia_valid & ATTR_CTIME)) {
- WARN_ON_ONCE(ia_valid & (ATTR_ATIME|ATTR_MTIME));
- return;
+ if (ia_valid & ATTR_CTIME) {
+ /*
+ * In the case of an update for a write delegation, we must respect
+ * the value in ia_ctime and not use the current time.
+ */
+ if (ia_valid & ATTR_DELEG)
+ now = inode_set_ctime_deleg(inode, attr->ia_ctime);
+ else
+ now = inode_set_ctime_current(inode);
+ } else {
+ /* If ATTR_CTIME isn't set, then ATTR_MTIME shouldn't be either. */
+ WARN_ON_ONCE(ia_valid & ATTR_MTIME);
}
- now = inode_set_ctime_current(inode);
if (ia_valid & ATTR_ATIME_SET)
inode_set_atime_to_ts(inode, attr->ia_atime);
else if (ia_valid & ATTR_ATIME)
@@ -354,8 +358,12 @@ void setattr_copy(struct mnt_idmap *idmap, struct inode *inode,
inode_set_atime_to_ts(inode, attr->ia_atime);
if (ia_valid & ATTR_MTIME)
inode_set_mtime_to_ts(inode, attr->ia_mtime);
- if (ia_valid & ATTR_CTIME)
- inode_set_ctime_to_ts(inode, attr->ia_ctime);
+ if (ia_valid & ATTR_CTIME) {
+ if (ia_valid & ATTR_DELEG)
+ inode_set_ctime_deleg(inode, attr->ia_ctime);
+ else
+ inode_set_ctime_to_ts(inode, attr->ia_ctime);
+ }
}
EXPORT_SYMBOL(setattr_copy);
diff --git a/fs/inode.c b/fs/inode.c
index 232b474218e6..614d0402e9ad 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2715,6 +2715,78 @@ struct timespec64 inode_set_ctime_current(struct inode *inode)
}
EXPORT_SYMBOL(inode_set_ctime_current);
+/**
+ * inode_set_ctime_deleg - try to update the ctime on a delegated inode
+ * @inode: inode to update
+ * @update: timespec64 to set the ctime
+ *
+ * Attempt to atomically update the ctime on behalf of a delegation holder.
+ *
+ * The nfs server can call back the holder of a delegation to get updated
+ * inode attributes, including the mtime. When updating the mtime we may
+ * need to update the ctime to a value at least equal to that.
+ *
+ * This can race with concurrent updates to the inode, in which
+ * case we just don't do the update.
+ *
+ * Note that this works even when multigrain timestamps are not enabled,
+ * so use it in either case.
+ */
+struct timespec64 inode_set_ctime_deleg(struct inode *inode, struct timespec64 update)
+{
+ struct timespec64 now, cur_ts;
+ u32 cur, old;
+
+ /* pairs with try_cmpxchg below */
+ cur = smp_load_acquire(&inode->i_ctime_nsec);
+ cur_ts.tv_nsec = cur & ~I_CTIME_QUERIED;
+ cur_ts.tv_sec = inode->i_ctime_sec;
+
+ /* If the update is older than the existing value, skip it. */
+ if (timespec64_compare(&update, &cur_ts) <= 0)
+ return cur_ts;
+
+ ktime_get_coarse_real_ts64_mg(&now);
+
+ /* Clamp the update to "now" if it's in the future */
+ if (timespec64_compare(&update, &now) > 0)
+ update = now;
+
+ update = timestamp_truncate(update, inode);
+
+ /* No need to update if the values are already the same */
+ if (timespec64_equal(&update, &cur_ts))
+ return cur_ts;
+
+ /*
+ * Try to swap the nsec value into place. If it fails, that means
+ * we raced with an update due to a write or similar activity. That
+ * stamp takes precedence, so just skip the update.
+ */
+retry:
+ old = cur;
+ if (try_cmpxchg(&inode->i_ctime_nsec, &cur, update.tv_nsec)) {
+ inode->i_ctime_sec = update.tv_sec;
+ mgtime_counter_inc(mg_ctime_swaps);
+ return update;
+ }
+
+ /*
+ * Was the change due to someone marking the old ctime QUERIED?
+ * If so then retry the swap. This can only happen once since
+ * the only way to clear I_CTIME_QUERIED is to stamp the inode
+ * with a new ctime.
+ */
+ if (!(old & I_CTIME_QUERIED) && (cur == (old | I_CTIME_QUERIED)))
+ goto retry;
+
+ /* Otherwise, it was a new timestamp. */
+ cur_ts.tv_sec = inode->i_ctime_sec;
+ cur_ts.tv_nsec = cur & ~I_CTIME_QUERIED;
+ return cur_ts;
+}
+EXPORT_SYMBOL(inode_set_ctime_deleg);
+
/**
* in_group_or_capable - check whether caller is CAP_FSETID privileged
* @idmap: idmap of the mount @inode was found from
diff --git a/include/linux/fs.h b/include/linux/fs.h
index eff688e75f2f..ea7ed437d2b1 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1544,6 +1544,8 @@ static inline bool fsuidgid_has_mapping(struct super_block *sb,
struct timespec64 current_time(struct inode *inode);
struct timespec64 inode_set_ctime_current(struct inode *inode);
+struct timespec64 inode_set_ctime_deleg(struct inode *inode,
+ struct timespec64 update);
static inline time64_t inode_get_atime_sec(const struct inode *inode)
{
--
2.46.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v8 05/11] fs: tracepoints around multigrain timestamp events
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
` (3 preceding siblings ...)
2024-09-14 17:07 ` [PATCH v8 04/11] fs: handle delegated timestamps in setattr_copy_mgtime Jeff Layton
@ 2024-09-14 17:07 ` Jeff Layton
2024-09-15 8:21 ` Steven Rostedt
2024-09-14 17:07 ` [PATCH v8 06/11] fs: add percpu counters for significant " Jeff Layton
` (6 subsequent siblings)
11 siblings, 1 reply; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
Add some tracepoints around various multigrain timestamp events.
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/inode.c | 9 ++-
fs/stat.c | 3 +
include/trace/events/timestamp.h | 124 +++++++++++++++++++++++++++++++++++++++
3 files changed, 135 insertions(+), 1 deletion(-)
diff --git a/fs/inode.c b/fs/inode.c
index 614d0402e9ad..d7da9d06921f 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -22,6 +22,9 @@
#include <linux/iversion.h>
#include <linux/rw_hint.h>
#include <trace/events/writeback.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/timestamp.h>
+
#include "internal.h"
/*
@@ -2603,6 +2606,7 @@ EXPORT_SYMBOL(inode_nohighmem);
struct timespec64 inode_set_ctime_to_ts(struct inode *inode, struct timespec64 ts)
{
+ trace_inode_set_ctime_to_ts(inode, &ts);
set_normalized_timespec64(&ts, ts.tv_sec, ts.tv_nsec);
inode->i_ctime_sec = ts.tv_sec;
inode->i_ctime_nsec = ts.tv_nsec;
@@ -2687,14 +2691,17 @@ struct timespec64 inode_set_ctime_current(struct inode *inode)
}
/* No need to cmpxchg if it's exactly the same */
- if (cns == now.tv_nsec && inode->i_ctime_sec == now.tv_sec)
+ if (cns == now.tv_nsec && inode->i_ctime_sec == now.tv_sec) {
+ trace_ctime_xchg_skip(inode, &now);
goto out;
+ }
cur = cns;
retry:
/* Try to swap the nsec value into place. */
if (try_cmpxchg(&inode->i_ctime_nsec, &cur, now.tv_nsec)) {
/* If swap occurred, then we're (mostly) done */
inode->i_ctime_sec = now.tv_sec;
+ trace_ctime_ns_xchg(inode, cns, now.tv_nsec, cur);
} else {
/*
* Was the change due to someone marking the old ctime QUERIED?
diff --git a/fs/stat.c b/fs/stat.c
index a449626fd460..9eb6d9b2d010 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -23,6 +23,8 @@
#include <linux/uaccess.h>
#include <asm/unistd.h>
+#include <trace/events/timestamp.h>
+
#include "internal.h"
#include "mount.h"
@@ -52,6 +54,7 @@ void fill_mg_cmtime(struct kstat *stat, u32 request_mask, struct inode *inode)
if (!(stat->ctime.tv_nsec & I_CTIME_QUERIED))
stat->ctime.tv_nsec = ((u32)atomic_fetch_or(I_CTIME_QUERIED, pcn));
stat->ctime.tv_nsec &= ~I_CTIME_QUERIED;
+ trace_fill_mg_cmtime(inode, &stat->ctime, &stat->mtime);
}
EXPORT_SYMBOL(fill_mg_cmtime);
diff --git a/include/trace/events/timestamp.h b/include/trace/events/timestamp.h
new file mode 100644
index 000000000000..c9e5ec930054
--- /dev/null
+++ b/include/trace/events/timestamp.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM timestamp
+
+#if !defined(_TRACE_TIMESTAMP_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_TIMESTAMP_H
+
+#include <linux/tracepoint.h>
+#include <linux/fs.h>
+
+#define CTIME_QUERIED_FLAGS \
+ { I_CTIME_QUERIED, "Q" }
+
+DECLARE_EVENT_CLASS(ctime,
+ TP_PROTO(struct inode *inode,
+ struct timespec64 *ctime),
+
+ TP_ARGS(inode, ctime),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(time64_t, ctime_s)
+ __field(u32, ctime_ns)
+ __field(u32, gen)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->gen = inode->i_generation;
+ __entry->ctime_s = ctime->tv_sec;
+ __entry->ctime_ns = ctime->tv_nsec;
+ ),
+
+ TP_printk("ino=%d:%d:%ld:%u ctime=%lld.%u",
+ MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __entry->gen,
+ __entry->ctime_s, __entry->ctime_ns
+ )
+);
+
+DEFINE_EVENT(ctime, inode_set_ctime_to_ts,
+ TP_PROTO(struct inode *inode,
+ struct timespec64 *ctime),
+ TP_ARGS(inode, ctime));
+
+DEFINE_EVENT(ctime, ctime_xchg_skip,
+ TP_PROTO(struct inode *inode,
+ struct timespec64 *ctime),
+ TP_ARGS(inode, ctime));
+
+TRACE_EVENT(ctime_ns_xchg,
+ TP_PROTO(struct inode *inode,
+ u32 old,
+ u32 new,
+ u32 cur),
+
+ TP_ARGS(inode, old, new, cur),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(u32, gen)
+ __field(u32, old)
+ __field(u32, new)
+ __field(u32, cur)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->gen = inode->i_generation;
+ __entry->old = old;
+ __entry->new = new;
+ __entry->cur = cur;
+ ),
+
+ TP_printk("ino=%d:%d:%ld:%u old=%u:%s new=%u cur=%u:%s",
+ MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __entry->gen,
+ __entry->old & ~I_CTIME_QUERIED,
+ __print_flags(__entry->old & I_CTIME_QUERIED, "|", CTIME_QUERIED_FLAGS),
+ __entry->new,
+ __entry->cur & ~I_CTIME_QUERIED,
+ __print_flags(__entry->cur & I_CTIME_QUERIED, "|", CTIME_QUERIED_FLAGS)
+ )
+);
+
+TRACE_EVENT(fill_mg_cmtime,
+ TP_PROTO(struct inode *inode,
+ struct timespec64 *ctime,
+ struct timespec64 *mtime),
+
+ TP_ARGS(inode, ctime, mtime),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(time64_t, ctime_s)
+ __field(time64_t, mtime_s)
+ __field(u32, ctime_ns)
+ __field(u32, mtime_ns)
+ __field(u32, gen)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->gen = inode->i_generation;
+ __entry->ctime_s = ctime->tv_sec;
+ __entry->mtime_s = mtime->tv_sec;
+ __entry->ctime_ns = ctime->tv_nsec;
+ __entry->mtime_ns = mtime->tv_nsec;
+ ),
+
+ TP_printk("ino=%d:%d:%ld:%u ctime=%lld.%u mtime=%lld.%u",
+ MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __entry->gen,
+ __entry->ctime_s, __entry->ctime_ns,
+ __entry->mtime_s, __entry->mtime_ns
+ )
+);
+#endif /* _TRACE_TIMESTAMP_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
--
2.46.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v8 06/11] fs: add percpu counters for significant multigrain timestamp events
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
` (4 preceding siblings ...)
2024-09-14 17:07 ` [PATCH v8 05/11] fs: tracepoints around multigrain timestamp events Jeff Layton
@ 2024-09-14 17:07 ` Jeff Layton
2024-09-16 10:20 ` Thomas Gleixner
2024-09-14 17:07 ` [PATCH v8 07/11] Documentation: add a new file documenting multigrain timestamps Jeff Layton
` (5 subsequent siblings)
11 siblings, 1 reply; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
New percpu counters for counting various stats around mgtimes, and a new
debugfs file for displaying them when CONFIG_DEBUG_FS is enabled:
- number of attempted ctime updates
- number of successful i_ctime_nsec swaps
- number of fine-grained timestamp fetches
- number of coarse-grained floor swaps
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/inode.c | 76 ++++++++++++++++++++++++++++++++++++--
include/linux/timekeeping.h | 1 +
kernel/time/timekeeping.c | 3 +-
kernel/time/timekeeping_debug.c | 12 ++++++
kernel/time/timekeeping_internal.h | 3 ++
5 files changed, 90 insertions(+), 5 deletions(-)
diff --git a/fs/inode.c b/fs/inode.c
index d7da9d06921f..1f0487104c71 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -21,6 +21,8 @@
#include <linux/list_lru.h>
#include <linux/iversion.h>
#include <linux/rw_hint.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
#include <trace/events/writeback.h>
#define CREATE_TRACE_POINTS
#include <trace/events/timestamp.h>
@@ -101,6 +103,70 @@ long get_nr_dirty_inodes(void)
return nr_dirty > 0 ? nr_dirty : 0;
}
+#ifdef CONFIG_DEBUG_FS
+static DEFINE_PER_CPU(long, mg_ctime_updates);
+static DEFINE_PER_CPU(long, mg_fine_stamps);
+static DEFINE_PER_CPU(long, mg_ctime_swaps);
+
+static long get_mg_ctime_updates(void)
+{
+ int i;
+ long sum = 0;
+
+ for_each_possible_cpu(i)
+ sum += per_cpu(mg_ctime_updates, i);
+ return sum < 0 ? 0 : sum;
+}
+
+static long get_mg_fine_stamps(void)
+{
+ int i;
+ long sum = 0;
+
+ for_each_possible_cpu(i)
+ sum += per_cpu(mg_fine_stamps, i);
+ return sum < 0 ? 0 : sum;
+}
+
+static long get_mg_ctime_swaps(void)
+{
+ int i;
+ long sum = 0;
+
+ for_each_possible_cpu(i)
+ sum += per_cpu(mg_ctime_swaps, i);
+ return sum < 0 ? 0 : sum;
+}
+
+#define mgtime_counter_inc(__var) this_cpu_inc(__var)
+
+static int mgts_show(struct seq_file *s, void *p)
+{
+ long ctime_updates = get_mg_ctime_updates();
+ long ctime_swaps = get_mg_ctime_swaps();
+ long fine_stamps = get_mg_fine_stamps();
+ long floor_swaps = get_mg_floor_swaps();
+
+ seq_printf(s, "%ld %ld %ld %ld\n",
+ ctime_updates, ctime_swaps, fine_stamps, floor_swaps);
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(mgts);
+
+static int __init mg_debugfs_init(void)
+{
+ debugfs_create_file("multigrain_timestamps", S_IFREG | S_IRUGO, NULL, NULL, &mgts_fops);
+ return 0;
+}
+late_initcall(mg_debugfs_init);
+
+#else /* ! CONFIG_DEBUG_FS */
+
+#define mgtime_counter_inc() do { } while (0)
+
+#endif /* CONFIG_DEBUG_FS */
+
/*
* Handle nr_inode sysctl
*/
@@ -2655,10 +2721,9 @@ EXPORT_SYMBOL(timestamp_truncate);
*
* If it is multigrain, then we first see if the coarse-grained timestamp is
* distinct from what we have. If so, then we'll just use that. If we have to
- * get a fine-grained timestamp, then do so, and try to swap it into the floor.
- * We accept the new floor value regardless of the outcome of the cmpxchg.
- * After that, we try to swap the new value into i_ctime_nsec. Again, we take
- * the resulting ctime, regardless of the outcome of the swap.
+ * get a fine-grained timestamp, then do so. After that, we try to swap the new
+ * value into i_ctime_nsec. We take the resulting ctime, regardless of the
+ * outcome of the swap.
*/
struct timespec64 inode_set_ctime_current(struct inode *inode)
{
@@ -2687,8 +2752,10 @@ struct timespec64 inode_set_ctime_current(struct inode *inode)
if (timespec64_compare(&now, &ctime) <= 0) {
ktime_get_real_ts64_mg(&now);
now = timestamp_truncate(now, inode);
+ mgtime_counter_inc(mg_fine_stamps);
}
}
+ mgtime_counter_inc(mg_ctime_updates);
/* No need to cmpxchg if it's exactly the same */
if (cns == now.tv_nsec && inode->i_ctime_sec == now.tv_sec) {
@@ -2702,6 +2769,7 @@ struct timespec64 inode_set_ctime_current(struct inode *inode)
/* If swap occurred, then we're (mostly) done */
inode->i_ctime_sec = now.tv_sec;
trace_ctime_ns_xchg(inode, cns, now.tv_nsec, cur);
+ mgtime_counter_inc(mg_ctime_swaps);
} else {
/*
* Was the change due to someone marking the old ctime QUERIED?
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
index 7aa85246c183..b9c8c597a073 100644
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -48,6 +48,7 @@ extern void ktime_get_coarse_real_ts64(struct timespec64 *ts);
/* Multigrain timestamp interfaces */
extern void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts);
extern void ktime_get_real_ts64_mg(struct timespec64 *ts);
+extern long get_mg_floor_swaps(void);
void getboottime64(struct timespec64 *ts);
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 16937242b904..94b0219955a2 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2440,7 +2440,7 @@ EXPORT_SYMBOL_GPL(ktime_get_coarse_real_ts64_mg);
* regardless of the outcome of the swap. Note that this is a filesystem
* specific interface and should be avoided outside of that context.
*/
-void ktime_get_real_ts64_mg(struct timespec64 *ts, u64 cookie)
+void ktime_get_real_ts64_mg(struct timespec64 *ts)
{
struct timekeeper *tk = &tk_core.timekeeper;
ktime_t old = atomic64_read(&mg_floor);
@@ -2464,6 +2464,7 @@ void ktime_get_real_ts64_mg(struct timespec64 *ts, u64 cookie)
if (atomic64_try_cmpxchg(&mg_floor, &old, mono)) {
ts->tv_nsec = 0;
timespec64_add_ns(ts, nsecs);
+ mgtime_counter_inc(mg_floor_swaps);
} else {
/*
* Something has changed mg_floor since "old" was
diff --git a/kernel/time/timekeeping_debug.c b/kernel/time/timekeeping_debug.c
index b73e8850e58d..9a3792072762 100644
--- a/kernel/time/timekeeping_debug.c
+++ b/kernel/time/timekeeping_debug.c
@@ -17,6 +17,9 @@
#define NUM_BINS 32
+/* incremented every time mg_floor is updated */
+DEFINE_PER_CPU(long, mg_floor_swaps);
+
static unsigned int sleep_time_bin[NUM_BINS] = {0};
static int tk_debug_sleep_time_show(struct seq_file *s, void *data)
@@ -53,3 +56,12 @@ void tk_debug_account_sleep_time(const struct timespec64 *t)
(s64)t->tv_sec, t->tv_nsec / NSEC_PER_MSEC);
}
+long get_mg_floor_swaps(void)
+{
+ int i;
+ long sum = 0;
+
+ for_each_possible_cpu(i)
+ sum += per_cpu(mg_floor_swaps, i);
+ return sum < 0 ? 0 : sum;
+}
diff --git a/kernel/time/timekeeping_internal.h b/kernel/time/timekeeping_internal.h
index 4ca2787d1642..2b49332b45a5 100644
--- a/kernel/time/timekeeping_internal.h
+++ b/kernel/time/timekeeping_internal.h
@@ -11,8 +11,11 @@
*/
#ifdef CONFIG_DEBUG_FS
extern void tk_debug_account_sleep_time(const struct timespec64 *t);
+DECLARE_PER_CPU(long, mg_floor_swaps);
+#define mgtime_counter_inc(__var) this_cpu_inc(__var)
#else
#define tk_debug_account_sleep_time(x)
+#define mgtime_counter_inc() do { } while (0)
#endif
#ifdef CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE
--
2.46.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v8 07/11] Documentation: add a new file documenting multigrain timestamps
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
` (5 preceding siblings ...)
2024-09-14 17:07 ` [PATCH v8 06/11] fs: add percpu counters for significant " Jeff Layton
@ 2024-09-14 17:07 ` Jeff Layton
2024-09-16 1:01 ` Bagas Sanjaya
2024-09-14 17:07 ` [PATCH v8 08/11] xfs: switch to " Jeff Layton
` (4 subsequent siblings)
11 siblings, 1 reply; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
Add a high-level document that describes how multigrain timestamps work,
rationale for them, and some info about implementation and tradeoffs.
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
Documentation/filesystems/index.rst | 1 +
Documentation/filesystems/multigrain-ts.rst | 121 ++++++++++++++++++++++++++++
2 files changed, 122 insertions(+)
diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystems/index.rst
index e8e496d23e1d..44e9e77ffe0d 100644
--- a/Documentation/filesystems/index.rst
+++ b/Documentation/filesystems/index.rst
@@ -29,6 +29,7 @@ algorithms work.
fiemap
files
locks
+ multigrain-ts
mount_api
quota
seq_file
diff --git a/Documentation/filesystems/multigrain-ts.rst b/Documentation/filesystems/multigrain-ts.rst
new file mode 100644
index 000000000000..97877ab3d933
--- /dev/null
+++ b/Documentation/filesystems/multigrain-ts.rst
@@ -0,0 +1,121 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================
+Multigrain Timestamps
+=====================
+
+Introduction
+============
+Historically, the kernel has always used coarse time values to stamp inodes.
+This value is updated every jiffy, so any change that happens within that jiffy
+will end up with the same timestamp.
+
+When the kernel goes to stamp an inode (due to a read or write), it first gets
+the current time and then compares it to the existing timestamp(s) to see
+whether anything will change. If nothing changed, then it can avoid updating
+the inode's metadata.
+
+Coarse timestamps are therefore good from a performance standpoint, since they
+reduce the need for metadata updates, but bad from the standpoint of
+determining whether anything has changed, since a lot of things can happen in a
+jiffy.
+
+They are particularly troublesome with NFSv3, where unchanging timestamps can
+make it difficult to tell whether to invalidate caches. NFSv4 provides a
+dedicated change attribute that should always show a visible change, but not
+all filesystems implement this properly, causing the NFS server to substitute
+the ctime in many cases.
+
+Multigrain timestamps aim to remedy this by selectively using fine-grained
+timestamps when a file has had its timestamps queried recently, and the current
+coarse-grained time does not cause a change.
+
+Inode Timestamps
+================
+There are currently 3 timestamps in the inode that are updated to the current
+wallclock time on different activity:
+
+ctime:
+ The inode change time. This is stamped with the current time whenever
+ the inode's metadata is changed. Note that this value is not settable
+ from userland.
+
+mtime:
+ The inode modification time. This is stamped with the current time
+ any time a file's contents change.
+
+atime:
+ The inode access time. This is stamped whenever an inode's contents are
+ read. Widely considered to be a terrible mistake. Usually avoided with
+ options like noatime or relatime.
+
+Updating the mtime always implies a change to the ctime, but updating the
+atime due to a read request does not.
+
+Multigrain timestamps are only tracked for the ctime and the mtime. atimes are
+not affected and always use the coarse-grained value (subject to the floor).
+
+Inode Timestamp Ordering
+========================
+
+In addition to just providing info about changes to individual files, file
+timestamps also serve an important purpose in applications like "make". These
+programs measure timestamps in order to determine whether source files might be
+newer than cached objects.
+
+Userland applications like make can only determine ordering based on
+operational boundaries. For a syscall those are the syscall entry and exit
+points. For io_uring or nfsd operations, that's the request submission and
+response. In the case of concurrent operations, userland can make no
+determination about the order in which things will occur.
+
+For instance, if a single thread modifies one file, and then another file in
+sequence, the second file must show an equal or later mtime than the first. The
+same is true if two threads are issuing similar operations that do not overlap
+in time.
+
+If however, two threads have racing syscalls that overlap in time, then there
+is no such guarantee, and the second file may appear to have been modified
+before, after or at the same time as the first, regardless of which one was
+submitted first.
+
+Multigrain Timestamp Implementation
+===================================
+Multigrain timestamps are aimed at ensuring that changes to a single file are
+always recognizable, without violating the ordering guarantees when multiple
+different files are modified. This affects the mtime and the ctime, but the
+atime will always use coarse-grained timestamps.
+
+It uses an unused bit in the i_ctime_nsec field to indicate whether the mtime
+or ctime has been queried. If either or both have, then the kernel takes
+special care to ensure the next timestamp update will display a visible change.
+This ensures tight cache coherency for use-cases like NFS, without sacrificing
+the benefits of reduced metadata updates when files aren't being watched.
+
+The Ctime Floor Value
+=====================
+It's not sufficient to simply use fine or coarse-grained timestamps based on
+whether the mtime or ctime has been queried. A file could get a fine grained
+timestamp, and then a second file modified later could get a coarse-grained one
+that appears earlier than the first, which would break the kernel's timestamp
+ordering guarantees.
+
+To mitigate this problem, we maintain a global floor value that ensures that
+this can't happen. The two files in the above example may appear to have been
+modified at the same time in such a case, but they will never show the reverse
+order. To avoid problems with realtime clock jumps, the floor is managed as a
+monotonic ktime_t, and the values are converted to realtime clock values as
+needed.
+
+Implementation Notes
+====================
+Multigrain timestamps are intended for use by local filesystems that get
+ctime values from the local clock. This is in contrast to network filesystems
+and the like that just mirror timestamp values from a server.
+
+For most filesystems, it's sufficient to just set the FS_MGTIME flag in the
+fstype->fs_flags in order to opt-in, providing the ctime is only ever set via
+inode_set_ctime_current(). If the filesystem has a ->getattr routine that
+doesn't call generic_fillattr, then you should have it call fill_mg_cmtime to
+fill those values. For setattr, it should use setattr_copy() to update the
+timestamps, or otherwise mimic its behavior.
--
2.46.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v8 08/11] xfs: switch to multigrain timestamps
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
` (6 preceding siblings ...)
2024-09-14 17:07 ` [PATCH v8 07/11] Documentation: add a new file documenting multigrain timestamps Jeff Layton
@ 2024-09-14 17:07 ` Jeff Layton
2024-09-14 17:07 ` [PATCH v8 09/11] ext4: " Jeff Layton
` (3 subsequent siblings)
11 siblings, 0 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
Enable multigrain timestamps, which should ensure that there is an
apparent change to the timestamp whenever it has been written after
being actively observed via getattr.
Also, anytime the mtime changes, the ctime must also change, and those
are now the only two options for xfs_trans_ichgtime. Have that function
unconditionally bump the ctime, and ASSERT that XFS_ICHGTIME_CHG is
always set.
Finally, stop setting STATX_CHANGE_COOKIE in getattr, since the ctime
should give us better semantics now.
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/xfs/libxfs/xfs_trans_inode.c | 6 +++---
fs/xfs/xfs_iops.c | 10 +++-------
fs/xfs/xfs_super.c | 2 +-
3 files changed, 7 insertions(+), 11 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_trans_inode.c b/fs/xfs/libxfs/xfs_trans_inode.c
index 3c40f37e82c7..c962ad64b0c1 100644
--- a/fs/xfs/libxfs/xfs_trans_inode.c
+++ b/fs/xfs/libxfs/xfs_trans_inode.c
@@ -62,12 +62,12 @@ xfs_trans_ichgtime(
ASSERT(tp);
xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
- tv = current_time(inode);
+ /* If the mtime changes, then ctime must also change */
+ ASSERT(flags & XFS_ICHGTIME_CHG);
+ tv = inode_set_ctime_current(inode);
if (flags & XFS_ICHGTIME_MOD)
inode_set_mtime_to_ts(inode, tv);
- if (flags & XFS_ICHGTIME_CHG)
- inode_set_ctime_to_ts(inode, tv);
if (flags & XFS_ICHGTIME_ACCESS)
inode_set_atime_to_ts(inode, tv);
if (flags & XFS_ICHGTIME_CREATE)
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 1cdc8034f54d..a1c4a350a6db 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -597,8 +597,9 @@ xfs_vn_getattr(
stat->gid = vfsgid_into_kgid(vfsgid);
stat->ino = ip->i_ino;
stat->atime = inode_get_atime(inode);
- stat->mtime = inode_get_mtime(inode);
- stat->ctime = inode_get_ctime(inode);
+
+ fill_mg_cmtime(stat, request_mask, inode);
+
stat->blocks = XFS_FSB_TO_BB(mp, ip->i_nblocks + ip->i_delayed_blks);
if (xfs_has_v3inodes(mp)) {
@@ -608,11 +609,6 @@ xfs_vn_getattr(
}
}
- if ((request_mask & STATX_CHANGE_COOKIE) && IS_I_VERSION(inode)) {
- stat->change_cookie = inode_query_iversion(inode);
- stat->result_mask |= STATX_CHANGE_COOKIE;
- }
-
/*
* Note: If you add another clause to set an attribute flag, please
* update attributes_mask below.
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 27e9f749c4c7..210481b03fdb 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -2052,7 +2052,7 @@ static struct file_system_type xfs_fs_type = {
.init_fs_context = xfs_init_fs_context,
.parameters = xfs_fs_parameters,
.kill_sb = xfs_kill_sb,
- .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
+ .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP | FS_MGTIME,
};
MODULE_ALIAS_FS("xfs");
--
2.46.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v8 09/11] ext4: switch to multigrain timestamps
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
` (7 preceding siblings ...)
2024-09-14 17:07 ` [PATCH v8 08/11] xfs: switch to " Jeff Layton
@ 2024-09-14 17:07 ` Jeff Layton
2024-09-14 17:07 ` [PATCH v8 10/11] btrfs: convert " Jeff Layton
` (2 subsequent siblings)
11 siblings, 0 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
Enable multigrain timestamps, which should ensure that there is an
apparent change to the timestamp whenever it has been written after
being actively observed via getattr.
For ext4, we only need to enable the FS_MGTIME flag.
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/ext4/super.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index e72145c4ae5a..a125d9435b8a 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -7298,7 +7298,7 @@ static struct file_system_type ext4_fs_type = {
.init_fs_context = ext4_init_fs_context,
.parameters = ext4_param_specs,
.kill_sb = ext4_kill_sb,
- .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
+ .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP | FS_MGTIME,
};
MODULE_ALIAS_FS("ext4");
--
2.46.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v8 10/11] btrfs: convert to multigrain timestamps
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
` (8 preceding siblings ...)
2024-09-14 17:07 ` [PATCH v8 09/11] ext4: " Jeff Layton
@ 2024-09-14 17:07 ` Jeff Layton
2024-09-14 17:07 ` [PATCH v8 11/11] tmpfs: add support for " Jeff Layton
2024-09-26 16:59 ` [PATCH v8 00/11] fs: multigrain timestamp redux Randy Dunlap
11 siblings, 0 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
Enable multigrain timestamps, which should ensure that there is an
apparent change to the timestamp whenever it has been written after
being actively observed via getattr.
Beyond enabling the FS_MGTIME flag, this patch eliminates
update_time_for_write, which goes to great pains to avoid in-memory
stores. Just have it overwrite the timestamps unconditionally.
Note that this also drops the IS_I_VERSION check and unconditionally
bumps the change attribute, since SB_I_VERSION is always set on btrfs.
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/btrfs/file.c | 25 ++++---------------------
fs/btrfs/super.c | 3 ++-
2 files changed, 6 insertions(+), 22 deletions(-)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 2aeb8116549c..1656ad7498b8 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1120,26 +1120,6 @@ void btrfs_check_nocow_unlock(struct btrfs_inode *inode)
btrfs_drew_write_unlock(&inode->root->snapshot_lock);
}
-static void update_time_for_write(struct inode *inode)
-{
- struct timespec64 now, ts;
-
- if (IS_NOCMTIME(inode))
- return;
-
- now = current_time(inode);
- ts = inode_get_mtime(inode);
- if (!timespec64_equal(&ts, &now))
- inode_set_mtime_to_ts(inode, now);
-
- ts = inode_get_ctime(inode);
- if (!timespec64_equal(&ts, &now))
- inode_set_ctime_to_ts(inode, now);
-
- if (IS_I_VERSION(inode))
- inode_inc_iversion(inode);
-}
-
int btrfs_write_check(struct kiocb *iocb, struct iov_iter *from, size_t count)
{
struct file *file = iocb->ki_filp;
@@ -1170,7 +1150,10 @@ int btrfs_write_check(struct kiocb *iocb, struct iov_iter *from, size_t count)
* need to start yet another transaction to update the inode as we will
* update the inode when we finish writing whatever data we write.
*/
- update_time_for_write(inode);
+ if (!IS_NOCMTIME(inode)) {
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+ inode_inc_iversion(inode);
+ }
start_pos = round_down(pos, fs_info->sectorsize);
oldsize = i_size_read(inode);
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 98fa0f382480..d423acfe11d0 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -2198,7 +2198,8 @@ static struct file_system_type btrfs_fs_type = {
.init_fs_context = btrfs_init_fs_context,
.parameters = btrfs_fs_parameters,
.kill_sb = btrfs_kill_super,
- .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA | FS_ALLOW_IDMAP,
+ .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA |
+ FS_ALLOW_IDMAP | FS_MGTIME,
};
MODULE_ALIAS_FS("btrfs");
--
2.46.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v8 11/11] tmpfs: add support for multigrain timestamps
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
` (9 preceding siblings ...)
2024-09-14 17:07 ` [PATCH v8 10/11] btrfs: convert " Jeff Layton
@ 2024-09-14 17:07 ` Jeff Layton
2024-09-26 16:59 ` [PATCH v8 00/11] fs: multigrain timestamp redux Randy Dunlap
11 siblings, 0 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 17:07 UTC (permalink / raw)
To: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
Enable multigrain timestamps, which should ensure that there is an
apparent change to the timestamp whenever it has been written after
being actively observed via getattr.
tmpfs only requires the FS_MGTIME flag.
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
mm/shmem.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index 5a77acf6ac6a..5f17eaaa32e2 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -4804,7 +4804,7 @@ static struct file_system_type shmem_fs_type = {
.parameters = shmem_fs_parameters,
#endif
.kill_sb = kill_litter_super,
- .fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP,
+ .fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP | FS_MGTIME,
};
void __init shmem_init(void)
--
2.46.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-14 17:07 ` [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper Jeff Layton
@ 2024-09-14 20:10 ` John Stultz
2024-09-14 23:14 ` Jeff Layton
2024-09-16 10:12 ` Thomas Gleixner
2024-09-30 19:13 ` Thomas Gleixner
2 siblings, 1 reply; 36+ messages in thread
From: John Stultz @ 2024-09-14 20:10 UTC (permalink / raw)
To: Jeff Layton
Cc: Thomas Gleixner, Stephen Boyd, Alexander Viro, Christian Brauner,
Jan Kara, Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet, Chandan Babu R, Darrick J. Wong,
Theodore Ts'o, Andreas Dilger, Chris Mason, Josef Bacik,
David Sterba, Hugh Dickins, Andrew Morton, Chuck Lever,
Vadim Fedorenko, Randy Dunlap, linux-kernel, linux-fsdevel,
linux-trace-kernel, linux-doc, linux-xfs, linux-ext4, linux-btrfs,
linux-nfs, linux-mm
On Sat, Sep 14, 2024 at 10:07 AM Jeff Layton <jlayton@kernel.org> wrote:
>
> For multigrain timestamps, we must keep track of the latest timestamp
> that has ever been handed out, and never hand out a coarse time below
> that value.
>
> Add a static singleton atomic64_t into timekeeper.c that we can use to
> keep track of the latest fine-grained time ever handed out. This is
> tracked as a monotonic ktime_t value to ensure that it isn't affected by
> clock jumps.
>
> Add two new public interfaces:
>
> - ktime_get_coarse_real_ts64_mg() fills a timespec64 with the later of the
> coarse-grained clock and the floor time
>
> - ktime_get_real_ts64_mg() gets the fine-grained clock value, and tries
> to swap it into the floor. A timespec64 is filled with the result.
>
> Since the floor is global, we take great pains to avoid updating it
> unless it's absolutely necessary. If we do the cmpxchg and find that the
> value has been updated since we fetched it, then we discard the
> fine-grained time that was fetched in favor of the recent update.
>
> To maximize the window of this occurring when multiple tasks are racing
> to update the floor, ktime_get_coarse_real_ts64_mg returns a cookie
> value that represents the state of the floor tracking word, and
> ktime_get_real_ts64_mg accepts a cookie value that it uses as the "old"
> value when calling cmpxchg().
This last bit seems out of date.
> ---
> include/linux/timekeeping.h | 4 +++
> kernel/time/timekeeping.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 86 insertions(+)
>
> diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
> index fc12a9ba2c88..7aa85246c183 100644
> --- a/include/linux/timekeeping.h
> +++ b/include/linux/timekeeping.h
> @@ -45,6 +45,10 @@ extern void ktime_get_real_ts64(struct timespec64 *tv);
> extern void ktime_get_coarse_ts64(struct timespec64 *ts);
> extern void ktime_get_coarse_real_ts64(struct timespec64 *ts);
>
> +/* Multigrain timestamp interfaces */
> +extern void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts);
> +extern void ktime_get_real_ts64_mg(struct timespec64 *ts);
> +
> void getboottime64(struct timespec64 *ts);
>
> /*
> diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
> index 5391e4167d60..16937242b904 100644
> --- a/kernel/time/timekeeping.c
> +++ b/kernel/time/timekeeping.c
> @@ -114,6 +114,13 @@ static struct tk_fast tk_fast_raw ____cacheline_aligned = {
> .base[1] = FAST_TK_INIT,
> };
>
> +/*
> + * This represents the latest fine-grained time that we have handed out as a
> + * timestamp on the system. Tracked as a monotonic ktime_t, and converted to the
> + * realtime clock on an as-needed basis.
> + */
> +static __cacheline_aligned_in_smp atomic64_t mg_floor;
> +
> static inline void tk_normalize_xtime(struct timekeeper *tk)
> {
> while (tk->tkr_mono.xtime_nsec >= ((u64)NSEC_PER_SEC << tk->tkr_mono.shift)) {
> @@ -2394,6 +2401,81 @@ void ktime_get_coarse_real_ts64(struct timespec64 *ts)
> }
> EXPORT_SYMBOL(ktime_get_coarse_real_ts64);
>
> +/**
> + * ktime_get_coarse_real_ts64_mg - get later of coarse grained time or floor
> + * @ts: timespec64 to be filled
> + *
> + * Adjust floor to realtime and compare it to the coarse time. Fill
> + * @ts with the latest one. Note that this is a filesystem-specific
> + * interface and should be avoided outside of that context.
> + */
> +void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts)
> +{
> + struct timekeeper *tk = &tk_core.timekeeper;
> + u64 floor = atomic64_read(&mg_floor);
> + ktime_t f_real, offset, coarse;
> + unsigned int seq;
> +
> + WARN_ON(timekeeping_suspended);
> +
> + do {
> + seq = read_seqcount_begin(&tk_core.seq);
> + *ts = tk_xtime(tk);
> + offset = *offsets[TK_OFFS_REAL];
> + } while (read_seqcount_retry(&tk_core.seq, seq));
> +
> + coarse = timespec64_to_ktime(*ts);
> + f_real = ktime_add(floor, offset);
> + if (ktime_after(f_real, coarse))
> + *ts = ktime_to_timespec64(f_real);
> +}
> +EXPORT_SYMBOL_GPL(ktime_get_coarse_real_ts64_mg);
> +
> +/**
> + * ktime_get_real_ts64_mg - attempt to update floor value and return result
> + * @ts: pointer to the timespec to be set
> + *
> + * Get a current monotonic fine-grained time value and attempt to swap
> + * it into the floor. @ts will be filled with the resulting floor value,
> + * regardless of the outcome of the swap. Note that this is a filesystem
> + * specific interface and should be avoided outside of that context.
> + */
> +void ktime_get_real_ts64_mg(struct timespec64 *ts, u64 cookie)
Still passing a cookie. It doesn't match the header definition, so I'm
surprised this builds.
> +{
> + struct timekeeper *tk = &tk_core.timekeeper;
> + ktime_t old = atomic64_read(&mg_floor);
> + ktime_t offset, mono;
> + unsigned int seq;
> + u64 nsecs;
> +
> + WARN_ON(timekeeping_suspended);
> +
> + do {
> + seq = read_seqcount_begin(&tk_core.seq);
> +
> + ts->tv_sec = tk->xtime_sec;
> + mono = tk->tkr_mono.base;
> + nsecs = timekeeping_get_ns(&tk->tkr_mono);
> + offset = *offsets[TK_OFFS_REAL];
> + } while (read_seqcount_retry(&tk_core.seq, seq));
> +
> + mono = ktime_add_ns(mono, nsecs);
> +
> + if (atomic64_try_cmpxchg(&mg_floor, &old, mono)) {
> + ts->tv_nsec = 0;
> + timespec64_add_ns(ts, nsecs);
> + } else {
> + /*
> + * Something has changed mg_floor since "old" was
> + * fetched. "old" has now been updated with the
> + * current value of mg_floor, so use that to return
> + * the current coarse floor value.
> + */
> + *ts = ktime_to_timespec64(ktime_add(old, offset));
> + }
> +}
> +EXPORT_SYMBOL_GPL(ktime_get_real_ts64_mg);
Other than those issues, I'm ok with it. Thanks again for working
through my concerns!
Since I'm traveling for LPC soon, to save the next cycle, once the
fixes above are sorted:
Acked-by: John Stultz <jstultz@google.com>
thanks
-john
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-14 20:10 ` John Stultz
@ 2024-09-14 23:14 ` Jeff Layton
0 siblings, 0 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-14 23:14 UTC (permalink / raw)
To: John Stultz
Cc: Thomas Gleixner, Stephen Boyd, Alexander Viro, Christian Brauner,
Jan Kara, Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet, Chandan Babu R, Darrick J. Wong,
Theodore Ts'o, Andreas Dilger, Chris Mason, Josef Bacik,
David Sterba, Hugh Dickins, Andrew Morton, Chuck Lever,
Vadim Fedorenko, Randy Dunlap, linux-kernel, linux-fsdevel,
linux-trace-kernel, linux-doc, linux-xfs, linux-ext4, linux-btrfs,
linux-nfs, linux-mm
On Sat, 2024-09-14 at 13:10 -0700, John Stultz wrote:
> On Sat, Sep 14, 2024 at 10:07 AM Jeff Layton <jlayton@kernel.org> wrote:
> >
> > For multigrain timestamps, we must keep track of the latest timestamp
> > that has ever been handed out, and never hand out a coarse time below
> > that value.
> >
> > Add a static singleton atomic64_t into timekeeper.c that we can use to
> > keep track of the latest fine-grained time ever handed out. This is
> > tracked as a monotonic ktime_t value to ensure that it isn't affected by
> > clock jumps.
> >
> > Add two new public interfaces:
> >
> > - ktime_get_coarse_real_ts64_mg() fills a timespec64 with the later of the
> > coarse-grained clock and the floor time
> >
> > - ktime_get_real_ts64_mg() gets the fine-grained clock value, and tries
> > to swap it into the floor. A timespec64 is filled with the result.
> >
> > Since the floor is global, we take great pains to avoid updating it
> > unless it's absolutely necessary. If we do the cmpxchg and find that the
> > value has been updated since we fetched it, then we discard the
> > fine-grained time that was fetched in favor of the recent update.
> >
> > To maximize the window of this occurring when multiple tasks are racing
> > to update the floor, ktime_get_coarse_real_ts64_mg returns a cookie
> > value that represents the state of the floor tracking word, and
> > ktime_get_real_ts64_mg accepts a cookie value that it uses as the "old"
> > value when calling cmpxchg().
>
> This last bit seems out of date.
>
Thanks. Dropped the last paragraph.
> > ---
> > include/linux/timekeeping.h | 4 +++
> > kernel/time/timekeeping.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 86 insertions(+)
> >
> > diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
> > index fc12a9ba2c88..7aa85246c183 100644
> > --- a/include/linux/timekeeping.h
> > +++ b/include/linux/timekeeping.h
> > @@ -45,6 +45,10 @@ extern void ktime_get_real_ts64(struct timespec64 *tv);
> > extern void ktime_get_coarse_ts64(struct timespec64 *ts);
> > extern void ktime_get_coarse_real_ts64(struct timespec64 *ts);
> >
> > +/* Multigrain timestamp interfaces */
> > +extern void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts);
> > +extern void ktime_get_real_ts64_mg(struct timespec64 *ts);
> > +
> > void getboottime64(struct timespec64 *ts);
> >
> > /*
> > diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
> > index 5391e4167d60..16937242b904 100644
> > --- a/kernel/time/timekeeping.c
> > +++ b/kernel/time/timekeeping.c
> > @@ -114,6 +114,13 @@ static struct tk_fast tk_fast_raw ____cacheline_aligned = {
> > .base[1] = FAST_TK_INIT,
> > };
> >
> > +/*
> > + * This represents the latest fine-grained time that we have handed out as a
> > + * timestamp on the system. Tracked as a monotonic ktime_t, and converted to the
> > + * realtime clock on an as-needed basis.
> > + */
> > +static __cacheline_aligned_in_smp atomic64_t mg_floor;
> > +
> > static inline void tk_normalize_xtime(struct timekeeper *tk)
> > {
> > while (tk->tkr_mono.xtime_nsec >= ((u64)NSEC_PER_SEC << tk->tkr_mono.shift)) {
> > @@ -2394,6 +2401,81 @@ void ktime_get_coarse_real_ts64(struct timespec64 *ts)
> > }
> > EXPORT_SYMBOL(ktime_get_coarse_real_ts64);
> >
> > +/**
> > + * ktime_get_coarse_real_ts64_mg - get later of coarse grained time or floor
> > + * @ts: timespec64 to be filled
> > + *
> > + * Adjust floor to realtime and compare it to the coarse time. Fill
> > + * @ts with the latest one. Note that this is a filesystem-specific
> > + * interface and should be avoided outside of that context.
> > + */
> > +void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts)
> > +{
> > + struct timekeeper *tk = &tk_core.timekeeper;
> > + u64 floor = atomic64_read(&mg_floor);
> > + ktime_t f_real, offset, coarse;
> > + unsigned int seq;
> > +
> > + WARN_ON(timekeeping_suspended);
> > +
> > + do {
> > + seq = read_seqcount_begin(&tk_core.seq);
> > + *ts = tk_xtime(tk);
> > + offset = *offsets[TK_OFFS_REAL];
> > + } while (read_seqcount_retry(&tk_core.seq, seq));
> > +
> > + coarse = timespec64_to_ktime(*ts);
> > + f_real = ktime_add(floor, offset);
> > + if (ktime_after(f_real, coarse))
> > + *ts = ktime_to_timespec64(f_real);
> > +}
> > +EXPORT_SYMBOL_GPL(ktime_get_coarse_real_ts64_mg);
> > +
> > +/**
> > + * ktime_get_real_ts64_mg - attempt to update floor value and return result
> > + * @ts: pointer to the timespec to be set
> > + *
> > + * Get a current monotonic fine-grained time value and attempt to swap
> > + * it into the floor. @ts will be filled with the resulting floor value,
> > + * regardless of the outcome of the swap. Note that this is a filesystem
> > + * specific interface and should be avoided outside of that context.
> > + */
> > +void ktime_get_real_ts64_mg(struct timespec64 *ts, u64 cookie)
>
> Still passing a cookie. It doesn't match the header definition, so I'm
> surprised this builds.
>
Yeah, I didn't see a warning when I built it. Luckily the extra
parameter is ignored anyway, so it no harm. I've fixed that up in my
tree.
> > +{
> > + struct timekeeper *tk = &tk_core.timekeeper;
> > + ktime_t old = atomic64_read(&mg_floor);
> > + ktime_t offset, mono;
> > + unsigned int seq;
> > + u64 nsecs;
> > +
> > + WARN_ON(timekeeping_suspended);
> > +
> > + do {
> > + seq = read_seqcount_begin(&tk_core.seq);
> > +
> > + ts->tv_sec = tk->xtime_sec;
> > + mono = tk->tkr_mono.base;
> > + nsecs = timekeeping_get_ns(&tk->tkr_mono);
> > + offset = *offsets[TK_OFFS_REAL];
> > + } while (read_seqcount_retry(&tk_core.seq, seq));
> > +
> > + mono = ktime_add_ns(mono, nsecs);
> > +
> > + if (atomic64_try_cmpxchg(&mg_floor, &old, mono)) {
> > + ts->tv_nsec = 0;
> > + timespec64_add_ns(ts, nsecs);
> > + } else {
> > + /*
> > + * Something has changed mg_floor since "old" was
> > + * fetched. "old" has now been updated with the
> > + * current value of mg_floor, so use that to return
> > + * the current coarse floor value.
> > + */
> > + *ts = ktime_to_timespec64(ktime_add(old, offset));
> > + }
> > +}
> > +EXPORT_SYMBOL_GPL(ktime_get_real_ts64_mg);
>
> Other than those issues, I'm ok with it. Thanks again for working
> through my concerns!
>
> Since I'm traveling for LPC soon, to save the next cycle, once the
> fixes above are sorted:
> Acked-by: John Stultz <jstultz@google.com>
>
Thanks for the review!
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 05/11] fs: tracepoints around multigrain timestamp events
2024-09-14 17:07 ` [PATCH v8 05/11] fs: tracepoints around multigrain timestamp events Jeff Layton
@ 2024-09-15 8:21 ` Steven Rostedt
0 siblings, 0 replies; 36+ messages in thread
From: Steven Rostedt @ 2024-09-15 8:21 UTC (permalink / raw)
To: Jeff Layton
Cc: John Stultz, Thomas Gleixner, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet, Chandan Babu R, Darrick J. Wong,
Theodore Ts'o, Andreas Dilger, Chris Mason, Josef Bacik,
David Sterba, Hugh Dickins, Andrew Morton, Chuck Lever,
Vadim Fedorenko, Randy Dunlap, linux-kernel, linux-fsdevel,
linux-trace-kernel, linux-doc, linux-xfs, linux-ext4, linux-btrfs,
linux-nfs, linux-mm
On Sat, 14 Sep 2024 13:07:18 -0400
Jeff Layton <jlayton@kernel.org> wrote:
> Add some tracepoints around various multigrain timestamp events.
>
> Reviewed-by: Josef Bacik <josef@toxicpanda.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Reviewed-by: Jan Kara <jack@suse.cz>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
From a tracing POV, nothing looks out of ordinary.
Reviewed-by: Steven Rostedt (Google) <rostedt@goodmis.org>
-- Steve
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 07/11] Documentation: add a new file documenting multigrain timestamps
2024-09-14 17:07 ` [PATCH v8 07/11] Documentation: add a new file documenting multigrain timestamps Jeff Layton
@ 2024-09-16 1:01 ` Bagas Sanjaya
2024-09-19 16:53 ` Jeff Layton
0 siblings, 1 reply; 36+ messages in thread
From: Bagas Sanjaya @ 2024-09-16 1:01 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Thomas Gleixner, Stephen Boyd,
Alexander Viro, Christian Brauner, Jan Kara, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet,
Chandan Babu R, Darrick J. Wong, Theodore Ts'o,
Andreas Dilger, Chris Mason, Josef Bacik, David Sterba,
Hugh Dickins, Andrew Morton, Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
[-- Attachment #1: Type: text/plain, Size: 508 bytes --]
On Sat, Sep 14, 2024 at 01:07:20PM -0400, Jeff Layton wrote:
> +Multigrain timestamps aim to remedy this by selectively using fine-grained
> +timestamps when a file has had its timestamps queried recently, and the current
> +coarse-grained time does not cause a change.
Do you mean using fine-grained timestamps when timestamps of a file has been
recently queried/modified BUT its coarse-grained timestamps aren't changed?
Confused...
--
An old man doll... just what I always wanted! - Clara
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-14 17:07 ` [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper Jeff Layton
2024-09-14 20:10 ` John Stultz
@ 2024-09-16 10:12 ` Thomas Gleixner
2024-09-16 10:32 ` Thomas Gleixner
2024-09-19 16:50 ` Jeff Layton
2024-09-30 19:13 ` Thomas Gleixner
2 siblings, 2 replies; 36+ messages in thread
From: Thomas Gleixner @ 2024-09-16 10:12 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
On Sat, Sep 14 2024 at 13:07, Jeff Layton wrote:
> For multigrain timestamps, we must keep track of the latest timestamp
What is a multgrain timestamp? Can you please describe the concept
behind it? I'm not going to chase random documentation or whatever
because change logs have to self contained.
And again 'we' do nothing. Describe the problem in technical terms and
do not impersonate code.
> To maximize the window of this occurring when multiple tasks are racing
> to update the floor, ktime_get_coarse_real_ts64_mg returns a cookie
> value that represents the state of the floor tracking word, and
> ktime_get_real_ts64_mg accepts a cookie value that it uses as the "old"
> value when calling cmpxchg().
Clearly:
> +void ktime_get_real_ts64_mg(struct timespec64 *ts, u64 cookie)
Can you please get your act together?
> +/**
> + * ktime_get_coarse_real_ts64_mg - get later of coarse grained time or floor
> + * @ts: timespec64 to be filled
> + *
> + * Adjust floor to realtime and compare it to the coarse time. Fill
> + * @ts with the latest one.
This explains nothing.
> Note that this is a filesystem-specific
> + * interface and should be avoided outside of that context.
> + */
> +void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts)
> +{
> + struct timekeeper *tk = &tk_core.timekeeper;
> + u64 floor = atomic64_read(&mg_floor);
> + ktime_t f_real, offset, coarse;
> + unsigned int seq;
> +
> + WARN_ON(timekeeping_suspended);
> +
> + do {
> + seq = read_seqcount_begin(&tk_core.seq);
> + *ts = tk_xtime(tk);
> + offset = *offsets[TK_OFFS_REAL];
Why this indirection? What's wrong with using
tk_core.timekeeper.offs_real directly?
> + } while (read_seqcount_retry(&tk_core.seq, seq));
> +
> + coarse = timespec64_to_ktime(*ts);
> + f_real = ktime_add(floor, offset);
How is any of this synchronized against concurrent updates of the floor
value or the offset? I'm failing to see anything which keeps this
consistent. If this is magically consistent then it wants a big fat
comment in the code which explains it.
> +void ktime_get_real_ts64_mg(struct timespec64 *ts, u64 cookie)
What is this cookie argument for and how does that match the
declaration?
> +extern void ktime_get_real_ts64_mg(struct timespec64 *ts);
This does not even build.
> +{
> + struct timekeeper *tk = &tk_core.timekeeper;
> + ktime_t old = atomic64_read(&mg_floor);
> + ktime_t offset, mono;
> + unsigned int seq;
> + u64 nsecs;
> +
> + WARN_ON(timekeeping_suspended);
WARN_ON_ONCE() if at all.
> + do {
> + seq = read_seqcount_begin(&tk_core.seq);
> +
> + ts->tv_sec = tk->xtime_sec;
> + mono = tk->tkr_mono.base;
> + nsecs = timekeeping_get_ns(&tk->tkr_mono);
> + offset = *offsets[TK_OFFS_REAL];
> + } while (read_seqcount_retry(&tk_core.seq, seq));
> +
> + mono = ktime_add_ns(mono, nsecs);
> +
> + if (atomic64_try_cmpxchg(&mg_floor, &old, mono)) {
> + ts->tv_nsec = 0;
> + timespec64_add_ns(ts, nsecs);
> + } else {
> + /*
> + * Something has changed mg_floor since "old" was
> + * fetched. "old" has now been updated with the
> + * current value of mg_floor, so use that to return
> + * the current coarse floor value.
'Something has changed' is a truly understandable technical
explanation.
I'm not going to accept this voodoo which makes everyone scratch his
head who wasn't involved in this.
Thanks,
tglx
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 06/11] fs: add percpu counters for significant multigrain timestamp events
2024-09-14 17:07 ` [PATCH v8 06/11] fs: add percpu counters for significant " Jeff Layton
@ 2024-09-16 10:20 ` Thomas Gleixner
0 siblings, 0 replies; 36+ messages in thread
From: Thomas Gleixner @ 2024-09-16 10:20 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
On Sat, Sep 14 2024 at 13:07, Jeff Layton wrote:
> fs/inode.c | 76 ++++++++++++++++++++++++++++++++++++--
> include/linux/timekeeping.h | 1 +
> kernel/time/timekeeping.c | 3 +-
> kernel/time/timekeeping_debug.c | 12 ++++++
> kernel/time/timekeeping_internal.h | 3 ++
So the subject says 'fs:'. This is not how it works.
Provide the timekeeping changes in a separate patch and then add the fs
voodoo. Documentation is pretty clear about this, no?
> diff --git a/kernel/time/timekeeping_debug.c b/kernel/time/timekeeping_debug.c
> index b73e8850e58d..9a3792072762 100644
> --- a/kernel/time/timekeeping_debug.c
> +++ b/kernel/time/timekeeping_debug.c
> @@ -17,6 +17,9 @@
>
> #define NUM_BINS 32
>
> +/* incremented every time mg_floor is updated */
Sentences start with a uppercase letter.
> +DEFINE_PER_CPU(long, mg_floor_swaps);
Why is this long? This is a counter which always counts up..
> static unsigned int sleep_time_bin[NUM_BINS] = {0};
>
> static int tk_debug_sleep_time_show(struct seq_file *s, void *data)
> @@ -53,3 +56,12 @@ void tk_debug_account_sleep_time(const struct timespec64 *t)
> (s64)t->tv_sec, t->tv_nsec / NSEC_PER_MSEC);
> }
>
> +long get_mg_floor_swaps(void)
Can we please have a proper subsystem prefix and not this get_*()
notation. It's horrible to grep for. timekeeping_mg_get_...() makes it
clear where this function belongs to, no?
> +{
> + int i;
> + long sum = 0;
https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#variable-declarations
Also please use 'cpu' instead of 'i'. Self explanatory variable names
have a value.
> + for_each_possible_cpu(i)
> + sum += per_cpu(mg_floor_swaps, i);
This needs annotation for kcsan as this is a racy access.
> + return sum < 0 ? 0 : sum;
> +}
> diff --git a/kernel/time/timekeeping_internal.h b/kernel/time/timekeeping_internal.h
> index 4ca2787d1642..2b49332b45a5 100644
> --- a/kernel/time/timekeeping_internal.h
> +++ b/kernel/time/timekeeping_internal.h
> @@ -11,8 +11,11 @@
> */
> #ifdef CONFIG_DEBUG_FS
> extern void tk_debug_account_sleep_time(const struct timespec64 *t);
> +DECLARE_PER_CPU(long, mg_floor_swaps);
> +#define mgtime_counter_inc(__var) this_cpu_inc(__var)
Please use static inlines for this.
Thanks,
tglx
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-16 10:12 ` Thomas Gleixner
@ 2024-09-16 10:32 ` Thomas Gleixner
2024-09-16 10:57 ` Jeff Layton
2024-09-19 16:50 ` Jeff Layton
1 sibling, 1 reply; 36+ messages in thread
From: Thomas Gleixner @ 2024-09-16 10:32 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
On Mon, Sep 16 2024 at 12:12, Thomas Gleixner wrote:
> On Sat, Sep 14 2024 at 13:07, Jeff Layton wrote:
>> + do {
>> + seq = read_seqcount_begin(&tk_core.seq);
>> +
>> + ts->tv_sec = tk->xtime_sec;
>> + mono = tk->tkr_mono.base;
>> + nsecs = timekeeping_get_ns(&tk->tkr_mono);
>> + offset = *offsets[TK_OFFS_REAL];
>> + } while (read_seqcount_retry(&tk_core.seq, seq));
>> +
>> + mono = ktime_add_ns(mono, nsecs);
>> +
>> + if (atomic64_try_cmpxchg(&mg_floor, &old, mono)) {
>> + ts->tv_nsec = 0;
>> + timespec64_add_ns(ts, nsecs);
>> + } else {
>> + /*
>> + * Something has changed mg_floor since "old" was
>> + * fetched. "old" has now been updated with the
>> + * current value of mg_floor, so use that to return
>> + * the current coarse floor value.
>
> 'Something has changed' is a truly understandable technical
> explanation.
old = mg_floor
mono = T1;
mg_floor = mono
preemption
do {
mono = T2;
}
cmpxchg fails and the function returns a value based on T1
No?
Thanks,
tglx
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-16 10:32 ` Thomas Gleixner
@ 2024-09-16 10:57 ` Jeff Layton
2024-09-30 19:16 ` Thomas Gleixner
0 siblings, 1 reply; 36+ messages in thread
From: Jeff Layton @ 2024-09-16 10:57 UTC (permalink / raw)
To: Thomas Gleixner, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, 2024-09-16 at 12:32 +0200, Thomas Gleixner wrote:
> On Mon, Sep 16 2024 at 12:12, Thomas Gleixner wrote:
> > On Sat, Sep 14 2024 at 13:07, Jeff Layton wrote:
> > > + do {
> > > + seq = read_seqcount_begin(&tk_core.seq);
> > > +
> > > + ts->tv_sec = tk->xtime_sec;
> > > + mono = tk->tkr_mono.base;
> > > + nsecs = timekeeping_get_ns(&tk->tkr_mono);
> > > + offset = *offsets[TK_OFFS_REAL];
> > > + } while (read_seqcount_retry(&tk_core.seq, seq));
> > > +
> > > + mono = ktime_add_ns(mono, nsecs);
> > > +
> > > + if (atomic64_try_cmpxchg(&mg_floor, &old, mono)) {
> > > + ts->tv_nsec = 0;
> > > + timespec64_add_ns(ts, nsecs);
> > > + } else {
> > > + /*
> > > + * Something has changed mg_floor since "old" was
> > > + * fetched. "old" has now been updated with the
> > > + * current value of mg_floor, so use that to return
> > > + * the current coarse floor value.
> >
> > 'Something has changed' is a truly understandable technical
> > explanation.
>
> old = mg_floor
> mono = T1;
> mg_floor = mono
> preemption
>
> do {
> mono = T2;
> }
>
> cmpxchg fails and the function returns a value based on T1
>
> No?
>
>
Packing for LPC, so I can't respond to all of these just now, but I
will later. You're correct, but either outcome is OK.
The requirement is that we don't hand out any values that were below
the floor at the time that the task entered the kernel. Since the time
changed while the task was already inside the kernel, either T1 or T2
would be valid timestamps.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-16 10:12 ` Thomas Gleixner
2024-09-16 10:32 ` Thomas Gleixner
@ 2024-09-19 16:50 ` Jeff Layton
2024-09-30 19:43 ` Thomas Gleixner
1 sibling, 1 reply; 36+ messages in thread
From: Jeff Layton @ 2024-09-19 16:50 UTC (permalink / raw)
To: Thomas Gleixner, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, 2024-09-16 at 12:12 +0200, Thomas Gleixner wrote:
> On Sat, Sep 14 2024 at 13:07, Jeff Layton wrote:
> > For multigrain timestamps, we must keep track of the latest timestamp
>
> What is a multgrain timestamp? Can you please describe the concept
> behind it? I'm not going to chase random documentation or whatever
> because change logs have to self contained.
>
> And again 'we' do nothing. Describe the problem in technical terms and
> do not impersonate code.
>
Hi Thomas!
Sorry for the delay in responding. I'll try to summarize below, but
I'll also note that patch #7 in the v8 series adds a file to
Documentation/ that explains this in a bit more depth:
Currently the kernel always stamps files (mtime, ctime, etc.) using the
coarse-grained clock. This is usually a good thing, since it reduces
the number of metadata updates, but means that you can't reliably use
file timestamps to detect whether there have been changes to the file
since it was last checked. This is particularly a problem for NFSv3
clients, which use timestamps to know when to invalidate their
pagecache for an inode [1].
The idea is to allow the kernel to use fine-grained timestamps (mtime
and ctime) on files when they are under direct observation. When a task
does a ->getattr against an inode for STATX_MTIME or STATX_CTIME, a
flag is set in the inode that tells the kernel to use the fine-grained
clock for the timestamp update iff the current coarse-grained clock
value would not cause a change to the mtime/ctime.
This works, but there is a problem:
It's possible for one inode to get a fine-grained timestamp, and then
another to get a coarse-grained timestamp. If this happens within a
single coarse-grained timer tick, then the files may appear to have
been modified in reverse order, which breaks POSIX guarantees (and
obscure programs like "make").
The fix for this is to establish a floor value for the coarse-grained
clock. When stamping a file with a fine-grained timestamp, we update
the floor value with the current monotonic time (using cmpxchg). Then
later, when a coarse-grained timestamp is requested, check whether the
floor is later than the current coarse-grained time. If it is, then the
kernel will return the floor value (converted to realtime) instead of
the current coarse-grained clock. That allows us to maintain the
ordering guarantees.
My original implementation of this tracked the floor value in
fs/inode.c (also using cmpxchg), but that caused a performance
regression, mostly due to multiple calls into the timekeeper functions
with seqcount loops. By adding the floor to the timekeeper we can get
that back down to 1 seqcount loop.
Let me know if you have more questions about this, or suggestions about
how to do this better. The timekeeping code is not my area of expertise
(obviously) so I'm open to doing this a better way if there is one.
Thanks for the review so far!
[1]: NFSv4 mandates an opaque change attribute (usually using
inode->i_version), but only some filesystems have a proper
implementation of it (XFS being the notable exception). For the others,
we end up using the ctime to generate a change attribute, which means
that NFSv4 has the same problem on those filesystems. i_version also
doesn't help NFSv3 clients and servers.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 07/11] Documentation: add a new file documenting multigrain timestamps
2024-09-16 1:01 ` Bagas Sanjaya
@ 2024-09-19 16:53 ` Jeff Layton
0 siblings, 0 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-19 16:53 UTC (permalink / raw)
To: Bagas Sanjaya, John Stultz, Thomas Gleixner, Stephen Boyd,
Alexander Viro, Christian Brauner, Jan Kara, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet,
Chandan Babu R, Darrick J. Wong, Theodore Ts'o,
Andreas Dilger, Chris Mason, Josef Bacik, David Sterba,
Hugh Dickins, Andrew Morton, Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, 2024-09-16 at 08:01 +0700, Bagas Sanjaya wrote:
> On Sat, Sep 14, 2024 at 01:07:20PM -0400, Jeff Layton wrote:
> > +Multigrain timestamps aim to remedy this by selectively using fine-grained
> > +timestamps when a file has had its timestamps queried recently, and the current
> > +coarse-grained time does not cause a change.
>
> Do you mean using fine-grained timestamps when timestamps of a file has been
> recently queried/modified BUT its coarse-grained timestamps aren't changed?
>
> Confused...
>
Yes, I'll do an s/and/but/ there in the Documentation. Basically, we
want to avoid updating the floor whenever we can (since it's a global
variable), so we only get a fine-grained time if there is no other way
to effect a change to the timestamps.
Thanks!
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 00/11] fs: multigrain timestamp redux
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
` (10 preceding siblings ...)
2024-09-14 17:07 ` [PATCH v8 11/11] tmpfs: add support for " Jeff Layton
@ 2024-09-26 16:59 ` Randy Dunlap
11 siblings, 0 replies; 36+ messages in thread
From: Randy Dunlap @ 2024-09-26 16:59 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Thomas Gleixner, Stephen Boyd,
Alexander Viro, Christian Brauner, Jan Kara, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet,
Chandan Babu R, Darrick J. Wong, Theodore Ts'o,
Andreas Dilger, Chris Mason, Josef Bacik, David Sterba,
Hugh Dickins, Andrew Morton, Chuck Lever, Vadim Fedorenko
Cc: linux-kernel, linux-fsdevel, linux-trace-kernel, linux-doc,
linux-xfs, linux-ext4, linux-btrfs, linux-nfs, linux-mm
Hi Jeff,
On 9/14/24 10:07 AM, Jeff Layton wrote:
> This is a fairly small update to the v7 set. It seems to pass all of my
> testing. Again, most of the changes are in the first two patches, but
> there are some differences in the patch that adds percpu counters as
> well.
>
> Since the report of a performance regression came just before the merge
> window, it looks like we're going to have to wait for yet another
> release, so consider this version v6.13 material.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
> ---
> Jeff Layton (11):
> timekeeping: move multigrain timestamp floor handling into timekeeper
> fs: add infrastructure for multigrain timestamps
> fs: have setattr_copy handle multigrain timestamps appropriately
> fs: handle delegated timestamps in setattr_copy_mgtime
> fs: tracepoints around multigrain timestamp events
> fs: add percpu counters for significant multigrain timestamp events
> Documentation: add a new file documenting multigrain timestamps
> xfs: switch to multigrain timestamps
> ext4: switch to multigrain timestamps
> btrfs: convert to multigrain timestamps
> tmpfs: add support for multigrain timestamps
>
> Documentation/filesystems/index.rst | 1 +
> Documentation/filesystems/multigrain-ts.rst | 121 ++++++++++++
> fs/attr.c | 60 +++++-
> fs/btrfs/file.c | 25 +--
> fs/btrfs/super.c | 3 +-
> fs/ext4/super.c | 2 +-
> fs/inode.c | 278 +++++++++++++++++++++++++---
> fs/stat.c | 42 ++++-
> fs/xfs/libxfs/xfs_trans_inode.c | 6 +-
> fs/xfs/xfs_iops.c | 10 +-
> fs/xfs/xfs_super.c | 2 +-
> include/linux/fs.h | 36 +++-
> include/linux/timekeeping.h | 5 +
> include/trace/events/timestamp.h | 124 +++++++++++++
> kernel/time/timekeeping.c | 83 +++++++++
> kernel/time/timekeeping_debug.c | 12 ++
> kernel/time/timekeeping_internal.h | 3 +
> mm/shmem.c | 2 +-
> 18 files changed, 742 insertions(+), 73 deletions(-)
> ---
> base-commit: da3ea35007d0af457a0afc87e84fddaebc4e0b63
IME it would be better to make this series apply to linux-next-($latest)
instead of base mainline Linux. for integration reasons.
You can add
Tested-by: Randy Dunlap <rdunlap@infradead.org> # documentation bits
for all patches if you want to.
> change-id: 20240913-mgtime-20c98bcda88e
>
> Best regards,
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-14 17:07 ` [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper Jeff Layton
2024-09-14 20:10 ` John Stultz
2024-09-16 10:12 ` Thomas Gleixner
@ 2024-09-30 19:13 ` Thomas Gleixner
2024-09-30 19:27 ` Jeff Layton
2 siblings, 1 reply; 36+ messages in thread
From: Thomas Gleixner @ 2024-09-30 19:13 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm, Jeff Layton
On Sat, Sep 14 2024 at 13:07, Jeff Layton wrote:
> For multigrain timestamps, we must keep track of the latest timestamp
> that has ever been handed out, and never hand out a coarse time below
> that value.
How is that correct when the clock is off by an hour and then set back
to the correct value? Then you'd get the same stale timestamp for an
hour unless something invokes ktime_get_real_ts64_mg() which will set
the "latest" timestamp back to a time before the previous one.
> Add a static singleton atomic64_t into timekeeper.c that we can use to
> keep track of the latest fine-grained time ever handed out. This is
> tracked as a monotonic ktime_t value to ensure that it isn't affected by
> clock jumps.
That's just wishful thinking.
ktime_get_real_ts64_mg(ts)
ts = Tmono_1 + offset_1; // TReal_1
floor = Tmono_1;
// newtime < TReal_1
clock_settime(REALTIME, newtime);
xtime = newtime; // TReal_2
offset_2 = offset_1 + Treal_2 - TReal(now);
--> offset_2 < offset_1
ktime_get_coarse_real_ts64_mg(ts)
ts = tk_xtime(); // TReal_2
offs = offset_2;
if (Tmono_1 + offset_2 > ts)
ts = Tmono_1 + offset_2; // Not taken
So this returns T_Real_2 because
offset_2 < offset_1
and therefore
Tmono_1 + offset_2 < TReal_2
so the returned time will jump backwards vs. TReal_1 as it should
because that's the actual time, no?
So if that's the intended behaviour then the changelog is misleading at
best.
If the intention is to never return a value < TReal_1 then this does not
work. You can make it work by using the Realtime timestamp as floor, but
that'd be more than questionable vs. clock_settime() making the clock go
backwards.
Thanks,
tglx
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-16 10:57 ` Jeff Layton
@ 2024-09-30 19:16 ` Thomas Gleixner
2024-09-30 19:37 ` Jeff Layton
0 siblings, 1 reply; 36+ messages in thread
From: Thomas Gleixner @ 2024-09-30 19:16 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, Sep 16 2024 at 06:57, Jeff Layton wrote:
> On Mon, 2024-09-16 at 12:32 +0200, Thomas Gleixner wrote:
>> > 'Something has changed' is a truly understandable technical
>> > explanation.
>>
>> old = mg_floor
>> mono = T1;
>> mg_floor = mono
>> preemption
>>
>> do {
>> mono = T2;
>> }
>>
>> cmpxchg fails and the function returns a value based on T1
>>
>> No?
>>
>>
>
> Packing for LPC, so I can't respond to all of these just now, but I
> will later. You're correct, but either outcome is OK.
>
> The requirement is that we don't hand out any values that were below
> the floor at the time that the task entered the kernel. Since the time
> changed while the task was already inside the kernel, either T1 or T2
> would be valid timestamps.
That really needs to be documented. A similar scenario exists
vs. ktime_get_coarse_real_ts64_mg().
Thanks,
tglx
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-30 19:13 ` Thomas Gleixner
@ 2024-09-30 19:27 ` Jeff Layton
2024-09-30 20:15 ` Thomas Gleixner
0 siblings, 1 reply; 36+ messages in thread
From: Jeff Layton @ 2024-09-30 19:27 UTC (permalink / raw)
To: Thomas Gleixner, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, 2024-09-30 at 21:13 +0200, Thomas Gleixner wrote:
> On Sat, Sep 14 2024 at 13:07, Jeff Layton wrote:
>
> > For multigrain timestamps, we must keep track of the latest timestamp
> > that has ever been handed out, and never hand out a coarse time below
> > that value.
>
> How is that correct when the clock is off by an hour and then set back
> to the correct value? Then you'd get the same stale timestamp for an
> hour unless something invokes ktime_get_real_ts64_mg() which will set
> the "latest" timestamp back to a time before the previous one.
>
> > Add a static singleton atomic64_t into timekeeper.c that we can use to
> > keep track of the latest fine-grained time ever handed out. This is
> > tracked as a monotonic ktime_t value to ensure that it isn't affected by
> > clock jumps.
>
> That's just wishful thinking.
>
> ktime_get_real_ts64_mg(ts)
> ts = Tmono_1 + offset_1; // TReal_1
> floor = Tmono_1;
>
> // newtime < TReal_1
> clock_settime(REALTIME, newtime);
> xtime = newtime; // TReal_2
> offset_2 = offset_1 + Treal_2 - TReal(now);
> --> offset_2 < offset_1
>
> ktime_get_coarse_real_ts64_mg(ts)
> ts = tk_xtime(); // TReal_2
> offs = offset_2;
>
> if (Tmono_1 + offset_2 > ts)
> ts = Tmono_1 + offset_2; // Not taken
>
> So this returns T_Real_2 because
>
> offset_2 < offset_1
>
> and therefore
>
> Tmono_1 + offset_2 < TReal_2
>
> so the returned time will jump backwards vs. TReal_1 as it should
> because that's the actual time, no?
>
> So if that's the intended behaviour then the changelog is misleading at
> best.
>
> If the intention is to never return a value < TReal_1 then this does not
> work. You can make it work by using the Realtime timestamp as floor, but
> that'd be more than questionable vs. clock_settime() making the clock go
> backwards.
>
That is the intended behavior and I'll plan to fix the changelog to
clarify this point:
If someone jumps the realtime clock backward by a large value, then the
realtime timestamp _can_ appear to go backward. This is a problem today
even without this patchset.
If two files get stamped and a realtime clock jump backward happens in
between them, all bets are off as to which one will appear to have been
modified first. I don't think that is something we can reasonably
prevent, since we must stamp files according to the realtime clock.
The main thing I'm trying to prevent is the timestamps being misordered
in the absence of such a clock jump. Without tracking the floor as I am
here, that's a possibility.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-30 19:16 ` Thomas Gleixner
@ 2024-09-30 19:37 ` Jeff Layton
2024-09-30 20:19 ` Thomas Gleixner
0 siblings, 1 reply; 36+ messages in thread
From: Jeff Layton @ 2024-09-30 19:37 UTC (permalink / raw)
To: Thomas Gleixner, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, 2024-09-30 at 21:16 +0200, Thomas Gleixner wrote:
> On Mon, Sep 16 2024 at 06:57, Jeff Layton wrote:
> > On Mon, 2024-09-16 at 12:32 +0200, Thomas Gleixner wrote:
> > > > 'Something has changed' is a truly understandable technical
> > > > explanation.
> > >
> > > old = mg_floor
> > > mono = T1;
> > > mg_floor = mono
> > > preemption
> > >
> > > do {
> > > mono = T2;
> > > }
> > >
> > > cmpxchg fails and the function returns a value based on T1
> > >
> > > No?
> > >
> > >
> >
> > Packing for LPC, so I can't respond to all of these just now, but I
> > will later. You're correct, but either outcome is OK.
> >
> > The requirement is that we don't hand out any values that were below
> > the floor at the time that the task entered the kernel. Since the time
> > changed while the task was already inside the kernel, either T1 or T2
> > would be valid timestamps.
>
> That really needs to be documented. A similar scenario exists
> vs. ktime_get_coarse_real_ts64_mg().
>
Yes.
I have the following section in the multigrain-ts.rst file that gets
added in patch 7 of this series. I'll also plan to add some extra
wording about how backward realtime clock jumps can affect ordering:
Inode Timestamp Ordering
========================
In addition to providing info about changes to individual files, file
timestamps also serve an important purpose in applications like "make". These
programs measure timestamps in order to determine whether source files might be
newer than cached objects.
Userland applications like make can only determine ordering based on
operational boundaries. For a syscall those are the syscall entry and exit
points. For io_uring or nfsd operations, that's the request submission and
response. In the case of concurrent operations, userland can make no
determination about the order in which things will occur.
For instance, if a single thread modifies one file, and then another file in
sequence, the second file must show an equal or later mtime than the first. The
same is true if two threads are issuing similar operations that do not overlap
in time.
If however, two threads have racing syscalls that overlap in time, then there
is no such guarantee, and the second file may appear to have been modified
before, after or at the same time as the first, regardless of which one was
submitted first.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-19 16:50 ` Jeff Layton
@ 2024-09-30 19:43 ` Thomas Gleixner
2024-09-30 20:12 ` Jeff Layton
0 siblings, 1 reply; 36+ messages in thread
From: Thomas Gleixner @ 2024-09-30 19:43 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Thu, Sep 19 2024 at 18:50, Jeff Layton wrote:
> The fix for this is to establish a floor value for the coarse-grained
> clock. When stamping a file with a fine-grained timestamp, we update
> the floor value with the current monotonic time (using cmpxchg). Then
> later, when a coarse-grained timestamp is requested, check whether the
> floor is later than the current coarse-grained time. If it is, then the
> kernel will return the floor value (converted to realtime) instead of
> the current coarse-grained clock. That allows us to maintain the
> ordering guarantees.
>
> My original implementation of this tracked the floor value in
> fs/inode.c (also using cmpxchg), but that caused a performance
> regression, mostly due to multiple calls into the timekeeper functions
> with seqcount loops. By adding the floor to the timekeeper we can get
> that back down to 1 seqcount loop.
>
> Let me know if you have more questions about this, or suggestions about
> how to do this better. The timekeeping code is not my area of expertise
> (obviously) so I'm open to doing this a better way if there is one.
The comments I made about races and the clock_settime() inconsistency
vs. the change log aside, I don't see room for improvement there.
What worries me is the atomic_cmpxchg() under contention on large
machines, but as it is not a cmpxchg() loop it might be not completely
horrible.
Thanks,
tglx
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-30 19:43 ` Thomas Gleixner
@ 2024-09-30 20:12 ` Jeff Layton
0 siblings, 0 replies; 36+ messages in thread
From: Jeff Layton @ 2024-09-30 20:12 UTC (permalink / raw)
To: Thomas Gleixner, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, 2024-09-30 at 21:43 +0200, Thomas Gleixner wrote:
> On Thu, Sep 19 2024 at 18:50, Jeff Layton wrote:
> > The fix for this is to establish a floor value for the coarse-grained
> > clock. When stamping a file with a fine-grained timestamp, we update
> > the floor value with the current monotonic time (using cmpxchg). Then
> > later, when a coarse-grained timestamp is requested, check whether the
> > floor is later than the current coarse-grained time. If it is, then the
> > kernel will return the floor value (converted to realtime) instead of
> > the current coarse-grained clock. That allows us to maintain the
> > ordering guarantees.
> >
> > My original implementation of this tracked the floor value in
> > fs/inode.c (also using cmpxchg), but that caused a performance
> > regression, mostly due to multiple calls into the timekeeper functions
> > with seqcount loops. By adding the floor to the timekeeper we can get
> > that back down to 1 seqcount loop.
> >
> > Let me know if you have more questions about this, or suggestions about
> > how to do this better. The timekeeping code is not my area of expertise
> > (obviously) so I'm open to doing this a better way if there is one.
>
> The comments I made about races and the clock_settime() inconsistency
> vs. the change log aside, I don't see room for improvement there.
>
> What worries me is the atomic_cmpxchg() under contention on large
> machines, but as it is not a cmpxchg() loop it might be not completely
> horrible.
>
Thanks for taking a look.
The code does try to avoid requesting a fine-grained timestamp unless
there is no other option. In practice, that means that a
write()+stat()+write() has to occur within the same coarse-grained
timer tick. That obviously does happen, but I'm hoping it's not very
frequent on real-world workloads.
The main place where we see a performance hit from this set is the
will-it-scale pipe test, which does 1-byte writes and reads on a pipe
as fast as possible.
The previous patchset tracked the floor inside fs/inode.c, and each
timestamp update on the pipe inode could make multiple trips into the
timekeeper seqcount loops. That added extra barriers and slowed things
down.
This patchset moves the code into the timekeeper, which means only a
single barrier. That gets the cost down, but it's not entirely free.
There is some small cost to dealing with the floor, regardless.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-30 19:27 ` Jeff Layton
@ 2024-09-30 20:15 ` Thomas Gleixner
0 siblings, 0 replies; 36+ messages in thread
From: Thomas Gleixner @ 2024-09-30 20:15 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, Sep 30 2024 at 15:27, Jeff Layton wrote:
> On Mon, 2024-09-30 at 21:13 +0200, Thomas Gleixner wrote:
>> So if that's the intended behaviour then the changelog is misleading at
>> best.
>
> That is the intended behavior and I'll plan to fix the changelog to
> clarify this point:
>
> If someone jumps the realtime clock backward by a large value, then the
> realtime timestamp _can_ appear to go backward. This is a problem today
> even without this patchset.
Correct.
> If two files get stamped and a realtime clock jump backward happens in
> between them, all bets are off as to which one will appear to have been
> modified first. I don't think that is something we can reasonably
> prevent, since we must stamp files according to the realtime clock.
True. I just was utterly confused about the changelog.
> The main thing I'm trying to prevent is the timestamps being misordered
> in the absence of such a clock jump. Without tracking the floor as I am
> here, that's a possibility.
Correct.
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-30 19:37 ` Jeff Layton
@ 2024-09-30 20:19 ` Thomas Gleixner
2024-09-30 20:53 ` Jeff Layton
0 siblings, 1 reply; 36+ messages in thread
From: Thomas Gleixner @ 2024-09-30 20:19 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, Sep 30 2024 at 15:37, Jeff Layton wrote:
> On Mon, 2024-09-30 at 21:16 +0200, Thomas Gleixner wrote:
> I have the following section in the multigrain-ts.rst file that gets
> added in patch 7 of this series. I'll also plan to add some extra
> wording about how backward realtime clock jumps can affect ordering:
Please also add comments into the code / interface.
> Inode Timestamp Ordering
> ========================
>
> In addition to providing info about changes to individual files, file
> timestamps also serve an important purpose in applications like "make". These
> programs measure timestamps in order to determine whether source files might be
> newer than cached objects.
>
> Userland applications like make can only determine ordering based on
> operational boundaries. For a syscall those are the syscall entry and exit
> points. For io_uring or nfsd operations, that's the request submission and
> response. In the case of concurrent operations, userland can make no
> determination about the order in which things will occur.
>
> For instance, if a single thread modifies one file, and then another file in
> sequence, the second file must show an equal or later mtime than the first. The
> same is true if two threads are issuing similar operations that do not overlap
> in time.
>
> If however, two threads have racing syscalls that overlap in time, then there
> is no such guarantee, and the second file may appear to have been modified
> before, after or at the same time as the first, regardless of which one was
> submitted first.
That makes me ask a question. Are the timestamps always taken in thread
(syscall) context or can they be taken in other contexts (worker,
[soft]interrupt, etc.) too?
Thanks,
tglx
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-30 20:19 ` Thomas Gleixner
@ 2024-09-30 20:53 ` Jeff Layton
2024-09-30 21:35 ` Thomas Gleixner
0 siblings, 1 reply; 36+ messages in thread
From: Jeff Layton @ 2024-09-30 20:53 UTC (permalink / raw)
To: Thomas Gleixner, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, 2024-09-30 at 22:19 +0200, Thomas Gleixner wrote:
> On Mon, Sep 30 2024 at 15:37, Jeff Layton wrote:
> > On Mon, 2024-09-30 at 21:16 +0200, Thomas Gleixner wrote:
> > I have the following section in the multigrain-ts.rst file that gets
> > added in patch 7 of this series. I'll also plan to add some extra
> > wording about how backward realtime clock jumps can affect ordering:
>
> Please also add comments into the code / interface.
>
Will do.
> > Inode Timestamp Ordering
> > ========================
> >
> > In addition to providing info about changes to individual files, file
> > timestamps also serve an important purpose in applications like "make". These
> > programs measure timestamps in order to determine whether source files might be
> > newer than cached objects.
> >
> > Userland applications like make can only determine ordering based on
> > operational boundaries. For a syscall those are the syscall entry and exit
> > points. For io_uring or nfsd operations, that's the request submission and
> > response. In the case of concurrent operations, userland can make no
> > determination about the order in which things will occur.
> >
> > For instance, if a single thread modifies one file, and then another file in
> > sequence, the second file must show an equal or later mtime than the first. The
> > same is true if two threads are issuing similar operations that do not overlap
> > in time.
> >
> > If however, two threads have racing syscalls that overlap in time, then there
> > is no such guarantee, and the second file may appear to have been modified
> > before, after or at the same time as the first, regardless of which one was
> > submitted first.
>
> That makes me ask a question. Are the timestamps always taken in thread
> (syscall) context or can they be taken in other contexts (worker,
> [soft]interrupt, etc.) too?
>
That's a good question.
The main place we do this is inode_set_ctime_current(). That is mostly
called in the context of a syscall or similar sort of operation
(io_uring, nfsd RPC request, etc.).
I certainly wouldn't rule out a workqueue job calling that function,
but this is something we do while dirtying an inode, and that's not
typically done in interrupt context.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-30 20:53 ` Jeff Layton
@ 2024-09-30 21:35 ` Thomas Gleixner
2024-10-01 9:45 ` Jeff Layton
0 siblings, 1 reply; 36+ messages in thread
From: Thomas Gleixner @ 2024-09-30 21:35 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, Sep 30 2024 at 16:53, Jeff Layton wrote:
> On Mon, 2024-09-30 at 22:19 +0200, Thomas Gleixner wrote:
>> On Mon, Sep 30 2024 at 15:37, Jeff Layton wrote:
>> > If however, two threads have racing syscalls that overlap in time, then there
>> > is no such guarantee, and the second file may appear to have been modified
>> > before, after or at the same time as the first, regardless of which one was
>> > submitted first.
>>
>> That makes me ask a question. Are the timestamps always taken in thread
>> (syscall) context or can they be taken in other contexts (worker,
>> [soft]interrupt, etc.) too?
>>
>
> That's a good question.
>
> The main place we do this is inode_set_ctime_current(). That is mostly
> called in the context of a syscall or similar sort of operation
> (io_uring, nfsd RPC request, etc.).
>
> I certainly wouldn't rule out a workqueue job calling that function,
> but this is something we do while dirtying an inode, and that's not
> typically done in interrupt context.
The reason I'm asking is that if it's always syscall context,
i.e. write() or io_uring()/RPC request etc., then you can avoid the
whole global floor value dance and make it strictly per thread, which
simplifies the exercise significantly.
But even if it's not syscall/thread context then the worker or io_uring
state machine might just require to serialize against itself and not
coordinate with something else. But what do I know.
Thanks,
tglx
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-09-30 21:35 ` Thomas Gleixner
@ 2024-10-01 9:45 ` Jeff Layton
2024-10-01 12:45 ` Thomas Gleixner
0 siblings, 1 reply; 36+ messages in thread
From: Jeff Layton @ 2024-10-01 9:45 UTC (permalink / raw)
To: Thomas Gleixner, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Mon, 2024-09-30 at 23:35 +0200, Thomas Gleixner wrote:
> On Mon, Sep 30 2024 at 16:53, Jeff Layton wrote:
> > On Mon, 2024-09-30 at 22:19 +0200, Thomas Gleixner wrote:
> > > On Mon, Sep 30 2024 at 15:37, Jeff Layton wrote:
> > > > If however, two threads have racing syscalls that overlap in time, then there
> > > > is no such guarantee, and the second file may appear to have been modified
> > > > before, after or at the same time as the first, regardless of which one was
> > > > submitted first.
> > >
> > > That makes me ask a question. Are the timestamps always taken in thread
> > > (syscall) context or can they be taken in other contexts (worker,
> > > [soft]interrupt, etc.) too?
> > >
> >
> > That's a good question.
> >
> > The main place we do this is inode_set_ctime_current(). That is mostly
> > called in the context of a syscall or similar sort of operation
> > (io_uring, nfsd RPC request, etc.).
> >
> > I certainly wouldn't rule out a workqueue job calling that function,
> > but this is something we do while dirtying an inode, and that's not
> > typically done in interrupt context.
>
> The reason I'm asking is that if it's always syscall context,
> i.e. write() or io_uring()/RPC request etc., then you can avoid the
> whole global floor value dance and make it strictly per thread, which
> simplifies the exercise significantly.
>
I'm not sure I follow what you're proposing here.
Consider two threads doing writes serially to different files. IOW, the
second thread enters the write() syscall after the first thread returns
from its write(). In that situation, the second timestamp mustn't
appear to be earlier than the first (assuming no backward clock jump,
of course).
How would we ensure that with only per-thread structures?
> But even if it's not syscall/thread context then the worker or io_uring
> state machine might just require to serialize against itself and not
> coordinate with something else. But what do I know.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-10-01 9:45 ` Jeff Layton
@ 2024-10-01 12:45 ` Thomas Gleixner
2024-10-02 12:41 ` Jeff Layton
0 siblings, 1 reply; 36+ messages in thread
From: Thomas Gleixner @ 2024-10-01 12:45 UTC (permalink / raw)
To: Jeff Layton, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Tue, Oct 01 2024 at 05:45, Jeff Layton wrote:
> On Mon, 2024-09-30 at 23:35 +0200, Thomas Gleixner wrote:
>> > I certainly wouldn't rule out a workqueue job calling that function,
>> > but this is something we do while dirtying an inode, and that's not
>> > typically done in interrupt context.
>>
>> The reason I'm asking is that if it's always syscall context,
>> i.e. write() or io_uring()/RPC request etc., then you can avoid the
>> whole global floor value dance and make it strictly per thread, which
>> simplifies the exercise significantly.
>>
>
> I'm not sure I follow what you're proposing here.
>
> Consider two threads doing writes serially to different files. IOW, the
> second thread enters the write() syscall after the first thread returns
> from its write(). In that situation, the second timestamp mustn't
> appear to be earlier than the first (assuming no backward clock jump,
> of course).
>
> How would we ensure that with only per-thread structures?
Bah. Right. Ignore my sleep deprived rambling.
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper
2024-10-01 12:45 ` Thomas Gleixner
@ 2024-10-02 12:41 ` Jeff Layton
0 siblings, 0 replies; 36+ messages in thread
From: Jeff Layton @ 2024-10-02 12:41 UTC (permalink / raw)
To: Thomas Gleixner, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Chandan Babu R,
Darrick J. Wong, Theodore Ts'o, Andreas Dilger, Chris Mason,
Josef Bacik, David Sterba, Hugh Dickins, Andrew Morton,
Chuck Lever, Vadim Fedorenko
Cc: Randy Dunlap, linux-kernel, linux-fsdevel, linux-trace-kernel,
linux-doc, linux-xfs, linux-ext4, linux-btrfs, linux-nfs,
linux-mm
On Tue, 2024-10-01 at 14:45 +0200, Thomas Gleixner wrote:
> On Tue, Oct 01 2024 at 05:45, Jeff Layton wrote:
> > On Mon, 2024-09-30 at 23:35 +0200, Thomas Gleixner wrote:
> > > > I certainly wouldn't rule out a workqueue job calling that function,
> > > > but this is something we do while dirtying an inode, and that's not
> > > > typically done in interrupt context.
> > >
> > > The reason I'm asking is that if it's always syscall context,
> > > i.e. write() or io_uring()/RPC request etc., then you can avoid the
> > > whole global floor value dance and make it strictly per thread, which
> > > simplifies the exercise significantly.
> > >
> >
> > I'm not sure I follow what you're proposing here.
> >
> > Consider two threads doing writes serially to different files. IOW, the
> > second thread enters the write() syscall after the first thread returns
> > from its write(). In that situation, the second timestamp mustn't
> > appear to be earlier than the first (assuming no backward clock jump,
> > of course).
> >
> > How would we ensure that with only per-thread structures?
>
> Bah. Right. Ignore my sleep deprived rambling.
No worries. My one big takeaway from working on all of this is that
anything dealing with clocks and time is just difficult to
conceptualize.
Could I trouble you for an Acked-by on this patch? I think this series
should probably go in via Christian's tree, but I don't think he wants
to merge it without an explicit ack from the timekeeping maintainers.
Thanks again for the review!
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 36+ messages in thread
end of thread, other threads:[~2024-10-02 12:41 UTC | newest]
Thread overview: 36+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-14 17:07 [PATCH v8 00/11] fs: multigrain timestamp redux Jeff Layton
2024-09-14 17:07 ` [PATCH v8 01/11] timekeeping: move multigrain timestamp floor handling into timekeeper Jeff Layton
2024-09-14 20:10 ` John Stultz
2024-09-14 23:14 ` Jeff Layton
2024-09-16 10:12 ` Thomas Gleixner
2024-09-16 10:32 ` Thomas Gleixner
2024-09-16 10:57 ` Jeff Layton
2024-09-30 19:16 ` Thomas Gleixner
2024-09-30 19:37 ` Jeff Layton
2024-09-30 20:19 ` Thomas Gleixner
2024-09-30 20:53 ` Jeff Layton
2024-09-30 21:35 ` Thomas Gleixner
2024-10-01 9:45 ` Jeff Layton
2024-10-01 12:45 ` Thomas Gleixner
2024-10-02 12:41 ` Jeff Layton
2024-09-19 16:50 ` Jeff Layton
2024-09-30 19:43 ` Thomas Gleixner
2024-09-30 20:12 ` Jeff Layton
2024-09-30 19:13 ` Thomas Gleixner
2024-09-30 19:27 ` Jeff Layton
2024-09-30 20:15 ` Thomas Gleixner
2024-09-14 17:07 ` [PATCH v8 02/11] fs: add infrastructure for multigrain timestamps Jeff Layton
2024-09-14 17:07 ` [PATCH v8 03/11] fs: have setattr_copy handle multigrain timestamps appropriately Jeff Layton
2024-09-14 17:07 ` [PATCH v8 04/11] fs: handle delegated timestamps in setattr_copy_mgtime Jeff Layton
2024-09-14 17:07 ` [PATCH v8 05/11] fs: tracepoints around multigrain timestamp events Jeff Layton
2024-09-15 8:21 ` Steven Rostedt
2024-09-14 17:07 ` [PATCH v8 06/11] fs: add percpu counters for significant " Jeff Layton
2024-09-16 10:20 ` Thomas Gleixner
2024-09-14 17:07 ` [PATCH v8 07/11] Documentation: add a new file documenting multigrain timestamps Jeff Layton
2024-09-16 1:01 ` Bagas Sanjaya
2024-09-19 16:53 ` Jeff Layton
2024-09-14 17:07 ` [PATCH v8 08/11] xfs: switch to " Jeff Layton
2024-09-14 17:07 ` [PATCH v8 09/11] ext4: " Jeff Layton
2024-09-14 17:07 ` [PATCH v8 10/11] btrfs: convert " Jeff Layton
2024-09-14 17:07 ` [PATCH v8 11/11] tmpfs: add support for " Jeff Layton
2024-09-26 16:59 ` [PATCH v8 00/11] fs: multigrain timestamp redux Randy Dunlap
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).