xref: /NextBSD/contrib/ofed/management/opensm/opensm/osm_qos.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /*
2  * Copyright (c) 2006-2008 Voltaire, Inc. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  *
32  */
33 
34 /*
35  * Abstract:
36  *    Implementation of OpenSM QoS infrastructure primitives
37  */
38 
39 #if HAVE_CONFIG_H
40 #  include <config.h>
41 #endif				/* HAVE_CONFIG_H */
42 
43 #include <stdlib.h>
44 #include <string.h>
45 
46 #include <iba/ib_types.h>
47 #include <complib/cl_qmap.h>
48 #include <complib/cl_debug.h>
49 #include <opensm/osm_opensm.h>
50 #include <opensm/osm_subnet.h>
51 #include <opensm/osm_qos_policy.h>
52 
53 struct qos_config {
54 	uint8_t max_vls;
55 	uint8_t vl_high_limit;
56 	ib_vl_arb_table_t vlarb_high[2];
57 	ib_vl_arb_table_t vlarb_low[2];
58 	ib_slvl_table_t sl2vl;
59 };
60 
61 static void qos_build_config(struct qos_config *cfg,
62 			     osm_qos_options_t * opt, osm_qos_options_t * dflt);
63 
64 /*
65  * QoS primitives
66  */
vlarb_update_table_block(osm_sm_t * sm,osm_physp_t * p,uint8_t port_num,unsigned force_update,const ib_vl_arb_table_t * table_block,unsigned block_length,unsigned block_num)67 static ib_api_status_t vlarb_update_table_block(osm_sm_t * sm,
68 						osm_physp_t * p,
69 						uint8_t port_num,
70 						unsigned force_update,
71 						const ib_vl_arb_table_t *
72 						table_block,
73 						unsigned block_length,
74 						unsigned block_num)
75 {
76 	ib_vl_arb_table_t block;
77 	osm_madw_context_t context;
78 	uint32_t attr_mod;
79 	unsigned vl_mask, i;
80 
81 	vl_mask = (1 << (ib_port_info_get_op_vls(&p->port_info) - 1)) - 1;
82 
83 	memset(&block, 0, sizeof(block));
84 	memcpy(&block, table_block, block_length * sizeof(block.vl_entry[0]));
85 	for (i = 0; i < block_length; i++)
86 		block.vl_entry[i].vl &= vl_mask;
87 
88 	if (!force_update &&
89 	    !memcmp(&p->vl_arb[block_num], &block,
90 		    block_length * sizeof(block.vl_entry[0])))
91 		return IB_SUCCESS;
92 
93 	context.vla_context.node_guid =
94 	    osm_node_get_node_guid(osm_physp_get_node_ptr(p));
95 	context.vla_context.port_guid = osm_physp_get_port_guid(p);
96 	context.vla_context.set_method = TRUE;
97 	attr_mod = ((block_num + 1) << 16) | port_num;
98 
99 	return osm_req_set(sm, osm_physp_get_dr_path_ptr(p),
100 			   (uint8_t *) & block, sizeof(block),
101 			   IB_MAD_ATTR_VL_ARBITRATION,
102 			   cl_hton32(attr_mod), CL_DISP_MSGID_NONE, &context);
103 }
104 
vlarb_update(osm_sm_t * sm,osm_physp_t * p,uint8_t port_num,unsigned force_update,const struct qos_config * qcfg)105 static ib_api_status_t vlarb_update(osm_sm_t * sm,
106 				    osm_physp_t * p, uint8_t port_num,
107 				    unsigned force_update,
108 				    const struct qos_config *qcfg)
109 {
110 	ib_api_status_t status = IB_SUCCESS;
111 	ib_port_info_t *p_pi = &p->port_info;
112 	unsigned len;
113 
114 	if (p_pi->vl_arb_low_cap > 0) {
115 		len = p_pi->vl_arb_low_cap < IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK ?
116 		    p_pi->vl_arb_low_cap : IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK;
117 		if ((status = vlarb_update_table_block(sm, p, port_num,
118 						       force_update,
119 						       &qcfg->vlarb_low[0],
120 						       len, 0)) != IB_SUCCESS)
121 			return status;
122 	}
123 	if (p_pi->vl_arb_low_cap > IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK) {
124 		len = p_pi->vl_arb_low_cap % IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK;
125 		if ((status = vlarb_update_table_block(sm, p, port_num,
126 						       force_update,
127 						       &qcfg->vlarb_low[1],
128 						       len, 1)) != IB_SUCCESS)
129 			return status;
130 	}
131 	if (p_pi->vl_arb_high_cap > 0) {
132 		len = p_pi->vl_arb_high_cap < IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK ?
133 		    p_pi->vl_arb_high_cap : IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK;
134 		if ((status = vlarb_update_table_block(sm, p, port_num,
135 						       force_update,
136 						       &qcfg->vlarb_high[0],
137 						       len, 2)) != IB_SUCCESS)
138 			return status;
139 	}
140 	if (p_pi->vl_arb_high_cap > IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK) {
141 		len = p_pi->vl_arb_high_cap % IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK;
142 		if ((status = vlarb_update_table_block(sm, p, port_num,
143 						       force_update,
144 						       &qcfg->vlarb_high[1],
145 						       len, 3)) != IB_SUCCESS)
146 			return status;
147 	}
148 
149 	return status;
150 }
151 
sl2vl_update_table(osm_sm_t * sm,osm_physp_t * p,uint8_t in_port,uint8_t out_port,unsigned force_update,const ib_slvl_table_t * sl2vl_table)152 static ib_api_status_t sl2vl_update_table(osm_sm_t * sm,
153 					  osm_physp_t * p, uint8_t in_port,
154 					  uint8_t out_port,
155 					  unsigned force_update,
156 					  const ib_slvl_table_t * sl2vl_table)
157 {
158 	osm_madw_context_t context;
159 	ib_slvl_table_t tbl, *p_tbl;
160 	osm_node_t *p_node = osm_physp_get_node_ptr(p);
161 	uint32_t attr_mod;
162 	unsigned vl_mask;
163 	uint8_t vl1, vl2;
164 	int i;
165 
166 	vl_mask = (1 << (ib_port_info_get_op_vls(&p->port_info) - 1)) - 1;
167 
168 	for (i = 0; i < IB_MAX_NUM_VLS / 2; i++) {
169 		vl1 = sl2vl_table->raw_vl_by_sl[i] >> 4;
170 		vl2 = sl2vl_table->raw_vl_by_sl[i] & 0xf;
171 		if (vl1 != 15)
172 			vl1 &= vl_mask;
173 		if (vl2 != 15)
174 			vl2 &= vl_mask;
175 		tbl.raw_vl_by_sl[i] = (vl1 << 4) | vl2;
176 	}
177 
178 	if (!force_update && (p_tbl = osm_physp_get_slvl_tbl(p, in_port)) &&
179 	    !memcmp(p_tbl, &tbl, sizeof(tbl)))
180 		return IB_SUCCESS;
181 
182 	context.slvl_context.node_guid = osm_node_get_node_guid(p_node);
183 	context.slvl_context.port_guid = osm_physp_get_port_guid(p);
184 	context.slvl_context.set_method = TRUE;
185 	attr_mod = in_port << 8 | out_port;
186 	return osm_req_set(sm, osm_physp_get_dr_path_ptr(p),
187 			   (uint8_t *) & tbl, sizeof(tbl),
188 			   IB_MAD_ATTR_SLVL_TABLE,
189 			   cl_hton32(attr_mod), CL_DISP_MSGID_NONE, &context);
190 }
191 
sl2vl_update(osm_sm_t * sm,osm_port_t * p_port,osm_physp_t * p,uint8_t port_num,unsigned force_update,const struct qos_config * qcfg)192 static ib_api_status_t sl2vl_update(osm_sm_t * sm, osm_port_t * p_port,
193 				    osm_physp_t * p, uint8_t port_num,
194 				    unsigned force_update,
195 				    const struct qos_config *qcfg)
196 {
197 	ib_api_status_t status;
198 	uint8_t i, num_ports;
199 	osm_physp_t *p_physp;
200 
201 	if (osm_node_get_type(osm_physp_get_node_ptr(p)) == IB_NODE_TYPE_SWITCH) {
202 		if (ib_port_info_get_vl_cap(&p->port_info) == 1) {
203 			/* Check port 0's capability mask */
204 			p_physp = p_port->p_physp;
205 			if (!
206 			    (p_physp->port_info.
207 			     capability_mask & IB_PORT_CAP_HAS_SL_MAP))
208 				return IB_SUCCESS;
209 		}
210 		num_ports = osm_node_get_num_physp(osm_physp_get_node_ptr(p));
211 	} else {
212 		if (!(p->port_info.capability_mask & IB_PORT_CAP_HAS_SL_MAP))
213 			return IB_SUCCESS;
214 		num_ports = 1;
215 	}
216 
217 	for (i = 0; i < num_ports; i++) {
218 		status =
219 		    sl2vl_update_table(sm, p, i, port_num,
220 				       force_update, &qcfg->sl2vl);
221 		if (status != IB_SUCCESS)
222 			return status;
223 	}
224 
225 	return IB_SUCCESS;
226 }
227 
qos_physp_setup(osm_log_t * p_log,osm_sm_t * sm,osm_port_t * p_port,osm_physp_t * p,uint8_t port_num,unsigned force_update,const struct qos_config * qcfg)228 static ib_api_status_t qos_physp_setup(osm_log_t * p_log, osm_sm_t * sm,
229 				       osm_port_t * p_port, osm_physp_t * p,
230 				       uint8_t port_num,
231 				       unsigned force_update,
232 				       const struct qos_config *qcfg)
233 {
234 	ib_api_status_t status;
235 
236 	/* OpVLs should be ok at this moment - just use it */
237 
238 	/* setup VL high limit on the physp later to be updated by link mgr */
239 	p->vl_high_limit = qcfg->vl_high_limit;
240 
241 	/* setup VLArbitration */
242 	status = vlarb_update(sm, p, port_num, force_update, qcfg);
243 	if (status != IB_SUCCESS) {
244 		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6202 : "
245 			"failed to update VLArbitration tables "
246 			"for port %" PRIx64 " #%d\n",
247 			cl_ntoh64(p->port_guid), port_num);
248 		return status;
249 	}
250 
251 	/* setup SL2VL tables */
252 	status = sl2vl_update(sm, p_port, p, port_num, force_update, qcfg);
253 	if (status != IB_SUCCESS) {
254 		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6203 : "
255 			"failed to update SL2VLMapping tables "
256 			"for port %" PRIx64 " #%d\n",
257 			cl_ntoh64(p->port_guid), port_num);
258 		return status;
259 	}
260 
261 	return IB_SUCCESS;
262 }
263 
osm_qos_setup(osm_opensm_t * p_osm)264 osm_signal_t osm_qos_setup(osm_opensm_t * p_osm)
265 {
266 	struct qos_config ca_config, sw0_config, swe_config, rtr_config;
267 	struct qos_config *cfg;
268 	cl_qmap_t *p_tbl;
269 	cl_map_item_t *p_next;
270 	osm_port_t *p_port;
271 	uint32_t num_physp;
272 	osm_physp_t *p_physp;
273 	osm_node_t *p_node;
274 	ib_api_status_t status;
275 	unsigned force_update;
276 	uint8_t i;
277 
278 	if (!p_osm->subn.opt.qos)
279 		return OSM_SIGNAL_DONE;
280 
281 	OSM_LOG_ENTER(&p_osm->log);
282 
283 	qos_build_config(&ca_config, &p_osm->subn.opt.qos_ca_options,
284 			 &p_osm->subn.opt.qos_options);
285 	qos_build_config(&sw0_config, &p_osm->subn.opt.qos_sw0_options,
286 			 &p_osm->subn.opt.qos_options);
287 	qos_build_config(&swe_config, &p_osm->subn.opt.qos_swe_options,
288 			 &p_osm->subn.opt.qos_options);
289 	qos_build_config(&rtr_config, &p_osm->subn.opt.qos_rtr_options,
290 			 &p_osm->subn.opt.qos_options);
291 
292 	cl_plock_excl_acquire(&p_osm->lock);
293 
294 	/* read QoS policy config file */
295 	osm_qos_parse_policy_file(&p_osm->subn);
296 
297 	p_tbl = &p_osm->subn.port_guid_tbl;
298 	p_next = cl_qmap_head(p_tbl);
299 	while (p_next != cl_qmap_end(p_tbl)) {
300 		p_port = (osm_port_t *) p_next;
301 		p_next = cl_qmap_next(p_next);
302 
303 		p_node = p_port->p_node;
304 		if (p_node->sw) {
305 			num_physp = osm_node_get_num_physp(p_node);
306 			for (i = 1; i < num_physp; i++) {
307 				p_physp = osm_node_get_physp_ptr(p_node, i);
308 				if (!p_physp)
309 					continue;
310 				force_update = p_physp->need_update ||
311 				    p_osm->subn.need_update;
312 				status =
313 				    qos_physp_setup(&p_osm->log, &p_osm->sm,
314 						    p_port, p_physp, i,
315 						    force_update, &swe_config);
316 			}
317 			/* skip base port 0 */
318 			if (!ib_switch_info_is_enhanced_port0
319 			    (&p_node->sw->switch_info))
320 				continue;
321 
322 			cfg = &sw0_config;
323 		} else if (osm_node_get_type(p_node) == IB_NODE_TYPE_ROUTER)
324 			cfg = &rtr_config;
325 		else
326 			cfg = &ca_config;
327 
328 		p_physp = p_port->p_physp;
329 		if (!p_physp)
330 			continue;
331 
332 		force_update = p_physp->need_update || p_osm->subn.need_update;
333 		status = qos_physp_setup(&p_osm->log, &p_osm->sm,
334 					 p_port, p_physp, 0, force_update, cfg);
335 	}
336 
337 	cl_plock_release(&p_osm->lock);
338 	OSM_LOG_EXIT(&p_osm->log);
339 
340 	return OSM_SIGNAL_DONE;
341 }
342 
343 /*
344  *  QoS config stuff
345  */
parse_one_unsigned(char * str,char delim,unsigned * val)346 static int parse_one_unsigned(char *str, char delim, unsigned *val)
347 {
348 	char *end;
349 	*val = strtoul(str, &end, 0);
350 	if (*end)
351 		end++;
352 	return (int)(end - str);
353 }
354 
parse_vlarb_entry(char * str,ib_vl_arb_element_t * e)355 static int parse_vlarb_entry(char *str, ib_vl_arb_element_t * e)
356 {
357 	unsigned val;
358 	char *p = str;
359 	p += parse_one_unsigned(p, ':', &val);
360 	e->vl = val % 15;
361 	p += parse_one_unsigned(p, ',', &val);
362 	e->weight = (uint8_t) val;
363 	return (int)(p - str);
364 }
365 
parse_sl2vl_entry(char * str,uint8_t * raw)366 static int parse_sl2vl_entry(char *str, uint8_t * raw)
367 {
368 	unsigned val1, val2;
369 	char *p = str;
370 	p += parse_one_unsigned(p, ',', &val1);
371 	p += parse_one_unsigned(p, ',', &val2);
372 	*raw = (val1 << 4) | (val2 & 0xf);
373 	return (int)(p - str);
374 }
375 
qos_build_config(struct qos_config * cfg,osm_qos_options_t * opt,osm_qos_options_t * dflt)376 static void qos_build_config(struct qos_config *cfg,
377 			     osm_qos_options_t * opt, osm_qos_options_t * dflt)
378 {
379 	int i;
380 	char *p;
381 
382 	memset(cfg, 0, sizeof(*cfg));
383 
384 	cfg->max_vls = opt->max_vls > 0 ? opt->max_vls : dflt->max_vls;
385 
386 	if (opt->high_limit >= 0)
387 		cfg->vl_high_limit = (uint8_t) opt->high_limit;
388 	else
389 		cfg->vl_high_limit = (uint8_t) dflt->high_limit;
390 
391 	p = opt->vlarb_high ? opt->vlarb_high : dflt->vlarb_high;
392 	for (i = 0; i < 2 * IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK; i++) {
393 		p += parse_vlarb_entry(p,
394 				       &cfg->vlarb_high[i /
395 							IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK].
396 				       vl_entry[i %
397 						IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK]);
398 	}
399 
400 	p = opt->vlarb_low ? opt->vlarb_low : dflt->vlarb_low;
401 	for (i = 0; i < 2 * IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK; i++) {
402 		p += parse_vlarb_entry(p,
403 				       &cfg->vlarb_low[i /
404 						       IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK].
405 				       vl_entry[i %
406 						IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK]);
407 	}
408 
409 	p = opt->sl2vl ? opt->sl2vl : dflt->sl2vl;
410 	for (i = 0; i < IB_MAX_NUM_VLS / 2; i++)
411 		p += parse_sl2vl_entry(p, &cfg->sl2vl.raw_vl_by_sl[i]);
412 
413 }
414