test_link_bonding_rssconf.c revision 8e6d9d11
1/*-
2 *   BSD LICENSE
3 *
4 *   Copyright(c) 2015 Intel Corporation. All rights reserved.
5 *   All rights reserved.
6 *
7 *   Redistribution and use in source and binary forms, with or without
8 *   modification, are permitted provided that the following conditions
9 *   are met:
10 *
11 *     * Redistributions of source code must retain the above copyright
12 *       notice, this list of conditions and the following disclaimer.
13 *     * Redistributions in binary form must reproduce the above copyright
14 *       notice, this list of conditions and the following disclaimer in
15 *       the documentation and/or other materials provided with the
16 *       distribution.
17 *     * Neither the name of Intel Corporation nor the names of its
18 *       contributors may be used to endorse or promote products derived
19 *       from this software without specific prior written permission.
20 *
21 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <string.h>
35#include <stdarg.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <stdint.h>
39#include <inttypes.h>
40#include <errno.h>
41#include <rte_cycles.h>
42#include <sys/queue.h>
43
44#include <rte_byteorder.h>
45#include <rte_common.h>
46#include <rte_debug.h>
47#include <rte_ethdev.h>
48#include <rte_log.h>
49#include <rte_lcore.h>
50#include <rte_memory.h>
51
52#include <rte_string_fns.h>
53#include <rte_errno.h>
54#include <rte_eth_bond.h>
55#include <rte_eth_null.h>
56
57#include "test.h"
58
59#define SLAVE_COUNT (4)
60
61#define RXTX_RING_SIZE			1024
62#define RXTX_QUEUE_COUNT		4
63
64#define BONDED_DEV_NAME         ("net_bonding_rss")
65
66#define SLAVE_DEV_NAME_FMT      ("rssconf_slave%d")
67#define SLAVE_RXTX_QUEUE_FMT      ("rssconf_slave%d_q%d")
68
69#define NUM_MBUFS 8191
70#define MBUF_SIZE (1600 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
71#define MBUF_CACHE_SIZE 250
72#define BURST_SIZE 32
73
74#define INVALID_SOCKET_ID       (-1)
75#define INVALID_PORT_ID         (0xFF)
76#define INVALID_BONDING_MODE    (-1)
77
78struct slave_conf {
79	uint8_t port_id;
80	struct rte_eth_dev_info dev_info;
81
82	struct rte_eth_rss_conf rss_conf;
83	uint8_t rss_key[40];
84	struct rte_eth_rss_reta_entry64 reta_conf[512 / RTE_RETA_GROUP_SIZE];
85
86	uint8_t is_slave;
87	struct rte_ring *rxtx_queue[RXTX_QUEUE_COUNT];
88};
89
90struct link_bonding_rssconf_unittest_params {
91	uint8_t bond_port_id;
92	struct rte_eth_dev_info bond_dev_info;
93	struct rte_eth_rss_reta_entry64 bond_reta_conf[512 / RTE_RETA_GROUP_SIZE];
94	struct slave_conf slave_ports[SLAVE_COUNT];
95
96	struct rte_mempool *mbuf_pool;
97};
98
99static struct link_bonding_rssconf_unittest_params test_params  = {
100	.bond_port_id = INVALID_PORT_ID,
101	.slave_ports = {
102		[0 ... SLAVE_COUNT - 1] = { .port_id = INVALID_PORT_ID, .is_slave = 0}
103	},
104	.mbuf_pool = NULL,
105};
106
107/**
108 * Default port configuration with RSS turned off
109 */
110static struct rte_eth_conf default_pmd_conf = {
111	.rxmode = {
112		.mq_mode = ETH_MQ_RX_NONE,
113		.max_rx_pkt_len = ETHER_MAX_LEN,
114		.split_hdr_size = 0,
115		.header_split   = 0, /**< Header Split disabled */
116		.hw_ip_checksum = 0, /**< IP checksum offload enabled */
117		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
118		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
119		.hw_strip_crc   = 1, /**< CRC stripped by hardware */
120	},
121	.txmode = {
122		.mq_mode = ETH_MQ_TX_NONE,
123	},
124	.lpbk_mode = 0,
125};
126
127static struct rte_eth_conf rss_pmd_conf = {
128	.rxmode = {
129		.mq_mode = ETH_MQ_RX_RSS,
130		.max_rx_pkt_len = ETHER_MAX_LEN,
131		.split_hdr_size = 0,
132		.header_split   = 0, /**< Header Split disabled */
133		.hw_ip_checksum = 0, /**< IP checksum offload enabled */
134		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
135		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
136		.hw_strip_crc   = 1, /**< CRC stripped by hardware */
137	},
138	.txmode = {
139		.mq_mode = ETH_MQ_TX_NONE,
140	},
141	.rx_adv_conf = {
142		.rss_conf = {
143			.rss_key = NULL,
144			.rss_hf = ETH_RSS_IPV6,
145		},
146	},
147	.lpbk_mode = 0,
148};
149
150#define FOR_EACH(_i, _item, _array, _size) \
151	for (_i = 0, _item = &_array[0]; _i < _size && (_item = &_array[_i]); _i++)
152
153/* Macro for iterating over every port that can be used as a slave
154 * in this test.
155 * _i variable used as an index in test_params->slave_ports
156 * _slave pointer to &test_params->slave_ports[_idx]
157 */
158#define FOR_EACH_PORT(_i, _port) \
159	FOR_EACH(_i, _port, test_params.slave_ports, \
160		RTE_DIM(test_params.slave_ports))
161
162static int
163configure_ethdev(uint8_t port_id, struct rte_eth_conf *eth_conf, uint8_t start)
164{
165	int rxq, txq;
166
167	TEST_ASSERT(rte_eth_dev_configure(port_id, RXTX_QUEUE_COUNT,
168			RXTX_QUEUE_COUNT, eth_conf) == 0, "Failed to configure device %u",
169			port_id);
170
171	for (rxq = 0; rxq < RXTX_QUEUE_COUNT; rxq++) {
172		TEST_ASSERT(rte_eth_rx_queue_setup(port_id, rxq, RXTX_RING_SIZE,
173				rte_eth_dev_socket_id(port_id), NULL,
174				test_params.mbuf_pool) == 0, "Failed to setup rx queue.");
175	}
176
177	for (txq = 0; txq < RXTX_QUEUE_COUNT; txq++) {
178		TEST_ASSERT(rte_eth_tx_queue_setup(port_id, txq, RXTX_RING_SIZE,
179				rte_eth_dev_socket_id(port_id), NULL) == 0,
180				"Failed to setup tx queue.");
181	}
182
183	if (start) {
184		TEST_ASSERT(rte_eth_dev_start(port_id) == 0,
185		"Failed to start device (%d).", port_id);
186	}
187
188	return 0;
189}
190
191/**
192 * Remove all slaves from bonding
193 */
194static int
195remove_slaves(void)
196{
197	unsigned n;
198	struct slave_conf *port;
199
200	FOR_EACH_PORT(n, port) {
201		port = &test_params.slave_ports[n];
202		if (port->is_slave) {
203			TEST_ASSERT_SUCCESS(rte_eth_bond_slave_remove(
204					test_params.bond_port_id, port->port_id),
205					"Cannot remove slave %d from bonding", port->port_id);
206			port->is_slave = 0;
207		}
208	}
209
210	return 0;
211}
212
213static int
214remove_slaves_and_stop_bonded_device(void)
215{
216	TEST_ASSERT_SUCCESS(remove_slaves(), "Removing slaves");
217	rte_eth_dev_stop(test_params.bond_port_id);
218	return TEST_SUCCESS;
219}
220
221/**
222 * Add all slaves to bonding
223 */
224static int
225bond_slaves(void)
226{
227	unsigned n;
228	struct slave_conf *port;
229
230	FOR_EACH_PORT(n, port) {
231		port = &test_params.slave_ports[n];
232		if (!port->is_slave) {
233			TEST_ASSERT_SUCCESS(rte_eth_bond_slave_add(test_params.bond_port_id,
234					port->port_id), "Cannot attach slave %d to the bonding",
235					port->port_id);
236			port->is_slave = 1;
237		}
238	}
239
240	return 0;
241}
242
243/**
244 * Set all RETA values in port_id to value
245 */
246static int
247reta_set(uint8_t port_id, uint8_t value, int reta_size)
248{
249	struct rte_eth_rss_reta_entry64 reta_conf[512/RTE_RETA_GROUP_SIZE];
250	int i, j;
251
252	for (i = 0; i < reta_size / RTE_RETA_GROUP_SIZE; i++) {
253		/* select all fields to set */
254		reta_conf[i].mask = ~0LL;
255		for (j = 0; j < RTE_RETA_GROUP_SIZE; j++)
256			reta_conf[i].reta[j] = value;
257	}
258
259	return rte_eth_dev_rss_reta_update(port_id, reta_conf, reta_size);
260}
261
262/**
263 * Check if slaves RETA is synchronized with bonding port. Returns 1 if slave
264 * port is synced with bonding port.
265 */
266static int
267reta_check_synced(struct slave_conf *port)
268{
269	unsigned i;
270
271	for (i = 0; i < test_params.bond_dev_info.reta_size;
272			i++) {
273
274		int index = i / RTE_RETA_GROUP_SIZE;
275		int shift = i % RTE_RETA_GROUP_SIZE;
276
277		if (port->reta_conf[index].reta[shift] !=
278				test_params.bond_reta_conf[index].reta[shift])
279			return 0;
280
281	}
282
283	return 1;
284}
285
286/**
287 * Fetch bonding ports RETA
288 */
289static int
290bond_reta_fetch(void) {
291	unsigned j;
292
293	for (j = 0; j < test_params.bond_dev_info.reta_size / RTE_RETA_GROUP_SIZE;
294			j++)
295		test_params.bond_reta_conf[j].mask = ~0LL;
296
297	TEST_ASSERT_SUCCESS(rte_eth_dev_rss_reta_query(test_params.bond_port_id,
298			test_params.bond_reta_conf, test_params.bond_dev_info.reta_size),
299			"Cannot take bonding ports RSS configuration");
300	return 0;
301}
302
303/**
304 * Fetch slaves RETA
305 */
306static int
307slave_reta_fetch(struct slave_conf *port) {
308	unsigned j;
309
310	for (j = 0; j < port->dev_info.reta_size / RTE_RETA_GROUP_SIZE; j++)
311		port->reta_conf[j].mask = ~0LL;
312
313	TEST_ASSERT_SUCCESS(rte_eth_dev_rss_reta_query(port->port_id,
314			port->reta_conf, port->dev_info.reta_size),
315			"Cannot take bonding ports RSS configuration");
316	return 0;
317}
318
319/**
320 * Remove and add slave to check if slaves configuration is synced with
321 * the bonding ports values after adding new slave.
322 */
323static int
324slave_remove_and_add(void)
325{
326	struct slave_conf *port = &(test_params.slave_ports[0]);
327
328	/* 1. Remove first slave from bonding */
329	TEST_ASSERT_SUCCESS(rte_eth_bond_slave_remove(test_params.bond_port_id,
330			port->port_id), "Cannot remove slave #d from bonding");
331
332	/* 2. Change removed (ex-)slave and bonding configuration to different
333	 *    values
334	 */
335	reta_set(test_params.bond_port_id, 1, test_params.bond_dev_info.reta_size);
336	bond_reta_fetch();
337
338	reta_set(port->port_id, 2, port->dev_info.reta_size);
339	slave_reta_fetch(port);
340
341	TEST_ASSERT(reta_check_synced(port) == 0,
342			"Removed slave didn't should be synchronized with bonding port");
343
344	/* 3. Add (ex-)slave and check if configuration changed*/
345	TEST_ASSERT_SUCCESS(rte_eth_bond_slave_add(test_params.bond_port_id,
346			port->port_id), "Cannot add slave");
347
348	bond_reta_fetch();
349	slave_reta_fetch(port);
350
351	return reta_check_synced(port);
352}
353
354/**
355 * Test configuration propagation over slaves.
356 */
357static int
358test_propagate(void)
359{
360	unsigned i;
361	uint8_t n;
362	struct slave_conf *port;
363	uint8_t bond_rss_key[40];
364	struct rte_eth_rss_conf bond_rss_conf;
365
366	int retval = 0;
367	uint64_t rss_hf = 0;
368	uint64_t default_rss_hf = 0;
369
370	rte_eth_dev_info_get(test_params.bond_port_id, &test_params.bond_dev_info);
371
372	/*
373	 *  Test hash function propagation
374	 */
375	for (i = 0; i < sizeof(test_params.bond_dev_info.flow_type_rss_offloads)*8;
376			i++) {
377
378		rss_hf = test_params.bond_dev_info.flow_type_rss_offloads & (1<<i);
379		if (rss_hf) {
380			bond_rss_conf.rss_key = NULL;
381			bond_rss_conf.rss_hf = rss_hf;
382
383			retval = rte_eth_dev_rss_hash_update(test_params.bond_port_id,
384					&bond_rss_conf);
385			TEST_ASSERT_SUCCESS(retval, "Cannot set slaves hash function");
386
387			FOR_EACH_PORT(n, port) {
388				port = &test_params.slave_ports[n];
389
390				retval = rte_eth_dev_rss_hash_conf_get(port->port_id,
391						&port->rss_conf);
392				TEST_ASSERT_SUCCESS(retval,
393						"Cannot take slaves RSS configuration");
394
395				TEST_ASSERT(port->rss_conf.rss_hf == rss_hf,
396						"Hash function not propagated for slave %d",
397						port->port_id);
398			}
399
400			default_rss_hf = rss_hf;
401		}
402
403	}
404
405	/*
406	 *  Test key propagation
407	 */
408	for (i = 1; i < 10; i++) {
409
410		/* Set all keys to zero */
411		FOR_EACH_PORT(n, port) {
412			port = &test_params.slave_ports[n];
413			memset(port->rss_conf.rss_key, 0, 40);
414			retval = rte_eth_dev_rss_hash_update(port->port_id,
415					&port->rss_conf);
416			TEST_ASSERT_SUCCESS(retval, "Cannot set slaves RSS keys");
417		}
418
419		memset(bond_rss_key, i, sizeof(bond_rss_key));
420		bond_rss_conf.rss_hf = default_rss_hf,
421		bond_rss_conf.rss_key = bond_rss_key;
422		bond_rss_conf.rss_key_len = 40;
423
424		retval = rte_eth_dev_rss_hash_update(test_params.bond_port_id,
425				&bond_rss_conf);
426		TEST_ASSERT_SUCCESS(retval, "Cannot set bonded port RSS keys");
427
428		FOR_EACH_PORT(n, port) {
429			port = &test_params.slave_ports[n];
430
431			retval = rte_eth_dev_rss_hash_conf_get(port->port_id,
432					&(port->rss_conf));
433
434			TEST_ASSERT_SUCCESS(retval,
435					"Cannot take slaves RSS configuration");
436
437			/* compare keys */
438			retval = memcmp(port->rss_conf.rss_key, bond_rss_key,
439					sizeof(bond_rss_key));
440			TEST_ASSERT(retval == 0, "Key value not propagated for slave %d",
441					port->port_id);
442		}
443	}
444
445	/*
446	 *  Test RETA propagation
447	 */
448	for (i = 0; i < RXTX_QUEUE_COUNT; i++) {
449
450		/* Set all keys to zero */
451		FOR_EACH_PORT(n, port) {
452			port = &test_params.slave_ports[n];
453			retval = reta_set(port->port_id, (i + 1) % RXTX_QUEUE_COUNT,
454					port->dev_info.reta_size);
455			TEST_ASSERT_SUCCESS(retval, "Cannot set slaves RETA");
456		}
457
458		TEST_ASSERT_SUCCESS(reta_set(test_params.bond_port_id,
459				i % RXTX_QUEUE_COUNT, test_params.bond_dev_info.reta_size),
460				"Cannot set bonded port RETA");
461
462		bond_reta_fetch();
463
464		FOR_EACH_PORT(n, port) {
465			port = &test_params.slave_ports[n];
466
467			slave_reta_fetch(port);
468			TEST_ASSERT(reta_check_synced(port) == 1, "RETAs inconsistent");
469		}
470	}
471
472	return TEST_SUCCESS;
473}
474
475/**
476 * Test propagation logic, when RX_RSS mq_mode is turned on for bonding port
477 */
478static int
479test_rss(void)
480{
481	/**
482	 * Configure bonding port in RSS mq mode
483	 */
484	TEST_ASSERT_SUCCESS(configure_ethdev(test_params.bond_port_id,
485			&rss_pmd_conf, 0), "Failed to configure bonding device\n");
486
487	rte_eth_dev_info_get(test_params.bond_port_id, &test_params.bond_dev_info);
488
489	TEST_ASSERT_SUCCESS(bond_slaves(), "Bonding slaves failed");
490
491	TEST_ASSERT_SUCCESS(rte_eth_dev_start(test_params.bond_port_id),
492			"Failed to start bonding port (%d).", test_params.bond_port_id);
493
494	TEST_ASSERT_SUCCESS(test_propagate(), "Propagation test failed");
495
496	TEST_ASSERT(slave_remove_and_add() == 1, "New slave should be synced");
497
498	remove_slaves_and_stop_bonded_device();
499
500	return TEST_SUCCESS;
501}
502
503/**
504 * Test propagation logic, when RX_RSS mq_mode is turned off for bonding port
505 */
506static int
507test_rss_lazy(void)
508{
509	TEST_ASSERT_SUCCESS(configure_ethdev(test_params.bond_port_id,
510			&default_pmd_conf, 0), "Failed to configure bonding device\n");
511
512	rte_eth_dev_info_get(test_params.bond_port_id, &test_params.bond_dev_info);
513
514	TEST_ASSERT_SUCCESS(bond_slaves(), "Bonding slaves failed");
515
516	TEST_ASSERT_SUCCESS(rte_eth_dev_start(test_params.bond_port_id),
517			"Failed to start bonding port (%d).", test_params.bond_port_id);
518
519	TEST_ASSERT_SUCCESS(test_propagate(), "Propagation test failed");
520
521	TEST_ASSERT(slave_remove_and_add() == 0, "New slave shouldn't be synced");
522
523	remove_slaves_and_stop_bonded_device();
524
525	return TEST_SUCCESS;
526}
527
528static int
529test_setup(void)
530{
531	unsigned n;
532	int retval;
533	int port_id;
534	char name[256];
535	struct slave_conf *port;
536	struct ether_addr mac_addr = { .addr_bytes = {0} };
537
538	if (test_params.mbuf_pool == NULL) {
539
540		test_params.mbuf_pool = rte_mempool_create("RSS_MBUF_POOL", NUM_MBUFS *
541				SLAVE_COUNT, MBUF_SIZE, MBUF_CACHE_SIZE,
542				sizeof(struct rte_pktmbuf_pool_private), rte_pktmbuf_pool_init,
543				NULL, rte_pktmbuf_init, NULL, rte_socket_id(), 0);
544
545		TEST_ASSERT(test_params.mbuf_pool != NULL,
546				"rte_mempool_create failed\n");
547	}
548
549	/* Create / initialize ring eth devs. */
550	FOR_EACH_PORT(n, port) {
551		port = &test_params.slave_ports[n];
552
553		port_id = rte_eth_dev_count();
554		snprintf(name, sizeof(name), SLAVE_DEV_NAME_FMT, port_id);
555
556		retval = eth_dev_null_create(name, 0, 64, 0);
557		TEST_ASSERT_SUCCESS(retval, "Failed to create null device '%s'\n",
558				name);
559
560		port->port_id = port_id;
561
562		port->rss_conf.rss_key = port->rss_key;
563		port->rss_conf.rss_key_len = 40;
564
565		retval = configure_ethdev(port->port_id, &default_pmd_conf, 0);
566		TEST_ASSERT_SUCCESS(retval, "Failed to configure virtual ethdev %s\n",
567				name);
568
569		/* assign a non-zero MAC */
570		mac_addr.addr_bytes[5] = 0x10 + port->port_id;
571		rte_eth_dev_default_mac_addr_set(port->port_id, &mac_addr);
572
573		rte_eth_dev_info_get(port->port_id, &port->dev_info);
574	}
575
576	if (test_params.bond_port_id == INVALID_PORT_ID) {
577		retval = rte_eth_bond_create(BONDED_DEV_NAME, 0, rte_socket_id());
578
579		TEST_ASSERT(retval >= 0, "Failed to create bonded ethdev %s",
580				BONDED_DEV_NAME);
581
582		test_params.bond_port_id = retval;
583
584		TEST_ASSERT_SUCCESS(configure_ethdev(test_params.bond_port_id,
585				&default_pmd_conf, 0), "Failed to configure bonding device\n");
586
587		rte_eth_dev_info_get(test_params.bond_port_id,
588				&test_params.bond_dev_info);
589	}
590
591	return TEST_SUCCESS;
592}
593
594static void
595testsuite_teardown(void)
596{
597	struct slave_conf *port;
598	uint8_t i;
599
600	/* Only stop ports.
601	 * Any cleanup/reset state is done when particular test is
602	 * started. */
603
604	rte_eth_dev_stop(test_params.bond_port_id);
605
606	FOR_EACH_PORT(i, port)
607		rte_eth_dev_stop(port->port_id);
608}
609
610static int
611check_environment(void)
612{
613	return TEST_SUCCESS;
614}
615
616static int
617test_rssconf_executor(int (*test_func)(void))
618{
619	int test_result;
620
621	/* Check if environment is clean. Fail to launch a test if there was
622	 * a critical error before that prevented to reset environment. */
623	TEST_ASSERT_SUCCESS(check_environment(),
624		"Refusing to launch test in dirty environment.");
625
626	RTE_VERIFY(test_func != NULL);
627	test_result = (*test_func)();
628
629	/* If test succeed check if environment wast left in good condition. */
630	if (test_result == TEST_SUCCESS)
631		test_result = check_environment();
632
633	/* Reset environment in case test failed to do that. */
634	if (test_result != TEST_SUCCESS) {
635		TEST_ASSERT_SUCCESS(remove_slaves_and_stop_bonded_device(),
636			"Failed to stop bonded device");
637	}
638
639	return test_result;
640}
641
642static int
643test_setup_wrapper(void)
644{
645	return test_rssconf_executor(&test_setup);
646}
647
648static int
649test_rss_wrapper(void)
650{
651	return test_rssconf_executor(&test_rss);
652}
653
654static int
655test_rss_lazy_wrapper(void)
656{
657	return test_rssconf_executor(&test_rss_lazy);
658}
659
660static struct unit_test_suite link_bonding_rssconf_test_suite  = {
661	.suite_name = "RSS Dynamic Configuration for Bonding Unit Test Suite",
662	.teardown = testsuite_teardown,
663	.unit_test_cases = {
664		TEST_CASE_NAMED("test_setup", test_setup_wrapper),
665		TEST_CASE_NAMED("test_rss", test_rss_wrapper),
666		TEST_CASE_NAMED("test_rss_lazy", test_rss_lazy_wrapper),
667
668		TEST_CASES_END()
669	}
670};
671
672static int
673test_link_bonding_rssconf(void)
674{
675	return unit_test_suite_runner(&link_bonding_rssconf_test_suite);
676}
677
678REGISTER_TEST_COMMAND(link_bonding_rssconf_autotest, test_link_bonding_rssconf);
679