misc: life_mngr: add uart channel module

In the uart module, the following functions are implemented:
- init_uart_channel
Initialize each field of uart channel instance.
- create_uart_channel_dev
Create one channel device instance to store information about
one uart channel device which will be opened.
- wait_uart_channel_devs_threads
Wait uart channel devices threads to exit
- destroy_uart_channel
Destroy uart channel and release channel device instance
- listen_uart_channel_dev
Wait to connect device in uart channel
- connect_uart_channel_dev
Send sync message every 5 second
- poll_and_dispatch_uart_channel_events
Poll and dispatch message received from uart channel
- find_uart_channel_dev
Find uart channel device instance according to fd
- find_uart_channel_dev_by_name
Find uart channel device instance according to device name
- disconnect_uart_channel_dev
Disconnect uart channel device instance
- stop_listen_uart_channel_dev
Stop to listen uart channel device
- enable_uart_channel_dev_resend
Set the uart channel device resending buffer and
resending time
- enable_all_uart_channel_dev_resend
Enable resend for all connected uart channel devices
- disable_uart_channel_dev_resend
Clear the uart channel device resending buffer and
resending time
- notify_all_connected_uart_channel_dev
Send message to each connected uart channel device
- check_uart_channel_connection_list_empty
Check whether uart channel connection list is empty or not

TODO:
	Will refine resending logic in the
	poll_and_dispatch_uart_channel_events, and add
	SYNC command resending.

v3-->v4:
	Initialize each field of uart channel instance.
	Add one flag indicating master channel device.
	Update interface names.
v4-->v5:
	Refine interface name.
v5-->v6:
	Move uart channel operations from command handler module
v6-->v7:
	Add enable_uart_channel_dev_resend and
	disable_uart_channel_dev_resend;
	Fix bugs about receiving message timeout.
v7-->v8:
	Add enable_all_uart_channel_dev_resend interface.
v8-->v9:
	Update error handling.
	Use strlen to calculate the length of string, this will be
	the parameter of send_message_by_uart.

Tracked-On: #6652

Signed-off-by: Xiangyang Wu <xiangyang.wu@intel.com>
Reviewed-by: fei1.li@intel.com
This commit is contained in:
Xiangyang Wu 2021-10-19 10:11:21 +08:00 committed by wenlingz
parent 591998e956
commit baf7982547
2 changed files with 503 additions and 0 deletions

View File

@ -0,0 +1,357 @@
/*
* 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 + SCECOND_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 enable_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 enable_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) {
enable_uart_channel_dev_resend(c_dev, msg, resend_time);
}
pthread_mutex_unlock(&c->tty_conn_list_lock);
}
void disable_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);
}
}

View File

@ -0,0 +1,146 @@
/*
* Copyright (C)2021 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _UART_CHANNEL_H_
#define _UART_CHANNEL_H_
#include <sys/queue.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/un.h>
#include "uart.h"
#define WAIT_USER_VM_POWEROFF (10*SCECOND_TO_US)
#define CHANNEL_DEV_NAME_MAX 128U
#define CHANNEL_DEV_BUF_LEN 256U
#define MIN_RESEND_TIME 3U
#define LISTEN_INTERVAL (5 * SCECOND_TO_US)
typedef void data_handler_f(const char *cmd_name, int fd);
struct channel_dev {
struct uart_dev *uart_device;
char name[CHANNEL_DEV_NAME_MAX]; /**< channel device name */
bool listening; /**< listen thread loop flag */
bool polling; /**< message polling thread loop flag */
pthread_t listen_thread;
pthread_t pool_thread;
char buf[CHANNEL_DEV_BUF_LEN]; /**< store received message */
LIST_ENTRY(channel_dev) list; /**< list node used in UART connection list */
LIST_ENTRY(channel_dev) open_list; /**< list node used UART opening list */
struct uart_channel *channel; /**< point to UART server */
sem_t dev_sem; /**< semaphore used to start polling message */
char resend_buf[CHANNEL_DEV_BUF_LEN]; /**< store the message that will be sent */
unsigned int resend_time; /**< the time which the message will be resent */
};
struct channel_config {
char identifier[CHANNEL_DEV_NAME_MAX]; /**< the user VM name which is configured by user */
};
struct uart_channel {
data_handler_f *data_handler;
LIST_HEAD(tty_head, channel_dev) tty_conn_head; /* UART connection list */
LIST_HEAD(tty_open_head, channel_dev) tty_open_head; /* UART opening list */
pthread_mutex_t tty_conn_list_lock;
struct channel_config conf;
};
/**
* @brief Initialize each field of uart channel instance, such as
* a lock and configuration of uart channel
*/
struct uart_channel *init_uart_channel(char *id);
/**
* @brief Create one uart channel device according to device name
*
* Create one channel device instance to store information about
* one uart channel device which will be opened.
* For master channel, create two threads, one thread
* is to listen and wait sync messaage from slave channel, another thread
* is to poll message from slave channel.
* For slave channel, create one thread to send sync message
* to master channel every 5 second until acked sync
* message is received from master channel and poll meessage from master channel.
*
* @param uart point to uart server
* @param path start address of the name of the device which will
* be opened
* @param fn the handler of handling message
*/
struct channel_dev *create_uart_channel_dev(struct uart_channel *c, char *path, data_handler_f *fn);
/**
* @brief Wait uart channel devices threads to exit
*/
void wait_uart_channel_devs_threads(struct uart_channel *c);
/**
* @brief Destroy uart channel and release channel device instance
*/
void deinit_uart_channel(struct uart_channel *c);
/**
* @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);
/**
* @brief Wait to connect device in the uart channel
*
* Send sync message every 5 second and wait acked sync message from master
* channel device, add uart channel device instance into uart connection list.
*/
void *connect_uart_channel_dev(void *arg);
/**
* @brief Poll and dispatch message received from uart channel
*
* If resend time is set, this interface will resend message unit the ACK message
* is received.
*/
void *poll_and_dispatch_uart_channel_events(void *arg);
/**
* @brief Find uart channel device instance according to fd
*/
struct channel_dev *find_uart_channel_dev(struct uart_channel *c, int fd);
/**
* @brief Find uart channel device instance according to device name
*/
struct channel_dev *find_uart_channel_dev_by_name(struct uart_channel *c, char *name);
/**
* @brief Disconnect uart channel device instance
*/
void disconnect_uart_channel_dev(struct channel_dev *c_dev, struct uart_channel *c);
/**
* @brief Stop to listen uart channel device
*/
void stop_listen_uart_channel_dev(struct uart_channel *c);
/**
* @brief Set the uart channel device resending buffer and resending time
*
* If ACK message is not received during specified time, resend
* message.
*/
void enable_uart_channel_dev_resend(struct channel_dev *c_dev, char *resend_buf, unsigned int resend_time);
/**
* @brief Enable resend for all connected uart channel devices
*/
void enable_all_uart_channel_dev_resend(struct uart_channel *c, char *msg, unsigned int resend_time);
/**
* @brief Clear the uart channel device resending buffer and resending time
*/
void disable_uart_channel_dev_resend(struct channel_dev *c_dev);
/**
* @brief Broadcast message to each connected uart channel device
*/
void notify_all_connected_uart_channel_dev(struct uart_channel *c, char *msg);
/**
* @brief Check whether uart channel connection list is empty or not
*/
bool is_uart_channel_connection_list_empty(struct uart_channel *c);
#endif