Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3450597
  • 博文数量: 198
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 7256
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-23 18:56
个人简介

将晦涩难懂的技术讲的通俗易懂

文章分类

全部博文(198)

文章存档

2024年(1)

2023年(9)

2022年(4)

2021年(12)

2020年(8)

2019年(18)

2018年(19)

2017年(9)

2016年(26)

2015年(18)

2014年(54)

2013年(20)

分类: LINUX

2021-10-23 20:56:16

DPDK KNI原理和实现

——lvyilong316

kni的整体的整体实现原理是采用共享内存方式,如下图通过DPDK创建rx_qtx_q,并将其地址传递给kni内核模块,来实现rx_qtx_q的内存共享,之后DPDK和内核就可以将数据放入对应的共享内存队列中完成报文的收发。下面分别介绍kni的内核部分和用户态部分。

用户态部分

首先我们看用户态部分。用户态部分我们直接看DPDKkni example。其中关键初始化函数是main函数中调用的init_kni()。其中主要调用rte_kni_init

点击(此处)折叠或打开

  1. void
  2. rte_kni_init(unsigned int max_kni_ifaces)
  3. {
  4.     uint32_t i;
  5.     struct rte_kni_memzone_slot *it;
  6.     const struct rte_memzone *mz;
  7. #define OBJNAMSIZ 32
  8.     char obj_name[OBJNAMSIZ];
  9.     char mz_name[RTE_MEMZONE_NAMESIZE];

  10.     /* Immediately return if KNI is already initialized */
  11.     if (kni_memzone_pool.initialized) {
  12.         RTE_LOG(WARNING, KNI, "Double call to rte_kni_init()");
  13.         return;
  14.     }

  15.     if (max_kni_ifaces == 0) {
  16.         RTE_LOG(ERR, KNI, "Invalid number of max_kni_ifaces %d\n",
  17.                             max_kni_ifaces);
  18.         RTE_LOG(ERR, KNI, "Unable to initialize KNI\n");
  19.         return;
  20.     }
  21.     /*/dev/kni调用open */
  22.     /* Check FD and open */
  23.     if (kni_fd < 0) {
  24.         kni_fd = open("/dev/" KNI_DEVICE, O_RDWR);
  25.         if (kni_fd < 0) {
  26.             RTE_LOG(ERR, KNI,
  27.                 "Can not open /dev/%s\n", KNI_DEVICE);
  28.             return;
  29.         }
  30.     }

  31.     /* Allocate slot objects */
  32.     kni_memzone_pool.slots = (struct rte_kni_memzone_slot *)
  33.                     rte_malloc(NULL,
  34.                     sizeof(struct rte_kni_memzone_slot) *
  35.                     max_kni_ifaces,
  36.                     0);
  37.     KNI_MEM_CHECK(kni_memzone_pool.slots == NULL);

  38.     /* Initialize general pool variables */
  39.     kni_memzone_pool.initialized = 1;
  40.     kni_memzone_pool.max_ifaces = max_kni_ifaces;
  41.     kni_memzone_pool.free = &kni_memzone_pool.slots[0];
  42.     rte_spinlock_init(&kni_memzone_pool.mutex);
  43.     /* 根据kni设备数量初始化对应的kni slot,每个slot存放着kni需要使用的各种queue */
  44.     /* Pre-allocate all memzones of all the slots; panic on error */
  45.     for (i = 0; i < max_kni_ifaces; i++) {

  46.         /* Recover current slot */
  47.         it = &kni_memzone_pool.slots[i];
  48.         it->id = i;

  49.         /* Allocate KNI context */
  50.         snprintf(mz_name, RTE_MEMZONE_NAMESIZE, "KNI_INFO_%d", i);
  51.         mz = kni_memzone_reserve(mz_name, sizeof(struct rte_kni),
  52.                     SOCKET_ID_ANY, 0);
  53.         KNI_MEM_CHECK(mz == NULL);
  54.         it->m_ctx = mz;

  55.         /* TX RING */
  56.         snprintf(obj_name, OBJNAMSIZ, "kni_tx_%d", i);
  57.         mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE,
  58.                             SOCKET_ID_ANY, 0);
  59.         KNI_MEM_CHECK(mz == NULL);
  60.         it->m_tx_q = mz;

  61.         /* RX RING */
  62.         snprintf(obj_name, OBJNAMSIZ, "kni_rx_%d", i);
  63.         mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE,
  64.                             SOCKET_ID_ANY, 0);
  65.         KNI_MEM_CHECK(mz == NULL);
  66.         it->m_rx_q = mz;

  67.         /* ALLOC RING */
  68.         snprintf(obj_name, OBJNAMSIZ, "kni_alloc_%d", i);
  69.         mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE,
  70.                             SOCKET_ID_ANY, 0);
  71.         KNI_MEM_CHECK(mz == NULL);
  72.         it->m_alloc_q = mz;

  73.         /* FREE RING */
  74.         snprintf(obj_name, OBJNAMSIZ, "kni_free_%d", i);
  75.         mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE,
  76.                             SOCKET_ID_ANY, 0);
  77.         KNI_MEM_CHECK(mz == NULL);
  78.         it->m_free_q = mz;

  79.         /* Request RING */
  80.         snprintf(obj_name, OBJNAMSIZ, "kni_req_%d", i);
  81.         mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE,
  82.                             SOCKET_ID_ANY, 0);
  83.         KNI_MEM_CHECK(mz == NULL);
  84.         it->m_req_q = mz;

  85.         /* Response RING */
  86.         snprintf(obj_name, OBJNAMSIZ, "kni_resp_%d", i);
  87.         mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE,
  88.                             SOCKET_ID_ANY, 0);
  89.         KNI_MEM_CHECK(mz == NULL);
  90.         it->m_resp_q = mz;

  91.         /* Req/Resp sync mem area */
  92.         snprintf(obj_name, OBJNAMSIZ, "kni_sync_%d", i);
  93.         mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE,
  94.                             SOCKET_ID_ANY, 0);
  95.         KNI_MEM_CHECK(mz == NULL);
  96.         it->m_sync_addr = mz;

  97.         if ((i+1) == max_kni_ifaces) {
  98.             it->next = NULL;
  99.             kni_memzone_pool.free_tail = it;
  100.         } else
  101.             it->next = &kni_memzone_pool.slots[i+1];
  102.     }

  103.     return;

  104. kni_fail:
  105.     RTE_LOG(ERR, KNI, "Unable to allocate memory for max_kni_ifaces:%d."
  106.         "Increase the amount of hugepages memory\n", max_kni_ifaces);
  107. }

