mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-11 13:03:15 +00:00
It is possible for multiple timeouts to occur in one mevent epoll iteration. Providing the number of timer expirations to the timer callback handlers can be useful. E.g., this could improve emulation of timing-sensitive hardware components. Tracked-On: #2319 Signed-off-by: Peter Fang <peter.fang@intel.com> Acked-by: Anthony Xu <anthony.xu@intel.com>
143 lines
2.9 KiB
C
143 lines
2.9 KiB
C
/*
|
|
* Copyright (C) <2018> Intel Corporation
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <sys/timerfd.h>
|
|
|
|
#include "vmmapi.h"
|
|
#include "mevent.h"
|
|
#include "timer.h"
|
|
|
|
/* We can use timerfd and epoll mechanism to emulate kinds of timers like
|
|
* PIT/RTC/WDT/PMTIMER/... in device model under Linux.
|
|
* Compare with sigevent mechanism, timerfd has a advantage that it could
|
|
* avoid race condition on resource accessing in the async sigev thread.
|
|
*
|
|
* Please note timerfd and epoll are all Linux specific. If the code need to be
|
|
* ported to other OS, we can modify the api with POSIX timers and sigevent
|
|
* mechanism.
|
|
*/
|
|
|
|
static void
|
|
timer_handler(int fd __attribute__((unused)),
|
|
enum ev_type t __attribute__((unused)),
|
|
void *arg)
|
|
{
|
|
struct acrn_timer *timer = arg;
|
|
uint64_t nexp;
|
|
ssize_t size;
|
|
void (*cb)(void *, uint64_t);
|
|
|
|
if (timer == NULL) {
|
|
return;
|
|
}
|
|
|
|
/* Consume I/O event for default EPOLLLT type.
|
|
* Here is a temporary solution, the processing could be moved to
|
|
* mevent.c once EVF_TIMER is supported.
|
|
*/
|
|
size = read(timer->fd, &nexp, sizeof(nexp));
|
|
|
|
if (size < 0) {
|
|
if (errno != EAGAIN) {
|
|
perror("acrn_timer read timerfd error");
|
|
}
|
|
return;
|
|
}
|
|
|
|
assert(size > 0 && nexp > 0);
|
|
|
|
if ((cb = timer->callback) != NULL) {
|
|
(*cb)(timer->callback_param, nexp);
|
|
}
|
|
}
|
|
|
|
int32_t
|
|
acrn_timer_init(struct acrn_timer *timer, void (*cb)(void *, uint64_t),
|
|
void *param)
|
|
{
|
|
if ((timer == NULL) || (cb == NULL)) {
|
|
return -1;
|
|
}
|
|
|
|
timer->fd = -1;
|
|
if ((timer->clockid == CLOCK_REALTIME) ||
|
|
(timer->clockid == CLOCK_MONOTONIC)) {
|
|
timer->fd = timerfd_create(timer->clockid,
|
|
TFD_NONBLOCK | TFD_CLOEXEC);
|
|
} else {
|
|
perror("acrn_timer clockid is not supported.\n");
|
|
}
|
|
|
|
if (timer->fd <= 0) {
|
|
perror("acrn_timer create failed.\n");
|
|
return -1;
|
|
}
|
|
|
|
timer->mevp = mevent_add(timer->fd, EVF_READ, timer_handler, timer, NULL, NULL);
|
|
if (timer->mevp == NULL) {
|
|
close(timer->fd);
|
|
perror("acrn_timer mevent add failed.\n");
|
|
return -1;
|
|
}
|
|
|
|
timer->callback = cb;
|
|
timer->callback_param = param;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
acrn_timer_deinit(struct acrn_timer *timer)
|
|
{
|
|
if (timer == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (timer->mevp != NULL) {
|
|
mevent_delete_close(timer->mevp);
|
|
timer->mevp = NULL;
|
|
}
|
|
|
|
timer->fd = -1;
|
|
timer->callback = NULL;
|
|
timer->callback_param = NULL;
|
|
}
|
|
|
|
int32_t
|
|
acrn_timer_settime(struct acrn_timer *timer, const struct itimerspec *new_value)
|
|
{
|
|
if (timer == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
return timerfd_settime(timer->fd, 0, new_value, NULL);
|
|
}
|
|
|
|
int32_t
|
|
acrn_timer_settime_abs(struct acrn_timer *timer,
|
|
const struct itimerspec *new_value)
|
|
{
|
|
if (timer == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
return timerfd_settime(timer->fd, TFD_TIMER_ABSTIME, new_value, NULL);
|
|
}
|
|
|
|
int32_t
|
|
acrn_timer_gettime(struct acrn_timer *timer, struct itimerspec *cur_value)
|
|
{
|
|
if (timer == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
return timerfd_gettime(timer->fd, cur_value);
|
|
}
|