]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*- |
2 | * BSD LICENSE | |
3 | * | |
4 | * Copyright (c) Intel Corporation. | |
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 | ||
11fdf7f2 TL |
34 | #include "spdk/stdinc.h" |
35 | ||
7c673cae FG |
36 | #include "env_internal.h" |
37 | ||
11fdf7f2 | 38 | #include "spdk/version.h" |
7c673cae FG |
39 | |
40 | #include <rte_config.h> | |
41 | #include <rte_eal.h> | |
42 | ||
43 | #define SPDK_ENV_DPDK_DEFAULT_NAME "spdk" | |
44 | #define SPDK_ENV_DPDK_DEFAULT_SHM_ID -1 | |
45 | #define SPDK_ENV_DPDK_DEFAULT_MEM_SIZE -1 | |
46 | #define SPDK_ENV_DPDK_DEFAULT_MASTER_CORE -1 | |
47 | #define SPDK_ENV_DPDK_DEFAULT_MEM_CHANNEL -1 | |
48 | #define SPDK_ENV_DPDK_DEFAULT_CORE_MASK "0x1" | |
49 | ||
11fdf7f2 TL |
50 | static char **eal_cmdline; |
51 | static int eal_cmdline_argcount; | |
52 | ||
7c673cae FG |
53 | static char * |
54 | _sprintf_alloc(const char *format, ...) | |
55 | { | |
56 | va_list args; | |
57 | va_list args_copy; | |
58 | char *buf; | |
59 | size_t bufsize; | |
60 | int rc; | |
61 | ||
62 | va_start(args, format); | |
63 | ||
64 | /* Try with a small buffer first. */ | |
65 | bufsize = 32; | |
66 | ||
67 | /* Limit maximum buffer size to something reasonable so we don't loop forever. */ | |
68 | while (bufsize <= 1024 * 1024) { | |
69 | buf = malloc(bufsize); | |
70 | if (buf == NULL) { | |
71 | va_end(args); | |
72 | return NULL; | |
73 | } | |
74 | ||
75 | va_copy(args_copy, args); | |
76 | rc = vsnprintf(buf, bufsize, format, args_copy); | |
77 | va_end(args_copy); | |
78 | ||
79 | /* | |
80 | * If vsnprintf() returned a count within our current buffer size, we are done. | |
81 | * The count does not include the \0 terminator, so rc == bufsize is not OK. | |
82 | */ | |
83 | if (rc >= 0 && (size_t)rc < bufsize) { | |
84 | va_end(args); | |
85 | return buf; | |
86 | } | |
87 | ||
88 | /* | |
89 | * vsnprintf() should return the required space, but some libc versions do not | |
90 | * implement this correctly, so just double the buffer size and try again. | |
91 | * | |
92 | * We don't need the data in buf, so rather than realloc(), use free() and malloc() | |
93 | * again to avoid a copy. | |
94 | */ | |
95 | free(buf); | |
96 | bufsize *= 2; | |
97 | } | |
98 | ||
99 | va_end(args); | |
100 | return NULL; | |
101 | } | |
102 | ||
11fdf7f2 TL |
103 | static void |
104 | spdk_env_unlink_shared_files(void) | |
105 | { | |
106 | /* Starting with DPDK 18.05, there are more files with unpredictable paths | |
107 | * and filenames. The --no-shconf option prevents from creating them, but | |
108 | * only for DPDK 18.08+. For DPDK 18.05 we just leave them be. | |
109 | */ | |
110 | #if RTE_VERSION < RTE_VERSION_NUM(18, 05, 0, 0) | |
111 | char buffer[PATH_MAX]; | |
112 | ||
113 | snprintf(buffer, PATH_MAX, "/var/run/.spdk_pid%d_hugepage_info", getpid()); | |
114 | if (unlink(buffer)) { | |
115 | fprintf(stderr, "Unable to unlink shared memory file: %s. Error code: %d\n", buffer, errno); | |
116 | } | |
117 | #endif | |
118 | } | |
119 | ||
7c673cae FG |
120 | void |
121 | spdk_env_opts_init(struct spdk_env_opts *opts) | |
122 | { | |
123 | if (!opts) { | |
124 | return; | |
125 | } | |
126 | ||
127 | memset(opts, 0, sizeof(*opts)); | |
128 | ||
129 | opts->name = SPDK_ENV_DPDK_DEFAULT_NAME; | |
130 | opts->core_mask = SPDK_ENV_DPDK_DEFAULT_CORE_MASK; | |
131 | opts->shm_id = SPDK_ENV_DPDK_DEFAULT_SHM_ID; | |
11fdf7f2 TL |
132 | opts->mem_size = SPDK_ENV_DPDK_DEFAULT_MEM_SIZE; |
133 | opts->master_core = SPDK_ENV_DPDK_DEFAULT_MASTER_CORE; | |
134 | opts->mem_channel = SPDK_ENV_DPDK_DEFAULT_MEM_CHANNEL; | |
7c673cae FG |
135 | } |
136 | ||
137 | static void | |
138 | spdk_free_args(char **args, int argcount) | |
139 | { | |
140 | int i; | |
141 | ||
7c673cae | 142 | for (i = 0; i < argcount; i++) { |
7c673cae FG |
143 | free(args[i]); |
144 | } | |
145 | ||
11fdf7f2 TL |
146 | if (argcount) { |
147 | free(args); | |
148 | } | |
7c673cae FG |
149 | } |
150 | ||
151 | static char ** | |
152 | spdk_push_arg(char *args[], int *argcount, char *arg) | |
153 | { | |
154 | char **tmp; | |
155 | ||
156 | if (arg == NULL) { | |
11fdf7f2 TL |
157 | fprintf(stderr, "%s: NULL arg supplied\n", __func__); |
158 | spdk_free_args(args, *argcount); | |
7c673cae FG |
159 | return NULL; |
160 | } | |
161 | ||
162 | tmp = realloc(args, sizeof(char *) * (*argcount + 1)); | |
163 | if (tmp == NULL) { | |
164 | spdk_free_args(args, *argcount); | |
165 | return NULL; | |
166 | } | |
167 | ||
168 | tmp[*argcount] = arg; | |
169 | (*argcount)++; | |
170 | ||
171 | return tmp; | |
172 | } | |
173 | ||
11fdf7f2 TL |
174 | static void |
175 | spdk_destruct_eal_cmdline(void) | |
176 | { | |
177 | spdk_free_args(eal_cmdline, eal_cmdline_argcount); | |
178 | } | |
179 | ||
180 | ||
7c673cae | 181 | static int |
11fdf7f2 | 182 | spdk_build_eal_cmdline(const struct spdk_env_opts *opts) |
7c673cae FG |
183 | { |
184 | int argcount = 0; | |
185 | char **args; | |
186 | ||
7c673cae FG |
187 | args = NULL; |
188 | ||
189 | /* set the program name */ | |
190 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("%s", opts->name)); | |
191 | if (args == NULL) { | |
192 | return -1; | |
193 | } | |
194 | ||
11fdf7f2 TL |
195 | /* disable shared configuration files when in single process mode. This allows for cleaner shutdown */ |
196 | if (opts->shm_id < 0) { | |
197 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("%s", "--no-shconf")); | |
198 | if (args == NULL) { | |
199 | return -1; | |
200 | } | |
201 | } | |
202 | ||
7c673cae | 203 | /* set the coremask */ |
11fdf7f2 TL |
204 | /* NOTE: If coremask starts with '[' and ends with ']' it is a core list |
205 | */ | |
206 | if (opts->core_mask[0] == '[') { | |
207 | char *l_arg = _sprintf_alloc("-l %s", opts->core_mask + 1); | |
208 | int len = strlen(l_arg); | |
209 | if (l_arg[len - 1] == ']') { | |
210 | l_arg[len - 1] = '\0'; | |
211 | } | |
212 | args = spdk_push_arg(args, &argcount, l_arg); | |
213 | } else { | |
214 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("-c %s", opts->core_mask)); | |
215 | } | |
216 | ||
7c673cae FG |
217 | if (args == NULL) { |
218 | return -1; | |
219 | } | |
220 | ||
221 | /* set the memory channel number */ | |
11fdf7f2 TL |
222 | if (opts->mem_channel > 0) { |
223 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("-n %d", opts->mem_channel)); | |
7c673cae FG |
224 | if (args == NULL) { |
225 | return -1; | |
226 | } | |
227 | } | |
228 | ||
229 | /* set the memory size */ | |
11fdf7f2 TL |
230 | if (opts->mem_size >= 0) { |
231 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("-m %d", opts->mem_size)); | |
7c673cae FG |
232 | if (args == NULL) { |
233 | return -1; | |
234 | } | |
235 | } | |
236 | ||
237 | /* set the master core */ | |
11fdf7f2 | 238 | if (opts->master_core > 0) { |
7c673cae | 239 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("--master-lcore=%d", |
11fdf7f2 TL |
240 | opts->master_core)); |
241 | if (args == NULL) { | |
242 | return -1; | |
243 | } | |
244 | } | |
245 | ||
246 | /* set no pci if enabled */ | |
247 | if (opts->no_pci) { | |
248 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("--no-pci")); | |
249 | if (args == NULL) { | |
250 | return -1; | |
251 | } | |
252 | } | |
253 | ||
254 | /* create just one hugetlbfs file */ | |
255 | if (opts->hugepage_single_segments) { | |
256 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("--single-file-segments")); | |
257 | if (args == NULL) { | |
258 | return -1; | |
259 | } | |
260 | } | |
261 | ||
262 | /* unlink hugepages after initialization */ | |
263 | if (opts->unlink_hugepage) { | |
264 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("--huge-unlink")); | |
7c673cae FG |
265 | if (args == NULL) { |
266 | return -1; | |
267 | } | |
268 | } | |
269 | ||
11fdf7f2 TL |
270 | #if RTE_VERSION >= RTE_VERSION_NUM(18, 05, 0, 0) && RTE_VERSION < RTE_VERSION_NUM(18, 5, 1, 0) |
271 | /* Dynamic memory management is buggy in DPDK 18.05.0. Don't use it. */ | |
272 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("--legacy-mem")); | |
273 | if (args == NULL) { | |
274 | return -1; | |
275 | } | |
276 | #endif | |
277 | ||
278 | if (opts->num_pci_addr) { | |
279 | size_t i; | |
280 | char bdf[32]; | |
281 | struct spdk_pci_addr *pci_addr = | |
282 | opts->pci_blacklist ? opts->pci_blacklist : opts->pci_whitelist; | |
283 | ||
284 | for (i = 0; i < opts->num_pci_addr; i++) { | |
285 | spdk_pci_addr_fmt(bdf, 32, &pci_addr[i]); | |
286 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("%s=%s", | |
287 | (opts->pci_blacklist ? "--pci-blacklist" : "--pci-whitelist"), | |
288 | bdf)); | |
289 | if (args == NULL) { | |
290 | return -1; | |
291 | } | |
292 | } | |
293 | } | |
294 | ||
7c673cae FG |
295 | #ifdef __linux__ |
296 | if (opts->shm_id < 0) { | |
297 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("--file-prefix=spdk_pid%d", | |
298 | getpid())); | |
299 | if (args == NULL) { | |
300 | return -1; | |
301 | } | |
302 | } else { | |
303 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("--file-prefix=spdk%d", | |
304 | opts->shm_id)); | |
305 | if (args == NULL) { | |
306 | return -1; | |
307 | } | |
308 | ||
11fdf7f2 TL |
309 | /* Set the base virtual address - it must be an address that is not in the |
310 | * ASAN shadow region, otherwise ASAN-enabled builds will ignore the | |
311 | * mmap hint. | |
312 | * | |
313 | * Ref: https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm | |
314 | */ | |
315 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("--base-virtaddr=0x200000000000")); | |
7c673cae FG |
316 | if (args == NULL) { |
317 | return -1; | |
318 | } | |
319 | ||
320 | /* set the process type */ | |
321 | args = spdk_push_arg(args, &argcount, _sprintf_alloc("--proc-type=auto")); | |
322 | if (args == NULL) { | |
323 | return -1; | |
324 | } | |
325 | } | |
326 | #endif | |
327 | ||
11fdf7f2 TL |
328 | eal_cmdline = args; |
329 | eal_cmdline_argcount = argcount; | |
330 | if (atexit(spdk_destruct_eal_cmdline) != 0) { | |
331 | fprintf(stderr, "Failed to register cleanup handler\n"); | |
332 | } | |
7c673cae FG |
333 | |
334 | return argcount; | |
335 | } | |
336 | ||
11fdf7f2 | 337 | int spdk_env_init(const struct spdk_env_opts *opts) |
7c673cae | 338 | { |
7c673cae | 339 | char **dpdk_args = NULL; |
11fdf7f2 TL |
340 | int i, rc; |
341 | int orig_optind; | |
7c673cae | 342 | |
11fdf7f2 TL |
343 | rc = spdk_build_eal_cmdline(opts); |
344 | if (rc < 0) { | |
7c673cae | 345 | fprintf(stderr, "Invalid arguments to initialize DPDK\n"); |
11fdf7f2 | 346 | return -1; |
7c673cae FG |
347 | } |
348 | ||
11fdf7f2 | 349 | printf("Starting %s / %s initialization...\n", SPDK_VERSION_STRING, rte_version()); |
7c673cae | 350 | printf("[ DPDK EAL parameters: "); |
11fdf7f2 TL |
351 | for (i = 0; i < eal_cmdline_argcount; i++) { |
352 | printf("%s ", eal_cmdline[i]); | |
7c673cae FG |
353 | } |
354 | printf("]\n"); | |
355 | ||
356 | /* DPDK rearranges the array we pass to it, so make a copy | |
357 | * before passing so we can still free the individual strings | |
358 | * correctly. | |
359 | */ | |
11fdf7f2 | 360 | dpdk_args = calloc(eal_cmdline_argcount, sizeof(char *)); |
7c673cae FG |
361 | if (dpdk_args == NULL) { |
362 | fprintf(stderr, "Failed to allocate dpdk_args\n"); | |
11fdf7f2 | 363 | return -1; |
7c673cae | 364 | } |
11fdf7f2 | 365 | memcpy(dpdk_args, eal_cmdline, sizeof(char *) * eal_cmdline_argcount); |
7c673cae FG |
366 | |
367 | fflush(stdout); | |
11fdf7f2 TL |
368 | orig_optind = optind; |
369 | optind = 1; | |
370 | rc = rte_eal_init(eal_cmdline_argcount, dpdk_args); | |
371 | optind = orig_optind; | |
7c673cae | 372 | |
7c673cae FG |
373 | free(dpdk_args); |
374 | ||
375 | if (rc < 0) { | |
376 | fprintf(stderr, "Failed to initialize DPDK\n"); | |
11fdf7f2 TL |
377 | return -1; |
378 | } | |
379 | ||
380 | if (opts->shm_id < 0 && !opts->hugepage_single_segments) { | |
381 | /* | |
382 | * Unlink hugepage and config info files after init. This will ensure they get | |
383 | * deleted on app exit, even if the app crashes and does not exit normally. | |
384 | * Only do this when not in multi-process mode, since for multi-process other | |
385 | * apps will need to open these files. These files are not created for | |
386 | * "single file segments". | |
387 | */ | |
388 | spdk_env_unlink_shared_files(); | |
389 | } | |
390 | ||
391 | if (spdk_mem_map_init() < 0) { | |
392 | fprintf(stderr, "Failed to allocate mem_map\n"); | |
393 | return -1; | |
394 | } | |
395 | if (spdk_vtophys_init() < 0) { | |
396 | fprintf(stderr, "Failed to initialize vtophys\n"); | |
397 | return -1; | |
7c673cae FG |
398 | } |
399 | ||
11fdf7f2 | 400 | return 0; |
7c673cae | 401 | } |