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/param.h>
28 #include <sys/iov.h>
29 #include <sys/dnv.h>
30 #include <sys/nv.h>
31
32 #include <err.h>
33 #include <regex.h>
34 #include <stdlib.h>
35
36 #include "iovctl.h"
37
38 /*
39 * Returns a writeable pointer to the configuration for the given device.
40 * If no configuration exists, a new nvlist with empty driver and iov
41 * sections is allocated and returned.
42 *
43 * Returning a writeable pointer requires removing the configuration from config
44 * using nvlist_take. It is the responsibility of the caller to re-insert the
45 * nvlist in config with nvlist_move_nvlist.
46 */
47 static nvlist_t *
find_config(nvlist_t * config,const char * device)48 find_config(nvlist_t *config, const char * device)
49 {
50 nvlist_t *subsystem, *empty_driver, *empty_iov;
51
52 subsystem = dnvlist_take_nvlist(config, device, NULL);
53
54 if (subsystem != NULL)
55 return (subsystem);
56
57 empty_driver = nvlist_create(NV_FLAG_IGNORE_CASE);
58 if (empty_driver == NULL)
59 err(1, "Could not allocate config nvlist");
60
61 empty_iov = nvlist_create(NV_FLAG_IGNORE_CASE);
62 if (empty_iov == NULL)
63 err(1, "Could not allocate config nvlist");
64
65 subsystem = nvlist_create(NV_FLAG_IGNORE_CASE);
66 if (subsystem == NULL)
67 err(1, "Could not allocate config nvlist");
68
69 nvlist_move_nvlist(subsystem, DRIVER_CONFIG_NAME, empty_driver);
70 nvlist_move_nvlist(subsystem, IOV_CONFIG_NAME, empty_iov);
71
72 return (subsystem);
73 }
74
75 static uint16_t
parse_vf_num(const char * key,regmatch_t * matches)76 parse_vf_num(const char *key, regmatch_t *matches)
77 {
78 u_long vf_num;
79
80 vf_num = strtoul(key + matches[1].rm_so, NULL, 10);
81
82 if (vf_num > UINT16_MAX)
83 errx(1, "VF number %lu is too large to be valid",
84 vf_num);
85
86 return (vf_num);
87 }
88
89 /*
90 * Apply the default values specified in device_defaults to the specified
91 * subsystem in the given device_config.
92 *
93 * This function assumes that the values specified in device_defaults have
94 * already been validated.
95 */
96 static void
apply_subsystem_defaults(nvlist_t * device_config,const char * subsystem,const nvlist_t * device_defaults)97 apply_subsystem_defaults(nvlist_t *device_config, const char *subsystem,
98 const nvlist_t *device_defaults)
99 {
100 nvlist_t *config;
101 const nvlist_t *defaults;
102 const char *name;
103 void *cookie;
104 size_t len;
105 const void *bin;
106 int type;
107
108 config = nvlist_take_nvlist(device_config, subsystem);
109 defaults = nvlist_get_nvlist(device_defaults, subsystem);
110
111 cookie = NULL;
112 while ((name = nvlist_next(defaults, &type, &cookie)) != NULL) {
113 if (nvlist_exists(config, name))
114 continue;
115
116 switch (type) {
117 case NV_TYPE_BOOL:
118 nvlist_add_bool(config, name,
119 nvlist_get_bool(defaults, name));
120 break;
121 case NV_TYPE_NUMBER:
122 nvlist_add_number(config, name,
123 nvlist_get_number(defaults, name));
124 break;
125 case NV_TYPE_STRING:
126 nvlist_add_string(config, name,
127 nvlist_get_string(defaults, name));
128 break;
129 case NV_TYPE_NVLIST:
130 nvlist_add_nvlist(config, name,
131 nvlist_get_nvlist(defaults, name));
132 break;
133 case NV_TYPE_BINARY:
134 bin = nvlist_get_binary(defaults, name, &len);
135 nvlist_add_binary(config, name, bin, len);
136 break;
137 default:
138 errx(1, "Unexpected type '%d'", type);
139 }
140 }
141 nvlist_move_nvlist(device_config, subsystem, config);
142 }
143
144 /*
145 * Iterate over every subsystem in the given VF device and apply default values
146 * for parameters that were not configured with a value.
147 *
148 * This function assumes that the values specified in defaults have already been
149 * validated.
150 */
151 static void
apply_defaults(nvlist_t * vf,const nvlist_t * defaults)152 apply_defaults(nvlist_t *vf, const nvlist_t *defaults)
153 {
154
155 apply_subsystem_defaults(vf, DRIVER_CONFIG_NAME, defaults);
156 apply_subsystem_defaults(vf, IOV_CONFIG_NAME, defaults);
157 }
158
159 /*
160 * Validate that all required parameters have been configured in the specified
161 * subsystem.
162 */
163 static void
validate_subsystem(const nvlist_t * device,const nvlist_t * device_schema,const char * subsystem_name,const char * config_name)164 validate_subsystem(const nvlist_t *device, const nvlist_t *device_schema,
165 const char *subsystem_name, const char *config_name)
166 {
167 const nvlist_t *subsystem, *schema, *config;
168 const char *name;
169 void *cookie;
170 int type;
171
172 subsystem = nvlist_get_nvlist(device, subsystem_name);
173 schema = nvlist_get_nvlist(device_schema, subsystem_name);
174
175 cookie = NULL;
176 while ((name = nvlist_next(schema, &type, &cookie)) != NULL) {
177 config = nvlist_get_nvlist(schema, name);
178
179 if (dnvlist_get_bool(config, REQUIRED_SCHEMA_NAME, false)) {
180 if (!nvlist_exists(subsystem, name))
181 errx(1,
182 "Required parameter '%s' not found in '%s'",
183 name, config_name);
184 }
185 }
186 }
187
188 /*
189 * Validate that all required parameters have been configured in all subsystems
190 * in the device.
191 */
192 static void
validate_device(const nvlist_t * device,const nvlist_t * schema,const char * config_name)193 validate_device(const nvlist_t *device, const nvlist_t *schema,
194 const char *config_name)
195 {
196
197 validate_subsystem(device, schema, DRIVER_CONFIG_NAME, config_name);
198 validate_subsystem(device, schema, IOV_CONFIG_NAME, config_name);
199 }
200
201 static uint16_t
get_num_vfs(const nvlist_t * pf)202 get_num_vfs(const nvlist_t *pf)
203 {
204 const nvlist_t *iov;
205
206 iov = nvlist_get_nvlist(pf, IOV_CONFIG_NAME);
207 return (nvlist_get_number(iov, "num_vfs"));
208 }
209
210 /*
211 * Validates the configuration that has been parsed into config using the given
212 * config schema. Note that the parser is required to not insert configuration
213 * keys that are not valid in the schema, and to not insert configuration values
214 * that are of the incorrect type. Therefore this function will not validate
215 * either condition. This function is only responsible for inserting config
216 * file defaults in individual VF sections and removing the DEFAULT_SCHEMA_NAME
217 * subsystem from config, validating that all required parameters in the schema
218 * are present in each PF and VF subsystem, and that there is no VF subsystem
219 * section whose number exceeds num_vfs.
220 */
221 void
validate_config(nvlist_t * config,const nvlist_t * schema,const regex_t * vf_pat)222 validate_config(nvlist_t *config, const nvlist_t *schema, const regex_t *vf_pat)
223 {
224 char device_name[VF_MAX_NAME];
225 regmatch_t matches[2];
226 nvlist_t *defaults, *pf, *vf;
227 const nvlist_t *vf_schema;
228 const char *key;
229 void *cookie;
230 int i, type;
231 uint16_t vf_num, num_vfs;
232
233 pf = find_config(config, PF_CONFIG_NAME);
234 validate_device(pf, nvlist_get_nvlist(schema, PF_CONFIG_NAME),
235 PF_CONFIG_NAME);
236 nvlist_move_nvlist(config, PF_CONFIG_NAME, pf);
237
238 num_vfs = get_num_vfs(pf);
239 vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME);
240
241 if (num_vfs == 0)
242 errx(1, "PF.num_vfs must be at least 1");
243
244 defaults = dnvlist_take_nvlist(config, DEFAULT_SCHEMA_NAME, NULL);
245
246 for (i = 0; i < num_vfs; i++) {
247 snprintf(device_name, sizeof(device_name), VF_PREFIX"%d",
248 i);
249
250 vf = find_config(config, device_name);
251
252 if (defaults != NULL)
253 apply_defaults(vf, defaults);
254
255 validate_device(vf, vf_schema, device_name);
256 nvlist_move_nvlist(config, device_name, vf);
257 }
258 nvlist_destroy(defaults);
259
260 cookie = NULL;
261 while ((key = nvlist_next(config, &type, &cookie)) != NULL) {
262 if (regexec(vf_pat, key, nitems(matches), matches, 0) == 0) {
263 vf_num = parse_vf_num(key, matches);
264 if (vf_num >= num_vfs)
265 errx(1,
266 "VF number %d is out of bounds (num_vfs=%d)",
267 vf_num, num_vfs);
268 }
269 }
270 }
271
272