前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VPP支持网卡热插拔了...

VPP支持网卡热插拔了...

作者头像
dpdk-vpp源码解读
发布2023-11-17 20:45:29
2330
发布2023-11-17 20:45:29
举报
文章被收录于专栏:DPDK VPP源码分析DPDK VPP源码分析

VPP的DPDK插件默认是不支持动态加载网卡的,需要在启动前在配置文件startup.conf设置网卡的PCI编号。也就是不支持网卡的热插拔动作。网卡热插拔主要解决高可靠性持续不间断运行的问题。在某些特殊的应用场合,如服务器、数据中心等,可能需要不关闭系统电源的情况下更换网卡。

最近社区提交了一个patch支持dpdk网卡热插拔功能,下面就来环境上实验一下:

此提交尚未合入主线,需要手动打补丁,提交链接: https://gerrit.fd.io/r/c/vpp/+/39121

下面是实验环境上存在2个virto-pci网卡,其中00:02.0已经作为管理口在用,我们可以将网卡0000:00:06.0动态加载到vpp上。

代码语言:javascript
复制
root@jinsh:~/workspace/vpp/build-root/install-vpp-native/external/bin# ./dpdk-devbind.py -s

Network devices using kernel driver
===================================
0000:00:02.0 'Virtio network device 1000' if=ens2 drv=virtio-pci unused=vfio-pci,uio_pci_generic *Active*
0000:00:06.0 'Virtio network device 1000' if=ens6 drv=virtio-pci unused=vfio-pci,uio_pci_generic 

vppctl命令登录vpp命令行视图下,执行“create interface dpdk”命令完成网卡动态加载。

代码语言:javascript
复制
#1、加载网卡。需要注意的PCI 编译需要写全格式,否则无法识别。
vpp# create interface dpdk 00:00:06.0 num-rx-desc 1024 num-tx-desc 1024
GigabitEthernet0/6/0
#2、接口设置接口up,并设置dhcp client模式。
vpp# set interface state GigabitEthernet0/6/0 up
vpp# set dhcp client intfc GigabitEthernet0/6/0 
#3、查询接口已获取到ip地址,ping网关也正常。
vpp# show interface address
GigabitEthernet0/6/0 (up):
  L3 192.168.100.133/24
local0 (dn):
vpp# ping 192.168.100.1
116 bytes from 192.168.100.1: icmp_seq=1 ttl=64 time=.1632 ms
116 bytes from 192.168.100.1: icmp_seq=2 ttl=64 time=.0990 ms
116 bytes from 192.168.100.1: icmp_seq=3 ttl=64 time=.0898 ms
116 bytes from 192.168.100.1: icmp_seq=4 ttl=64 time=.0911 ms
Aborted due to a keypress.

Statistics: 4 sent, 4 received, 0% packet loss
代码语言:javascript
复制
代码语言:javascript
复制
执行“delete interface dpdk”可以将网卡从vpp中卸载掉。
代码语言:javascript
复制
代码语言:javascript
复制
vpp# delete interface dpdk GigabitEthernet0/6/0 
vpp# show interface 
              Name               Idx    State  MTU (L3/IP4/IP6/MPLS)     Counter          Count     
