]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/netif-sriov.c
uefi: emphasize a bit that EV_IPL event logs is the past, EV_EVENT_TAG the future
[thirdparty/systemd.git] / src / shared / netif-sriov.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "device-util.h"
5 #include "netlink-util.h"
6 #include "netif-sriov.h"
7 #include "parse-util.h"
8 #include "set.h"
9 #include "stdio-util.h"
10 #include "string-util.h"
11
12 static int sr_iov_new(SRIOV **ret) {
13 SRIOV *sr_iov;
14
15 assert(ret);
16
17 sr_iov = new(SRIOV, 1);
18 if (!sr_iov)
19 return -ENOMEM;
20
21 *sr_iov = (SRIOV) {
22 .vf = UINT32_MAX,
23 .vlan_proto = ETH_P_8021Q,
24 .vf_spoof_check_setting = -1,
25 .trust = -1,
26 .query_rss = -1,
27 .link_state = _SR_IOV_LINK_STATE_INVALID,
28 };
29
30 *ret = TAKE_PTR(sr_iov);
31
32 return 0;
33 }
34
35 static int sr_iov_new_static(OrderedHashmap **sr_iov_by_section, const char *filename, unsigned section_line, SRIOV **ret) {
36 _cleanup_(config_section_freep) ConfigSection *n = NULL;
37 _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL;
38 SRIOV *existing = NULL;
39 int r;
40
41 assert(sr_iov_by_section);
42 assert(filename);
43 assert(section_line > 0);
44 assert(ret);
45
46 r = config_section_new(filename, section_line, &n);
47 if (r < 0)
48 return r;
49
50 existing = ordered_hashmap_get(*sr_iov_by_section, n);
51 if (existing) {
52 *ret = existing;
53 return 0;
54 }
55
56 r = sr_iov_new(&sr_iov);
57 if (r < 0)
58 return r;
59
60 r = ordered_hashmap_ensure_put(sr_iov_by_section, &config_section_hash_ops, n, sr_iov);
61 if (r < 0)
62 return r;
63
64 sr_iov->section = TAKE_PTR(n);
65 sr_iov->sr_iov_by_section = *sr_iov_by_section;
66
67 *ret = TAKE_PTR(sr_iov);
68 return 0;
69 }
70
71 SRIOV *sr_iov_free(SRIOV *sr_iov) {
72 if (!sr_iov)
73 return NULL;
74
75 if (sr_iov->sr_iov_by_section && sr_iov->section)
76 ordered_hashmap_remove(sr_iov->sr_iov_by_section, sr_iov->section);
77
78 config_section_free(sr_iov->section);
79
80 return mfree(sr_iov);
81 }
82
83 void sr_iov_hash_func(const SRIOV *sr_iov, struct siphash *state) {
84 assert(sr_iov);
85 assert(state);
86
87 siphash24_compress_typesafe(sr_iov->vf, state);
88 }
89
90 int sr_iov_compare_func(const SRIOV *s1, const SRIOV *s2) {
91 assert(s1);
92 assert(s2);
93
94 return CMP(s1->vf, s2->vf);
95 }
96
97 DEFINE_PRIVATE_HASH_OPS(
98 sr_iov_hash_ops,
99 SRIOV,
100 sr_iov_hash_func,
101 sr_iov_compare_func);
102
103 int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req) {
104 int r;
105
106 assert(sr_iov);
107 assert(req);
108
109 r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST);
110 if (r < 0)
111 return r;
112
113 r = sd_netlink_message_open_container(req, IFLA_VF_INFO);
114 if (r < 0)
115 return r;
116
117 if (!ether_addr_is_null(&sr_iov->mac)) {
118 struct ifla_vf_mac ivm = {
119 .vf = sr_iov->vf,
120 };
121
122 memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN);
123 r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac));
124 if (r < 0)
125 return r;
126 }
127
128 if (sr_iov->vf_spoof_check_setting >= 0) {
129 struct ifla_vf_spoofchk ivs = {
130 .vf = sr_iov->vf,
131 .setting = sr_iov->vf_spoof_check_setting,
132 };
133
134 r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk));
135 if (r < 0)
136 return r;
137 }
138
139 if (sr_iov->query_rss >= 0) {
140 struct ifla_vf_rss_query_en ivs = {
141 .vf = sr_iov->vf,
142 .setting = sr_iov->query_rss,
143 };
144
145 r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en));
146 if (r < 0)
147 return r;
148 }
149
150 if (sr_iov->trust >= 0) {
151 struct ifla_vf_trust ivt = {
152 .vf = sr_iov->vf,
153 .setting = sr_iov->trust,
154 };
155
156 r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust));
157 if (r < 0)
158 return r;
159 }
160
161 if (sr_iov->link_state >= 0) {
162 struct ifla_vf_link_state ivl = {
163 .vf = sr_iov->vf,
164 .link_state = sr_iov->link_state,
165 };
166
167 r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state));
168 if (r < 0)
169 return r;
170 }
171
172 if (sr_iov->vlan > 0) {
173 /* Because of padding, first the buffer must be initialized with 0. */
174 struct ifla_vf_vlan_info ivvi = {};
175 ivvi.vf = sr_iov->vf;
176 ivvi.vlan = sr_iov->vlan;
177 ivvi.qos = sr_iov->qos;
178 ivvi.vlan_proto = htobe16(sr_iov->vlan_proto);
179
180 r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST);
181 if (r < 0)
182 return r;
183
184 r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info));
185 if (r < 0)
186 return r;
187
188 r = sd_netlink_message_close_container(req);
189 if (r < 0)
190 return r;
191 }
192
193 r = sd_netlink_message_close_container(req);
194 if (r < 0)
195 return r;
196
197 r = sd_netlink_message_close_container(req);
198 if (r < 0)
199 return r;
200
201 return 0;
202 }
203
204 int sr_iov_get_num_vfs(sd_device *device, uint32_t *ret) {
205 const char *str;
206 uint32_t n;
207 int r;
208
209 assert(device);
210 assert(ret);
211
212 r = sd_device_get_sysattr_value(device, "device/sriov_numvfs", &str);
213 if (r < 0)
214 return r;
215
216 r = safe_atou32(str, &n);
217 if (r < 0)
218 return r;
219
220 *ret = n;
221 return 0;
222 }
223
224 int sr_iov_set_num_vfs(sd_device *device, uint32_t num_vfs, OrderedHashmap *sr_iov_by_section) {
225 char val[DECIMAL_STR_MAX(uint32_t)];
226 const char *str;
227 int r;
228
229 assert(device);
230
231 if (num_vfs == UINT32_MAX) {
232 uint32_t current_num_vfs;
233 SRIOV *sr_iov;
234
235 /* If the number of virtual functions is not specified, then use the maximum number of VF + 1. */
236
237 num_vfs = 0;
238 ORDERED_HASHMAP_FOREACH(sr_iov, sr_iov_by_section)
239 num_vfs = MAX(num_vfs, sr_iov->vf + 1);
240
241 if (num_vfs == 0) /* No VF is configured. */
242 return 0;
243
244 r = sr_iov_get_num_vfs(device, &current_num_vfs);
245 if (r < 0)
246 return log_device_debug_errno(device, r, "Failed to get the current number of SR-IOV virtual functions: %m");
247
248 /* Enough VFs already exist. */
249 if (num_vfs <= current_num_vfs)
250 return 0;
251
252 } else if (num_vfs == 0) {
253 r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", "0");
254 if (r < 0)
255 log_device_debug_errno(device, r, "Failed to write device/sriov_numvfs sysfs attribute, ignoring: %m");
256
257 /* Gracefully handle the error in disabling VFs when the interface does not support SR-IOV. */
258 return r == -ENOENT ? 0 : r;
259 }
260
261 /* So, the interface does not have enough VFs. Before increasing the number of VFs, check the
262 * maximum allowed number of VFs from the sriov_totalvfs sysattr. Note that the sysattr
263 * currently exists only for PCI drivers. Hence, ignore -ENOENT.
264 * TODO: netdevsim provides the information in debugfs. */
265 r = sd_device_get_sysattr_value(device, "device/sriov_totalvfs", &str);
266 if (r >= 0) {
267 uint32_t max_num_vfs;
268
269 r = safe_atou32(str, &max_num_vfs);
270 if (r < 0)
271 return log_device_debug_errno(device, r, "Failed to parse device/sriov_totalvfs sysfs attribute '%s': %m", str);
272
273 if (num_vfs > max_num_vfs)
274 return log_device_debug_errno(device, SYNTHETIC_ERRNO(ERANGE),
275 "Specified number of virtual functions is out of range. "
276 "The maximum allowed value is %"PRIu32".",
277 max_num_vfs);
278
279 } else if (r != -ENOENT) /* Currently, only PCI driver has the attribute. */
280 return log_device_debug_errno(device, r, "Failed to read device/sriov_totalvfs sysfs attribute: %m");
281
282 xsprintf(val, "%"PRIu32, num_vfs);
283 r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", val);
284 if (r == -EBUSY) {
285 /* Some devices e.g. netdevsim refuse to set sriov_numvfs if it has non-zero value. */
286 r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", "0");
287 if (r >= 0)
288 r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", val);
289 }
290 if (r < 0)
291 return log_device_debug_errno(device, r, "Failed to write device/sriov_numvfs sysfs attribute: %m");
292
293 log_device_debug(device, "device/sriov_numvfs sysfs attribute set to '%s'.", val);
294 return 0;
295 }
296
297 static int sr_iov_section_verify(uint32_t num_vfs, SRIOV *sr_iov) {
298 assert(sr_iov);
299
300 if (section_is_invalid(sr_iov->section))
301 return -EINVAL;
302
303 if (sr_iov->vf == UINT32_MAX)
304 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
305 "%s: [SR-IOV] section without VirtualFunction= field configured. "
306 "Ignoring [SR-IOV] section from line %u.",
307 sr_iov->section->filename, sr_iov->section->line);
308
309 if (sr_iov->vf >= num_vfs)
310 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
311 "%s: VirtualFunction= must be smaller than the value specified in SR-IOVVirtualFunctions=. "
312 "Ignoring [SR-IOV] section from line %u.",
313 sr_iov->section->filename, sr_iov->section->line);
314
315 return 0;
316 }
317
318 int sr_iov_drop_invalid_sections(uint32_t num_vfs, OrderedHashmap *sr_iov_by_section) {
319 _cleanup_set_free_ Set *set = NULL;
320 SRIOV *sr_iov;
321 int r;
322
323 ORDERED_HASHMAP_FOREACH(sr_iov, sr_iov_by_section) {
324 SRIOV *dup;
325
326 if (sr_iov_section_verify(num_vfs, sr_iov) < 0) {
327 sr_iov_free(sr_iov);
328 continue;
329 }
330
331 dup = set_remove(set, sr_iov);
332 if (dup) {
333 log_warning("%s: Conflicting [SR-IOV] section is specified at line %u and %u, "
334 "dropping the [SR-IOV] section specified at line %u.",
335 dup->section->filename, sr_iov->section->line,
336 dup->section->line, dup->section->line);
337 sr_iov_free(dup);
338 }
339
340 r = set_ensure_put(&set, &sr_iov_hash_ops, sr_iov);
341 if (r < 0)
342 return log_oom();
343 assert(r > 0);
344 }
345
346 return 0;
347 }
348
349 int config_parse_sr_iov_uint32(
350 const char *unit,
351 const char *filename,
352 unsigned line,
353 const char *section,
354 unsigned section_line,
355 const char *lvalue,
356 int ltype,
357 const char *rvalue,
358 void *data,
359 void *userdata) {
360
361 _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
362 OrderedHashmap **sr_iov_by_section = ASSERT_PTR(data);
363 uint32_t k;
364 int r;
365
366 assert(filename);
367 assert(lvalue);
368 assert(rvalue);
369
370 r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
371 if (r < 0)
372 return r;
373
374 if (isempty(rvalue)) {
375 if (streq(lvalue, "VirtualFunction"))
376 sr_iov->vf = UINT32_MAX;
377 else if (streq(lvalue, "VLANId"))
378 sr_iov->vlan = 0;
379 else if (streq(lvalue, "QualityOfService"))
380 sr_iov->qos = 0;
381 else
382 assert_not_reached();
383
384 TAKE_PTR(sr_iov);
385 return 0;
386 }
387
388 r = safe_atou32(rvalue, &k);
389 if (r < 0) {
390 log_syntax(unit, LOG_WARNING, filename, line, r,
391 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
392 return 0;
393 }
394
395 if (streq(lvalue, "VLANId")) {
396 if (k == 0 || k > 4095) {
397 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV VLANId: %u", k);
398 return 0;
399 }
400 sr_iov->vlan = k;
401 } else if (streq(lvalue, "VirtualFunction")) {
402 if (k >= INT_MAX) {
403 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV virtual function: %u", k);
404 return 0;
405 }
406 sr_iov->vf = k;
407 } else if (streq(lvalue, "QualityOfService"))
408 sr_iov->qos = k;
409 else
410 assert_not_reached();
411
412 TAKE_PTR(sr_iov);
413 return 0;
414 }
415
416 int config_parse_sr_iov_vlan_proto(
417 const char *unit,
418 const char *filename,
419 unsigned line,
420 const char *section,
421 unsigned section_line,
422 const char *lvalue,
423 int ltype,
424 const char *rvalue,
425 void *data,
426 void *userdata) {
427
428 _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
429 OrderedHashmap **sr_iov_by_section = ASSERT_PTR(data);
430 int r;
431
432 assert(filename);
433 assert(lvalue);
434 assert(rvalue);
435
436 r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
437 if (r < 0)
438 return r;
439
440 if (isempty(rvalue) || streq(rvalue, "802.1Q"))
441 sr_iov->vlan_proto = ETH_P_8021Q;
442 else if (streq(rvalue, "802.1ad"))
443 sr_iov->vlan_proto = ETH_P_8021AD;
444 else {
445 log_syntax(unit, LOG_WARNING, filename, line, 0,
446 "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
447 return 0;
448 }
449
450 TAKE_PTR(sr_iov);
451 return 0;
452 }
453
454 int config_parse_sr_iov_link_state(
455 const char *unit,
456 const char *filename,
457 unsigned line,
458 const char *section,
459 unsigned section_line,
460 const char *lvalue,
461 int ltype,
462 const char *rvalue,
463 void *data,
464 void *userdata) {
465
466 _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
467 OrderedHashmap **sr_iov_by_section = ASSERT_PTR(data);
468 int r;
469
470 assert(filename);
471 assert(lvalue);
472 assert(rvalue);
473
474 r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
475 if (r < 0)
476 return r;
477
478 /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
479 * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
480
481 if (isempty(rvalue)) {
482 sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID;
483 TAKE_PTR(sr_iov);
484 return 0;
485 }
486
487 if (streq(rvalue, "auto")) {
488 sr_iov->link_state = SR_IOV_LINK_STATE_AUTO;
489 TAKE_PTR(sr_iov);
490 return 0;
491 }
492
493 r = parse_boolean(rvalue);
494 if (r < 0) {
495 log_syntax(unit, LOG_WARNING, filename, line, r,
496 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
497 return 0;
498 }
499
500 sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE;
501 TAKE_PTR(sr_iov);
502 return 0;
503 }
504
505 int config_parse_sr_iov_boolean(
506 const char *unit,
507 const char *filename,
508 unsigned line,
509 const char *section,
510 unsigned section_line,
511 const char *lvalue,
512 int ltype,
513 const char *rvalue,
514 void *data,
515 void *userdata) {
516
517 _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
518 OrderedHashmap **sr_iov_by_section = ASSERT_PTR(data);
519 int r;
520
521 assert(filename);
522 assert(lvalue);
523 assert(rvalue);
524
525 r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
526 if (r < 0)
527 return r;
528
529 if (isempty(rvalue)) {
530 if (streq(lvalue, "MACSpoofCheck"))
531 sr_iov->vf_spoof_check_setting = -1;
532 else if (streq(lvalue, "QueryReceiveSideScaling"))
533 sr_iov->query_rss = -1;
534 else if (streq(lvalue, "Trust"))
535 sr_iov->trust = -1;
536 else
537 assert_not_reached();
538
539 TAKE_PTR(sr_iov);
540 return 0;
541 }
542
543 r = parse_boolean(rvalue);
544 if (r < 0) {
545 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue);
546 return 0;
547 }
548
549 if (streq(lvalue, "MACSpoofCheck"))
550 sr_iov->vf_spoof_check_setting = r;
551 else if (streq(lvalue, "QueryReceiveSideScaling"))
552 sr_iov->query_rss = r;
553 else if (streq(lvalue, "Trust"))
554 sr_iov->trust = r;
555 else
556 assert_not_reached();
557
558 TAKE_PTR(sr_iov);
559 return 0;
560 }
561
562 int config_parse_sr_iov_mac(
563 const char *unit,
564 const char *filename,
565 unsigned line,
566 const char *section,
567 unsigned section_line,
568 const char *lvalue,
569 int ltype,
570 const char *rvalue,
571 void *data,
572 void *userdata) {
573
574 _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
575 OrderedHashmap **sr_iov_by_section = ASSERT_PTR(data);
576 int r;
577
578 assert(filename);
579 assert(lvalue);
580 assert(rvalue);
581
582 r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
583 if (r < 0)
584 return r;
585
586 if (isempty(rvalue)) {
587 sr_iov->mac = ETHER_ADDR_NULL;
588 TAKE_PTR(sr_iov);
589 return 0;
590 }
591
592 r = parse_ether_addr(rvalue, &sr_iov->mac);
593 if (r < 0) {
594 log_syntax(unit, LOG_WARNING, filename, line, r,
595 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
596 return 0;
597 }
598
599 TAKE_PTR(sr_iov);
600 return 0;
601 }
602
603 int config_parse_sr_iov_num_vfs(
604 const char *unit,
605 const char *filename,
606 unsigned line,
607 const char *section,
608 unsigned section_line,
609 const char *lvalue,
610 int ltype,
611 const char *rvalue,
612 void *data,
613 void *userdata) {
614
615 uint32_t n, *num_vfs = ASSERT_PTR(data);
616 int r;
617
618 assert(filename);
619 assert(lvalue);
620 assert(rvalue);
621
622 if (isempty(rvalue)) {
623 *num_vfs = UINT32_MAX;
624 return 0;
625 }
626
627 r = safe_atou32(rvalue, &n);
628 if (r < 0) {
629 log_syntax(unit, LOG_WARNING, filename, line, r,
630 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
631 return 0;
632 }
633
634 if (n > INT_MAX) {
635 log_syntax(unit, LOG_WARNING, filename, line, 0,
636 "The number of SR-IOV virtual functions is too large. It must be equal to "
637 "or smaller than 2147483647. Ignoring assignment: %"PRIu32, n);
638 return 0;
639 }
640
641 *num_vfs = n;
642 return 0;
643 }