Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

um: add a pseudo RTC

Add a pseudo RTC that simply is able to send an alarm signal
waking up the system at a given time in the future.

Since apparently timerfd_create() FDs don't support SIGIO, we
use the sigio-creating helper thread, which just learned to do
suspend/resume properly in the previous patch.

For time-travel mode, OTOH, just add an event at the specified
time in the future, and that's already sufficient to wake up
the system at that point in time since suspend will just be in
an "endless wait".

For s2idle support also call pm_system_wakeup().

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>

authored by

Johannes Berg and committed by
Richard Weinberger
dde8b58d bfc58e2b

+339 -1
+11
arch/um/drivers/Kconfig
··· 346 346 help 347 347 This driver provides support for virtio based paravirtual device 348 348 drivers over vhost-user sockets. 349 + 350 + config UML_RTC 351 + bool "UML RTC driver" 352 + depends on RTC_CLASS 353 + # there's no use in this if PM_SLEEP isn't enabled ... 354 + depends on PM_SLEEP 355 + help 356 + When PM_SLEEP is configured, it may be desirable to wake up using 357 + rtcwake, especially in time-travel mode. This driver enables that 358 + by providing a fake RTC clock that causes a wakeup at the right 359 + time.
+2
arch/um/drivers/Makefile
··· 17 17 ubd-objs := ubd_kern.o ubd_user.o 18 18 port-objs := port_kern.o port_user.o 19 19 harddog-objs := harddog_kern.o harddog_user.o 20 + rtc-objs := rtc_kern.o rtc_user.o 20 21 21 22 LDFLAGS_pcap.o = $(shell $(CC) $(KBUILD_CFLAGS) -print-file-name=libpcap.a) 22 23 ··· 63 62 obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o 64 63 obj-$(CONFIG_UML_RANDOM) += random.o 65 64 obj-$(CONFIG_VIRTIO_UML) += virtio_uml.o 65 + obj-$(CONFIG_UML_RTC) += rtc.o 66 66 67 67 # pcap_user.o must be added explicitly. 68 68 USER_OBJS := fd.o null.o pty.o tty.o xterm.o slip_common.o pcap_user.o vde_user.o vector_user.o
+15
arch/um/drivers/rtc.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2020 Intel Corporation 4 + * Author: Johannes Berg <johannes@sipsolutions.net> 5 + */ 6 + #ifndef __UM_RTC_H__ 7 + #define __UM_RTC_H__ 8 + 9 + int uml_rtc_start(bool timetravel); 10 + int uml_rtc_enable_alarm(unsigned long long delta_seconds); 11 + void uml_rtc_disable_alarm(void); 12 + void uml_rtc_stop(bool timetravel); 13 + void uml_rtc_send_timetravel_alarm(void); 14 + 15 + #endif /* __UM_RTC_H__ */
+211
arch/um/drivers/rtc_kern.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2020 Intel Corporation 4 + * Author: Johannes Berg <johannes@sipsolutions.net> 5 + */ 6 + #include <linux/platform_device.h> 7 + #include <linux/time-internal.h> 8 + #include <linux/suspend.h> 9 + #include <linux/err.h> 10 + #include <linux/rtc.h> 11 + #include <kern_util.h> 12 + #include <irq_kern.h> 13 + #include <os.h> 14 + #include "rtc.h" 15 + 16 + static time64_t uml_rtc_alarm_time; 17 + static bool uml_rtc_alarm_enabled; 18 + static struct rtc_device *uml_rtc; 19 + static int uml_rtc_irq_fd, uml_rtc_irq; 20 + 21 + #ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT 22 + 23 + static void uml_rtc_time_travel_alarm(struct time_travel_event *ev) 24 + { 25 + uml_rtc_send_timetravel_alarm(); 26 + } 27 + 28 + static struct time_travel_event uml_rtc_alarm_event = { 29 + .fn = uml_rtc_time_travel_alarm, 30 + }; 31 + #endif 32 + 33 + static int uml_rtc_read_time(struct device *dev, struct rtc_time *tm) 34 + { 35 + struct timespec64 ts; 36 + 37 + /* Use this to get correct time in time-travel mode */ 38 + read_persistent_clock64(&ts); 39 + rtc_time64_to_tm(timespec64_to_ktime(ts) / NSEC_PER_SEC, tm); 40 + 41 + return 0; 42 + } 43 + 44 + static int uml_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 45 + { 46 + rtc_time64_to_tm(uml_rtc_alarm_time, &alrm->time); 47 + alrm->enabled = uml_rtc_alarm_enabled; 48 + 49 + return 0; 50 + } 51 + 52 + static int uml_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) 53 + { 54 + unsigned long long secs; 55 + 56 + if (!enable && !uml_rtc_alarm_enabled) 57 + return 0; 58 + 59 + uml_rtc_alarm_enabled = enable; 60 + 61 + secs = uml_rtc_alarm_time - ktime_get_real_seconds(); 62 + 63 + if (time_travel_mode == TT_MODE_OFF) { 64 + if (!enable) { 65 + uml_rtc_disable_alarm(); 66 + return 0; 67 + } 68 + 69 + /* enable or update */ 70 + return uml_rtc_enable_alarm(secs); 71 + } else { 72 + time_travel_del_event(&uml_rtc_alarm_event); 73 + 74 + if (enable) 75 + time_travel_add_event_rel(&uml_rtc_alarm_event, 76 + secs * NSEC_PER_SEC); 77 + } 78 + 79 + return 0; 80 + } 81 + 82 + static int uml_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 83 + { 84 + uml_rtc_alarm_irq_enable(dev, 0); 85 + uml_rtc_alarm_time = rtc_tm_to_time64(&alrm->time); 86 + uml_rtc_alarm_irq_enable(dev, alrm->enabled); 87 + 88 + return 0; 89 + } 90 + 91 + static const struct rtc_class_ops uml_rtc_ops = { 92 + .read_time = uml_rtc_read_time, 93 + .read_alarm = uml_rtc_read_alarm, 94 + .alarm_irq_enable = uml_rtc_alarm_irq_enable, 95 + .set_alarm = uml_rtc_set_alarm, 96 + }; 97 + 98 + static irqreturn_t uml_rtc_interrupt(int irq, void *data) 99 + { 100 + unsigned long long c = 0; 101 + 102 + /* alarm triggered, it's now off */ 103 + uml_rtc_alarm_enabled = false; 104 + 105 + os_read_file(uml_rtc_irq_fd, &c, sizeof(c)); 106 + WARN_ON(c == 0); 107 + 108 + pm_system_wakeup(); 109 + rtc_update_irq(uml_rtc, 1, RTC_IRQF | RTC_AF); 110 + 111 + return IRQ_HANDLED; 112 + } 113 + 114 + static int uml_rtc_setup(void) 115 + { 116 + int err; 117 + 118 + err = uml_rtc_start(time_travel_mode != TT_MODE_OFF); 119 + if (WARN(err < 0, "err = %d\n", err)) 120 + return err; 121 + 122 + uml_rtc_irq_fd = err; 123 + 124 + err = um_request_irq(UM_IRQ_ALLOC, uml_rtc_irq_fd, IRQ_READ, 125 + uml_rtc_interrupt, 0, "rtc", NULL); 126 + if (err < 0) { 127 + uml_rtc_stop(time_travel_mode != TT_MODE_OFF); 128 + return err; 129 + } 130 + 131 + irq_set_irq_wake(err, 1); 132 + 133 + uml_rtc_irq = err; 134 + return 0; 135 + } 136 + 137 + static void uml_rtc_cleanup(void) 138 + { 139 + um_free_irq(uml_rtc_irq, NULL); 140 + uml_rtc_stop(time_travel_mode != TT_MODE_OFF); 141 + } 142 + 143 + static int uml_rtc_probe(struct platform_device *pdev) 144 + { 145 + int err; 146 + 147 + err = uml_rtc_setup(); 148 + if (err) 149 + return err; 150 + 151 + uml_rtc = devm_rtc_allocate_device(&pdev->dev); 152 + if (IS_ERR(uml_rtc)) { 153 + err = PTR_ERR(uml_rtc); 154 + goto cleanup; 155 + } 156 + 157 + uml_rtc->ops = &uml_rtc_ops; 158 + 159 + device_init_wakeup(&pdev->dev, 1); 160 + 161 + err = devm_rtc_register_device(uml_rtc); 162 + if (err) 163 + goto cleanup; 164 + 165 + return 0; 166 + cleanup: 167 + uml_rtc_cleanup(); 168 + return err; 169 + } 170 + 171 + static int uml_rtc_remove(struct platform_device *pdev) 172 + { 173 + device_init_wakeup(&pdev->dev, 0); 174 + uml_rtc_cleanup(); 175 + return 0; 176 + } 177 + 178 + static struct platform_driver uml_rtc_driver = { 179 + .probe = uml_rtc_probe, 180 + .remove = uml_rtc_remove, 181 + .driver = { 182 + .name = "uml-rtc", 183 + }, 184 + }; 185 + 186 + static int __init uml_rtc_init(void) 187 + { 188 + struct platform_device *pdev; 189 + int err; 190 + 191 + err = platform_driver_register(&uml_rtc_driver); 192 + if (err) 193 + return err; 194 + 195 + pdev = platform_device_alloc("uml-rtc", 0); 196 + if (!pdev) { 197 + err = -ENOMEM; 198 + goto unregister; 199 + } 200 + 201 + err = platform_device_add(pdev); 202 + if (err) 203 + goto unregister; 204 + return 0; 205 + 206 + unregister: 207 + platform_device_put(pdev); 208 + platform_driver_unregister(&uml_rtc_driver); 209 + return err; 210 + } 211 + device_initcall(uml_rtc_init);
+80
arch/um/drivers/rtc_user.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2020 Intel Corporation 4 + * Author: Johannes Berg <johannes@sipsolutions.net> 5 + */ 6 + #include <os.h> 7 + #include <errno.h> 8 + #include <sched.h> 9 + #include <unistd.h> 10 + #include <kern_util.h> 11 + #include <sys/select.h> 12 + #include <stdio.h> 13 + #include <sys/timerfd.h> 14 + #include "rtc.h" 15 + 16 + static int uml_rtc_irq_fds[2]; 17 + 18 + void uml_rtc_send_timetravel_alarm(void) 19 + { 20 + unsigned long long c = 1; 21 + 22 + CATCH_EINTR(write(uml_rtc_irq_fds[1], &c, sizeof(c))); 23 + } 24 + 25 + int uml_rtc_start(bool timetravel) 26 + { 27 + int err; 28 + 29 + if (timetravel) { 30 + int err = os_pipe(uml_rtc_irq_fds, 1, 1); 31 + if (err) 32 + goto fail; 33 + } else { 34 + uml_rtc_irq_fds[0] = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); 35 + if (uml_rtc_irq_fds[0] < 0) { 36 + err = -errno; 37 + goto fail; 38 + } 39 + 40 + /* apparently timerfd won't send SIGIO, use workaround */ 41 + sigio_broken(uml_rtc_irq_fds[0]); 42 + err = add_sigio_fd(uml_rtc_irq_fds[0]); 43 + if (err < 0) { 44 + close(uml_rtc_irq_fds[0]); 45 + goto fail; 46 + } 47 + } 48 + 49 + return uml_rtc_irq_fds[0]; 50 + fail: 51 + uml_rtc_stop(timetravel); 52 + return err; 53 + } 54 + 55 + int uml_rtc_enable_alarm(unsigned long long delta_seconds) 56 + { 57 + struct itimerspec it = { 58 + .it_value = { 59 + .tv_sec = delta_seconds, 60 + }, 61 + }; 62 + 63 + if (timerfd_settime(uml_rtc_irq_fds[0], 0, &it, NULL)) 64 + return -errno; 65 + return 0; 66 + } 67 + 68 + void uml_rtc_disable_alarm(void) 69 + { 70 + uml_rtc_enable_alarm(0); 71 + } 72 + 73 + void uml_rtc_stop(bool timetravel) 74 + { 75 + if (timetravel) 76 + os_close_file(uml_rtc_irq_fds[1]); 77 + else 78 + ignore_sigio_fd(uml_rtc_irq_fds[0]); 79 + os_close_file(uml_rtc_irq_fds[0]); 80 + }
+11
arch/um/include/linux/time-internal.h
··· 55 55 } 56 56 57 57 void time_travel_add_irq_event(struct time_travel_event *e); 58 + void time_travel_add_event_rel(struct time_travel_event *e, 59 + unsigned long long delay_ns); 60 + bool time_travel_del_event(struct time_travel_event *e); 58 61 #else 59 62 struct time_travel_event { 60 63 }; ··· 83 80 { 84 81 WARN_ON(1); 85 82 } 83 + 84 + /* 85 + * not inlines so the data structure need not exist, 86 + * cause linker failures 87 + */ 88 + extern void time_travel_not_configured(void); 89 + #define time_travel_add_event_rel(...) time_travel_not_configured() 90 + #define time_travel_del_event(...) time_travel_not_configured() 86 91 #endif /* CONFIG_UML_TIME_TRAVEL_SUPPORT */ 87 92 88 93 /*
+9 -1
arch/um/kernel/time.c
··· 321 321 __time_travel_add_event(e, time); 322 322 } 323 323 324 + void time_travel_add_event_rel(struct time_travel_event *e, 325 + unsigned long long delay_ns) 326 + { 327 + time_travel_add_event(e, time_travel_time + delay_ns); 328 + } 329 + 324 330 void time_travel_periodic_timer(struct time_travel_event *e) 325 331 { 326 332 time_travel_add_event(&time_travel_timer_event, ··· 390 384 } 391 385 } 392 386 393 - static bool time_travel_del_event(struct time_travel_event *e) 387 + bool time_travel_del_event(struct time_travel_event *e) 394 388 { 395 389 unsigned long flags; 396 390 ··· 600 594 601 595 /* these are empty macros so the struct/fn need not exist */ 602 596 #define time_travel_add_event(e, time) do { } while (0) 597 + /* externally not usable - redefine here so we can */ 598 + #undef time_travel_del_event 603 599 #define time_travel_del_event(e) do { } while (0) 604 600 #endif 605 601