其核心就是根据kni设备数量初始化对应的 rte_kni_memzone_slot,其中 rte_kni_memzone_slot中主要保存着这个kni设备需要在数据传输中使用的各种queue,如tx_qrx_qfree_q等,每个queue对应一个DPDK memzone。初始化完如下图所示。

另外一个关键初始化函数是main函数中对每个kni port调用的kni_alloc,而kni_alloc中主要调用rte_kni_alloc。关于rte_kni_alloc的部分这里就不再展开,主要是分配一个可用的rte_kni_memzone_slot,并将其相关信息赋值给一个struct rte_kni_device_info结构,然后用struct rte_kni_device_info作为参数调用kni设备的ioctl create

ret = ioctl(kni_fd, RTE_KNI_IOCTL_CREATE, &dev_info);

这样就将DPDK用户态那些queue对应的内存和内核kni设备关联起来了。然后我们看用户态数据是如何发往内核的。DPDK侧的主要发送函数为rte_kni_tx_burst

点击(此处)折叠或打开

  1. unsigned
  2. rte_kni_tx_burst(struct rte_kni *kni, struct rte_mbuf **mbufs, unsigned num)
  3. {
  4.     void *phy_mbufs[num];
  5.     unsigned int ret;
  6.     unsigned int i;

  7.     for (i = 0; i < num; i++)
  8.         phy_mbufs[i] = va2pa(mbufs[i]);

  9.     ret = kni_fifo_put(kni->rx_q, phy_mbufs, num);

  10.     /* Get mbufs from free_q and then free them */
  11.     kni_free_mbufs(kni);

  12.     return ret;
  13. }

rte_kni_tx_burst主要调用 kni_fifo_putmbuf的物理地址写入fifo中。

