]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * Copyright 2014 6WIND S.A. | |
7c673cae FG |
3 | */ |
4 | ||
5 | /* This file manages the list of devices and their arguments, as given | |
6 | * by the user at startup | |
7c673cae FG |
7 | */ |
8 | ||
9 | #include <stdio.h> | |
10 | #include <string.h> | |
9f95a23c | 11 | #include <stdarg.h> |
7c673cae | 12 | |
9f95a23c TL |
13 | #include <rte_bus.h> |
14 | #include <rte_class.h> | |
15 | #include <rte_compat.h> | |
16 | #include <rte_dev.h> | |
7c673cae | 17 | #include <rte_devargs.h> |
9f95a23c TL |
18 | #include <rte_errno.h> |
19 | #include <rte_kvargs.h> | |
20 | #include <rte_log.h> | |
21 | #include <rte_tailq.h> | |
7c673cae FG |
22 | #include "eal_private.h" |
23 | ||
9f95a23c TL |
24 | /** user device double-linked queue type definition */ |
25 | TAILQ_HEAD(rte_devargs_list, rte_devargs); | |
26 | ||
7c673cae | 27 | /** Global list of user devices */ |
9f95a23c | 28 | static struct rte_devargs_list devargs_list = |
7c673cae FG |
29 | TAILQ_HEAD_INITIALIZER(devargs_list); |
30 | ||
9f95a23c TL |
31 | static size_t |
32 | devargs_layer_count(const char *s) | |
33 | { | |
34 | size_t i = s ? 1 : 0; | |
35 | ||
36 | while (s != NULL && s[0] != '\0') { | |
37 | i += s[0] == '/'; | |
38 | s++; | |
39 | } | |
40 | return i; | |
41 | } | |
42 | ||
7c673cae | 43 | int |
9f95a23c TL |
44 | rte_devargs_layers_parse(struct rte_devargs *devargs, |
45 | const char *devstr) | |
7c673cae | 46 | { |
9f95a23c TL |
47 | struct { |
48 | const char *key; | |
49 | const char *str; | |
50 | struct rte_kvargs *kvlist; | |
51 | } layers[] = { | |
52 | { "bus=", NULL, NULL, }, | |
53 | { "class=", NULL, NULL, }, | |
54 | { "driver=", NULL, NULL, }, | |
55 | }; | |
56 | struct rte_kvargs_pair *kv = NULL; | |
57 | struct rte_class *cls = NULL; | |
58 | struct rte_bus *bus = NULL; | |
59 | const char *s = devstr; | |
60 | size_t nblayer; | |
61 | size_t i = 0; | |
62 | int ret = 0; | |
7c673cae | 63 | |
9f95a23c TL |
64 | /* Split each sub-lists. */ |
65 | nblayer = devargs_layer_count(devstr); | |
66 | if (nblayer > RTE_DIM(layers)) { | |
67 | RTE_LOG(ERR, EAL, "Invalid format: too many layers (%zu)\n", | |
68 | nblayer); | |
69 | ret = -E2BIG; | |
70 | goto get_out; | |
71 | } | |
7c673cae | 72 | |
9f95a23c TL |
73 | /* If the devargs points the devstr |
74 | * as source data, then it should not allocate | |
75 | * anything and keep referring only to it. | |
76 | */ | |
77 | if (devargs->data != devstr) { | |
78 | devargs->data = strdup(devstr); | |
79 | if (devargs->data == NULL) { | |
80 | RTE_LOG(ERR, EAL, "OOM\n"); | |
81 | ret = -ENOMEM; | |
82 | goto get_out; | |
83 | } | |
84 | s = devargs->data; | |
85 | } | |
7c673cae | 86 | |
9f95a23c TL |
87 | while (s != NULL) { |
88 | if (i >= RTE_DIM(layers)) { | |
89 | RTE_LOG(ERR, EAL, "Unrecognized layer %s\n", s); | |
90 | ret = -EINVAL; | |
91 | goto get_out; | |
92 | } | |
93 | /* | |
94 | * The last layer is free-form. | |
95 | * The "driver" key is not required (but accepted). | |
96 | */ | |
97 | if (strncmp(layers[i].key, s, strlen(layers[i].key)) && | |
98 | i != RTE_DIM(layers) - 1) | |
99 | goto next_layer; | |
100 | layers[i].str = s; | |
101 | layers[i].kvlist = rte_kvargs_parse_delim(s, NULL, "/"); | |
102 | if (layers[i].kvlist == NULL) { | |
103 | RTE_LOG(ERR, EAL, "Could not parse %s\n", s); | |
104 | ret = -EINVAL; | |
105 | goto get_out; | |
106 | } | |
107 | s = strchr(s, '/'); | |
108 | if (s != NULL) | |
109 | s++; | |
110 | next_layer: | |
111 | i++; | |
7c673cae FG |
112 | } |
113 | ||
9f95a23c TL |
114 | /* Parse each sub-list. */ |
115 | for (i = 0; i < RTE_DIM(layers); i++) { | |
116 | if (layers[i].kvlist == NULL) | |
117 | continue; | |
118 | kv = &layers[i].kvlist->pairs[0]; | |
119 | if (strcmp(kv->key, "bus") == 0) { | |
120 | bus = rte_bus_find_by_name(kv->value); | |
121 | if (bus == NULL) { | |
122 | RTE_LOG(ERR, EAL, "Could not find bus \"%s\"\n", | |
123 | kv->value); | |
124 | ret = -EFAULT; | |
125 | goto get_out; | |
126 | } | |
127 | } else if (strcmp(kv->key, "class") == 0) { | |
128 | cls = rte_class_find_by_name(kv->value); | |
129 | if (cls == NULL) { | |
130 | RTE_LOG(ERR, EAL, "Could not find class \"%s\"\n", | |
131 | kv->value); | |
132 | ret = -EFAULT; | |
133 | goto get_out; | |
134 | } | |
135 | } else if (strcmp(kv->key, "driver") == 0) { | |
136 | /* Ignore */ | |
137 | continue; | |
138 | } | |
139 | } | |
140 | ||
141 | /* Fill devargs fields. */ | |
142 | devargs->bus_str = layers[0].str; | |
143 | devargs->cls_str = layers[1].str; | |
144 | devargs->drv_str = layers[2].str; | |
145 | devargs->bus = bus; | |
146 | devargs->cls = cls; | |
147 | ||
148 | /* If we own the data, clean up a bit | |
149 | * the several layers string, to ease | |
150 | * their parsing afterward. | |
151 | */ | |
152 | if (devargs->data != devstr) { | |
153 | char *s = (void *)(intptr_t)(devargs->data); | |
154 | ||
155 | while ((s = strchr(s, '/'))) { | |
156 | *s = '\0'; | |
157 | s++; | |
158 | } | |
159 | } | |
160 | ||
161 | get_out: | |
162 | for (i = 0; i < RTE_DIM(layers); i++) { | |
163 | if (layers[i].kvlist) | |
164 | rte_kvargs_free(layers[i].kvlist); | |
165 | } | |
166 | if (ret != 0) | |
167 | rte_errno = -ret; | |
168 | return ret; | |
169 | } | |
170 | ||
171 | static int | |
172 | bus_name_cmp(const struct rte_bus *bus, const void *name) | |
173 | { | |
174 | return strncmp(bus->name, name, strlen(bus->name)); | |
175 | } | |
176 | ||
177 | int | |
178 | rte_devargs_parse(struct rte_devargs *da, const char *dev) | |
179 | { | |
180 | struct rte_bus *bus = NULL; | |
181 | const char *devname; | |
182 | const size_t maxlen = sizeof(da->name); | |
183 | size_t i; | |
184 | ||
185 | if (da == NULL) | |
186 | return -EINVAL; | |
187 | ||
188 | /* Retrieve eventual bus info */ | |
189 | do { | |
190 | devname = dev; | |
191 | bus = rte_bus_find(bus, bus_name_cmp, dev); | |
192 | if (bus == NULL) | |
193 | break; | |
194 | devname = dev + strlen(bus->name) + 1; | |
195 | if (rte_bus_find_by_device_name(devname) == bus) | |
196 | break; | |
197 | } while (1); | |
198 | /* Store device name */ | |
199 | i = 0; | |
200 | while (devname[i] != '\0' && devname[i] != ',') { | |
201 | da->name[i] = devname[i]; | |
202 | i++; | |
203 | if (i == maxlen) { | |
204 | RTE_LOG(WARNING, EAL, "Parsing \"%s\": device name should be shorter than %zu\n", | |
205 | dev, maxlen); | |
206 | da->name[i - 1] = '\0'; | |
207 | return -EINVAL; | |
208 | } | |
209 | } | |
210 | da->name[i] = '\0'; | |
211 | if (bus == NULL) { | |
212 | bus = rte_bus_find_by_device_name(da->name); | |
213 | if (bus == NULL) { | |
214 | RTE_LOG(ERR, EAL, "failed to parse device \"%s\"\n", | |
215 | da->name); | |
216 | return -EFAULT; | |
217 | } | |
218 | } | |
219 | da->bus = bus; | |
220 | /* Parse eventual device arguments */ | |
221 | if (devname[i] == ',') | |
222 | da->args = strdup(&devname[i + 1]); | |
223 | else | |
224 | da->args = strdup(""); | |
225 | if (da->args == NULL) { | |
226 | RTE_LOG(ERR, EAL, "not enough memory to parse arguments\n"); | |
227 | return -ENOMEM; | |
7c673cae FG |
228 | } |
229 | return 0; | |
230 | } | |
231 | ||
7c673cae | 232 | int |
9f95a23c | 233 | rte_devargs_parsef(struct rte_devargs *da, const char *format, ...) |
7c673cae | 234 | { |
9f95a23c TL |
235 | va_list ap; |
236 | size_t len; | |
237 | char *dev; | |
7c673cae FG |
238 | int ret; |
239 | ||
9f95a23c TL |
240 | if (da == NULL) |
241 | return -EINVAL; | |
7c673cae | 242 | |
9f95a23c TL |
243 | va_start(ap, format); |
244 | len = vsnprintf(NULL, 0, format, ap); | |
245 | va_end(ap); | |
7c673cae | 246 | |
9f95a23c TL |
247 | dev = calloc(1, len + 1); |
248 | if (dev == NULL) { | |
249 | RTE_LOG(ERR, EAL, "not enough memory to parse device\n"); | |
250 | return -ENOMEM; | |
251 | } | |
252 | ||
253 | va_start(ap, format); | |
254 | vsnprintf(dev, len + 1, format, ap); | |
255 | va_end(ap); | |
7c673cae | 256 | |
9f95a23c TL |
257 | ret = rte_devargs_parse(da, dev); |
258 | ||
259 | free(dev); | |
260 | return ret; | |
261 | } | |
7c673cae | 262 | |
9f95a23c TL |
263 | int |
264 | rte_devargs_insert(struct rte_devargs **da) | |
265 | { | |
266 | struct rte_devargs *listed_da; | |
267 | void *tmp; | |
268 | ||
269 | if (*da == NULL || (*da)->bus == NULL) | |
270 | return -1; | |
7c673cae | 271 | |
9f95a23c TL |
272 | TAILQ_FOREACH_SAFE(listed_da, &devargs_list, next, tmp) { |
273 | if (listed_da == *da) | |
274 | /* devargs already in the list */ | |
275 | return 0; | |
276 | if (strcmp(listed_da->bus->name, (*da)->bus->name) == 0 && | |
277 | strcmp(listed_da->name, (*da)->name) == 0) { | |
278 | /* device already in devargs list, must be updated */ | |
279 | listed_da->type = (*da)->type; | |
280 | listed_da->policy = (*da)->policy; | |
281 | free(listed_da->args); | |
282 | listed_da->args = (*da)->args; | |
283 | listed_da->bus = (*da)->bus; | |
284 | listed_da->cls = (*da)->cls; | |
285 | listed_da->bus_str = (*da)->bus_str; | |
286 | listed_da->cls_str = (*da)->cls_str; | |
287 | listed_da->data = (*da)->data; | |
288 | /* replace provided devargs with found one */ | |
289 | free(*da); | |
290 | *da = listed_da; | |
291 | return 0; | |
292 | } | |
7c673cae | 293 | } |
9f95a23c TL |
294 | /* new device in the list */ |
295 | TAILQ_INSERT_TAIL(&devargs_list, *da, next); | |
296 | return 0; | |
297 | } | |
7c673cae | 298 | |
9f95a23c TL |
299 | /* store a whitelist parameter for later parsing */ |
300 | int | |
301 | rte_devargs_add(enum rte_devtype devtype, const char *devargs_str) | |
302 | { | |
303 | struct rte_devargs *devargs = NULL; | |
304 | struct rte_bus *bus = NULL; | |
305 | const char *dev = devargs_str; | |
306 | ||
307 | /* use calloc instead of rte_zmalloc as it's called early at init */ | |
308 | devargs = calloc(1, sizeof(*devargs)); | |
309 | if (devargs == NULL) | |
310 | goto fail; | |
311 | ||
312 | if (rte_devargs_parse(devargs, dev)) | |
313 | goto fail; | |
314 | devargs->type = devtype; | |
315 | bus = devargs->bus; | |
316 | if (devargs->type == RTE_DEVTYPE_BLACKLISTED_PCI) | |
317 | devargs->policy = RTE_DEV_BLACKLISTED; | |
318 | if (bus->conf.scan_mode == RTE_BUS_SCAN_UNDEFINED) { | |
319 | if (devargs->policy == RTE_DEV_WHITELISTED) | |
320 | bus->conf.scan_mode = RTE_BUS_SCAN_WHITELIST; | |
321 | else if (devargs->policy == RTE_DEV_BLACKLISTED) | |
322 | bus->conf.scan_mode = RTE_BUS_SCAN_BLACKLIST; | |
323 | } | |
7c673cae FG |
324 | TAILQ_INSERT_TAIL(&devargs_list, devargs, next); |
325 | return 0; | |
326 | ||
327 | fail: | |
7c673cae FG |
328 | if (devargs) { |
329 | free(devargs->args); | |
330 | free(devargs); | |
331 | } | |
332 | ||
333 | return -1; | |
334 | } | |
335 | ||
9f95a23c TL |
336 | int |
337 | rte_devargs_remove(struct rte_devargs *devargs) | |
338 | { | |
339 | struct rte_devargs *d; | |
340 | void *tmp; | |
341 | ||
342 | if (devargs == NULL || devargs->bus == NULL) | |
343 | return -1; | |
344 | ||
345 | TAILQ_FOREACH_SAFE(d, &devargs_list, next, tmp) { | |
346 | if (strcmp(d->bus->name, devargs->bus->name) == 0 && | |
347 | strcmp(d->name, devargs->name) == 0) { | |
348 | TAILQ_REMOVE(&devargs_list, d, next); | |
349 | free(d->args); | |
350 | free(d); | |
351 | return 0; | |
352 | } | |
353 | } | |
354 | return 1; | |
355 | } | |
356 | ||
7c673cae FG |
357 | /* count the number of devices of a specified type */ |
358 | unsigned int | |
9f95a23c | 359 | rte_devargs_type_count(enum rte_devtype devtype) |
7c673cae FG |
360 | { |
361 | struct rte_devargs *devargs; | |
362 | unsigned int count = 0; | |
363 | ||
364 | TAILQ_FOREACH(devargs, &devargs_list, next) { | |
365 | if (devargs->type != devtype) | |
366 | continue; | |
367 | count++; | |
368 | } | |
369 | return count; | |
370 | } | |
371 | ||
372 | /* dump the user devices on the console */ | |
373 | void | |
9f95a23c | 374 | rte_devargs_dump(FILE *f) |
7c673cae FG |
375 | { |
376 | struct rte_devargs *devargs; | |
377 | ||
9f95a23c | 378 | fprintf(f, "User device list:\n"); |
7c673cae | 379 | TAILQ_FOREACH(devargs, &devargs_list, next) { |
9f95a23c TL |
380 | fprintf(f, " [%s]: %s %s\n", |
381 | (devargs->bus ? devargs->bus->name : "??"), | |
382 | devargs->name, devargs->args); | |
383 | } | |
384 | } | |
385 | ||
386 | /* bus-aware rte_devargs iterator. */ | |
387 | struct rte_devargs * | |
388 | rte_devargs_next(const char *busname, const struct rte_devargs *start) | |
389 | { | |
390 | struct rte_devargs *da; | |
391 | ||
392 | if (start != NULL) | |
393 | da = TAILQ_NEXT(start, next); | |
394 | else | |
395 | da = TAILQ_FIRST(&devargs_list); | |
396 | while (da != NULL) { | |
397 | if (busname == NULL || | |
398 | (strcmp(busname, da->bus->name) == 0)) | |
399 | return da; | |
400 | da = TAILQ_NEXT(da, next); | |
7c673cae | 401 | } |
9f95a23c | 402 | return NULL; |
7c673cae | 403 | } |