@@ -114,6 +114,23 @@ static struct tk_fast tk_fast_raw ____cacheline_aligned = {
114
114
.base [1 ] = FAST_TK_INIT ,
115
115
};
116
116
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
+
117
134
static inline void tk_normalize_xtime (struct timekeeper * tk )
118
135
{
119
136
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)
2408
2425
}
2409
2426
EXPORT_SYMBOL (ktime_get_coarse_real_ts64 );
2410
2427
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
+
2411
2516
void ktime_get_coarse_ts64 (struct timespec64 * ts )
2412
2517
{
2413
2518
struct timekeeper * tk = & tk_core .timekeeper ;
0 commit comments