a68570b5d9
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>
441 lines
13 KiB
C
441 lines
13 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 2023. Huawei Technologies Co., Ltd. All rights reserved.
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/socket.h>
|
|
|
|
#include "conn.h"
|
|
#include "qtfs-server.h"
|
|
#include "comm.h"
|
|
#include "log.h"
|
|
#include "req.h"
|
|
#include "symbol_wrapper.h"
|
|
|
|
#define QTFS_EPOLL_TIMEO 1000 // unit ms
|
|
#define QTFS_EPOLL_RETRY_INTVL 500 // unit ms
|
|
|
|
int qtfs_server_thread_run = 1;
|
|
DEFINE_RWLOCK(g_userp_rwlock);
|
|
struct qtserver_fd_bitmap qtfs_fd_bitmap;
|
|
|
|
long qtfs_server_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
|
|
|
struct file_operations qtfs_misc_fops = {
|
|
.owner=THIS_MODULE,
|
|
.unlocked_ioctl = qtfs_server_misc_ioctl,
|
|
};
|
|
|
|
struct qtfs_server_epoll_s qtfs_epoll = {
|
|
.epfd = -1,
|
|
.event_nums = 0,
|
|
.events = NULL,
|
|
.kevents = NULL,
|
|
};
|
|
rwlock_t qtfs_epoll_rwlock;
|
|
|
|
void qtfs_server_epoll_exit(void)
|
|
{
|
|
write_lock(&qtfs_epoll_rwlock);
|
|
if (qtfs_epoll.kevents == NULL) {
|
|
write_unlock(&qtfs_epoll_rwlock);
|
|
return;
|
|
}
|
|
kfree(qtfs_epoll.kevents);
|
|
qtfs_epoll.kevents = NULL;
|
|
write_unlock(&qtfs_epoll_rwlock);
|
|
return;
|
|
}
|
|
|
|
long qtfs_server_epoll_thread(struct qtfs_conn_var_s *pvar)
|
|
{
|
|
int n;
|
|
struct qtreq_epollevt *req;
|
|
struct qtrsp_epollevt *rsp;
|
|
struct qtreq *head;
|
|
int sendlen;
|
|
int ret = 0;
|
|
int i = 0;
|
|
|
|
if (qtfs_epoll.epfd == -1) {
|
|
qtfs_err("qtfs epoll wait error, epfd is invalid.");
|
|
return QTERROR;
|
|
}
|
|
if (false == pvar->conn_ops->conn_connected(&pvar->conn_var)) {
|
|
qtfs_warn("qtfs epoll thread disconnected, now try to reconnect.");
|
|
ret = qtfs_sm_reconnect(pvar);
|
|
} else {
|
|
ret = qtfs_sm_active(pvar);
|
|
}
|
|
if (ret != QTOK) {
|
|
qtfs_err("qtfs epoll thread connect state error, can't work.");
|
|
msleep(QTFS_EPOLL_RETRY_INTVL);
|
|
return QTERROR;
|
|
}
|
|
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
|
|
rsp = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_RECV);
|
|
head = pvar->vec_send.iov_base;
|
|
do {
|
|
n = qtfs_syscall_epoll_wait(qtfs_epoll.epfd, qtfs_epoll.events, qtfs_epoll.event_nums, 0);
|
|
if (n == 0) {
|
|
msleep(1);
|
|
break;
|
|
}
|
|
if (n < 0 || n > QTFS_MAX_EPEVENTS_NUM) {
|
|
msleep(QTFS_EPOLL_RETRY_INTVL);
|
|
qtfs_err("epoll get new events number failed:%d ", n);
|
|
break;
|
|
}
|
|
qtfs_info(">>epoll get new events number:%d.", n);
|
|
if (copy_from_user(qtfs_epoll.kevents, qtfs_epoll.events, sizeof(struct epoll_event) * n)) {
|
|
qtfs_err("qtfs copy epoll events failed, events lost.");
|
|
WARN_ON(1);
|
|
break;
|
|
}
|
|
for (i = 0; i < n; i++) {
|
|
req->events[i].data = qtfs_epoll.kevents[i].data;
|
|
req->events[i].events = qtfs_epoll.kevents[i].events;
|
|
qtfs_info("epoll thread head req:%lx.", (unsigned long)req);
|
|
}
|
|
req->event_nums = n;
|
|
sendlen = sizeof(struct qtreq_epollevt) - sizeof(req->events) + n * sizeof(struct qtreq_epoll_event);
|
|
if (sendlen > QTFS_REQ_MAX_LEN) {
|
|
qtfs_err("qtfs epoll events size(%d) larger than qtfs message size.", sendlen);
|
|
WARN_ON(1);
|
|
break;
|
|
}
|
|
pvar->vec_send.iov_len = QTFS_MSG_HEAD_LEN + sendlen;
|
|
head->len = sendlen;
|
|
head->type = QTFS_REQ_EPOLL_EVENT;
|
|
ret = qtfs_conn_send(pvar);
|
|
qtfs_info("qtfs send msg conn: sendlen:%lu ret:%d.",
|
|
(unsigned long)pvar->vec_send.iov_len, ret);
|
|
if (ret == -EPIPE) {
|
|
qtfs_err("epoll wait send events failed get EPIPE, just wait new connection.");
|
|
qtfs_sm_reconnect(pvar);
|
|
break;
|
|
}
|
|
if (ret < 0) {
|
|
qtfs_err("epoll wait send events failed, ret:%d.", ret);
|
|
WARN_ON(1);
|
|
}
|
|
retry:
|
|
ret = qtfs_conn_recv_block(pvar);
|
|
if (ret == -EAGAIN) {
|
|
if (qtfs_server_thread_run == 0) {
|
|
qtfs_warn("qtfs module exiting, goodbye!");
|
|
return QTEXIT;
|
|
}
|
|
goto retry;
|
|
}
|
|
if (ret == -EPIPE) {
|
|
qtfs_err("epoll recv events failed get EPIPE, just wait new connection.");
|
|
qtfs_sm_reconnect(pvar);
|
|
break;
|
|
}
|
|
}while (0);
|
|
|
|
return (ret < 0) ? QTERROR : QTOK;
|
|
}
|
|
|
|
long qtfs_server_epoll_init(void)
|
|
{
|
|
struct qtfs_conn_var_s *pvar = NULL;
|
|
struct qtreq_epollevt *req;
|
|
|
|
pvar = qtfs_epoll_establish_conn();
|
|
if (pvar == NULL) {
|
|
return QTERROR;
|
|
}
|
|
qtfs_epoll_var = pvar;
|
|
|
|
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
|
|
qtfs_info("qtfs epoll events req size:%lu, events size:%lu, struct:%lu.",
|
|
sizeof(struct qtreq_epollevt), sizeof(req->events),
|
|
sizeof(struct qtreq_epoll_event));
|
|
qtfs_info("qtfs epoll wait thread, epfd:%d nums:%d.",
|
|
qtfs_epoll.epfd, qtfs_epoll.event_nums);
|
|
|
|
return QTOK;
|
|
}
|
|
|
|
int qtfs_server_fd_bitmap_init(void)
|
|
{
|
|
struct rlimit fd_rlim;
|
|
if (qtfs_fd_bitmap.bitmap != NULL) {
|
|
qtfs_info("free old bitmap");
|
|
kfree(qtfs_fd_bitmap.bitmap);
|
|
qtfs_fd_bitmap.bitmap = NULL;
|
|
qtfs_fd_bitmap.nbits = 0;
|
|
}
|
|
// fd_rlim is get from current task struct, can be trusted
|
|
fd_rlim = current->signal->rlim[RLIMIT_NOFILE];
|
|
qtfs_info("task rlimit cur:%lu max:%lu", fd_rlim.rlim_cur, fd_rlim.rlim_max);
|
|
qtfs_fd_bitmap.bitmap = (unsigned long *)kmalloc(BITS_TO_BYTES(fd_rlim.rlim_cur), GFP_KERNEL);
|
|
if (qtfs_fd_bitmap.bitmap == NULL) {
|
|
qtfs_err("kmalloc len:%lu failed.", BITS_TO_BYTES(fd_rlim.rlim_cur));
|
|
return -1;
|
|
}
|
|
qtfs_fd_bitmap.nbits = fd_rlim.rlim_cur;
|
|
bitmap_zero(qtfs_fd_bitmap.bitmap, qtfs_fd_bitmap.nbits);
|
|
return 0;
|
|
}
|
|
|
|
long qtfs_server_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int i;
|
|
long ret = 0;
|
|
struct qtfs_conn_var_s *pvar;
|
|
struct qtfs_thread_init_s init_userp;
|
|
switch (cmd) {
|
|
case QTFS_IOCTL_THREAD_INIT:
|
|
pvar = qtfs_conn_get_param_errcode();
|
|
if (IS_ERR_OR_NULL(pvar)) {
|
|
qtfs_err("init pvar get failed, pvar:%ld", (long)pvar);
|
|
if (PTR_ERR(pvar) == -EADDRINUSE)
|
|
return EADDRINUSE;
|
|
} else {
|
|
qtfs_conn_put_param(pvar);
|
|
}
|
|
if (!write_trylock(&g_userp_rwlock)) {
|
|
qtfs_err("try lock userps failed.");
|
|
return QTERROR;
|
|
}
|
|
if (qtfs_server_fd_bitmap_init() < 0) {
|
|
qtfs_err("fd bitmap init failed.");
|
|
write_unlock(&g_userp_rwlock);
|
|
return QTERROR;
|
|
}
|
|
if (copy_from_user(&init_userp, (void __user *)arg, sizeof(struct qtfs_thread_init_s))) {
|
|
qtfs_err("qtfs ioctl thread init copy from user failed.");
|
|
write_unlock(&g_userp_rwlock);
|
|
return QTERROR;
|
|
}
|
|
if (qtfs_userps == NULL || init_userp.thread_nums > QTFS_MAX_THREADS || init_userp.thread_nums == 0) {
|
|
qtfs_err("qtfs ioctl thread init userps invalid thread nums:%d.", init_userp.thread_nums);
|
|
write_unlock(&g_userp_rwlock);
|
|
return QTERROR;
|
|
}
|
|
memset(qtfs_userps, 0, QTFS_MAX_THREADS * sizeof(struct qtfs_server_userp_s));
|
|
if (init_userp.thread_nums > QTFS_MAX_THREADS) {
|
|
qtfs_err("qtfs ioctl thread init invalid input thread_num:%d", init_userp.thread_nums);
|
|
write_unlock(&g_userp_rwlock);
|
|
return QTERROR;
|
|
}
|
|
if (copy_from_user(qtfs_userps, (void __user *)init_userp.userp,
|
|
init_userp.thread_nums * sizeof(struct qtfs_server_userp_s))) {
|
|
qtfs_err("qtfs ioctl thread init copy from userp failed.");
|
|
write_unlock(&g_userp_rwlock);
|
|
return QTERROR;
|
|
}
|
|
for (i = 0; i < init_userp.thread_nums; i++) {
|
|
if (qtfs_userps[i].size > QTFS_USERP_MAXSIZE ||
|
|
!access_ok(qtfs_userps[i].userp, qtfs_userps[i].size) ||
|
|
!access_ok(qtfs_userps[i].userp2, qtfs_userps[i].size)) {
|
|
qtfs_err("userp set failed");
|
|
ret = QTERROR;
|
|
write_unlock(&g_userp_rwlock);
|
|
break;
|
|
}
|
|
qtfs_info("userp set idx:%d size:%lu", i, qtfs_userps[i].size);
|
|
}
|
|
write_unlock(&g_userp_rwlock);
|
|
break;
|
|
case QTFS_IOCTL_THREAD_RUN:
|
|
pvar = qtfs_conn_get_param();
|
|
if (pvar == NULL)
|
|
return QTERROR;
|
|
ret = qtfs_conn_server_run(pvar);
|
|
if (ret == QTEXIT) {
|
|
qtfs_warn("qtfs thread idx:%d exit.", pvar->cur_threadidx);
|
|
qtfs_sm_exit(pvar);
|
|
qtinfo_cntdec(QTINF_ACTIV_CONN);
|
|
}
|
|
qtfs_conn_put_param(pvar);
|
|
break;
|
|
case QTFS_IOCTL_EPFDSET:
|
|
write_lock(&qtfs_epoll_rwlock);
|
|
if (qtfs_epoll.kevents != NULL) {
|
|
kfree(qtfs_epoll.kevents);
|
|
qtfs_epoll.kevents = NULL;
|
|
}
|
|
if (copy_from_user(&qtfs_epoll, (void __user *)arg, sizeof(struct qtfs_server_epoll_s))) {
|
|
qtfs_err("copy epoll struct from arg failed.");
|
|
ret = QTERROR;
|
|
write_unlock(&qtfs_epoll_rwlock);
|
|
break;
|
|
}
|
|
if (qtfs_epoll.event_nums > QTFS_MAX_EPEVENTS_NUM || qtfs_epoll.event_nums == 0) {
|
|
qtfs_err("epoll arg set failed, event nums:%d too big", qtfs_epoll.event_nums);
|
|
ret = QTERROR;
|
|
write_unlock(&qtfs_epoll_rwlock);
|
|
break;
|
|
}
|
|
if (qtfs_epoll.epfd < 3) {
|
|
qtfs_err("epoll epfd set failed, epfd:%d should be greater than 2", qtfs_epoll.epfd);
|
|
ret = QTERROR;
|
|
write_unlock(&qtfs_epoll_rwlock);
|
|
break;
|
|
}
|
|
if (!access_ok(qtfs_epoll.events, qtfs_epoll.event_nums * sizeof(struct epoll_event))) {
|
|
qtfs_err("epoll events set failed, check pointer of qtfs_epoll.events failed");
|
|
ret = QTERROR;
|
|
write_unlock(&qtfs_epoll_rwlock);
|
|
break;
|
|
}
|
|
qtfs_info("epoll arg set, epfd:%d event nums:%d events.",
|
|
qtfs_epoll.epfd, qtfs_epoll.event_nums);
|
|
qtfs_epoll.kevents = (struct epoll_event *)kmalloc(sizeof(struct epoll_event) *
|
|
qtfs_epoll.event_nums, GFP_KERNEL);
|
|
if (qtfs_epoll.kevents == NULL) {
|
|
qtfs_err("epoll kernel events kmalloc failed.");
|
|
ret = QTERROR;
|
|
write_unlock(&qtfs_epoll_rwlock);
|
|
break;
|
|
}
|
|
write_unlock(&qtfs_epoll_rwlock);
|
|
break;
|
|
case QTFS_IOCTL_EPOLL_THREAD_INIT:
|
|
write_lock(&qtfs_epoll_rwlock);
|
|
ret = qtfs_server_epoll_init();
|
|
write_unlock(&qtfs_epoll_rwlock);
|
|
break;
|
|
case QTFS_IOCTL_EPOLL_THREAD_RUN:
|
|
write_lock(&qtfs_epoll_rwlock);
|
|
if (qtfs_epoll_var == NULL) {
|
|
qtfs_err("qtfs epoll thread run failed, var is invalid.");
|
|
ret = QTERROR;
|
|
write_unlock(&qtfs_epoll_rwlock);
|
|
break;
|
|
}
|
|
ret = qtfs_server_epoll_thread(qtfs_epoll_var);
|
|
write_unlock(&qtfs_epoll_rwlock);
|
|
if (ret == QTEXIT) {
|
|
qtfs_info("qtfs epoll thread exit.");
|
|
qtfs_epoll_cut_conn(qtfs_epoll_var);
|
|
}
|
|
break;
|
|
case QTFS_IOCTL_EXIT:
|
|
if (arg != 0 && arg != 1) {
|
|
qtfs_err("qtfs exit input invalid, should be 0 or 1.");
|
|
ret = QTERROR;
|
|
break;
|
|
}
|
|
qtfs_whitelist_clearall();
|
|
qtfs_info("qtfs server threads run set to:%lu.", arg);
|
|
qtfs_server_thread_run = arg;
|
|
break;
|
|
case QTFS_IOCTL_ALLINFO:
|
|
case QTFS_IOCTL_CLEARALL:
|
|
case QTFS_IOCTL_LOGLEVEL:
|
|
case QTFS_IOCTL_WL_ADD:
|
|
case QTFS_IOCTL_WL_DEL:
|
|
case QTFS_IOCTL_WL_GET:
|
|
ret = qtfs_misc_ioctl(file, cmd, arg);
|
|
break;
|
|
default:
|
|
qtfs_err("qtfs misc ioctl unknown cmd:%u.", cmd);
|
|
ret = QTERROR;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int __init qtfs_server_init(void)
|
|
{
|
|
qtfs_log_init(qtfs_log_level, sizeof(qtfs_log_level));
|
|
if (qtfs_kallsyms_hack_init() != 0)
|
|
return -1;
|
|
rwlock_init(&qtfs_epoll_rwlock);
|
|
qtfs_whitelist_initset();
|
|
|
|
qtfs_diag_info = (struct qtinfo *)kmalloc(sizeof(struct qtinfo), GFP_KERNEL);
|
|
if (qtfs_diag_info == NULL) {
|
|
qtfs_err("kmalloc qtfs diag info failed.");
|
|
return -1;
|
|
}
|
|
memset(qtfs_diag_info, 0, sizeof(struct qtinfo));
|
|
qtfs_userps = (struct qtfs_server_userp_s *)kmalloc(
|
|
QTFS_MAX_THREADS * sizeof(struct qtfs_server_userp_s), GFP_KERNEL);
|
|
if (qtfs_userps == NULL) {
|
|
qtfs_err("kmalloc qtfs userps failed, nums:%d", QTFS_MAX_THREADS);
|
|
kfree(qtfs_diag_info);
|
|
return -1;
|
|
}
|
|
memset(qtfs_userps, 0, QTFS_MAX_THREADS * sizeof(struct qtfs_server_userp_s));
|
|
qtfs_fd_bitmap.bitmap = NULL;
|
|
qtfs_fd_bitmap.nbits = 0;
|
|
qtfs_conn_param_init();
|
|
if (qtfs_syscall_replace_start()) {
|
|
qtfs_err("qtfs syscall replace failed.");
|
|
goto err_syscall;
|
|
}
|
|
if (qtfs_misc_register()) {
|
|
qtfs_err("qtfs misc device register failed.");
|
|
goto err_misc;
|
|
}
|
|
return 0;
|
|
err_misc:
|
|
qtfs_syscall_replace_stop();
|
|
err_syscall:
|
|
kfree(qtfs_userps);
|
|
kfree(qtfs_diag_info);
|
|
return -1;
|
|
}
|
|
|
|
static void __exit qtfs_server_exit(void)
|
|
{
|
|
qtfs_mod_exiting = true;
|
|
qtfs_server_thread_run = 0;
|
|
|
|
qtfs_conn_param_fini();
|
|
|
|
if (qtfs_epoll_var != NULL) {
|
|
qtfs_epoll_cut_conn(qtfs_epoll_var);
|
|
qtfs_conn_fini(qtfs_epoll_var);
|
|
qtfs_epoll_var->conn_ops->conn_var_fini(qtfs_epoll_var);
|
|
kfree(qtfs_epoll_var);
|
|
qtfs_epoll_var = NULL;
|
|
}
|
|
if (qtfs_diag_info != NULL) {
|
|
kfree(qtfs_diag_info);
|
|
qtfs_diag_info = NULL;
|
|
}
|
|
write_lock(&g_userp_rwlock);
|
|
if (qtfs_userps != NULL) {
|
|
kfree(qtfs_userps);
|
|
qtfs_userps = NULL;
|
|
}
|
|
if (qtfs_fd_bitmap.bitmap != NULL) {
|
|
kfree(qtfs_fd_bitmap.bitmap);
|
|
qtfs_fd_bitmap.bitmap = NULL;
|
|
qtfs_fd_bitmap.nbits = 0;
|
|
}
|
|
write_unlock(&g_userp_rwlock);
|
|
qtfs_whitelist_exit();
|
|
qtfs_server_epoll_exit();
|
|
|
|
qtfs_misc_destroy();
|
|
qtfs_syscall_replace_stop();
|
|
qtfs_info("qtfs server exit done.\n");
|
|
return;
|
|
}
|
|
|
|
module_init(qtfs_server_init);
|
|
module_exit(qtfs_server_exit);
|
|
MODULE_AUTHOR("liqiang64@huawei.com");
|
|
MODULE_LICENSE("GPL");
|