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
sr_iov_new(SRIOV ** ret)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
sr_iov_new_static(OrderedHashmap ** sr_iov_by_section,const char * filename,unsigned section_line,SRIOV ** ret)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
sr_iov_free(SRIOV * sr_iov)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
sr_iov_hash_func(const SRIOV * sr_iov,struct siphash * state)83 void sr_iov_hash_func(const SRIOV *sr_iov, struct siphash *state) {
84 assert(sr_iov);
85 assert(state);
86
87 siphash24_compress(&sr_iov->vf, sizeof(sr_iov->vf), state);
88 }
89
sr_iov_compare_func(const SRIOV * s1,const SRIOV * s2)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
sr_iov_set_netlink_message(SRIOV * sr_iov,sd_netlink_message * req)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
sr_iov_get_num_vfs(sd_device * device,uint32_t * ret)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
sr_iov_set_num_vfs(sd_device * device,uint32_t num_vfs,OrderedHashmap * sr_iov_by_section)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, ¤t_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
sr_iov_section_verify(uint32_t num_vfs,SRIOV * sr_iov)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
sr_iov_drop_invalid_sections(uint32_t num_vfs,OrderedHashmap * sr_iov_by_section)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
config_parse_sr_iov_uint32(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)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 = data;
363 uint32_t k;
364 int r;
365
366 assert(filename);
367 assert(lvalue);
368 assert(rvalue);
369 assert(data);
370
371 r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
372 if (r < 0)
373 return r;
374
375 if (isempty(rvalue)) {
376 if (streq(lvalue, "VirtualFunction"))
377 sr_iov->vf = UINT32_MAX;
378 else if (streq(lvalue, "VLANId"))
379 sr_iov->vlan = 0;
380 else if (streq(lvalue, "QualityOfService"))
381 sr_iov->qos = 0;
382 else
383 assert_not_reached();
384
385 TAKE_PTR(sr_iov);
386 return 0;
387 }
388
389 r = safe_atou32(rvalue, &k);
390 if (r < 0) {
391 log_syntax(unit, LOG_WARNING, filename, line, r,
392 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
393 return 0;
394 }
395
396 if (streq(lvalue, "VLANId")) {
397 if (k == 0 || k > 4095) {
398 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV VLANId: %d", k);
399 return 0;
400 }
401 sr_iov->vlan = k;
402 } else if (streq(lvalue, "VirtualFunction")) {
403 if (k >= INT_MAX) {
404 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV virtual function: %d", k);
405 return 0;
406 }
407 sr_iov->vf = k;
408 } else if (streq(lvalue, "QualityOfService"))
409 sr_iov->qos = k;
410 else
411 assert_not_reached();
412
413 TAKE_PTR(sr_iov);
414 return 0;
415 }
416
config_parse_sr_iov_vlan_proto(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)417 int config_parse_sr_iov_vlan_proto(
418 const char *unit,
419 const char *filename,
420 unsigned line,
421 const char *section,
422 unsigned section_line,
423 const char *lvalue,
424 int ltype,
425 const char *rvalue,
426 void *data,
427 void *userdata) {
428
429 _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
430 OrderedHashmap **sr_iov_by_section = data;
431 int r;
432
433 assert(filename);
434 assert(lvalue);
435 assert(rvalue);
436 assert(data);
437
438 r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
439 if (r < 0)
440 return r;
441
442 if (isempty(rvalue) || streq(rvalue, "802.1Q"))
443 sr_iov->vlan_proto = ETH_P_8021Q;
444 else if (streq(rvalue, "802.1ad"))
445 sr_iov->vlan_proto = ETH_P_8021AD;
446 else {
447 log_syntax(unit, LOG_WARNING, filename, line, 0,
448 "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
449 return 0;
450 }
451
452 TAKE_PTR(sr_iov);
453 return 0;
454 }
455
config_parse_sr_iov_link_state(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)456 int config_parse_sr_iov_link_state(
457 const char *unit,
458 const char *filename,
459 unsigned line,
460 const char *section,
461 unsigned section_line,
462 const char *lvalue,
463 int ltype,
464 const char *rvalue,
465 void *data,
466 void *userdata) {
467
468 _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
469 OrderedHashmap **sr_iov_by_section = data;
470 int r;
471
472 assert(filename);
473 assert(lvalue);
474 assert(rvalue);
475 assert(data);
476
477 r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
478 if (r < 0)
479 return r;
480
481 /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
482 * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
483
484 if (isempty(rvalue)) {
485 sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID;
486 TAKE_PTR(sr_iov);
487 return 0;
488 }
489
490 if (streq(rvalue, "auto")) {
491 sr_iov->link_state = SR_IOV_LINK_STATE_AUTO;
492 TAKE_PTR(sr_iov);
493 return 0;
494 }
495
496 r = parse_boolean(rvalue);
497 if (r < 0) {
498 log_syntax(unit, LOG_WARNING, filename, line, r,
499 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
500 return 0;
501 }
502
503 sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE;
504 TAKE_PTR(sr_iov);
505 return 0;
506 }
507
config_parse_sr_iov_boolean(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)508 int config_parse_sr_iov_boolean(
509 const char *unit,
510 const char *filename,
511 unsigned line,
512 const char *section,
513 unsigned section_line,
514 const char *lvalue,
515 int ltype,
516 const char *rvalue,
517 void *data,
518 void *userdata) {
519
520 _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
521 OrderedHashmap **sr_iov_by_section = data;
522 int r;
523
524 assert(filename);
525 assert(lvalue);
526 assert(rvalue);
527 assert(data);
528
529 r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
530 if (r < 0)
531 return r;
532
533 if (isempty(rvalue)) {
534 if (streq(lvalue, "MACSpoofCheck"))
535 sr_iov->vf_spoof_check_setting = -1;
536 else if (streq(lvalue, "QueryReceiveSideScaling"))
537 sr_iov->query_rss = -1;
538 else if (streq(lvalue, "Trust"))
539 sr_iov->trust = -1;
540 else
541 assert_not_reached();
542
543 TAKE_PTR(sr_iov);
544 return 0;
545 }
546
547 r = parse_boolean(rvalue);
548 if (r < 0) {
549 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue);
550 return 0;
551 }
552
553 if (streq(lvalue, "MACSpoofCheck"))
554 sr_iov->vf_spoof_check_setting = r;
555 else if (streq(lvalue, "QueryReceiveSideScaling"))
556 sr_iov->query_rss = r;
557 else if (streq(lvalue, "Trust"))
558 sr_iov->trust = r;
559 else
560 assert_not_reached();
561
562 TAKE_PTR(sr_iov);
563 return 0;
564 }
565
config_parse_sr_iov_mac(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)566 int config_parse_sr_iov_mac(
567 const char *unit,
568 const char *filename,
569 unsigned line,
570 const char *section,
571 unsigned section_line,
572 const char *lvalue,
573 int ltype,
574 const char *rvalue,
575 void *data,
576 void *userdata) {
577
578 _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
579 OrderedHashmap **sr_iov_by_section = data;
580 int r;
581
582 assert(filename);
583 assert(lvalue);
584 assert(rvalue);
585 assert(data);
586
587 r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
588 if (r < 0)
589 return r;
590
591 if (isempty(rvalue)) {
592 sr_iov->mac = ETHER_ADDR_NULL;
593 TAKE_PTR(sr_iov);
594 return 0;
595 }
596
597 r = parse_ether_addr(rvalue, &sr_iov->mac);
598 if (r < 0) {
599 log_syntax(unit, LOG_WARNING, filename, line, r,
600 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
601 return 0;
602 }
603
604 TAKE_PTR(sr_iov);
605 return 0;
606 }
607
config_parse_sr_iov_num_vfs(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)608 int config_parse_sr_iov_num_vfs(
609 const char *unit,
610 const char *filename,
611 unsigned line,
612 const char *section,
613 unsigned section_line,
614 const char *lvalue,
615 int ltype,
616 const char *rvalue,
617 void *data,
618 void *userdata) {
619
620 uint32_t n, *num_vfs = data;
621 int r;
622
623 assert(filename);
624 assert(lvalue);
625 assert(rvalue);
626 assert(data);
627
628 if (isempty(rvalue)) {
629 *num_vfs = UINT32_MAX;
630 return 0;
631 }
632
633 r = safe_atou32(rvalue, &n);
634 if (r < 0) {
635 log_syntax(unit, LOG_WARNING, filename, line, r,
636 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
637 return 0;
638 }
639
640 if (n > INT_MAX) {
641 log_syntax(unit, LOG_WARNING, filename, line, 0,
642 "The number of SR-IOV virtual functions is too large. It must be equal to "
643 "or smaller than 2147483647. Ignoring assignment: %"PRIu32, n);
644 return 0;
645 }
646
647 *num_vfs = n;
648 return 0;
649 }
650