| 1 | |
| 2 | |
| 3 | * Copyright (C) 2015 Anton Ivanov (aivanov@{brocade.com,kot-begemot.co.uk})
|
| 4 | * Copyright (C) 2015 Thomas Meyer ([email protected])
|
| 5 | * Copyright (C) 2012-2014 Cisco Systems
|
| 6 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike{addtoit,linux.intel}.com)
|
| 7 |
|
| 8 |
|
| 9 | #include <stddef.h>
|
| 10 | #include <unistd.h>
|
| 11 | #include <errno.h>
|
| 12 | #include <signal.h>
|
| 13 | #include <time.h>
|
| 14 | #include <sys/signalfd.h>
|
| 15 | #include <sys/time.h>
|
| 16 | #include <kern_util.h>
|
| 17 | #include <os.h>
|
| 18 | #include <smp.h>
|
| 19 | #include <string.h>
|
| 20 | #include "internal.h"
|
| 21 |
|
| 22 | static timer_t event_high_res_timer[CONFIG_NR_CPUS] = { 0 };
|
| 23 |
|
| 24 | static inline long long timespec_to_ns(const struct timespec *ts)
|
| 25 | {
|
| 26 | return ((long long) ts->tv_sec * UM_NSEC_PER_SEC) + ts->tv_nsec;
|
| 27 | }
|
| 28 |
|
| 29 | long long os_persistent_clock_emulation(void)
|
| 30 | {
|
| 31 | struct timespec realtime_tp;
|
| 32 |
|
| 33 | clock_gettime(CLOCK_REALTIME, &realtime_tp);
|
| 34 | return timespec_to_ns(&realtime_tp);
|
| 35 | }
|
| 36 |
|
| 37 | #ifndef sigev_notify_thread_id
|
| 38 | #define sigev_notify_thread_id _sigev_un._tid
|
| 39 | #endif
|
| 40 |
|
| 41 | |
| 42 | * os_timer_create() - create an new posix (interval) timer
|
| 43 |
|
| 44 | int os_timer_create(void)
|
| 45 | {
|
| 46 | int cpu = uml_curr_cpu();
|
| 47 | timer_t *t = &event_high_res_timer[cpu];
|
| 48 | struct sigevent sev = {
|
| 49 | .sigev_notify = SIGEV_THREAD_ID,
|
| 50 | .sigev_signo = SIGALRM,
|
| 51 | .sigev_value.sival_ptr = t,
|
| 52 | .sigev_notify_thread_id = gettid(),
|
| 53 | };
|
| 54 |
|
| 55 | if (timer_create(CLOCK_MONOTONIC, &sev, t) == -1)
|
| 56 | return -1;
|
| 57 |
|
| 58 | return 0;
|
| 59 | }
|
| 60 |
|
| 61 | int os_timer_set_interval(int cpu, unsigned long long nsecs)
|
| 62 | {
|
| 63 | struct itimerspec its;
|
| 64 |
|
| 65 | its.it_value.tv_sec = nsecs / UM_NSEC_PER_SEC;
|
| 66 | its.it_value.tv_nsec = nsecs % UM_NSEC_PER_SEC;
|
| 67 |
|
| 68 | its.it_interval.tv_sec = nsecs / UM_NSEC_PER_SEC;
|
| 69 | its.it_interval.tv_nsec = nsecs % UM_NSEC_PER_SEC;
|
| 70 |
|
| 71 | if (timer_settime(event_high_res_timer[cpu], 0, &its, NULL) == -1)
|
| 72 | return -errno;
|
| 73 |
|
| 74 | return 0;
|
| 75 | }
|
| 76 |
|
| 77 | int os_timer_one_shot(int cpu, unsigned long long nsecs)
|
| 78 | {
|
| 79 | struct itimerspec its = {
|
| 80 | .it_value.tv_sec = nsecs / UM_NSEC_PER_SEC,
|
| 81 | .it_value.tv_nsec = nsecs % UM_NSEC_PER_SEC,
|
| 82 |
|
| 83 | .it_interval.tv_sec = 0,
|
| 84 | .it_interval.tv_nsec = 0, |
| 85 | };
|
| 86 |
|
| 87 | timer_settime(event_high_res_timer[cpu], 0, &its, NULL);
|
| 88 | return 0;
|
| 89 | }
|
| 90 |
|
| 91 | |
| 92 | * os_timer_disable() - disable the posix (interval) timer
|
| 93 | * @cpu: the CPU for which the timer is to be disabled
|
| 94 |
|
| 95 | void os_timer_disable(int cpu)
|
| 96 | {
|
| 97 | struct itimerspec its;
|
| 98 |
|
| 99 | memset(&its, 0, sizeof(struct itimerspec));
|
| 100 | timer_settime(event_high_res_timer[cpu], 0, &its, NULL);
|
| 101 | }
|
| 102 |
|
| 103 | long long os_nsecs(void)
|
| 104 | {
|
| 105 | struct timespec ts;
|
| 106 |
|
| 107 | clock_gettime(CLOCK_MONOTONIC,&ts);
|
| 108 | return timespec_to_ns(&ts);
|
| 109 | }
|
| 110 |
|
| 111 | static __thread int wake_signals;
|
| 112 |
|
| 113 | void os_idle_prepare(void)
|
| 114 | {
|
| 115 | sigset_t set;
|
| 116 |
|
| 117 | sigemptyset(&set);
|
| 118 | sigaddset(&set, SIGALRM);
|
| 119 | sigaddset(&set, IPI_SIGNAL);
|
| 120 |
|
| 121 | |
| 122 | * We need to use signalfd rather than sigsuspend in idle sleep
|
| 123 | * because the IPI signal is a real-time signal that carries data,
|
| 124 | * and unlike handling SIGALRM, we cannot simply flag it in
|
| 125 | * signals_pending.
|
| 126 |
|
| 127 | wake_signals = signalfd(-1, &set, SFD_CLOEXEC);
|
| 128 | if (wake_signals < 0)
|
| 129 | panic("Failed to create signal FD, errno = %d", errno);
|
| 130 | }
|
| 131 |
|
| 132 | |
| 133 | * os_idle_sleep() - sleep until interrupted
|
| 134 |
|
| 135 | void os_idle_sleep(void)
|
| 136 | {
|
| 137 | sigset_t set;
|
| 138 |
|
| 139 | |
| 140 | * Block SIGALRM while performing the need_resched check.
|
| 141 | * Note that, because IRQs are disabled, the IPI signal is
|
| 142 | * already blocked.
|
| 143 |
|
| 144 | sigemptyset(&set);
|
| 145 | sigaddset(&set, SIGALRM);
|
| 146 | sigprocmask(SIG_BLOCK, &set, NULL);
|
| 147 |
|
| 148 | |
| 149 | * Because disabling IRQs does not block SIGALRM, it is also
|
| 150 | * necessary to check for any pending timer alarms.
|
| 151 |
|
| 152 | if (!uml_need_resched() && !timer_alarm_pending())
|
| 153 | os_poll(1, &wake_signals);
|
| 154 |
|
| 155 |
|
| 156 | sigprocmask(SIG_UNBLOCK, &set, NULL);
|
| 157 | }
|