点击(此处)折叠或打开

  1. static inline uint32_t
  2. kni_fifo_put(struct rte_kni_fifo *fifo, void **data, uint32_t num)
  3. {
  4.     uint32_t i = 0;
  5.     uint32_t fifo_write = fifo->write;
  6.     uint32_t fifo_read = fifo->read;
  7.     uint32_t new_write = fifo_write;

  8.     for (i = 0; i < num; i++) {
  9.         new_write = (new_write + 1) & (fifo->len - 1);

  10.         if (new_write == fifo_read)
  11.             break;
  12.         fifo->buffer[fifo_write] = data[i];
  13.         fifo_write = new_write;
  14.     }
  15.     fifo->write = fifo_write;

  16.     return i;
  17. }

    至于内核的接收部分的接收逻辑我们在后续内核部分讲。用户态的接收逻辑也类似,从对应的rx_q fifo中取出数据转换为mbuf,这里就不再展开了。

内核部分

KNIDPDK中带有的内核模块,在新版本的DPDKKNI已经从DPDK代码库中移除独立维护了。我们这里就以老版本的DPDK来分析,以DPDK17.11.2来说,KNI模块对应的代码路径为:dpdk-stable-17.11.2\lib\librte_eal\linuxapp\kni

其初始化函数为kni_init

点击(此处)折叠或打开

  1. static int __init
  2. kni_init(void)
  3. {
  4.     int rc;

  5.     if (kni_parse_kthread_mode() < 0) { //kni的线程模式、单线程还是多线程
  6.         pr_err("Invalid parameter for kthread_mode\n");
  7.         return -EINVAL;
  8.     }

  9.     if (multiple_kthread_on == 0)
  10.         pr_debug("Single kernel thread for all KNI devices\n");
  11.     else
  12.         pr_debug("Multiple kernel thread mode enabled\n");

  13. #ifdef HAVE_SIMPLIFIED_PERNET_OPERATIONS
  14.     rc = register_pernet_subsys(&kni_net_ops); //在每个namespace中执行kni_net_ops->init函数
  15. #else
  16.     rc = register_pernet_gen_subsys(&kni_net_id, &kni_net_ops);
  17. #endif
  18.     if (rc)
  19.         return -EPERM;

  20.     rc = misc_register(&kni_misc); //注册kni misc设备
  21.     if (rc != 0) {
  22.         pr_err("Misc registration failed\n");
  23.         goto out;
  24.     }

  25.     /* Configure the lo mode according to the input parameter */
  26.     kni_net_config_lo_mode(lo_mode);

  27.     return 0;

  28. out:
  29. #ifdef HAVE_SIMPLIFIED_PERNET_OPERATIONS
  30.     unregister_pernet_subsys(&kni_net_ops);
  31. #else
  32.     unregister_pernet_gen_subsys(kni_net_id, &kni_net_ops);
  33. #endif
  34.     return rc;
  35. }

代码比较简单,首先选择kni的线程模式,分为单线程还是多线程,所谓单线程是指所有的kni端口收发都由一个线程守护,多线程只是每一个kni端口分为由一个线程守护,这部分是在插入模块时带入参数选择。

接着调用register_pernet_subsys在每个namespace中调用kni_net_ops->init函数,也就是kni_init_netkni_init_net会在对应的namespace分配struct kni_net结构。

     最后调用注册函数misc_register,将kni注册为一个混杂设备,注册之后就会出现/dev/kni路径,用户态程序就可以对其进行open。其中kni_misc结构体里面定义了该混杂设备的一些操作

点击(此处)折叠或打开

  1. static struct miscdevice kni_misc = {
  2.     .minor = MISC_DYNAMIC_MINOR,
  3.     .name = KNI_DEVICE,
  4.     .fops = &kni_fops,
  5. };

这里主要看.fops里面的结构体

点击(此处)折叠或打开

  1. static const struct file_operations kni_fops = {
  2.     .owner = THIS_MODULE,
  3.     .open = kni_open,
  4.     .release = kni_release,
  5.     .unlocked_ioctl = (void *)kni_ioctl,
  6.     .compat_ioctl = (void *)kni_compat_ioctl,
  7. };

   其中任何用户态程序(如DPDK)要想内核kni发送数据前都需要调用这里的open函数,即kni_open

点击(此处)折叠或打开

  1. static int
  2. kni_open(struct inode *inode, struct file *file)
  3. {
  4.     struct net *net = current->nsproxy->net_ns;
  5.     struct kni_net *knet = net_generic(net, kni_net_id);

  6.     /* kni device can be opened by one user only per netns */
  7.     if (test_and_set_bit(KNI_DEV_IN_USE_BIT_NUM, &knet->device_in_use))
  8.         return -EBUSY;

  9.     file->private_data = get_net(net);
  10.     pr_debug("/dev/kni opened\n");

  11.     return 0;
  12. }

