diff options
Diffstat (limited to 'kernel/time')
| -rw-r--r-- | kernel/time/timekeeping.c | 59 |
1 files changed, 30 insertions, 29 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 4786df904c22..cb738f825c12 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/nmi.h> | 17 | #include <linux/nmi.h> |
| 18 | #include <linux/sched.h> | 18 | #include <linux/sched.h> |
| 19 | #include <linux/sched/loadavg.h> | 19 | #include <linux/sched/loadavg.h> |
| 20 | #include <linux/sched/clock.h> | ||
| 20 | #include <linux/syscore_ops.h> | 21 | #include <linux/syscore_ops.h> |
| 21 | #include <linux/clocksource.h> | 22 | #include <linux/clocksource.h> |
| 22 | #include <linux/jiffies.h> | 23 | #include <linux/jiffies.h> |
| @@ -1496,18 +1497,20 @@ void __weak read_persistent_clock64(struct timespec64 *ts64) | |||
| 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 | * Weak dummy function for arches that do not yet support it. | 1503 | * Weak dummy function for arches that do not yet support it. |
| 1502 | * Function to read the exact time the system has been started. | 1504 | * wall_time - current time as returned by persistent clock |
| 1503 | * Returns a timespec64 with tv_sec=0 and tv_nsec=0 if unsupported. | 1505 | * boot_offset - offset that is defined as wall_time - boot_time |
| 1504 | * | 1506 | * default to 0. |
| 1505 | * XXX - Do be sure to remove it once all arches implement it. | ||
| 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; | 1512 | read_persistent_clock64(wall_time); |
| 1510 | ts->tv_nsec = 0; | 1513 | *boot_offset = (struct timespec64){0}; |
| 1511 | } | 1514 | } |
| 1512 | 1515 | ||
| 1513 | /* Flag for if timekeeping_resume() has injected sleeptime */ | 1516 | /* Flag for if timekeeping_resume() has injected sleeptime */ |
| @@ -1521,28 +1524,29 @@ static bool persistent_clock_exists; | |||
| 1521 | */ | 1524 | */ |
| 1522 | void __init timekeeping_init(void) | 1525 | void __init timekeeping_init(void) |
| 1523 | { | 1526 | { |
| 1527 | struct timespec64 wall_time, boot_offset, wall_to_mono; | ||
| 1524 | struct timekeeper *tk = &tk_core.timekeeper; | 1528 | struct timekeeper *tk = &tk_core.timekeeper; |
| 1525 | struct clocksource *clock; | 1529 | struct clocksource *clock; |
| 1526 | unsigned long flags; | 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); | 1532 | read_persistent_wall_and_boot_offset(&wall_time, &boot_offset); |
| 1539 | if (!timespec64_valid_strict(&boot)) { | 1533 | if (timespec64_valid_strict(&wall_time) && |
| 1540 | pr_warn("WARNING: Boot clock returned invalid value!\n" | 1534 | timespec64_to_ns(&wall_time) > 0) { |
| 1541 | " Check your CMOS/BIOS settings.\n"); | 1535 | persistent_clock_exists = true; |
| 1542 | boot.tv_sec = 0; | 1536 | } else { |
| 1543 | boot.tv_nsec = 0; | 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 | raw_spin_lock_irqsave(&timekeeper_lock, flags); | 1550 | raw_spin_lock_irqsave(&timekeeper_lock, flags); |
| 1547 | write_seqcount_begin(&tk_core.seq); | 1551 | write_seqcount_begin(&tk_core.seq); |
| 1548 | ntp_init(); | 1552 | ntp_init(); |
| @@ -1552,13 +1556,10 @@ void __init timekeeping_init(void) | |||
| 1552 | clock->enable(clock); | 1556 | clock->enable(clock); |
| 1553 | tk_setup_internals(tk, clock); | 1557 | tk_setup_internals(tk, clock); |
| 1554 | 1558 | ||
| 1555 | tk_set_xtime(tk, &now); | 1559 | tk_set_xtime(tk, &wall_time); |
| 1556 | tk->raw_sec = 0; | 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); | 1562 | tk_set_wall_to_mono(tk, wall_to_mono); |
| 1561 | tk_set_wall_to_mono(tk, tmp); | ||
| 1562 | 1563 | ||
| 1563 | timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET); | 1564 | timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET); |
| 1564 | 1565 | ||
