1 /*-
2 * Copyright (c) 2013-2015 Sandvine Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 #include <sys/param.h>
29 #include <sys/iov.h>
30 #include <sys/dnv.h>
31 #include <sys/nv.h>
32
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <regex.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 #include "iovctl.h"
43
44 static void config_action(const char *filename, int dryrun);
45 static void delete_action(const char *device, int dryrun);
46 static void print_schema(const char *device);
47
48 /*
49 * Fetch the config schema from the kernel via ioctl. This function has to
50 * call the ioctl twice: the first returns the amount of memory that we need
51 * to allocate for the schema, and the second actually fetches the schema.
52 */
53 static nvlist_t *
get_schema(int fd)54 get_schema(int fd)
55 {
56 struct pci_iov_schema arg;
57 nvlist_t *schema;
58 int error;
59
60 /* Do the ioctl() once to fetch the size of the schema. */
61 arg.schema = NULL;
62 arg.len = 0;
63 arg.error = 0;
64 error = ioctl(fd, IOV_GET_SCHEMA, &arg);
65 if (error != 0)
66 err(1, "Could not fetch size of config schema");
67
68 arg.schema = malloc(arg.len);
69 if (arg.schema == NULL)
70 err(1, "Could not allocate %zu bytes for schema",
71 arg.len);
72
73 /* Now do the ioctl() for real to get the schema. */
74 error = ioctl(fd, IOV_GET_SCHEMA, &arg);
75 if (error != 0 || arg.error != 0) {
76 if (arg.error != 0)
77 errno = arg.error;
78 err(1, "Could not fetch config schema");
79 }
80
81 schema = nvlist_unpack(arg.schema, arg.len, NV_FLAG_IGNORE_CASE);
82 if (schema == NULL)
83 err(1, "Could not unpack schema");
84
85 free(arg.schema);
86 return (schema);
87 }
88
89 /*
90 * Call the ioctl that activates SR-IOV and creates the VFs.
91 */
92 static void
config_iov(int fd,const char * dev_name,const nvlist_t * config,int dryrun)93 config_iov(int fd, const char *dev_name, const nvlist_t *config, int dryrun)
94 {
95 struct pci_iov_arg arg;
96 int error;
97
98 arg.config = nvlist_pack(config, &arg.len);
99 if (arg.config == NULL)
100 err(1, "Could not pack configuration");
101
102 if (dryrun) {
103 printf("Would enable SR-IOV on device '%s'.\n", dev_name);
104 printf(
105 "The following configuration parameters would be used:\n");
106 nvlist_fdump(config, stdout);
107 printf(
108 "The configuration parameters consume %zu bytes when packed.\n",
109 arg.len);
110 } else {
111 error = ioctl(fd, IOV_CONFIG, &arg);
112 if (error != 0)
113 err(1, "Failed to configure SR-IOV");
114 }
115
116 free(arg.config);
117 }
118
119 static int
open_device(const char * dev_name)120 open_device(const char *dev_name)
121 {
122 char *dev;
123 int fd;
124 size_t copied, size;
125 long path_max;
126
127 path_max = pathconf("/dev", _PC_PATH_MAX);
128 if (path_max < 0)
129 err(1, "Could not get maximum path length");
130
131 size = path_max;
132 dev = malloc(size);
133 if (dev == NULL)
134 err(1, "Could not allocate memory for device path");
135
136 if (dev_name[0] == '/')
137 copied = strlcpy(dev, dev_name, size);
138 else
139 copied = snprintf(dev, size, "/dev/iov/%s", dev_name);
140
141 /* >= to account for null terminator. */
142 if (copied >= size)
143 errx(1, "Provided file name too long");
144
145 fd = open(dev, O_RDWR);
146 if (fd < 0)
147 err(1, "Could not open device '%s'", dev);
148
149 free(dev);
150 return (fd);
151 }
152
153 static void
usage(void)154 usage(void)
155 {
156
157 warnx("Usage: iovctl -C -f <config file> [-n]");
158 warnx(" iovctl -D [-d <PF device> | -f <config file>] [-n]");
159 warnx(" iovctl -S [-d <PF device> | -f <config file>]");
160 exit(1);
161
162 }
163
164 enum main_action {
165 NONE,
166 CONFIG,
167 DELETE,
168 PRINT_SCHEMA,
169 };
170
171 int
main(int argc,char ** argv)172 main(int argc, char **argv)
173 {
174 char *device;
175 const char *filename;
176 int ch, dryrun;
177 enum main_action action;
178
179 device = NULL;
180 filename = NULL;
181 dryrun = 0;
182 action = NONE;
183
184 while ((ch = getopt(argc, argv, "Cd:Df:nS")) != -1) {
185 switch (ch) {
186 case 'C':
187 if (action != NONE) {
188 warnx(
189 "Only one of -C, -D or -S may be specified");
190 usage();
191 }
192 action = CONFIG;
193 break;
194 case 'd':
195 device = strdup(optarg);
196 break;
197 case 'D':
198 if (action != NONE) {
199 warnx(
200 "Only one of -C, -D or -S may be specified");
201 usage();
202 }
203 action = DELETE;
204 break;
205 case 'f':
206 filename = optarg;
207 break;
208 case 'n':
209 dryrun = 1;
210 break;
211 case 'S':
212 if (action != NONE) {
213 warnx(
214 "Only one of -C, -D or -S may be specified");
215 usage();
216 }
217 action = PRINT_SCHEMA;
218 break;
219 case '?':
220 warnx("Unrecognized argument '-%c'\n", optopt);
221 usage();
222 break;
223 }
224 }
225
226 if (device != NULL && filename != NULL) {
227 warnx("Only one of the -d and -f flags may be specified");
228 usage();
229 }
230
231 if (device == NULL && filename == NULL && action != CONFIG) {
232 warnx("Either the -d or -f flag must be specified");
233 usage();
234 }
235
236 switch (action) {
237 case CONFIG:
238 if (device != NULL) {
239 warnx("-d flag cannot be used with the -C flag");
240 usage();
241 }
242 if (filename == NULL) {
243 warnx("The -f flag must be specified");
244 usage();
245 }
246 config_action(filename, dryrun);
247 break;
248 case DELETE:
249 if (device == NULL)
250 device = find_device(filename);
251 delete_action(device, dryrun);
252 free(device);
253 break;
254 case PRINT_SCHEMA:
255 if (dryrun) {
256 warnx("-n flag cannot be used with the -S flag");
257 usage();
258 }
259 if (device == NULL)
260 device = find_device(filename);
261 print_schema(device);
262 free(device);
263 break;
264 default:
265 usage();
266 break;
267 }
268
269 exit(0);
270 }
271
272 static void
config_action(const char * filename,int dryrun)273 config_action(const char *filename, int dryrun)
274 {
275 char *dev;
276 nvlist_t *schema, *config;
277 int fd;
278
279 dev = find_device(filename);
280 fd = open(dev, O_RDWR);
281 if (fd < 0)
282 err(1, "Could not open device '%s'", dev);
283
284 schema = get_schema(fd);
285 config = parse_config_file(filename, schema);
286 if (config == NULL)
287 errx(1, "Could not parse config");
288
289 config_iov(fd, dev, config, dryrun);
290
291 nvlist_destroy(config);
292 nvlist_destroy(schema);
293 free(dev);
294 close(fd);
295 }
296
297 static void
delete_action(const char * dev_name,int dryrun)298 delete_action(const char *dev_name, int dryrun)
299 {
300 int fd, error;
301
302 fd = open_device(dev_name);
303
304 if (dryrun)
305 printf("Would attempt to delete all VF children of '%s'\n",
306 dev_name);
307 else {
308 error = ioctl(fd, IOV_DELETE);
309 if (error != 0)
310 err(1, "Failed to delete VFs");
311 }
312
313 close(fd);
314 }
315
316 static void
print_default_value(const nvlist_t * parameter,const char * type)317 print_default_value(const nvlist_t *parameter, const char *type)
318 {
319 const uint8_t *mac;
320 size_t size;
321
322 if (strcasecmp(type, "bool") == 0)
323 printf(" (default = %s)",
324 nvlist_get_bool(parameter, DEFAULT_SCHEMA_NAME) ? "true" :
325 "false");
326 else if (strcasecmp(type, "string") == 0)
327 printf(" (default = %s)",
328 nvlist_get_string(parameter, DEFAULT_SCHEMA_NAME));
329 else if (strcasecmp(type, "uint8_t") == 0)
330 printf(" (default = %ju)",
331 (uintmax_t)nvlist_get_number(parameter,
332 DEFAULT_SCHEMA_NAME));
333 else if (strcasecmp(type, "uint16_t") == 0)
334 printf(" (default = %ju)",
335 (uintmax_t)nvlist_get_number(parameter,
336 DEFAULT_SCHEMA_NAME));
337 else if (strcasecmp(type, "uint32_t") == 0)
338 printf(" (default = %ju)",
339 (uintmax_t)nvlist_get_number(parameter,
340 DEFAULT_SCHEMA_NAME));
341 else if (strcasecmp(type, "uint64_t") == 0)
342 printf(" (default = %ju)",
343 (uintmax_t)nvlist_get_number(parameter,
344 DEFAULT_SCHEMA_NAME));
345 else if (strcasecmp(type, "unicast-mac") == 0) {
346 mac = nvlist_get_binary(parameter, DEFAULT_SCHEMA_NAME, &size);
347 printf(" (default = %02x:%02x:%02x:%02x:%02x:%02x)", mac[0],
348 mac[1], mac[2], mac[3], mac[4], mac[5]);
349 } else if (strcasecmp(type, "vlan") == 0) {
350 uint16_t vlan = nvlist_get_number(parameter, DEFAULT_SCHEMA_NAME);
351 if (vlan == VF_VLAN_TRUNK)
352 printf(" (default = trunk)");
353 else
354 printf(" (default = %d)", vlan);
355 } else
356 errx(1, "Unexpected type in schema: '%s'", type);
357 }
358
359 static void
print_subsystem_schema(const nvlist_t * subsystem_schema)360 print_subsystem_schema(const nvlist_t * subsystem_schema)
361 {
362 const char *name, *type;
363 const nvlist_t *parameter;
364 void *it;
365 int nvtype;
366
367 it = NULL;
368 while ((name = nvlist_next(subsystem_schema, &nvtype, &it)) != NULL) {
369 parameter = nvlist_get_nvlist(subsystem_schema, name);
370 type = nvlist_get_string(parameter, TYPE_SCHEMA_NAME);
371
372 printf("\t%s : %s", name, type);
373 if (dnvlist_get_bool(parameter, REQUIRED_SCHEMA_NAME, false))
374 printf(" (required)");
375 else if (nvlist_exists(parameter, DEFAULT_SCHEMA_NAME))
376 print_default_value(parameter, type);
377 else
378 printf(" (optional)");
379 printf("\n");
380 }
381 }
382
383 static void
print_schema(const char * dev_name)384 print_schema(const char *dev_name)
385 {
386 nvlist_t *schema;
387 const nvlist_t *iov_schema, *driver_schema, *pf_schema, *vf_schema;
388 int fd;
389
390 fd = open_device(dev_name);
391 schema = get_schema(fd);
392
393 pf_schema = nvlist_get_nvlist(schema, PF_CONFIG_NAME);
394 iov_schema = nvlist_get_nvlist(pf_schema, IOV_CONFIG_NAME);
395 driver_schema = nvlist_get_nvlist(pf_schema, DRIVER_CONFIG_NAME);
396 printf(
397 "The following configuration parameters may be configured on the PF:\n");
398 print_subsystem_schema(iov_schema);
399 print_subsystem_schema(driver_schema);
400
401 vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME);
402 iov_schema = nvlist_get_nvlist(vf_schema, IOV_CONFIG_NAME);
403 driver_schema = nvlist_get_nvlist(vf_schema, DRIVER_CONFIG_NAME);
404 printf(
405 "\nThe following configuration parameters may be configured on a VF:\n");
406 print_subsystem_schema(iov_schema);
407 print_subsystem_schema(driver_schema);
408
409 nvlist_destroy(schema);
410 close(fd);
411 }
412