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