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 46 static void config_action(const char *filename, int dryrun); 47 static void delete_action(const char *device, int dryrun); 48 static 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 */ 55 static nvlist_t * 56 get_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 */ 94 static void 95 config_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 121 static int 122 open_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 155 static void 156 usage(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 166 enum main_action { 167 NONE, 168 CONFIG, 169 DELETE, 170 PRINT_SCHEMA, 171 }; 172 173 int 174 main(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) { 234 warnx("Either the -d or -f flag must be specified"); 235 usage(); 236 } 237 238 switch (action) { 239 case CONFIG: 240 if (filename == NULL) { 241 warnx("-d flag cannot be used with the -C flag"); 242 usage(); 243 } 244 config_action(filename, dryrun); 245 break; 246 case DELETE: 247 if (device == NULL) 248 device = find_device(filename); 249 delete_action(device, dryrun); 250 free(device); 251 break; 252 case PRINT_SCHEMA: 253 if (dryrun) { 254 warnx("-n flag cannot be used with the -S flag"); 255 usage(); 256 } 257 if (device == NULL) 258 device = find_device(filename); 259 print_schema(device); 260 free(device); 261 break; 262 default: 263 usage(); 264 break; 265 } 266 267 exit(0); 268 } 269 270 static void 271 config_action(const char *filename, int dryrun) 272 { 273 char *dev; 274 nvlist_t *schema, *config; 275 int fd; 276 277 dev = find_device(filename); 278 fd = open(dev, O_RDWR); 279 if (fd < 0) 280 err(1, "Could not open device '%s'", dev); 281 282 schema = get_schema(fd); 283 config = parse_config_file(filename, schema); 284 if (config == NULL) 285 errx(1, "Could not parse config"); 286 287 config_iov(fd, dev, config, dryrun); 288 289 nvlist_destroy(config); 290 nvlist_destroy(schema); 291 free(dev); 292 close(fd); 293 } 294 295 static void 296 delete_action(const char *dev_name, int dryrun) 297 { 298 int fd, error; 299 300 fd = open_device(dev_name); 301 302 if (dryrun) 303 printf("Would attempt to delete all VF children of '%s'\n", 304 dev_name); 305 else { 306 error = ioctl(fd, IOV_DELETE); 307 if (error != 0) 308 err(1, "Failed to delete VFs"); 309 } 310 311 close(fd); 312 } 313 314 static void 315 print_default_value(const nvlist_t *parameter, const char *type) 316 { 317 const uint8_t *mac; 318 size_t size; 319 320 if (strcasecmp(type, "bool") == 0) 321 printf(" (default = %s)", 322 nvlist_get_bool(parameter, DEFAULT_SCHEMA_NAME) ? "true" : 323 "false"); 324 else if (strcasecmp(type, "string") == 0) 325 printf(" (default = %s)", 326 nvlist_get_string(parameter, DEFAULT_SCHEMA_NAME)); 327 else if (strcasecmp(type, "uint8_t") == 0) 328 printf(" (default = %ju)", 329 (uintmax_t)nvlist_get_number(parameter, 330 DEFAULT_SCHEMA_NAME)); 331 else if (strcasecmp(type, "uint16_t") == 0) 332 printf(" (default = %ju)", 333 (uintmax_t)nvlist_get_number(parameter, 334 DEFAULT_SCHEMA_NAME)); 335 else if (strcasecmp(type, "uint32_t") == 0) 336 printf(" (default = %ju)", 337 (uintmax_t)nvlist_get_number(parameter, 338 DEFAULT_SCHEMA_NAME)); 339 else if (strcasecmp(type, "uint64_t") == 0) 340 printf(" (default = %ju)", 341 (uintmax_t)nvlist_get_number(parameter, 342 DEFAULT_SCHEMA_NAME)); 343 else if (strcasecmp(type, "unicast-mac") == 0) { 344 mac = nvlist_get_binary(parameter, DEFAULT_SCHEMA_NAME, &size); 345 printf(" (default = %02x:%02x:%02x:%02x:%02x:%02x)", mac[0], 346 mac[1], mac[2], mac[3], mac[4], mac[5]); 347 } else 348 errx(1, "Unexpected type in schema: '%s'", type); 349 } 350 351 static void 352 print_subsystem_schema(const nvlist_t * subsystem_schema) 353 { 354 const char *name, *type; 355 const nvlist_t *parameter; 356 void *it; 357 int nvtype; 358 359 it = NULL; 360 while ((name = nvlist_next(subsystem_schema, &nvtype, &it)) != NULL) { 361 parameter = nvlist_get_nvlist(subsystem_schema, name); 362 type = nvlist_get_string(parameter, TYPE_SCHEMA_NAME); 363 364 printf("\t%s : %s", name, type); 365 if (dnvlist_get_bool(parameter, REQUIRED_SCHEMA_NAME, false)) 366 printf(" (required)"); 367 else if (nvlist_exists(parameter, DEFAULT_SCHEMA_NAME)) 368 print_default_value(parameter, type); 369 else 370 printf(" (optional)"); 371 printf("\n"); 372 } 373 } 374 375 static void 376 print_schema(const char *dev_name) 377 { 378 nvlist_t *schema; 379 const nvlist_t *iov_schema, *driver_schema, *pf_schema, *vf_schema; 380 int fd; 381 382 fd = open_device(dev_name); 383 schema = get_schema(fd); 384 385 pf_schema = nvlist_get_nvlist(schema, PF_CONFIG_NAME); 386 iov_schema = nvlist_get_nvlist(pf_schema, IOV_CONFIG_NAME); 387 driver_schema = nvlist_get_nvlist(pf_schema, DRIVER_CONFIG_NAME); 388 printf( 389 "The following configuration parameters may be configured on the PF:\n"); 390 print_subsystem_schema(iov_schema); 391 print_subsystem_schema(driver_schema); 392 393 vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME); 394 iov_schema = nvlist_get_nvlist(vf_schema, IOV_CONFIG_NAME); 395 driver_schema = nvlist_get_nvlist(vf_schema, DRIVER_CONFIG_NAME); 396 printf( 397 "\nThe following configuration parameters may be configured on a VF:\n"); 398 print_subsystem_schema(iov_schema); 399 print_subsystem_schema(driver_schema); 400 401 nvlist_destroy(schema); 402 close(fd); 403 } 404