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 * 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 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 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 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 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 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 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 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 350 errx(1, "Unexpected type in schema: '%s'", type); 351 } 352 353 static void 354 print_subsystem_schema(const nvlist_t * subsystem_schema) 355 { 356 const char *name, *type; 357 const nvlist_t *parameter; 358 void *it; 359 int nvtype; 360 361 it = NULL; 362 while ((name = nvlist_next(subsystem_schema, &nvtype, &it)) != NULL) { 363 parameter = nvlist_get_nvlist(subsystem_schema, name); 364 type = nvlist_get_string(parameter, TYPE_SCHEMA_NAME); 365 366 printf("\t%s : %s", name, type); 367 if (dnvlist_get_bool(parameter, REQUIRED_SCHEMA_NAME, false)) 368 printf(" (required)"); 369 else if (nvlist_exists(parameter, DEFAULT_SCHEMA_NAME)) 370 print_default_value(parameter, type); 371 else 372 printf(" (optional)"); 373 printf("\n"); 374 } 375 } 376 377 static void 378 print_schema(const char *dev_name) 379 { 380 nvlist_t *schema; 381 const nvlist_t *iov_schema, *driver_schema, *pf_schema, *vf_schema; 382 int fd; 383 384 fd = open_device(dev_name); 385 schema = get_schema(fd); 386 387 pf_schema = nvlist_get_nvlist(schema, PF_CONFIG_NAME); 388 iov_schema = nvlist_get_nvlist(pf_schema, IOV_CONFIG_NAME); 389 driver_schema = nvlist_get_nvlist(pf_schema, DRIVER_CONFIG_NAME); 390 printf( 391 "The following configuration parameters may be configured on the PF:\n"); 392 print_subsystem_schema(iov_schema); 393 print_subsystem_schema(driver_schema); 394 395 vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME); 396 iov_schema = nvlist_get_nvlist(vf_schema, IOV_CONFIG_NAME); 397 driver_schema = nvlist_get_nvlist(vf_schema, DRIVER_CONFIG_NAME); 398 printf( 399 "\nThe following configuration parameters may be configured on a VF:\n"); 400 print_subsystem_schema(iov_schema); 401 print_subsystem_schema(driver_schema); 402 403 nvlist_destroy(schema); 404 close(fd); 405 } 406