今天我们来讲讲dpvs的控制平面的代码
在dpvs和ipvsadm的通信中dpvs作为服务端,ipvsadm作为客户端,传送的媒介是套接字
一、套接字资源的初始化
在sockopt_init函数中对创建套接字,设置套接字属性,绑定套接字,在套接字上侦听数据
static inline int sockopt_init(void)
{
struct sockaddr_un srv_addr;
int srv_fd_flags = 0;
INIT_LIST_HEAD(&sockopt_list);
//进程间通信采用unix domain方式
memset(ipc_unix_domain, 0, sizeof(ipc_unix_domain));
strncpy(ipc_unix_domain, UNIX_DOMAIN_DEF, sizeof(ipc_unix_domain) - 1);
//创建套接字
srv_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (srv_fd < 0) {
RTE_LOG(ERR, MSGMGR, "%s: Fail to create server socket\n", __func__);
return EDPVS_IO;
}
//设置监听套接字的非阻塞属性
srv_fd_flags = fcntl(srv_fd, F_GETFL, 0);
srv_fd_flags |= O_NONBLOCK;
if (-1 == fcntl(srv_fd, F_SETFL, srv_fd_flags)) {
RTE_LOG(ERR, MSGMGR, "%s: Fail to set server socket NONBLOCK\n", __func__);
return EDPVS_IO;
}
memset(&srv_addr, 0, sizeof(struct sockaddr_un));
srv_addr.sun_family = AF_UNIX;
strncpy(srv_addr.sun_path, ipc_unix_domain, sizeof(srv_addr.sun_path) - 1);
unlink(ipc_unix_domain);
//绑定套接字到网络地址
if (-1 == bind(srv_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))) {
RTE_LOG(ERR, MSGMGR, "%s: Fail to bind server socket\n", __func__);
close(srv_fd);
unlink(ipc_unix_domain);
return EDPVS_IO;
}
//侦听套接字
if (-1 == listen(srv_fd, 1)) {
RTE_LOG(ERR, MSGMGR, "%s: Server socket listen failed\n", __func__);
close(srv_fd);
unlink(ipc_unix_domain);
return EDPVS_IO;
}
return EDPVS_OK;
}
二、接受连接,并收发数据
在ctrl.c文件的sockopt_ctl中调用accept接受tcp连接。
int sockopt_ctl(__rte_unused void *arg)
{
int clt_fd;
int ret;
socklen_t clt_len;
struct sockaddr_un clt_addr;
struct dpvs_sockopts *skopt;
struct dpvs_sock_msg *msg;
struct dpvs_sock_msg_reply reply_hdr;
void *reply_data = NULL;
size_t reply_data_len = 0;
memset(&clt_addr, 0, sizeof(struct sockaddr_un));
clt_len = sizeof(clt_addr);
/* Note: srv_fd is nonblock */
/*接受新连接*/
clt_fd = accept(srv_fd, (struct sockaddr*)&clt_addr, &clt_len);
if (clt_fd < 0) {
//非阻塞套接字,判断超时情况
if (EWOULDBLOCK != errno) {
RTE_LOG(WARNING, MSGMGR, "%s: Fail to accept client request\n", __func__);
}
return EDPVS_IO;
}
/* Note: clt_fd is block */
//阻塞方式接收客户端的消息,先接收消息头,再接收消息体
ret = sockopt_msg_recv(clt_fd, &msg);
if (unlikely(EDPVS_OK != ret)) {
close(clt_fd);
return ret;
}
skopt = sockopts_get(msg);
if (skopt) {
if (msg->type == SOCKOPT_GET)//SOCKOPT_GET消息类型,则调用get回调函数
ret = skopt->get(msg->id, msg->data, msg->len, &reply_data, &reply_data_len);
else if (msg->type == SOCKOPT_SET)SOCKOPT_SET消息类型,则调用set回调函数
ret = skopt->set(msg->id, msg->data, msg->len);
if (ret < 0) {
/* assume that reply_data is freed by user when callback fails */
reply_data = NULL;
reply_data_len = 0;
RTE_LOG(INFO, MSGMGR, "%s: socket msg<type=%s, id=%d> callback failed\n",
__func__, msg->type == SOCKOPT_GET ? "GET" : "SET", msg->id);
}
//响应头的初始化
memset(&reply_hdr, 0, sizeof(reply_hdr));
reply_hdr.version = SOCKOPT_VERSION;
reply_hdr.id = msg->id;
reply_hdr.type = msg->type;
reply_hdr.errcode = ret;//使用get和set函数的返回值,赋值给响应头的errcode
strncpy(reply_hdr.errstr, dpvs_strerror(ret), SOCKOPT_ERRSTR_LEN - 1);//出错信息
reply_hdr.len = reply_data_len;
/* send response */
//发送响应到对端
ret = sockopt_msg_send(clt_fd, &reply_hdr, reply_data, reply_data_len);
if (reply_data)
rte_free(reply_data);
if (EDPVS_OK != ret) {
sockopt_msg_free(msg);
close(clt_fd);
return ret;
}
}
sockopt_msg_free(msg);
close(clt_fd);
return EDPVS_OK;
}
流程是:
接收新连接 -> 接收消息 -> 判断消息类型,调用相应get/set回调函数 -> 发送响应对对端
我们来看下sockopts_get函数在什么?
static struct dpvs_sockopts* sockopts_get(struct dpvs_sock_msg *msg)
{
struct dpvs_sockopts *skopt;
if (unlikely(NULL == msg))
return NULL;
switch (msg->type) {
case SOCKOPT_GET:
list_for_each_entry(skopt, &sockopt_list, list) {
if (judge_id_betw(msg->id, skopt->get_opt_min, skopt->get_opt_max)) {
if (unlikely(skopt->version != msg->version)) {
RTE_LOG(WARNING, MSGMGR, "%s: socket msg version not match\n", __func__);
return NULL;
}
return skopt;
}
}
return NULL;
break;
case SOCKOPT_SET:
list_for_each_entry(skopt, &sockopt_list, list) {
if (judge_id_betw(msg->id, skopt->set_opt_min, skopt->set_opt_max)) {
if (unlikely(skopt->version != msg->version)) {
RTE_LOG(WARNING, MSGMGR, "%s: socket msg version not match\n", __func__);
return NULL;
}
return skopt;
}
}
return NULL;
break;
default:
RTE_LOG(WARNING, MSGMGR, "%s: unkown sock msg type: %d\n", __func__, msg->type);
}
return NULL;
}
使用list_for_each_entry遍历sockopt_list链表,
调用judge_id_betw函数判断msg->id的值处于skopt->set_opt_min和skopt->set_opt_max之间
调用judge_id_betw函数判断msg->id的值处于skopt->get_opt_min和skopt->get_opt_max之间
各位心里是不是有个疑问,sockopt_list链表中的元素是何时添加的呢?
啊哈,找到了,是sockopt_register函数
int sockopt_register(struct dpvs_sockopts *sockopts)
{
if (unlikely(NULL == sockopts)) {
RTE_LOG(WARNING, MSGMGR, "%s: invalid socket msg type\n", __func__);
return EDPVS_INVAL;
}
if (sockopts_exist(sockopts)) {
RTE_LOG(WARNING, MSGMGR, "%s: socket msg type already exist\n", __func__);
rte_exit(EXIT_FAILURE, "sockopt type already exist ->\n"
"\t\tget: %d - %d\n\t\tset: %d - %d\n",
sockopts->get_opt_min, sockopts->get_opt_max,
sockopts->set_opt_min, sockopts->set_opt_max);
return EDPVS_EXIST;
}
//将sockopts元素添加到sockopt_list链表中
list_add_tail(&sockopts->list, &sockopt_list);
return EDPVS_OK;
}
其中末尾的list_add_tail函数将sockopts元素添加到sockopt_list链表中。
在插入之前,先调用sockopt_exist函数判断下是否存在
如netif.c模块,此模块想接收ctrl控制面的控制指令,就需要调用sockopt_register函数进行注册
int netif_ctrl_init(void)
{
int err;
g_master_lcore_id = rte_get_master_lcore();
netif_get_slave_lcores(&g_slave_lcore_num, &g_slave_lcore_mask);
netif_get_isol_rx_lcores(&g_isol_rx_lcore_num, &g_isol_rx_lcore_mask);
if ((err = sockopt_register(&netif_sockopt)) != EDPVS_OK)
return err;
if ((err = lcore_stats_msg_init()) != EDPVS_OK)
return err;
return EDPVS_OK;
}
注册的dpvs_sockopts结构体如下
struct dpvs_sockopts netif_sockopt = {
.version = SOCKOPT_VERSION,
.get_opt_min = SOCKOPT_NETIF_GET_LCORE_MASK,
.get_opt_max = SOCKOPT_NETIF_GET_MAX,
.get = netif_sockopt_get,
.set_opt_min = SOCKOPT_NETIF_SET_LCORE,
.set_opt_max = SOCKOPT_NETIF_SET_MAX,
.set = netif_sockopt_set,
};
如果在程序中,你添加了新的控制语句,
要确保下msg id的范围是在设置的get_opt_min和get_opt_max之间
要确保下msg id的范围是在设置的set_opt_min和set_opt_max之间
再细说下dpvs_sockopts结构体中的get回调函数指针类型和set回调函数指针类型
struct dpvs_sockopts {
uint32_t version;
struct list_head list;
sockoptid_t set_opt_min;
sockoptid_t set_opt_max;
int (*set)(sockoptid_t opt, const void *in, size_t inlen);
sockoptid_t get_opt_min;
sockoptid_t get_opt_max;
int (*get)(sockoptid_t opt, const void *in, size_t inlen, void **out, size_t *outlen);
};
set函数指针类型指向的函数形参
in:输入缓冲区的指针
inlen:输入缓冲区的长度
get函数指针类型指向的函数形参
in:输入缓冲区的指针
inlen:输入缓冲区的长度
out:输出缓冲区的指针
outlen:输出缓冲区的长度
相信大家对这块已经掌握的可以了,每天进步一点点,心里更踏实。