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, &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 
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