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