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