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 * 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 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 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 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 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 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 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 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 349 errx(1, "Unexpected type in schema: '%s'", type); 350 } 351 352 static void 353 print_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 376 static void 377 print_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