mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-05-16 12:19:42 +00:00
Add user VM reboot command and related command handler in lifecycle manager to support user VM reboot. Libvirt will send user VM reboot command to lifecycle manager of service VM through socket, this command is forwarded to the specified user VM, user VM will execute reboot command to start reboot itself. v1-->v2: Update some interfaces name to make it reable: (1) enable_uart_channel_dev_resend --> start_uart_channel_dev_resend (2) enable_all_uart_channel_dev_resend --> start_all_uart_channel_dev_resend (3) disable_uart_channel_dev_resend --> stop_uart_channel_dev_resend (4) get_reboot_flag --> get_user_vm_reboot_flag Tracked-On: #5921 Signed-off-by: Xiangyang Wu <xiangyang.wu@intel.com> Acked-by: Wang, Yu1 <yu1.wang@intel.com>
358 lines
10 KiB
C
358 lines
10 KiB
C
/*
|
|
* Copyright (C)2021 Intel Corporation
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/queue.h>
|
|
#include <pthread.h>
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
#include "uart_channel.h"
|
|
#include "log.h"
|
|
#include "config.h"
|
|
#include "command.h"
|
|
|
|
#define SYNC_FMT "sync:%s"
|
|
|
|
static void parse_channel_dev_id(struct channel_dev *c_dev)
|
|
{
|
|
char *saveptr;
|
|
|
|
(void) strtok_r(c_dev->buf, ":", &saveptr);
|
|
if (strlen(saveptr) > 0) {
|
|
strncpy(c_dev->name, saveptr, CHANNEL_DEV_NAME_MAX - 1U);
|
|
LOG_PRINTF("Device fd:%d, VM name:%s\n",
|
|
get_uart_dev_fd(c_dev->uart_device), c_dev->name);
|
|
}
|
|
}
|
|
static void add_uart_channel_dev_connection_list(struct channel_dev *c_dev)
|
|
{
|
|
struct uart_channel *c = c_dev->channel;
|
|
|
|
pthread_mutex_lock(&c->tty_conn_list_lock);
|
|
LIST_INSERT_HEAD(&c->tty_conn_head, c_dev, list);
|
|
pthread_mutex_unlock(&c->tty_conn_list_lock);
|
|
}
|
|
/**
|
|
* @brief Wait to connect device in uart channel
|
|
*
|
|
* Wait sync message from slave channel device, parse slave channel device
|
|
* indentifier from sync message, then add channel device into uart channel
|
|
* device connection list.
|
|
*/
|
|
void *listen_uart_channel_dev(void *arg)
|
|
{
|
|
ssize_t num;
|
|
struct channel_dev *c_dev = (struct channel_dev *)arg;
|
|
|
|
LOG_PRINTF("Lifecycle manager in service VM fd=%d tty node=%s\n",
|
|
get_uart_dev_fd(c_dev->uart_device), get_uart_dev_path(c_dev->uart_device));
|
|
memset(c_dev->buf, 0, sizeof(c_dev->buf));
|
|
while (c_dev->listening) {
|
|
num = receive_message_by_uart(c_dev->uart_device, (void *)c_dev->buf,
|
|
sizeof(c_dev->buf));
|
|
if (num == 0) {
|
|
usleep(LISTEN_INTERVAL);
|
|
continue;
|
|
}
|
|
parse_channel_dev_id(c_dev);
|
|
if (strncmp(SYNC_CMD, c_dev->buf, sizeof(SYNC_CMD)) == 0) {
|
|
/** Add channel device instance into UART connection list */
|
|
add_uart_channel_dev_connection_list(c_dev);
|
|
|
|
c_dev->listening = false;
|
|
LOG_PRINTF("Receive sync message from user VM (%s), start to talk.\n",
|
|
c_dev->name);
|
|
usleep(2 * WAIT_RECV);
|
|
(void)send_message_by_uart(c_dev->uart_device, ACK_SYNC, strlen(ACK_SYNC));
|
|
sem_post(&c_dev->dev_sem);
|
|
}
|
|
}
|
|
|
|
LOG_PRINTF("Lifecycle manager stops to listen device:%s\n",
|
|
get_uart_dev_path(c_dev->uart_device));
|
|
return NULL;
|
|
}
|
|
/**
|
|
* @brief Wait to connect device in the uart channel
|
|
* and poll message
|
|
*
|
|
* Send sync message every 5 second and wait acked sync message,
|
|
* if acked sync message is received, add uart channel device instance
|
|
* into uart connection list. It acked sync message is not received, the lifecycle
|
|
* manager will exit.
|
|
*/
|
|
void *connect_uart_channel_dev(void *arg)
|
|
{
|
|
ssize_t ret;
|
|
struct channel_dev *c_dev = (struct channel_dev *)arg;
|
|
struct uart_channel *c = c_dev->channel;
|
|
char buf[CHANNEL_DEV_NAME_MAX + SYNC_LEN];
|
|
|
|
snprintf(buf, sizeof(buf), SYNC_FMT, c->conf.identifier);
|
|
/* TODO: will add SYNC resending */
|
|
LOG_PRINTF("Send sync command:%s identifier=%s\n", buf, c->conf.identifier);
|
|
ret = send_message_by_uart(c_dev->uart_device, (void *)buf, strlen(buf));
|
|
if (ret < 0) {
|
|
LOG_WRITE("Send sync command to service VM fail\n");
|
|
} else {
|
|
usleep(LISTEN_INTERVAL);
|
|
memset(c_dev->buf, 0, sizeof(c_dev->buf));
|
|
(void) receive_message_by_uart(c_dev->uart_device, (void *)c_dev->buf, sizeof(c_dev->buf));
|
|
if (strncmp(ACK_SYNC, c_dev->buf, sizeof(ACK_SYNC)) == 0) {
|
|
/** Add channel device instance into UART connection list */
|
|
add_uart_channel_dev_connection_list(c_dev);
|
|
LOG_WRITE("Lifecycle manager: connected\n");
|
|
} else {
|
|
ret = -1;
|
|
LOG_PRINTF("Device in the (%s): failed to connect\n", c->conf.identifier);
|
|
}
|
|
}
|
|
if (ret < 0)
|
|
c_dev->polling = false;
|
|
c_dev->listening = false;
|
|
sem_post(&c_dev->dev_sem);
|
|
return NULL;
|
|
}
|
|
|
|
void *poll_and_dispatch_uart_channel_events(void *arg)
|
|
{
|
|
struct channel_dev *c_dev = (struct channel_dev *)arg;
|
|
ssize_t num, ret;
|
|
struct uart_channel *c;
|
|
|
|
c = c_dev->channel;
|
|
sem_wait(&c_dev->dev_sem);
|
|
LOG_PRINTF("UART polling fd=%d...\n", get_uart_dev_fd(c_dev->uart_device));
|
|
while (c_dev->polling) {
|
|
memset(c_dev->buf, 0, sizeof(c_dev->buf));
|
|
num = receive_message_by_uart(c_dev->uart_device, (void *)c_dev->buf,
|
|
sizeof(c_dev->buf));
|
|
/**
|
|
* Resend message if resend_time is set.
|
|
*/
|
|
if (num == 0) {
|
|
if (c_dev->resend_time > 1) {
|
|
usleep(LISTEN_INTERVAL + SECOND_TO_US);
|
|
LOG_PRINTF("Resend (%s) to (%s)\n", c_dev->resend_buf, c_dev->name);
|
|
ret = send_message_by_uart(c_dev->uart_device, (void *)c_dev->resend_buf,
|
|
strlen(c_dev->resend_buf));
|
|
if (ret < 0)
|
|
LOG_WRITE("Send poweroff message to user VM fail\n");
|
|
c_dev->resend_time--;
|
|
} else if (c_dev->resend_time == 1) {
|
|
memcpy(c_dev->buf, ACK_TIMEOUT, strlen(ACK_TIMEOUT));
|
|
num = strlen(ACK_TIMEOUT);
|
|
} else {
|
|
/* No action if resend_time is 0 */
|
|
}
|
|
}
|
|
if (num > 0) {
|
|
parse_channel_dev_id(c_dev);
|
|
c->data_handler((const char *)c_dev->buf, get_uart_dev_fd(c_dev->uart_device));
|
|
}
|
|
}
|
|
LOG_PRINTF("Lifecycle manager stops to poll device:%s\n",
|
|
get_uart_dev_path(c_dev->uart_device));
|
|
return NULL;
|
|
}
|
|
struct channel_dev *find_uart_channel_dev(struct uart_channel *c, int fd)
|
|
{
|
|
struct channel_dev *c_dev = NULL;
|
|
|
|
LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
|
|
if (get_uart_dev_fd(c_dev->uart_device) == fd)
|
|
break;
|
|
}
|
|
return c_dev;
|
|
}
|
|
struct channel_dev *find_uart_channel_dev_by_name(struct uart_channel *c, char *name)
|
|
{
|
|
struct channel_dev *c_dev = NULL;
|
|
|
|
LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
|
|
if (strncmp(name, c_dev->name, sizeof(c_dev->name)) == 0)
|
|
break;
|
|
}
|
|
return c_dev;
|
|
}
|
|
/**
|
|
* @brief Set message polling loop flag as flase and remove channel device instance from
|
|
* the connection list
|
|
*
|
|
* @param c_dev point to uart channel device instance
|
|
* @param c point to uart channel instance
|
|
*/
|
|
void disconnect_uart_channel_dev(struct channel_dev *c_dev, struct uart_channel *c)
|
|
{
|
|
c_dev->listening = false;
|
|
c_dev->polling = false;
|
|
|
|
pthread_mutex_lock(&c->tty_conn_list_lock);
|
|
LIST_REMOVE(c_dev, list);
|
|
pthread_mutex_unlock(&c->tty_conn_list_lock);
|
|
}
|
|
/**
|
|
* @brief Traverse uart channel open list to set listening loop flag and polling loop flag
|
|
* to false for each channel device which is in listening state.
|
|
*
|
|
* @param c point to uart channel instance
|
|
*/
|
|
void stop_listen_uart_channel_dev(struct uart_channel *c)
|
|
{
|
|
struct channel_dev *c_dev;
|
|
|
|
LIST_FOREACH(c_dev, &c->tty_open_head, open_list) {
|
|
if (c_dev->listening) {
|
|
LOG_PRINTF("Stop to listen uart device (%s)\n",
|
|
get_uart_dev_path(c_dev->uart_device));
|
|
c_dev->listening = false;
|
|
c_dev->polling = false;
|
|
sem_post(&c_dev->dev_sem);
|
|
}
|
|
}
|
|
}
|
|
void start_uart_channel_dev_resend(struct channel_dev *c_dev, char *resend_buf, unsigned int resend_time)
|
|
{
|
|
if (resend_time < MIN_RESEND_TIME)
|
|
resend_time = MIN_RESEND_TIME;
|
|
strncpy(c_dev->resend_buf, resend_buf, CHANNEL_DEV_BUF_LEN - 1);
|
|
c_dev->resend_time = resend_time + 1;
|
|
}
|
|
void start_all_uart_channel_dev_resend(struct uart_channel *c, char *msg, unsigned int resend_time)
|
|
{
|
|
struct channel_dev *c_dev;
|
|
|
|
/* Enable resend for all connected uart channel devices */
|
|
pthread_mutex_lock(&c->tty_conn_list_lock);
|
|
LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
|
|
start_uart_channel_dev_resend(c_dev, msg, resend_time);
|
|
}
|
|
pthread_mutex_unlock(&c->tty_conn_list_lock);
|
|
}
|
|
void stop_uart_channel_dev_resend(struct channel_dev *c_dev)
|
|
{
|
|
if (c_dev->resend_time == 1U)
|
|
LOG_PRINTF("Timeout of receiving ACK message from (%s)\n", c_dev->name);
|
|
c_dev->resend_time = 0U;
|
|
memset(c_dev->resend_buf, 0x0, CHANNEL_DEV_BUF_LEN);
|
|
}
|
|
/**
|
|
* @brief Send message to each connected uart channel device
|
|
*
|
|
* @param c uart channel instance
|
|
* @param msg pointer which points to the message to be sent
|
|
*/
|
|
void notify_all_connected_uart_channel_dev(struct uart_channel *c, char *msg)
|
|
{
|
|
struct channel_dev *c_dev;
|
|
|
|
/* Send message to all tty connected devices*/
|
|
pthread_mutex_lock(&c->tty_conn_list_lock);
|
|
LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
|
|
LOG_PRINTF("Send (%s) to (%s)\n", msg, c_dev->name);
|
|
(void) send_message_by_uart(c_dev->uart_device, msg, strlen(msg));
|
|
}
|
|
pthread_mutex_unlock(&c->tty_conn_list_lock);
|
|
}
|
|
bool is_uart_channel_connection_list_empty(struct uart_channel *c)
|
|
{
|
|
bool ret = false;
|
|
|
|
pthread_mutex_lock(&c->tty_conn_list_lock);
|
|
if (LIST_EMPTY(&c->tty_conn_head))
|
|
ret = true;
|
|
pthread_mutex_unlock(&c->tty_conn_list_lock);
|
|
return ret;
|
|
}
|
|
struct channel_dev *create_uart_channel_dev(struct uart_channel *c, char *path, data_handler_f *fn)
|
|
{
|
|
struct uart_dev *dev;
|
|
struct channel_dev *c_dev;
|
|
|
|
if (c == NULL || path == NULL || fn == NULL)
|
|
return NULL;
|
|
c->data_handler = fn;
|
|
dev = init_uart_dev(path);
|
|
if (dev == NULL) {
|
|
LOG_PRINTF("Failed to initialize UART device %s\n", path);
|
|
return NULL;
|
|
}
|
|
|
|
c_dev = calloc(1, sizeof(*c_dev));
|
|
if (!c_dev) {
|
|
LOG_PRINTF("%s: Failed to allocate memory for UART channel device\n", __func__);
|
|
deinit_uart_dev(dev);
|
|
return NULL;
|
|
}
|
|
memset(c_dev, 0x0, sizeof(*c_dev));
|
|
c_dev->uart_device = dev;
|
|
c_dev->channel = c;
|
|
c_dev->listening = true;
|
|
c_dev->polling = true;
|
|
sem_init(&c_dev->dev_sem, 0, 0);
|
|
/** Add channel device instance into open list */
|
|
pthread_mutex_lock(&c->tty_conn_list_lock);
|
|
LIST_INSERT_HEAD(&c->tty_open_head, c_dev, open_list);
|
|
pthread_mutex_unlock(&c->tty_conn_list_lock);
|
|
return c_dev;
|
|
}
|
|
static void destroy_uart_channel_devs(struct uart_channel *c)
|
|
{
|
|
struct channel_dev *c_dev;
|
|
|
|
LIST_FOREACH(c_dev, &c->tty_open_head, open_list) {
|
|
pthread_mutex_lock(&c->tty_conn_list_lock);
|
|
LIST_REMOVE(c_dev, open_list);
|
|
pthread_mutex_unlock(&c->tty_conn_list_lock);
|
|
|
|
deinit_uart_dev(c_dev->uart_device);
|
|
free(c_dev);
|
|
}
|
|
}
|
|
void wait_uart_channel_devs_threads(struct uart_channel *c)
|
|
{
|
|
struct channel_dev *c_dev;
|
|
|
|
LIST_FOREACH(c_dev, &c->tty_open_head, open_list) {
|
|
pthread_join(c_dev->listen_thread, NULL);
|
|
pthread_join(c_dev->pool_thread, NULL);
|
|
}
|
|
}
|
|
struct uart_channel *init_uart_channel(char *id)
|
|
{
|
|
struct uart_channel *c;
|
|
|
|
if (id == NULL) {
|
|
LOG_PRINTF("%s:invlid parameter\n", __func__);
|
|
return NULL;
|
|
}
|
|
c = calloc(1, sizeof(*c));
|
|
if (!c) {
|
|
LOG_PRINTF("%s: Failed to allocate memory for UART channel\n", __func__);
|
|
return NULL;
|
|
}
|
|
c->data_handler = NULL;
|
|
LIST_INIT(&c->tty_conn_head);
|
|
LIST_INIT(&c->tty_open_head);
|
|
pthread_mutex_init(&c->tty_conn_list_lock, NULL);
|
|
memcpy(c->conf.identifier, id, strlen(id));
|
|
return c;
|
|
}
|
|
void deinit_uart_channel(struct uart_channel *c)
|
|
{
|
|
if (c != NULL) {
|
|
destroy_uart_channel_devs(c);
|
|
free(c);
|
|
}
|
|
}
|