Skip to content

Commit b7f6d3a

Browse files
committed
Merge branch 'timers/vfs' into timers/core
Pick up the VFS specific interfaces so further timekeeping changes can be based on them.
2 parents 8c111f1 + 96f9a36 commit b7f6d3a

File tree

4 files changed

+138
-0
lines changed

4 files changed

+138
-0
lines changed

include/linux/timekeeping.h

+5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ extern void ktime_get_real_ts64(struct timespec64 *tv);
4545
extern void ktime_get_coarse_ts64(struct timespec64 *ts);
4646
extern void ktime_get_coarse_real_ts64(struct timespec64 *ts);
4747

48+
/* Multigrain timestamp interfaces */
49+
extern void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts);
50+
extern void ktime_get_real_ts64_mg(struct timespec64 *ts);
51+
extern unsigned long timekeeping_get_mg_floor_swaps(void);
52+
4853
void getboottime64(struct timespec64 *ts);
4954

5055
/*

kernel/time/timekeeping.c

+105
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,23 @@ static struct tk_fast tk_fast_raw ____cacheline_aligned = {
114114
.base[1] = FAST_TK_INIT,
115115
};
116116

117+
/*
118+
* Multigrain timestamps require tracking the latest fine-grained timestamp
119+
* that has been issued, and never returning a coarse-grained timestamp that is
120+
* earlier than that value.
121+
*
122+
* mg_floor represents the latest fine-grained time that has been handed out as
123+
* a file timestamp on the system. This is tracked as a monotonic ktime_t, and
124+
* converted to a realtime clock value on an as-needed basis.
125+
*
126+
* Maintaining mg_floor ensures the multigrain interfaces never issue a
127+
* timestamp earlier than one that has been previously issued.
128+
*
129+
* The exception to this rule is when there is a backward realtime clock jump. If
130+
* such an event occurs, a timestamp can appear to be earlier than a previous one.
131+
*/
132+
static __cacheline_aligned_in_smp atomic64_t mg_floor;
133+
117134
static inline void tk_normalize_xtime(struct timekeeper *tk)
118135
{
119136
while (tk->tkr_mono.xtime_nsec >= ((u64)NSEC_PER_SEC << tk->tkr_mono.shift)) {
@@ -2408,6 +2425,94 @@ void ktime_get_coarse_real_ts64(struct timespec64 *ts)
24082425
}
24092426
EXPORT_SYMBOL(ktime_get_coarse_real_ts64);
24102427

2428+
/**
2429+
* ktime_get_coarse_real_ts64_mg - return latter of coarse grained time or floor
2430+
* @ts: timespec64 to be filled
2431+
*
2432+
* Fetch the global mg_floor value, convert it to realtime and compare it
2433+
* to the current coarse-grained time. Fill @ts with whichever is
2434+
* latest. Note that this is a filesystem-specific interface and should be
2435+
* avoided outside of that context.
2436+
*/
2437+
void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts)
2438+
{
2439+
struct timekeeper *tk = &tk_core.timekeeper;
2440+
u64 floor = atomic64_read(&mg_floor);
2441+
ktime_t f_real, offset, coarse;
2442+
unsigned int seq;
2443+
2444+
do {
2445+
seq = read_seqcount_begin(&tk_core.seq);
2446+
*ts = tk_xtime(tk);
2447+
offset = tk_core.timekeeper.offs_real;
2448+
} while (read_seqcount_retry(&tk_core.seq, seq));
2449+
2450+
coarse = timespec64_to_ktime(*ts);
2451+
f_real = ktime_add(floor, offset);
2452+
if (ktime_after(f_real, coarse))
2453+
*ts = ktime_to_timespec64(f_real);
2454+
}
2455+
2456+
/**
2457+
* ktime_get_real_ts64_mg - attempt to update floor value and return result
2458+
* @ts: pointer to the timespec to be set
2459+
*
2460+
* Get a monotonic fine-grained time value and attempt to swap it into
2461+
* mg_floor. If that succeeds then accept the new floor value. If it fails
2462+
* then another task raced in during the interim time and updated the
2463+
* floor. Since any update to the floor must be later than the previous
2464+
* floor, either outcome is acceptable.
2465+
*
2466+
* Typically this will be called after calling ktime_get_coarse_real_ts64_mg(),
2467+
* and determining that the resulting coarse-grained timestamp did not effect
2468+
* a change in ctime. Any more recent floor value would effect a change to
2469+
* ctime, so there is no need to retry the atomic64_try_cmpxchg() on failure.
2470+
*
2471+
* @ts will be filled with the latest floor value, regardless of the outcome of
2472+
* the cmpxchg. Note that this is a filesystem specific interface and should be
2473+
* avoided outside of that context.
2474+
*/
2475+
void ktime_get_real_ts64_mg(struct timespec64 *ts)
2476+
{
2477+
struct timekeeper *tk = &tk_core.timekeeper;
2478+
ktime_t old = atomic64_read(&mg_floor);
2479+
ktime_t offset, mono;
2480+
unsigned int seq;
2481+
u64 nsecs;
2482+
2483+
do {
2484+
seq = read_seqcount_begin(&tk_core.seq);
2485+
2486+
ts->tv_sec = tk->xtime_sec;
2487+
mono = tk->tkr_mono.base;
2488+
nsecs = timekeeping_get_ns(&tk->tkr_mono);
2489+
offset = tk_core.timekeeper.offs_real;
2490+
} while (read_seqcount_retry(&tk_core.seq, seq));
2491+
2492+
mono = ktime_add_ns(mono, nsecs);
2493+
2494+
/*
2495+
* Attempt to update the floor with the new time value. As any
2496+
* update must be later then the existing floor, and would effect
2497+
* a change to ctime from the perspective of the current task,
2498+
* accept the resulting floor value regardless of the outcome of
2499+
* the swap.
2500+
*/
2501+
if (atomic64_try_cmpxchg(&mg_floor, &old, mono)) {
2502+
ts->tv_nsec = 0;
2503+
timespec64_add_ns(ts, nsecs);
2504+
timekeeping_inc_mg_floor_swaps();
2505+
} else {
2506+
/*
2507+
* Another task changed mg_floor since "old" was fetched.
2508+
* "old" has been updated with the latest value of "mg_floor".
2509+
* That value is newer than the previous floor value, which
2510+
* is enough to effect a change to ctime. Accept it.
2511+
*/
2512+
*ts = ktime_to_timespec64(ktime_add(old, offset));
2513+
}
2514+
}
2515+
24112516
void ktime_get_coarse_ts64(struct timespec64 *ts)
24122517
{
24132518
struct timekeeper *tk = &tk_core.timekeeper;

kernel/time/timekeeping_debug.c

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
#define NUM_BINS 32
1919

20+
/* Incremented every time mg_floor is updated */
21+
DEFINE_PER_CPU(unsigned long, timekeeping_mg_floor_swaps);
22+
2023
static unsigned int sleep_time_bin[NUM_BINS] = {0};
2124

2225
static int tk_debug_sleep_time_show(struct seq_file *s, void *data)
@@ -53,3 +56,13 @@ void tk_debug_account_sleep_time(const struct timespec64 *t)
5356
(s64)t->tv_sec, t->tv_nsec / NSEC_PER_MSEC);
5457
}
5558

59+
unsigned long timekeeping_get_mg_floor_swaps(void)
60+
{
61+
unsigned long sum = 0;
62+
int cpu;
63+
64+
for_each_possible_cpu(cpu)
65+
sum += data_race(per_cpu(timekeeping_mg_floor_swaps, cpu));
66+
67+
return sum;
68+
}

kernel/time/timekeeping_internal.h

+15
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,24 @@
1010
* timekeeping debug functions
1111
*/
1212
#ifdef CONFIG_DEBUG_FS
13+
14+
DECLARE_PER_CPU(unsigned long, timekeeping_mg_floor_swaps);
15+
16+
static inline void timekeeping_inc_mg_floor_swaps(void)
17+
{
18+
this_cpu_inc(timekeeping_mg_floor_swaps);
19+
}
20+
1321
extern void tk_debug_account_sleep_time(const struct timespec64 *t);
22+
1423
#else
24+
1525
#define tk_debug_account_sleep_time(x)
26+
27+
static inline void timekeeping_inc_mg_floor_swaps(void)
28+
{
29+
}
30+
1631
#endif
1732

1833
#ifdef CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE

0 commit comments

Comments
 (0)