computing-offload/qtfs/qtfs_common/user_engine.c
Yikun Jiang a68570b5d9 Add computing offloading code
1. Add computing offloading code
2. Add script.md
3. Add virsh_demo.xml

Change-Id: Id9ef883e2f0eb727eb5448b9d1c47767f46b1021
Signed-off-by: Yikun Jiang <yikunkero@gmail.com>
2023-10-23 19:29:57 +08:00

522 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/******************************************************************************
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
* qtfs licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v2 for more details.
* Author: Liqiang
* Create: 2023-03-20
* Description:
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <glib.h>
#include <malloc.h>
#include <errno.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <sys/file.h>
#include <sys/epoll.h>
#include <linux/vm_sockets.h>
#include "comm.h"
#include "ipc/uds_main.h"
char wl_type_str[QTFS_WHITELIST_MAX][16] = {
"Open",
"Write",
"Read",
"Readdir",
"Mkdir",
"Rmdir",
"Create",
"Unlink",
"Rename",
"Setattr",
"Setxattr",
"Mount",
"Kill",
"Udsconnect"
};
#define engine_out(info, ...) \
do {\
printf("[Engine::%s:%3d]"info"\n", __func__, __LINE__, ##__VA_ARGS__);\
} while (0);
#define engine_out2(info, ...) \
do {\
printf("[Engine::%s:%3d]"info, __func__, __LINE__, ##__VA_ARGS__);\
} while (0);
#define engine_err(info, ...) \
do {\
printf("[ERROR:Engine::%s:%3d]"info"\n", __func__, __LINE__, ##__VA_ARGS__);\
} while (0);
#define WHITELIST_FILE "/etc/qtfs/whitelist"
struct engine_arg {
int psize;
int fd;
int thread_idx;
};
#define QTFS_USERP_SIZE QTFS_USERP_MAXSIZE
#define QTFS_SERVER_FILE "/dev/qtfs_server"
#define ENGINE_LOCK_ADDR "/var/run/qtfs/engine.lock"
#define ENGINE_LOCK_FILE_DIR "/var/run/qtfs/"
static volatile sig_atomic_t sig_int_flag = 0;
static int engine_env_prepare()
{
DIR *dir;
if (access(ENGINE_LOCK_ADDR, 0) == 0)
return 0;
if ((dir = opendir(ENGINE_LOCK_FILE_DIR)) == NULL) {
if (mkdir(ENGINE_LOCK_FILE_DIR, 0600) < 0) {
engine_err("mkdir %s failed.", ENGINE_LOCK_FILE_DIR);
return -1;
}
} else {
closedir(dir);
}
return 0;
}
int engine_socket_lock(void)
{
int lock_fd = open(ENGINE_LOCK_ADDR, O_RDONLY | O_CREAT, 0600);
if (lock_fd == -1)
return -EINVAL;
return flock(lock_fd, LOCK_EX | LOCK_NB);
}
int qtfs_fd;
int engine_run = 1;
static void qtfs_engine_userp_free(struct qtfs_server_userp_s *userp, int thread_nums)
{
for (int i = 0; i < thread_nums; i++) {
if (userp[i].userp != NULL)
free(userp[i].userp);
if (userp[i].userp2 != NULL)
free(userp[i].userp2);
}
free(userp);
return;
}
static struct qtfs_server_userp_s *qtfs_engine_thread_init(int fd, int thread_nums, int psize)
{
struct qtfs_server_userp_s *userp;
userp = (struct qtfs_server_userp_s *)calloc(thread_nums, sizeof(struct qtfs_server_userp_s));
if (userp == NULL) {
engine_out("engine thread init malloc failed.");
return NULL;
}
for (int i = 0; i < thread_nums; i++) {
userp[i].size = psize;
userp[i].userp = (void *)memalign(sysconf(_SC_PAGESIZE), psize);
if (userp[i].userp == NULL) {
engine_out("engine userp malloc failed.");
goto rollback;
}
userp[i].userp2 = (void *)memalign(sysconf(_SC_PAGESIZE), psize);
if (userp[i].userp2 == NULL) {
engine_out("engine userp2 malloc failed.");
goto rollback;
}
}
struct qtfs_thread_init_s init_userp;
int ret;
init_userp.thread_nums = thread_nums;
init_userp.userp = userp;
ret = ioctl(fd, QTFS_IOCTL_THREAD_INIT, (unsigned long)&init_userp);
if (ret != QTOK) {
engine_err("Engine thread init failed reason:%s",
(ret == EADDRINUSE) ? "Address already in use" : "userp init failed.");
goto rollback;
}
return userp;
rollback:
qtfs_engine_userp_free(userp, thread_nums);
return NULL;
}
static void *qtfs_engine_kthread(void *arg)
{
struct engine_arg *parg = (struct engine_arg *)arg;
int psize = parg->psize;
long ret;
while (engine_run) {
ret = ioctl(parg->fd, QTFS_IOCTL_THREAD_RUN, 0);
if (ret == QTEXIT) {
engine_out("qtfs server thread:%d exit.", parg->thread_idx);
break;
}
if (sig_int_flag == 1) {
engine_out("qtfs engine recv SIGINT.");
if (qtfs_fd < 0) {
engine_err("qtfs engine signal int file:%s open failed, fd:%d.", QTFS_SERVER_FILE, qtfs_fd);
continue;
}
ret = ioctl(qtfs_fd, QTFS_IOCTL_EXIT, 0);
engine_out("qtfs engine send QTFS_IOCTL_EXIT to kernel, get return value:%ld.", ret);
engine_run = 0;
break;
}
if (ret != QTOK) {
usleep(1000);
}
}
end:
engine_out("qtfs user engine over.");
return NULL;
}
static void qtfs_signal_int(int signum)
{
sig_int_flag = 1;
return;
}
static void *qtfs_engine_epoll_thread(void *data)
{
struct engine_arg *arg = (struct engine_arg *)data;
int fd = arg->fd;
long ret;
engine_out("qtfs server epoll thread run.");
do {
ret = ioctl(fd, QTFS_IOCTL_EPOLL_THREAD_INIT, NULL);
if (ret == QTEXIT) {
engine_out("qtfs server epoll thread init exit.");
goto end;
}
} while (ret != 0 && engine_run);
while (engine_run) {
ret = ioctl(fd, QTFS_IOCTL_EPOLL_THREAD_RUN, NULL);
if (ret == QTEXIT) {
engine_out("qtfs server epoll thread exit.");
break;
}
if (ret != QTOK) {
usleep(1000);
}
}
end:
engine_out("qtfs server epoll thread exit, ret:%ld.", ret);
return NULL;
}
int qtfs_epoll_init(int fd)
{
int epfd = epoll_create1(0);
if (epfd < 0) {
engine_err("epoll create error, ret:%d.", epfd);
return -1;
}
struct qtfs_server_epoll_s ep;
struct epoll_event *evts;
evts = calloc(QTFS_MAX_EPEVENTS_NUM, sizeof(struct epoll_event));
if (evts == NULL) {
engine_err("calloc events failed.");
close(epfd);
return -1;
}
engine_out("qtfs engine set epoll arg, fd:%d event nums:%d.", epfd, QTFS_MAX_EPEVENTS_NUM);
ep.epfd = epfd;
ep.event_nums = QTFS_MAX_EPEVENTS_NUM;
ep.events = evts;
int ret = ioctl(fd, QTFS_IOCTL_EPFDSET, &ep);
if (ret != 0) {
engine_err("failed to set epoll fd, ret:%d.", ret);
close(epfd);
return -1;
}
return epfd;
}
static void qtfs_whitelist_free_items(char **items, gsize count)
{
for (gsize j = 0; j < count; j++) {
if (items[j])
free(items[j]);
}
if (items)
free(items);
return;
}
static int qtfs_whitelist_transfer(int fd, GKeyFile *config, int type)
{
gsize i, wl_count;
int ret;
char **items;
struct qtfs_wl_item head;
items = g_key_file_get_string_list(config, wl_type_str[type], "Path", &wl_count, NULL);
if (wl_count <= 0) {
engine_err("Can't find whitelist item %s", wl_type_str[type]);
return -1;
}
if (wl_count > QTFS_WL_MAX_NUM)
wl_count = QTFS_WL_MAX_NUM;
head.type = type;
head.index = 0; // not use in add
for(i = 0; i < wl_count; i++){
head.len = strlen(items[i]);
if (head.len >= QTFS_PATH_MAX) {
engine_err("Whitelist type:%s invalid path:%s is too long(> %d)", wl_type_str[type], items[i], QTFS_PATH_MAX - 1);
continue;
}
head.path = items[i];
ret = ioctl(fd, QTFS_IOCTL_WL_ADD, &head);
if (ret == QTERROR) {
engine_err("Failed to add whitelist:%s type:%d, engine start failed.", items[i], type);
goto end;
}
}
engine_out("Successed to add white list items type:%s count:%d", wl_type_str[type], wl_count);
qtfs_whitelist_free_items(items, wl_count);
return 0;
end:
qtfs_whitelist_free_items(items, wl_count);
return -1;
}
int qtfs_whitelist_init(int fd)
{
int mount_whitelist = 0;
int ret, i;
GKeyFile *config = g_key_file_new();
g_key_file_load_from_file(config, WHITELIST_FILE, G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, NULL);
for (i = 0; i < QTFS_WHITELIST_MAX; i++) {
ret = qtfs_whitelist_transfer(fd, config, i);
if (ret != 0) {
engine_err("failed to set whitelist item %s, get error:%d", wl_type_str[i], ret);
// failure of one whitelist type should not stop others.
continue;
}
if (i == QTFS_WHITELIST_MOUNT)
mount_whitelist = 1;
}
g_key_file_free(config);
// if wl file not exist or mount not set, result is mount_whitelist == 0, stop the engine
if (mount_whitelist == 0) {
engine_err("Please create whitelist file and add white list items.");
engine_err("At least one [Mount] whitelist is required for the qtfs to work normally.");
return -1;
}
return 0;
}
#ifdef UDS_TEST_MODE
static int qtfs_engine_check_port(unsigned short port, char *ip)
#else
static int qtfs_engine_check_port(unsigned short port, char *scid)
#endif
{
#ifdef UDS_TEST_MODE
struct sockaddr_in sin;
if (inet_pton(AF_INET, ip, &sin.sin_addr) != 1) {
engine_err("%s inet_pton error.", ip);
return -1;
}
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
engine_err("socket error, fd:%d", sockfd);
return -1;
}
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) {
engine_err("ip:%s port:%u bind failed, errno:%d.", ip, port, errno);
close(sockfd);
return -1;
}
close(sockfd);
#else
#define DEC 10
long cid = strtol(scid, NULL, DEC); // base 10
if (errno == ERANGE) {
engine_err("The cid value out of range\n");
return -1;
}
if (cid != -1) {
if (cid < VMADDR_CID_HOST || cid > 0xFFFFFFFF) {
engine_err("The cid value[%ld] was invalid\n", cid);
return -1;
}
}
struct sockaddr_vm saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.svm_family = AF_VSOCK;
saddr.svm_port = port;
saddr.svm_cid = cid;
int sock_fd = socket(saddr.svm_family, SOCK_STREAM, 0);
if (bind(sock_fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
engine_err("cid:%ld port:%u bind failed, errno:%d.", cid, port, errno);
close(sock_fd);
return -1;
}
close(sock_fd);
#endif
return 0;
}
#define IS_NUMBER(c) (c >= '0' && c <= '9')
static int qtfs_engine_env_check(char *argv[])
{
struct qtinfo diag;
int ret;
int fd;
for (int i = 0; i < strlen(argv[1]); i ++) {
if (!IS_NUMBER(argv[1][i])) {
engine_err("invalid thread number :%s", argv[1]);
return -1;
}
}
if (qtfs_engine_check_port(atoi(argv[4]), argv[3]) < 0)
goto err;
return 0;
err:
return -1;
}
#define QTFS_ENGINE_FD_LIMIT 65536
static void engine_rlimit()
{
struct rlimit lim;
getrlimit(RLIMIT_NOFILE, &lim);
if (lim.rlim_cur >= QTFS_ENGINE_FD_LIMIT)
return;
engine_out("engine fd cur limit:%d, change to:%d", lim.rlim_cur, QTFS_ENGINE_FD_LIMIT);
lim.rlim_cur = QTFS_ENGINE_FD_LIMIT;
setrlimit(RLIMIT_NOFILE, &lim);
return;
}
int main(int argc, char *argv[])
{
int ret = 0;
if (argc != 7) {
engine_out("Usage: %s <number of threads> <uds proxy thread num> <host ip> <uds proxy port> <dpu ip> <uds proxy port>.", argv[0]);
engine_out(" Example: %s 16 1 192.168.10.10 12121 192.168.10.11 12121.", argv[0]);
return -1;
}
if (engine_env_prepare() != 0 || engine_socket_lock() < 0) {
engine_err("Engine is running.");
return -1;
}
if (qtfs_engine_env_check(argv) < 0) {
engine_err("Environment check failed, engine exit.");
return -1;
}
engine_rlimit();
int thread_nums = atoi(argv[1]);
int fd = open(QTFS_SERVER_FILE, O_RDONLY);
if (fd < 0) {
engine_err("qtfs server file:%s open failed, fd:%d.", QTFS_SERVER_FILE, fd);
return -1;
}
qtfs_fd = fd;
// init epoll
int epfd = qtfs_epoll_init(fd);
if (epfd < 0) {
close(fd);
return -1;
}
ret = ioctl(fd, QTFS_IOCTL_EXIT, 1);
if (ret == QTERROR) {
goto end;
}
ret = qtfs_whitelist_init(fd);
if (ret)
goto end;
umask(0);
pthread_t texec[QTFS_MAX_THREADS];
pthread_t tepoll;
if (thread_nums < 0 || thread_nums > QTFS_MAX_THREADS) {
engine_err("qtfs engine parm invalid, thread_nums:%d(must <= %d).",
thread_nums, QTFS_MAX_THREADS);
ret = -1;
goto end;
}
signal(SIGINT, qtfs_signal_int);
signal(SIGTERM, qtfs_signal_int);
struct qtfs_server_userp_s *userp = qtfs_engine_thread_init(fd, thread_nums, QTFS_USERP_SIZE);
if (userp == NULL) {
engine_out("qtfs engine userp init failed.");
ret = -1;
goto end;
}
struct engine_arg arg[QTFS_MAX_THREADS];
for (int i = 0; i < thread_nums; i++) {
arg[i].psize = QTFS_USERP_SIZE;
arg[i].fd = fd;
arg[i].thread_idx = i;
(void)pthread_create(&texec[i], NULL, qtfs_engine_kthread, &arg[i]);
}
(void)pthread_create(&tepoll, NULL, qtfs_engine_epoll_thread, &arg[0]);
// 必须放在这个位置uds main里面最终也有join
if (uds_proxy_main(6, &argv[1]) != 0) {
engine_out("uds proxy start failed.");
ret = -1;
goto engine_free;
}
for (int i = 0; i < thread_nums; i++) {
pthread_join(texec[i], NULL);
engine_out("qtfs engine join thread %d.", i);
}
pthread_join(tepoll, NULL);
engine_free:
qtfs_engine_userp_free(userp, thread_nums);
engine_out("qtfs engine join epoll thread.");
end:
(void)ioctl(fd, QTFS_IOCTL_EXIT, 0);
close(epfd);
close(fd);
engine_out("qtfs engine over.");
return ret;
}