zoukankan      html  css  js  c++  java
  • DPDK QoS_meter 源码阅读

    main.c

    /* SPDX-License-Identifier: BSD-3-Clause
     * Copyright(c) 2010-2016 Intel Corporation
     */
    
    #include <stdio.h>
    #include <getopt.h>
    
    #include <rte_common.h>
    #include <rte_eal.h>
    #include <rte_malloc.h>
    #include <rte_mempool.h>
    #include <rte_ethdev.h>
    #include <rte_cycles.h>
    #include <rte_mbuf.h>
    #include <rte_meter.h>
    
    /*
     * Traffic metering configuration
     *
     */
    #define APP_MODE_FWD                    0
    #define APP_MODE_SRTCM_COLOR_BLIND      1
    #define APP_MODE_SRTCM_COLOR_AWARE      2
    #define APP_MODE_TRTCM_COLOR_BLIND      3
    #define APP_MODE_TRTCM_COLOR_AWARE      4
    
    #define APP_MODE	APP_MODE_SRTCM_COLOR_BLIND // 手动设定 选取 srTCM的色盲模式。
    
    
    #include "main.h"
    
    
    #define APP_PKT_FLOW_POS                33
    #define APP_PKT_COLOR_POS               5
    
    
    #if APP_PKT_FLOW_POS > 64 || APP_PKT_COLOR_POS > 64
    #error Byte offset needs to be less than 64
    #endif
    
    /*
     * Buffer pool configuration
     *
     ***/
    #define NB_MBUF             8192
    #define MEMPOOL_CACHE_SIZE  256
    
    static struct rte_mempool *pool = NULL;
    
    /*
     * NIC configuration
     *
     ***/
    static struct rte_eth_conf port_conf = { // 端口配置信息
    	.rxmode = { // rx 侧
    		.mq_mode	= ETH_MQ_RX_RSS, // .mq_mode:多队列模式,后面的enum是RSS开启的多队列模式
    		.max_rx_pkt_len = ETHER_MAX_LEN,  // 能接受的最大pkt长度,当 JUMBO_FRAME 启用时生效。
    		.split_hdr_size = 0, // hdr buf size (header_split enabled).
    		.offloads = (DEV_RX_OFFLOAD_CHECKSUM |
    			     DEV_RX_OFFLOAD_CRC_STRIP), // 网卡offload就是把一些操作,转移到专用硬件上(比如网卡),从而释放CPU资源。这里就是校验和的计算。
    	},
    	.rx_adv_conf = { // RX filtering configuration.
    		.rss_conf = { // RSS configuration
    			.rss_key = NULL, // If not NULL, 40-byte hash key. 否则使用默认的key
    			.rss_hf = ETH_RSS_IP, // Hash functions to apply. 这里就是会把IP包分到特定队列。
    		},
    	},
    	.txmode = { // tx 侧
    		.mq_mode = ETH_DCB_NONE, // It is in neither DCB nor VT mode.
    	},
    };
    
    #define NIC_RX_QUEUE_DESC               1024
    #define NIC_TX_QUEUE_DESC               1024
    
    #define NIC_RX_QUEUE                    0
    #define NIC_TX_QUEUE                    0
    
    /*
     * Packet RX/TX
     *
     ***/
    #define PKT_RX_BURST_MAX                32
    #define PKT_TX_BURST_MAX                32
    #define TIME_TX_DRAIN                   200000ULL
    
    static uint16_t port_rx;
    static uint16_t port_tx;
    static struct rte_mbuf *pkts_rx[PKT_RX_BURST_MAX];
    struct rte_eth_dev_tx_buffer *tx_buffer;
    
    struct rte_meter_srtcm_params app_srtcm_params = {
    	.cir = 1000000 * 46, // 令牌桶每秒增加的令牌数量,单位字节
    	.cbs = 2048, // 令牌桶C的最大大小
    	.ebs = 2048  // 令牌桶E的最大大小
    };
    
    struct rte_meter_srtcm_profile app_srtcm_profile;
    
    struct rte_meter_trtcm_params app_trtcm_params = {
    	.cir = 1000000 * 46, // 令牌桶C的增长速率,单位字节每秒
    	.pir = 1500000 * 46, // 令牌桶P的增长速率
    	.cbs = 2048, // 令牌桶C的最大大小
    	.pbs = 2048  // 令牌桶P的最大大小
    };
    
    struct rte_meter_trtcm_profile app_trtcm_profile;
    
    #define APP_FLOWS_MAX  256
    
    FLOW_METER app_flows[APP_FLOWS_MAX];
    // 一种flow对应一组令牌桶。
    
    // FLOW_METER: 根据选取的算法选择一种结构体,里面存放的信息是令牌桶的可用bytes
    // #define FLOW_METER  int
    // #define FLOW_METER  struct rte_meter_trtcm
    // #define FLOW_METER  struct rte_meter_srtcm
    
    static int
    app_configure_flow_table(void)
    {
    	uint32_t i;
    	int ret;
    	/*rte_meter_srtcm_profile_config() 配置srTCM算法的参数到profile
    	参数两个
    	1. rte_meter_srtcm_profile * ,也就是profile指针
    	2. rte_meter_srtcm_params * 类型,是srTCM的三个参数。
    	*/
    	ret = rte_meter_srtcm_profile_config(&app_srtcm_profile,
    		&app_srtcm_params);
    	if (ret) // 返回值 0 代表配置成功
    		return ret;
    
    	// 配置 trTCM 算法
    	ret = rte_meter_trtcm_profile_config(&app_trtcm_profile,
    		&app_trtcm_params);
    	if (ret)
    		return ret;
    
    	// 用到的宏定义见main.h
    	for (i = 0; i < APP_FLOWS_MAX; i++) {
    		/* rte_meter_srtcm_config() 和  rte_meter_trtcm_config() 
    		对每一个限速的flow进行scTCM/trtcm配置。
    		参数1:struct rte_meter_srtcm * 或 struct rte_meter_trtcm *
    		参数2:有效的profile指针。
    		*/
    		ret = FUNC_CONFIG(&app_flows[i], &PROFILE); 
    		if (ret)
    			return ret;
    	}
    	
    
    	return 0;
    }
    
    static inline void
    app_set_pkt_color(uint8_t *pkt_data, enum policer_action color) // 为packet标记上Meter计算出来的颜色
    {
    	pkt_data[APP_PKT_COLOR_POS] = (uint8_t)color;
    }
    
    static inline int
    app_pkt_handle(struct rte_mbuf *pkt, uint64_t time)
    {
    	uint8_t input_color, output_color;
    	uint8_t *pkt_data = rte_pktmbuf_mtod(pkt, uint8_t *); // rte_pktmbuf_mtod:返回 mbuf 中 packet data 的起始地址
    	uint32_t pkt_len = rte_pktmbuf_pkt_len(pkt) - sizeof(struct ether_hdr); // 计算去掉以太网头部(14字节)的包长度
    	uint8_t flow_id = (uint8_t)(pkt_data[APP_PKT_FLOW_POS] & (APP_FLOWS_MAX - 1));
    	input_color = pkt_data[APP_PKT_COLOR_POS];  // 宏:APP_PKT_COLOR_POS为5。这个数组是uint8_t的,所以颜色就是 pkt_data[5]
    	// “为了简化调试,颜色被定义为目标MAC地址的LSB(最低有效位)字节。”—— sample guide
    	enum policer_action action;
    
    	/* color input is not used for blind modes */
    	// FUNC_METER: rte_meter_srtcm_color_blind_check() 或 aware_check,执行算法的限速,返回值是处理结果的颜色。	
    	output_color = (uint8_t) FUNC_METER(&app_flows[flow_id],
    		&PROFILE, // profile指针
    		time, // 当前CPU的时间戳
    		pkt_len, // IP pkt 的长度
    		(enum rte_meter_color) input_color); // 若是非色盲模式,第五个参数是 input color
    
    	/* Apply policing and set the output color */
    	action = policer_table[input_color][output_color];
    	// policer_table 是一个二维数组,见main.h
    
    	app_set_pkt_color(pkt_data, action); // marker
    
    	return action;
    }
    
    
    static __attribute__((noreturn)) int
    main_loop(__attribute__((unused)) void *dummy)
    {
    	uint64_t current_time, last_time = rte_rdtsc(); // 获取从开机起至当前的时间戳
    	uint32_t lcore_id = rte_lcore_id(); // 获取自己的 lcore id
    
    	printf("Core %u: port RX = %d, port TX = %d
    ", lcore_id, port_rx, port_tx);
    
    	while (1) {
    		uint64_t time_diff;
    		int i, nb_rx;
    
    		/* Mechanism to avoid stale packets in the output buffer */
    		current_time = rte_rdtsc(); // 获取从开机起至当前的时间戳
    		time_diff = current_time - last_time; 
    		if (unlikely(time_diff > TIME_TX_DRAIN)) {// 时间到了
    			/* Flush tx buffer */
    			rte_eth_tx_buffer_flush(port_tx, NIC_TX_QUEUE, tx_buffer); // 将 buffer 里的 pkt 全部从 port id为 port_tx 的 0号 Tx queue 发出去
    			last_time = current_time;
    		}
    
    		/* Read packet burst from NIC RX */
    		// 收包,在port_rx端口上,0号队列
    		nb_rx = rte_eth_rx_burst(port_rx, NIC_RX_QUEUE, pkts_rx, PKT_RX_BURST_MAX);
    
    		/* Handle packets */
    		for (i = 0; i < nb_rx; i ++) {
    			struct rte_mbuf *pkt = pkts_rx[i]; // 操作该包的方法:收包之后创建mbuf结构体指针
    
    			/* Handle current packet */
    			if (app_pkt_handle(pkt, current_time) == DROP) // 收到包之后,对其进行 QoS meter,若为DROP,就丢弃
    				rte_pktmbuf_free(pkt); 
    			else
    				rte_eth_tx_buffer(port_tx, NIC_TX_QUEUE, tx_buffer, pkt); // 其余颜色都是普通的转发。本实例没有针对其余两种颜色设计更多的逻辑。
    		}
    	}
    }
    
    static void
    print_usage(const char *prgname)
    {
    	printf ("%s [EAL options] -- -p PORTMASK
    "
    		"  -p PORTMASK: hexadecimal bitmask of ports to configure
    ",
    		prgname);
    }
    
    static int
    parse_portmask(const char *portmask)
    {
    	char *end = NULL;
    	unsigned long pm;
    
    	/* parse hexadecimal string */
    	pm = strtoul(portmask, &end, 16);
    	if ((portmask[0] == '') || (end == NULL) || (*end != ''))
    		return -1;
    
    	if (pm == 0)
    		return -1;
    
    	return pm;
    }
    
    /* Parse the argument given in the command line of the application */
    static int
    parse_args(int argc, char **argv)
    {
    	int opt;
    	char **argvopt;
    	int option_index;
    	char *prgname = argv[0];
    	static struct option lgopts[] = {
    		{NULL, 0, 0, 0}
    	};
    	uint64_t port_mask, i, mask;
    
    	argvopt = argv;
    
    	while ((opt = getopt_long(argc, argvopt, "p:", lgopts, &option_index)) != EOF) {
    		switch (opt) {
    		case 'p': // 端口掩码
    			port_mask = parse_portmask(optarg);
    			if (port_mask == 0) { // 返回值是16进制的数字
    				printf("invalid port mask (null port mask)
    ");
    				print_usage(prgname);
    				return -1;
    			}
    
    			for (i = 0, mask = 1; i < 64; i ++, mask <<= 1){
    				if (mask & port_mask){ // 分配一个用于 rx 的 port
    					port_rx = i;
    					port_mask &= ~ mask;
    					break;
    				}
    			}
    
    			for (i = 0, mask = 1; i < 64; i ++, mask <<= 1){
    				if (mask & port_mask){ // 分配一个用于 tx 的 port
    					port_tx = i;
    					port_mask &= ~ mask;
    					break;
    				}
    			}
    
    			if (port_mask != 0) {
    				printf("invalid port mask (more than 2 ports)
    "); // 该程序只需要两个port
    				print_usage(prgname);
    				return -1;
    			}
    			break;
    
    		default:
    			print_usage(prgname);
    			return -1;
    		}
    	}
    
    	if (optind <= 1) {
    		print_usage(prgname);
    		return -1;
    	}
    
    	argv[optind-1] = prgname;
    
    	optind = 1; /* reset getopt lib */
    	return 0;
    }
    
    int
    main(int argc, char **argv)
    {
    	uint32_t lcore_id;
    	uint16_t nb_rxd = NIC_RX_QUEUE_DESC;
    	uint16_t nb_txd = NIC_TX_QUEUE_DESC;
    	struct rte_eth_conf conf;
    	struct rte_eth_rxconf rxq_conf;
    	struct rte_eth_txconf txq_conf;
    	struct rte_eth_dev_info dev_info;
    	int ret;
    
    	/* EAL init */ // 初始化 EAL
    	ret = rte_eal_init(argc, argv);
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Invalid EAL parameters
    ");
    	argc -= ret;
    	argv += ret;
    	if (rte_lcore_count() != 1) { // 本程序只需要一个逻辑核心
    		rte_exit(EXIT_FAILURE, "This application does not accept more than one core. "
    		"Please adjust the "-c COREMASK" parameter accordingly.
    ");
    	}
    
    	/* Application non-EAL arguments parse */
    	ret = parse_args(argc, argv);
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Invalid input arguments
    ");
    
    	/* Buffer pool init */
    	// 创建缓存池
    	pool = rte_pktmbuf_pool_create("pool", NB_MBUF, MEMPOOL_CACHE_SIZE,
    		0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
    	if (pool == NULL)
    		rte_exit(EXIT_FAILURE, "Buffer pool creation error
    ");
    
    	/* NIC init */
    	conf = port_conf;
    	rte_eth_dev_info_get(port_rx, &dev_info); // 查询rx网口的信息,网卡的各种功能都可以通过此结构体访问
    
    	if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
    		conf.txmode.offloads |= DEV_TX_OFFLOAD_MBUF_FAST_FREE; // 如果有 mbuf fast free 功能就开启
    
    	conf.rx_adv_conf.rss_conf.rss_hf &= dev_info.flow_type_rss_offloads; // 配置成网卡支持的rss设置
    	if (conf.rx_adv_conf.rss_conf.rss_hf !=
    			port_conf.rx_adv_conf.rss_conf.rss_hf) { // 若更改了rss配置,给出一条提示信息
    		printf("Port %u modified RSS hash function based on hardware support,"
    			"requested:%#"PRIx64" configured:%#"PRIx64"
    ",  // #define PRIx64    "llx"
    			port_rx,
    			port_conf.rx_adv_conf.rss_conf.rss_hf,
    			conf.rx_adv_conf.rss_conf.rss_hf);
    	}
    
    	ret = rte_eth_dev_configure(port_rx, 1, 1, &conf); // 为 rx 口配置收发队列各一条
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Port %d configuration error (%d)
    ", port_rx, ret);
    
    	ret = rte_eth_dev_adjust_nb_rx_tx_desc(port_rx, &nb_rxd, &nb_txd);
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Port %d adjust number of descriptors error (%d)
    ",
    				port_rx, ret);
    
    	rxq_conf = dev_info.default_rxconf;
    	rxq_conf.offloads = conf.rxmode.offloads;
    	ret = rte_eth_rx_queue_setup(port_rx, NIC_RX_QUEUE, nb_rxd,
    				rte_eth_dev_socket_id(port_rx),
    				&rxq_conf, pool); // 为 rx port setup rx queue。
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Port %d RX queue setup error (%d)
    ", port_rx, ret);
    
    	txq_conf = dev_info.default_txconf;
    	txq_conf.offloads = conf.txmode.offloads;
    	ret = rte_eth_tx_queue_setup(port_rx, NIC_TX_QUEUE, nb_txd,
    				rte_eth_dev_socket_id(port_rx),// 为 rx port setup tx queue。
    				&txq_conf);
    	if (ret < 0)
    	rte_exit(EXIT_FAILURE, "Port %d TX queue setup error (%d)
    ", port_rx, ret);
    
    	conf = port_conf;
    	rte_eth_dev_info_get(port_tx, &dev_info); // 获取 tx port 的设备信息
    	if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) // 开启 tx port 的 mbuf fast free
    		conf.txmode.offloads |= DEV_TX_OFFLOAD_MBUF_FAST_FREE;
    
    	conf.rx_adv_conf.rss_conf.rss_hf &= dev_info.flow_type_rss_offloads; // 查看 tx port 的 rss 配置是否满足要求
    	if (conf.rx_adv_conf.rss_conf.rss_hf !=
    			port_conf.rx_adv_conf.rss_conf.rss_hf) {
    		printf("Port %u modified RSS hash function based on hardware support,"
    			"requested:%#"PRIx64" configured:%#"PRIx64"
    ",
    			port_tx,
    			port_conf.rx_adv_conf.rss_conf.rss_hf,
    			conf.rx_adv_conf.rss_conf.rss_hf);
    	}
    
    	ret = rte_eth_dev_configure(port_tx, 1, 1, &conf); // 为 tx port 配置收发队列各一条
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Port %d configuration error (%d)
    ", port_tx, ret);
    
    	nb_rxd = NIC_RX_QUEUE_DESC;
    	nb_txd = NIC_TX_QUEUE_DESC;
    	ret = rte_eth_dev_adjust_nb_rx_tx_desc(port_tx, &nb_rxd, &nb_txd);
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Port %d adjust number of descriptors error (%d)
    ",
    				port_tx, ret);
    
    	rxq_conf = dev_info.default_rxconf;
    	rxq_conf.offloads = conf.rxmode.offloads;
    	ret = rte_eth_rx_queue_setup(port_tx, NIC_RX_QUEUE, nb_rxd,
    				rte_eth_dev_socket_id(port_tx), // 为 tx port setup rx queue
    				NULL, pool);
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Port %d RX queue setup error (%d)
    ", port_tx, ret);
    
    	txq_conf = dev_info.default_txconf;
    	txq_conf.offloads = conf.txmode.offloads;
    	ret = rte_eth_tx_queue_setup(port_tx, NIC_TX_QUEUE, nb_txd,
    				rte_eth_dev_socket_id(port_tx), // 为 tx port setup tx queue
    				NULL);
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Port %d TX queue setup error (%d)
    ", port_tx, ret);
    
    	// 为 tx 分配 buffer
    	tx_buffer = rte_zmalloc_socket("tx_buffer",
    			RTE_ETH_TX_BUFFER_SIZE(PKT_TX_BURST_MAX), 0,
    			rte_eth_dev_socket_id(port_tx));
    	if (tx_buffer == NULL)
    		rte_exit(EXIT_FAILURE, "Port %d TX buffer allocation error
    ",
    				port_tx);
    
    	rte_eth_tx_buffer_init(tx_buffer, PKT_TX_BURST_MAX);
    
    	ret = rte_eth_dev_start(port_rx);
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Port %d start error (%d)
    ", port_rx, ret);
    
    	ret = rte_eth_dev_start(port_tx);
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Port %d start error (%d)
    ", port_tx, ret);
    
    	rte_eth_promiscuous_enable(port_rx);
    
    	rte_eth_promiscuous_enable(port_tx);
    
    	/* App configuration */
    	ret = app_configure_flow_table();
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Invalid configure flow table
    ");
    
    	/* Launch per-lcore init on every lcore */
    	rte_eal_mp_remote_launch(main_loop, NULL, CALL_MASTER);
    	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
    		if (rte_eal_wait_lcore(lcore_id) < 0)
    			return -1;
    	}
    
    	return 0;
    }
    
    

    main.h

    
    /* SPDX-License-Identifier: BSD-3-Clause
     * Copyright(c) 2010-2014 Intel Corporation
     */
    
    #ifndef _MAIN_H_
    #define _MAIN_H_
    
    enum policer_action {
            GREEN = e_RTE_METER_GREEN,
            YELLOW = e_RTE_METER_YELLOW,
            RED = e_RTE_METER_RED,
            DROP = 3,
    };
    
    // 其中行表示输入颜色,列表示输出颜色,值指示要针对该特定情况采取的操作
    enum policer_action policer_table[e_RTE_METER_COLORS][e_RTE_METER_COLORS] =
    {
    	{ GREEN, RED, RED},
    	{ DROP, YELLOW, RED},
    	{ DROP, DROP, RED}
    };
    // GREEN,YELLOW,RED表示tag绿,黄,红。DROP就表示丢包
    // 每一个输入和输出颜色相同的包,都保持相同的颜色。
    // 每个颜色改进(变成更好的颜色)的包都会被丢弃(但这种特殊情况不会发生,因此不会使用这些值)。
    // 其余情况下,颜色改为红色。
    
    #if APP_MODE == APP_MODE_FWD // forward 模式
    
    #define FUNC_METER(m, p, time, pkt_len, pkt_color)	
    ({							
    	void *mp = m;					
    	void *pp = p;					
    	mp = mp;					
    	pp = pp;					
    	time = time;					
    	pkt_len = pkt_len;				
    	pkt_color;					
    })
    #define FUNC_CONFIG(a, b) 0
    #define FLOW_METER int
    #define PROFILE	app_srtcm_profile
    
    #elif APP_MODE == APP_MODE_SRTCM_COLOR_BLIND //  srTCM算法,色盲模式
    
    #define FUNC_METER(m, p, time, pkt_len, pkt_color)	
    	rte_meter_srtcm_color_blind_check(m, p, time, pkt_len)
    #define FUNC_CONFIG   rte_meter_srtcm_config
    #define FLOW_METER    struct rte_meter_srtcm
    #define PROFILE       app_srtcm_profile
    
    #elif (APP_MODE == APP_MODE_SRTCM_COLOR_AWARE) // srTCM算法,非色盲模式
    
    #define FUNC_METER    rte_meter_srtcm_color_aware_check
    #define FUNC_CONFIG   rte_meter_srtcm_config
    #define FLOW_METER    struct rte_meter_srtcm
    #define PROFILE       app_srtcm_profile
    
    #elif (APP_MODE == APP_MODE_TRTCM_COLOR_BLIND) // trTCM算法,色盲模式
    
    #define FUNC_METER(m, p, time, pkt_len, pkt_color)	
    	rte_meter_trtcm_color_blind_check(m, p, time, pkt_len)
    #define FUNC_CONFIG  rte_meter_trtcm_config
    #define FLOW_METER   struct rte_meter_trtcm
    #define PROFILE      app_trtcm_profile
    
    #elif (APP_MODE == APP_MODE_TRTCM_COLOR_AWARE) // trTCM算法,非色盲模式
    
    #define FUNC_METER rte_meter_trtcm_color_aware_check
    #define FUNC_CONFIG  rte_meter_trtcm_config
    #define FLOW_METER   struct rte_meter_trtcm
    #define PROFILE      app_trtcm_profile
    
    #else
    #error Invalid value for APP_MODE
    #endif
    
    #endif /* _MAIN_H_ */
    
    

    这是一个在简单的forwarding基础上基于srTCM或trTCM的QoS Meter程序,使用两个端口,每个端口都有一条收发队列。DPDK的API能配置srTCM或trTCM算法的参数,再用DPDK的API进行限速:将packet的长度进行对应的令牌桶算法,返回特定的颜色。到此,RFC上的算法的实现都由DPDK API完成了,之后对应的逻辑需要自行编码设定,典型的RFC中说的就是红色就丢包,当然本sample也给了另一种情况,可以看看。这部分是可以编程的地方,不过可能需要比较复杂的调试。

    颜色本来是应该放在IP header中的DS字段的,但本sample guide中说:“为了调试方便,将目的MAC地址的最低有效位取为颜色字段”。这部分也需要另外改变源代码。

    若线速产生包,经过此sample转发,基于sample中给的参数进行QoS,会有特定的流量速率。数据在 sample guide中给出了:

    有一个问题是明明收发队列只配置了各一条,为何还开启了一大堆RSS的选项。

    接下来的考虑:要回顾流分类和RSS的概念,感觉摸了点皮毛,需要好好做下总结。再看看QoS meter(限速)和flow classify(流分类),flow filtering (流过滤)的sample,总结一下它们之间有没有什么共通或者可以结合的地方。开始编程和调试,这一步迟早要迈出去。DPDK QoS 还有两个模块 RED Dropper 和 Scheduler 以及 Scheduler 的 sample 需要攻坚。

  • 相关阅读:
    codevs 2632 非常好友
    codevs 1213 解的个数
    codevs 2751 军训分批
    codevs 1519 过路费
    codevs 1503 愚蠢的宠物
    codevs 2639 约会计划
    codevs 3369 膜拜
    codevs 3135 River Hopscotch
    数论模板
    JXOJ 9.7 NOIP 放松模拟赛 总结
  • 原文地址:https://www.cnblogs.com/ZCplayground/p/9452364.html
Copyright © 2011-2022 走看看