ff_run函数详解

ff_run详解

  • ff_run(loop_func_t loop, void* arg)
    • loop为入口函数
    • arg为user pointer
        void ff_run(loop_func_t loop, void* arg)
        {
            ff_dpdk_run(loop, arg);
        }
    
  • ff_dpdk_run函数
        void ff_dpdk_run(loop_func_t loop, void *arg)
        {
            /**
              *保存回调指针和上下文
              */
            	struct loop_routine *lr = rte_malloc(NULL,
    			  sizeof(struct loop_routine), 0);
            	lr->loop = loop;
            	lr->arg = arg;
            	/**
            	  * launch a function on dpdk lcore
            	  * 内部会回调用户传入loop函数指针
            	  */
            	rte_eal_mp_remote_launch(main_loop, lr, CALL_MASTER);
            	rte_eal_mp_wait_lcore();
            	rte_free(lr);
        }
    
  • main_loop函数
    • dpdk lcore上实际入口
    • 功能
      • 定时器管理
      • pcap处理
      • 刷新tx缓冲
      • rx数据包
        • 通过dispatch_ring传递过来的数据包
        • 当前queue接收的数据包
      • msg_ring消息处理
      • 用户loop回调
        static int main_loop(void *arg)
        {
        	struct loop_routine *lr = (struct loop_routine *)arg;
        	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
        	uint64_t prev_tsc, diff_tsc, cur_tsc, usch_tsc, div_tsc, usr_tsc, sys_tsc, end_tsc, idle_sleep_tsc;
        	int i, j, nb_rx, idle;
        	uint16_t port_id, queue_id;
        	struct lcore_conf *qconf;
        	/**
        	  *计算间隔时间100us对应tick数
        	  */
        	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) /
        							   US_PER_S * BURST_TX_DRAIN_US;
        	struct ff_dpdk_if_context *ctx;
        	prev_tsc = 0;
        	usch_tsc = 0;
        	qconf = &lcore_conf; //lcore分配信息
        	while (1) {
        		/**
        		 * 每经过一个时钟周期,tsc寄存器就自动加1, rte_rdtsc()只是获得tsc寄存器的值
        		 * rte_get_tsc_hz() 返回一秒的tsc数目
        		 * CPU MHz为1600,那么tsc的1就是1/1600/1000/1000的时间
        		 */
        		cur_tsc = rte_rdtsc();//获取当前时钟数
        		if (unlikely(freebsd_clock.expire < cur_tsc)) {
        			//定时器到期,管理定时器列表并执行定时器回调函数
        			rte_timer_manage();
        		}
        		/**
        		 * idel=1:表示没有新的数据包发送或者接收
        		 * idle=0:表示有新的数据达到或者发送,可能有感兴趣时间触发,需要调用用户回调
        		 */
        		idle = 1;
        		sys_tsc = 0;
        		usr_tsc = 0;
        		/**
        		 * 数据实际发送至物理网卡不是有数据包就立即发送,而是间隔100us使用burst方式发送
        		 */
        		diff_tsc = cur_tsc - prev_tsc;
        		if (unlikely(diff_tsc > drain_tsc)) {
        			/**
        			 * 发送间隔到期,遍历所有的port,发送缓存的需要的数据包
        			 */
        			for (i = 0; i < qconf->nb_tx_port; i++) {
        				port_id = qconf->tx_port_id[i];
        				if (qconf->tx_mbufs[port_id].len == 0)
        					continue;
        				idle = 0;
        				/**
        				 * 1. 如果启用pcap功能,记录tx数据包
        				 * 2.内部调用rte_eth_tx_burst发送数据包
        				 */
        				send_burst(qconf,
        					qconf->tx_mbufs[port_id].len,
        					port_id);
        				qconf->tx_mbufs[port_id].len = 0;
        			}
        			/**
        			 * 更新prev_tsc时钟
        			 */
        			prev_tsc = cur_tsc;
        		}
        		/**
        		 * 处理rx队列上数据包,包括该lcore对应queueid上数据包,和通过dispatcher分发的数据包
        		 * 遍历proc_id处理的所有网卡队列
        		 */
        		for (i = 0; i < qconf->nb_rx_queue; ++i) {
        			port_id = qconf->rx_queue_list[i].port_id;
        			queue_id = qconf->rx_queue_list[i].queue_id;
        			/**
        			 * 获取协议栈相关上下文struct ff_dpdk_if_context*
        			 */
        			ctx = veth_ctx[port_id];
        #ifdef FF_KNI
        			/**
        			 * 如果开启了kni功能,primary lcore核上处理需要上传给kni模块数据包
        			 */
        			if (enable_kni && rte_eal_process_type() == RTE_PROC_PRIMARY) {
        			    /**
        			      *1. 从kni_rp上获取数据包,上传至kni模块
        			      *2. 将kni模块处理返回数据包发送至物理网卡
        			      */
        				ff_kni_process(port_id, queue_id, pkts_burst, MAX_PKT_BURST);
        			}
        #endif
                /**
                  *处理其它lcore上分发过来的rx数据包
                  */
        			process_dispatch_ring(port_id, queue_id, pkts_burst, ctx);
        			/**
        			 * 从网卡队列读取数据包
        			 */
        			nb_rx = rte_eth_rx_burst(port_id, queue_id, pkts_burst,
        					MAX_PKT_BURST);
        			if (nb_rx == 0)
        				continue;
        			idle = 0;
        			/**
        			 * 处理新收到的每个数据包
        			 */
        			/* Prefetch first packets */
        			for (j = 0; j < PREFETCH_OFFSET && j < nb_rx; j++) {
        				rte_prefetch0(rte_pktmbuf_mtod(
        						pkts_burst[j], void *));
        			}
        			/* Prefetch and handle already prefetched packets */
        			for (j = 0; j < (nb_rx - PREFETCH_OFFSET); j++) {
        				rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[
        							j + PREFETCH_OFFSET], void *));
        				//最后一个参数为0,表示数据包不是从dispatch_ring上获取到的
        				process_packets(port_id, queue_id, &pkts_burst[j], 1, ctx, 0);
        			}
        			/* Handle remaining prefetched packets */
        			for (; j < nb_rx; j++) {
        				process_packets(port_id, queue_id, &pkts_burst[j], 1, ctx, 0);
        			}
        		}
        		/**
        		  *处理控制消息数据
        		  */
        		process_msg_ring(qconf->proc_id);
        		div_tsc = rte_rdtsc();
        		/**
        		  *调用用户回调函数,调用条件为
        		  *1)有新的数据处理过
        		  *2)距离上次回调超过100us
        		  */
        		if (likely(lr->loop != NULL && (!idle || cur_tsc - usch_tsc > drain_tsc))) {
        			usch_tsc = cur_tsc;//更新用户上次回调事件,最大间隔为100us
        			lr->loop(lr->arg);
        		}
        		/**
        		 * 1.如果设置了idle_sleep,并且当前没有新的数据包,睡眠
        		 */
        		idle_sleep_tsc = rte_rdtsc();
        		if (likely(idle && idle_sleep)) {
        			usleep(idle_sleep);
        			end_tsc = rte_rdtsc();
        		} else {
        			end_tsc = idle_sleep_tsc;
        		}
        		end_tsc = rte_rdtsc();
        		if (usch_tsc == cur_tsc) {
        			usr_tsc = idle_sleep_tsc - div_tsc;
        		}
        		if (!idle) {
        			sys_tsc = div_tsc - cur_tsc;
        			ff_top_status.sys_tsc += sys_tsc;
        		}
        		ff_top_status.usr_tsc += usr_tsc;
        		ff_top_status.work_tsc += end_tsc - cur_tsc;
        		ff_top_status.idle_tsc += end_tsc - cur_tsc - usr_tsc - sys_tsc;
        		ff_top_status.loops++;
        	}
        	return 0;
        }
    
  • process_packets
          static inline void process_packets(uint16_t port_id, uint16_t queue_id, struct rte_mbuf **bufs,
        	uint16_t count, const struct ff_dpdk_if_context *ctx, int pkts_from_ring)
        {
        	struct lcore_conf *qconf = &lcore_conf;
        	uint16_t nb_queues = qconf->nb_queue_list[port_id];//获取该网卡开启的队列数
        	uint16_t i;
        	for (i = 0; i < count; i++) {
        		struct rte_mbuf *rtem = bufs[i];
        		if (unlikely(qconf->pcap[port_id] != NULL)) {
    		   /**
    				* 1.如果pkts_from_ring=0,则表示bufs是从lcore对应queueid接收数据包
    				* 2. 如果pcap功能开启的话,记录数据包
    				*/
        			if (!pkts_from_ring) {
        				ff_dump_packets(qconf->pcap[port_id], rtem);
        			}
        		}
        		void *data = rte_pktmbuf_mtod(rtem, void *);//data指向数据包的起始位置
        		uint16_t len = rte_pktmbuf_data_len(rtem);//获取数据包的长度
        		if (!pkts_from_ring) {
        			//更新rx统计信息
        			ff_traffic.rx_packets++;
        			ff_traffic.rx_bytes += len;
        		}
        		/**
        		 * packet_dispatcher为用户设置的自定义分发函数
        		 * FF_DISPATCH_ERROR:错误数据包,直接丢弃
        		 * FF_DISPATCH_RESPONSE:用户已经处理完成
        		 * 返回0~nb_queues-1:将该数据包分发到返回的queueid上处理
        		 */
        		if (!pkts_from_ring && packet_dispatcher) {
        			int ret = (*packet_dispatcher)(data, &len, queue_id, nb_queues);
        			/**
        				 * 如果用户已经处理完该数据包,直接将该数据包缓存在发送缓冲中
        				 */
        			if (ret == FF_DISPATCH_RESPONSE) {
        				rte_pktmbuf_pkt_len(rtem) = rte_pktmbuf_data_len(rtem) = len;
        				send_single_packet(rtem, port_id);
        				continue;
        			}
        			if (ret == FF_DISPATCH_ERROR || ret >= nb_queues) {
        				/**
        				 * 如果返回错误,则该数据包直接丢弃,不会交由freebsd协议栈处理
        				 */
        				rte_pktmbuf_free(rtem);
        				continue;
        			}
        			/**
        			 * 如果返回ret与接收queue_id不相等,即该数据包需要交由其它lcore处理,将该数据包加入对应dispatch_ring
        			 */
        			if (ret != queue_id) {
        				ret = rte_ring_enqueue(dispatch_ring[port_id][ret], rtem);
        				if (ret < 0)
        					rte_pktmbuf_free(rtem);
        				continue;
        			}
        		}
        		enum FilterReturn filter = protocol_filter(data, len);
        		if (filter == FILTER_ARP) {
        			/**
        			 * 如果数据包是ARP数据包,则将其广播至所有的队列上
        			 */
        			struct rte_mempool *mbuf_pool;
        			struct rte_mbuf *mbuf_clone;
        			if (!pkts_from_ring) {
        				uint16_t j;
        				for (j = 0; j < nb_queues; ++j) {
        					if (j == queue_id)
        						continue;
        					unsigned socket_id = 0;
        					if (numa_on) {
        						uint16_t lcore_id = qconf->port_cfgs[port_id].lcore_list[j];
        						socket_id = rte_lcore_to_socket_id(lcore_id);
        					}
        					mbuf_pool = pktmbuf_pool[socket_id];
        					//深度拷贝数据包,主要需要注意rte_mbuf分段情况
        					mbuf_clone = pktmbuf_deep_clone(rtem, mbuf_pool);
        					if (mbuf_clone) {
        						int ret = rte_ring_enqueue(dispatch_ring[port_id][j],
        								  mbuf_clone);
        						if (ret < 0)
        							rte_pktmbuf_free(mbuf_clone);
        					}
        				}
        			}
        #ifdef FF_KNI
             //ARP数据包copy至kni模块,因为目前kni模块只区分tcp/udp端口
        			if (enable_kni && rte_eal_process_type() == RTE_PROC_PRIMARY) {
        				mbuf_pool = pktmbuf_pool[qconf->socket_id];
        				mbuf_clone = pktmbuf_deep_clone(rtem, mbuf_pool);
        				if (mbuf_clone) {
        					ff_kni_enqueue(port_id, mbuf_clone);
        				}
        			}
        #endif
             //数据包传递至freebsd协议栈处理
        			ff_veth_input(ctx, rtem);
        #ifdef FF_KNI
        		} else if (enable_kni &&
        			((filter == FILTER_KNI && kni_accept) ||
        				(filter == FILTER_UNKNOWN && !kni_accept))) {
        			//如果是kni处理数据包,push进kni对应ring队列中
        			ff_kni_enqueue(port_id, rtem);
        #endif
        		} else {
        		    //交由freebsd协议栈处理
        			ff_veth_input(ctx, rtem);
        		}
        	}
        }
    
  • ff_veth_input
    • 该函数主要将rte_mbuf转化成freebsd协议栈中mbuf数据结构
    • 将mbuf递交给ether_input处理
        static void ff_veth_input(const struct ff_dpdk_if_context *ctx, struct rte_mbuf *pkt)
        {
        	/**
        	 * 如果开启了校验和检验硬件负载功能,检查mbuf的标志
        	 */
        	uint8_t rx_csum = ctx->hw_features.rx_csum;
        	if (rx_csum) {
        		if (pkt->ol_flags & (PKT_RX_IP_CKSUM_BAD | PKT_RX_L4_CKSUM_BAD)) {
        			rte_pktmbuf_free(pkt);
        			return;
        		}
        	}
        	//data指向实际数据开端
        	void *data = rte_pktmbuf_mtod(pkt, void*);
        	//len表示数据包长度
        	uint16_t len = rte_pktmbuf_data_len(pkt);
        	/**
        	 * 将dpdk框架下rte_mbuf转换成freebsd协议栈上mbuf结构
        	 * 1. 初始化mbuf结构
        	 * 2.将rte_mbuf结构与mbuf结构关联起来,mbuf->m_ext实际数据指向data,零copy
        	 */
        	void *hdr = ff_mbuf_gethdr(pkt, pkt->pkt_len, data, len, rx_csum);
        	if (hdr == NULL) {
        		rte_pktmbuf_free(pkt);
        		return;
        	}
        	if (pkt->ol_flags & PKT_RX_VLAN_STRIPPED) {
        		/**
        		 * 如果vlan头被剥离,设置mbuf中vlan信息
        		 */
        		ff_mbuf_set_vlan_info(hdr, pkt->vlan_tci);
        	}
        	/**
        	 * 处理rte_mbuf分段,在mbuf中将分段数据通过m_next指针连接
        	 */
        	struct rte_mbuf *pn = pkt->next;
        	void *prev = hdr;
        	while (pn != NULL) {
        		data = rte_pktmbuf_mtod(pn, void *);
        		len = rte_pktmbuf_data_len(pn);
        		void *mb = ff_mbuf_get(prev, data, len);
        		if (mb == NULL) {
        			ff_mbuf_free(hdr);
        			rte_pktmbuf_free(pkt);
        			return;
        		}
        		pn = pn->next;
        		prev = mb;
        	}
        	/**
        	 * 调用ifnet->if_input函数交由freebsd协议处理,实际处理函数为ether_input
        	 */
        	ff_veth_process_packet(ctx->ifp, hdr);
        }
    
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值