]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*- |
2 | * BSD LICENSE | |
3 | * | |
4 | * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. | |
5 | * Copyright(c) 2014 6WIND S.A. | |
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 <stdlib.h> | |
35 | #include <unistd.h> | |
36 | #include <string.h> | |
37 | #include <syslog.h> | |
38 | #include <ctype.h> | |
39 | #include <limits.h> | |
40 | #include <errno.h> | |
41 | #include <getopt.h> | |
42 | #include <dlfcn.h> | |
43 | #include <sys/types.h> | |
44 | #include <sys/stat.h> | |
45 | #include <dirent.h> | |
46 | ||
47 | #include <rte_eal.h> | |
48 | #include <rte_log.h> | |
49 | #include <rte_lcore.h> | |
50 | #include <rte_version.h> | |
51 | #include <rte_devargs.h> | |
52 | #include <rte_memcpy.h> | |
53 | ||
54 | #include "eal_internal_cfg.h" | |
55 | #include "eal_options.h" | |
56 | #include "eal_filesystem.h" | |
57 | ||
58 | #define BITS_PER_HEX 4 | |
59 | ||
60 | const char | |
61 | eal_short_options[] = | |
62 | "b:" /* pci-blacklist */ | |
63 | "c:" /* coremask */ | |
64 | "d:" /* driver */ | |
65 | "h" /* help */ | |
66 | "l:" /* corelist */ | |
67 | "m:" /* memory size */ | |
68 | "n:" /* memory channels */ | |
69 | "r:" /* memory ranks */ | |
70 | "v" /* version */ | |
71 | "w:" /* pci-whitelist */ | |
72 | ; | |
73 | ||
74 | const struct option | |
75 | eal_long_options[] = { | |
76 | {OPT_BASE_VIRTADDR, 1, NULL, OPT_BASE_VIRTADDR_NUM }, | |
77 | {OPT_CREATE_UIO_DEV, 0, NULL, OPT_CREATE_UIO_DEV_NUM }, | |
78 | {OPT_FILE_PREFIX, 1, NULL, OPT_FILE_PREFIX_NUM }, | |
79 | {OPT_HELP, 0, NULL, OPT_HELP_NUM }, | |
80 | {OPT_HUGE_DIR, 1, NULL, OPT_HUGE_DIR_NUM }, | |
81 | {OPT_HUGE_UNLINK, 0, NULL, OPT_HUGE_UNLINK_NUM }, | |
82 | {OPT_LCORES, 1, NULL, OPT_LCORES_NUM }, | |
83 | {OPT_LOG_LEVEL, 1, NULL, OPT_LOG_LEVEL_NUM }, | |
84 | {OPT_MASTER_LCORE, 1, NULL, OPT_MASTER_LCORE_NUM }, | |
85 | {OPT_NO_HPET, 0, NULL, OPT_NO_HPET_NUM }, | |
86 | {OPT_NO_HUGE, 0, NULL, OPT_NO_HUGE_NUM }, | |
87 | {OPT_NO_PCI, 0, NULL, OPT_NO_PCI_NUM }, | |
88 | {OPT_NO_SHCONF, 0, NULL, OPT_NO_SHCONF_NUM }, | |
89 | {OPT_PCI_BLACKLIST, 1, NULL, OPT_PCI_BLACKLIST_NUM }, | |
90 | {OPT_PCI_WHITELIST, 1, NULL, OPT_PCI_WHITELIST_NUM }, | |
91 | {OPT_PROC_TYPE, 1, NULL, OPT_PROC_TYPE_NUM }, | |
92 | {OPT_SOCKET_MEM, 1, NULL, OPT_SOCKET_MEM_NUM }, | |
93 | {OPT_SYSLOG, 1, NULL, OPT_SYSLOG_NUM }, | |
94 | {OPT_VDEV, 1, NULL, OPT_VDEV_NUM }, | |
95 | {OPT_VFIO_INTR, 1, NULL, OPT_VFIO_INTR_NUM }, | |
96 | {OPT_VMWARE_TSC_MAP, 0, NULL, OPT_VMWARE_TSC_MAP_NUM }, | |
97 | {OPT_XEN_DOM0, 0, NULL, OPT_XEN_DOM0_NUM }, | |
98 | {0, 0, NULL, 0 } | |
99 | }; | |
100 | ||
101 | TAILQ_HEAD(shared_driver_list, shared_driver); | |
102 | ||
103 | /* Definition for shared object drivers. */ | |
104 | struct shared_driver { | |
105 | TAILQ_ENTRY(shared_driver) next; | |
106 | ||
107 | char name[PATH_MAX]; | |
108 | void* lib_handle; | |
109 | }; | |
110 | ||
111 | /* List of external loadable drivers */ | |
112 | static struct shared_driver_list solib_list = | |
113 | TAILQ_HEAD_INITIALIZER(solib_list); | |
114 | ||
115 | /* Default path of external loadable drivers */ | |
116 | static const char *default_solib_dir = RTE_EAL_PMD_PATH; | |
117 | ||
118 | /* | |
119 | * Stringified version of solib path used by dpdk-pmdinfo.py | |
120 | * Note: PLEASE DO NOT ALTER THIS without making a corresponding | |
121 | * change to tools/dpdk-pmdinfo.py | |
122 | */ | |
123 | static const char dpdk_solib_path[] __attribute__((used)) = | |
124 | "DPDK_PLUGIN_PATH=" RTE_EAL_PMD_PATH; | |
125 | ||
126 | ||
127 | static int master_lcore_parsed; | |
128 | static int mem_parsed; | |
129 | ||
130 | void | |
131 | eal_reset_internal_config(struct internal_config *internal_cfg) | |
132 | { | |
133 | int i; | |
134 | ||
135 | internal_cfg->memory = 0; | |
136 | internal_cfg->force_nrank = 0; | |
137 | internal_cfg->force_nchannel = 0; | |
138 | internal_cfg->hugefile_prefix = HUGEFILE_PREFIX_DEFAULT; | |
139 | internal_cfg->hugepage_dir = NULL; | |
140 | internal_cfg->force_sockets = 0; | |
141 | /* zero out the NUMA config */ | |
142 | for (i = 0; i < RTE_MAX_NUMA_NODES; i++) | |
143 | internal_cfg->socket_mem[i] = 0; | |
144 | /* zero out hugedir descriptors */ | |
145 | for (i = 0; i < MAX_HUGEPAGE_SIZES; i++) | |
146 | internal_cfg->hugepage_info[i].lock_descriptor = -1; | |
147 | internal_cfg->base_virtaddr = 0; | |
148 | ||
149 | internal_cfg->syslog_facility = LOG_DAEMON; | |
150 | /* default value from build option */ | |
151 | #if RTE_LOG_LEVEL >= RTE_LOG_DEBUG | |
152 | internal_cfg->log_level = RTE_LOG_INFO; | |
153 | #else | |
154 | internal_cfg->log_level = RTE_LOG_LEVEL; | |
155 | #endif | |
156 | ||
157 | internal_cfg->xen_dom0_support = 0; | |
158 | ||
159 | /* if set to NONE, interrupt mode is determined automatically */ | |
160 | internal_cfg->vfio_intr_mode = RTE_INTR_MODE_NONE; | |
161 | ||
162 | #ifdef RTE_LIBEAL_USE_HPET | |
163 | internal_cfg->no_hpet = 0; | |
164 | #else | |
165 | internal_cfg->no_hpet = 1; | |
166 | #endif | |
167 | internal_cfg->vmware_tsc_map = 0; | |
168 | internal_cfg->create_uio_dev = 0; | |
169 | } | |
170 | ||
171 | static int | |
172 | eal_plugin_add(const char *path) | |
173 | { | |
174 | struct shared_driver *solib; | |
175 | ||
176 | solib = malloc(sizeof(*solib)); | |
177 | if (solib == NULL) { | |
178 | RTE_LOG(ERR, EAL, "malloc(solib) failed\n"); | |
179 | return -1; | |
180 | } | |
181 | memset(solib, 0, sizeof(*solib)); | |
182 | strncpy(solib->name, path, PATH_MAX-1); | |
183 | solib->name[PATH_MAX-1] = 0; | |
184 | TAILQ_INSERT_TAIL(&solib_list, solib, next); | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | static int | |
190 | eal_plugindir_init(const char *path) | |
191 | { | |
192 | DIR *d = NULL; | |
193 | struct dirent *dent = NULL; | |
194 | char sopath[PATH_MAX]; | |
195 | ||
196 | if (path == NULL || *path == '\0') | |
197 | return 0; | |
198 | ||
199 | d = opendir(path); | |
200 | if (d == NULL) { | |
201 | RTE_LOG(ERR, EAL, "failed to open directory %s: %s\n", | |
202 | path, strerror(errno)); | |
203 | return -1; | |
204 | } | |
205 | ||
206 | while ((dent = readdir(d)) != NULL) { | |
207 | struct stat sb; | |
208 | ||
209 | snprintf(sopath, PATH_MAX-1, "%s/%s", path, dent->d_name); | |
210 | sopath[PATH_MAX-1] = 0; | |
211 | ||
212 | if (!(stat(sopath, &sb) == 0 && S_ISREG(sb.st_mode))) | |
213 | continue; | |
214 | ||
215 | if (eal_plugin_add(sopath) == -1) | |
216 | break; | |
217 | } | |
218 | ||
219 | closedir(d); | |
220 | /* XXX this ignores failures from readdir() itself */ | |
221 | return (dent == NULL) ? 0 : -1; | |
222 | } | |
223 | ||
224 | int | |
225 | eal_plugins_init(void) | |
226 | { | |
227 | struct shared_driver *solib = NULL; | |
228 | ||
229 | if (*default_solib_dir != '\0') | |
230 | eal_plugin_add(default_solib_dir); | |
231 | ||
232 | TAILQ_FOREACH(solib, &solib_list, next) { | |
233 | struct stat sb; | |
234 | ||
235 | if (stat(solib->name, &sb) == 0 && S_ISDIR(sb.st_mode)) { | |
236 | if (eal_plugindir_init(solib->name) == -1) { | |
237 | RTE_LOG(ERR, EAL, | |
238 | "Cannot init plugin directory %s\n", | |
239 | solib->name); | |
240 | return -1; | |
241 | } | |
242 | } else { | |
243 | RTE_LOG(DEBUG, EAL, "open shared lib %s\n", | |
244 | solib->name); | |
245 | solib->lib_handle = dlopen(solib->name, RTLD_NOW); | |
246 | if (solib->lib_handle == NULL) { | |
247 | RTE_LOG(ERR, EAL, "%s\n", dlerror()); | |
248 | return -1; | |
249 | } | |
250 | } | |
251 | ||
252 | } | |
253 | return 0; | |
254 | } | |
255 | ||
256 | /* | |
257 | * Parse the coremask given as argument (hexadecimal string) and fill | |
258 | * the global configuration (core role and core count) with the parsed | |
259 | * value. | |
260 | */ | |
261 | static int xdigit2val(unsigned char c) | |
262 | { | |
263 | int val; | |
264 | ||
265 | if (isdigit(c)) | |
266 | val = c - '0'; | |
267 | else if (isupper(c)) | |
268 | val = c - 'A' + 10; | |
269 | else | |
270 | val = c - 'a' + 10; | |
271 | return val; | |
272 | } | |
273 | ||
274 | static int | |
275 | eal_parse_coremask(const char *coremask) | |
276 | { | |
277 | struct rte_config *cfg = rte_eal_get_configuration(); | |
278 | int i, j, idx = 0; | |
279 | unsigned count = 0; | |
280 | char c; | |
281 | int val; | |
282 | ||
283 | if (coremask == NULL) | |
284 | return -1; | |
285 | /* Remove all blank characters ahead and after . | |
286 | * Remove 0x/0X if exists. | |
287 | */ | |
288 | while (isblank(*coremask)) | |
289 | coremask++; | |
290 | if (coremask[0] == '0' && ((coremask[1] == 'x') | |
291 | || (coremask[1] == 'X'))) | |
292 | coremask += 2; | |
293 | i = strlen(coremask); | |
294 | while ((i > 0) && isblank(coremask[i - 1])) | |
295 | i--; | |
296 | if (i == 0) | |
297 | return -1; | |
298 | ||
299 | for (i = i - 1; i >= 0 && idx < RTE_MAX_LCORE; i--) { | |
300 | c = coremask[i]; | |
301 | if (isxdigit(c) == 0) { | |
302 | /* invalid characters */ | |
303 | return -1; | |
304 | } | |
305 | val = xdigit2val(c); | |
306 | for (j = 0; j < BITS_PER_HEX && idx < RTE_MAX_LCORE; j++, idx++) | |
307 | { | |
308 | if ((1 << j) & val) { | |
309 | if (!lcore_config[idx].detected) { | |
310 | RTE_LOG(ERR, EAL, "lcore %u " | |
311 | "unavailable\n", idx); | |
312 | return -1; | |
313 | } | |
314 | cfg->lcore_role[idx] = ROLE_RTE; | |
315 | lcore_config[idx].core_index = count; | |
316 | count++; | |
317 | } else { | |
318 | cfg->lcore_role[idx] = ROLE_OFF; | |
319 | lcore_config[idx].core_index = -1; | |
320 | } | |
321 | } | |
322 | } | |
323 | for (; i >= 0; i--) | |
324 | if (coremask[i] != '0') | |
325 | return -1; | |
326 | for (; idx < RTE_MAX_LCORE; idx++) { | |
327 | cfg->lcore_role[idx] = ROLE_OFF; | |
328 | lcore_config[idx].core_index = -1; | |
329 | } | |
330 | if (count == 0) | |
331 | return -1; | |
332 | /* Update the count of enabled logical cores of the EAL configuration */ | |
333 | cfg->lcore_count = count; | |
334 | return 0; | |
335 | } | |
336 | ||
337 | static int | |
338 | eal_parse_corelist(const char *corelist) | |
339 | { | |
340 | struct rte_config *cfg = rte_eal_get_configuration(); | |
341 | int i, idx = 0; | |
342 | unsigned count = 0; | |
343 | char *end = NULL; | |
344 | int min, max; | |
345 | ||
346 | if (corelist == NULL) | |
347 | return -1; | |
348 | ||
349 | /* Remove all blank characters ahead and after */ | |
350 | while (isblank(*corelist)) | |
351 | corelist++; | |
352 | i = strlen(corelist); | |
353 | while ((i > 0) && isblank(corelist[i - 1])) | |
354 | i--; | |
355 | ||
356 | /* Reset config */ | |
357 | for (idx = 0; idx < RTE_MAX_LCORE; idx++) { | |
358 | cfg->lcore_role[idx] = ROLE_OFF; | |
359 | lcore_config[idx].core_index = -1; | |
360 | } | |
361 | ||
362 | /* Get list of cores */ | |
363 | min = RTE_MAX_LCORE; | |
364 | do { | |
365 | while (isblank(*corelist)) | |
366 | corelist++; | |
367 | if (*corelist == '\0') | |
368 | return -1; | |
369 | errno = 0; | |
370 | idx = strtoul(corelist, &end, 10); | |
371 | if (errno || end == NULL) | |
372 | return -1; | |
373 | while (isblank(*end)) | |
374 | end++; | |
375 | if (*end == '-') { | |
376 | min = idx; | |
377 | } else if ((*end == ',') || (*end == '\0')) { | |
378 | max = idx; | |
379 | if (min == RTE_MAX_LCORE) | |
380 | min = idx; | |
381 | for (idx = min; idx <= max; idx++) { | |
382 | if (cfg->lcore_role[idx] != ROLE_RTE) { | |
383 | cfg->lcore_role[idx] = ROLE_RTE; | |
384 | lcore_config[idx].core_index = count; | |
385 | count++; | |
386 | } | |
387 | } | |
388 | min = RTE_MAX_LCORE; | |
389 | } else | |
390 | return -1; | |
391 | corelist = end + 1; | |
392 | } while (*end != '\0'); | |
393 | ||
394 | if (count == 0) | |
395 | return -1; | |
396 | ||
397 | /* Update the count of enabled logical cores of the EAL configuration */ | |
398 | cfg->lcore_count = count; | |
399 | ||
400 | return 0; | |
401 | } | |
402 | ||
403 | /* Changes the lcore id of the master thread */ | |
404 | static int | |
405 | eal_parse_master_lcore(const char *arg) | |
406 | { | |
407 | char *parsing_end; | |
408 | struct rte_config *cfg = rte_eal_get_configuration(); | |
409 | ||
410 | errno = 0; | |
411 | cfg->master_lcore = (uint32_t) strtol(arg, &parsing_end, 0); | |
412 | if (errno || parsing_end[0] != 0) | |
413 | return -1; | |
414 | if (cfg->master_lcore >= RTE_MAX_LCORE) | |
415 | return -1; | |
416 | master_lcore_parsed = 1; | |
417 | return 0; | |
418 | } | |
419 | ||
420 | /* | |
421 | * Parse elem, the elem could be single number/range or '(' ')' group | |
422 | * 1) A single number elem, it's just a simple digit. e.g. 9 | |
423 | * 2) A single range elem, two digits with a '-' between. e.g. 2-6 | |
424 | * 3) A group elem, combines multiple 1) or 2) with '( )'. e.g (0,2-4,6) | |
425 | * Within group elem, '-' used for a range separator; | |
426 | * ',' used for a single number. | |
427 | */ | |
428 | static int | |
429 | eal_parse_set(const char *input, uint16_t set[], unsigned num) | |
430 | { | |
431 | unsigned idx; | |
432 | const char *str = input; | |
433 | char *end = NULL; | |
434 | unsigned min, max; | |
435 | ||
436 | memset(set, 0, num * sizeof(uint16_t)); | |
437 | ||
438 | while (isblank(*str)) | |
439 | str++; | |
440 | ||
441 | /* only digit or left bracket is qualify for start point */ | |
442 | if ((!isdigit(*str) && *str != '(') || *str == '\0') | |
443 | return -1; | |
444 | ||
445 | /* process single number or single range of number */ | |
446 | if (*str != '(') { | |
447 | errno = 0; | |
448 | idx = strtoul(str, &end, 10); | |
449 | if (errno || end == NULL || idx >= num) | |
450 | return -1; | |
451 | else { | |
452 | while (isblank(*end)) | |
453 | end++; | |
454 | ||
455 | min = idx; | |
456 | max = idx; | |
457 | if (*end == '-') { | |
458 | /* process single <number>-<number> */ | |
459 | end++; | |
460 | while (isblank(*end)) | |
461 | end++; | |
462 | if (!isdigit(*end)) | |
463 | return -1; | |
464 | ||
465 | errno = 0; | |
466 | idx = strtoul(end, &end, 10); | |
467 | if (errno || end == NULL || idx >= num) | |
468 | return -1; | |
469 | max = idx; | |
470 | while (isblank(*end)) | |
471 | end++; | |
472 | if (*end != ',' && *end != '\0') | |
473 | return -1; | |
474 | } | |
475 | ||
476 | if (*end != ',' && *end != '\0' && | |
477 | *end != '@') | |
478 | return -1; | |
479 | ||
480 | for (idx = RTE_MIN(min, max); | |
481 | idx <= RTE_MAX(min, max); idx++) | |
482 | set[idx] = 1; | |
483 | ||
484 | return end - input; | |
485 | } | |
486 | } | |
487 | ||
488 | /* process set within bracket */ | |
489 | str++; | |
490 | while (isblank(*str)) | |
491 | str++; | |
492 | if (*str == '\0') | |
493 | return -1; | |
494 | ||
495 | min = RTE_MAX_LCORE; | |
496 | do { | |
497 | ||
498 | /* go ahead to the first digit */ | |
499 | while (isblank(*str)) | |
500 | str++; | |
501 | if (!isdigit(*str)) | |
502 | return -1; | |
503 | ||
504 | /* get the digit value */ | |
505 | errno = 0; | |
506 | idx = strtoul(str, &end, 10); | |
507 | if (errno || end == NULL || idx >= num) | |
508 | return -1; | |
509 | ||
510 | /* go ahead to separator '-',',' and ')' */ | |
511 | while (isblank(*end)) | |
512 | end++; | |
513 | if (*end == '-') { | |
514 | if (min == RTE_MAX_LCORE) | |
515 | min = idx; | |
516 | else /* avoid continuous '-' */ | |
517 | return -1; | |
518 | } else if ((*end == ',') || (*end == ')')) { | |
519 | max = idx; | |
520 | if (min == RTE_MAX_LCORE) | |
521 | min = idx; | |
522 | for (idx = RTE_MIN(min, max); | |
523 | idx <= RTE_MAX(min, max); idx++) | |
524 | set[idx] = 1; | |
525 | ||
526 | min = RTE_MAX_LCORE; | |
527 | } else | |
528 | return -1; | |
529 | ||
530 | str = end + 1; | |
531 | } while (*end != '\0' && *end != ')'); | |
532 | ||
533 | /* | |
534 | * to avoid failure that tail blank makes end character check fail | |
535 | * in eal_parse_lcores( ) | |
536 | */ | |
537 | while (isblank(*str)) | |
538 | str++; | |
539 | ||
540 | return str - input; | |
541 | } | |
542 | ||
543 | /* convert from set array to cpuset bitmap */ | |
544 | static int | |
545 | convert_to_cpuset(rte_cpuset_t *cpusetp, | |
546 | uint16_t *set, unsigned num) | |
547 | { | |
548 | unsigned idx; | |
549 | ||
550 | CPU_ZERO(cpusetp); | |
551 | ||
552 | for (idx = 0; idx < num; idx++) { | |
553 | if (!set[idx]) | |
554 | continue; | |
555 | ||
556 | if (!lcore_config[idx].detected) { | |
557 | RTE_LOG(ERR, EAL, "core %u " | |
558 | "unavailable\n", idx); | |
559 | return -1; | |
560 | } | |
561 | ||
562 | CPU_SET(idx, cpusetp); | |
563 | } | |
564 | ||
565 | return 0; | |
566 | } | |
567 | ||
568 | /* | |
569 | * The format pattern: --lcores='<lcores[@cpus]>[<,lcores[@cpus]>...]' | |
570 | * lcores, cpus could be a single digit/range or a group. | |
571 | * '(' and ')' are necessary if it's a group. | |
572 | * If not supply '@cpus', the value of cpus uses the same as lcores. | |
573 | * e.g. '1,2@(5-7),(3-5)@(0,2),(0,6),7-8' means start 9 EAL thread as below | |
574 | * lcore 0 runs on cpuset 0x41 (cpu 0,6) | |
575 | * lcore 1 runs on cpuset 0x2 (cpu 1) | |
576 | * lcore 2 runs on cpuset 0xe0 (cpu 5,6,7) | |
577 | * lcore 3,4,5 runs on cpuset 0x5 (cpu 0,2) | |
578 | * lcore 6 runs on cpuset 0x41 (cpu 0,6) | |
579 | * lcore 7 runs on cpuset 0x80 (cpu 7) | |
580 | * lcore 8 runs on cpuset 0x100 (cpu 8) | |
581 | */ | |
582 | static int | |
583 | eal_parse_lcores(const char *lcores) | |
584 | { | |
585 | struct rte_config *cfg = rte_eal_get_configuration(); | |
586 | static uint16_t set[RTE_MAX_LCORE]; | |
587 | unsigned idx = 0; | |
588 | unsigned count = 0; | |
589 | const char *lcore_start = NULL; | |
590 | const char *end = NULL; | |
591 | int offset; | |
592 | rte_cpuset_t cpuset; | |
593 | int lflags; | |
594 | int ret = -1; | |
595 | ||
596 | if (lcores == NULL) | |
597 | return -1; | |
598 | ||
599 | /* Remove all blank characters ahead and after */ | |
600 | while (isblank(*lcores)) | |
601 | lcores++; | |
602 | ||
603 | CPU_ZERO(&cpuset); | |
604 | ||
605 | /* Reset lcore config */ | |
606 | for (idx = 0; idx < RTE_MAX_LCORE; idx++) { | |
607 | cfg->lcore_role[idx] = ROLE_OFF; | |
608 | lcore_config[idx].core_index = -1; | |
609 | CPU_ZERO(&lcore_config[idx].cpuset); | |
610 | } | |
611 | ||
612 | /* Get list of cores */ | |
613 | do { | |
614 | while (isblank(*lcores)) | |
615 | lcores++; | |
616 | if (*lcores == '\0') | |
617 | goto err; | |
618 | ||
619 | lflags = 0; | |
620 | ||
621 | /* record lcore_set start point */ | |
622 | lcore_start = lcores; | |
623 | ||
624 | /* go across a complete bracket */ | |
625 | if (*lcore_start == '(') { | |
626 | lcores += strcspn(lcores, ")"); | |
627 | if (*lcores++ == '\0') | |
628 | goto err; | |
629 | } | |
630 | ||
631 | /* scan the separator '@', ','(next) or '\0'(finish) */ | |
632 | lcores += strcspn(lcores, "@,"); | |
633 | ||
634 | if (*lcores == '@') { | |
635 | /* explicit assign cpu_set */ | |
636 | offset = eal_parse_set(lcores + 1, set, RTE_DIM(set)); | |
637 | if (offset < 0) | |
638 | goto err; | |
639 | ||
640 | /* prepare cpu_set and update the end cursor */ | |
641 | if (0 > convert_to_cpuset(&cpuset, | |
642 | set, RTE_DIM(set))) | |
643 | goto err; | |
644 | end = lcores + 1 + offset; | |
645 | } else { /* ',' or '\0' */ | |
646 | /* haven't given cpu_set, current loop done */ | |
647 | end = lcores; | |
648 | ||
649 | /* go back to check <number>-<number> */ | |
650 | offset = strcspn(lcore_start, "(-"); | |
651 | if (offset < (end - lcore_start) && | |
652 | *(lcore_start + offset) != '(') | |
653 | lflags = 1; | |
654 | } | |
655 | ||
656 | if (*end != ',' && *end != '\0') | |
657 | goto err; | |
658 | ||
659 | /* parse lcore_set from start point */ | |
660 | if (0 > eal_parse_set(lcore_start, set, RTE_DIM(set))) | |
661 | goto err; | |
662 | ||
663 | /* without '@', by default using lcore_set as cpu_set */ | |
664 | if (*lcores != '@' && | |
665 | 0 > convert_to_cpuset(&cpuset, set, RTE_DIM(set))) | |
666 | goto err; | |
667 | ||
668 | /* start to update lcore_set */ | |
669 | for (idx = 0; idx < RTE_MAX_LCORE; idx++) { | |
670 | if (!set[idx]) | |
671 | continue; | |
672 | ||
673 | if (cfg->lcore_role[idx] != ROLE_RTE) { | |
674 | lcore_config[idx].core_index = count; | |
675 | cfg->lcore_role[idx] = ROLE_RTE; | |
676 | count++; | |
677 | } | |
678 | ||
679 | if (lflags) { | |
680 | CPU_ZERO(&cpuset); | |
681 | CPU_SET(idx, &cpuset); | |
682 | } | |
683 | rte_memcpy(&lcore_config[idx].cpuset, &cpuset, | |
684 | sizeof(rte_cpuset_t)); | |
685 | } | |
686 | ||
687 | lcores = end + 1; | |
688 | } while (*end != '\0'); | |
689 | ||
690 | if (count == 0) | |
691 | goto err; | |
692 | ||
693 | cfg->lcore_count = count; | |
694 | ret = 0; | |
695 | ||
696 | err: | |
697 | ||
698 | return ret; | |
699 | } | |
700 | ||
701 | static int | |
702 | eal_parse_syslog(const char *facility, struct internal_config *conf) | |
703 | { | |
704 | int i; | |
705 | static struct { | |
706 | const char *name; | |
707 | int value; | |
708 | } map[] = { | |
709 | { "auth", LOG_AUTH }, | |
710 | { "cron", LOG_CRON }, | |
711 | { "daemon", LOG_DAEMON }, | |
712 | { "ftp", LOG_FTP }, | |
713 | { "kern", LOG_KERN }, | |
714 | { "lpr", LOG_LPR }, | |
715 | { "mail", LOG_MAIL }, | |
716 | { "news", LOG_NEWS }, | |
717 | { "syslog", LOG_SYSLOG }, | |
718 | { "user", LOG_USER }, | |
719 | { "uucp", LOG_UUCP }, | |
720 | { "local0", LOG_LOCAL0 }, | |
721 | { "local1", LOG_LOCAL1 }, | |
722 | { "local2", LOG_LOCAL2 }, | |
723 | { "local3", LOG_LOCAL3 }, | |
724 | { "local4", LOG_LOCAL4 }, | |
725 | { "local5", LOG_LOCAL5 }, | |
726 | { "local6", LOG_LOCAL6 }, | |
727 | { "local7", LOG_LOCAL7 }, | |
728 | { NULL, 0 } | |
729 | }; | |
730 | ||
731 | for (i = 0; map[i].name; i++) { | |
732 | if (!strcmp(facility, map[i].name)) { | |
733 | conf->syslog_facility = map[i].value; | |
734 | return 0; | |
735 | } | |
736 | } | |
737 | return -1; | |
738 | } | |
739 | ||
740 | static int | |
741 | eal_parse_log_level(const char *level, uint32_t *log_level) | |
742 | { | |
743 | char *end; | |
744 | unsigned long tmp; | |
745 | ||
746 | errno = 0; | |
747 | tmp = strtoul(level, &end, 0); | |
748 | ||
749 | /* check for errors */ | |
750 | if ((errno != 0) || (level[0] == '\0') || | |
751 | end == NULL || (*end != '\0')) | |
752 | return -1; | |
753 | ||
754 | /* log_level is a uint32_t */ | |
755 | if (tmp >= UINT32_MAX) | |
756 | return -1; | |
757 | ||
758 | *log_level = tmp; | |
759 | return 0; | |
760 | } | |
761 | ||
762 | static enum rte_proc_type_t | |
763 | eal_parse_proc_type(const char *arg) | |
764 | { | |
765 | if (strncasecmp(arg, "primary", sizeof("primary")) == 0) | |
766 | return RTE_PROC_PRIMARY; | |
767 | if (strncasecmp(arg, "secondary", sizeof("secondary")) == 0) | |
768 | return RTE_PROC_SECONDARY; | |
769 | if (strncasecmp(arg, "auto", sizeof("auto")) == 0) | |
770 | return RTE_PROC_AUTO; | |
771 | ||
772 | return RTE_PROC_INVALID; | |
773 | } | |
774 | ||
775 | int | |
776 | eal_parse_common_option(int opt, const char *optarg, | |
777 | struct internal_config *conf) | |
778 | { | |
779 | switch (opt) { | |
780 | /* blacklist */ | |
781 | case 'b': | |
782 | if (rte_eal_devargs_add(RTE_DEVTYPE_BLACKLISTED_PCI, | |
783 | optarg) < 0) { | |
784 | return -1; | |
785 | } | |
786 | break; | |
787 | /* whitelist */ | |
788 | case 'w': | |
789 | if (rte_eal_devargs_add(RTE_DEVTYPE_WHITELISTED_PCI, | |
790 | optarg) < 0) { | |
791 | return -1; | |
792 | } | |
793 | break; | |
794 | /* coremask */ | |
795 | case 'c': | |
796 | if (eal_parse_coremask(optarg) < 0) { | |
797 | RTE_LOG(ERR, EAL, "invalid coremask\n"); | |
798 | return -1; | |
799 | } | |
800 | break; | |
801 | /* corelist */ | |
802 | case 'l': | |
803 | if (eal_parse_corelist(optarg) < 0) { | |
804 | RTE_LOG(ERR, EAL, "invalid core list\n"); | |
805 | return -1; | |
806 | } | |
807 | break; | |
808 | /* size of memory */ | |
809 | case 'm': | |
810 | conf->memory = atoi(optarg); | |
811 | conf->memory *= 1024ULL; | |
812 | conf->memory *= 1024ULL; | |
813 | mem_parsed = 1; | |
814 | break; | |
815 | /* force number of channels */ | |
816 | case 'n': | |
817 | conf->force_nchannel = atoi(optarg); | |
818 | if (conf->force_nchannel == 0) { | |
819 | RTE_LOG(ERR, EAL, "invalid channel number\n"); | |
820 | return -1; | |
821 | } | |
822 | break; | |
823 | /* force number of ranks */ | |
824 | case 'r': | |
825 | conf->force_nrank = atoi(optarg); | |
826 | if (conf->force_nrank == 0 || | |
827 | conf->force_nrank > 16) { | |
828 | RTE_LOG(ERR, EAL, "invalid rank number\n"); | |
829 | return -1; | |
830 | } | |
831 | break; | |
832 | /* force loading of external driver */ | |
833 | case 'd': | |
834 | if (eal_plugin_add(optarg) == -1) | |
835 | return -1; | |
836 | break; | |
837 | case 'v': | |
838 | /* since message is explicitly requested by user, we | |
839 | * write message at highest log level so it can always | |
840 | * be seen | |
841 | * even if info or warning messages are disabled */ | |
842 | RTE_LOG(CRIT, EAL, "RTE Version: '%s'\n", rte_version()); | |
843 | break; | |
844 | ||
845 | /* long options */ | |
846 | case OPT_HUGE_UNLINK_NUM: | |
847 | conf->hugepage_unlink = 1; | |
848 | break; | |
849 | ||
850 | case OPT_NO_HUGE_NUM: | |
851 | conf->no_hugetlbfs = 1; | |
852 | break; | |
853 | ||
854 | case OPT_NO_PCI_NUM: | |
855 | conf->no_pci = 1; | |
856 | break; | |
857 | ||
858 | case OPT_NO_HPET_NUM: | |
859 | conf->no_hpet = 1; | |
860 | break; | |
861 | ||
862 | case OPT_VMWARE_TSC_MAP_NUM: | |
863 | conf->vmware_tsc_map = 1; | |
864 | break; | |
865 | ||
866 | case OPT_NO_SHCONF_NUM: | |
867 | conf->no_shconf = 1; | |
868 | break; | |
869 | ||
870 | case OPT_PROC_TYPE_NUM: | |
871 | conf->process_type = eal_parse_proc_type(optarg); | |
872 | break; | |
873 | ||
874 | case OPT_MASTER_LCORE_NUM: | |
875 | if (eal_parse_master_lcore(optarg) < 0) { | |
876 | RTE_LOG(ERR, EAL, "invalid parameter for --" | |
877 | OPT_MASTER_LCORE "\n"); | |
878 | return -1; | |
879 | } | |
880 | break; | |
881 | ||
882 | case OPT_VDEV_NUM: | |
883 | if (rte_eal_devargs_add(RTE_DEVTYPE_VIRTUAL, | |
884 | optarg) < 0) { | |
885 | return -1; | |
886 | } | |
887 | break; | |
888 | ||
889 | case OPT_SYSLOG_NUM: | |
890 | if (eal_parse_syslog(optarg, conf) < 0) { | |
891 | RTE_LOG(ERR, EAL, "invalid parameters for --" | |
892 | OPT_SYSLOG "\n"); | |
893 | return -1; | |
894 | } | |
895 | break; | |
896 | ||
897 | case OPT_LOG_LEVEL_NUM: { | |
898 | uint32_t log; | |
899 | ||
900 | if (eal_parse_log_level(optarg, &log) < 0) { | |
901 | RTE_LOG(ERR, EAL, | |
902 | "invalid parameters for --" | |
903 | OPT_LOG_LEVEL "\n"); | |
904 | return -1; | |
905 | } | |
906 | conf->log_level = log; | |
907 | break; | |
908 | } | |
909 | case OPT_LCORES_NUM: | |
910 | if (eal_parse_lcores(optarg) < 0) { | |
911 | RTE_LOG(ERR, EAL, "invalid parameter for --" | |
912 | OPT_LCORES "\n"); | |
913 | return -1; | |
914 | } | |
915 | break; | |
916 | ||
917 | /* don't know what to do, leave this to caller */ | |
918 | default: | |
919 | return 1; | |
920 | ||
921 | } | |
922 | ||
923 | return 0; | |
924 | } | |
925 | ||
926 | int | |
927 | eal_adjust_config(struct internal_config *internal_cfg) | |
928 | { | |
929 | int i; | |
930 | struct rte_config *cfg = rte_eal_get_configuration(); | |
931 | ||
932 | if (internal_config.process_type == RTE_PROC_AUTO) | |
933 | internal_config.process_type = eal_proc_type_detect(); | |
934 | ||
935 | /* default master lcore is the first one */ | |
936 | if (!master_lcore_parsed) | |
937 | cfg->master_lcore = rte_get_next_lcore(-1, 0, 0); | |
938 | ||
939 | /* if no memory amounts were requested, this will result in 0 and | |
940 | * will be overridden later, right after eal_hugepage_info_init() */ | |
941 | for (i = 0; i < RTE_MAX_NUMA_NODES; i++) | |
942 | internal_cfg->memory += internal_cfg->socket_mem[i]; | |
943 | ||
944 | return 0; | |
945 | } | |
946 | ||
947 | int | |
948 | eal_check_common_options(struct internal_config *internal_cfg) | |
949 | { | |
950 | struct rte_config *cfg = rte_eal_get_configuration(); | |
951 | ||
952 | if (cfg->lcore_role[cfg->master_lcore] != ROLE_RTE) { | |
953 | RTE_LOG(ERR, EAL, "Master lcore is not enabled for DPDK\n"); | |
954 | return -1; | |
955 | } | |
956 | ||
957 | if (internal_cfg->process_type == RTE_PROC_INVALID) { | |
958 | RTE_LOG(ERR, EAL, "Invalid process type specified\n"); | |
959 | return -1; | |
960 | } | |
961 | if (index(internal_cfg->hugefile_prefix, '%') != NULL) { | |
962 | RTE_LOG(ERR, EAL, "Invalid char, '%%', in --"OPT_FILE_PREFIX" " | |
963 | "option\n"); | |
964 | return -1; | |
965 | } | |
966 | if (mem_parsed && internal_cfg->force_sockets == 1) { | |
967 | RTE_LOG(ERR, EAL, "Options -m and --"OPT_SOCKET_MEM" cannot " | |
968 | "be specified at the same time\n"); | |
969 | return -1; | |
970 | } | |
971 | if (internal_cfg->no_hugetlbfs && internal_cfg->force_sockets == 1) { | |
972 | RTE_LOG(ERR, EAL, "Option --"OPT_SOCKET_MEM" cannot " | |
973 | "be specified together with --"OPT_NO_HUGE"\n"); | |
974 | return -1; | |
975 | } | |
976 | ||
977 | if (internal_cfg->no_hugetlbfs && internal_cfg->hugepage_unlink) { | |
978 | RTE_LOG(ERR, EAL, "Option --"OPT_HUGE_UNLINK" cannot " | |
979 | "be specified together with --"OPT_NO_HUGE"\n"); | |
980 | return -1; | |
981 | } | |
982 | ||
983 | if (rte_eal_devargs_type_count(RTE_DEVTYPE_WHITELISTED_PCI) != 0 && | |
984 | rte_eal_devargs_type_count(RTE_DEVTYPE_BLACKLISTED_PCI) != 0) { | |
985 | RTE_LOG(ERR, EAL, "Options blacklist (-b) and whitelist (-w) " | |
986 | "cannot be used at the same time\n"); | |
987 | return -1; | |
988 | } | |
989 | ||
990 | return 0; | |
991 | } | |
992 | ||
993 | void | |
994 | eal_common_usage(void) | |
995 | { | |
996 | printf("[options]\n\n" | |
997 | "EAL common options:\n" | |
998 | " -c COREMASK Hexadecimal bitmask of cores to run on\n" | |
999 | " -l CORELIST List of cores to run on\n" | |
1000 | " The argument format is <c1>[-c2][,c3[-c4],...]\n" | |
1001 | " where c1, c2, etc are core indexes between 0 and %d\n" | |
1002 | " --"OPT_LCORES" COREMAP Map lcore set to physical cpu set\n" | |
1003 | " The argument format is\n" | |
1004 | " '<lcores[@cpus]>[<,lcores[@cpus]>...]'\n" | |
1005 | " lcores and cpus list are grouped by '(' and ')'\n" | |
1006 | " Within the group, '-' is used for range separator,\n" | |
1007 | " ',' is used for single number separator.\n" | |
1008 | " '( )' can be omitted for single element group,\n" | |
1009 | " '@' can be omitted if cpus and lcores have the same value\n" | |
1010 | " --"OPT_MASTER_LCORE" ID Core ID that is used as master\n" | |
1011 | " -n CHANNELS Number of memory channels\n" | |
1012 | " -m MB Memory to allocate (see also --"OPT_SOCKET_MEM")\n" | |
1013 | " -r RANKS Force number of memory ranks (don't detect)\n" | |
1014 | " -b, --"OPT_PCI_BLACKLIST" Add a PCI device in black list.\n" | |
1015 | " Prevent EAL from using this PCI device. The argument\n" | |
1016 | " format is <domain:bus:devid.func>.\n" | |
1017 | " -w, --"OPT_PCI_WHITELIST" Add a PCI device in white list.\n" | |
1018 | " Only use the specified PCI devices. The argument format\n" | |
1019 | " is <[domain:]bus:devid.func>. This option can be present\n" | |
1020 | " several times (once per device).\n" | |
1021 | " [NOTE: PCI whitelist cannot be used with -b option]\n" | |
1022 | " --"OPT_VDEV" Add a virtual device.\n" | |
1023 | " The argument format is <driver><id>[,key=val,...]\n" | |
1024 | " (ex: --vdev=net_pcap0,iface=eth2).\n" | |
1025 | " -d LIB.so|DIR Add a driver or driver directory\n" | |
1026 | " (can be used multiple times)\n" | |
1027 | " --"OPT_VMWARE_TSC_MAP" Use VMware TSC map instead of native RDTSC\n" | |
1028 | " --"OPT_PROC_TYPE" Type of this process (primary|secondary|auto)\n" | |
1029 | " --"OPT_SYSLOG" Set syslog facility\n" | |
1030 | " --"OPT_LOG_LEVEL" Set default log level\n" | |
1031 | " -v Display version information on startup\n" | |
1032 | " -h, --help This help\n" | |
1033 | "\nEAL options for DEBUG use only:\n" | |
1034 | " --"OPT_HUGE_UNLINK" Unlink hugepage files after init\n" | |
1035 | " --"OPT_NO_HUGE" Use malloc instead of hugetlbfs\n" | |
1036 | " --"OPT_NO_PCI" Disable PCI\n" | |
1037 | " --"OPT_NO_HPET" Disable HPET\n" | |
1038 | " --"OPT_NO_SHCONF" No shared config (mmap'd files)\n" | |
1039 | "\n", RTE_MAX_LCORE); | |
1040 | } |