深入浅出DPDK KNI核心技术

一、KNI

KNI全称:Kernel NIC Interface,内核网卡接口,允许用户态程序访问linux控制平面。

在DPDK报文处理中,有些报文需要发送到内核协议栈进行处理,如GTP-C控制报文

如果报文数量较少,可以使用内核提供的TAP/TUN设备,但是鉴于这种设备使用的系统调用的方式,还涉及到copy_to_user()和copy_from_user()的开销,因此,提供了KNI接口用于改善用户态和内核态间报文的处理效率。

二、使用KNI的优势

比 Linux TUN/TAP interfaces的操作快(通过取消系统调用copy_to_user()/copy_from_user())。

可使用Linux标准网络工具ethtool, ifconfig和tcpdump管理DPDK端口。

允许端口使用内核网络协议栈。

kni的功能也是分为用户态KNI和内核态KNI两部分的

用户态的KNI代码在lib\librte_kni目录下

内核态的KNI代码在kernel/linux/kni目录下

三、用户态KNI处理

3、1 加载kni内核模块

在加载kni模块时,可以设置它的内核线程模式

insmod kmod/rte_kni.ko kthread_mode=single
insmod kmod/rte_kni.ko kthread_mode=multiple

single模式(默认):只在内核侧创建一个内核线程,来接收所有kni设备上的数据包,一个线程 vs 所有kni设备

multiple模式:每个kni接口创建一个内核线程,用来接收数据包,一个线程 vs 一个kni设备

dpdk在加载kni模块时,默认是采用的single模式,同时还可以为此内核线程设置cpu亲和性

小伙伴们可能疑问,这里的single和multiple模式是什么意思

参考官网链接:https://doc.dpdk.org/guides/prog_guide/kernel_nic_interface.html

例子程序位于examples/kni/main.c文件

3、2 初始化流程

main(int argc, char** argv)
{
    /* eal初始化 */
    ret = rte_eal_init(argc, argv);
​
    /* 创建mbuf内存池*/
    pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", NB_MBUF,
        MEMPOOL_CACHE_SZ, 0, MBUF_DATA_SZ, rte_socket_id());
​
    /* 初始化KNI子系统*/
    init_kni();
​
    /* 初始化端口port,并调用kni_alloc */
    RTE_ETH_FOREACH_DEV(port) {
        init_port(port);//初始化端口
        kni_alloc(port);//kni申请资源
    }
​
    /* Launch per-lcore function on every lcore */
    // 每个lcore逻辑核心上运行一个函数main_loop,CALL_MASTER表示在master core上运行

    rte_eal_mp_remote_launch(main_loop, NULL, CALL_MASTER);
​
    /* 释放资源 */
    RTE_ETH_FOREACH_DEV(port) {
        kni_free_kni(port);
    }
    return 0;
}

下面重点分析下init_kni,kni_alloc,kni_free_kni,main_loop这四个函数。

3、2、1 init_kni函数

init_kni函数中仅仅调用rte_kni_init。

rte_kni_init(unsigned int max_kni_ifaces __rte_unused)
{
    /* Check FD and open */
    if (kni_fd < 0) {
        kni_fd = open("/dev/" KNI_DEVICE, O_RDWR);//打开/dev/kni设备
    }
​
    return 0;
}

DPDK在初始化阶段会调用rte_kni_init,打开kni设备。

3、2、2 kni_alloc函数

kni_alloc函数主要调用了rte_kni_alloc函数

问题1:rte_kni_alloc对应KNI内核态的什么代码呢?

答:kni_ioctl_create()函数,后面会分析

3、2、3 kni_free_kni函数

kni_free_kni主要是调用rte_kni_release函数

问题2:rte_kni_release对应KNI内核态的什么代码呢?

答:kni_ioctl_release()函数,后面会分析

3、2、4 主逻辑函数main_loop

定位到main_loop逻辑处理函数

static int
main_loop(__rte_unused void *arg)
{
    uint16_t i;
    int32_t f_stop;
    const unsigned lcore_id = rte_lcore_id();
    enum lcore_rxtx {
        LCORE_NONE,
        LCORE_RX,
        LCORE_TX,
        LCORE_MAX
    };
    enum lcore_rxtx flag = LCORE_NONE;
​
    //遍历设备列表,判断当前的lcore逻辑核心是负责rx还是tx
    RTE_ETH_FOREACH_DEV(i) {
        if (kni_port_params_array[i]->lcore_rx == (uint8_t)lcore_id) {
            flag = LCORE_RX;
            break;
        } else if (kni_port_params_array[i]->lcore_tx == lcore_id) {
            flag = LCORE_TX;
            break;
        }
    }
​
    //如果是接收数据,则循环调用kni_ingress,直到f_stop被设置为true跳出循环
    if (flag == LCORE_RX) {
        while (1) {
            kni_ingress(kni_port_params_array[i]);
        }
    } 
    //如果是发送数据,则循环调用kni_egress,直到f_stop被设置为true跳出循环
    else if (flag == LCORE_TX) {
        while (1) {
            kni_egress(kni_port_params_array[i]);
        }
    }
​
    return 0;
}

步骤如下:

获取配置,判断当前lcore是负责rx还是tx

如果lcore负责rx,则死循环调用接口kni_ingress进行报文的收取。

如果lcore负责tx,则死循环调用接口kni_egress进行报文

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值