1 /*- 2 * Copyright (c) 2014-2015 Sandvine Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 #include <sys/param.h> 29 #include <sys/iov.h> 30 #include <sys/dnv.h> 31 #include <sys/nv.h> 32 33 #include <err.h> 34 #include <regex.h> 35 #include <stdlib.h> 36 37 #include "iovctl.h" 38 39 /* 40 * Returns a writeable pointer to the configuration for the given device. 41 * If no configuration exists, a new nvlist with empty driver and iov 42 * sections is allocated and returned. 43 * 44 * Returning a writeable pointer requires removing the configuration from config 45 * using nvlist_take. It is the responsibility of the caller to re-insert the 46 * nvlist in config with nvlist_move_nvlist. 47 */ 48 static nvlist_t * 49 find_config(nvlist_t *config, const char * device) 50 { 51 nvlist_t *subsystem, *empty_driver, *empty_iov; 52 53 subsystem = dnvlist_take_nvlist(config, device, NULL); 54 55 if (subsystem != NULL) 56 return (subsystem); 57 58 empty_driver = nvlist_create(NV_FLAG_IGNORE_CASE); 59 if (empty_driver == NULL) 60 err(1, "Could not allocate config nvlist"); 61 62 empty_iov = nvlist_create(NV_FLAG_IGNORE_CASE); 63 if (empty_iov == NULL) 64 err(1, "Could not allocate config nvlist"); 65 66 subsystem = nvlist_create(NV_FLAG_IGNORE_CASE); 67 if (subsystem == NULL) 68 err(1, "Could not allocate config nvlist"); 69 70 nvlist_move_nvlist(subsystem, DRIVER_CONFIG_NAME, empty_driver); 71 nvlist_move_nvlist(subsystem, IOV_CONFIG_NAME, empty_iov); 72 73 return (subsystem); 74 } 75 76 static uint16_t 77 parse_vf_num(const char *key, regmatch_t *matches) 78 { 79 u_long vf_num; 80 81 vf_num = strtoul(key + matches[1].rm_so, NULL, 10); 82 83 if (vf_num > UINT16_MAX) 84 errx(1, "VF number %lu is too large to be valid", 85 vf_num); 86 87 return (vf_num); 88 } 89 90 /* 91 * Apply the default values specified in device_defaults to the specified 92 * subsystem in the given device_config. 93 * 94 * This function assumes that the values specified in device_defaults have 95 * already been validated. 96 */ 97 static void 98 apply_subsystem_defaults(nvlist_t *device_config, const char *subsystem, 99 const nvlist_t *device_defaults) 100 { 101 nvlist_t *config; 102 const nvlist_t *defaults; 103 const char *name; 104 void *cookie; 105 size_t len; 106 const void *bin; 107 int type; 108 109 config = nvlist_take_nvlist(device_config, subsystem); 110 defaults = nvlist_get_nvlist(device_defaults, subsystem); 111 112 cookie = NULL; 113 while ((name = nvlist_next(defaults, &type, &cookie)) != NULL) { 114 if (nvlist_exists(config, name)) 115 continue; 116 117 switch (type) { 118 case NV_TYPE_BOOL: 119 nvlist_add_bool(config, name, 120 nvlist_get_bool(defaults, name)); 121 break; 122 case NV_TYPE_NUMBER: 123 nvlist_add_number(config, name, 124 nvlist_get_number(defaults, name)); 125 break; 126 case NV_TYPE_STRING: 127 nvlist_add_string(config, name, 128 nvlist_get_string(defaults, name)); 129 break; 130 case NV_TYPE_NVLIST: 131 nvlist_add_nvlist(config, name, 132 nvlist_get_nvlist(defaults, name)); 133 break; 134 case NV_TYPE_BINARY: 135 bin = nvlist_get_binary(defaults, name, &len); 136 nvlist_add_binary(config, name, bin, len); 137 break; 138 default: 139 errx(1, "Unexpected type '%d'", type); 140 } 141 } 142 nvlist_move_nvlist(device_config, subsystem, config); 143 } 144 145 /* 146 * Iterate over every subsystem in the given VF device and apply default values 147 * for parameters that were not configured with a value. 148 * 149 * This function assumes that the values specified in defaults have already been 150 * validated. 151 */ 152 static void 153 apply_defaults(nvlist_t *vf, const nvlist_t *defaults) 154 { 155 156 apply_subsystem_defaults(vf, DRIVER_CONFIG_NAME, defaults); 157 apply_subsystem_defaults(vf, IOV_CONFIG_NAME, defaults); 158 } 159 160 /* 161 * Validate that all required parameters have been configured in the specified 162 * subsystem. 163 */ 164 static void 165 validate_subsystem(const nvlist_t *device, const nvlist_t *device_schema, 166 const char *subsystem_name, const char *config_name) 167 { 168 const nvlist_t *subsystem, *schema, *config; 169 const char *name; 170 void *cookie; 171 int type; 172 173 subsystem = nvlist_get_nvlist(device, subsystem_name); 174 schema = nvlist_get_nvlist(device_schema, subsystem_name); 175 176 cookie = NULL; 177 while ((name = nvlist_next(schema, &type, &cookie)) != NULL) { 178 config = nvlist_get_nvlist(schema, name); 179 180 if (dnvlist_get_bool(config, REQUIRED_SCHEMA_NAME, false)) { 181 if (!nvlist_exists(subsystem, name)) 182 errx(1, 183 "Required parameter '%s' not found in '%s'", 184 name, config_name); 185 } 186 } 187 } 188 189 /* 190 * Validate that all required parameters have been configured in all subsystems 191 * in the device. 192 */ 193 static void 194 validate_device(const nvlist_t *device, const nvlist_t *schema, 195 const char *config_name) 196 { 197 198 validate_subsystem(device, schema, DRIVER_CONFIG_NAME, config_name); 199 validate_subsystem(device, schema, IOV_CONFIG_NAME, config_name); 200 } 201 202 static uint16_t 203 get_num_vfs(const nvlist_t *pf) 204 { 205 const nvlist_t *iov; 206 207 iov = nvlist_get_nvlist(pf, IOV_CONFIG_NAME); 208 return (nvlist_get_number(iov, "num_vfs")); 209 } 210 211 /* 212 * Validates the configuration that has been parsed into config using the given 213 * config schema. Note that the parser is required to not insert configuration 214 * keys that are not valid in the schema, and to not insert configuration values 215 * that are of the incorrect type. Therefore this function will not validate 216 * either condition. This function is only responsible for inserting config 217 * file defaults in individual VF sections and removing the DEFAULT_SCHEMA_NAME 218 * subsystem from config, validating that all required parameters in the schema 219 * are present in each PF and VF subsystem, and that there is no VF subsystem 220 * section whose number exceeds num_vfs. 221 */ 222 void 223 validate_config(nvlist_t *config, const nvlist_t *schema, const regex_t *vf_pat) 224 { 225 char device_name[VF_MAX_NAME]; 226 regmatch_t matches[2]; 227 nvlist_t *defaults, *pf, *vf; 228 const nvlist_t *vf_schema; 229 const char *key; 230 void *cookie; 231 int i, type; 232 uint16_t vf_num, num_vfs; 233 234 pf = find_config(config, PF_CONFIG_NAME); 235 validate_device(pf, nvlist_get_nvlist(schema, PF_CONFIG_NAME), 236 PF_CONFIG_NAME); 237 nvlist_move_nvlist(config, PF_CONFIG_NAME, pf); 238 239 num_vfs = get_num_vfs(pf); 240 vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME); 241 242 if (num_vfs == 0) 243 errx(1, "PF.num_vfs must be at least 1"); 244 245 defaults = dnvlist_take_nvlist(config, DEFAULT_SCHEMA_NAME, NULL); 246 247 for (i = 0; i < num_vfs; i++) { 248 snprintf(device_name, sizeof(device_name), VF_PREFIX"%d", 249 i); 250 251 vf = find_config(config, device_name); 252 253 if (defaults != NULL) 254 apply_defaults(vf, defaults); 255 256 validate_device(vf, vf_schema, device_name); 257 nvlist_move_nvlist(config, device_name, vf); 258 } 259 nvlist_destroy(defaults); 260 261 cookie = NULL; 262 while ((key = nvlist_next(config, &type, &cookie)) != NULL) { 263 if (regexec(vf_pat, key, nitems(matches), matches, 0) == 0) { 264 vf_num = parse_vf_num(key, matches); 265 if (vf_num >= num_vfs) 266 errx(1, 267 "VF number %d is out of bounds (num_vfs=%d)", 268 vf_num, num_vfs); 269 } 270 } 271 } 272 273