552 lines
11 KiB
C
552 lines
11 KiB
C
/*
|
|
* Copyright (C)2018 Intel Corporation
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <time.h>
|
|
#include <pthread.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/stat.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
#include <stdbool.h>
|
|
#include <errno.h>
|
|
#include "mevent.h"
|
|
#include "acrnctl.h"
|
|
#include "acrn_mngr.h"
|
|
#include "ioc.h"
|
|
|
|
/* acrnd worker timer */
|
|
|
|
struct work_arg {
|
|
char name[VMNAME_LEN];
|
|
};
|
|
|
|
struct acrnd_work {
|
|
void (*func) (struct work_arg *arg);
|
|
time_t expire; /* when execute this work */
|
|
|
|
struct work_arg arg;
|
|
|
|
LIST_ENTRY(acrnd_work) list;
|
|
};
|
|
|
|
static LIST_HEAD(acrnd_work_list, acrnd_work) work_head;
|
|
static pthread_mutex_t work_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
/* acrnd_add_work(), add a worker function.
|
|
* @func, the worker function.
|
|
* @sec, when add a @func(), after @sec seconds @func() will be called.
|
|
* @arg, a cpoy of @arg will be pass to @func.
|
|
*/
|
|
int acrnd_add_work(void (*func) (struct work_arg *arg),
|
|
struct work_arg *arg, unsigned sec)
|
|
{
|
|
struct acrnd_work *work;
|
|
time_t current;
|
|
|
|
if (!func) {
|
|
pdebug();
|
|
return -1;
|
|
}
|
|
|
|
work = calloc(1, sizeof(*work));
|
|
if (!work) {
|
|
perror("Alloc work struct fail:");
|
|
return -1;
|
|
}
|
|
|
|
current = time(NULL);
|
|
if (current == (time_t) - 1) {
|
|
perror("Get current time by timer() fail:");
|
|
free(work);
|
|
return -1;
|
|
}
|
|
|
|
work->func = func;
|
|
work->expire = sec + current;
|
|
if (arg)
|
|
memcpy(&work->arg, arg, sizeof(*arg));
|
|
|
|
pthread_mutex_lock(&work_mutex);
|
|
LIST_INSERT_HEAD(&work_head, work, list);
|
|
pthread_mutex_unlock(&work_mutex);
|
|
return 0;
|
|
}
|
|
|
|
/* check if the works in work_head is expired, if expired then run
|
|
* work func() once and delete it
|
|
*/
|
|
static void try_do_works(void)
|
|
{
|
|
time_t current;
|
|
struct acrnd_work *work, *twork;
|
|
|
|
current = time(NULL);
|
|
if (current == (time_t) - 1) {
|
|
perror("Get current time by timer() fail:");
|
|
return;
|
|
}
|
|
|
|
list_foreach_safe(work, &work_head, list, twork) {
|
|
if (current > work->expire) {
|
|
pthread_mutex_lock(&work_mutex);
|
|
work->func(&work->arg);
|
|
LIST_REMOVE(work, list);
|
|
pthread_mutex_unlock(&work_mutex);
|
|
free(work);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void acrnd_run_vm(char *name);
|
|
|
|
/* Time to run/resume VM */
|
|
void acrnd_vm_timer_func(struct work_arg *arg)
|
|
{
|
|
struct vmmngr_struct *vm;
|
|
|
|
if (!arg) {
|
|
pdebug();
|
|
return;
|
|
}
|
|
|
|
vmmngr_update();
|
|
vm = vmmngr_find(arg->name);
|
|
if (!vm) {
|
|
pdebug();
|
|
return;
|
|
}
|
|
|
|
switch (vm->state) {
|
|
case VM_CREATED:
|
|
acrnd_run_vm(arg->name);
|
|
break;
|
|
case VM_PAUSED:
|
|
resume_vm(arg->name);
|
|
break;
|
|
default:
|
|
pdebug();
|
|
}
|
|
}
|
|
|
|
#define TIMER_LIST_FILE "/opt/acrn/conf/timer_list"
|
|
static pthread_mutex_t timer_file_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
/* load/store_timer_list to file to keep timers if SOS poweroff */
|
|
static int load_timer_list(void)
|
|
{
|
|
FILE *fp;
|
|
struct work_arg arg = {};
|
|
time_t expire;
|
|
time_t record;
|
|
time_t current;
|
|
char l[256];
|
|
char s1[16], s2[64], s3[64]; /* vmname & expire */
|
|
int ret = 0;
|
|
|
|
pthread_mutex_lock(&timer_file_mutex);
|
|
|
|
fp = fopen(TIMER_LIST_FILE, "r");
|
|
if (!fp) {
|
|
perror("Open timer list file");
|
|
ret = -1;
|
|
goto open_file_err;
|
|
}
|
|
|
|
current = time(NULL);
|
|
|
|
while (!feof(fp)) {
|
|
memset(l, 0, 256);
|
|
fgets(l, 255, fp);
|
|
|
|
memset(s1, 0, 16);
|
|
memset(s2, 0, 64);
|
|
memset(s3, 0, 64);
|
|
|
|
sscanf(l, "%s\t%s\t%s", s1, s2, s3);
|
|
|
|
if (strlen(s1) == 0 || strlen(s1) > 16) {
|
|
perror("Invalid vmname from timer list file");
|
|
continue;
|
|
}
|
|
|
|
memset(arg.name, 0, sizeof(arg.name));
|
|
strncpy(arg.name, s1, sizeof(s1));
|
|
|
|
expire = strtoul(s2, NULL, 10);
|
|
if (expire == 0 || errno == ERANGE) {
|
|
perror("Invalid expire from timer list file");
|
|
continue;
|
|
}
|
|
|
|
record = strtoul(s3, NULL, 10);
|
|
if (record == 0 || errno == ERANGE) {
|
|
perror("Invalid record time from timer list file");
|
|
continue;
|
|
}
|
|
|
|
if (current == (time_t) -1)
|
|
current = record;
|
|
|
|
if (expire > current)
|
|
expire -= current;
|
|
else
|
|
expire = 1;
|
|
|
|
if (acrnd_add_work(acrnd_vm_timer_func, &arg, expire))
|
|
ret = -1;
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
open_file_err:
|
|
pthread_mutex_unlock(&timer_file_mutex);
|
|
return ret;
|
|
}
|
|
|
|
#define ACRND_LOG_FMT "/opt/acrn/%s.log"
|
|
|
|
static void acrnd_run_vm(char *name)
|
|
{
|
|
char log_path[128] = {};
|
|
|
|
snprintf(log_path, sizeof(log_path) -1, ACRND_LOG_FMT, name);
|
|
unlink(log_path);
|
|
stdin = freopen(log_path, "w+", stdin);
|
|
stdout = freopen(log_path, "w+", stdout);
|
|
stderr = freopen(log_path, "w+", stderr);
|
|
fflush(stdin);
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
|
|
start_vm(name);
|
|
printf("%s exited!\n", name);
|
|
exit(0);
|
|
}
|
|
|
|
static int active_all_vms(void)
|
|
{
|
|
struct vmmngr_struct *vm;
|
|
int ret = 0;
|
|
pid_t pid;
|
|
|
|
vmmngr_update();
|
|
|
|
LIST_FOREACH(vm, &vmmngr_head, list) {
|
|
switch (vm->state) {
|
|
case VM_CREATED:
|
|
pid = fork();
|
|
if (!pid)
|
|
acrnd_run_vm(vm->name);
|
|
break;
|
|
case VM_PAUSED:
|
|
ret += resume_vm(vm->name);
|
|
break;
|
|
default:
|
|
pdebug();
|
|
}
|
|
}
|
|
|
|
return ret ? -1 : 0;
|
|
}
|
|
|
|
#define SOS_LCS_SOCK "sos-lcs"
|
|
#define DEFAULT_TIMEOUT 2U
|
|
#define SOS_ADVANCE_WKUP 10U /* WKUP SOS 10 sec in advance */
|
|
#define ACRND_NAME "acrnd"
|
|
static int acrnd_fd = -1;
|
|
|
|
unsigned get_sos_wakeup_reason(void)
|
|
{
|
|
int client_fd, ret = 0;
|
|
struct req_wakeup_reason req;
|
|
struct ack_wakeup_reason ack;
|
|
|
|
client_fd = mngr_open_un(SOS_LCS_SOCK, MNGR_CLIENT);
|
|
if (client_fd <= 0) {
|
|
fprintf(stderr, "Failed to open the socket(%s) to query the "
|
|
"reason for the wake-up", SOS_LCS_SOCK);
|
|
goto EXIT;
|
|
}
|
|
|
|
req.msg.magic = MNGR_MSG_MAGIC;
|
|
req.msg.msgid = WAKEUP_REASON;
|
|
req.msg.timestamp = time(NULL);
|
|
req.msg.len = sizeof(struct req_wakeup_reason);
|
|
|
|
if (mngr_send_msg(client_fd, (void *)&req, (void *)&ack, sizeof(ack),
|
|
DEFAULT_TIMEOUT))
|
|
fprintf(stderr, "Failed to get wakeup_reason from SOS, err(%d)\n", ret);
|
|
else
|
|
ret = ack.reason;
|
|
|
|
mngr_close(client_fd);
|
|
EXIT:
|
|
return ret;
|
|
}
|
|
|
|
static void handle_timer_req(struct mngr_msg *msg, int client_fd, void *param)
|
|
{
|
|
struct req_acrnd_timer *req = (void *)msg;
|
|
struct ack_acrnd_timer ack;
|
|
struct vmmngr_struct *vm;
|
|
struct work_arg arg = {};
|
|
|
|
ack.msg.msgid = req->msg.msgid;
|
|
ack.msg.len = sizeof(ack);
|
|
ack.msg.timestamp = req->msg.timestamp;
|
|
ack.err = -1;
|
|
|
|
vmmngr_update();
|
|
vm = vmmngr_find(req->name);
|
|
if (!vm) {
|
|
pdebug();
|
|
goto reply_ack;
|
|
}
|
|
|
|
strncpy(arg.name, req->name, sizeof(arg.name) - 1);
|
|
|
|
if (acrnd_add_work(acrnd_vm_timer_func, &arg, req->t)) {
|
|
pdebug();
|
|
goto reply_ack;
|
|
}
|
|
|
|
ack.err = 0;
|
|
reply_ack:
|
|
if (client_fd > 0)
|
|
mngr_send_msg(client_fd, (void *)&ack, NULL, 0, 0);
|
|
}
|
|
|
|
static int set_sos_timer(time_t due_time)
|
|
{
|
|
int client_fd, ret;
|
|
int retry = 1;
|
|
struct req_rtc_timer req;
|
|
struct ack_rtc_timer ack;
|
|
|
|
client_fd = mngr_open_un(SOS_LCS_SOCK, MNGR_CLIENT);
|
|
if (client_fd <= 0) {
|
|
perror("Failed to open sock for to req wkup_reason");
|
|
ret = client_fd;
|
|
goto EXIT;
|
|
}
|
|
|
|
req.msg.magic = MNGR_MSG_MAGIC;
|
|
req.msg.msgid = RTC_TIMER;
|
|
req.msg.timestamp = time(NULL);
|
|
req.msg.len = sizeof(struct req_rtc_timer);
|
|
req.t = due_time;
|
|
|
|
RETRY:
|
|
ret =
|
|
mngr_send_msg(client_fd, (void *)&req, (void *)&ack, sizeof(ack),
|
|
DEFAULT_TIMEOUT);
|
|
while (ret != 0 && retry < 5) {
|
|
printf("Fail to set sos wakeup timer(err:%d), retry %d...\n",
|
|
ret, retry++);
|
|
goto RETRY;
|
|
}
|
|
|
|
mngr_close(client_fd);
|
|
EXIT:
|
|
return ret;
|
|
}
|
|
|
|
static int store_timer_list(void)
|
|
{
|
|
FILE *fp;
|
|
struct acrnd_work *w;
|
|
time_t sys_wakeup = 0;
|
|
time_t current;
|
|
int ret = 0;
|
|
|
|
current = time(NULL);
|
|
if (current == (time_t) - 1) {
|
|
pdebug();
|
|
return -1;
|
|
}
|
|
|
|
pthread_mutex_lock(&timer_file_mutex);
|
|
fp = fopen(TIMER_LIST_FILE, "w+");
|
|
if (!fp) {
|
|
perror("Open timer list file");
|
|
ret = -1;
|
|
goto open_file_err;
|
|
}
|
|
pthread_mutex_lock(&work_mutex);
|
|
LIST_FOREACH(w, &work_head, list) {
|
|
if (w->func != acrnd_vm_timer_func)
|
|
continue;
|
|
if (!sys_wakeup)
|
|
sys_wakeup = w->expire;
|
|
if (w->expire < sys_wakeup)
|
|
sys_wakeup = w->expire;
|
|
fprintf(fp, "%s\t%lu\t%lu\n", w->arg.name, w->expire, current);
|
|
}
|
|
pthread_mutex_unlock(&work_mutex);
|
|
|
|
/* If any timer is stored
|
|
* system must be awake at sys_wakeup */
|
|
if (sys_wakeup) {
|
|
if (sys_wakeup > SOS_ADVANCE_WKUP)
|
|
sys_wakeup -= SOS_ADVANCE_WKUP;
|
|
set_sos_timer(sys_wakeup);
|
|
} else {
|
|
unlink(TIMER_LIST_FILE);
|
|
}
|
|
|
|
fclose(fp);
|
|
open_file_err:
|
|
pthread_mutex_unlock(&timer_file_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int check_vms_status(unsigned int status)
|
|
{
|
|
struct vmmngr_struct *s;
|
|
|
|
vmmngr_update();
|
|
|
|
LIST_FOREACH(s, &vmmngr_head, list)
|
|
if (s->state != status && s->state != VM_CREATED)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _handle_acrnd_stop(unsigned int timeout)
|
|
{
|
|
unsigned long t = timeout;
|
|
|
|
/*Let ospm stopping UOSs */
|
|
|
|
/* list and update the vm status */
|
|
do {
|
|
if (check_vms_status(VM_CREATED) == 0)
|
|
return 0;
|
|
sleep(1);
|
|
}
|
|
while (t--);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void handle_acrnd_stop(struct mngr_msg *msg, int client_fd, void *param)
|
|
{
|
|
struct req_acrnd_stop *req = (void *)msg;
|
|
struct ack_acrnd_stop ack;
|
|
|
|
ack.msg.msgid = req->msg.msgid;
|
|
ack.msg.len = sizeof(ack);
|
|
ack.msg.timestamp = req->msg.timestamp;
|
|
ack.err = _handle_acrnd_stop(req->timeout);
|
|
|
|
store_timer_list();
|
|
|
|
if (client_fd > 0)
|
|
mngr_send_msg(client_fd, (void *)&ack, NULL, 0, 0);
|
|
}
|
|
|
|
void handle_acrnd_resume(struct mngr_msg *msg, int client_fd, void *param)
|
|
{
|
|
struct req_acrnd_resume *req = (void *)msg;
|
|
struct ack_acrnd_resume ack;
|
|
struct stat st;
|
|
int wakeup_reason;
|
|
|
|
ack.msg.msgid = req->msg.msgid;
|
|
ack.msg.len = sizeof(ack);
|
|
ack.msg.timestamp = req->msg.timestamp;
|
|
ack.err = 0;
|
|
|
|
/* Do we have a timer list file to load? */
|
|
if (!stat(TIMER_LIST_FILE, &st))
|
|
if (S_ISREG(st.st_mode)) {
|
|
ack.err = load_timer_list();
|
|
if (ack.err)
|
|
pdebug();
|
|
goto reply_ack;
|
|
}
|
|
|
|
/* acrnd get wakeup_reason from sos lcs */
|
|
wakeup_reason = get_sos_wakeup_reason();
|
|
|
|
if (wakeup_reason & CBC_WK_RSN_RTC) {
|
|
/* do nothing, just wait the acrnd_work to expire */
|
|
goto reply_ack;
|
|
}
|
|
|
|
ack.err = active_all_vms();
|
|
|
|
reply_ack:
|
|
unlink(TIMER_LIST_FILE);
|
|
|
|
if (client_fd > 0)
|
|
mngr_send_msg(client_fd, (void *)&ack, NULL, 0, 0);
|
|
}
|
|
|
|
static void handle_on_exit(void)
|
|
{
|
|
store_timer_list();
|
|
|
|
if (acrnd_fd > 0) {
|
|
mngr_close(acrnd_fd);
|
|
acrnd_fd = -1;
|
|
}
|
|
}
|
|
|
|
int init_vm(void)
|
|
{
|
|
unsigned int wakeup_reason;
|
|
struct stat st;
|
|
|
|
if (!stat(TIMER_LIST_FILE, &st))
|
|
if (S_ISREG(st.st_mode))
|
|
return load_timer_list();
|
|
|
|
/* init all UOSs, according wakeup_reason */
|
|
wakeup_reason = get_sos_wakeup_reason();
|
|
|
|
if (wakeup_reason & CBC_WK_RSN_RTC)
|
|
return load_timer_list();
|
|
else {
|
|
/* TODO: auto start UOSs */
|
|
return active_all_vms();
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
/* create listening thread */
|
|
acrnd_fd = mngr_open_un(ACRND_NAME, MNGR_SERVER);
|
|
if (acrnd_fd < 0) {
|
|
pdebug();
|
|
return -1;
|
|
}
|
|
|
|
if (init_vm()) {
|
|
pdebug();
|
|
return -1;
|
|
}
|
|
|
|
unlink(TIMER_LIST_FILE);
|
|
|
|
atexit(handle_on_exit);
|
|
|
|
mngr_add_handler(acrnd_fd, ACRND_TIMER, handle_timer_req, NULL);
|
|
mngr_add_handler(acrnd_fd, ACRND_STOP, handle_acrnd_stop, NULL);
|
|
mngr_add_handler(acrnd_fd, ACRND_RESUME, handle_acrnd_resume, NULL);
|
|
|
|
/* Last thing, run our timer works */
|
|
while (1) {
|
|
try_do_works();
|
|
sleep(1);
|
|
}
|
|
|
|
return 0;
|
|
}
|