local0                            0     down          0/0/0/0       
vpp# 
代码语言:javascript
复制
下面是网卡加载和卸载的函数,代码相当简洁,如下:
代码语言:javascript
复制
int
dpdk_create_if (vlib_main_t *vm, dpdk_create_if_args_t *args)
{
  u8 *pci_addr = 0;
  dpdk_main_t *dm = &dpdk_main;
  vnet_main_t *vnm = vnet_get_main ();
  u16 port_id = ~0;
  args->rv = 0;
  args->error = NULL;
  dpdk_device_t *xd = NULL;

  vec_reset_length (pci_addr);
  pci_addr =
    format (0, "%U%c", format_vlib_pci_addr, &args->config.pci_addr, 0);
  /*查询当前网卡是否已经被dpdk纳管*/
  args->rv = rte_eth_dev_get_port_by_name ((char *) pci_addr, &port_id);
  if (!args->rv && vec_len (dm->devices))
    {
      vec_foreach (xd, dm->devices)
    {
      if (xd->port_id == port_id)
        {
          goto cleanup;
        }
    }
    }
 /*网卡绑定UIO*/
  args->error =
    vlib_pci_bind_to_uio (vm, &args->config.pci_addr, (char *) "auto", 1);
  if (args->error)
    {
      args->rv = args->error->code;
      goto cleanup;
    }
  /*执行网卡热加载函数*/
  args->rv = rte_eal_hotplug_add ("pci", (char *) pci_addr, NULL);
  if (args->rv)
    {
      args->error = clib_error_return (0, "rte_eal_hotplug_add failed");
      goto cleanup;
    }
  /*查询网卡port id*/
  args->rv = rte_eth_dev_get_port_by_name ((char *) pci_addr, &port_id);
  if (args->rv)
    {
      args->error =
    clib_error_return (0, "rte_eth_dev_get_port_by_name failed");
      goto cleanup;
    }
  /*根据实际workers数量更新rx tx队列*/
  if (args->config.workers && args->config.num_rx_queues == 0)
    args->config.num_rx_queues =
      clib_bitmap_count_set_bits (args->config.workers);
  else if (args->config.workers &&
       clib_bitmap_count_set_bits (args->config.workers) !=
         args->config.num_rx_queues)
    {
      args->error =
    clib_error_return (0,
               "%U: number of worker threads must be "
               "equal to number of rx queues",
               format_vlib_pci_addr, &args->config.pci_addr);
      goto cleanup;
    }
  /*在vpp中创建网卡接口*/
  u32 hw_if_index = dpdk_port_init (dm, port_id, &args->config);
  vnet_hw_interface_t *hif = vnet_get_hw_interface (vnm, hw_if_index);
  args->sw_if_index = hif->sw_if_index;

  xd = vec_elt_at_index (dm->devices, hif->dev_instance);

  vnet_hw_if_update_runtime_data (vnm, hw_if_index);
  /*更新网卡状态*/
  f64 now = vlib_time_now (vm);
  dpdk_update_link_state (xd, now);

cleanup:
  vec_free (pci_addr);

  return args->rv;
}

int
dpdk_delete_if (vlib_main_t *vm, u32 sw_if_index)
{
  int rv = 0;
  dpdk_main_t *dm = &dpdk_main;
  vnet_main_t *vnm = vnet_get_main ();
  struct rte_eth_dev_info di = {};

  if (sw_if_index == ~0)
    {
      dpdk_log_warn ("[%u] invalid sw interface.", sw_if_index);
      return VNET_ERR_INVALID_SW_IF_INDEX;
    }

  vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
  if (!si)
    {
      dpdk_log_warn ("[%u] failed to get sw interface.", sw_if_index);
      return VNET_ERR_INVALID_SW_IF_INDEX;
    }

  vnet_hw_interface_t *hif = vnet_get_hw_interface (vnm, si->hw_if_index);
  if (!hif)
    {
      dpdk_log_warn ("[%u] failed to get hw interface.", si->hw_if_index);
      return VNET_ERR_NO_SUCH_ENTRY;
    }

  dpdk_device_t *xd = vec_elt_at_index (dm->devices, hif->dev_instance);
  if (!xd)
    {
      dpdk_log_warn ("[%u] failed to get dpdk device.", hif->dev_instance);
      return VNET_ERR_NO_SUCH_ENTRY;
    }
  /*删除接口*/
  ethernet_delete_interface (vnm, si->hw_if_index);
  /*网卡设备stop掉*/
  dpdk_device_stop (xd);

  if ((rv = rte_eth_dev_info_get (xd->port_id, &di)) != 0)
    {
      dpdk_log_warn ("[%u] failed to get device info. skipping device.",
             xd->port_id);
      return VNET_ERR_NO_SUCH_ENTRY;
    }
  /*将网卡卸载掉*/
  rv = rte_dev_remove (di.device);
  if (rv)
    {
      dpdk_log_warn ("rte_dev_remove failed.");
      return VNET_ERR_NO_SUCH_ENTRY;
    }

  vec_del1 (dm->devices, xd - dm->devices);

  return 0;
}
代码语言:javascript
复制
代码语言:javascript
复制

此插件在vpp不重启的情况下已经实现了网卡加载和卸载的基本功能接口,但是并不是和系统热插拔事件相关联的。插件功能并不是很完善,当网卡绑定其他资源(子接口、l2xc)等功能时,卸载网卡可能会导致vpp异常,还需要释放相应的资源。

参考资料:

1、https://blog.csdn.net/longyu_wlz/article/details/127243979 2、https://zhuanlan.zhihu.com/p/560645934

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-11-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DPDK VPP源码分析 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档