kni_open主要是完成内核file结构private_data的设置。内核另外一个关键初始化函数为kni_ioctl_create,这是由用户态程序调用ioctl create执行的。此外我们注意到knikni_fops没有read/write函数,所以其不能像tap设备一样使用读写文件的方式进行数据收发。下面重点看kni_ioctl_create,简化后其主要流程如下:

点击(此处)折叠或打开

  1. static int
  2. kni_ioctl_create(struct net *net, uint32_t ioctl_num,
  3.         unsigned long ioctl_param)
  4. {
  5.     struct kni_net *knet = net_generic(net, kni_net_id);
  6.     int ret;
  7.     struct rte_kni_device_info dev_info;
  8.     struct net_device *net_dev = NULL;
  9.     struct kni_dev *kni, *dev, *n;

  10.     /* Copy kni info from user space */
  11.     ret = copy_from_user(&dev_info, (void *)ioctl_param, sizeof(dev_info));
  12.     if (ret) {
  13.         pr_err("copy_from_user in kni_ioctl_create");
  14.         return -EIO;
  15.     }

  16.     list_for_each_entry_safe(dev, n, &knet->kni_list_head, list) {
  17.         if (kni_check_param(dev, &dev_info) < 0) {
  18.             up_read(&knet->kni_list_lock);
  19.             return -EINVAL;
  20.         }
  21.     }
  22.     up_read(&knet->kni_list_lock);

  23.     /* 分配struct net_device结构,并将struct kni_dev作为net_device的private,并使用入参初始化 */
  24.     net_dev = alloc_netdev(sizeof(struct kni_dev), dev_info.name,
  25.                             kni_net_init);
  26.     if (net_dev == NULL) {
  27.         pr_err("error allocating device \"%s\"\n", dev_info.name);
  28.         return -EBUSY;
  29.     }
  30.     
  31.     dev_net_set(net_dev, net);

  32.     kni = netdev_priv(net_dev);

  33.     kni->net_dev = net_dev;
  34.     kni->group_id = dev_info.group_id;
  35.     kni->core_id = dev_info.core_id;
  36.     strncpy(kni->name, dev_info.name, RTE_KNI_NAMESIZE);

  37.     /* Translate user space info into kernel space info */
  38.     kni->tx_q = phys_to_virt(dev_info.tx_phys);
  39.     kni->rx_q = phys_to_virt(dev_info.rx_phys);
  40.     kni->alloc_q = phys_to_virt(dev_info.alloc_phys);
  41.     kni->free_q = phys_to_virt(dev_info.free_phys);

  42.     kni->req_q = phys_to_virt(dev_info.req_phys);
  43.     kni->resp_q = phys_to_virt(dev_info.resp_phys);
  44.     kni->sync_va = dev_info.sync_va;
  45.     kni->sync_kva = phys_to_virt(dev_info.sync_phys);

  46.     kni->mbuf_size = dev_info.mbuf_size;

  47.     if (kni->lad_dev)
  48.         ether_addr_copy(net_dev->dev_addr, kni->lad_dev->dev_addr);
  49.     else
  50.         /*
  51.          * Generate random mac address. eth_random_addr() is the newer
  52.          * version of generating mac address in linux kernel.
  53.          */
  54.         random_ether_addr(net_dev->dev_addr);
  55.     /* 将net_device结构注册在系统 */
  56.     ret = register_netdev(net_dev);

  57.     /* 创建数据处理线程 */
  58.     ret = kni_run_thread(knet, kni, dev_info.force_bind);
  59.     if (ret != 0)
  60.         return ret;

  61.     down_write(&knet->kni_list_lock);
  62.     list_add(&kni->list, &knet->kni_list_head);
  63.     up_write(&knet->kni_list_lock);

  64.     return 0;
  65. }

分配struct net_device结构使用kni_net_init进行初始化,并将struct kni_dev作为net_deviceprivate,并使用入参初始化net_device,入参我们从DPDK用户态部分知道为struct rte_kni_device_info,而其又是存放kni设备对应的rte_kni_memzone_slot信息。所以这里就将用户态DPDKrte_kni_memzone_slot中存放的各种queue的内存地址传递给内核kni_dev设备,实现了queue区域的内存共享。如下图所示。

