summaryrefslogtreecommitdiff
path: root/src/vf/spp_vf.c
blob: 012e52998c1aaf8a4a5290a33fa68126602473cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2017-2019 Nippon Telegraph and Telephone Corporation
 */

#include <netinet/in.h>
#include <arpa/inet.h>
#include <getopt.h>

#include "classifier.h"
#include "forwarder.h"
#include "shared/secondary/common.h"
#include "shared/secondary/utils.h"
#include "shared/secondary/spp_worker_th/cmd_utils.h"
#include "shared/secondary/return_codes.h"
#include "shared/secondary/add_port.h"
#include "shared/secondary/spp_worker_th/cmd_runner.h"
#include "shared/secondary/spp_worker_th/cmd_parser.h"
#include "shared/secondary/spp_worker_th/port_capability.h"

#define RTE_LOGTYPE_SPP_VF RTE_LOGTYPE_USER1

#ifdef SPP_RINGLATENCYSTATS_ENABLE
#include "shared/secondary/spp_worker_th/latency_stats.h"
#endif

/* getopt_long return value for long option */
enum SPP_LONGOPT_RETVAL {
	SPP_LONGOPT_RETVAL__ = 127,

	/* Return value definition for getopt_long(). Only for long option. */
	SPP_LONGOPT_RETVAL_CLIENT_ID,    /* For `--client-id` */
	SPP_LONGOPT_RETVAL_VHOST_CLIENT  /* For `--vhost-client` */
};

/* Declare global variables */
/* Interface management information */
static struct iface_info g_iface_info;

/* Component management information */
static struct sppwk_comp_info g_component_info[RTE_MAX_LCORE];

/* Core management information */
static struct core_mng_info g_core_info[RTE_MAX_LCORE];

/* Array of update indicator for core management information */
static int g_change_core[RTE_MAX_LCORE];

/* Array of update indicator for component management information */
static int g_change_component[RTE_MAX_LCORE];

/* Backup information for cancel command */
static struct cancel_backup_info g_backup_info;

/* Print help message */
static void
usage(const char *progname)
{
	RTE_LOG(INFO, SPP_VF, "Usage: %s [EAL args] --"
			" --client-id CLIENT_ID"
			" -s SERVER_IP:SERVER_PORT"
			" [--vhost-client]\n"
			" --client-id CLIENT_ID   : My client ID\n"
			" -s SERVER_IP:SERVER_PORT  :"
			" Access information to the server\n"
			" --vhost-client            : Run vhost on client\n"
			, progname);
}

/* Parse options for client app */
static int
parse_app_args(int argc, char *argv[])
{
	int cli_id;  /* Client ID. */
	char *ctl_ip;  /* IP address of spp_ctl. */
	int ctl_port;  /* Port num to connect spp_ctl. */
	int ret;
	int cnt;
	int option_index, opt;

	int proc_flg = 0;
	int server_flg = 0;

	const int argcopt = argc;
	char *argvopt[argcopt];
	const char *progname = argv[0];

	static struct option lgopts[] = {
			{ "client-id", required_argument, NULL,
					SPP_LONGOPT_RETVAL_CLIENT_ID },
			{ "vhost-client", no_argument, NULL,
					SPP_LONGOPT_RETVAL_VHOST_CLIENT },
			{ 0 },
	};

	/**
	 * Save argv to argvopt to avoid losing the order of options
	 * by getopt_long()
	 */
	for (cnt = 0; cnt < argcopt; cnt++)
		argvopt[cnt] = argv[cnt];

	/* vhost_cli is disabled as default. */
	set_vhost_cli_mode(0);

	/* Check options of application */
	optind = 0;
	opterr = 0;
	while ((opt = getopt_long(argc, argvopt, "s:", lgopts,
			&option_index)) != EOF) {
		switch (opt) {
		case SPP_LONGOPT_RETVAL_CLIENT_ID:
			if (parse_client_id(&cli_id, optarg) != SPPWK_RET_OK) {
				usage(progname);
				return SPPWK_RET_NG;
			}
			set_client_id(cli_id);

			proc_flg = 1;
			break;
		case SPP_LONGOPT_RETVAL_VHOST_CLIENT:
			set_vhost_cli_mode(1);
			break;
		case 's':
			ret = parse_server(&ctl_ip, &ctl_port, optarg);
			set_spp_ctl_ip(ctl_ip);
			set_spp_ctl_port(ctl_port);
			if (ret != SPPWK_RET_OK) {
				usage(progname);
				return SPPWK_RET_NG;
			}
			server_flg = 1;
			break;
		default:
			usage(progname);
			return SPPWK_RET_NG;
		}
	}

	/* Check mandatory parameters */
	if ((proc_flg == 0) || (server_flg == 0)) {
		usage(progname);
		return SPPWK_RET_NG;
	}
	RTE_LOG(INFO, SPP_VF,
			"Parsed app args (client_id=%d,server=%s:%d,"
			"vhost_client=%d)\n",
			cli_id, ctl_ip, ctl_port, get_vhost_cli_mode());
	return SPPWK_RET_OK;
}

