Index: head/sys/dev/pci/pci_iov_schema.c =================================================================== --- head/sys/dev/pci/pci_iov_schema.c (revision 279451) +++ head/sys/dev/pci/pci_iov_schema.c (revision 279452) @@ -1,208 +1,608 @@ /*- * Copyright (c) 2014-2015 Sandvine Inc. All rights reserved. * 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 __FBSDID("$FreeBSD$"); #include #include +#include #include #include +#include #include #include #include #include +#include #include #include +#include + #include -static const char *pci_iov_schema_valid_types[] = { - "bool", - "string", - "uint8_t", - "uint16_t", - "uint32_t", - "uint64_t", - "unicast-mac", +struct config_type_validator; +typedef int (validate_func)(const struct config_type_validator *, + const nvlist_t *, const char *name); + +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; + +struct config_type_validator { + const char *type_name; + validate_func *validate; + uintmax_t limit; }; -static void -pci_iov_schema_add_type(nvlist_t *entry, const char *type) +static struct config_type_validator pci_iov_schema_validators[] = { + { "bool", pci_iov_schema_validate_bool }, + { "string", pci_iov_schema_validate_string }, + { "uint8_t", pci_iov_schema_validate_uint, UINT8_MAX }, + { "uint16_t", pci_iov_schema_validate_uint, UINT16_MAX }, + { "uint32_t", pci_iov_schema_validate_uint, UINT32_MAX }, + { "uint64_t", pci_iov_schema_validate_uint, UINT64_MAX }, + { "unicast-mac", pci_iov_schema_validate_unicast_mac }, +}; + +static const struct config_type_validator * +pci_iov_schema_find_validator(const char *type) { - int i, error; + struct config_type_validator *validator; + int i; - error = EINVAL; - for (i = 0; i < nitems(pci_iov_schema_valid_types); i++) { - if (strcmp(type, pci_iov_schema_valid_types[i]) == 0) { - error = 0; - break; - } + 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); } - if (error != 0) { - nvlist_set_error(entry, error); + 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); +} + +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 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"); +} + +/* + * 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)); -} \ No newline at end of file +} Index: head/sys/sys/iov.h =================================================================== --- head/sys/sys/iov.h (revision 279451) +++ head/sys/sys/iov.h (revision 279452) @@ -1,171 +1,176 @@ /*- * Copyright (c) 2013-2015 Sandvine Inc. All rights reserved. * 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. * * $FreeBSD$ */ #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" struct pci_iov_arg { int num_vfs; int passthrough; }; /* * 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; }; #define IOV_CONFIG _IOWR('p', 10, struct pci_iov_arg) #define IOV_DELETE _IO('p', 11) #define IOV_GET_SCHEMA _IOWR('p', 12, struct pci_iov_schema) #endif