DM: use acrn_timer api to emulate rtc

Current RTC emulation is based on sigevent mechanism, replace with
acrn_timer API which based on timerfd/epoll mechanism to avoid
potential resource accessing conflict in the async thread.

Tracked-On: #1489
Signed-off-by: Victor Sun <victor.sun@intel.com>
Reviewed-by: Jian Jun Chen <jian.jun.chen@intel.com>
Acked-by: Yin Fengwei <fengwei.yin@intel.com>
This commit is contained in:
Victor Sun 2018-10-12 15:42:59 +08:00 committed by wenlingz
parent 8fdea84a63
commit 0e897c0a6a

View File

@ -31,19 +31,14 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/syscall.h>
#include <sys/signalfd.h>
#include "vmmapi.h"
#include "inout.h"
#include "mc146818rtc.h"
#include "rtc.h"
#include "mevent.h"
#include "timer.h"
/* #define DEBUG_RTC */
#ifdef DEBUG_RTC
@ -76,10 +71,8 @@ struct rtcdev {
struct vrtc {
struct vmctx *vm;
pthread_mutex_t mtx;
int signal_fd;
struct mevent *mevp;
timer_t periodic_timer_id; /*periodic timer id*/
timer_t update_timer_id; /*update timer id*/
struct acrn_timer update_timer; /* timer for update interrupt */
struct acrn_timer periodic_timer; /* timer for periodic interrupt */
u_int addr; /* RTC register to read or write */
time_t base_uptime;
time_t base_rtctime;
@ -576,34 +569,17 @@ fail:
}
static void
vrtc_create_timer(timer_t *timer_id, time_t sec, time_t nsec)
vrtc_start_timer(struct acrn_timer *timer, time_t sec, time_t nsec)
{
struct sigevent sigevt;
struct itimerspec ts;
memset(&sigevt, 0, sizeof(struct sigevent));
memset(&ts, 0, sizeof(struct itimerspec));
/* there is no glibc wrapper for gettid */
sigevt._sigev_un._tid = syscall(SYS_gettid);
sigevt.sigev_value.sival_ptr = timer_id;
sigevt.sigev_notify = SIGEV_THREAD_ID | SIGEV_SIGNAL;
sigevt.sigev_signo = VRTC_TIMER_SIGNO;
assert(timer_create(CLOCK_REALTIME, &sigevt, timer_id) == 0);
/*setting the interval time*/
ts.it_interval.tv_sec = sec;
ts.it_interval.tv_nsec = nsec;
/*set the delay time it will be started when timer_setting*/
ts.it_value.tv_sec = sec;
ts.it_value.tv_nsec = nsec;
assert(timer_settime(*timer_id, 0, &ts, NULL) == 0);
}
static void
vrtc_delete_timer(timer_t timerid)
{
timer_delete(timerid);
assert(acrn_timer_settime(timer, &ts) == 0);
}
static int
@ -814,15 +790,8 @@ vrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval)
newfreq = vrtc_freq(vrtc);
if (pintr_enabled(vrtc) && newfreq != oldfreq) {
/*stop the previous timer*/
if (vrtc->periodic_timer_id > 0) {
assert(timer_delete(vrtc->periodic_timer_id) == 0);
vrtc->periodic_timer_id = 0;
}
/*create the new periodic timer*/
vrtc_create_timer(&vrtc->periodic_timer_id, 0, newfreq);
assert(vrtc->periodic_timer_id > 0);
/*start the new periodic timer*/
vrtc_start_timer(&vrtc->periodic_timer, 0, newfreq);
} else {
/*Nothing to do*/
@ -875,15 +844,8 @@ vrtc_set_reg_a(struct vrtc *vrtc, uint8_t newval)
newfreq = vrtc_freq(vrtc);
if (pintr_enabled(vrtc) && newfreq != oldfreq) {
/*stop the previous timer*/
if (vrtc->periodic_timer_id > 0) {
assert(timer_delete(vrtc->periodic_timer_id) == 0);
vrtc->periodic_timer_id = 0;
}
/*create the new periodic timer*/
vrtc_create_timer(&vrtc->periodic_timer_id, 0, newfreq);
assert(vrtc->periodic_timer_id > 0);
/*start the new periodic timer*/
vrtc_start_timer(&vrtc->periodic_timer, 0, newfreq);
} else {
/*Nothing to do*/
}
@ -1108,41 +1070,6 @@ vrtc_enable_localtime(int l_time)
local_time = l_time;
}
static void
vrtc_handle_timer(int fd __attribute__((unused)),
enum ev_type t __attribute__((unused)),
void *arg)
{
struct vrtc *vrtc = arg;
struct signalfd_siginfo info;
timer_t *timer_id;
int len;
while (1) {
len = read(vrtc->signal_fd, &info, sizeof(info));
if (len == -1 && errno == EAGAIN)
break;
if (len == -1) {
RTC_DEBUG("signal_fd read failed: error = %d\n",
errno);
break;
}
if (len != sizeof(info)) {
RTC_DEBUG("signal_fd read len error: len = %d\n",
len);
break;
}
timer_id = (timer_t *)info.ssi_ptr;
if (*timer_id == vrtc->update_timer_id)
vrtc_update_timer(vrtc);
else if (*timer_id == vrtc->periodic_timer_id)
vrtc_periodic_timer(vrtc);
else
RTC_DEBUG("unsupported timer_id 0xlx\n", *timer_id);
}
}
int
vrtc_init(struct vmctx *ctx)
{
@ -1151,8 +1078,6 @@ vrtc_init(struct vmctx *ctx)
int err;
struct rtcdev *rtc;
time_t curtime;
sigset_t mask;
int flags;
struct inout_port rtc_addr, rtc_data;
vrtc = calloc(1, sizeof(struct vrtc));
@ -1226,30 +1151,14 @@ vrtc_init(struct vmctx *ctx)
secs_to_rtc(curtime, vrtc, 0);
pthread_mutex_unlock(&vrtc->mtx);
/*block VRTC_TIMER_SIGNO so it can be polled by signalfd*/
sigemptyset(&mask);
sigaddset(&mask, VRTC_TIMER_SIGNO);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
/* init periodic interrupt timer */
vrtc->periodic_timer.clockid = CLOCK_REALTIME;
acrn_timer_init(&vrtc->periodic_timer, vrtc_periodic_timer, vrtc);
/*create signalfd*/
vrtc->signal_fd = signalfd(-1, &mask, 0);
assert(vrtc->signal_fd > 0);
/*set close-on-exec*/
flags = fcntl(vrtc->signal_fd, F_GETFD);
fcntl(vrtc->signal_fd, F_SETFD, flags | FD_CLOEXEC);
/*set non-block*/
flags = fcntl(vrtc->signal_fd, F_GETFL);
fcntl(vrtc->signal_fd, F_SETFL, flags | O_NONBLOCK);
vrtc->mevp = mevent_add(vrtc->signal_fd, EVF_READ,
vrtc_handle_timer, vrtc);
assert(vrtc->mevp != NULL);
/*create update interrupt timer(1s)*/
vrtc_create_timer(&vrtc->update_timer_id, 1, 0);
assert(vrtc->update_timer_id > 0);
/* init update interrupt timer(1s)*/
vrtc->update_timer.clockid = CLOCK_REALTIME;
acrn_timer_init(&vrtc->update_timer, vrtc_update_timer, vrtc);
vrtc_start_timer(&vrtc->update_timer, 1, 0);
return 0;
}
@ -1259,29 +1168,10 @@ vrtc_deinit(struct vmctx *ctx)
{
struct vrtc *vrtc = ctx->vrtc;
struct inout_port iop;
sig_t prev_sig_handler;
/*delete timer*/
if (vrtc->update_timer_id > 0) {
vrtc_delete_timer(vrtc->update_timer_id);
vrtc->update_timer_id = (timer_t)-1;
}
if (vrtc->periodic_timer_id > 0) {
vrtc_delete_timer(vrtc->periodic_timer_id);
vrtc->periodic_timer_id = (timer_t)-1;
}
if (vrtc->mevp)
mevent_delete(vrtc->mevp);
if (vrtc->signal_fd > 0)
close(vrtc->signal_fd);
/*clear pending signals*/
prev_sig_handler = signal(VRTC_TIMER_SIGNO, SIG_IGN);
if (prev_sig_handler != SIG_ERR)
signal(VRTC_TIMER_SIGNO, prev_sig_handler);
/*deinit acrn_timer*/
acrn_timer_deinit(&vrtc->periodic_timer);
acrn_timer_deinit(&vrtc->update_timer);
memset(&iop, 0, sizeof(struct inout_port));
iop.name = "rtc";