acrn-hypervisor/devicemodel/core/monitor.c
Geoffroy Van Cutsem 8b16be9185 Remove "All rights reserved" string headers
Many of the license and Intel copyright headers include the "All rights
reserved" string. It is not relevant in the context of the BSD-3-Clause
license that the code is released under. This patch removes those strings
throughout the code (hypervisor, devicemodel and misc).

Tracked-On: #7254
Signed-off-by: Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com>
2022-04-06 13:21:02 +08:00

520 lines
12 KiB
C

/*
* Project Acrn
* Acrn-dm-monitor
*
* Copyright (C) 2018 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*
* Author: TaoYuhong <yuhong.tao@intel.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <unistd.h>
#include <pthread.h>
#include "dm.h"
#include "dm_string.h"
#include "monitor.h"
#include "acrn_mngr.h"
#include "pm.h"
#include "vmmapi.h"
#include "log.h"
#define INTR_STORM_MONITOR_PERIOD 10 /* 10 seconds */
#define INTR_STORM_THRESHOLD 100000 /* 10K times per second */
#define DELAY_INTR_TIME 1 /* 1ms */
#define DELAY_DURATION 100000 /* 100ms of total duration for delay intr */
#define TIME_TO_CHECK_AGAIN 2 /* 2seconds */
struct intr_monitor_setting_t {
bool enable;
uint32_t threshold; /* intr count in probe_period when intr storm happens */
uint32_t probe_period; /* seconds: the period to probe intr data */
uint32_t delay_time; /* ms: the time to delay each intr injection */
uint32_t delay_duration; /* us: the delay duration, after it, intr injection restore to normal */
};
union intr_monitor_t {
struct acrn_intr_monitor monitor;
char reserved[4096];
} __aligned(4096);
static union intr_monitor_t intr_data;
static uint64_t intr_cnt_buf[MAX_PTDEV_NUM * 2];
static pthread_t intr_storm_monitor_pid;
static struct intr_monitor_setting_t intr_monitor_setting = {
.enable = false,
};
/* switch macro, just open in debug */
/* #define INTR_MONITOR_DBG */
#ifdef INTR_MONITOR_DBG
static FILE * dbg_file;
#define DPRINTF(format, args...) \
do { fprintf(dbg_file, format, args); fflush(dbg_file); } while (0)
/* this is a debug function */
static void write_intr_data_to_file(const struct acrn_intr_monitor *hdr)
{
static int wr_cnt;
int j;
wr_cnt++;
fprintf(dbg_file, "\n==%d time devs=%d==\n", wr_cnt, hdr->buf_cnt / 2);
fprintf(dbg_file, "IRQ\t\tCount\n");
for (j = 0; j < hdr->buf_cnt; j += 2) {
if (hdr->buffer[j + 1] != 0) {
fprintf(dbg_file, "%ld\t\t%ld\n", hdr->buffer[j], hdr->buffer[j + 1]);
}
}
fflush(dbg_file);
}
#else
#define DPRINTF(format, arg...)
#endif
static void *intr_storm_monitor_thread(void *arg)
{
struct vmctx *ctx = (struct vmctx *)arg;
struct acrn_intr_monitor *hdr = &intr_data.monitor;
uint64_t delta = 0UL;
int ret, i;
#ifdef INTR_MONITOR_DBG
dbg_file = fopen("/tmp/intr_log", "w+");
#endif
sleep(intr_monitor_setting.probe_period);
/* first to get interrupt data */
hdr->cmd = INTR_CMD_GET_DATA;
hdr->buf_cnt = MAX_PTDEV_NUM * 2;
memset(hdr->buffer, 0, sizeof(uint64_t) * hdr->buf_cnt);
ret = vm_intr_monitor(ctx, hdr);
if (ret) {
DPRINTF("first get intr data failed, ret: %d\n", ret);
intr_storm_monitor_pid = 0;
return NULL;
}
while (1) {
#ifdef INTR_MONITOR_DBG
write_intr_data_to_file(hdr);
#endif
memcpy(intr_cnt_buf, hdr->buffer, sizeof(uint64_t) * hdr->buf_cnt);
sleep(intr_monitor_setting.probe_period);
/* next time to get interrupt data */
memset(hdr->buffer, 0, sizeof(uint64_t) * hdr->buf_cnt);
ret = vm_intr_monitor(ctx, hdr);
if (ret) {
pr_err("next get intr data failed, ret: %d\n", ret);
intr_storm_monitor_pid = 0;
break;
}
/*
* calc the delta of the two times count of interrupt;
* compare the IRQ num first, if not same just drop it,
* for it just happens rarelly when devices dynamically
* allocation in Service VM or User VM, it can be calc next time
*/
for (i = 0; i < hdr->buf_cnt; i += 2) {
if (hdr->buffer[i] != intr_cnt_buf[i])
continue;
/* avoid delta overflow */
if (hdr->buffer[i + 1] < intr_cnt_buf[i + 1])
continue;
delta = hdr->buffer[i + 1] - intr_cnt_buf[i + 1];
if (delta > intr_monitor_setting.threshold) {
#ifdef INTR_MONITOR_DBG
write_intr_data_to_file(hdr);
#endif
break;
}
}
/* storm detected, handle the intr abnormal status */
if (i < hdr->buf_cnt) {
pr_notice("irq=%ld, delta=%ld\n", intr_cnt_buf[i], delta);
hdr->cmd = INTR_CMD_DELAY_INT;
hdr->buffer[0] = intr_monitor_setting.delay_time;
vm_intr_monitor(ctx, hdr);
usleep(intr_monitor_setting.delay_duration); /* sleep-delay intr */
hdr->buffer[0] = 0; /* cancel to delay intr */
vm_intr_monitor(ctx, hdr);
sleep(TIME_TO_CHECK_AGAIN); /* time to get data again */
hdr->cmd = INTR_CMD_GET_DATA;
hdr->buf_cnt = MAX_PTDEV_NUM * 2;
memset(hdr->buffer, 0, sizeof(uint64_t) * hdr->buf_cnt);
vm_intr_monitor(ctx, hdr);
}
}
return NULL;
}
static void start_intr_storm_monitor(struct vmctx *ctx)
{
if (intr_monitor_setting.enable) {
int ret = pthread_create(&intr_storm_monitor_pid, NULL, intr_storm_monitor_thread, ctx);
if (ret) {
pr_err("failed %s %d\n", __func__, __LINE__);
intr_storm_monitor_pid = 0;
}
pthread_setname_np(intr_storm_monitor_pid, "storm_monitor");
pr_info("start monitor interrupt data...\n");
}
}
static void stop_intr_storm_monitor(void)
{
if (intr_storm_monitor_pid) {
void *ret;
pthread_cancel(intr_storm_monitor_pid);
pthread_join(intr_storm_monitor_pid, &ret);
intr_storm_monitor_pid = 0;
}
}
/*
.* interrupt monitor setting params, current interrupt mitigation will delay User VM's
.* pass-through devices' interrupt injection, the settings input from acrn-dm:
.* params:
.* threshold: each intr count/second when intr storm happens;
.* probe_period: seconds -- the period to probe intr data;
.* delay_time: ms -- the time to delay each intr injection;
* delay_duration; us -- the delay duration, after it, intr injection restore to normal
.*/
int acrn_parse_intr_monitor(const char *opt)
{
uint32_t threshold, period, delay, duration;
char *cp;
if((!dm_strtoui(opt, &cp, 10, &threshold) && *cp == ',') &&
(!dm_strtoui(cp + 1, &cp, 10, &period) && *cp == ',') &&
(!dm_strtoui(cp + 1, &cp, 10, &delay) && *cp == ',') &&
(!dm_strtoui(cp + 1, &cp, 10, &duration))) {
pr_dbg("interrupt storm monitor params: %d, %d, %d, %d\n", threshold, period, delay, duration);
} else {
pr_err("%s: not correct, it should be like: --intr_monitor 10000,10,1,100, please check!\n", opt);
return -1;
}
intr_monitor_setting.enable = true;
intr_monitor_setting.threshold = threshold * period;
intr_monitor_setting.probe_period = period;
intr_monitor_setting.delay_time = delay;
intr_monitor_setting.delay_duration = duration * 1000;
return 0;
}
struct vm_ops {
char name[16];
void *arg;
struct monitor_vm_ops *ops;
LIST_ENTRY(vm_ops) list;
};
static unsigned wakeup_reason = 0;
unsigned get_wakeup_reason(void)
{
return wakeup_reason;
}
int set_wakeup_timer(time_t t)
{
int acrnd_fd;
struct mngr_msg req;
struct mngr_msg ack;
int ret;
acrnd_fd = mngr_open_un("acrnd", MNGR_CLIENT);
if (acrnd_fd < 0) {
return -1;
}
req.magic = MNGR_MSG_MAGIC;
req.msgid = ACRND_TIMER;
req.timestamp = time(NULL);
req.data.rtc_timer.t = t;
strncpy(req.data.rtc_timer.vmname, vmname,
sizeof(req.data.rtc_timer.vmname));
memset(&ack, 0, sizeof(struct mngr_msg));
ret = mngr_send_msg(acrnd_fd, &req, &ack, 2);
mngr_close(acrnd_fd);
if (ret != sizeof(ack)) {
pr_err("%s %d\r\n", __func__, __LINE__);
return -1;
}
return ack.data.err;
}
static LIST_HEAD(vm_ops_list, vm_ops) vm_ops_head;
static pthread_mutex_t vm_ops_mtx = PTHREAD_MUTEX_INITIALIZER;
int monitor_register_vm_ops(struct monitor_vm_ops *mops, void *arg,
const char *name)
{
struct vm_ops *ops;
if (!mops) {
pr_err("%s %d\r\n", __func__, __LINE__);
return -1;
}
ops = calloc(1, sizeof(*ops));
if (!ops) {
pr_err("Alloc ops");
return -1;
}
if (name)
strncpy(ops->name, name, sizeof(ops->name) - 1);
ops->ops = mops;
ops->arg = arg;
pthread_mutex_lock(&vm_ops_mtx);
LIST_INSERT_HEAD(&vm_ops_head, ops, list);
pthread_mutex_unlock(&vm_ops_mtx);
return 0;
}
static int monitor_fd = -1;
/* handlers */
#define ACK_TIMEOUT 1
#define DEFINE_HANDLER(name, func) \
static void name(struct mngr_msg *msg, int client_fd, void *param) \
{ \
struct mngr_msg ack; \
struct vm_ops *ops; \
\
int ret = 0; \
int count = 0; \
\
ack.magic = MNGR_MSG_MAGIC; \
ack.msgid = msg->msgid; \
ack.timestamp = msg->timestamp; \
\
LIST_FOREACH(ops, &vm_ops_head, list) { \
if (ops->ops->func) { \
ret += ops->ops->func(ops->arg); \
count++; \
} \
} \
\
if (!count) { \
ack.data.err = -1; \
pr_err("No handler for id:%u\r\n", msg->msgid); \
} else \
ack.data.err = ret; \
\
mngr_send_msg(client_fd, &ack, NULL, ACK_TIMEOUT); \
}
DEFINE_HANDLER(handle_suspend, suspend);
static void handle_stop(struct mngr_msg *msg, int client_fd, void *param)
{
struct mngr_msg ack;
struct vm_ops *ops;
int ret = 0;
int count = 0;
ack.magic = MNGR_MSG_MAGIC;
ack.msgid = msg->msgid;
ack.timestamp = msg->timestamp;
if (msg->data.acrnd_stop.force && !is_rtvm) {
pr_info("%s: setting VM state to %s\n", __func__, vm_state_to_str(VM_SUSPEND_POWEROFF));
vm_set_suspend_mode(VM_SUSPEND_POWEROFF);
ack.data.err = 0;
} else {
LIST_FOREACH(ops, &vm_ops_head, list) {
if (ops->ops->stop) {
ret += ops->ops->stop(ops->arg);
count++;
}
}
if (!count) {
ack.data.err = -1;
pr_err("No handler for id:%u\r\n", msg->msgid);
} else
ack.data.err = ret;
}
mngr_send_msg(client_fd, &ack, NULL, ACK_TIMEOUT);
}
static void handle_resume(struct mngr_msg *msg, int client_fd, void *param)
{
struct mngr_msg ack;
struct vm_ops *ops;
int ret = 0;
int count = 0;
ack.magic = MNGR_MSG_MAGIC;
ack.msgid = msg->msgid;
ack.timestamp = msg->timestamp;
wakeup_reason = msg->data.reason;
LIST_FOREACH(ops, &vm_ops_head, list) {
if (ops->ops->resume) {
ret += ops->ops->resume(ops->arg);
count++;
}
}
if (!count) {
ack.data.err = -1;
pr_err("No handler for id:%u\r\n", msg->msgid);
} else
ack.data.err = ret;
mngr_send_msg(client_fd, &ack, NULL, ACK_TIMEOUT);
}
static void handle_query(struct mngr_msg *msg, int client_fd, void *param)
{
struct mngr_msg ack;
struct vm_ops *ops;
ack.magic = MNGR_MSG_MAGIC;
ack.msgid = msg->msgid;
ack.timestamp = msg->timestamp;
ack.data.state = -1;
LIST_FOREACH(ops, &vm_ops_head, list) {
if (ops->ops->query) {
ack.data.state = ops->ops->query(ops->arg);
break;
}
}
mngr_send_msg(client_fd, &ack, NULL, ACK_TIMEOUT);
}
static void handle_blkrescan(struct mngr_msg *msg, int client_fd, void *param)
{
struct mngr_msg ack;
struct vm_ops *ops;
int ret = 0;
int count = 0;
ack.magic = MNGR_MSG_MAGIC;
ack.msgid = msg->msgid;
ack.timestamp = msg->timestamp;
wakeup_reason = msg->data.reason;
LIST_FOREACH(ops, &vm_ops_head, list) {
if (ops->ops->rescan) {
ret += ops->ops->rescan(ops->arg, msg->data.devargs);
count++;
}
}
if (!count) {
ack.data.err = -1;
pr_err("No handler for id:%u\r\n", msg->msgid);
} else
ack.data.err = ret;
mngr_send_msg(client_fd, &ack, NULL, ACK_TIMEOUT);
}
static struct monitor_vm_ops pmc_ops = {
.stop = NULL,
.resume = vm_monitor_resume,
.suspend = NULL,
.pause = NULL,
.unpause = NULL,
.query = vm_monitor_query,
};
int monitor_init(struct vmctx *ctx)
{
int ret;
char path[128] = {};
ret = check_dir(ACRN_DM_BASE_PATH, CHK_CREAT);
if (ret) {
pr_err("%s %d\r\n", __func__, __LINE__);
goto dir_err;
}
ret = check_dir(ACRN_DM_SOCK_PATH, CHK_CREAT);
if (ret) {
pr_err("%s %d\r\n", __func__, __LINE__);
goto dir_err;
}
snprintf(path, sizeof(path) - 1, "%s.monitor", vmname);
monitor_fd = mngr_open_un(path, MNGR_SERVER);
if (monitor_fd < 0) {
pr_err("%s %d\r\n", __func__, __LINE__);
goto server_err;
}
ret = 0;
ret += mngr_add_handler(monitor_fd, DM_STOP, handle_stop, NULL);
ret += mngr_add_handler(monitor_fd, DM_SUSPEND, handle_suspend, NULL);
ret += mngr_add_handler(monitor_fd, DM_RESUME, handle_resume, NULL);
ret += mngr_add_handler(monitor_fd, DM_QUERY, handle_query, NULL);
ret += mngr_add_handler(monitor_fd, DM_BLKRESCAN, handle_blkrescan, NULL);
if (ret) {
pr_err("%s %d\r\n", __func__, __LINE__);
goto handlers_err;
}
monitor_register_vm_ops(&pmc_ops, ctx, "PMC_VM_OPs");
start_intr_storm_monitor(ctx);
return 0;
handlers_err:
mngr_close(monitor_fd);
monitor_fd = -1;
server_err:
dir_err:
return -1;
}
void monitor_close(void)
{
if (monitor_fd >= 0)
mngr_close(monitor_fd);
stop_intr_storm_monitor();
}