内容简介: 接口层是硬件驱动和上层软件之间一层抽象代码,屏蔽硬件的差异,为上层软件提供一些统一的操作接口。上层软件调用接口层的操作进行报文的读入与发出,同时可以进行硬件设备的设置以及相关信息(比如统计数据)的读取。 vpp支持多种驱动类型的网络设备,比如dpdk,netmap,af_packet等等。同一种驱动的物理设备可以有多个,比如一个设备可以有多个dpdk接口,因此抽象了链路层接口hw_interface。在网络中有很多的虚拟设备,它们依附于物理设备,例如vlan设备对于同一个h
VPP接口层分析
接口层是硬件驱动和上层软件之间一层抽象代码,屏蔽硬件的差异,为上层软件提供一些统一的操作接口。上层软件调用接口层的操作进行报文的读入与发出,同时可以进行硬件设备的设置以及相关信息(比如统计数据)的读取。
vpp支持多种驱动类型的网络设备,比如dpdk,netmap,af_packet等等。同一种驱动的物理设备可以有多个,比如一个设备可以有多个dpdk接口,因此抽象了链路层接口hw_interface。在网络中有很多的虚拟设备,它们依附于物理设备,例如vlan设备对于同一个hw_interface可以有4096个虚拟的子接口。为了描述这些虚拟接口,vpp在hw_interface的基础上又封装了一层sw_interface来表示接口。sw_interface是整个接口层对上层软件的一个抽象,上层软件使用sw_interface索引用来表示具体操作的设备。
VPP支持的物理网络设备类型
一类设备表示使用相同硬件驱动的设备,比如dpdk类型的以太网设备,af_packet类型的虚拟以太网设备,netmap以太网设备等,属于物理层的描述。
设备类描述结构
/* A class of hardware interface devices. */ /* 一类硬件接口的操作函数集合,这些函数是在硬件设备的驱动上封装的一层 */ typedef struct _vnet_device_class { /* Index into main vector.类索引 */ u32 index; /* Device name (e.g. "FOOBAR 1234a").设备类名字 */ char *name; /* Function to call when hardware interface is added/deleted. */ /* 添加/删除一个该类设备的实例函数 */ vnet_interface_function_t *interface_add_del_function; /* Function to bring device administratively up/down. */ /* 设备UP/DOWN操作函数 */ vnet_interface_function_t *admin_up_down_function; /* Function to call when sub-interface is added/deleted */ /* 以该类型设备为主设备,添加/删除一个子接口的函数 */ vnet_subif_add_del_function_t *subif_add_del_function; /* Function to call interface rx mode is changed */ /* 接收模式变化函数 */ vnet_interface_set_rx_mode_function_t *rx_mode_change_function; /* Function to call interface l2 mode is changed */ /* 接口的二层模式变化操作函数 */ vnet_interface_set_l2_mode_function_t *set_l2_mode_function; /* Redistribute flag changes/existence of this interface class. */ u32 redistribute; /* Transmit function. */ /* 发送函数 */ vlib_node_function_t *tx_function; /* Transmit function candidate registration with priority */ /* 注册的多个候选的发送函数,最终根据优先级选择一个最高的赋值给tx_function */ vlib_node_fn_registration_t *tx_fn_registrations; /* Error strings indexed by error code for this node. */ /* 发送函数错误原因字符数组 */ char **tx_function_error_strings; /* Number of error codes used by this node. */ /* tx_function_error_strings数组大小 */ u32 tx_function_n_errors; /* Renumber device name [only!] support, a control-plane kludge */ int (*name_renumber) (struct vnet_hw_interface_t * hi, u32 new_dev_instance); /* Interface flow offload operations */ /* 流量卸载功能操作函数集合 */ vnet_flow_dev_ops_function_t *flow_ops_function; /* Format device instance as name. */ format_function_t *format_device_name; /* Parse function for device name. */ unformat_function_t *unformat_device_name; /* Format device verbosely for this class. */ format_function_t *format_device; /* Trace buffer format for TX function. */ format_function_t *format_tx_trace; /* Format flow offload entry */ format_function_t *format_flow; /* Function to clear hardware counters for device. */ /* 清除统计函数 */ void (*clear_counters) (u32 dev_class_instance); uword (*is_valid_class_for_interface) (struct vnet_main_t * vnm, u32 hw_if_index, u32 hw_class_index); /* Called when hardware class of an interface changes. */ void (*hw_class_change) (struct vnet_main_t * vnm, u32 hw_if_index, u32 new_hw_class_index); /* Called to redirect traffic from a specific interface instance */ /* 强制重定向一个接口的流量到指定的node */ void (*rx_redirect_to_node) (struct vnet_main_t * vnm, u32 hw_if_index, u32 node_index); /* Link-list of all device classes set up by constructors created below */ /* 形成链表 */ struct _vnet_device_class *next_class_registration; /* Function to set mac address. */ /* 链路层地址变化函数 */ vnet_interface_set_mac_address_function_t *mac_addr_change_function; } vnet_device_class_t;
设备类输出函数注册结构
该结构与node的功能函数注册结构是一样的
typedef struct _vlib_node_fn_registration { vlib_node_function_t *function; int priority; struct _vlib_node_fn_registration *next_registration; char *name; } vlib_node_fn_registration_t;
注册宏
#ifndef CLIB_MARCH_VARIANT /* 注册/去注册设备操作类函数集合的宏 */ #define VNET_DEVICE_CLASS(x,...) \ __VA_ARGS__ vnet_device_class_t x; \ static void __vnet_add_device_class_registration_##x (void) \ __attribute__((__constructor__)) ; \ static void __vnet_add_device_class_registration_##x (void) \ { \ vnet_main_t * vnm = vnet_get_main(); \ x.next_class_registration = vnm->device_class_registrations; \ vnm->device_class_registrations = &x; \ } \ static void __vnet_rm_device_class_registration_##x (void) \ __attribute__((__destructor__)) ; \ static void __vnet_rm_device_class_registration_##x (void) \ { \ vnet_main_t * vnm = vnet_get_main(); \ VLIB_REMOVE_FROM_LINKED_LIST (vnm->device_class_registrations, \ &x, next_class_registration); \ } \ __VA_ARGS__ vnet_device_class_t x #else /* create unused pointer to silence compiler warnings and get whole function optimized out */ #define VNET_DEVICE_CLASS(x,...) \ static __clib_unused vnet_device_class_t __clib_unused_##x #endif /* 注册/去注册设备类的输出函数宏 */ #define VNET_DEVICE_CLASS_TX_FN(devclass) \ uword CLIB_MARCH_SFX (devclass##_tx_fn)(); \ static vlib_node_fn_registration_t \ CLIB_MARCH_SFX(devclass##_tx_fn_registration) = \ { .function = &CLIB_MARCH_SFX (devclass##_tx_fn), }; \ \ static void __clib_constructor \ CLIB_MARCH_SFX (devclass##_tx_fn_multiarch_register) (void) \ { \ extern vnet_device_class_t devclass; \ vlib_node_fn_registration_t *r; \ r = &CLIB_MARCH_SFX (devclass##_tx_fn_registration); \ r->priority = CLIB_MARCH_FN_PRIORITY(); \ r->next_registration = devclass.tx_fn_registrations; \ devclass.tx_fn_registrations = r; \ } \ uword CLIB_CPU_OPTIMIZED CLIB_MARCH_SFX (devclass##_tx_fn)
实例
/* *INDENT-OFF* */ VNET_DEVICE_CLASS (dpdk_device_class) = { .name = "dpdk", .tx_function_n_errors = DPDK_TX_FUNC_N_ERROR, .tx_function_error_strings = dpdk_tx_func_error_strings, .format_device_name = format_dpdk_device_name, .format_device = format_dpdk_device, .format_tx_trace = format_dpdk_tx_trace, .clear_counters = dpdk_clear_hw_interface_counters, .admin_up_down_function = dpdk_interface_admin_up_down, .subif_add_del_function = dpdk_subif_add_del_function, .rx_redirect_to_node = dpdk_set_interface_next_node, .mac_addr_change_function = dpdk_set_mac_address, .format_flow = format_dpdk_flow, .flow_ops_function = dpdk_flow_ops_fn, };
链路层interface类型
在物理设备上,根据接口的功能进一步抽象一层硬件设备类型,七层中属于链路层。比如ethernet设备,bond设备,vlan设备等。
interface类型描述结构
/* Layer-2 (e.g. Ethernet) interface class. */ typedef struct _vnet_hw_interface_class { /* Index into main vector. */ /* 索引 */ u32 index; /* Class name (e.g. "Ethernet"). 其所属类名字 */ char *name; /* Flags 标志,目前只有一个,是否为点到点设备,点到点设备不需要邻居信息 */ vnet_hw_interface_class_flags_t flags; /* Function to call when hardware interface is added/deleted. */ vnet_interface_function_t *interface_add_del_function; /* Function to bring interface administratively up/down. */ vnet_interface_function_t *admin_up_down_function; /* Function to call when link state changes. */ vnet_interface_function_t *link_up_down_function; /* Function to call when link MAC changes. */ vnet_interface_set_mac_address_function_t *mac_addr_change_function; /* Format function to display interface name. */ format_function_t *format_interface_name; /* Format function to display interface address. */ format_function_t *format_address; /* Format packet header for this interface class. */ format_function_t *format_header; /* Format device verbosely for this class. */ format_function_t *format_device; /* Parser for hardware (e.g. ethernet) address. */ unformat_function_t *unformat_hw_address; /* Parser for packet header for e.g. rewrite string. */ unformat_function_t *unformat_header; /* Builds a rewrite string for the interface to the destination * for the payload/link type. */ u8 *(*build_rewrite) (struct vnet_main_t * vnm, u32 sw_if_index, vnet_link_t link_type, const void *dst_hw_address); /* Update an adjacency added by FIB (as opposed to via the * neighbour resolution protocol). */ void (*update_adjacency) (struct vnet_main_t * vnm, u32 sw_if_index, u32 adj_index); uword (*is_valid_class_for_interface) (struct vnet_main_t * vnm, u32 hw_if_index, u32 hw_class_index); /* Called when hw interface class is changed and old hardware instance may want to be deleted. */ void (*hw_class_change) (struct vnet_main_t * vnm, u32 hw_if_index, u32 old_class_index, u32 new_class_index); /* List of hw interface classes, built by constructors */ struct _vnet_hw_interface_class *next_class_registration; } vnet_hw_interface_class_t;
注册宏
/** * @brief Default adjacency update function */ extern void default_update_adjacency (struct vnet_main_t *vnm, u32 sw_if_index, u32 adj_index); #define VNET_HW_INTERFACE_CLASS(x,...) \ __VA_ARGS__ vnet_hw_interface_class_t x; \ static void __vnet_add_hw_interface_class_registration_##x (void) \ __attribute__((__constructor__)) ; \ static void __vnet_add_hw_interface_class_registration_##x (void) \ { \ vnet_main_t * vnm = vnet_get_main(); \ x.next_class_registration = vnm->hw_interface_class_registrations; \ vnm->hw_interface_class_registrations = &x; \ } \ static void __vnet_rm_hw_interface_class_registration_##x (void) \ __attribute__((__destructor__)) ; \ static void __vnet_rm_hw_interface_class_registration_##x (void) \ { \ vnet_main_t * vnm = vnet_get_main(); \ VLIB_REMOVE_FROM_LINKED_LIST (vnm->hw_interface_class_registrations,\ &x, next_class_registration); \ } \ __VA_ARGS__ vnet_hw_interface_class_t x
实例
/* *INDENT-OFF* */ VNET_HW_INTERFACE_CLASS (ethernet_hw_interface_class) = { .name = "Ethernet", .format_address = format_ethernet_address, .format_header = format_ethernet_header_with_length, .unformat_hw_address = unformat_ethernet_address, .unformat_header = unformat_ethernet_header, .build_rewrite = ethernet_build_rewrite, .update_adjacency = ethernet_update_adjacency, .mac_addr_change_function = ethernet_mac_change, }; /* *INDENT-ON* */
软件层interface
typedef enum vnet_sw_interface_flags_t_ { VNET_SW_INTERFACE_FLAG_NONE = 0, /* Interface is "up" meaning administratively up. Up in the sense of link state being up is maintained by hardware interface. */ VNET_SW_INTERFACE_FLAG_ADMIN_UP = (1 << 0), /* Interface is disabled for forwarding: punt all traffic to slow-path. */ VNET_SW_INTERFACE_FLAG_PUNT = (1 << 1), VNET_SW_INTERFACE_FLAG_PROXY_ARP = (1 << 2), VNET_SW_INTERFACE_FLAG_UNNUMBERED = (1 << 3), VNET_SW_INTERFACE_FLAG_BOND_SLAVE = (1 << 4), /* Interface does not appear in CLI/API */ VNET_SW_INTERFACE_FLAG_HIDDEN = (1 << 5), /* Interface in ERROR state */ VNET_SW_INTERFACE_FLAG_ERROR = (1 << 6), /* Interface has IP configured directed broadcast */ VNET_SW_INTERFACE_FLAG_DIRECTED_BCAST = (1 << 7), } __attribute__ ((packed)) vnet_sw_interface_flags_t; /* Software-interface. This corresponds to a Ethernet VLAN, ATM vc, a tunnel, etc. Configuration (e.g. IP address) gets attached to software interface. * 软件层接口,例如vlan,atm,tunnel等 */ typedef struct { /* 接口类型 */ vnet_sw_interface_type_t type:16; /* 接口标志 */ vnet_sw_interface_flags_t flags; /* Index for this interface. */ /* 软件接口索引 */ u32 sw_if_index; /* Software interface index of super-interface; equal to sw_if_index if this interface is not a sub-interface. * 如果该接口是一个子接口的话,那么该值为其依附的接口的sw_if_index,否则就是sw_if_index */ u32 sup_sw_if_index; /* this swif is unnumbered, use addresses on unnumbered_sw_if_index... */ u32 unnumbered_sw_if_index; /* VNET_SW_INTERFACE_TYPE_HARDWARE. */ u32 hw_if_index; /* MTU for network layer (not including L2 headers) */ /* 网络层mtu,不包括链路层 */ u32 mtu[VNET_N_MTU]; /* VNET_SW_INTERFACE_TYPE_SUB. */ /* 如果该接口是子接口的话,那么子接口信息保存在该结构体中 */ vnet_sub_interface_t sub; /* VNET_SW_INTERFACE_TYPE_P2P. */ vnet_p2p_sub_interface_t p2p; vnet_flood_class_t flood_class; } vnet_sw_interface_t;
子接口
typedef enum { /* A hw interface. */ VNET_SW_INTERFACE_TYPE_HARDWARE, /* A sub-interface. */ VNET_SW_INTERFACE_TYPE_SUB, VNET_SW_INTERFACE_TYPE_P2P, VNET_SW_INTERFACE_TYPE_PIPE, } vnet_sw_interface_type_t; typedef struct { /* * Subinterface ID. A number 0-N to uniquely identify * this subinterface under the main (parent?) interface */ u32 id; /* Classification data. Used to associate packet header with subinterface. */ struct { u16 outer_vlan_id; u16 inner_vlan_id; union { u16 raw_flags; struct { u16 no_tags:1; u16 one_tag:1; u16 two_tags:1; u16 dot1ad:1; /* 0 = dot1q, 1=dot1ad */ u16 exact_match:1; u16 default_sub:1; u16 outer_vlan_id_any:1; u16 inner_vlan_id_any:1; } flags; }; } eth; } vnet_sub_interface_t;
接口层初始化过程
前面所述的设备和接口注册信息,是在main函数之前进行注册的,main函数需要对这些信息进行整理加工。在vpp中vnet_main_t中的interface_main成员维护了接口相关的信息。
接口管理接口结构
/* 接口管理全局结构 */ typedef struct { /* Hardware interfaces. 硬件接口数组,所有链路层接口都放置在该数组中 */ vnet_hw_interface_t *hw_interfaces; /* Hash table mapping HW interface name to index. 链路岑该接口名字与索引映射表*/ uword *hw_interface_by_name; /* Vectors if hardware interface classes and device classes. */ /* 静态注册的链路层接口类型链表 */ vnet_hw_interface_class_t *hw_interface_classes; /* 静态注册的物理层设备类型链表 */ vnet_device_class_t *device_classes; /* Hash table mapping name to hw interface/device class. */ /* 链路层接口类型索引与名字映射表 */ uword *hw_interface_class_by_name; /* 设备类索引与名字映射表 */ uword *device_class_by_name; /* Software interfaces. */ /* 软件接口数组 */ vnet_sw_interface_t *sw_interfaces; /* Hash table mapping sub intfc sw_if_index by sup sw_if_index and sub id */ uword *sw_if_index_by_sup_and_sub; /* Software interface counters both simple and combined * packet and byte counters. * 软件接口统计与其对应的锁 */ volatile u32 *sw_if_counter_lock; vlib_simple_counter_main_t *sw_if_counters; vlib_combined_counter_main_t *combined_sw_if_counters; vnet_hw_interface_nodes_t *deleted_hw_interface_nodes; /* pcap drop tracing */ int drop_pcap_enable; pcap_main_t pcap_main; u8 *pcap_filename; u32 pcap_sw_if_index; u32 pcap_pkts_to_capture; uword *pcap_drop_filter_hash; /* per-thread data */ vnet_interface_per_thread_data_t *per_thread_data; /* enable GSO processing in packet path if this count is > 0 */ u32 gso_interface_count; /* feature_arc_index */ u8 output_feature_arc_index; } vnet_interface_main_t;
接口管理初始化函数
/* 接口初始化 */ clib_error_t * vnet_interface_init (vlib_main_t * vm) { vnet_main_t *vnm = vnet_get_main (); vnet_interface_main_t *im = &vnm->interface_main; vlib_buffer_t *b = 0; vnet_buffer_opaque_t *o = 0; clib_error_t *error; /* * Keep people from shooting themselves in the foot. */ if (sizeof (b->opaque) != sizeof (vnet_buffer_opaque_t)) { #define _(a) if (sizeof(o->a) > sizeof (o->unused)) \ clib_warning \ ("FATAL: size of opaque union subtype %s is %d (max %d)", \ #a, sizeof(o->a), sizeof (o->unused)); foreach_buffer_opaque_union_subtype; #undef _ return clib_error_return (0, "FATAL: size of vlib buffer opaque %d, size of vnet opaque %d", sizeof (b->opaque), sizeof (vnet_buffer_opaque_t)); } /* 统计信息锁,负责保护sw_if_counters */ im->sw_if_counter_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, CLIB_CACHE_LINE_BYTES); im->sw_if_counter_lock[0] = 1; /* should be no need */ vec_validate (im->sw_if_counters, VNET_N_SIMPLE_INTERFACE_COUNTER - 1); #define _(E,n,p) /* 各种统计信息初始化 */ \ im->sw_if_counters[VNET_INTERFACE_COUNTER_##E].name = #n; \ im->sw_if_counters[VNET_INTERFACE_COUNTER_##E].stat_segment_name = "/" #p "/" #n; foreach_simple_interface_counter_name #undef _ vec_validate (im->combined_sw_if_counters, VNET_N_COMBINED_INTERFACE_COUNTER - 1); #define _(E,n,p) \ im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_##E].name = #n; \ im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_##E].stat_segment_name = "/" #p "/" #n; foreach_combined_interface_counter_name #undef _ im->sw_if_counter_lock[0] = 0; /* 构建设备类型描述结构体索引与类型名之间的映射表 */ im->device_class_by_name = hash_create_string ( /* size */ 0, sizeof (uword)); { vnet_device_class_t *c; /* 遍历每一个注册的设备 */ c = vnm->device_class_registrations; while (c) { c->index = vec_len (im->device_classes);/* 分配索引 */ /* 设置索引与名字之间的映射关系 */ hash_set_mem (im->device_class_by_name, c->name, c->index); if (c->tx_fn_registrations) { vlib_node_fn_registration_t *fnr = c->tx_fn_registrations; int priority = -1; /* to avoid confusion, please remove ".tx_function" statement from VNET_DEVICE_CLASS() if using function candidates */ ASSERT (c->tx_function == 0); /* 获取最高优先级的发送函数 */ while (fnr) { if (fnr->priority > priority) { priority = fnr->priority; c->tx_function = fnr->function; } fnr = fnr->next_registration; } } vec_add1 (im->device_classes, c[0]); c = c->next_class_registration; } } /* 初始化硬件interface索引与名字映射表 */ im->hw_interface_class_by_name = hash_create_string ( /* size */ 0, sizeof (uword)); im->sw_if_index_by_sup_and_sub = hash_create_mem (0, sizeof (u64), sizeof (uword)); { vnet_hw_interface_class_t *c; /* 遍历每一个注册的链路层interface类型 */ c = vnm->hw_interface_class_registrations; while (c) { c->index = vec_len (im->hw_interface_classes); hash_set_mem (im->hw_interface_class_by_name, c->name, c->index); if (NULL == c->build_rewrite) c->build_rewrite = default_build_rewrite; if (NULL == c->update_adjacency) c->update_adjacency = default_update_adjacency; vec_add1 (im->hw_interface_classes, c[0]); c = c->next_class_registration; } } im->gso_interface_count = 0; /* init per-thread data */ vec_validate_aligned (im->per_thread_data, vlib_num_workers (), CLIB_CACHE_LINE_BYTES); if ((error = vlib_call_init_function (vm, vnet_interface_cli_init))) return error; vnm->interface_tag_by_sw_if_index = hash_create (0, sizeof (uword)); #if VLIB_BUFFER_TRACE_TRAJECTORY > 0 if ((error = vlib_call_init_function (vm, trajectory_trace_init))) return error; #endif return 0; } VLIB_INIT_FUNCTION (vnet_interface_init);
添加一个接口
以af_packet类型的以太网接口为例进行说明
/* 创建一个af_packet类型的接口 */ int af_packet_create_if (vlib_main_t * vm, u8 * host_if_name, u8 * hw_addr_set, u32 * sw_if_index) { af_packet_main_t *apm = &af_packet_main; int ret, fd = -1, fd2 = -1; struct tpacket_req *rx_req = 0; struct tpacket_req *tx_req = 0; struct ifreq ifr; u8 *ring = 0; af_packet_if_t *apif = 0; u8 hw_addr[6]; clib_error_t *error; vnet_sw_interface_t *sw; vnet_hw_interface_t *hw; vlib_thread_main_t *tm = vlib_get_thread_main (); vnet_main_t *vnm = vnet_get_main (); uword *p; uword if_index; u8 *host_if_name_dup = vec_dup (host_if_name); int host_if_index = -1; p = mhash_get (&apm->if_index_by_host_if_name, host_if_name); if (p) { apif = vec_elt_at_index (apm->interfaces, p[0]); *sw_if_index = apif->sw_if_index; return VNET_API_ERROR_IF_ALREADY_EXISTS; } vec_validate (rx_req, 0); rx_req->tp_block_size = AF_PACKET_RX_BLOCK_SIZE; rx_req->tp_frame_size = AF_PACKET_RX_FRAME_SIZE; rx_req->tp_block_nr = AF_PACKET_RX_BLOCK_NR; rx_req->tp_frame_nr = AF_PACKET_RX_FRAME_NR; vec_validate (tx_req, 0); tx_req->tp_block_size = AF_PACKET_TX_BLOCK_SIZE; tx_req->tp_frame_size = AF_PACKET_TX_FRAME_SIZE; tx_req->tp_block_nr = AF_PACKET_TX_BLOCK_NR; tx_req->tp_frame_nr = AF_PACKET_TX_FRAME_NR; /* * make sure host side of interface is 'UP' before binding AF_PACKET * socket on it. * 需要确保af_packet类型的接口依附的主机侧的接口是up的 */ if ((fd2 = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0) { vlib_log_debug (apm->log_class, "Failed to create socket"); ret = VNET_API_ERROR_SYSCALL_ERROR_1; goto error; } clib_memcpy (ifr.ifr_name, (const char *) host_if_name, vec_len (host_if_name)); /* 根据名字获取主机接口的索引 */ if ((ret = ioctl (fd2, SIOCGIFINDEX, &ifr)) < 0) { vlib_log_debug (apm->log_class, "af_packet_create error: %d", ret); close (fd2); return VNET_API_ERROR_INVALID_INTERFACE; } host_if_index = ifr.ifr_ifindex; /* 获取主机接口的标志信息 */ if ((ret = ioctl (fd2, SIOCGIFFLAGS, &ifr)) < 0) { vlib_log_warn (apm->log_class, "af_packet_create error: %d", ret); goto error; } /* 如果没有up,那么设置其为UP */ if (!(ifr.ifr_flags & IFF_UP)) { ifr.ifr_flags |= IFF_UP; if ((ret = ioctl (fd2, SIOCSIFFLAGS, &ifr)) < 0) { vlib_log_warn (apm->log_class, "af_packet_create error: %d", ret); goto error; } } if (fd2 > -1) close (fd2); /* 创建af_packet套接字 */ ret = create_packet_v2_sock (host_if_index, rx_req, tx_req, &fd, &ring); if (ret != 0) goto error; ret = is_bridge (host_if_name); if (ret == 0) /* is a bridge, ignore state */ host_if_index = -1; /* So far everything looks good, let's create interface */ pool_get (apm->interfaces, apif); if_index = apif - apm->interfaces; apif->host_if_index = host_if_index; apif->fd = fd; apif->rx_ring = ring; apif->tx_ring = ring + rx_req->tp_block_size * rx_req->tp_block_nr; apif->rx_req = rx_req; apif->tx_req = tx_req; apif->host_if_name = host_if_name_dup; apif->per_interface_next_index = ~0; apif->next_tx_frame = 0; apif->next_rx_frame = 0; if (tm->n_vlib_mains > 1)/* 添加epoll监听事件,用于模拟中断 */ clib_spinlock_init (&apif->lockp); { clib_file_t template = { 0 }; template.read_function = af_packet_fd_read_ready; template.file_descriptor = fd; template.private_data = if_index; template.flags = UNIX_FILE_EVENT_EDGE_TRIGGERED; template.description = format (0, "%U", format_af_packet_device_name, if_index); apif->clib_file_index = clib_file_add (&file_main, &template); } /*use configured or generate random MAC address */ if (hw_addr_set) clib_memcpy (hw_addr, hw_addr_set, 6); else { f64 now = vlib_time_now (vm); u32 rnd; rnd = (u32) (now * 1e6); rnd = random_u32 (&rnd); clib_memcpy (hw_addr + 2, &rnd, sizeof (rnd)); hw_addr[0] = 2; hw_addr[1] = 0xfe; } /* 注册以太网接口 */ error = ethernet_register_interface (vnm, af_packet_device_class.index, if_index, hw_addr, &apif->hw_if_index, af_packet_eth_flag_change); if (error) { clib_memset (apif, 0, sizeof (*apif)); pool_put (apm->interfaces, apif); vlib_log_err (apm->log_class, "Unable to register interface: %U", format_clib_error, error); clib_error_free (error); ret = VNET_API_ERROR_SYSCALL_ERROR_1; goto error; } sw = vnet_get_hw_sw_interface (vnm, apif->hw_if_index); hw = vnet_get_hw_interface (vnm, apif->hw_if_index); apif->sw_if_index = sw->sw_if_index; /* 设置该接口对应的输入节点索引 */ vnet_hw_interface_set_input_node (vnm, apif->hw_if_index, af_packet_input_node.index); /* 分配该接口到指定的收包线程中 */ vnet_hw_interface_assign_rx_thread (vnm, apif->hw_if_index, 0, /* queue */ ~0 /* any cpu */ ); /* 接口支持中断模式 */ hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_INT_MODE; vnet_hw_interface_set_flags (vnm, apif->hw_if_index, VNET_HW_INTERFACE_FLAG_LINK_UP); vnet_hw_interface_set_rx_mode (vnm, apif->hw_if_index, 0, VNET_HW_INTERFACE_RX_MODE_INTERRUPT); mhash_set_mem (&apm->if_index_by_host_if_name, host_if_name_dup, &if_index, 0); if (sw_if_index) *sw_if_index = apif->sw_if_index; return 0; error: if (fd2 > -1) close (fd2); vec_free (host_if_name_dup); vec_free (rx_req); vec_free (tx_req); return ret; }
注册ethernet接口
/* 注册以太网接口 */ clib_error_t * ethernet_register_interface (vnet_main_t * vnm, u32 dev_class_index,/* 设备类型 */ u32 dev_instance,/* 设备实例索引 */ const u8 * address,/* 链路层地址 */ u32 * hw_if_index_return,/* 返回的硬件设备索引 */ ethernet_flag_change_function_t flag_change) { ethernet_main_t *em = ðernet_main; ethernet_interface_t *ei; vnet_hw_interface_t *hi; clib_error_t *error = 0; u32 hw_if_index; /* 获取一个以太网设备实例 */ pool_get (em->interfaces, ei); /* 用于配置以太网设备的硬件特性,比如混杂模式等 */ ei->flag_change = flag_change; /* 注册以太网接口,给该以太网设备分配一个硬件索引 */ hw_if_index = vnet_register_interface (vnm, dev_class_index, dev_instance, ethernet_hw_interface_class.index, ei - em->interfaces); *hw_if_index_return = hw_if_index; hi = vnet_get_hw_interface (vnm, hw_if_index); /* 设置node的以太网相关的函数 */ ethernet_setup_node (vnm->vlib_main, hi->output_node_index); hi->min_packet_bytes = hi->min_supported_packet_bytes = ETHERNET_MIN_PACKET_BYTES; hi->max_packet_bytes = hi->max_supported_packet_bytes = ETHERNET_MAX_PACKET_BYTES; /* Standard default ethernet MTU. */ /* 标准的以太网mtu为9000 */ vnet_sw_interface_set_mtu (vnm, hi->sw_if_index, 9000); clib_memcpy (ei->address, address, sizeof (ei->address)); vec_add (hi->hw_address, address, sizeof (ei->address)); if (error) { pool_put (em->interfaces, ei); return error; } return error; }
向接口管理中注册一个接口
/* Register an interface instance. */ u32 vnet_register_interface (vnet_main_t * vnm, u32 dev_class_index, u32 dev_instance,/* 使用同一种驱动的设备实例编号 */ u32 hw_class_index, u32 hw_instance)/* 同一类硬件设备的编号,比如以太网设备 */ { vnet_interface_main_t *im = &vnm->interface_main; vnet_hw_interface_t *hw; /* 根据设备类型索引获取对应的类型描述控制块,相当于 linux 内核的驱动ops */ vnet_device_class_t *dev_class = vnet_get_device_class (vnm, dev_class_index); vnet_hw_interface_class_t *hw_class = vnet_get_hw_interface_class (vnm, hw_class_index); vlib_main_t *vm = vnm->vlib_main; vnet_feature_config_main_t *fcm; vnet_config_main_t *cm; u32 hw_index, i; char *tx_node_name = NULL, *output_node_name = NULL; /* 分配一个硬件接口描述控制块 */ pool_get (im->hw_interfaces, hw); clib_memset (hw, 0, sizeof (*hw)); hw_index = hw - im->hw_interfaces; hw->hw_if_index = hw_index; /* 默认模式就是POLLING模式 */ hw->default_rx_mode = VNET_HW_INTERFACE_RX_MODE_POLLING; /* 设置设备名字 */ if (dev_class->format_device_name) hw->name = format (0, "%U", dev_class->format_device_name, dev_instance); else if (hw_class->format_interface_name) hw->name = format (0, "%U", hw_class->format_interface_name, dev_instance); else hw->name = format (0, "%s%x", hw_class->name, dev_instance); /* 构建接口名字到接口索引的hash表 */ if (!im->hw_interface_by_name) im->hw_interface_by_name = hash_create_vec ( /* size */ 0, sizeof (hw->name[0]), sizeof (uword)); /* 设置本接口名字与索引的映射关系 */ hash_set_mem (im->hw_interface_by_name, hw->name, hw_index); /* Make hardware interface point to software interface. */ /* 在硬件接口的基础上构建软件接口 */ { vnet_sw_interface_t sw = { .type = VNET_SW_INTERFACE_TYPE_HARDWARE, .flood_class = VNET_FLOOD_CLASS_NORMAL, .hw_if_index = hw_index }; /* 分配软件接口索引 */ hw->sw_if_index = vnet_create_sw_interface_no_callbacks (vnm, &sw); } /* 设置设备类索引 */ hw->dev_class_index = dev_class_index; /* 同类设备的索引 */ hw->dev_instance = dev_instance; /* 硬件类索引 */ hw->hw_class_index = hw_class_index; /* 同硬件类索引 */ hw->hw_instance = hw_instance; hw->max_rate_bits_per_sec = 0; hw->min_packet_bytes = 0; /* 设置mtu默认为0 */ vnet_sw_interface_set_mtu (vnm, hw->sw_if_index, 0); /* 这类设备没有发送函数,创建一个无发送类型节点 */ if (dev_class->tx_function == 0) goto no_output_nodes; /* No output/tx nodes to create */ /* interface不仅需要承当报文输入功能,同时也要承担报文输出功能,所以需要两个node * 输入功能采用的是依附于input-node,而输出功能需要单独添加一个node,在vpp实现中 * 采用了两个node来实现输出功能,一个是hw-interface层,用于构建链路层头,另外一个 * 物理层,调用驱动发包函数发送报文。 */ tx_node_name = (char *) format (0, "%v-tx", hw->name); output_node_name = (char *) format (0, "%v-output", hw->name); /* If we have previously deleted interface nodes, re-use them. */ /* 如果以前有些接口node删除了,复用它们 */ if (vec_len (im->deleted_hw_interface_nodes) > 0) { vnet_hw_interface_nodes_t *hn; vlib_node_t *node; vlib_node_runtime_t *nrt; hn = vec_end (im->deleted_hw_interface_nodes) - 1; hw->tx_node_index = hn->tx_node_index; hw->output_node_index = hn->output_node_index; vlib_node_rename (vm, hw->tx_node_index, "%v", tx_node_name); vlib_node_rename (vm, hw->output_node_index, "%v", output_node_name); /* *INDENT-OFF* */ foreach_vlib_main ( { vnet_interface_output_runtime_t *rt; rt = vlib_node_get_runtime_data (this_vlib_main, hw->output_node_index); ASSERT (rt->is_deleted == 1); rt->is_deleted = 0; rt->hw_if_index = hw_index; rt->sw_if_index = hw->sw_if_index; rt->dev_instance = hw->dev_instance; rt = vlib_node_get_runtime_data (this_vlib_main, hw->tx_node_index); rt->hw_if_index = hw_index; rt->sw_if_index = hw->sw_if_index; rt->dev_instance = hw->dev_instance; }); /* *INDENT-ON* */ /* The new class may differ from the old one. * Functions have to be updated. */ node = vlib_get_node (vm, hw->output_node_index); node->function = vnet_interface_output_node; node->format_trace = format_vnet_interface_output_trace; /* *INDENT-OFF* */ foreach_vlib_main ( { nrt = vlib_node_get_runtime (this_vlib_main, hw->output_node_index); nrt->function = node->function; }); /* *INDENT-ON* */ node = vlib_get_node (vm, hw->tx_node_index); node->function = dev_class->tx_function; node->format_trace = dev_class->format_tx_trace; /* *INDENT-OFF* */ foreach_vlib_main ( { nrt = vlib_node_get_runtime (this_vlib_main, hw->tx_node_index); nrt->function = node->function; }); /* *INDENT-ON* */ _vec_len (im->deleted_hw_interface_nodes) -= 1; } else { vlib_node_registration_t r; vnet_interface_output_runtime_t rt = { .hw_if_index = hw_index, .sw_if_index = hw->sw_if_index, .dev_instance = hw->dev_instance, .is_deleted = 0, }; /* 注册物理层输出节点,该节点直接将报文输出,不需要下一个节点,除非报文出错 */ clib_memset (&r, 0, sizeof (r)); r.type = VLIB_NODE_TYPE_INTERNAL; r.runtime_data = &rt; r.runtime_data_bytes = sizeof (rt); r.scalar_size = 0; r.vector_size = sizeof (u32); r.flags = VLIB_NODE_FLAG_IS_OUTPUT; r.name = tx_node_name; r.function = dev_class->tx_function; hw->tx_node_index = vlib_register_node (vm, &r); vlib_node_add_named_next_with_slot (vm, hw->tx_node_index, "error-drop", VNET_INTERFACE_TX_NEXT_DROP); /* 注册链路层输出节点,用于构建链路层信息,指向物理层节点 */ r.flags = 0; r.name = output_node_name; r.function = vnet_interface_output_node; r.format_trace = format_vnet_interface_output_trace; { static char *e[] = { "interface is down", "interface is deleted", "no buffers to segment GSO", }; r.n_errors = ARRAY_LEN (e); r.error_strings = e; } hw->output_node_index = vlib_register_node (vm, &r); vlib_node_add_named_next_with_slot (vm, hw->output_node_index, "error-drop", VNET_INTERFACE_OUTPUT_NEXT_DROP); /* 指向物理层输出节点 */ vlib_node_add_next_with_slot (vm, hw->output_node_index, hw->tx_node_index, VNET_INTERFACE_OUTPUT_NEXT_TX); /* add interface to the list of "output-interface" feature arc start nodes * and clone nexts from 1st interface if it exists * 构建链路层输出节点的output-featrue-arc。 * 将本output节点作为output-featrue-arc的一个起始几点。 */ fcm = vnet_feature_get_config_main (im->output_feature_arc_index); cm = &fcm->config_main; i = vec_len (cm->start_node_indices); vec_validate (cm->start_node_indices, i); cm->start_node_indices[i] = hw->output_node_index; if (hw_index) { /* copy nexts from 1st interface */ vnet_hw_interface_t *first_hw; vlib_node_t *first_node; first_hw = vnet_get_hw_interface (vnm, /* hw_if_index */ 0); first_node = vlib_get_node (vm, first_hw->output_node_index); /* 1st 2 nexts are already added above */ for (i = 2; i < vec_len (first_node->next_nodes); i++) vlib_node_add_next_with_slot (vm, hw->output_node_index, first_node->next_nodes[i], i); } } /* 构建链路层输出节点 */ setup_output_node (vm, hw->output_node_index, hw_class); /* 构建物理层输出节点 */ setup_tx_node (vm, hw->tx_node_index, dev_class); no_output_nodes: /* Call all up/down callbacks with zero flags when interface is created. */ vnet_sw_interface_set_flags_helper (vnm, hw->sw_if_index, /* flags */ 0, VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE); vnet_hw_interface_set_flags_helper (vnm, hw_index, /* flags */ 0, VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE); vec_free (tx_node_name); vec_free (output_node_name); return hw_index; }
给接口分配指定的收包线程
在函数af_packet_create_if中调用了如下代码,用于设置该接口的收包线程,对于af_packet采用的是中断模式收包。
/* 设置该接口对应的输入节点索引 */ vnet_hw_interface_set_input_node (vnm, apif->hw_if_index, af_packet_input_node.index); /* 分配该接口到指定的收包线程中 */ vnet_hw_interface_assign_rx_thread (vnm, apif->hw_if_index, 0, /* queue */ ~0 /* any cpu */ );
vnet_hw_interface_assign_rx_thread
/* 给接口分配收包线程 */ void vnet_hw_interface_assign_rx_thread (vnet_main_t * vnm, u32 hw_if_index, u16 queue_id, uword thread_index) { vnet_device_main_t *vdm = &vnet_device_main; vlib_main_t *vm, *vm0; vnet_device_input_runtime_t *rt; vnet_device_and_queue_t *dq; vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); ASSERT (hw->input_node_index > 0); if (vdm->first_worker_thread_index == 0)/* 没有收包线程,即主线程负责所有工作,则使用主模式进行收包 */ thread_index = 0; if (thread_index != 0 &&/* 选取合适的线程进行收包,算法为rr */ (thread_index < vdm->first_worker_thread_index || thread_index > vdm->last_worker_thread_index)) { thread_index = vdm->next_worker_thread_index++; if (vdm->next_worker_thread_index > vdm->last_worker_thread_index)/* 开始下一个来回 */ vdm->next_worker_thread_index = vdm->first_worker_thread_index; } vm = vlib_mains[thread_index];/* 收包线程 */ vm0 = vlib_get_main ();/* 本线程,一般是主线程 */ vlib_worker_thread_barrier_sync (vm0);/* 开始同步 */ rt = vlib_node_get_runtime_data (vm, hw->input_node_index);/* 获取输入节点的运行数据 */ vec_add2 (rt->devices_and_queues, dq, 1);/* 增加一个队列 */ dq->hw_if_index = hw_if_index; dq->dev_instance = hw->dev_instance; dq->queue_id = queue_id; dq->mode = VNET_HW_INTERFACE_RX_MODE_POLLING;/* 默认设置轮询模式 */ rt->enabled_node_state = VLIB_NODE_STATE_POLLING; vnet_device_queue_update (vnm, rt); /* 建立队列与线程之间的映射关系 */ vec_validate (hw->input_node_thread_index_by_queue, queue_id); vec_validate (hw->rx_mode_by_queue, queue_id); hw->input_node_thread_index_by_queue[queue_id] = thread_index; hw->rx_mode_by_queue[queue_id] = VNET_HW_INTERFACE_RX_MODE_POLLING; vlib_worker_thread_barrier_release (vm0); /* 更新节点状态统计信息 */ vlib_node_set_state (vm, hw->input_node_index, rt->enabled_node_state); }
删除一个interface
我们还是以af_packet接口为例:
int af_packet_delete_if (vlib_main_t * vm, u8 * host_if_name) { vnet_main_t *vnm = vnet_get_main (); af_packet_main_t *apm = &af_packet_main; af_packet_if_t *apif; uword *p; uword if_index; u32 ring_sz; p = mhash_get (&apm->if_index_by_host_if_name, host_if_name); if (p == NULL) { vlib_log_warn (apm->log_class, "Host interface %s does not exist", host_if_name); return VNET_API_ERROR_SYSCALL_ERROR_1; } apif = pool_elt_at_index (apm->interfaces, p[0]); if_index = apif - apm->interfaces; /* bring down the interface 将主机接口down掉*/ vnet_hw_interface_set_flags (vnm, apif->hw_if_index, 0); /* 从input节点摘除 */ vnet_hw_interface_unassign_rx_thread (vnm, apif->hw_if_index, 0); /* clean up 清除注册的中断,没有中断了将不会收包 */ if (apif->clib_file_index != ~0) { clib_file_del (&file_main, file_main.file_pool + apif->clib_file_index); apif->clib_file_index = ~0; } else close (apif->fd); ring_sz = apif->rx_req->tp_block_size * apif->rx_req->tp_block_nr + apif->tx_req->tp_block_size * apif->tx_req->tp_block_nr; if (munmap (apif->rx_ring, ring_sz)) vlib_log_warn (apm->log_class, "Host interface %s could not free rx/tx ring", host_if_name); apif->rx_ring = NULL; apif->tx_ring = NULL; apif->fd = -1; vec_free (apif->rx_req); apif->rx_req = NULL; vec_free (apif->tx_req); apif->tx_req = NULL; vec_free (apif->host_if_name); apif->host_if_name = NULL; apif->host_if_index = -1; mhash_unset (&apm->if_index_by_host_if_name, host_if_name, &if_index); /* 将以太网端口删除 */ ethernet_delete_interface (vnm, apif->hw_if_index); pool_put (apm->interfaces, apif); return 0; }
解除收包线程
int vnet_hw_interface_unassign_rx_thread (vnet_main_t * vnm, u32 hw_if_index, u16 queue_id) { vlib_main_t *vm, *vm0; vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); vnet_device_input_runtime_t *rt; vnet_device_and_queue_t *dq; uword old_thread_index; vnet_hw_interface_rx_mode mode; if (hw->input_node_thread_index_by_queue == 0) return VNET_API_ERROR_INVALID_INTERFACE; if (vec_len (hw->input_node_thread_index_by_queue) < queue_id + 1) return VNET_API_ERROR_INVALID_INTERFACE; old_thread_index = hw->input_node_thread_index_by_queue[queue_id]; vm = vlib_mains[old_thread_index]; /* 从该输入节点的运行数据中删除hw->input_node_index */ rt = vlib_node_get_runtime_data (vm, hw->input_node_index); vec_foreach (dq, rt->devices_and_queues) if (dq->hw_if_index == hw_if_index && dq->queue_id == queue_id) { mode = dq->mode; goto delete; } return VNET_API_ERROR_INVALID_INTERFACE; delete: vm0 = vlib_get_main (); vlib_worker_thread_barrier_sync (vm0); vec_del1 (rt->devices_and_queues, dq - rt->devices_and_queues); vnet_device_queue_update (vnm, rt); hw->rx_mode_by_queue[queue_id] = VNET_HW_INTERFACE_RX_MODE_UNKNOWN; vlib_worker_thread_barrier_release (vm0); if (vec_len (rt->devices_and_queues) == 0)/*如果该类型的input节点没有了接口了,那么将该接口设置为disable*/ vlib_node_set_state (vm, hw->input_node_index, VLIB_NODE_STATE_DISABLED); else if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING) { /* * if the deleted interface is polling, we may need to set the node state * to interrupt if there is no more polling interface for this device's * corresponding thread. This is because mixed interfaces * (polling and interrupt), assigned to the same thread, set the * thread to polling prior to the deletion. */ vec_foreach (dq, rt->devices_and_queues) { if (dq->mode == VNET_HW_INTERFACE_RX_MODE_POLLING) return 0; } rt->enabled_node_state = VLIB_NODE_STATE_INTERRUPT; vlib_node_set_state (vm, hw->input_node_index, rt->enabled_node_state); } return 0; }
ethernet_delete_interface
void ethernet_delete_interface (vnet_main_t * vnm, u32 hw_if_index) { ethernet_main_t *em = ðernet_main; ethernet_interface_t *ei; vnet_hw_interface_t *hi; main_intf_t *main_intf; vlan_table_t *vlan_table; u32 idx; hi = vnet_get_hw_interface (vnm, hw_if_index); ei = pool_elt_at_index (em->interfaces, hi->hw_instance); /* Delete vlan mapping table for dot1q and dot1ad. */ main_intf = vec_elt_at_index (em->main_intfs, hi->hw_if_index); if (main_intf->dot1q_vlans) { vlan_table = vec_elt_at_index (em->vlan_pool, main_intf->dot1q_vlans); for (idx = 0; idx < ETHERNET_N_VLAN; idx++) { if (vlan_table->vlans[idx].qinqs) { pool_put_index (em->qinq_pool, vlan_table->vlans[idx].qinqs); vlan_table->vlans[idx].qinqs = 0; } } pool_put_index (em->vlan_pool, main_intf->dot1q_vlans); main_intf->dot1q_vlans = 0; } if (main_intf->dot1ad_vlans) { vlan_table = vec_elt_at_index (em->vlan_pool, main_intf->dot1ad_vlans); for (idx = 0; idx < ETHERNET_N_VLAN; idx++) { if (vlan_table->vlans[idx].qinqs) { pool_put_index (em->qinq_pool, vlan_table->vlans[idx].qinqs); vlan_table->vlans[idx].qinqs = 0; } } pool_put_index (em->vlan_pool, main_intf->dot1ad_vlans); main_intf->dot1ad_vlans = 0; } vnet_delete_hw_interface (vnm, hw_if_index); pool_put (em->interfaces, ei); }
删除硬件接口信息
void vnet_delete_hw_interface (vnet_main_t * vnm, u32 hw_if_index) { vnet_interface_main_t *im = &vnm->interface_main; vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); vlib_main_t *vm = vnm->vlib_main; vnet_device_class_t *dev_class = vnet_get_device_class (vnm, hw->dev_class_index); /* If it is up, mark it down. */ if (hw->flags != 0) vnet_hw_interface_set_flags (vnm, hw_if_index, /* flags */ 0); /* Call delete callbacks. */ call_hw_interface_add_del_callbacks (vnm, hw_if_index, /* is_create */ 0); /* Delete any sub-interfaces. */ { u32 id, sw_if_index; /* *INDENT-OFF* */ hash_foreach (id, sw_if_index, hw->sub_interface_sw_if_index_by_id, ( { vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index); u64 sup_and_sub_key = ((u64) (si->sup_sw_if_index) << 32) | (u64) si->sub.id; hash_unset_mem_free (&im->sw_if_index_by_sup_and_sub, &sup_and_sub_key); vnet_delete_sw_interface (vnm, sw_if_index); })); hash_free (hw->sub_interface_sw_if_index_by_id); /* *INDENT-ON* */ } /* Delete software interface corresponding to hardware interface. */ vnet_delete_sw_interface (vnm, hw->sw_if_index); if (dev_class->tx_function) { /* Put output/tx nodes into recycle pool */ vnet_hw_interface_nodes_t *dn; /* *INDENT-OFF* 遍历每一个线程,设置该节点的运行节点信息的标志位rt->is_deleted为1, * 而不是摘除该节点,提升处理速度。 */ foreach_vlib_main ( { vnet_interface_output_runtime_t *rt = vlib_node_get_runtime_data (this_vlib_main, hw->output_node_index); /* Mark node runtime as deleted so output node (if called) * will drop packets. */ rt->is_deleted = 1; }); /* *INDENT-ON* */ /* 重命名节点,会遍历所有的线程 */ vlib_node_rename (vm, hw->output_node_index, "interface-%d-output-deleted", hw_if_index); vlib_node_rename (vm, hw->tx_node_index, "interface-%d-tx-deleted", hw_if_index); /* 放置在 */ vec_add2 (im->deleted_hw_interface_nodes, dn, 1); dn->tx_node_index = hw->tx_node_index; dn->output_node_index = hw->output_node_index; } hash_unset_mem (im->hw_interface_by_name, hw->name); vec_free (hw->name); vec_free (hw->hw_address); vec_free (hw->input_node_thread_index_by_queue); vec_free (hw->dq_runtime_index_by_queue); pool_put (im->hw_interfaces, hw); }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 小程序端接口需求分析
- PicaComic接口分析手记
- socket接口层分析
- Golang之面向接口编程及使用分析
- srs之state thread库接口分析
- 「docker实战篇」python的docker-抖音分析接口数据分析(21)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
浪潮之巅(第三版)(上下册)
吴军 / 人民邮电出版社 / 2016-5 / 99.00元
一个企业的发展与崛起,绝非只是空有领导强人即可达成。任何的决策、同期的商业环境,都在都影响着企业的兴衰。《浪潮之巅》不只是一本历史书,除了讲述科技顶尖企业的发展规律,对于华尔街如何左右科技公司,以及金融风暴对科技产业的冲击,也多有着墨。此外,这本书也着力讲述很多尚在普及 或将要发生的,比如微博和云计算,以及对下一代互联网科技产业浪潮的判断和预测。因为在极度商业化的今天,科技的进步和商机是分不开的。......一起来看看 《浪潮之巅(第三版)(上下册)》 这本书的介绍吧!