/* Main process of slave core */
static int
slave_main(void *arg __attribute__ ((unused)))
{
	int ret = 0;
	int cnt = 0;
	unsigned int lcore_id = rte_lcore_id();
	enum sppwk_lcore_status status = SPPWK_LCORE_STOPPED;
	struct core_mng_info *info = &g_core_info[lcore_id];
	struct core_info *core = get_core_info(lcore_id);

	RTE_LOG(INFO, SPP_VF, "Slave started on lcore %d.\n", lcore_id);
	set_core_status(lcore_id, SPPWK_LCORE_IDLING);

	while ((status = sppwk_get_lcore_status(lcore_id)) !=
			SPPWK_LCORE_REQ_STOP) {
		if (status != SPPWK_LCORE_RUNNING)
			continue;

		if (spp_check_core_update(lcore_id) == SPPWK_RET_OK) {
			/* Setting with the flush command trigger. */
			info->ref_index = (info->upd_index+1) % TWO_SIDES;
			core = get_core_info(lcore_id);
		}

		/* It is for processing multiple components. */
		for (cnt = 0; cnt < core->num; cnt++) {
			/* Component classification to call a function. */
			if (spp_get_component_type(core->id[cnt]) ==
					SPPWK_TYPE_CLS) {
				/* Component type for classifier. */
				ret = classify_packets(core->id[cnt]);
				if (unlikely(ret != 0))
					break;
			} else {
				/* Component type for forward or merge. */
				ret = forward_packets(core->id[cnt]);
				if (unlikely(ret != 0))
					break;
			}
		}
		if (unlikely(ret != 0)) {
			RTE_LOG(ERR, SPP_VF, "Failed to forward on lcore %d. "
					"(id = %d).\n",
					lcore_id, core->id[cnt]);
			break;
		}
	}

	set_core_status(lcore_id, SPPWK_LCORE_STOPPED);
	RTE_LOG(INFO, SPP_VF, "Terminated slave on lcore %d.\n", lcore_id);
	return ret;
}

/**
 * Main function
 *
 * Return SPPWK_RET_NG explicitly if error is occurred.
 */