随后调用register_netdevkni对应的struct net_device结构结构注册给系统,因此kni设备我们也能使用ifconfig看到对应的网卡设备,可以使用tcpdump对其进行抓包。

最后kni_run_thread创建内存kni设备的收发线程。我们只看单线程情况,其线程执行函数为kni_thread_single

点击(此处)折叠或打开

  1. static int
  2. kni_thread_single(void *data)
  3. {
  4.     struct kni_net *knet = data;
  5.     int j;
  6.     struct kni_dev *dev;

  7.     while (!kthread_should_stop()) {
  8.         down_read(&knet->kni_list_lock);
  9.         for (j = 0; j < KNI_RX_LOOP_NUM; j++) {
  10.             list_for_each_entry(dev, &knet->kni_list_head, list) {
  11.                 kni_net_rx(dev);
  12.                 kni_net_poll_resp(dev);
  13.             }
  14.         }
  15.         up_read(&knet->kni_list_lock);
  16.     }

  17.     return 0;
  18. }

   其主要循环遍历所有kni设备,调用kni_net_rx。而kni_net_rx又进一步调用kni_net_rx_funckni_net_rx_func被初始化为kni_net_rx_normal

点击(此处)折叠或打开

  1. static void
  2. kni_net_rx_normal(struct kni_dev *kni)
  3. {
  4.     uint32_t ret;
  5.     uint32_t len;
  6.     uint32_t i, num_rx, num_fq;
  7.     struct rte_kni_mbuf *kva;
  8.     void *data_kva;
  9.     struct sk_buff *skb;
  10.     struct net_device *dev = kni->net_dev;

  11.     /* Get the number of free entries in free_q */
  12.     num_fq = kni_fifo_free_count(kni->free_q);
  13.     if (num_fq == 0) {
  14.         /* No room on the free_q, bail out */
  15.         return;
  16.     }

  17.     /* Calculate the number of entries to dequeue from rx_q */
  18.     num_rx = min_t(uint32_t, num_fq, MBUF_BURST_SZ);

  19.     /* Burst dequeue from rx_q */
  20.     num_rx = kni_fifo_get(kni->rx_q, kni->pa, num_rx);
  21.     if (num_rx == 0)
  22.         return;

  23.     /* Transfer received packets to netif */
  24.     for (i = 0; i < num_rx; i++) {
  25.         kva = pa2kva(kni->pa[i]);
  26.         len = kva->pkt_len;
  27.         data_kva = kva2data_kva(kva);
  28.         kni->va[i] = pa2va(kni->pa[i], kva);

  29.         skb = dev_alloc_skb(len + 2);
  30.         if (!skb) {
  31.             /* Update statistics */
  32.             kni->stats.rx_dropped++;
  33.             continue;
  34.         }

  35.         /* Align IP on 16B boundary */
  36.         skb_reserve(skb, 2);

  37.         if (kva->nb_segs == 1) {
  38.             memcpy(skb_put(skb, len), data_kva, len);
  39.         } else {
  40.             int nb_segs;
  41.             int kva_nb_segs = kva->nb_segs;

  42.             for (nb_segs = 0; nb_segs < kva_nb_segs; nb_segs++) {
  43.                 memcpy(skb_put(skb, kva->data_len),
  44.                     data_kva, kva->data_len);

  45.                 if (!kva->next)
  46.                     break;

  47.                 kva = pa2kva(va2pa(kva->next, kva));
  48.                 data_kva = kva2data_kva(kva);
  49.             }
  50.         }

  51.         skb->dev = dev;
  52.         skb->protocol = eth_type_trans(skb, dev);
  53.         skb->ip_summed = CHECKSUM_UNNECESSARY;

  54.         /* Call netif interface */
  55.         netif_rx_ni(skb);

  56.         /* Update statistics */
  57.         kni->stats.rx_bytes += len;
  58.         kni->stats.rx_packets++;
  59.     }

  60.     /* Burst enqueue mbufs into free_q */
  61.     ret = kni_fifo_put(kni->free_q, kni->va, num_rx);
  62.     if (ret != num_rx)
  63.         /* Failing should not happen */
  64.         pr_err("Fail to enqueue entries into free_q\n");
  65. }

   其主要就是从共享内存队列的fifo中取出报文复制到对应的skb中,并注入free buf最终调用netif_rx_ni(skb)进入协议栈

   如果内核态向DPDK侧发送数据呢?前面讲到分配struct net_device结构使用kni_net_init进行初始化,其中将net_devicenetdev_ops设置为kni_net_netdev_ops

