diff --git a/sys/dev/pci/pci_iov_schema.c b/sys/dev/pci/pci_iov_schema.c index 742dfbd2b4d9..bb87e7215bc8 100644 --- a/sys/dev/pci/pci_iov_schema.c +++ b/sys/dev/pci/pci_iov_schema.c @@ -1,867 +1,928 @@ /*- * Copyright (c) 2014-2015 Sandvine Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct config_type_validator; typedef int (validate_func)(const struct config_type_validator *, const nvlist_t *, const char *name); typedef int (default_validate_t)(const struct config_type_validator *, const nvlist_t *); static validate_func pci_iov_schema_validate_bool; static validate_func pci_iov_schema_validate_string; static validate_func pci_iov_schema_validate_uint; static validate_func pci_iov_schema_validate_unicast_mac; +static validate_func pci_iov_schema_validate_vlan; static default_validate_t pci_iov_validate_bool_default; static default_validate_t pci_iov_validate_string_default; static default_validate_t pci_iov_validate_uint_default; static default_validate_t pci_iov_validate_unicast_mac_default; +static default_validate_t pci_iov_validate_vlan_default; struct config_type_validator { const char *type_name; validate_func *validate; default_validate_t *default_validate; uintmax_t limit; }; static struct config_type_validator pci_iov_schema_validators[] = { { .type_name = "bool", .validate = pci_iov_schema_validate_bool, .default_validate = pci_iov_validate_bool_default }, { .type_name = "string", .validate = pci_iov_schema_validate_string, .default_validate = pci_iov_validate_string_default }, { .type_name = "uint8_t", .validate = pci_iov_schema_validate_uint, .default_validate = pci_iov_validate_uint_default, .limit = UINT8_MAX }, { .type_name = "uint16_t", .validate = pci_iov_schema_validate_uint, .default_validate = pci_iov_validate_uint_default, .limit = UINT16_MAX }, { .type_name = "uint32_t", .validate = pci_iov_schema_validate_uint, .default_validate = pci_iov_validate_uint_default, .limit = UINT32_MAX }, { .type_name = "uint64_t", .validate = pci_iov_schema_validate_uint, .default_validate = pci_iov_validate_uint_default, .limit = UINT64_MAX }, { .type_name = "unicast-mac", .validate = pci_iov_schema_validate_unicast_mac, .default_validate = pci_iov_validate_unicast_mac_default, }, + { + .type_name = "vlan", + .validate = pci_iov_schema_validate_vlan, + .default_validate = pci_iov_validate_vlan_default, + }, }; static const struct config_type_validator * pci_iov_schema_find_validator(const char *type) { struct config_type_validator *validator; int i; for (i = 0; i < nitems(pci_iov_schema_validators); i++) { validator = &pci_iov_schema_validators[i]; if (strcmp(type, validator->type_name) == 0) return (validator); } return (NULL); } static void pci_iov_schema_add_type(nvlist_t *entry, const char *type) { if (pci_iov_schema_find_validator(type) == NULL) { nvlist_set_error(entry, EINVAL); return; } nvlist_add_string(entry, "type", type); } static void pci_iov_schema_add_required(nvlist_t *entry, uint32_t flags) { if (flags & IOV_SCHEMA_REQUIRED) { if (flags & IOV_SCHEMA_HASDEFAULT) { nvlist_set_error(entry, EINVAL); return; } nvlist_add_bool(entry, "required", 1); } } void pci_iov_schema_add_bool(nvlist_t *schema, const char *name, uint32_t flags, int defaultVal) { nvlist_t *entry; entry = nvlist_create(NV_FLAG_IGNORE_CASE); if (entry == NULL) { nvlist_set_error(schema, ENOMEM); return; } pci_iov_schema_add_type(entry, "bool"); if (flags & IOV_SCHEMA_HASDEFAULT) nvlist_add_bool(entry, "default", defaultVal); pci_iov_schema_add_required(entry, flags); nvlist_move_nvlist(schema, name, entry); } void pci_iov_schema_add_string(nvlist_t *schema, const char *name, uint32_t flags, const char *defaultVal) { nvlist_t *entry; entry = nvlist_create(NV_FLAG_IGNORE_CASE); if (entry == NULL) { nvlist_set_error(schema, ENOMEM); return; } pci_iov_schema_add_type(entry, "string"); if (flags & IOV_SCHEMA_HASDEFAULT) nvlist_add_string(entry, "default", defaultVal); pci_iov_schema_add_required(entry, flags); nvlist_move_nvlist(schema, name, entry); } static void pci_iov_schema_int(nvlist_t *schema, const char *name, const char *type, uint32_t flags, uint64_t defaultVal) { nvlist_t *entry; entry = nvlist_create(NV_FLAG_IGNORE_CASE); if (entry == NULL) { nvlist_set_error(schema, ENOMEM); return; } pci_iov_schema_add_type(entry, type); if (flags & IOV_SCHEMA_HASDEFAULT) nvlist_add_number(entry, "default", defaultVal); pci_iov_schema_add_required(entry, flags); nvlist_move_nvlist(schema, name, entry); } void pci_iov_schema_add_uint8(nvlist_t *schema, const char *name, uint32_t flags, uint8_t defaultVal) { pci_iov_schema_int(schema, name, "uint8_t", flags, defaultVal); } void pci_iov_schema_add_uint16(nvlist_t *schema, const char *name, uint32_t flags, uint16_t defaultVal) { pci_iov_schema_int(schema, name, "uint16_t", flags, defaultVal); } void pci_iov_schema_add_uint32(nvlist_t *schema, const char *name, uint32_t flags, uint32_t defaultVal) { pci_iov_schema_int(schema, name, "uint32_t", flags, defaultVal); } void pci_iov_schema_add_uint64(nvlist_t *schema, const char *name, uint32_t flags, uint64_t defaultVal) { pci_iov_schema_int(schema, name, "uint64_t", flags, defaultVal); } void pci_iov_schema_add_unicast_mac(nvlist_t *schema, const char *name, uint32_t flags, const uint8_t * defaultVal) { nvlist_t *entry; entry = nvlist_create(NV_FLAG_IGNORE_CASE); if (entry == NULL) { nvlist_set_error(schema, ENOMEM); return; } pci_iov_schema_add_type(entry, "unicast-mac"); if (flags & IOV_SCHEMA_HASDEFAULT) nvlist_add_binary(entry, "default", defaultVal, ETHER_ADDR_LEN); pci_iov_schema_add_required(entry, flags); nvlist_move_nvlist(schema, name, entry); } +void +pci_iov_schema_add_vlan(nvlist_t *schema, const char *name, + uint32_t flags, const uint16_t defaultVal) +{ + nvlist_t *entry; + + entry = nvlist_create(NV_FLAG_IGNORE_CASE); + if (entry == NULL) { + nvlist_set_error(schema, ENOMEM); + return; + } + + pci_iov_schema_add_type(entry, "vlan"); + if (flags & IOV_SCHEMA_HASDEFAULT) + nvlist_add_number(entry, "default", defaultVal); + pci_iov_schema_add_required(entry, flags); + + nvlist_move_nvlist(schema, name, entry); +} + static int pci_iov_schema_validate_bool(const struct config_type_validator * validator, const nvlist_t *config, const char *name) { if (!nvlist_exists_bool(config, name)) return (EINVAL); return (0); } static int pci_iov_schema_validate_string(const struct config_type_validator * validator, const nvlist_t *config, const char *name) { if (!nvlist_exists_string(config, name)) return (EINVAL); return (0); } static int pci_iov_schema_validate_uint(const struct config_type_validator * validator, const nvlist_t *config, const char *name) { uint64_t value; if (!nvlist_exists_number(config, name)) return (EINVAL); value = nvlist_get_number(config, name); if (value > validator->limit) return (EINVAL); return (0); } static int pci_iov_schema_validate_unicast_mac( const struct config_type_validator * validator, const nvlist_t *config, const char *name) { const uint8_t *mac; size_t size; if (!nvlist_exists_binary(config, name)) return (EINVAL); mac = nvlist_get_binary(config, name, &size); if (size != ETHER_ADDR_LEN) return (EINVAL); if (ETHER_IS_MULTICAST(mac)) return (EINVAL); return (0); } +static int +pci_iov_schema_validate_vlan( + const struct config_type_validator * validator, + const nvlist_t *config, const char *name) +{ + uint16_t vlan; + + if (!nvlist_exists_number(config, name)) + return (EINVAL); + + vlan = nvlist_get_number(config, name); + + if (vlan > 4095 && vlan != VF_VLAN_TRUNK) + return (EINVAL); + + return (0); +} + static void pci_iov_config_add_default(const nvlist_t *param_schema, const char *name, nvlist_t *config) { const void *binary; size_t len; if (nvlist_exists_binary(param_schema, "default")) { binary = nvlist_get_binary(param_schema, "default", &len); nvlist_add_binary(config, name, binary, len); } else if (nvlist_exists_bool(param_schema, "default")) nvlist_add_bool(config, name, nvlist_get_bool(param_schema, "default")); else if (nvlist_exists_number(param_schema, "default")) nvlist_add_number(config, name, nvlist_get_number(param_schema, "default")); else if (nvlist_exists_nvlist(param_schema, "default")) nvlist_add_nvlist(config, name, nvlist_get_nvlist(param_schema, "default")); else if (nvlist_exists_string(param_schema, "default")) nvlist_add_string(config, name, nvlist_get_string(param_schema, "default")); else panic("Unexpected nvlist type"); } static int pci_iov_validate_bool_default(const struct config_type_validator * validator, const nvlist_t *param) { if (!nvlist_exists_bool(param, DEFAULT_SCHEMA_NAME)) return (EINVAL); return (0); } static int pci_iov_validate_string_default(const struct config_type_validator * validator, const nvlist_t *param) { if (!nvlist_exists_string(param, DEFAULT_SCHEMA_NAME)) return (EINVAL); return (0); } static int pci_iov_validate_uint_default(const struct config_type_validator * validator, const nvlist_t *param) { uint64_t defaultVal; if (!nvlist_exists_number(param, DEFAULT_SCHEMA_NAME)) return (EINVAL); defaultVal = nvlist_get_number(param, DEFAULT_SCHEMA_NAME); if (defaultVal > validator->limit) return (EINVAL); return (0); } static int pci_iov_validate_unicast_mac_default( const struct config_type_validator * validator, const nvlist_t *param) { const uint8_t *mac; size_t size; if (!nvlist_exists_binary(param, DEFAULT_SCHEMA_NAME)) return (EINVAL); mac = nvlist_get_binary(param, DEFAULT_SCHEMA_NAME, &size); if (size != ETHER_ADDR_LEN) return (EINVAL); if (ETHER_IS_MULTICAST(mac)) return (EINVAL); return (0); } +static int +pci_iov_validate_vlan_default( + const struct config_type_validator * validator, const nvlist_t *param) +{ + uint16_t vlan; + + if (! nvlist_exists_number(param, DEFAULT_SCHEMA_NAME)) + return (EINVAL); + + vlan = nvlist_get_number(param, DEFAULT_SCHEMA_NAME); + if (vlan > 4095 && vlan != VF_VLAN_TRUNK) + return (EINVAL); + + return (0); +} + static int pci_iov_validate_param_schema(const nvlist_t *schema) { const struct config_type_validator *validator; const char *type; int error; /* All parameters must define a type. */ if (!nvlist_exists_string(schema, TYPE_SCHEMA_NAME)) return (EINVAL); type = nvlist_get_string(schema, TYPE_SCHEMA_NAME); validator = pci_iov_schema_find_validator(type); if (validator == NULL) return (EINVAL); /* Validate that the default value conforms to the type. */ if (nvlist_exists(schema, DEFAULT_SCHEMA_NAME)) { error = validator->default_validate(validator, schema); if (error != 0) return (error); /* Required and Default are mutually exclusive. */ if (nvlist_exists(schema, REQUIRED_SCHEMA_NAME)) return (EINVAL); } /* The "Required" field must be a bool. */ if (nvlist_exists(schema, REQUIRED_SCHEMA_NAME)) { if (!nvlist_exists_bool(schema, REQUIRED_SCHEMA_NAME)) return (EINVAL); } return (0); } static int pci_iov_validate_subsystem_schema(const nvlist_t *dev_schema, const char *name) { const nvlist_t *sub_schema, *param_schema; const char *param_name; void *it; int type, error; if (!nvlist_exists_nvlist(dev_schema, name)) return (EINVAL); sub_schema = nvlist_get_nvlist(dev_schema, name); it = NULL; while ((param_name = nvlist_next(sub_schema, &type, &it)) != NULL) { if (type != NV_TYPE_NVLIST) return (EINVAL); param_schema = nvlist_get_nvlist(sub_schema, param_name); error = pci_iov_validate_param_schema(param_schema); if (error != 0) return (error); } return (0); } /* * Validate that the driver schema does not define any configuration parameters * whose names collide with configuration parameters defined in the iov schema. */ static int pci_iov_validate_param_collisions(const nvlist_t *dev_schema) { const nvlist_t *iov_schema, *driver_schema; const char *name; void *it; int type; driver_schema = nvlist_get_nvlist(dev_schema, DRIVER_CONFIG_NAME); iov_schema = nvlist_get_nvlist(dev_schema, IOV_CONFIG_NAME); it = NULL; while ((name = nvlist_next(driver_schema, &type, &it)) != NULL) { if (nvlist_exists(iov_schema, name)) return (EINVAL); } return (0); } /* * Validate that we only have IOV and DRIVER subsystems beneath the given * device schema node. */ static int pci_iov_validate_schema_subsystems(const nvlist_t *dev_schema) { const char *name; void *it; int type; it = NULL; while ((name = nvlist_next(dev_schema, &type, &it)) != NULL) { if (strcmp(name, IOV_CONFIG_NAME) != 0 && strcmp(name, DRIVER_CONFIG_NAME) != 0) return (EINVAL); } return (0); } static int pci_iov_validate_device_schema(const nvlist_t *schema, const char *name) { const nvlist_t *dev_schema; int error; if (!nvlist_exists_nvlist(schema, name)) return (EINVAL); dev_schema = nvlist_get_nvlist(schema, name); error = pci_iov_validate_subsystem_schema(dev_schema, IOV_CONFIG_NAME); if (error != 0) return (error); error = pci_iov_validate_subsystem_schema(dev_schema, DRIVER_CONFIG_NAME); if (error != 0) return (error); error = pci_iov_validate_param_collisions(dev_schema); if (error != 0) return (error); return (pci_iov_validate_schema_subsystems(dev_schema)); } /* Validate that we only have PF and VF devices beneath the top-level schema. */ static int pci_iov_validate_schema_devices(const nvlist_t *dev_schema) { const char *name; void *it; int type; it = NULL; while ((name = nvlist_next(dev_schema, &type, &it)) != NULL) { if (strcmp(name, PF_CONFIG_NAME) != 0 && strcmp(name, VF_SCHEMA_NAME) != 0) return (EINVAL); } return (0); } int pci_iov_validate_schema(const nvlist_t *schema) { int error; error = pci_iov_validate_device_schema(schema, PF_CONFIG_NAME); if (error != 0) return (error); error = pci_iov_validate_device_schema(schema, VF_SCHEMA_NAME); if (error != 0) return (error); return (pci_iov_validate_schema_devices(schema)); } /* * Validate that all required parameters from the schema are specified in the * config. If any parameter with a default value is not specified in the * config, add it to config. */ static int pci_iov_schema_validate_required(const nvlist_t *schema, nvlist_t *config) { const nvlist_t *param_schema; const char *name; void *cookie; int type; cookie = NULL; while ((name = nvlist_next(schema, &type, &cookie)) != NULL) { param_schema = nvlist_get_nvlist(schema, name); if (dnvlist_get_bool(param_schema, "required", 0)) { if (!nvlist_exists(config, name)) return (EINVAL); } if (nvlist_exists(param_schema, "default") && !nvlist_exists(config, name)) pci_iov_config_add_default(param_schema, name, config); } return (nvlist_error(config)); } static int pci_iov_schema_validate_param(const nvlist_t *schema_param, const char *name, const nvlist_t *config) { const struct config_type_validator *validator; const char *type; type = nvlist_get_string(schema_param, "type"); validator = pci_iov_schema_find_validator(type); KASSERT(validator != NULL, ("Schema was not validated: Unknown type %s", type)); return (validator->validate(validator, config, name)); } /* * Validate that all parameters in config are defined in the schema. Also * validate that the type of the parameter matches the type in the schema. */ static int pci_iov_schema_validate_types(const nvlist_t *schema, const nvlist_t *config) { const nvlist_t *schema_param; void *cookie; const char *name; int type, error; cookie = NULL; while ((name = nvlist_next(config, &type, &cookie)) != NULL) { if (!nvlist_exists_nvlist(schema, name)) return (EINVAL); schema_param = nvlist_get_nvlist(schema, name); error = pci_iov_schema_validate_param(schema_param, name, config); if (error != 0) return (error); } return (0); } static int pci_iov_schema_validate_device(const nvlist_t *schema, nvlist_t *config, const char *schema_device, const char *config_device) { const nvlist_t *device_schema, *iov_schema, *driver_schema; nvlist_t *device_config, *iov_config, *driver_config; int error; device_config = NULL; iov_config = NULL; driver_config = NULL; device_schema = nvlist_get_nvlist(schema, schema_device); iov_schema = nvlist_get_nvlist(device_schema, IOV_CONFIG_NAME); driver_schema = nvlist_get_nvlist(device_schema, DRIVER_CONFIG_NAME); device_config = dnvlist_take_nvlist(config, config_device, NULL); if (device_config == NULL) { error = EINVAL; goto out; } iov_config = dnvlist_take_nvlist(device_config, IOV_CONFIG_NAME, NULL); if (iov_config == NULL) { error = EINVAL; goto out; } driver_config = dnvlist_take_nvlist(device_config, DRIVER_CONFIG_NAME, NULL); if (driver_config == NULL) { error = EINVAL; goto out; } error = pci_iov_schema_validate_required(iov_schema, iov_config); if (error != 0) goto out; error = pci_iov_schema_validate_required(driver_schema, driver_config); if (error != 0) goto out; error = pci_iov_schema_validate_types(iov_schema, iov_config); if (error != 0) goto out; error = pci_iov_schema_validate_types(driver_schema, driver_config); if (error != 0) goto out; out: /* Note that these functions handle NULL pointers safely. */ nvlist_move_nvlist(device_config, IOV_CONFIG_NAME, iov_config); nvlist_move_nvlist(device_config, DRIVER_CONFIG_NAME, driver_config); nvlist_move_nvlist(config, config_device, device_config); return (error); } static int pci_iov_schema_validate_vfs(const nvlist_t *schema, nvlist_t *config, uint16_t num_vfs) { char device[VF_MAX_NAME]; int i, error; for (i = 0; i < num_vfs; i++) { snprintf(device, sizeof(device), VF_PREFIX"%d", i); error = pci_iov_schema_validate_device(schema, config, VF_SCHEMA_NAME, device); if (error != 0) return (error); } return (0); } /* * Validate that the device node only has IOV and DRIVER subnodes. */ static int pci_iov_schema_validate_device_subsystems(const nvlist_t *config) { void *cookie; const char *name; int type; cookie = NULL; while ((name = nvlist_next(config, &type, &cookie)) != NULL) { if (strcasecmp(name, IOV_CONFIG_NAME) == 0) continue; else if (strcasecmp(name, DRIVER_CONFIG_NAME) == 0) continue; return (EINVAL); } return (0); } /* * Validate that the string is a valid device node name. It must either be "PF" * or "VF-n", where n is an integer in the range [0, num_vfs). */ static int pci_iov_schema_validate_dev_name(const char *name, uint16_t num_vfs) { const char *number_start; char *endp; u_long vf_num; if (strcasecmp(PF_CONFIG_NAME, name) == 0) return (0); /* Ensure that we start with "VF-" */ if (strncasecmp(name, VF_PREFIX, VF_PREFIX_LEN) != 0) return (EINVAL); number_start = name + VF_PREFIX_LEN; /* Filter out name == "VF-" (no number) */ if (number_start[0] == '\0') return (EINVAL); /* Disallow leading whitespace or +/- */ if (!isdigit(number_start[0])) return (EINVAL); vf_num = strtoul(number_start, &endp, 10); if (*endp != '\0') return (EINVAL); /* Disallow leading zeros on VF-[1-9][0-9]* */ if (vf_num != 0 && number_start[0] == '0') return (EINVAL); /* Disallow leading zeros on VF-0 */ if (vf_num == 0 && number_start[1] != '\0') return (EINVAL); if (vf_num >= num_vfs) return (EINVAL); return (0); } /* * Validate that there are no device nodes in config other than the ones for * the PF and the VFs. This includes validating that all config nodes of the * form VF-n specify a VF number that is < num_vfs. */ static int pci_iov_schema_validate_device_names(const nvlist_t *config, uint16_t num_vfs) { const nvlist_t *device; void *cookie; const char *name; int type, error; cookie = NULL; while ((name = nvlist_next(config, &type, &cookie)) != NULL) { error = pci_iov_schema_validate_dev_name(name, num_vfs); if (error != 0) return (error); /* * Note that as this is a valid PF/VF node, we know that * pci_iov_schema_validate_device() has already checked that * the PF/VF node is an nvlist. */ device = nvlist_get_nvlist(config, name); error = pci_iov_schema_validate_device_subsystems(device); if (error != 0) return (error); } return (0); } int pci_iov_schema_validate_config(const nvlist_t *schema, nvlist_t *config) { int error; uint16_t num_vfs; error = pci_iov_schema_validate_device(schema, config, PF_CONFIG_NAME, PF_CONFIG_NAME); if (error != 0) return (error); num_vfs = pci_iov_config_get_num_vfs(config); error = pci_iov_schema_validate_vfs(schema, config, num_vfs); if (error != 0) return (error); return (pci_iov_schema_validate_device_names(config, num_vfs)); } /* * Return value of the num_vfs parameter. config must have already been * validated, which guarantees that the parameter exists. */ uint16_t pci_iov_config_get_num_vfs(const nvlist_t *config) { const nvlist_t *pf, *iov; pf = nvlist_get_nvlist(config, PF_CONFIG_NAME); iov = nvlist_get_nvlist(pf, IOV_CONFIG_NAME); return (nvlist_get_number(iov, "num_vfs")); } /* Allocate a new empty schema node. */ nvlist_t * pci_iov_schema_alloc_node(void) { return (nvlist_create(NV_FLAG_IGNORE_CASE)); } diff --git a/sys/sys/iov.h b/sys/sys/iov.h index 0171fc5a98cf..2ae7e5ac6767 100644 --- a/sys/sys/iov.h +++ b/sys/sys/iov.h @@ -1,256 +1,258 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013-2015 Sandvine Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _SYS_IOV_H_ #define _SYS_IOV_H_ #include #define PF_CONFIG_NAME "PF" #define VF_SCHEMA_NAME "VF" #define VF_PREFIX "VF-" #define VF_PREFIX_LEN 3 #define VF_NUM_LEN 5 /* The maximum VF num is 65535. */ #define VF_MAX_NAME (VF_PREFIX_LEN + VF_NUM_LEN + 1) #define DRIVER_CONFIG_NAME "DRIVER" #define IOV_CONFIG_NAME "IOV" #define TYPE_SCHEMA_NAME "TYPE" #define DEFAULT_SCHEMA_NAME "DEFAULT" #define REQUIRED_SCHEMA_NAME "REQUIRED" +#define VF_VLAN_TRUNK 4096 + /* * Because each PF device is expected to expose a unique set of possible * configurations, the SR-IOV infrastructure dynamically queries the PF * driver for its capabilities. These capabilities are exposed to userland * with a configuration schema. The schema is exported from the kernel as a * packed nvlist. See nv(3) for the details of the nvlist API. The expected * format of the nvlist is: * * BASIC RULES * 1) All keys are case-insensitive. * 2) No keys that are not specified below may exist at any level of the * schema. * 3) All keys are mandatory unless explicitly documented as optional. If a * key is mandatory then the associated value is also mandatory. * 4) Order of keys is irrelevant. * * TOP LEVEL * 1) There must be a top-level key with the name PF_CONFIG_NAME. The value * associated with this key is a nvlist that follows the device schema * node format. The parameters in this node specify the configuration * parameters that may be applied to a PF. * 2) There must be a top-level key with the name VF_SCHEMA_NAME. The value * associated with this key is a nvlist that follows the device schema * node format. The parameters in this node specify the configuration * parameters that may be applied to a VF. * * DEVICE SCHEMA NODE * 1) There must be a key with the name DRIVER_CONFIG_NAME. The value * associated with this key is a nvlist that follows the device/subsystem * schema node format. The parameters in this node specify the * configuration parameters that are specific to a particular device * driver. * 2) There must be a key with the name IOV_CONFIG_NAME. The value associated * with this key is an nvlist that follows the device/subsystem schema node * format. The parameters in this node specify the configuration * parameters that are applied by the SR-IOV infrastructure. * * DEVICE/SUBSYSTEM SCHEMA NODE * 1) All keys in the device/subsystem schema node are optional. * 2) Each key specifies the name of a valid configuration parameter that may * be applied to the device/subsystem combination specified by this node. * The value associated with the key specifies the format of valid * configuration values, and must be a nvlist in parameter schema node * format. * * PARAMETER SCHEMA NODE * 1) The parameter schema node must contain a key with the name * TYPE_SCHEMA_NAME. The value associated with this key must be a string. * This string specifies the type of value that the parameter specified by * this node must take. The string must have one of the following values: * - "bool" - The configuration value must be a boolean. * - "mac-addr" - The configuration value must be a binary value. In * addition, the value must be exactly 6 bytes long and * the value must not be a multicast or broadcast mac. * - "uint8_t" - The configuration value must be a integer value in * the range [0, UINT8_MAX]. * - "uint16_t" - The configuration value must be a integer value in * the range [0, UINT16_MAX]. * - "uint32_t" - The configuration value must be a integer value in * the range [0, UINT32_MAX]. * - "uint64_t" - The configuration value must be a integer value in * the range [0, UINT64_MAX]. * 2) The parameter schema may contain a key with the name * REQUIRED_SCHEMA_NAME. This key is optional. If this key is present, the * value associated with it must have a boolean type. If the value is true, * then the parameter specified by this schema is a required parameter. All * valid configurations must include all required parameters. * 3) The parameter schema may contain a key with the name DEFAULT_SCHEMA_NAME. * This key is optional. This key must not be present if the parameter * specified by this schema is required. If this key is present, the value * associated with the parent key must follow all restrictions specified by * the type specified by this schema. If a configuration does not supply a * value for the parameter specified by this schema, then the kernel will * apply the value associated with this key in its place. * * The following is an example of a valid schema, as printed by nvlist_dump. * Keys are printed followed by the type of the value in parantheses. The * value is displayed following a colon. The indentation level reflects the * level of nesting of nvlists. String values are displayed between [] * brackets. Binary values are shown with the length of the binary value (in * bytes) followed by the actual binary values. * * PF (NVLIST): * IOV (NVLIST): * num_vfs (NVLIST): * type (STRING): [uint16_t] * required (BOOL): TRUE * device (NVLIST): * type (STRING): [string] * required (BOOL): TRUE * DRIVER (NVLIST): * VF (NVLIST): * IOV (NVLIST): * passthrough (NVLIST): * type (STRING): [bool] * default (BOOL): FALSE * DRIVER (NVLIST): * mac-addr (NVLIST): * type (STRING): [mac-addr] * default (BINARY): 6 000000000000 * vlan (NVLIST): * type (STRING): [uint16_t] * spoof-check (NVLIST): * type (STRING): [bool] * default (BOOL): TRUE * allow-set-mac (NVLIST): * type (STRING): [bool] * default (BOOL): FALSE */ struct pci_iov_schema { void *schema; size_t len; int error; }; /* * SR-IOV configuration is passed to the kernel as a packed nvlist. See nv(3) * for the details of the nvlist API. The expected format of the nvlist is: * * BASIC RULES * 1) All keys are case-insensitive. * 2) No keys that are not specified below may exist at any level of the * config nvlist. * 3) Unless otherwise specified, all keys are optional. It should go without * saying a key being mandatory is transitive: that is, if a key is * specified to contain a sub-nodes that contains a mandatory key, then * the outer key is implicitly mandatory. If a key is mandatory then the * associated value is also mandatory. * 4) Order of keys is irrelevant. * * TOP LEVEL OF CONFIG NVLIST * 1) All keys specified in this section are mandatory. * 2) There must be a top-level key with the name PF_CONFIG_NAME. The value * associated is an nvlist that follows the "device node" format. The * parameters in this node specify parameters that apply to the PF. * 3) For every VF being configured (this is set via the "num_vfs" parameter * in the PF section), there must be a top-level key whose name is VF_PREFIX * immediately followed by the index of the VF as a decimal integer. For * example, this would be VF-0 for the first VF. VFs are numbered starting * from 0. The value associated with this key follows the "device node" * format. The parameters in this node specify configuration that applies * to the VF specified in the key. Leading zeros are not permitted in VF * index. Configuration for the second VF must be specified in a node with * the key VF-1. VF-01 is not a valid key. * * DEVICE NODES * 1) All keys specified in this section are mandatory. * 2) The device node must contain a key with the name DRIVER_CONFIG_NAME. The * value associated with this key is an nvlist following the subsystem node * format. The parameters in this key specify configuration that is specific * to a particular device driver. * 3) The device node must contain a key with the name IOV_CONFIG_NAME. The * value associated with this key is an nvlist following the subsystem node * format. The parameters in this key specify configuration that is consumed * by the SR-IOV infrastructure. * * SUBSYSTEM NODES * 1) A subsystem node specifies configuration parameters that apply to a * particular subsystem (driver or infrastructure) of a particular device * (PF or individual VF). * Note: We will refer to the section of the configuration schema that * specifies the parameters for this subsystem and device * configuration as the device/subsystem schema. * 2) The subsystem node must contain only keys that correspond to parameters * that are specified in the device/subsystem schema. * 3) Every parameter specified as required in the device/subsystem schema is * a mandatory key in the subsystem node. * Note: All parameters that are not required in device/subsystem schema are * optional keys. In particular, any parameter specified to have a * default value in the device/subsystem schema is optional. The * kernel is responsible for applying default values. * 4) The value of every parameter in the device node must conform to the * restrictions of the type specified for that parameter in the device/ * subsystem schema. * * The following is an example of a valid configuration, when validated against * the schema example given above. * * PF (NVLIST): * driver (NVLIST): * iov (NVLIST): * num_vfs (NUMBER): 3 (3) (0x3) * device (STRING): [ix0] * VF-0 (NVLIST): * driver (NVLIST): * vlan (NUMBER): 1000 (1000) (0x3e8) * iov (NVLIST): * passthrough (BOOL): TRUE * VF-1 (NVLIST): * driver (NVLIST): * iov (NVLIST): * VF-2 (NVLIST): * driver (NVLIST): * mac-addr (BINARY): 6 020102030405 * iov (NVLIST): */ struct pci_iov_arg { void *config; size_t len; }; #define IOV_CONFIG _IOW('p', 10, struct pci_iov_arg) #define IOV_DELETE _IO('p', 11) #define IOV_GET_SCHEMA _IOWR('p', 12, struct pci_iov_schema) #endif diff --git a/sys/sys/iov_schema.h b/sys/sys/iov_schema.h index cb50f7796d30..66c883540563 100644 --- a/sys/sys/iov_schema.h +++ b/sys/sys/iov_schema.h @@ -1,52 +1,53 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2014-2015 Sandvine Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _SYS_IOV_SCHEMA_H_ #define _SYS_IOV_SCHEMA_H_ #define IOV_SCHEMA_HASDEFAULT (1 << 0) #define IOV_SCHEMA_REQUIRED (1 << 1) nvlist_t *pci_iov_schema_alloc_node(void); void pci_iov_schema_add_bool(nvlist_t *schema, const char *name, uint32_t flags, int defaultVal); void pci_iov_schema_add_string(nvlist_t *schema, const char *name, uint32_t flags, const char *defaultVal); void pci_iov_schema_add_uint8(nvlist_t *schema, const char *name, uint32_t flags, uint8_t defaultVal); void pci_iov_schema_add_uint16(nvlist_t *schema, const char *name, uint32_t flags, uint16_t defaultVal); void pci_iov_schema_add_uint32(nvlist_t *schema, const char *name, uint32_t flags, uint32_t defaultVal); void pci_iov_schema_add_uint64(nvlist_t *schema, const char *name, uint32_t flags, uint64_t defaultVal); void pci_iov_schema_add_unicast_mac(nvlist_t *schema, const char *name, uint32_t flags, const uint8_t * defaultVal); - +void pci_iov_schema_add_vlan(nvlist_t *schema, const char *name, + uint32_t flags, const uint16_t defaultVal); #endif diff --git a/usr.sbin/iovctl/iovctl.c b/usr.sbin/iovctl/iovctl.c index 413fb7b2f4a5..368fa51c594f 100644 --- a/usr.sbin/iovctl/iovctl.c +++ b/usr.sbin/iovctl/iovctl.c @@ -1,405 +1,411 @@ /*- * Copyright (c) 2013-2015 Sandvine Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iovctl.h" static void config_action(const char *filename, int dryrun); static void delete_action(const char *device, int dryrun); static void print_schema(const char *device); /* * Fetch the config schema from the kernel via ioctl. This function has to * call the ioctl twice: the first returns the amount of memory that we need * to allocate for the schema, and the second actually fetches the schema. */ static nvlist_t * get_schema(int fd) { struct pci_iov_schema arg; nvlist_t *schema; int error; /* Do the ioctl() once to fetch the size of the schema. */ arg.schema = NULL; arg.len = 0; arg.error = 0; error = ioctl(fd, IOV_GET_SCHEMA, &arg); if (error != 0) err(1, "Could not fetch size of config schema"); arg.schema = malloc(arg.len); if (arg.schema == NULL) err(1, "Could not allocate %zu bytes for schema", arg.len); /* Now do the ioctl() for real to get the schema. */ error = ioctl(fd, IOV_GET_SCHEMA, &arg); if (error != 0 || arg.error != 0) { if (arg.error != 0) errno = arg.error; err(1, "Could not fetch config schema"); } schema = nvlist_unpack(arg.schema, arg.len, NV_FLAG_IGNORE_CASE); if (schema == NULL) err(1, "Could not unpack schema"); free(arg.schema); return (schema); } /* * Call the ioctl that activates SR-IOV and creates the VFs. */ static void config_iov(int fd, const char *dev_name, const nvlist_t *config, int dryrun) { struct pci_iov_arg arg; int error; arg.config = nvlist_pack(config, &arg.len); if (arg.config == NULL) err(1, "Could not pack configuration"); if (dryrun) { printf("Would enable SR-IOV on device '%s'.\n", dev_name); printf( "The following configuration parameters would be used:\n"); nvlist_fdump(config, stdout); printf( "The configuration parameters consume %zu bytes when packed.\n", arg.len); } else { error = ioctl(fd, IOV_CONFIG, &arg); if (error != 0) err(1, "Failed to configure SR-IOV"); } free(arg.config); } static int open_device(const char *dev_name) { char *dev; int fd; size_t copied, size; long path_max; path_max = pathconf("/dev", _PC_PATH_MAX); if (path_max < 0) err(1, "Could not get maximum path length"); size = path_max; dev = malloc(size); if (dev == NULL) err(1, "Could not allocate memory for device path"); if (dev_name[0] == '/') copied = strlcpy(dev, dev_name, size); else copied = snprintf(dev, size, "/dev/iov/%s", dev_name); /* >= to account for null terminator. */ if (copied >= size) errx(1, "Provided file name too long"); fd = open(dev, O_RDWR); if (fd < 0) err(1, "Could not open device '%s'", dev); free(dev); return (fd); } static void usage(void) { warnx("Usage: iovctl -C -f [-n]"); warnx(" iovctl -D [-d | -f ] [-n]"); warnx(" iovctl -S [-d | -f ]"); exit(1); } enum main_action { NONE, CONFIG, DELETE, PRINT_SCHEMA, }; int main(int argc, char **argv) { char *device; const char *filename; int ch, dryrun; enum main_action action; device = NULL; filename = NULL; dryrun = 0; action = NONE; while ((ch = getopt(argc, argv, "Cd:Df:nS")) != -1) { switch (ch) { case 'C': if (action != NONE) { warnx( "Only one of -C, -D or -S may be specified"); usage(); } action = CONFIG; break; case 'd': device = strdup(optarg); break; case 'D': if (action != NONE) { warnx( "Only one of -C, -D or -S may be specified"); usage(); } action = DELETE; break; case 'f': filename = optarg; break; case 'n': dryrun = 1; break; case 'S': if (action != NONE) { warnx( "Only one of -C, -D or -S may be specified"); usage(); } action = PRINT_SCHEMA; break; case '?': warnx("Unrecognized argument '-%c'\n", optopt); usage(); break; } } if (device != NULL && filename != NULL) { warnx("Only one of the -d and -f flags may be specified"); usage(); } if (device == NULL && filename == NULL && action != CONFIG) { warnx("Either the -d or -f flag must be specified"); usage(); } switch (action) { case CONFIG: if (device != NULL) { warnx("-d flag cannot be used with the -C flag"); usage(); } if (filename == NULL) { warnx("The -f flag must be specified"); usage(); } config_action(filename, dryrun); break; case DELETE: if (device == NULL) device = find_device(filename); delete_action(device, dryrun); free(device); break; case PRINT_SCHEMA: if (dryrun) { warnx("-n flag cannot be used with the -S flag"); usage(); } if (device == NULL) device = find_device(filename); print_schema(device); free(device); break; default: usage(); break; } exit(0); } static void config_action(const char *filename, int dryrun) { char *dev; nvlist_t *schema, *config; int fd; dev = find_device(filename); fd = open(dev, O_RDWR); if (fd < 0) err(1, "Could not open device '%s'", dev); schema = get_schema(fd); config = parse_config_file(filename, schema); if (config == NULL) errx(1, "Could not parse config"); config_iov(fd, dev, config, dryrun); nvlist_destroy(config); nvlist_destroy(schema); free(dev); close(fd); } static void delete_action(const char *dev_name, int dryrun) { int fd, error; fd = open_device(dev_name); if (dryrun) printf("Would attempt to delete all VF children of '%s'\n", dev_name); else { error = ioctl(fd, IOV_DELETE); if (error != 0) err(1, "Failed to delete VFs"); } close(fd); } static void print_default_value(const nvlist_t *parameter, const char *type) { const uint8_t *mac; size_t size; if (strcasecmp(type, "bool") == 0) printf(" (default = %s)", nvlist_get_bool(parameter, DEFAULT_SCHEMA_NAME) ? "true" : "false"); else if (strcasecmp(type, "string") == 0) printf(" (default = %s)", nvlist_get_string(parameter, DEFAULT_SCHEMA_NAME)); else if (strcasecmp(type, "uint8_t") == 0) printf(" (default = %ju)", (uintmax_t)nvlist_get_number(parameter, DEFAULT_SCHEMA_NAME)); else if (strcasecmp(type, "uint16_t") == 0) printf(" (default = %ju)", (uintmax_t)nvlist_get_number(parameter, DEFAULT_SCHEMA_NAME)); else if (strcasecmp(type, "uint32_t") == 0) printf(" (default = %ju)", (uintmax_t)nvlist_get_number(parameter, DEFAULT_SCHEMA_NAME)); else if (strcasecmp(type, "uint64_t") == 0) printf(" (default = %ju)", (uintmax_t)nvlist_get_number(parameter, DEFAULT_SCHEMA_NAME)); else if (strcasecmp(type, "unicast-mac") == 0) { mac = nvlist_get_binary(parameter, DEFAULT_SCHEMA_NAME, &size); printf(" (default = %02x:%02x:%02x:%02x:%02x:%02x)", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + } else if (strcasecmp(type, "vlan") == 0) { + uint16_t vlan = nvlist_get_number(parameter, DEFAULT_SCHEMA_NAME); + if (vlan == VF_VLAN_TRUNK) + printf(" (default = trunk)"); + else + printf(" (default = %d)", vlan); } else errx(1, "Unexpected type in schema: '%s'", type); } static void print_subsystem_schema(const nvlist_t * subsystem_schema) { const char *name, *type; const nvlist_t *parameter; void *it; int nvtype; it = NULL; while ((name = nvlist_next(subsystem_schema, &nvtype, &it)) != NULL) { parameter = nvlist_get_nvlist(subsystem_schema, name); type = nvlist_get_string(parameter, TYPE_SCHEMA_NAME); printf("\t%s : %s", name, type); if (dnvlist_get_bool(parameter, REQUIRED_SCHEMA_NAME, false)) printf(" (required)"); else if (nvlist_exists(parameter, DEFAULT_SCHEMA_NAME)) print_default_value(parameter, type); else printf(" (optional)"); printf("\n"); } } static void print_schema(const char *dev_name) { nvlist_t *schema; const nvlist_t *iov_schema, *driver_schema, *pf_schema, *vf_schema; int fd; fd = open_device(dev_name); schema = get_schema(fd); pf_schema = nvlist_get_nvlist(schema, PF_CONFIG_NAME); iov_schema = nvlist_get_nvlist(pf_schema, IOV_CONFIG_NAME); driver_schema = nvlist_get_nvlist(pf_schema, DRIVER_CONFIG_NAME); printf( "The following configuration parameters may be configured on the PF:\n"); print_subsystem_schema(iov_schema); print_subsystem_schema(driver_schema); vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME); iov_schema = nvlist_get_nvlist(vf_schema, IOV_CONFIG_NAME); driver_schema = nvlist_get_nvlist(vf_schema, DRIVER_CONFIG_NAME); printf( "\nThe following configuration parameters may be configured on a VF:\n"); print_subsystem_schema(iov_schema); print_subsystem_schema(driver_schema); nvlist_destroy(schema); close(fd); } diff --git a/usr.sbin/iovctl/iovctl.conf.5 b/usr.sbin/iovctl/iovctl.conf.5 index a7eb6c2077ee..1bae96e150ba 100644 --- a/usr.sbin/iovctl/iovctl.conf.5 +++ b/usr.sbin/iovctl/iovctl.conf.5 @@ -1,169 +1,172 @@ .\" .\" Copyright (c) 2014 Sandvine Inc. .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 29, 2020 +.Dd May 30, 2024 .Dt IOVCTL.CONF 5 .Os .Sh NAME .Nm iovctl.conf .Nd IOVCTL configuration file .Sh DESCRIPTION The .Nm file is the configuration file for the .Xr iovctl 8 program. This file specifies configuration parameters for a single Physical Function .Pq PF device. To configure SR-IOV on multiple PF devices, use one configuration file for each PF. The locations of all .Xr iovctl 8 configuration files are specified in .Xr rc.conf 5 . .Pp The .Nm file uses UCL format. UCL syntax is documented at the official UCL website: .Lk http://github.com/vstakhov/libucl . .Pp There are three types of sections in the .Nm file. A section is a key at the top level of the file with a list as its value. The list may contain the keys specified in the .Sx OPTIONS section of this manual page. Individual PF driver implementations may specify additional device-specific configuration keys that they will accept. The order in which sections appear in .Nm is ignored. No two sections may have the same key. For example, two sections for VF-1 must not be defined. .Pp The first section type is the PF section. This section always has the key "PF"; therefore, only one such section may be defined. This section defines configuration parameters that apply to the PF as a whole. .Pp The second section type is the VF section. This section has the key "VF-" followed by a VF index. VF indices start at 0 and always increment by 1. Valid VF indices are in the range of 0 to .Pq num_vfs - 1 . The VF index must be given as a decimal integer with no leading zeros. This section defines configuration parameters that apply to a single VF. .Pp The third section type is the default section. This section always has the key "DEFAULT"; therefore, only one such section may be specified. This section defines default configuration parameters that apply to all VFs. All configuration keys that are valid to be applied to a VF are valid in this section. An individual VF section may override a default specified in this section by providing a different value for the configuration parameter. Note that the default section applies to ALL VFs. The default section must appear before any VF sections. The default section may appear before or after the PF section. .Pp The following option types are supported: .Bl -tag -width indent .It boolean Accepts a boolean value of true or false. +.It vlan +Accepts a VLAN ID, or "trunk" to allow any VLAN ID. .It mac-addr Accepts a unicast MAC address specified as a string of the form xx:xx:xx:xx:xx:xx, where xx is one or two hexadecimal digits. .It string Accepts any string value. .It uint8_t Accepts any integer in the range 0 to 255, inclusive. .It uint16_t Accepts any integer in the range 0 to 65535, inclusive. .It uint32_t Accepts any integer in the range 0 to .Pq 2**32 - 1 , inclusive. .It uint64_t Accepts any integer in the range 0 to .Pq 2**64 - 1 , inclusive. .El .Sh OPTIONS The following parameters are accepted by all PF drivers: .Bl -tag -width indent .It device Pq string This parameter specifies the name of the PF device. This parameter is required to be specified. .It num_vfs Pq uint16_t This parameter specifies the number of VF children to create. This parameter may not be zero. The maximum value of this parameter is device-specific. .El .Pp The following parameters are accepted by all VFs: .Bl -tag -width indent .It passthrough Pq boolean This parameter controls whether the VF is reserved for the use of the .Xr bhyve 8 hypervisor as a PCI passthrough device. If this parameter is set to true, then the VF will be reserved as a PCI passthrough device and it will not be accessible from the host OS. The default value of this parameter is false. .El .Pp See the PF driver manual page for configuration parameters specific to particular hardware. .Sh EXAMPLES This sample file will create 3 VFs as children of the ix0 device. VF-1 and VF-2 are set as .Xr bhyve 8 passthrough devices through the use of the default section. VF-0 is not configured as a passthrough device as it explicitly overrides the default. VF-0 also sets a device-specific parameter named mac-addr. .Bd -literal -offset ident PF { device : "ix0"; num_vfs : 3; } DEFAULT { passthrough : true; } VF-0 { mac-addr : "02:56:48:7e:d9:f7"; passthrough : false; + vlan: 1; } .Ed .Sh SEE ALSO .Xr rc.conf 5 , .Xr iovctl 8 .Sh AUTHORS This manual page was written by .An Ryan Stone Aq Mt rstone@FreeBSD.org . diff --git a/usr.sbin/iovctl/parse.c b/usr.sbin/iovctl/parse.c index a5810ba077eb..5114bd19e381 100644 --- a/usr.sbin/iovctl/parse.c +++ b/usr.sbin/iovctl/parse.c @@ -1,414 +1,439 @@ /*- * Copyright (c) 2014-2015 Sandvine Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iovctl.h" static void report_config_error(const char *key, const ucl_object_t *obj, const char *type) { errx(1, "Value '%s' of key '%s' is not of type %s", ucl_object_tostring(obj), key, type); } /* * Verifies that the value specified in the config file is a boolean value, and * then adds the value to the configuration. */ static void add_bool_config(const char *key, const ucl_object_t *obj, nvlist_t *config) { bool val; if (!ucl_object_toboolean_safe(obj, &val)) report_config_error(key, obj, "bool"); nvlist_add_bool(config, key, val); } /* * Verifies that the value specified in the config file is a string, and then * adds the value to the configuration. */ static void add_string_config(const char *key, const ucl_object_t *obj, nvlist_t *config) { const char *val; if (!ucl_object_tostring_safe(obj, &val)) report_config_error(key, obj, "string"); nvlist_add_string(config, key, val); } /* * Verifies that the value specified in the config file is a integer value * within the specified range, and then adds the value to the configuration. */ static void add_uint_config(const char *key, const ucl_object_t *obj, nvlist_t *config, const char *type, uint64_t max) { int64_t val; uint64_t uval; /* I must use a signed type here as libucl doesn't provide unsigned. */ if (!ucl_object_toint_safe(obj, &val)) report_config_error(key, obj, type); if (val < 0) report_config_error(key, obj, type); uval = val; if (uval > max) report_config_error(key, obj, type); nvlist_add_number(config, key, uval); } /* * Verifies that the value specified in the config file is a unicast MAC * address, and then adds the value to the configuration. */ static void add_unicast_mac_config(const char *key, const ucl_object_t *obj, nvlist_t *config) { uint8_t mac[ETHER_ADDR_LEN]; const char *val, *token; char *parse, *orig_parse, *tokpos, *endpos; size_t len; u_long value; int i; if (!ucl_object_tostring_safe(obj, &val)) report_config_error(key, obj, "unicast-mac"); parse = strdup(val); orig_parse = parse; i = 0; while ((token = strtok_r(parse, ":", &tokpos)) != NULL) { parse = NULL; len = strlen(token); if (len < 1 || len > 2) report_config_error(key, obj, "unicast-mac"); value = strtoul(token, &endpos, 16); if (*endpos != '\0') report_config_error(key, obj, "unicast-mac"); if (value > UINT8_MAX) report_config_error(key, obj, "unicast-mac"); if (i >= ETHER_ADDR_LEN) report_config_error(key, obj, "unicast-mac"); mac[i] = value; i++; } free(orig_parse); if (i != ETHER_ADDR_LEN) report_config_error(key, obj, "unicast-mac"); if (ETHER_IS_MULTICAST(mac)) errx(1, "Value '%s' of key '%s' is a multicast address", ucl_object_tostring(obj), key); nvlist_add_binary(config, key, mac, ETHER_ADDR_LEN); } +static void +add_vlan_config(const char *key, const ucl_object_t *obj, nvlist_t *config) +{ + int64_t val; + const char *strVal = ""; + + if(ucl_object_tostring_safe(obj, &strVal)) { + if (strcasecmp(strVal, "trunk") == 0) { + nvlist_add_number(config, key, VF_VLAN_TRUNK); + return; + } + report_config_error(key, obj, "vlan"); + } + + if (!ucl_object_toint_safe(obj, &val)) + report_config_error(key, obj, "vlan"); + + if (val < 0 || val > 4095) + report_config_error(key, obj, "vlan"); + + nvlist_add_number(config, key, val); +} + /* * Validates that the given configuation value has the right type as specified * in the schema, and then adds the value to the configuation node. */ static void add_config(const char *key, const ucl_object_t *obj, nvlist_t *config, const nvlist_t *schema) { const char *type; type = nvlist_get_string(schema, TYPE_SCHEMA_NAME); if (strcasecmp(type, "bool") == 0) add_bool_config(key, obj, config); else if (strcasecmp(type, "string") == 0) add_string_config(key, obj, config); else if (strcasecmp(type, "uint8_t") == 0) add_uint_config(key, obj, config, type, UINT8_MAX); else if (strcasecmp(type, "uint16_t") == 0) add_uint_config(key, obj, config, type, UINT16_MAX); else if (strcasecmp(type, "uint32_t") == 0) add_uint_config(key, obj, config, type, UINT32_MAX); else if (strcasecmp(type, "uint64_t") == 0) add_uint_config(key, obj, config, type, UINT64_MAX); else if (strcasecmp(type, "unicast-mac") == 0) add_unicast_mac_config(key, obj, config); + else if (strcasecmp(type, "vlan") == 0) + add_vlan_config(key, obj, config); else errx(1, "Unexpected type '%s' in schema", type); } /* * Parses all values specified in a device section in the configuration file, * validates that the key/value pair is valid in the schema, and then adds * the key/value pair to the correct subsystem in the config. */ static void parse_device_config(const ucl_object_t *top, nvlist_t *config, const char *subsystem, const nvlist_t *schema) { ucl_object_iter_t it; const ucl_object_t *obj; nvlist_t *subsystem_config, *driver_config, *iov_config; const nvlist_t *driver_schema, *iov_schema; const char *key; if (nvlist_exists(config, subsystem)) errx(1, "Multiple definitions of '%s' in config file", subsystem); driver_schema = nvlist_get_nvlist(schema, DRIVER_CONFIG_NAME); iov_schema = nvlist_get_nvlist(schema, IOV_CONFIG_NAME); driver_config = nvlist_create(NV_FLAG_IGNORE_CASE); if (driver_config == NULL) err(1, "Could not allocate config nvlist"); iov_config = nvlist_create(NV_FLAG_IGNORE_CASE); if (iov_config == NULL) err(1, "Could not allocate config nvlist"); subsystem_config = nvlist_create(NV_FLAG_IGNORE_CASE); if (subsystem_config == NULL) err(1, "Could not allocate config nvlist"); it = NULL; while ((obj = ucl_iterate_object(top, &it, true)) != NULL) { key = ucl_object_key(obj); if (nvlist_exists_nvlist(iov_schema, key)) add_config(key, obj, iov_config, nvlist_get_nvlist(iov_schema, key)); else if (nvlist_exists_nvlist(driver_schema, key)) add_config(key, obj, driver_config, nvlist_get_nvlist(driver_schema, key)); else errx(1, "%s: Invalid config key '%s'", subsystem, key); } nvlist_move_nvlist(subsystem_config, DRIVER_CONFIG_NAME, driver_config); nvlist_move_nvlist(subsystem_config, IOV_CONFIG_NAME, iov_config); nvlist_move_nvlist(config, subsystem, subsystem_config); } /* * Parses the specified config file using the given schema, and returns an * nvlist containing the configuration specified by the file. * * Exits with a message to stderr and an error if any config validation fails. */ nvlist_t * parse_config_file(const char *filename, const nvlist_t *schema) { ucl_object_iter_t it; struct ucl_parser *parser; ucl_object_t *top; const ucl_object_t *obj; nvlist_t *config; const nvlist_t *pf_schema, *vf_schema; const char *errmsg, *key; regex_t vf_pat; int regex_err, processed_vf; regex_err = regcomp(&vf_pat, "^"VF_PREFIX"([1-9][0-9]*|0)$", REG_EXTENDED | REG_ICASE); if (regex_err != 0) errx(1, "Could not compile VF regex"); parser = ucl_parser_new(0); if (parser == NULL) err(1, "Could not allocate parser"); if (!ucl_parser_add_file(parser, filename)) err(1, "Could not open '%s' for reading", filename); errmsg = ucl_parser_get_error(parser); if (errmsg != NULL) errx(1, "Could not parse '%s': %s", filename, errmsg); config = nvlist_create(NV_FLAG_IGNORE_CASE); if (config == NULL) err(1, "Could not allocate config nvlist"); pf_schema = nvlist_get_nvlist(schema, PF_CONFIG_NAME); vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME); processed_vf = 0; top = ucl_parser_get_object(parser); it = NULL; while ((obj = ucl_iterate_object(top, &it, true)) != NULL) { key = ucl_object_key(obj); if (strcasecmp(key, PF_CONFIG_NAME) == 0) parse_device_config(obj, config, key, pf_schema); else if (strcasecmp(key, DEFAULT_SCHEMA_NAME) == 0) { /* * Enforce that the default section must come before all * VF sections. This will hopefully prevent confusing * the user by having a default value apply to a VF * that was declared earlier in the file. * * This also gives us the flexibility to extend the file * format in the future to allow for multiple default * sections that do only apply to subsequent VF * sections. */ if (processed_vf) errx(1, "'default' section must precede all VF sections"); parse_device_config(obj, config, key, vf_schema); } else if (regexec(&vf_pat, key, 0, NULL, 0) == 0) { processed_vf = 1; parse_device_config(obj, config, key, vf_schema); } else errx(1, "Unexpected top-level node: %s", key); } validate_config(config, schema, &vf_pat); ucl_object_unref(top); ucl_parser_free(parser); regfree(&vf_pat); return (config); } /* * Parse the PF configuration section for and return the value specified for * the device parameter, or NULL if the device is not specified. */ static const char * find_pf_device(const ucl_object_t *pf) { ucl_object_iter_t it; const ucl_object_t *obj; const char *key, *device; it = NULL; while ((obj = ucl_iterate_object(pf, &it, true)) != NULL) { key = ucl_object_key(obj); if (strcasecmp(key, "device") == 0) { if (!ucl_object_tostring_safe(obj, &device)) err(1, "Config PF.device must be a string"); return (device); } } return (NULL); } /* * Manually parse the config file looking for the name of the PF device. We * have to do this separately because we need the config schema to call the * normal config file parsing code, and we need to know the name of the PF * device so that we can fetch the schema from it. * * This will always exit on failure, so if it returns then it is guaranteed to * have returned a valid device name. */ char * find_device(const char *filename) { char *device; const char *deviceName; ucl_object_iter_t it; struct ucl_parser *parser; ucl_object_t *top; const ucl_object_t *obj; const char *errmsg, *key; int error; device = NULL; deviceName = NULL; parser = ucl_parser_new(0); if (parser == NULL) err(1, "Could not allocate parser"); if (!ucl_parser_add_file(parser, filename)) err(1, "Could not open '%s' for reading", filename); errmsg = ucl_parser_get_error(parser); if (errmsg != NULL) errx(1, "Could not parse '%s': %s", filename, errmsg); top = ucl_parser_get_object (parser); it = NULL; while ((obj = ucl_iterate_object(top, &it, true)) != NULL) { key = ucl_object_key(obj); if (strcasecmp(key, PF_CONFIG_NAME) == 0) { deviceName = find_pf_device(obj); break; } } if (deviceName == NULL) errx(1, "Config file does not specify device"); error = asprintf(&device, "/dev/iov/%s", deviceName); if (error < 0) err(1, "Could not allocate memory for device"); ucl_object_unref(top); ucl_parser_free(parser); return (device); }