1219820Sjeff/*
2219820Sjeff * Copyright (c) 2006,2007 Voltaire, Inc. All rights reserved.
3219820Sjeff * Copyright (c) 2008      Mellanox Technologies LTD. All rights reserved.
4219820Sjeff *
5219820Sjeff * This software is available to you under a choice of one of two
6219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
7219820Sjeff * General Public License (GPL) Version 2, available from the file
8219820Sjeff * COPYING in the main directory of this source tree, or the
9219820Sjeff * OpenIB.org BSD license below:
10219820Sjeff *
11219820Sjeff *     Redistribution and use in source and binary forms, with or
12219820Sjeff *     without modification, are permitted provided that the following
13219820Sjeff *     conditions are met:
14219820Sjeff *
15219820Sjeff *      - Redistributions of source code must retain the above
16219820Sjeff *        copyright notice, this list of conditions and the following
17219820Sjeff *        disclaimer.
18219820Sjeff *
19219820Sjeff *      - Redistributions in binary form must reproduce the above
20219820Sjeff *        copyright notice, this list of conditions and the following
21219820Sjeff *        disclaimer in the documentation and/or other materials
22219820Sjeff *        provided with the distribution.
23219820Sjeff *
24219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31219820Sjeff * SOFTWARE.
32219820Sjeff *
33219820Sjeff */
34219820Sjeff
35219820Sjeff/*
36219820Sjeff * Abstract:
37219820Sjeff *    Implementation of OpenSM unicast routing module which loads
38219820Sjeff *    routes from the dump file
39219820Sjeff */
40219820Sjeff
41219820Sjeff#if HAVE_CONFIG_H
42219820Sjeff#  include <config.h>
43219820Sjeff#endif				/* HAVE_CONFIG_H */
44219820Sjeff
45219820Sjeff#include <stdlib.h>
46219820Sjeff#include <string.h>
47219820Sjeff#include <ctype.h>
48219820Sjeff
49219820Sjeff#include <iba/ib_types.h>
50219820Sjeff#include <complib/cl_qmap.h>
51219820Sjeff#include <complib/cl_debug.h>
52219820Sjeff#include <opensm/osm_opensm.h>
53219820Sjeff#include <opensm/osm_switch.h>
54219820Sjeff#include <opensm/osm_log.h>
55219820Sjeff
56219820Sjeffstatic uint16_t remap_lid(osm_opensm_t * p_osm, uint16_t lid, ib_net64_t guid)
57219820Sjeff{
58219820Sjeff	osm_port_t *p_port;
59219820Sjeff	uint16_t min_lid, max_lid;
60219820Sjeff	uint8_t lmc;
61219820Sjeff
62219820Sjeff	p_port = osm_get_port_by_guid(&p_osm->subn, guid);
63219820Sjeff	if (!p_port) {
64219820Sjeff		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
65219820Sjeff			"cannot find port guid 0x%016" PRIx64
66219820Sjeff			" , will use the same lid\n", cl_ntoh64(guid));
67219820Sjeff		return lid;
68219820Sjeff	}
69219820Sjeff
70219820Sjeff	osm_port_get_lid_range_ho(p_port, &min_lid, &max_lid);
71219820Sjeff	if (min_lid <= lid && lid <= max_lid)
72219820Sjeff		return lid;
73219820Sjeff
74219820Sjeff	lmc = osm_port_get_lmc(p_port);
75219820Sjeff	return min_lid + (lid & ((1 << lmc) - 1));
76219820Sjeff}
77219820Sjeff
78219820Sjeffstatic void add_path(osm_opensm_t * p_osm,
79219820Sjeff		     osm_switch_t * p_sw, uint16_t lid, uint8_t port_num,
80219820Sjeff		     ib_net64_t port_guid)
81219820Sjeff{
82219820Sjeff	uint16_t new_lid;
83219820Sjeff	uint8_t old_port;
84219820Sjeff
85219820Sjeff	new_lid = port_guid ? remap_lid(p_osm, lid, port_guid) : lid;
86219820Sjeff	old_port = osm_switch_get_port_by_lid(p_sw, new_lid);
87219820Sjeff	if (old_port != OSM_NO_PATH && old_port != port_num) {
88219820Sjeff		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
89219820Sjeff			"LID collision is detected on switch "
90219820Sjeff			"0x016%" PRIx64 ", will overwrite LID %u entry\n",
91219820Sjeff			cl_ntoh64(osm_node_get_node_guid(p_sw->p_node)),
92219820Sjeff			new_lid);
93219820Sjeff	}
94219820Sjeff
95219820Sjeff	p_sw->new_lft[new_lid] = port_num;
96219820Sjeff	if (!(p_osm->subn.opt.port_profile_switch_nodes && port_guid &&
97219820Sjeff	      osm_get_switch_by_guid(&p_osm->subn, port_guid)))
98219820Sjeff		osm_switch_count_path(p_sw, port_num);
99219820Sjeff
100219820Sjeff	OSM_LOG(&p_osm->log, OSM_LOG_DEBUG,
101219820Sjeff		"route 0x%04x(was 0x%04x) %u 0x%016" PRIx64
102219820Sjeff		" is added to switch 0x%016" PRIx64 "\n",
103219820Sjeff		new_lid, lid, port_num, cl_ntoh64(port_guid),
104219820Sjeff		cl_ntoh64(osm_node_get_node_guid(p_sw->p_node)));
105219820Sjeff}
106219820Sjeff
107219820Sjeffstatic void add_lid_hops(osm_opensm_t * p_osm, osm_switch_t * p_sw,
108219820Sjeff			 uint16_t lid, ib_net64_t guid,
109219820Sjeff			 uint8_t hops[], unsigned len)
110219820Sjeff{
111219820Sjeff	uint16_t new_lid;
112219820Sjeff	uint8_t i;
113219820Sjeff
114219820Sjeff	new_lid = guid ? remap_lid(p_osm, lid, guid) : lid;
115219820Sjeff	if (len > p_sw->num_ports)
116219820Sjeff		len = p_sw->num_ports;
117219820Sjeff
118219820Sjeff	for (i = 0; i < len; i++)
119219820Sjeff		osm_switch_set_hops(p_sw, lid, i, hops[i]);
120219820Sjeff}
121219820Sjeff
122219820Sjeffstatic int do_ucast_file_load(void *context)
123219820Sjeff{
124219820Sjeff	char line[1024];
125219820Sjeff	char *file_name;
126219820Sjeff	FILE *file;
127219820Sjeff	ib_net64_t sw_guid, port_guid;
128219820Sjeff	osm_opensm_t *p_osm = context;
129219820Sjeff	osm_switch_t *p_sw;
130219820Sjeff	uint16_t lid;
131219820Sjeff	uint8_t port_num;
132219820Sjeff	unsigned lineno;
133219820Sjeff
134219820Sjeff	file_name = p_osm->subn.opt.lfts_file;
135219820Sjeff	if (!file_name) {
136219820Sjeff		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
137219820Sjeff			"LFTs file name is not given; "
138219820Sjeff			"using default routing algorithm\n");
139219820Sjeff		return 1;
140219820Sjeff	}
141219820Sjeff
142219820Sjeff	file = fopen(file_name, "r");
143219820Sjeff	if (!file) {
144219820Sjeff		OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS, "ERR 6302: "
145219820Sjeff			"cannot open ucast dump file \'%s\': %m\n", file_name);
146219820Sjeff		return -1;
147219820Sjeff	}
148219820Sjeff
149219820Sjeff	lineno = 0;
150219820Sjeff	p_sw = NULL;
151219820Sjeff
152219820Sjeff	while (fgets(line, sizeof(line) - 1, file) != NULL) {
153219820Sjeff		char *p, *q;
154219820Sjeff		lineno++;
155219820Sjeff
156219820Sjeff		p = line;
157219820Sjeff		while (isspace(*p))
158219820Sjeff			p++;
159219820Sjeff
160219820Sjeff		if (*p == '#')
161219820Sjeff			continue;
162219820Sjeff
163219820Sjeff		if (!strncmp(p, "Multicast mlids", 15)) {
164219820Sjeff			OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS,
165219820Sjeff				"ERR 6303: "
166219820Sjeff				"Multicast dump file detected; "
167219820Sjeff				"skipping parsing. Using default "
168219820Sjeff				"routing algorithm\n");
169219820Sjeff		} else if (!strncmp(p, "Unicast lids", 12)) {
170219820Sjeff			if (p_sw)
171219820Sjeff				osm_ucast_mgr_set_fwd_table(&p_osm->sm.
172219820Sjeff							    ucast_mgr, p_sw);
173219820Sjeff			q = strstr(p, " guid 0x");
174219820Sjeff			if (!q) {
175219820Sjeff				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
176219820Sjeff					"PARSE ERROR: %s:%u: "
177219820Sjeff					"cannot parse switch definition\n",
178219820Sjeff					file_name, lineno);
179219820Sjeff				return -1;
180219820Sjeff			}
181219820Sjeff			p = q + 8;
182219820Sjeff			sw_guid = strtoull(p, &q, 16);
183219820Sjeff			if (q == p || !isspace(*q)) {
184219820Sjeff				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
185219820Sjeff					"PARSE ERROR: %s:%u: "
186219820Sjeff					"cannot parse switch guid: \'%s\'\n",
187219820Sjeff					file_name, lineno, p);
188219820Sjeff				return -1;
189219820Sjeff			}
190219820Sjeff			sw_guid = cl_hton64(sw_guid);
191219820Sjeff
192219820Sjeff			p_sw = osm_get_switch_by_guid(&p_osm->subn, sw_guid);
193219820Sjeff			if (!p_sw) {
194219820Sjeff				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
195219820Sjeff					"cannot find switch %016" PRIx64 "\n",
196219820Sjeff					cl_ntoh64(sw_guid));
197219820Sjeff				continue;
198219820Sjeff			}
199219820Sjeff			memset(p_sw->new_lft, OSM_NO_PATH,
200219820Sjeff			       IB_LID_UCAST_END_HO + 1);
201219820Sjeff		} else if (p_sw && !strncmp(p, "0x", 2)) {
202219820Sjeff			p += 2;
203219820Sjeff			lid = (uint16_t) strtoul(p, &q, 16);
204219820Sjeff			if (q == p || !isspace(*q)) {
205219820Sjeff				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
206219820Sjeff					"PARSE ERROR: %s:%u: "
207219820Sjeff					"cannot parse lid: \'%s\'\n",
208219820Sjeff					file_name, lineno, p);
209219820Sjeff				return -1;
210219820Sjeff			}
211219820Sjeff			p = q;
212219820Sjeff			while (isspace(*p))
213219820Sjeff				p++;
214219820Sjeff			port_num = (uint8_t) strtoul(p, &q, 10);
215219820Sjeff			if (q == p || !isspace(*q)) {
216219820Sjeff				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
217219820Sjeff					"PARSE ERROR: %s:%u: "
218219820Sjeff					"cannot parse port: \'%s\'\n",
219219820Sjeff					file_name, lineno, p);
220219820Sjeff				return -1;
221219820Sjeff			}
222219820Sjeff			p = q;
223219820Sjeff			/* additionally try to exract guid */
224219820Sjeff			q = strstr(p, " portguid 0x");
225219820Sjeff			if (!q) {
226219820Sjeff				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
227219820Sjeff					"PARSE WARNING: %s:%u: "
228219820Sjeff					"cannot find port guid "
229219820Sjeff					"(maybe broken dump): \'%s\'\n",
230219820Sjeff					file_name, lineno, p);
231219820Sjeff				port_guid = 0;
232219820Sjeff			} else {
233219820Sjeff				p = q + 12;
234219820Sjeff				port_guid = strtoull(p, &q, 16);
235219820Sjeff				if (q == p || (!isspace(*q) && *q != ':')) {
236219820Sjeff					OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
237219820Sjeff						"PARSE WARNING: %s:%u: "
238219820Sjeff						"cannot parse port guid "
239219820Sjeff						"(maybe broken dump): \'%s\'\n",
240219820Sjeff						file_name, lineno, p);
241219820Sjeff					port_guid = 0;
242219820Sjeff				}
243219820Sjeff			}
244219820Sjeff			port_guid = cl_hton64(port_guid);
245219820Sjeff			add_path(p_osm, p_sw, lid, port_num, port_guid);
246219820Sjeff		}
247219820Sjeff	}
248219820Sjeff
249219820Sjeff	if (p_sw)
250219820Sjeff		osm_ucast_mgr_set_fwd_table(&p_osm->sm.ucast_mgr, p_sw);
251219820Sjeff
252219820Sjeff	fclose(file);
253219820Sjeff	return 0;
254219820Sjeff}
255219820Sjeff
256219820Sjeffstatic int do_lid_matrix_file_load(void *context)
257219820Sjeff{
258219820Sjeff	char line[1024];
259219820Sjeff	uint8_t hops[256];
260219820Sjeff	char *file_name;
261219820Sjeff	FILE *file;
262219820Sjeff	ib_net64_t guid;
263219820Sjeff	osm_opensm_t *p_osm = context;
264219820Sjeff	osm_switch_t *p_sw;
265219820Sjeff	unsigned lineno;
266219820Sjeff	uint16_t lid;
267219820Sjeff
268219820Sjeff	file_name = p_osm->subn.opt.lid_matrix_dump_file;
269219820Sjeff	if (!file_name) {
270219820Sjeff		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
271219820Sjeff			"lid matrix file name is not given; "
272219820Sjeff			"using default lid matrix generation algorithm\n");
273219820Sjeff		return 1;
274219820Sjeff	}
275219820Sjeff
276219820Sjeff	file = fopen(file_name, "r");
277219820Sjeff	if (!file) {
278219820Sjeff		OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS, "ERR 6305: "
279219820Sjeff			"cannot open lid matrix file \'%s\': %m\n", file_name);
280219820Sjeff		return -1;
281219820Sjeff	}
282219820Sjeff
283219820Sjeff	lineno = 0;
284219820Sjeff	p_sw = NULL;
285219820Sjeff
286219820Sjeff	while (fgets(line, sizeof(line) - 1, file) != NULL) {
287219820Sjeff		char *p, *q;
288219820Sjeff		lineno++;
289219820Sjeff
290219820Sjeff		p = line;
291219820Sjeff		while (isspace(*p))
292219820Sjeff			p++;
293219820Sjeff
294219820Sjeff		if (*p == '#')
295219820Sjeff			continue;
296219820Sjeff
297219820Sjeff		if (!strncmp(p, "Switch", 6)) {
298219820Sjeff			q = strstr(p, " guid 0x");
299219820Sjeff			if (!q) {
300219820Sjeff				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
301219820Sjeff					"PARSE ERROR: %s:%u: "
302219820Sjeff					"cannot parse switch definition\n",
303219820Sjeff					file_name, lineno);
304219820Sjeff				return -1;
305219820Sjeff			}
306219820Sjeff			p = q + 8;
307219820Sjeff			guid = strtoull(p, &q, 16);
308219820Sjeff			if (q == p || !isspace(*q)) {
309219820Sjeff				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
310219820Sjeff					"PARSE ERROR: %s:%u: "
311219820Sjeff					"cannot parse switch guid: \'%s\'\n",
312219820Sjeff					file_name, lineno, p);
313219820Sjeff				return -1;
314219820Sjeff			}
315219820Sjeff			guid = cl_hton64(guid);
316219820Sjeff
317219820Sjeff			p_sw = osm_get_switch_by_guid(&p_osm->subn, guid);
318219820Sjeff			if (!p_sw) {
319219820Sjeff				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
320219820Sjeff					"cannot find switch %016" PRIx64 "\n",
321219820Sjeff					cl_ntoh64(guid));
322219820Sjeff				continue;
323219820Sjeff			}
324219820Sjeff		} else if (p_sw && !strncmp(p, "0x", 2)) {
325219820Sjeff			unsigned long num;
326219820Sjeff			unsigned len = 0;
327219820Sjeff
328219820Sjeff			memset(hops, 0xff, sizeof(hops));
329219820Sjeff
330219820Sjeff			p += 2;
331219820Sjeff			num = strtoul(p, &q, 16);
332219820Sjeff			if (num > 0xffff || q == p ||
333219820Sjeff			    (*q != ':' && !isspace(*q))) {
334219820Sjeff				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
335219820Sjeff					"PARSE ERROR: %s:%u: "
336219820Sjeff					"cannot parse lid: \'%s\'\n",
337219820Sjeff					file_name, lineno, p);
338219820Sjeff				return -1;
339219820Sjeff			}
340219820Sjeff			/* Just checked the range, so casting is safe */
341219820Sjeff			lid = (uint16_t) num;
342219820Sjeff			p = q;
343219820Sjeff			while (isspace(*p) || *p == ':')
344219820Sjeff				p++;
345219820Sjeff			while (len < 256 && *p && *p != '#') {
346219820Sjeff				num = strtoul(p, &q, 16);
347219820Sjeff				if (num > 0xff || q == p) {
348219820Sjeff					OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
349219820Sjeff						"PARSE ERROR: %s:%u: "
350219820Sjeff						"cannot parse hops number: \'%s\'\n",
351219820Sjeff						file_name, lineno, p);
352219820Sjeff					return -1;
353219820Sjeff				}
354219820Sjeff				/* Just checked the range, so casting is safe */
355219820Sjeff				hops[len++] = (uint8_t) num;
356219820Sjeff				p = q;
357219820Sjeff				while (isspace(*p))
358219820Sjeff					p++;
359219820Sjeff			}
360219820Sjeff			/* additionally try to extract guid */
361219820Sjeff			q = strstr(p, " portguid 0x");
362219820Sjeff			if (!q) {
363219820Sjeff				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
364219820Sjeff					"PARSE WARNING: %s:%u: "
365219820Sjeff					"cannot find port guid "
366219820Sjeff					"(maybe broken dump): \'%s\'\n",
367219820Sjeff					file_name, lineno, p);
368219820Sjeff				guid = 0;
369219820Sjeff			} else {
370219820Sjeff				p = q + 12;
371219820Sjeff				guid = strtoull(p, &q, 16);
372219820Sjeff				if (q == p || !isspace(*q)) {
373219820Sjeff					OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
374219820Sjeff						"PARSE WARNING: %s:%u: "
375219820Sjeff						"cannot parse port guid "
376219820Sjeff						"(maybe broken dump): \'%s\'\n",
377219820Sjeff						file_name, lineno, p);
378219820Sjeff					guid = 0;
379219820Sjeff				}
380219820Sjeff			}
381219820Sjeff			guid = cl_hton64(guid);
382219820Sjeff			add_lid_hops(p_osm, p_sw, lid, guid, hops, len);
383219820Sjeff		}
384219820Sjeff	}
385219820Sjeff
386219820Sjeff	fclose(file);
387219820Sjeff	return 0;
388219820Sjeff}
389219820Sjeff
390219820Sjeffint osm_ucast_file_setup(struct osm_routing_engine *r, osm_opensm_t *osm)
391219820Sjeff{
392219820Sjeff	r->context = osm;
393219820Sjeff	r->build_lid_matrices = do_lid_matrix_file_load;
394219820Sjeff	r->ucast_build_fwd_tables = do_ucast_file_load;
395219820Sjeff	return 0;
396219820Sjeff}
397