Line data Source code
1 : /* SPDX-License-Identifier: BSD-2-Clause
2 : * Copyright(c) HCL TECHNOLOGIES LTD
3 : * Submitted on behalf of a third-party: Intel Corporation, a
4 : * Delaware corporation, having its principal place of business
5 : * at 2200 Mission College Boulevard,
6 : * Santa Clara, California 95052, USA
7 : */
8 :
9 : #include <errno.h>
10 : #include <getopt.h>
11 : #include <stdbool.h>
12 : #include <sys/queue.h>
13 :
14 : #include <rte_eal.h>
15 : #include <rte_dev.h>
16 : #include <string.h>
17 :
18 : #include "vr_dpdk.h"
19 : #include "vr_dpdk_n3k_config.h"
20 : #include "vr_dpdk_n3k_service_core.h"
21 :
22 : #define VR_DPDK_N3K_OPTION_HANDLER_INITIALIZER(match, routine) \
23 : { \
24 : .match_str = (match), .handler_routine = (routine) \
25 : }
26 :
27 : #define REPRESENTORS_MAX_NUMBER 2
28 :
29 : struct vr_dpdk_n3k_eal_opt {
30 : STAILQ_ENTRY(vr_dpdk_n3k_eal_opt) next;
31 :
32 : char *opt;
33 : char *value;
34 : };
35 :
36 : STAILQ_HEAD(vr_dpdk_n3k_eal_opts, vr_dpdk_n3k_eal_opt);
37 :
38 : struct vr_dpdk_n3k_config {
39 : char phy_representor_name[REPRESENTORS_MAX_NUMBER][RTE_DEV_NAME_MAX_LEN];
40 : size_t phy_representor_name_nbr;
41 : bool drop_offload_enabled;
42 : bool aging_service_core_enabled;
43 : bool force_vdpa_mapping;
44 : bool multihoming_enabled;
45 : struct vr_dpdk_n3k_eal_opts eal_opts;
46 : };
47 :
48 : struct vr_dpdk_n3k_config n3k_config = { .phy_representor_name_nbr = 0,
49 : .drop_offload_enabled = true,
50 : .aging_service_core_enabled = false,
51 : .force_vdpa_mapping = false,
52 : .multihoming_enabled = false,
53 : .eal_opts = STAILQ_HEAD_INITIALIZER(
54 : n3k_config.eal_opts) };
55 :
56 : typedef int (*option_handler_t)(struct vr_dpdk_n3k_config *, size_t, char **);
57 :
58 : struct vr_dpdk_n3k_option_handler {
59 : const char *match_str;
60 : option_handler_t handler_routine;
61 : };
62 :
63 : static size_t
64 0 : get_argv_last_filled_pos(size_t argv_sz, char **argv)
65 : {
66 0 : size_t last_filled_pos = 0;
67 0 : for (; last_filled_pos < argv_sz; ++last_filled_pos) {
68 0 : if (argv[last_filled_pos] == NULL) {
69 0 : break;
70 : }
71 : }
72 :
73 0 : return --last_filled_pos;
74 : }
75 :
76 : static size_t
77 0 : get_n3k_eal_argc(struct vr_dpdk_n3k_config *cfg, size_t vr_eal_argc)
78 : {
79 : struct vr_dpdk_n3k_eal_opt *opt;
80 0 : size_t n3k_eal_param_cnt = 0;
81 :
82 0 : STAILQ_FOREACH(opt, &cfg->eal_opts, next)
83 : {
84 0 : n3k_eal_param_cnt++;
85 0 : if (opt->value != NULL) {
86 0 : n3k_eal_param_cnt++;
87 : }
88 : }
89 :
90 0 : return vr_eal_argc + n3k_eal_param_cnt;
91 : }
92 :
93 : static void
94 0 : free_eal_opts(struct vr_dpdk_n3k_config *cfg)
95 : {
96 : struct vr_dpdk_n3k_eal_opt *opt;
97 :
98 0 : while (!STAILQ_EMPTY(&cfg->eal_opts)) {
99 0 : opt = STAILQ_FIRST(&cfg->eal_opts);
100 0 : STAILQ_REMOVE_HEAD(&cfg->eal_opts, next);
101 0 : free(opt);
102 : }
103 0 : }
104 :
105 : static int
106 0 : new_eal_opt(struct vr_dpdk_n3k_config *cfg, char *opt, char *value)
107 : {
108 0 : struct vr_dpdk_n3k_eal_opt *eal_opt = malloc(sizeof(*eal_opt));
109 0 : if (!eal_opt) {
110 0 : RTE_LOG(
111 : ERR, VROUTER, "N3K config: Allocating memory for eal opt failed\n");
112 0 : return -ENOMEM;
113 : }
114 :
115 0 : RTE_LOG(INFO,
116 : VROUTER,
117 : "Passing %s%s%s to EAL\n",
118 : opt,
119 : value == NULL ? "" : " with value: ",
120 : value == NULL ? "" : value);
121 :
122 0 : eal_opt->opt = opt;
123 0 : eal_opt->value = value;
124 :
125 0 : STAILQ_INSERT_TAIL(&cfg->eal_opts, eal_opt, next);
126 :
127 0 : return 0;
128 : }
129 :
130 : static void
131 0 : append_opts_to_eal_args(struct vr_dpdk_n3k_config *cfg,
132 : size_t arg_pos,
133 : char **argv)
134 : {
135 : struct vr_dpdk_n3k_eal_opt *opt;
136 :
137 0 : STAILQ_FOREACH(opt, &cfg->eal_opts, next)
138 : {
139 0 : argv[arg_pos++] = opt->opt;
140 0 : if (opt->value) {
141 0 : argv[arg_pos++] = opt->value;
142 : }
143 : }
144 0 : }
145 :
146 : static char *
147 1 : get_provided_opt(char **argv)
148 : {
149 1 : return argv[optind - 1];
150 : }
151 :
152 : static char *
153 1 : get_provided_opt_value(size_t argc, char **argv)
154 : {
155 1 : bool option_has_value = (optind < argc) && (*argv[optind] != '-');
156 :
157 1 : return option_has_value ? argv[optind++] : NULL;
158 : }
159 :
160 : static int
161 0 : handle_opt_eal(struct vr_dpdk_n3k_config *cfg, size_t vr_argc, char **vr_argv)
162 : {
163 0 : if (optind >= vr_argc) {
164 0 : RTE_LOG(ERR, VROUTER, "N3K Config: Expected EAL opt, got nothing\n");
165 0 : return -EINVAL;
166 : }
167 :
168 : // Skip '--eal'
169 0 : optind++;
170 :
171 0 : char *opt = get_provided_opt(vr_argv);
172 :
173 0 : if (*opt != '-') {
174 0 : RTE_LOG(ERR, VROUTER, "N3K Config: Expected EAL option, got %s\n", opt);
175 0 : return -EINVAL;
176 : }
177 :
178 0 : char *value = get_provided_opt_value(vr_argc, vr_argv);
179 :
180 0 : return new_eal_opt(cfg, opt, value);
181 : }
182 :
183 : static int
184 0 : handle_opt_whitelist(struct vr_dpdk_n3k_config *cfg,
185 : size_t vr_argc,
186 : char **vr_argv)
187 : {
188 : static char whitelist_eal_opt[] = "--pci-whitelist";
189 0 : char *value = get_provided_opt_value(vr_argc, vr_argv);
190 :
191 0 : if (!value) {
192 0 : RTE_LOG(ERR, VROUTER, "N3K Config: Expected whitelist parameter\n");
193 0 : return -EINVAL;
194 : }
195 :
196 0 : if (strstr(value, "insert_type=flr-sim") != NULL) {
197 0 : vr_dpdk_n3k_service_core_flr_sim_enable();
198 : }
199 :
200 0 : return new_eal_opt(cfg, whitelist_eal_opt, value);
201 : }
202 :
203 : static unsigned int
204 2 : get_subarg_len(char *ptrStr,
205 : unsigned int idx)
206 : {
207 2 : unsigned int i = 0;
208 2 : char *strTmp = NULL;
209 :
210 2 : for(i=0; ptrStr; i++) {
211 2 : strTmp = strstr(ptrStr, ",");
212 2 : if(i == idx) {
213 1 : if(!strTmp) return strlen(ptrStr);
214 : else {
215 0 : return strTmp - ptrStr;
216 : }
217 : }
218 1 : if(!strTmp) break;
219 :
220 0 : ptrStr = strTmp + 1;
221 : }
222 :
223 1 : return 0;
224 : }
225 :
226 : static char*
227 1 : get_subarg_val(char *ptrStr,
228 : unsigned int idx)
229 : {
230 1 : unsigned int i = 0;
231 1 : char *strTmp = NULL;
232 :
233 1 : for(i=0; ptrStr; i++) {
234 1 : strTmp = strstr(ptrStr, ",");
235 1 : if(i == idx) {
236 1 : return ptrStr;
237 : }
238 0 : if(!strTmp) break;
239 :
240 0 : ptrStr = strTmp + 1;
241 : }
242 :
243 0 : return NULL;
244 : }
245 :
246 : static int
247 1 : handle_opt_enable_n3k(struct vr_dpdk_n3k_config *cfg,
248 : size_t vr_argc,
249 : char **vr_argv)
250 : {
251 : int i;
252 1 : unsigned int subLen = 0;
253 1 : char *tmpStr = get_provided_opt_value(vr_argc, vr_argv);
254 :
255 1 : if(!tmpStr) {
256 0 : RTE_LOG(ERR, VROUTER, "N3K Config: Missing representor name\n");
257 0 : return -EINVAL;
258 : }
259 :
260 1 : RTE_LOG(INFO, VROUTER, "N3K Config: Enabling N3K offloads\n");
261 :
262 1 : cfg->phy_representor_name_nbr = 0;
263 2 : for(i=0; i<REPRESENTORS_MAX_NUMBER; i++) {
264 2 : subLen = get_subarg_len(tmpStr, i);
265 2 : if(subLen) {
266 1 : subLen = (subLen>(RTE_DEV_NAME_MAX_LEN-1))?RTE_DEV_NAME_MAX_LEN-1:subLen;
267 1 : strncpy(cfg->phy_representor_name[i], get_subarg_val(tmpStr, i), subLen);
268 1 : cfg->phy_representor_name[i][subLen] = 0;
269 1 : cfg->phy_representor_name_nbr++;
270 : } else {
271 1 : break;
272 : }
273 1 : RTE_LOG(INFO, VROUTER, "N3K Config: Representor[%d]: %s\n", i, cfg->phy_representor_name[i]);
274 : }
275 :
276 1 : if(!strcmp("l3mh", cfg->phy_representor_name[0])) {
277 0 : cfg->multihoming_enabled = true;
278 0 : for(i=0; i<REPRESENTORS_MAX_NUMBER; i++) {
279 0 : snprintf(cfg->phy_representor_name[i], RTE_DEV_NAME_MAX_LEN, "net_n3k0_phy%d", i);
280 : }
281 0 : RTE_LOG(INFO, VROUTER, "N3K Config: Multihomming enabled\n");
282 : }
283 :
284 1 : return 0;
285 : }
286 :
287 : static int
288 0 : handle_opt_no_drop_offload(struct vr_dpdk_n3k_config *cfg,
289 : __attribute__((unused)) size_t vr_argc,
290 : __attribute__((unused)) char **vr_argv)
291 : {
292 0 : cfg->drop_offload_enabled = false;
293 :
294 0 : RTE_LOG(INFO, VROUTER, "N3K Config: Disabling drop offload\n");
295 :
296 0 : return 0;
297 : }
298 :
299 : static int
300 0 : handle_opt_aging_lcore(struct vr_dpdk_n3k_config *cfg,
301 : __attribute__((unused)) size_t vr_argc,
302 : __attribute__((unused)) char **vr_argv)
303 : {
304 0 : cfg->aging_service_core_enabled = true;
305 :
306 0 : RTE_LOG(INFO, VROUTER, "N3K Config: Enabling aging lcore\n");
307 :
308 0 : return 0;
309 : }
310 :
311 : static int
312 0 : handle_opt_force_vdpa_mapping(struct vr_dpdk_n3k_config *cfg,
313 : __attribute__((unused)) size_t vr_argc,
314 : __attribute__((unused)) char **vr_argv)
315 : {
316 0 : cfg->force_vdpa_mapping = true;
317 :
318 0 : RTE_LOG(INFO, VROUTER, "N3K Config: Forcing vDPA mapping\n");
319 :
320 0 : return 0;
321 : }
322 :
323 : struct vr_dpdk_n3k_option_handler n3k_options[] = {
324 : VR_DPDK_N3K_OPTION_HANDLER_INITIALIZER("--enable_n3k",
325 : handle_opt_enable_n3k),
326 : VR_DPDK_N3K_OPTION_HANDLER_INITIALIZER("--no_drop_offload",
327 : handle_opt_no_drop_offload),
328 : VR_DPDK_N3K_OPTION_HANDLER_INITIALIZER("--aging_lcore",
329 : handle_opt_aging_lcore),
330 : VR_DPDK_N3K_OPTION_HANDLER_INITIALIZER("--eal", handle_opt_eal),
331 : VR_DPDK_N3K_OPTION_HANDLER_INITIALIZER("--whitelist", handle_opt_whitelist),
332 : VR_DPDK_N3K_OPTION_HANDLER_INITIALIZER("--force_vdpa_mapping",
333 : handle_opt_force_vdpa_mapping),
334 : };
335 :
336 : int
337 1 : vr_dpdk_n3k_config_parse_opt(size_t vr_argc, char **vr_argv, char vr_opt)
338 : {
339 1 : struct vr_dpdk_n3k_config *cfg = &n3k_config;
340 1 : int i = 0;
341 1 : char *opt_provided = get_provided_opt(vr_argv);
342 :
343 1 : for (; i < RTE_DIM(n3k_options); ++i) {
344 1 : struct vr_dpdk_n3k_option_handler *curr_opt = &n3k_options[i];
345 1 : bool opt_matched = strcmp(opt_provided, curr_opt->match_str) == 0;
346 :
347 1 : if (opt_matched) {
348 1 : return curr_opt->handler_routine(cfg, vr_argc, vr_argv);
349 : }
350 : }
351 :
352 0 : return -EINVAL;
353 : }
354 :
355 : int
356 0 : vr_dpdk_n3k_config_get_updated_eal_args(
357 : size_t argc,
358 : char **argv,
359 : struct vr_dpdk_n3k_config_eal_args *updated_args)
360 : {
361 0 : struct vr_dpdk_n3k_config *cfg = &n3k_config;
362 0 : size_t vr_eal_argc = get_argv_last_filled_pos(argc, argv) + 1;
363 0 : size_t new_size = get_n3k_eal_argc(cfg, vr_eal_argc);
364 : // Freed by caller
365 0 : char **updated_argv = malloc(sizeof(*updated_argv) * new_size);
366 0 : if (!updated_argv) {
367 0 : RTE_LOG(ERR,
368 : VROUTER,
369 : "N3K config: Allocating memory for updated eal args failed\n");
370 0 : return -ENOMEM;
371 : }
372 :
373 0 : memcpy(updated_argv, argv, vr_eal_argc * sizeof(*argv));
374 :
375 0 : append_opts_to_eal_args(cfg, vr_eal_argc, updated_argv);
376 :
377 0 : updated_args->argc = new_size;
378 0 : updated_args->argv = updated_argv;
379 :
380 0 : return 0;
381 : }
382 :
383 : static void
384 0 : n3k_usage(void)
385 : {
386 0 : RTE_LOG(
387 : INFO,
388 : VROUTER,
389 : "\n"
390 : " --whitelist NAME Add pci address to a PCI whitelist\n"
391 : " To use the device in vDPA mode: "
392 : "--whitelist <PCI>,vdpa=1\n"
393 : " equivalent of --eal --pci-whitelist "
394 : "...\n"
395 : " --enable_n3k NAME Use n3k specific callbacks\n"
396 : " it is needed to specify physical "
397 : "representor name here\n"
398 : " --no_drop_offload Do not offload drop flows to HW\n"
399 : " --aging_lcore Enable N3000 aging lcore\n"
400 : " --eal Options for EAL must begin with --eal "
401 : "opt, e.g.\n"
402 : " --eal --log-level lib.eal:8\n"
403 : " --force_vdpa_mapping Assign random n3k VF for all vifs");
404 0 : }
405 :
406 : void
407 0 : vr_dpdk_n3k_config_print_usage(void)
408 : {
409 0 : n3k_usage();
410 0 : }
411 :
412 : void
413 0 : vr_dpdk_n3k_config_init(void)
414 : {
415 0 : return;
416 : }
417 :
418 : void
419 0 : vr_dpdk_n3k_config_exit(void)
420 : {
421 0 : struct vr_dpdk_n3k_config *cfg = &n3k_config;
422 :
423 0 : free_eal_opts(cfg);
424 0 : }
425 :
426 : bool
427 0 : vr_dpdk_n3k_config_is_aging_service_core_enabled(void)
428 : {
429 0 : return n3k_config.aging_service_core_enabled;
430 : }
431 :
432 : bool
433 2 : vr_dpdk_n3k_config_is_n3k_enabled(void)
434 : {
435 2 : return n3k_config.phy_representor_name_nbr != 0;
436 : }
437 :
438 : bool
439 0 : vr_dpdk_n3k_config_is_drop_offload_enabled(void)
440 : {
441 0 : return n3k_config.drop_offload_enabled;
442 : }
443 :
444 : bool
445 0 : vr_dpdk_n3k_config_vdpa_mapping_enabled(void)
446 : {
447 0 : return n3k_config.force_vdpa_mapping;
448 : }
449 :
450 0 : static int get_mac_by_name(const char *ptr_name, struct rte_ether_addr *ptr_mac_addr)
451 : {
452 0 : int rc = -1;
453 0 : uint16_t port_id = 0;
454 :
455 0 : rc = rte_eth_dev_get_port_by_name(ptr_name, &port_id);
456 0 : if (!rc) {
457 0 : rc = rte_eth_macaddr_get(port_id, ptr_mac_addr);
458 : }
459 :
460 0 : return rc;
461 : }
462 :
463 0 : static const char* get_multihoming_representor(struct vr_interface *vif)
464 : {
465 : int i;
466 0 : const char *representor_name = NULL;
467 : struct rte_ether_addr mac_addr;
468 :
469 0 : if( !vif ) {
470 0 : return NULL;
471 : }
472 :
473 0 : for(i=0; i<REPRESENTORS_MAX_NUMBER; i++) {
474 0 : if( !get_mac_by_name(n3k_config.phy_representor_name[i], &mac_addr) ) {
475 0 : if(!memcmp(vif->vif_mac, mac_addr.addr_bytes, sizeof(vif->vif_mac))) {
476 0 : representor_name = n3k_config.phy_representor_name[i];
477 0 : RTE_LOG(INFO, VROUTER, "Vif <%s> match to representor <%s>\n", vif->vif_name, representor_name);
478 0 : break;
479 : }
480 : }
481 : }
482 :
483 0 : return representor_name;
484 : }
485 :
486 : const char *
487 2 : vr_dpdk_n3k_config_get_phy_repr_name(struct vr_interface *vif)
488 : {
489 2 : const char *representor_name = NULL;
490 :
491 2 : if( n3k_config.multihoming_enabled ) {
492 0 : representor_name = get_multihoming_representor(vif);
493 : }
494 : else {
495 2 : representor_name = n3k_config.phy_representor_name[0];
496 : }
497 :
498 2 : return representor_name;
499 : }
|