int
main(int argc, char *argv[])
{
	int ret;
	char ctl_ip[IPADDR_LEN] = { 0 };
	int ctl_port;
	unsigned int master_lcore;
	unsigned int lcore_id;

#ifdef SPP_DEMONIZE
	/* Daemonize process */
	ret = daemon(0, 0);
	if (unlikely(ret != 0)) {
		RTE_LOG(ERR, SPP_VF, "daemonize is failed. (ret = %d)\n",
				ret);
		return ret;
	}
#endif

	/* Signal handler registration (SIGTERM/SIGINT) */
	signal(SIGTERM, stop_process);
	signal(SIGINT,  stop_process);

	ret = rte_eal_init(argc, argv);
	if (unlikely(ret < 0))
		rte_exit(EXIT_FAILURE, "Invalid EAL arguments.\n");

	argc -= ret;
	argv += ret;

	/**
	 * It should be initialized outside of while loop, or failed to
	 * compile because it is referred when finalize `g_core_info`.
	 */
	master_lcore = rte_get_master_lcore();

	/**
	 * If any failure is occured in the while block, break to go the end
	 * of the block to finalize.
	 */
	while (1) {
		/* Parse spp_vf specific parameters */
		ret = parse_app_args(argc, argv);
		if (unlikely(ret != SPPWK_RET_OK))
			break;

		if (sppwk_set_mng_data(&g_iface_info, g_component_info,
					g_core_info, g_change_core,
					g_change_component,
					&g_backup_info) < SPPWK_RET_OK) {
			RTE_LOG(ERR, SPP_VF,
				"Failed to set management data.\n");
			break;
		}

		ret = init_mng_data();
		if (unlikely(ret != SPPWK_RET_OK))
			break;

		ret = init_cls_mng_info();
		if (unlikely(ret != SPPWK_RET_OK))
			break;

		init_forwarder();
		sppwk_port_capability_init();

		/* Setup connection for accepting commands from controller */
		get_spp_ctl_ip(ctl_ip);
		ctl_port = get_spp_ctl_port();
		ret = sppwk_cmd_runner_conn(ctl_ip, ctl_port);
		if (unlikely(ret != SPPWK_RET_OK))
			break;

#ifdef SPP_RINGLATENCYSTATS_ENABLE
		int port_type, port_id;
		char dev_name[RTE_DEV_NAME_MAX_LEN] = { 0 };
		int nof_rings = 0;
		for (int i = 0; i < RTE_MAX_ETHPORTS; i++) {
			if (!rte_eth_dev_is_valid_port(i))
				continue;
			rte_eth_dev_get_name_by_port(i, dev_name);
			ret = parse_dev_name(dev_name, &port_type, &port_id);
			if (port_type == RING)
				nof_rings++;
		}
		ret = sppwk_init_ring_latency_stats(
				SPP_RING_LATENCY_STATS_SAMPLING_INTERVAL,
				nof_rings);
		if (unlikely(ret != SPPWK_RET_OK))
			break;
#endif /* SPP_RINGLATENCYSTATS_ENABLE */

		/* Start worker threads of classifier and forwarder */
		RTE_LCORE_FOREACH_SLAVE(lcore_id) {
			rte_eal_remote_launch(slave_main, NULL, lcore_id);
		}

		/* Set the status of main thread to idle */
		g_core_info[master_lcore].status = SPPWK_LCORE_IDLING;
		ret = check_core_status_wait(SPPWK_LCORE_IDLING);
		if (unlikely(ret != SPPWK_RET_OK))
			break;

		/* Start forwarding */
		set_all_core_status(SPPWK_LCORE_RUNNING);
		RTE_LOG(INFO, SPP_VF, "My ID %d start handling message\n", 0);
		RTE_LOG(INFO, SPP_VF, "[Press Ctrl-C to quit ...]\n");

		/* Backup the management information after initialization */
		backup_mng_info(&g_backup_info);

		/* Enter loop for accepting commands */
		while (likely(g_core_info[master_lcore].status !=
					SPPWK_LCORE_REQ_STOP))
		{
			/* Receive command */
			ret = sppwk_run_cmd();
			if (unlikely(ret != SPPWK_RET_OK))
				break;

		       /*
			* Wait to avoid CPU overloaded.
			*/
			usleep(100);

#ifdef SPP_RINGLATENCYSTATS_ENABLE
			print_ring_latency_stats(&g_iface_info);
#endif /* SPP_RINGLATENCYSTATS_ENABLE */
		}

		if (unlikely(ret != SPPWK_RET_OK)) {
			set_all_core_status(SPPWK_LCORE_REQ_STOP);
			break;
		}

		ret = SPPWK_RET_OK;
		break;
	}

	/* Finalize to exit */
	g_core_info[master_lcore].status = SPPWK_LCORE_STOPPED;
	ret = check_core_status_wait(SPPWK_LCORE_STOPPED);
	if (unlikely(ret != SPPWK_RET_OK))
		RTE_LOG(ERR, SPP_VF, "Failed to terminate master thread.\n");

	/*
	 * Remove vhost sock file if it is not running
	 *  in vhost-client mode
	 */
	del_vhost_sockfile(g_iface_info.vhost);

#ifdef SPP_RINGLATENCYSTATS_ENABLE
	sppwk_clean_ring_latency_stats();
#endif /* SPP_RINGLATENCYSTATS_ENABLE */

	RTE_LOG(INFO, SPP_VF, "Exit spp_vf.\n");
	return ret;
}