If architecture does not support exact boot time, it is challenging to estimate boot time without having a reference to the current persistent clock value. Yet, it cannot read the persistent clock time again, because this may lead to math discrepancies with the caller of read_boot_clock64() who have read the persistent clock at a different time. This is why it is better to provide two values simultaneously: the persistent clock value, and the boot time. Replace read_boot_clock64() with: read_persistent_wall_and_boot_offset(wall_time, boot_offset) Where wall_time is returned by read_persistent_clock() And boot_offset is wall_time - boot time, which defaults to 0.
Showing the changes to kernel/time (1 of 2 files). Show all
Unified · Split
| @@ -17,6 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | #include <linux/nmi.h> |
| 18 | 18 | #include <linux/sched.h> |
| 19 | 19 | #include <linux/sched/loadavg.h> |
| 20 | +#include <linux/sched/clock.h> | |
| 20 | 21 | #include <linux/syscore_ops.h> |
| 21 | 22 | #include <linux/clocksource.h> |
| 22 | 23 | #include <linux/jiffies.h> |
| @@ -1496,18 +1497,20 @@ void __weak read_persistent_clock64(struct timespec64 *ts64) · 1473 unmodified lines | ||
| 1496 | 1497 | } |
| 1497 | 1498 | |
| 1498 | 1499 | /** |
| 1499 | − * read_boot_clock64 - Return time of the system start. | |
| 1500 | + * read_persistent_wall_and_boot_offset - Read persistent clock, and also offset | |
| 1501 | + * from the boot. | |
| 1500 | 1502 | * |
| 1501 | 1503 | * Weak dummy function for arches that do not yet support it. |
| 1502 | − * Function to read the exact time the system has been started. | |
| 1503 | − * Returns a timespec64 with tv_sec=0 and tv_nsec=0 if unsupported. | |
| 1504 | − * | |
| 1505 | − * XXX - Do be sure to remove it once all arches implement it. | |
| 1504 | + * wall_time - current time as returned by persistent clock | |
| 1505 | + * boot_offset - offset that is defined as wall_time - boot_time | |
| 1506 | + * default to 0. | |
| 1506 | 1507 | */ |
| 1507 | −void __weak read_boot_clock64(struct timespec64 *ts) | |
| 1508 | +void __weak __init | |
| 1509 | +read_persistent_wall_and_boot_offset(struct timespec64 *wall_time, | |
| 1510 | + struct timespec64 *boot_offset) | |
| 1508 | 1511 | { |
| 1509 | − ts->tv_sec = 0; | |
| 1510 | − ts->tv_nsec = 0; | |
| 1512 | + read_persistent_clock64(wall_time); | |
| 1513 | + *boot_offset = (struct timespec64){0}; | |
| 1511 | 1514 | } |
| 1512 | 1515 | |
| 1513 | 1516 | /* Flag for if timekeeping_resume() has injected sleeptime */ |
| @@ -1521,28 +1524,29 @@ static bool persistent_clock_exists; · 7 unmodified lines | ||
| 1521 | 1524 | */ |
| 1522 | 1525 | void __init timekeeping_init(void) |
| 1523 | 1526 | { |
| 1527 | + struct timespec64 wall_time, boot_offset, wall_to_mono; | |
| 1524 | 1528 | struct timekeeper *tk = &tk_core.timekeeper; |
| 1525 | 1529 | struct clocksource *clock; |
| 1526 | 1530 | unsigned long flags; |
| 1527 | − struct timespec64 now, boot, tmp; | |
| 1528 | − | |
| 1529 | − read_persistent_clock64(&now); | |
| 1530 | − if (!timespec64_valid_strict(&now)) { | |
| 1531 | − pr_warn("WARNING: Persistent clock returned invalid value!\n" | |
| 1532 | − " Check your CMOS/BIOS settings.\n"); | |
| 1533 | − now.tv_sec = 0; | |
| 1534 | − now.tv_nsec = 0; | |
| 1535 | − } else if (now.tv_sec || now.tv_nsec) | |
| 1536 | − persistent_clock_exists = true; | |
| 1537 | 1531 | |
| 1538 | − read_boot_clock64(&boot); | |
| 1539 | − if (!timespec64_valid_strict(&boot)) { | |
| 1540 | − pr_warn("WARNING: Boot clock returned invalid value!\n" | |
| 1541 | − " Check your CMOS/BIOS settings.\n"); | |
| 1542 | − boot.tv_sec = 0; | |
| 1543 | − boot.tv_nsec = 0; | |
| 1532 | + read_persistent_wall_and_boot_offset(&wall_time, &boot_offset); | |
| 1533 | + if (timespec64_valid_strict(&wall_time) && | |
| 1534 | + timespec64_to_ns(&wall_time) > 0) { | |
| 1535 | + persistent_clock_exists = true; | |
| 1536 | + } else { | |
| 1537 | + pr_warn("Persistent clock returned invalid value"); | |
| 1538 | + wall_time = (struct timespec64){0}; | |
| 1544 | 1539 | } |
| 1545 | 1540 | |
| 1541 | + if (timespec64_compare(&wall_time, &boot_offset) < 0) | |
| 1542 | + boot_offset = (struct timespec64){0}; | |
| 1543 | + | |
| 1544 | + /* | |
| 1545 | + * We want set wall_to_mono, so the following is true: | |
| 1546 | + * wall time + wall_to_mono = boot time | |
| 1547 | + */ | |
| 1548 | + wall_to_mono = timespec64_sub(boot_offset, wall_time); | |
| 1549 | + | |
| 1546 | 1550 | raw_spin_lock_irqsave(&timekeeper_lock, flags); |
| 1547 | 1551 | write_seqcount_begin(&tk_core.seq); |
| 1548 | 1552 | ntp_init(); |
| @@ -1552,13 +1556,10 @@ void __init timekeeping_init(void) · 3 unmodified lines | ||
| 1552 | 1556 | clock->enable(clock); |
| 1553 | 1557 | tk_setup_internals(tk, clock); |
| 1554 | 1558 | |
| 1555 | − tk_set_xtime(tk, &now); | |
| 1559 | + tk_set_xtime(tk, &wall_time); | |
| 1556 | 1560 | tk->raw_sec = 0; |
| 1557 | − if (boot.tv_sec == 0 && boot.tv_nsec == 0) | |
| 1558 | − boot = tk_xtime(tk); | |
| 1559 | 1561 | |
| 1560 | − set_normalized_timespec64(&tmp, -boot.tv_sec, -boot.tv_nsec); | |
| 1561 | − tk_set_wall_to_mono(tk, tmp); | |
| 1562 | + tk_set_wall_to_mono(tk, wall_to_mono); | |
| 1562 | 1563 | |
| 1563 | 1564 | timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET); |
| 1564 | 1565 | |