1 /*
2  * Copyright (C) 2018-2022 Intel Corporation.
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 #include <stdio.h>
7 #include <stdbool.h>
8 #include <unistd.h>
9 #include <errno.h>
10 #include <sys/timerfd.h>
11 
12 #include "vmmapi.h"
13 #include "mevent.h"
14 #include "timer.h"
15 #include "log.h"
16 
17 /* We can use timerfd and epoll mechanism to emulate kinds of timers like
18  * PIT/RTC/WDT/PMTIMER/... in device model under Linux.
19  * Compare with sigevent mechanism, timerfd has a advantage that it could
20  * avoid race condition on resource accessing in the async sigev thread.
21  *
22  * Please note timerfd and epoll are all Linux specific. If the code need to be
23  * ported to other OS, we can modify the api with POSIX timers and sigevent
24  * mechanism.
25  */
26 
27 static void
timer_handler(int fd,enum ev_type t,void * arg)28 timer_handler(int fd __attribute__((unused)),
29 		  enum ev_type t __attribute__((unused)),
30 		  void *arg)
31 {
32 	struct acrn_timer *timer = arg;
33 	uint64_t nexp;
34 	ssize_t size;
35 	void (*cb)(void *, uint64_t);
36 
37 	if (timer == NULL) {
38 		return;
39 	}
40 
41 	/* Consume I/O event for default EPOLLLT type.
42 	 * Here is a temporary solution, the processing could be moved to
43 	 * mevent.c once EVF_TIMER is supported.
44 	 */
45 	size = read(timer->fd, &nexp, sizeof(nexp));
46 
47 	if (size < 0) {
48 		if (errno != EAGAIN) {
49 			pr_err("acrn_timer read timerfd error");
50 		}
51 		return;
52 	}
53 
54 	/* check the validity of timer expiration. */
55 	if ((size == 0) || (nexp == 0))
56 		return;
57 
58 	if ((cb = timer->callback) != NULL) {
59 		(*cb)(timer->callback_param, nexp);
60 	}
61 }
62 
63 int32_t
acrn_timer_init(struct acrn_timer * timer,void (* cb)(void *,uint64_t),void * param)64 acrn_timer_init(struct acrn_timer *timer, void (*cb)(void *, uint64_t),
65 		void *param)
66 {
67 	if ((timer == NULL) || (cb == NULL)) {
68 		return -1;
69 	}
70 
71 	timer->fd = -1;
72 	if ((timer->clockid == CLOCK_REALTIME) ||
73 			(timer->clockid == CLOCK_MONOTONIC)) {
74 		timer->fd = timerfd_create(timer->clockid,
75 					TFD_NONBLOCK | TFD_CLOEXEC);
76 	} else {
77 		pr_err("acrn_timer clockid is not supported.\n");
78 	}
79 
80 	if (timer->fd <= 0) {
81 		pr_err("acrn_timer create failed.\n");
82 		return -1;
83 	}
84 
85 	timer->mevp = mevent_add(timer->fd, EVF_READ, timer_handler, timer, NULL, NULL);
86 	if (timer->mevp == NULL) {
87 		close(timer->fd);
88 		pr_err("acrn_timer mevent add failed.\n");
89 		return -1;
90 	}
91 
92 	timer->callback = cb;
93 	timer->callback_param = param;
94 
95 	return 0;
96 }
97 
98 void
acrn_timer_deinit(struct acrn_timer * timer)99 acrn_timer_deinit(struct acrn_timer *timer)
100 {
101 	if (timer == NULL) {
102 		return;
103 	}
104 
105 	if (timer->mevp != NULL) {
106 		mevent_delete_close(timer->mevp);
107 		timer->mevp = NULL;
108 	}
109 
110 	timer->fd = -1;
111 	timer->callback = NULL;
112 	timer->callback_param = NULL;
113 }
114 
115 int32_t
acrn_timer_settime(struct acrn_timer * timer,const struct itimerspec * new_value)116 acrn_timer_settime(struct acrn_timer *timer, const struct itimerspec *new_value)
117 {
118 	if (timer == NULL) {
119 		errno = EINVAL;
120 		return -1;
121 	}
122 
123 	return timerfd_settime(timer->fd, 0, new_value, NULL);
124 }
125 
126 int32_t
acrn_timer_settime_abs(struct acrn_timer * timer,const struct itimerspec * new_value)127 acrn_timer_settime_abs(struct acrn_timer *timer,
128 		const struct itimerspec *new_value)
129 {
130 	if (timer == NULL) {
131 		errno = EINVAL;
132 		return -1;
133 	}
134 
135 	return timerfd_settime(timer->fd, TFD_TIMER_ABSTIME, new_value, NULL);
136 }
137 
138 int32_t
acrn_timer_gettime(struct acrn_timer * timer,struct itimerspec * cur_value)139 acrn_timer_gettime(struct acrn_timer *timer, struct itimerspec *cur_value)
140 {
141 	if (timer == NULL) {
142 		errno = EINVAL;
143 		return -1;
144 	}
145 
146 	return timerfd_gettime(timer->fd, cur_value);
147 }
148