1/*
2 * Copyright (c) 2006-2008 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2006,2008-2009 Mellanox Technologies LTD. All rights reserved.
4 *
5 * This software is available to you under a choice of one of two
6 * licenses.  You may choose to be licensed under the terms of the GNU
7 * General Public License (GPL) Version 2, available from the file
8 * COPYING in the main directory of this source tree, or the
9 * OpenIB.org BSD license below:
10 *
11 *     Redistribution and use in source and binary forms, with or
12 *     without modification, are permitted provided that the following
13 *     conditions are met:
14 *
15 *      - Redistributions of source code must retain the above
16 *        copyright notice, this list of conditions and the following
17 *        disclaimer.
18 *
19 *      - Redistributions in binary form must reproduce the above
20 *        copyright notice, this list of conditions and the following
21 *        disclaimer in the documentation and/or other materials
22 *        provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 *
33 */
34
35/*
36 * Abstract:
37 *    Implementation of OpenSM unicast routing module which loads
38 *    routes from the dump file
39 */
40
41#if HAVE_CONFIG_H
42#  include <config.h>
43#endif				/* HAVE_CONFIG_H */
44
45#include <stdlib.h>
46#include <string.h>
47#include <ctype.h>
48
49#include <iba/ib_types.h>
50#include <complib/cl_qmap.h>
51#include <complib/cl_debug.h>
52#include <opensm/osm_file_ids.h>
53#define FILE_ID OSM_FILE_UCAST_FILE_C
54#include <opensm/osm_opensm.h>
55#include <opensm/osm_switch.h>
56#include <opensm/osm_log.h>
57
58static uint16_t remap_lid(osm_opensm_t * p_osm, uint16_t lid, ib_net64_t guid)
59{
60	osm_port_t *p_port;
61	uint16_t min_lid, max_lid;
62	uint8_t lmc;
63
64	p_port = osm_get_port_by_guid(&p_osm->subn, guid);
65	if (!p_port) {
66		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
67			"cannot find port guid 0x%016" PRIx64
68			" , will use the same lid\n", cl_ntoh64(guid));
69		return lid;
70	}
71
72	osm_port_get_lid_range_ho(p_port, &min_lid, &max_lid);
73	if (min_lid <= lid && lid <= max_lid)
74		return lid;
75
76	lmc = osm_port_get_lmc(p_port);
77	return min_lid + (lid & ((1 << lmc) - 1));
78}
79
80static void add_path(osm_opensm_t * p_osm,
81		     osm_switch_t * p_sw, uint16_t lid, uint8_t port_num,
82		     ib_net64_t port_guid)
83{
84	uint16_t new_lid;
85	uint8_t old_port;
86
87	new_lid = port_guid ? remap_lid(p_osm, lid, port_guid) : lid;
88	old_port = osm_switch_get_port_by_lid(p_sw, new_lid, OSM_LFT);
89	if (old_port != OSM_NO_PATH && old_port != port_num) {
90		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
91			"LID collision is detected on switch "
92			"0x016%" PRIx64 ", will overwrite LID %u entry\n",
93			cl_ntoh64(osm_node_get_node_guid(p_sw->p_node)),
94			new_lid);
95	}
96
97	p_sw->new_lft[new_lid] = port_num;
98	if (!(p_osm->subn.opt.port_profile_switch_nodes && port_guid &&
99	      osm_get_switch_by_guid(&p_osm->subn, port_guid)))
100		osm_switch_count_path(p_sw, port_num);
101
102	OSM_LOG(&p_osm->log, OSM_LOG_DEBUG,
103		"route 0x%04x(was 0x%04x) %u 0x%016" PRIx64
104		" is added to switch 0x%016" PRIx64 "\n",
105		new_lid, lid, port_num, cl_ntoh64(port_guid),
106		cl_ntoh64(osm_node_get_node_guid(p_sw->p_node)));
107}
108
109static void add_lid_hops(osm_opensm_t * p_osm, osm_switch_t * p_sw,
110			 uint16_t lid, ib_net64_t guid,
111			 uint8_t hops[], unsigned len)
112{
113	uint8_t i;
114
115	if (len > p_sw->num_ports)
116		len = p_sw->num_ports;
117
118	for (i = 0; i < len; i++)
119		osm_switch_set_hops(p_sw, lid, i, hops[i]);
120}
121
122static int do_ucast_file_load(void *context)
123{
124	char line[1024];
125	char *file_name;
126	FILE *file;
127	ib_net64_t sw_guid, port_guid;
128	osm_opensm_t *p_osm = context;
129	osm_switch_t *p_sw;
130	uint16_t lid;
131	uint8_t port_num;
132	unsigned lineno;
133	int status = -1;
134
135	file_name = p_osm->subn.opt.lfts_file;
136	if (!file_name) {
137		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
138			"LFTs file name is not given; "
139			"using default routing algorithm\n");
140		return 1;
141	}
142
143	file = fopen(file_name, "r");
144	if (!file) {
145		OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS, "ERR 6302: "
146			"Can't open ucast dump file \'%s\': %m\n", file_name);
147		goto Exit;
148	}
149
150	lineno = 0;
151	p_sw = NULL;
152
153	while (fgets(line, sizeof(line) - 1, file) != NULL) {
154		char *p, *q;
155		lineno++;
156
157		p = line;
158		while (isspace(*p))
159			p++;
160
161		if (*p == '#')
162			continue;
163
164		if (!strncmp(p, "Multicast mlids", 15)) {
165			OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS,
166				"ERR 6303: "
167				"Multicast dump file detected; "
168				"skipping parsing. Using default "
169				"routing algorithm\n");
170		} else if (!strncmp(p, "Unicast lids", 12)) {
171			q = strstr(p, " guid 0x");
172			if (!q) {
173				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
174					"PARSE ERROR: %s:%u: "
175					"cannot parse switch definition\n",
176					file_name, lineno);
177				goto Exit;
178			}
179			p = q + 8;
180			sw_guid = strtoull(p, &q, 16);
181			if (q == p || !isspace(*q)) {
182				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
183					"PARSE ERROR: %s:%u: "
184					"cannot parse switch guid: \'%s\'\n",
185					file_name, lineno, p);
186				goto Exit;
187			}
188			sw_guid = cl_hton64(sw_guid);
189
190			p_sw = osm_get_switch_by_guid(&p_osm->subn, sw_guid);
191			if (!p_sw) {
192				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
193					"cannot find switch %016" PRIx64 "\n",
194					cl_ntoh64(sw_guid));
195				continue;
196			}
197			memset(p_sw->new_lft, OSM_NO_PATH, p_sw->lft_size);
198		} else if (p_sw && !strncmp(p, "0x", 2)) {
199			p += 2;
200			lid = (uint16_t) strtoul(p, &q, 16);
201			if (q == p || !isspace(*q)) {
202				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
203					"PARSE ERROR: %s:%u: "
204					"cannot parse lid: \'%s\'\n",
205					file_name, lineno, p);
206				goto Exit;
207			}
208			p = q;
209			while (isspace(*p))
210				p++;
211			port_num = (uint8_t) strtoul(p, &q, 10);
212			if (q == p || !isspace(*q)) {
213				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
214					"PARSE ERROR: %s:%u: "
215					"cannot parse port: \'%s\'\n",
216					file_name, lineno, p);
217				goto Exit;
218			}
219			if (port_num >=
220				osm_node_get_num_physp(p_sw->p_node)) {
221					OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
222						"Invalid port %d found "
223						"for switch %016" PRIx64 "\n",
224						port_num,
225						cl_ntoh64(osm_node_get_node_guid
226							(p_sw->p_node)));
227					goto Exit;
228			}
229
230			p = q;
231			/* additionally try to extract guid */
232			q = strstr(p, " portguid 0x");
233			if (!q) {
234				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
235					"PARSE WARNING: %s:%u: "
236					"cannot find port guid "
237					"(maybe broken dump): \'%s\'\n",
238					file_name, lineno, p);
239				port_guid = 0;
240			} else {
241				p = q + 12;
242				port_guid = strtoull(p, &q, 16);
243				if (q == p || (!isspace(*q) && *q != ':')) {
244					OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
245						"PARSE WARNING: %s:%u: "
246						"cannot parse port guid "
247						"(maybe broken dump): \'%s\'\n",
248						file_name, lineno, p);
249					port_guid = 0;
250				}
251			}
252			port_guid = cl_hton64(port_guid);
253			add_path(p_osm, p_sw, lid, port_num, port_guid);
254		}
255	}
256	status = 0;
257Exit:
258	if (file)
259		fclose(file);
260	return status;
261}
262
263static int do_lid_matrix_file_load(void *context)
264{
265	char line[1024];
266	uint8_t hops[256];
267	char *file_name;
268	FILE *file;
269	ib_net64_t guid;
270	osm_opensm_t *p_osm = context;
271	osm_switch_t *p_sw;
272	unsigned lineno;
273	uint16_t lid;
274	int status = -1;
275
276	file_name = p_osm->subn.opt.lid_matrix_dump_file;
277	if (!file_name) {
278		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
279			"lid matrix file name is not given; "
280			"using default lid matrix generation algorithm\n");
281		return 1;
282	}
283
284	file = fopen(file_name, "r");
285	if (!file) {
286		OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS, "ERR 6305: "
287			"Can't open lid matrix file \'%s\': %m\n", file_name);
288		goto Exit;
289	}
290
291	lineno = 0;
292	p_sw = NULL;
293
294	while (fgets(line, sizeof(line) - 1, file) != NULL) {
295		char *p, *q;
296		lineno++;
297
298		p = line;
299		while (isspace(*p))
300			p++;
301
302		if (*p == '#')
303			continue;
304
305		if (!strncmp(p, "Switch", 6)) {
306			q = strstr(p, " guid 0x");
307			if (!q) {
308				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
309					"PARSE ERROR: %s:%u: "
310					"cannot parse switch definition\n",
311					file_name, lineno);
312				goto Exit;
313			}
314			p = q + 8;
315			guid = strtoull(p, &q, 16);
316			if (q == p || !isspace(*q)) {
317				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
318					"PARSE ERROR: %s:%u: "
319					"cannot parse switch guid: \'%s\'\n",
320					file_name, lineno, p);
321				goto Exit;
322			}
323			guid = cl_hton64(guid);
324
325			p_sw = osm_get_switch_by_guid(&p_osm->subn, guid);
326			if (!p_sw) {
327				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
328					"cannot find switch %016" PRIx64 "\n",
329					cl_ntoh64(guid));
330				continue;
331			}
332		} else if (p_sw && !strncmp(p, "0x", 2)) {
333			unsigned long num;
334			unsigned len = 0;
335
336			memset(hops, 0xff, sizeof(hops));
337
338			p += 2;
339			num = strtoul(p, &q, 16);
340			if (num > 0xffff || q == p ||
341			    (*q != ':' && !isspace(*q))) {
342				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
343					"PARSE ERROR: %s:%u: "
344					"cannot parse lid: \'%s\'\n",
345					file_name, lineno, p);
346				goto Exit;
347			}
348			/* Just checked the range, so casting is safe */
349			lid = (uint16_t) num;
350			p = q;
351			while (isspace(*p) || *p == ':')
352				p++;
353			while (len < 256 && *p && *p != '#') {
354				num = strtoul(p, &q, 16);
355				if (num > 0xff || q == p) {
356					OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
357						"PARSE ERROR: %s:%u: "
358						"cannot parse hops number: \'%s\'\n",
359						file_name, lineno, p);
360					goto Exit;
361				}
362				/* Just checked the range, so casting is safe */
363				hops[len++] = (uint8_t) num;
364				p = q;
365				while (isspace(*p))
366					p++;
367			}
368			/* additionally try to extract guid */
369			q = strstr(p, " portguid 0x");
370			if (!q) {
371				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
372					"PARSE WARNING: %s:%u: "
373					"cannot find port guid "
374					"(maybe broken dump): \'%s\'\n",
375					file_name, lineno, p);
376				guid = 0;
377			} else {
378				p = q + 12;
379				guid = strtoull(p, &q, 16);
380				if (q == p || !isspace(*q)) {
381					OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
382						"PARSE WARNING: %s:%u: "
383						"cannot parse port guid "
384						"(maybe broken dump): \'%s\'\n",
385						file_name, lineno, p);
386					guid = 0;
387				}
388			}
389			guid = cl_hton64(guid);
390			add_lid_hops(p_osm, p_sw, lid, guid, hops, len);
391		}
392	}
393	status = 0;
394Exit:
395	if (file)
396		fclose(file);
397	return status;
398}
399
400int osm_ucast_file_setup(struct osm_routing_engine *r, osm_opensm_t *osm)
401{
402	r->context = osm;
403	r->build_lid_matrices = do_lid_matrix_file_load;
404	r->ucast_build_fwd_tables = do_ucast_file_load;
405	return 0;
406}
407