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
43static void	config_action(const char *filename, int dryrun);
44static void	delete_action(const char *device, int dryrun);
45static 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 */
52static nvlist_t *
53get_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 */
91static void
92config_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
118static int
119open_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
152static void
153usage(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
163enum main_action {
164	NONE,
165	CONFIG,
166	DELETE,
167	PRINT_SCHEMA,
168};
169
170int
171main(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
271static void
272config_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
296static void
297delete_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
315static void
316print_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
349		errx(1, "Unexpected type in schema: '%s'", type);
350}
351
352static void
353print_subsystem_schema(const nvlist_t * subsystem_schema)
354{
355	const char *name, *type;
356	const nvlist_t *parameter;
357	void *it;
358	int nvtype;
359
360	it = NULL;
361	while ((name = nvlist_next(subsystem_schema, &nvtype, &it)) != NULL) {
362		parameter = nvlist_get_nvlist(subsystem_schema, name);
363		type = nvlist_get_string(parameter, TYPE_SCHEMA_NAME);
364
365		printf("\t%s : %s", name, type);
366		if (dnvlist_get_bool(parameter, REQUIRED_SCHEMA_NAME, false))
367			printf(" (required)");
368		else if (nvlist_exists(parameter, DEFAULT_SCHEMA_NAME))
369			print_default_value(parameter, type);
370		else
371			printf(" (optional)");
372		printf("\n");
373	}
374}
375
376static void
377print_schema(const char *dev_name)
378{
379	nvlist_t *schema;
380	const nvlist_t *iov_schema, *driver_schema, *pf_schema, *vf_schema;
381	int fd;
382
383	fd = open_device(dev_name);
384	schema = get_schema(fd);
385
386	pf_schema = nvlist_get_nvlist(schema, PF_CONFIG_NAME);
387	iov_schema = nvlist_get_nvlist(pf_schema, IOV_CONFIG_NAME);
388	driver_schema = nvlist_get_nvlist(pf_schema, DRIVER_CONFIG_NAME);
389	printf(
390"The following configuration parameters may be configured on the PF:\n");
391	print_subsystem_schema(iov_schema);
392	print_subsystem_schema(driver_schema);
393
394	vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME);
395	iov_schema = nvlist_get_nvlist(vf_schema, IOV_CONFIG_NAME);
396	driver_schema = nvlist_get_nvlist(vf_schema, DRIVER_CONFIG_NAME);
397	printf(
398"\nThe following configuration parameters may be configured on a VF:\n");
399	print_subsystem_schema(iov_schema);
400	print_subsystem_schema(driver_schema);
401
402	nvlist_destroy(schema);
403	close(fd);
404}
405