summaryrefslogtreecommitdiff
path: root/arch/um/os-Linux/time.c
blob: 13ebc86918d4421a83ee75660e03c67fe6c2c96a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2015 Anton Ivanov (aivanov@{brocade.com,kot-begemot.co.uk})
 * Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
 * Copyright (C) 2012-2014 Cisco Systems
 * Copyright (C) 2000 - 2007 Jeff Dike (jdike{addtoit,linux.intel}.com)
 */

#include <stddef.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <sys/signalfd.h>
#include <sys/time.h>
#include <kern_util.h>
#include <os.h>
#include <smp.h>
#include <string.h>
#include "internal.h"

static timer_t event_high_res_timer[CONFIG_NR_CPUS] = { 0 };

static inline long long timespec_to_ns(const struct timespec *ts)
{
	return ((long long) ts->tv_sec * UM_NSEC_PER_SEC) + ts->tv_nsec;
}

long long os_persistent_clock_emulation(void)
{
	struct timespec realtime_tp;

	clock_gettime(CLOCK_REALTIME, &realtime_tp);
	return timespec_to_ns(&realtime_tp);
}

#ifndef sigev_notify_thread_id
#define sigev_notify_thread_id _sigev_un._tid
#endif

/**
 * os_timer_create() - create an new posix (interval) timer
 */
int os_timer_create(void)
{
	int cpu = uml_curr_cpu();
	timer_t *t = &event_high_res_timer[cpu];
	struct sigevent sev = {
		.sigev_notify = SIGEV_THREAD_ID,
		.sigev_signo = SIGALRM,
		.sigev_value.sival_ptr = t,
		.sigev_notify_thread_id = gettid(),
	};

	if (timer_create(CLOCK_MONOTONIC, &sev, t) == -1)
		return -1;

	return 0;
}

int os_timer_set_interval(int cpu, unsigned long long nsecs)
{
	struct itimerspec its;

	its.it_value.tv_sec = nsecs / UM_NSEC_PER_SEC;
	its.it_value.tv_nsec = nsecs % UM_NSEC_PER_SEC;

	its.it_interval.tv_sec = nsecs / UM_NSEC_PER_SEC;
	its.it_interval.tv_nsec = nsecs % UM_NSEC_PER_SEC;

	if (timer_settime(event_high_res_timer[cpu], 0, &its, NULL) == -1)
		return -errno;

	return 0;
}

int os_timer_one_shot(int cpu, unsigned long long nsecs)
{
	struct itimerspec its = {
		.it_value.tv_sec = nsecs / UM_NSEC_PER_SEC,
		.it_value.tv_nsec = nsecs % UM_NSEC_PER_SEC,

		.it_interval.tv_sec = 0,
		.it_interval.tv_nsec = 0, // we cheat here
	};

	timer_settime(event_high_res_timer[cpu], 0, &its, NULL);
	return 0;
}

/**
 * os_timer_disable() - disable the posix (interval) timer
 * @cpu: the CPU for which the timer is to be disabled
 */
void os_timer_disable(int cpu)
{
	struct itimerspec its;

	memset(&its, 0, sizeof(struct itimerspec));
	timer_settime(event_high_res_timer[cpu], 0, &its, NULL);
}

long long os_nsecs(void)
{
	struct timespec ts;

	clock_gettime(CLOCK_MONOTONIC,&ts);
	return timespec_to_ns(&ts);
}

static __thread int wake_signals;

void os_idle_prepare(void)
{
	sigset_t set;

	sigemptyset(&set);
	sigaddset(&set, SIGALRM);
	sigaddset(&set, IPI_SIGNAL);

	/*
	 * We need to use signalfd rather than sigsuspend in idle sleep
	 * because the IPI signal is a real-time signal that carries data,
	 * and unlike handling SIGALRM, we cannot simply flag it in
	 * signals_pending.
	 */
	wake_signals = signalfd(-1, &set, SFD_CLOEXEC);
	if (wake_signals < 0)
		panic("Failed to create signal FD, errno = %d", errno);
}

/**
 * os_idle_sleep() - sleep until interrupted
 */
void os_idle_sleep(void)
{
	sigset_t set;

	/*
	 * Block SIGALRM while performing the need_resched check.
	 * Note that, because IRQs are disabled, the IPI signal is
	 * already blocked.
	 */
	sigemptyset(&set);
	sigaddset(&set, SIGALRM);
	sigprocmask(SIG_BLOCK, &set, NULL);

	/*
	 * Because disabling IRQs does not block SIGALRM, it is also
	 * necessary to check for any pending timer alarms.
	 */
	if (!uml_need_resched() && !timer_alarm_pending())
		os_poll(1, &wake_signals);

	/* Restore the signal mask. */
	sigprocmask(SIG_UNBLOCK, &set, NULL);
}