dev->netdev_ops = &kni_net_netdev_ops;

ndo_start_xmit被设置为kni_net_tx,当我们从kni对应的net设备发送数据时最终就会调用到kni_net_tx

点击(此处)折叠或打开

  1. static const struct net_device_ops kni_net_netdev_ops = {
  2.     .ndo_open = kni_net_open,
  3.     .ndo_stop = kni_net_release,
  4.     .ndo_set_config = kni_net_config,
  5.     .ndo_start_xmit = kni_net_tx,
  6.     .ndo_change_mtu = kni_net_change_mtu,
  7.     .ndo_do_ioctl = kni_net_ioctl,
  8.     .ndo_set_rx_mode = kni_net_set_rx_mode,
  9.     .ndo_get_stats = kni_net_stats,
  10.     .ndo_tx_timeout = kni_net_tx_timeout,
  11.     .ndo_set_mac_address = kni_net_set_mac,
  12. #ifdef HAVE_CHANGE_CARRIER_CB
  13.     .ndo_change_carrier = kni_net_change_carrier,
  14. #endif
  15. };
  16. static int
  17. kni_net_tx(struct sk_buff *skb, struct net_device *dev)
  18. {
  19.     int len = 0;
  20.     uint32_t ret;
  21.     struct kni_dev *kni = netdev_priv(dev);
  22.     struct rte_kni_mbuf *pkt_kva = NULL;
  23.     void *pkt_pa = NULL;
  24.     void *pkt_va = NULL;

  25.     /**
  26.      * Check if it has at least one free entry in tx_q and
  27.      * one entry in alloc_q.
  28.      */
  29.     if (kni_fifo_free_count(kni->tx_q) == 0 ||
  30.             kni_fifo_count(kni->alloc_q) == 0) {
  31.         /**
  32.          * If no free entry in tx_q or no entry in alloc_q,
  33.          * drops skb and goes out.
  34.          */
  35.         goto drop;
  36.     }

  37.     /* dequeue a mbuf from alloc_q */
  38.     ret = kni_fifo_get(kni->alloc_q, &pkt_pa, 1);
  39.     if (likely(ret == 1)) {
  40.         void *data_kva;

  41.         pkt_kva = pa2kva(pkt_pa);
  42.         data_kva = kva2data_kva(pkt_kva);
  43.         pkt_va = pa2va(pkt_pa, pkt_kva);

  44.         len = skb->len;
  45.         memcpy(data_kva, skb->data, len);
  46.         if (unlikely(len < ETH_ZLEN)) {
  47.             memset(data_kva + len, 0, ETH_ZLEN - len);
  48.             len = ETH_ZLEN;
  49.         }
  50.         pkt_kva->pkt_len = len;
  51.         pkt_kva->data_len = len;

  52.         /* enqueue mbuf into tx_q */
  53.         ret = kni_fifo_put(kni->tx_q, &pkt_va, 1);
  54.         if (unlikely(ret != 1)) {
  55.             /* Failing should not happen */
  56.             pr_err("Fail to enqueue mbuf into tx_q\n");
  57.             goto drop;
  58.         }
  59.     } else {
  60.         /* Failing should not happen */
  61.         pr_err("Fail to dequeue mbuf from alloc_q\n");
  62.         goto drop;
  63.     }

  64.     /* Free skb and update statistics */
  65.     dev_kfree_skb(skb);
  66.     kni->stats.tx_bytes += len;
  67.     kni->stats.tx_packets++;

  68.     return NETDEV_TX_OK;

  69. drop:
  70.     /* Free skb and update statistics */
  71.     dev_kfree_skb(skb);
  72.     kni->stats.tx_dropped++;

  73.     return NETDEV_TX_OK;
  74. }

   其主要逻辑就是将skb报文放入tx_qfifo中。然后另一端DPDKfifo中取出报文,完成内核到用户态的交互。以上我们就分析了kni设备的的DPDK和内核交互原理。

阅读(4255) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~