1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2007 Voltaire Inc.  All rights reserved.
3219820Sjeff * Copyright (c) 2007 Xsigo Systems Inc.  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/*               FABRIC SCANNER SPECIFIC DATA             */
37219820Sjeff/*========================================================*/
38219820Sjeff
39219820Sjeff#if HAVE_CONFIG_H
40219820Sjeff#  include <config.h>
41219820Sjeff#endif /* HAVE_CONFIG_H */
42219820Sjeff
43219820Sjeff#include <stdint.h>
44219820Sjeff#include <stdlib.h>
45219820Sjeff#include <inttypes.h>
46219820Sjeff
47219820Sjeff#include <infiniband/common.h>
48219820Sjeff#include <infiniband/mad.h>
49219820Sjeff
50219820Sjeff#include "ibnetdiscover.h"
51219820Sjeff#include "grouping.h"
52219820Sjeff
53219820Sjeff#define OUT_BUFFER_SIZE 16
54219820Sjeff
55219820Sjeff
56219820Sjeffextern Node *nodesdist[MAXHOPS+1];	/* last is CA list */
57219820Sjeffextern Node *mynode;
58219820Sjeffextern Port *myport;
59219820Sjeffextern int maxhops_discovered;
60219820Sjeff
61219820SjeffAllChassisList mylist;
62219820Sjeff
63219820Sjeffchar *ChassisTypeStr[5] = { "", "ISR9288", "ISR9096", "ISR2012", "ISR2004" };
64219820Sjeffchar *ChassisSlotStr[4] = { "", "Line", "Spine", "SRBD" };
65219820Sjeff
66219820Sjeff
67219820Sjeffchar *get_chassis_type(unsigned char chassistype)
68219820Sjeff{
69219820Sjeff	if (chassistype == UNRESOLVED_CT || chassistype > ISR2004_CT)
70219820Sjeff		return NULL;
71219820Sjeff	return ChassisTypeStr[chassistype];
72219820Sjeff}
73219820Sjeff
74219820Sjeffchar *get_chassis_slot(unsigned char chassisslot)
75219820Sjeff{
76219820Sjeff	if (chassisslot == UNRESOLVED_CS || chassisslot > SRBD_CS)
77219820Sjeff		return NULL;
78219820Sjeff	return ChassisSlotStr[chassisslot];
79219820Sjeff}
80219820Sjeff
81219820Sjeffstatic struct ChassisList *find_chassisnum(unsigned char chassisnum)
82219820Sjeff{
83219820Sjeff	ChassisList *current;
84219820Sjeff
85219820Sjeff	for (current = mylist.first; current; current = current->next) {
86219820Sjeff		if (current->chassisnum == chassisnum)
87219820Sjeff			return current;
88219820Sjeff	}
89219820Sjeff
90219820Sjeff	return NULL;
91219820Sjeff}
92219820Sjeff
93219820Sjeffstatic uint64_t topspin_chassisguid(uint64_t guid)
94219820Sjeff{
95219820Sjeff	/* Byte 3 in system image GUID is chassis type, and */
96219820Sjeff	/* Byte 4 is location ID (slot) so just mask off byte 4 */
97219820Sjeff	return guid & 0xffffffff00ffffffULL;
98219820Sjeff}
99219820Sjeff
100219820Sjeffint is_xsigo_guid(uint64_t guid)
101219820Sjeff{
102219820Sjeff	if ((guid & 0xffffff0000000000ULL) == 0x0013970000000000ULL)
103219820Sjeff		return 1;
104219820Sjeff	else
105219820Sjeff		return 0;
106219820Sjeff}
107219820Sjeff
108219820Sjeffstatic int is_xsigo_leafone(uint64_t guid)
109219820Sjeff{
110219820Sjeff	if ((guid & 0xffffffffff000000ULL) == 0x0013970102000000ULL)
111219820Sjeff		return 1;
112219820Sjeff	else
113219820Sjeff		return 0;
114219820Sjeff}
115219820Sjeff
116219820Sjeffint is_xsigo_hca(uint64_t guid)
117219820Sjeff{
118219820Sjeff	/* NodeType 2 is HCA */
119219820Sjeff	if ((guid & 0xffffffff00000000ULL) == 0x0013970200000000ULL)
120219820Sjeff		return 1;
121219820Sjeff	else
122219820Sjeff		return 0;
123219820Sjeff}
124219820Sjeff
125219820Sjeffint is_xsigo_tca(uint64_t guid)
126219820Sjeff{
127219820Sjeff	/* NodeType 3 is TCA */
128219820Sjeff	if ((guid & 0xffffffff00000000ULL) == 0x0013970300000000ULL)
129219820Sjeff		return 1;
130219820Sjeff	else
131219820Sjeff		return 0;
132219820Sjeff}
133219820Sjeff
134219820Sjeffstatic int is_xsigo_ca(uint64_t guid)
135219820Sjeff{
136219820Sjeff	if (is_xsigo_hca(guid) || is_xsigo_tca(guid))
137219820Sjeff		return 1;
138219820Sjeff	else
139219820Sjeff		return 0;
140219820Sjeff}
141219820Sjeff
142219820Sjeffstatic int is_xsigo_switch(uint64_t guid)
143219820Sjeff{
144219820Sjeff	if ((guid & 0xffffffff00000000ULL) == 0x0013970100000000ULL)
145219820Sjeff		return 1;
146219820Sjeff	else
147219820Sjeff		return 0;
148219820Sjeff}
149219820Sjeff
150219820Sjeffstatic uint64_t xsigo_chassisguid(Node *node)
151219820Sjeff{
152219820Sjeff	if (!is_xsigo_ca(node->sysimgguid)) {
153219820Sjeff		/* Byte 3 is NodeType and byte 4 is PortType */
154219820Sjeff		/* If NodeType is 1 (switch), PortType is masked */
155219820Sjeff		if (is_xsigo_switch(node->sysimgguid))
156219820Sjeff			return node->sysimgguid & 0xffffffff00ffffffULL;
157219820Sjeff		else
158219820Sjeff			return node->sysimgguid;
159219820Sjeff	} else {
160219820Sjeff		/* Is there a peer port ? */
161219820Sjeff		if (!node->ports->remoteport)
162219820Sjeff			return node->sysimgguid;
163219820Sjeff
164219820Sjeff		/* If peer port is Leaf 1, use its chassis GUID */
165219820Sjeff		if (is_xsigo_leafone(node->ports->remoteport->node->sysimgguid))
166219820Sjeff			return node->ports->remoteport->node->sysimgguid &
167219820Sjeff			       0xffffffff00ffffffULL;
168219820Sjeff		else
169219820Sjeff			return node->sysimgguid;
170219820Sjeff	}
171219820Sjeff}
172219820Sjeff
173219820Sjeffstatic uint64_t get_chassisguid(Node *node)
174219820Sjeff{
175219820Sjeff	if (node->vendid == TS_VENDOR_ID || node->vendid == SS_VENDOR_ID)
176219820Sjeff		return topspin_chassisguid(node->sysimgguid);
177219820Sjeff	else if (node->vendid == XS_VENDOR_ID || is_xsigo_guid(node->sysimgguid))
178219820Sjeff		return xsigo_chassisguid(node);
179219820Sjeff	else
180219820Sjeff		return node->sysimgguid;
181219820Sjeff}
182219820Sjeff
183219820Sjeffstatic struct ChassisList *find_chassisguid(Node *node)
184219820Sjeff{
185219820Sjeff	ChassisList *current;
186219820Sjeff	uint64_t chguid;
187219820Sjeff
188219820Sjeff	chguid = get_chassisguid(node);
189219820Sjeff	for (current = mylist.first; current; current = current->next) {
190219820Sjeff		if (current->chassisguid == chguid)
191219820Sjeff			return current;
192219820Sjeff	}
193219820Sjeff
194219820Sjeff	return NULL;
195219820Sjeff}
196219820Sjeff
197219820Sjeffuint64_t get_chassis_guid(unsigned char chassisnum)
198219820Sjeff{
199219820Sjeff	ChassisList *chassis;
200219820Sjeff
201219820Sjeff	chassis = find_chassisnum(chassisnum);
202219820Sjeff	if (chassis)
203219820Sjeff		return chassis->chassisguid;
204219820Sjeff	else
205219820Sjeff		return 0;
206219820Sjeff}
207219820Sjeff
208219820Sjeffstatic int is_router(Node *node)
209219820Sjeff{
210219820Sjeff	return (node->devid == VTR_DEVID_IB_FC_ROUTER ||
211219820Sjeff		node->devid == VTR_DEVID_IB_IP_ROUTER);
212219820Sjeff}
213219820Sjeff
214219820Sjeffstatic int is_spine_9096(Node *node)
215219820Sjeff{
216219820Sjeff	return (node->devid == VTR_DEVID_SFB4 ||
217219820Sjeff		node->devid == VTR_DEVID_SFB4_DDR);
218219820Sjeff}
219219820Sjeff
220219820Sjeffstatic int is_spine_9288(Node *node)
221219820Sjeff{
222219820Sjeff	return (node->devid == VTR_DEVID_SFB12 ||
223219820Sjeff		node->devid == VTR_DEVID_SFB12_DDR);
224219820Sjeff}
225219820Sjeff
226219820Sjeffstatic int is_spine_2004(Node *node)
227219820Sjeff{
228219820Sjeff	return (node->devid == VTR_DEVID_SFB2004);
229219820Sjeff}
230219820Sjeff
231219820Sjeffstatic int is_spine_2012(Node *node)
232219820Sjeff{
233219820Sjeff	return (node->devid == VTR_DEVID_SFB2012);
234219820Sjeff}
235219820Sjeff
236219820Sjeffstatic int is_spine(Node *node)
237219820Sjeff{
238219820Sjeff	return (is_spine_9096(node) || is_spine_9288(node) ||
239219820Sjeff		is_spine_2004(node) || is_spine_2012(node));
240219820Sjeff}
241219820Sjeff
242219820Sjeffstatic int is_line_24(Node *node)
243219820Sjeff{
244219820Sjeff	return (node->devid == VTR_DEVID_SLB24 ||
245219820Sjeff		node->devid == VTR_DEVID_SLB24_DDR ||
246219820Sjeff		node->devid == VTR_DEVID_SRB2004);
247219820Sjeff}
248219820Sjeff
249219820Sjeffstatic int is_line_8(Node *node)
250219820Sjeff{
251219820Sjeff	return (node->devid == VTR_DEVID_SLB8);
252219820Sjeff}
253219820Sjeff
254219820Sjeffstatic int is_line_2024(Node *node)
255219820Sjeff{
256219820Sjeff	return (node->devid == VTR_DEVID_SLB2024);
257219820Sjeff}
258219820Sjeff
259219820Sjeffstatic int is_line(Node *node)
260219820Sjeff{
261219820Sjeff	return (is_line_24(node) || is_line_8(node) || is_line_2024(node));
262219820Sjeff}
263219820Sjeff
264219820Sjeffint is_chassis_switch(Node *node)
265219820Sjeff{
266219820Sjeff    return (is_spine(node) || is_line(node));
267219820Sjeff}
268219820Sjeff
269219820Sjeff/* these structs help find Line (Anafa) slot number while using spine portnum */
270219820Sjeffint line_slot_2_sfb4[25]        = { 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4 };
271219820Sjeffint anafa_line_slot_2_sfb4[25]  = { 0, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2 };
272219820Sjeffint line_slot_2_sfb12[25]       = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10, 10, 11, 11, 12, 12 };
273219820Sjeffint anafa_line_slot_2_sfb12[25] = { 0, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 };
274219820Sjeff
275219820Sjeff/* IPR FCR modules connectivity while using sFB4 port as reference */
276219820Sjeffint ipr_slot_2_sfb4_port[25]    = { 0, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1 };
277219820Sjeff
278219820Sjeff/* these structs help find Spine (Anafa) slot number while using spine portnum */
279219820Sjeffint spine12_slot_2_slb[25]      = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
280219820Sjeffint anafa_spine12_slot_2_slb[25]= { 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
281219820Sjeffint spine4_slot_2_slb[25]       = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
282219820Sjeffint anafa_spine4_slot_2_slb[25] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
283219820Sjeff/*	reference                     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; */
284219820Sjeff
285219820Sjeffstatic void get_sfb_slot(Node *node, Port *lineport)
286219820Sjeff{
287219820Sjeff	ChassisRecord *ch = node->chrecord;
288219820Sjeff
289219820Sjeff	ch->chassisslot = SPINE_CS;
290219820Sjeff	if (is_spine_9096(node)) {
291219820Sjeff		ch->chassistype = ISR9096_CT;
292219820Sjeff		ch->slotnum = spine4_slot_2_slb[lineport->portnum];
293219820Sjeff		ch->anafanum = anafa_spine4_slot_2_slb[lineport->portnum];
294219820Sjeff	} else if (is_spine_9288(node)) {
295219820Sjeff		ch->chassistype = ISR9288_CT;
296219820Sjeff		ch->slotnum = spine12_slot_2_slb[lineport->portnum];
297219820Sjeff		ch->anafanum = anafa_spine12_slot_2_slb[lineport->portnum];
298219820Sjeff	} else if (is_spine_2012(node)) {
299219820Sjeff		ch->chassistype = ISR2012_CT;
300219820Sjeff		ch->slotnum = spine12_slot_2_slb[lineport->portnum];
301219820Sjeff		ch->anafanum = anafa_spine12_slot_2_slb[lineport->portnum];
302219820Sjeff	} else if (is_spine_2004(node)) {
303219820Sjeff		ch->chassistype = ISR2004_CT;
304219820Sjeff		ch->slotnum = spine4_slot_2_slb[lineport->portnum];
305219820Sjeff		ch->anafanum = anafa_spine4_slot_2_slb[lineport->portnum];
306219820Sjeff	} else {
307219820Sjeff		IBPANIC("Unexpected node found: guid 0x%016" PRIx64, node->nodeguid);
308219820Sjeff	}
309219820Sjeff}
310219820Sjeff
311219820Sjeffstatic void get_router_slot(Node *node, Port *spineport)
312219820Sjeff{
313219820Sjeff	ChassisRecord *ch = node->chrecord;
314219820Sjeff	int guessnum = 0;
315219820Sjeff
316219820Sjeff	if (!ch) {
317219820Sjeff		if (!(node->chrecord = calloc(1, sizeof(ChassisRecord))))
318219820Sjeff			IBPANIC("out of mem");
319219820Sjeff		ch = node->chrecord;
320219820Sjeff	}
321219820Sjeff
322219820Sjeff	ch->chassisslot = SRBD_CS;
323219820Sjeff	if (is_spine_9096(spineport->node)) {
324219820Sjeff		ch->chassistype = ISR9096_CT;
325219820Sjeff		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
326219820Sjeff		ch->anafanum = ipr_slot_2_sfb4_port[spineport->portnum];
327219820Sjeff	} else if (is_spine_9288(spineport->node)) {
328219820Sjeff		ch->chassistype = ISR9288_CT;
329219820Sjeff		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
330219820Sjeff		/* this is a smart guess based on nodeguids order on sFB-12 module */
331219820Sjeff		guessnum = spineport->node->nodeguid % 4;
332219820Sjeff		/* module 1 <--> remote anafa 3 */
333219820Sjeff		/* module 2 <--> remote anafa 2 */
334219820Sjeff		/* module 3 <--> remote anafa 1 */
335219820Sjeff		ch->anafanum = (guessnum == 3 ? 1 : (guessnum == 1 ? 3 : 2));
336219820Sjeff	} else if (is_spine_2012(spineport->node)) {
337219820Sjeff		ch->chassistype = ISR2012_CT;
338219820Sjeff		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
339219820Sjeff		/* this is a smart guess based on nodeguids order on sFB-12 module */
340219820Sjeff		guessnum = spineport->node->nodeguid % 4;
341219820Sjeff		// module 1 <--> remote anafa 3
342219820Sjeff		// module 2 <--> remote anafa 2
343219820Sjeff		// module 3 <--> remote anafa 1
344219820Sjeff		ch->anafanum = (guessnum == 3? 1 : (guessnum == 1 ? 3 : 2));
345219820Sjeff	} else if (is_spine_2004(spineport->node)) {
346219820Sjeff		ch->chassistype = ISR2004_CT;
347219820Sjeff		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
348219820Sjeff		ch->anafanum = ipr_slot_2_sfb4_port[spineport->portnum];
349219820Sjeff	} else {
350219820Sjeff		IBPANIC("Unexpected node found: guid 0x%016" PRIx64, spineport->node->nodeguid);
351219820Sjeff	}
352219820Sjeff}
353219820Sjeff
354219820Sjeffstatic void get_slb_slot(ChassisRecord *ch, Port *spineport)
355219820Sjeff{
356219820Sjeff	ch->chassisslot = LINE_CS;
357219820Sjeff	if (is_spine_9096(spineport->node)) {
358219820Sjeff		ch->chassistype = ISR9096_CT;
359219820Sjeff		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
360219820Sjeff		ch->anafanum = anafa_line_slot_2_sfb4[spineport->portnum];
361219820Sjeff	} else if (is_spine_9288(spineport->node)) {
362219820Sjeff		ch->chassistype = ISR9288_CT;
363219820Sjeff		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
364219820Sjeff		ch->anafanum = anafa_line_slot_2_sfb12[spineport->portnum];
365219820Sjeff	} else if (is_spine_2012(spineport->node)) {
366219820Sjeff		ch->chassistype = ISR2012_CT;
367219820Sjeff		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
368219820Sjeff		ch->anafanum = anafa_line_slot_2_sfb12[spineport->portnum];
369219820Sjeff	} else if (is_spine_2004(spineport->node)) {
370219820Sjeff		ch->chassistype = ISR2004_CT;
371219820Sjeff		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
372219820Sjeff		ch->anafanum = anafa_line_slot_2_sfb4[spineport->portnum];
373219820Sjeff	} else {
374219820Sjeff		IBPANIC("Unexpected node found: guid 0x%016" PRIx64, spineport->node->nodeguid);
375219820Sjeff	}
376219820Sjeff}
377219820Sjeff
378219820Sjeff/*
379219820Sjeff	This function called for every Voltaire node in fabric
380219820Sjeff	It could be optimized so, but time overhead is very small
381219820Sjeff	and its only diag.util
382219820Sjeff*/
383219820Sjeffstatic void fill_chassis_record(Node *node)
384219820Sjeff{
385219820Sjeff	Port *port;
386219820Sjeff	Node *remnode = 0;
387219820Sjeff	ChassisRecord *ch = 0;
388219820Sjeff
389219820Sjeff	if (node->chrecord) /* somehow this node has already been passed */
390219820Sjeff		return;
391219820Sjeff
392219820Sjeff	if (!(node->chrecord = calloc(1, sizeof(ChassisRecord))))
393219820Sjeff		IBPANIC("out of mem");
394219820Sjeff
395219820Sjeff	ch = node->chrecord;
396219820Sjeff
397219820Sjeff	/* node is router only in case of using unique lid */
398219820Sjeff	/* (which is lid of chassis router port) */
399219820Sjeff	/* in such case node->ports is actually a requested port... */
400219820Sjeff	if (is_router(node) && is_spine(node->ports->remoteport->node))
401219820Sjeff		get_router_slot(node, node->ports->remoteport);
402219820Sjeff	else if (is_spine(node)) {
403219820Sjeff		for (port = node->ports; port; port = port->next) {
404219820Sjeff			if (!port->remoteport)
405219820Sjeff				continue;
406219820Sjeff			remnode = port->remoteport->node;
407219820Sjeff			if (remnode->type != SWITCH_NODE) {
408219820Sjeff				if (!remnode->chrecord)
409219820Sjeff					get_router_slot(remnode, port);
410219820Sjeff				continue;
411219820Sjeff			}
412219820Sjeff			if (!ch->chassistype)
413219820Sjeff				/* we assume here that remoteport belongs to line */
414219820Sjeff				get_sfb_slot(node, port->remoteport);
415219820Sjeff
416219820Sjeff				/* we could break here, but need to find if more routers connected */
417219820Sjeff		}
418219820Sjeff
419219820Sjeff	} else if (is_line(node)) {
420219820Sjeff		for (port = node->ports; port; port = port->next) {
421219820Sjeff			if (port->portnum > 12)
422219820Sjeff				continue;
423219820Sjeff			if (!port->remoteport)
424219820Sjeff				continue;
425219820Sjeff			/* we assume here that remoteport belongs to spine */
426219820Sjeff			get_slb_slot(ch, port->remoteport);
427219820Sjeff			break;
428219820Sjeff		}
429219820Sjeff	}
430219820Sjeff
431219820Sjeff	return;
432219820Sjeff}
433219820Sjeff
434219820Sjeffstatic int get_line_index(Node *node)
435219820Sjeff{
436219820Sjeff	int retval = 3 * (node->chrecord->slotnum - 1) + node->chrecord->anafanum;
437219820Sjeff
438219820Sjeff	if (retval > LINES_MAX_NUM || retval < 1)
439219820Sjeff		IBPANIC("Internal error");
440219820Sjeff	return retval;
441219820Sjeff}
442219820Sjeff
443219820Sjeffstatic int get_spine_index(Node *node)
444219820Sjeff{
445219820Sjeff	int retval;
446219820Sjeff
447219820Sjeff	if (is_spine_9288(node) || is_spine_2012(node))
448219820Sjeff		retval = 3 * (node->chrecord->slotnum - 1) + node->chrecord->anafanum;
449219820Sjeff	else
450219820Sjeff		retval = node->chrecord->slotnum;
451219820Sjeff
452219820Sjeff	if (retval > SPINES_MAX_NUM || retval < 1)
453219820Sjeff		IBPANIC("Internal error");
454219820Sjeff	return retval;
455219820Sjeff}
456219820Sjeff
457219820Sjeffstatic void insert_line_router(Node *node, ChassisList *chassislist)
458219820Sjeff{
459219820Sjeff	int i = get_line_index(node);
460219820Sjeff
461219820Sjeff	if (chassislist->linenode[i])
462219820Sjeff		return;		/* already filled slot */
463219820Sjeff
464219820Sjeff	chassislist->linenode[i] = node;
465219820Sjeff	node->chrecord->chassisnum = chassislist->chassisnum;
466219820Sjeff}
467219820Sjeff
468219820Sjeffstatic void insert_spine(Node *node, ChassisList *chassislist)
469219820Sjeff{
470219820Sjeff	int i = get_spine_index(node);
471219820Sjeff
472219820Sjeff	if (chassislist->spinenode[i])
473219820Sjeff		return;		/* already filled slot */
474219820Sjeff
475219820Sjeff	chassislist->spinenode[i] = node;
476219820Sjeff	node->chrecord->chassisnum = chassislist->chassisnum;
477219820Sjeff}
478219820Sjeff
479219820Sjeffstatic void pass_on_lines_catch_spines(ChassisList *chassislist)
480219820Sjeff{
481219820Sjeff	Node *node, *remnode;
482219820Sjeff	Port *port;
483219820Sjeff	int i;
484219820Sjeff
485219820Sjeff	for (i = 1; i <= LINES_MAX_NUM; i++) {
486219820Sjeff		node = chassislist->linenode[i];
487219820Sjeff
488219820Sjeff		if (!(node && is_line(node)))
489219820Sjeff			continue;	/* empty slot or router */
490219820Sjeff
491219820Sjeff		for (port = node->ports; port; port = port->next) {
492219820Sjeff			if (port->portnum > 12)
493219820Sjeff				continue;
494219820Sjeff
495219820Sjeff			if (!port->remoteport)
496219820Sjeff				continue;
497219820Sjeff			remnode = port->remoteport->node;
498219820Sjeff
499219820Sjeff			if (!remnode->chrecord)
500219820Sjeff				continue;	/* some error - spine not initialized ? FIXME */
501219820Sjeff			insert_spine(remnode, chassislist);
502219820Sjeff		}
503219820Sjeff	}
504219820Sjeff}
505219820Sjeff
506219820Sjeffstatic void pass_on_spines_catch_lines(ChassisList *chassislist)
507219820Sjeff{
508219820Sjeff	Node *node, *remnode;
509219820Sjeff	Port *port;
510219820Sjeff	int i;
511219820Sjeff
512219820Sjeff	for (i = 1; i <= SPINES_MAX_NUM; i++) {
513219820Sjeff		node = chassislist->spinenode[i];
514219820Sjeff		if (!node)
515219820Sjeff			continue;	/* empty slot */
516219820Sjeff		for (port = node->ports; port; port = port->next) {
517219820Sjeff			if (!port->remoteport)
518219820Sjeff				continue;
519219820Sjeff			remnode = port->remoteport->node;
520219820Sjeff
521219820Sjeff			if (!remnode->chrecord)
522219820Sjeff				continue;	/* some error - line/router not initialized ? FIXME */
523219820Sjeff			insert_line_router(remnode, chassislist);
524219820Sjeff		}
525219820Sjeff	}
526219820Sjeff}
527219820Sjeff
528219820Sjeff/*
529219820Sjeff	Stupid interpolation algorithm...
530219820Sjeff	But nothing to do - have to be compliant with VoltaireSM/NMS
531219820Sjeff*/
532219820Sjeffstatic void pass_on_spines_interpolate_chguid(ChassisList *chassislist)
533219820Sjeff{
534219820Sjeff	Node *node;
535219820Sjeff	int i;
536219820Sjeff
537219820Sjeff	for (i = 1; i <= SPINES_MAX_NUM; i++) {
538219820Sjeff		node = chassislist->spinenode[i];
539219820Sjeff		if (!node)
540219820Sjeff			continue;	/* skip the empty slots */
541219820Sjeff
542219820Sjeff		/* take first guid minus one to be consistent with SM */
543219820Sjeff		chassislist->chassisguid = node->nodeguid - 1;
544219820Sjeff		break;
545219820Sjeff	}
546219820Sjeff}
547219820Sjeff
548219820Sjeff/*
549219820Sjeff	This function fills chassislist structure with all nodes
550219820Sjeff	in that chassis
551219820Sjeff	chassislist structure = structure of one standalone chassis
552219820Sjeff*/
553219820Sjeffstatic void build_chassis(Node *node, ChassisList *chassislist)
554219820Sjeff{
555219820Sjeff	Node *remnode = 0;
556219820Sjeff	Port *port = 0;
557219820Sjeff
558219820Sjeff	/* we get here with node = chassis_spine */
559219820Sjeff	chassislist->chassistype = node->chrecord->chassistype;
560219820Sjeff	insert_spine(node, chassislist);
561219820Sjeff
562219820Sjeff	/* loop: pass on all ports of node */
563219820Sjeff	for (port = node->ports; port; port = port->next) {
564219820Sjeff		if (!port->remoteport)
565219820Sjeff			continue;
566219820Sjeff		remnode = port->remoteport->node;
567219820Sjeff
568219820Sjeff		if (!remnode->chrecord)
569219820Sjeff			continue; /* some error - line or router not initialized ? FIXME */
570219820Sjeff
571219820Sjeff		insert_line_router(remnode, chassislist);
572219820Sjeff	}
573219820Sjeff
574219820Sjeff	pass_on_lines_catch_spines(chassislist);
575219820Sjeff	/* this pass needed for to catch routers, since routers connected only */
576219820Sjeff	/* to spines in slot 1 or 4 and we could miss them first time */
577219820Sjeff	pass_on_spines_catch_lines(chassislist);
578219820Sjeff
579219820Sjeff	/* additional 2 passes needed for to overcome a problem of pure "in-chassis" */
580219820Sjeff	/* connectivity - extra pass to ensure that all related chips/modules */
581219820Sjeff	/* inserted into the chassislist */
582219820Sjeff	pass_on_lines_catch_spines(chassislist);
583219820Sjeff	pass_on_spines_catch_lines(chassislist);
584219820Sjeff	pass_on_spines_interpolate_chguid(chassislist);
585219820Sjeff}
586219820Sjeff
587219820Sjeff/*========================================================*/
588219820Sjeff/*                INTERNAL TO EXTERNAL PORT MAPPING       */
589219820Sjeff/*========================================================*/
590219820Sjeff
591219820Sjeff/*
592219820SjeffDescription : On ISR9288/9096 external ports indexing
593219820Sjeff              is not matching the internal ( anafa ) port
594219820Sjeff              indexes. Use this MAP to translate the data you get from
595219820Sjeff              the OpenIB diagnostics (smpquery, ibroute, ibtracert, etc.)
596219820Sjeff
597219820Sjeff
598219820SjeffModule : sLB-24
599219820Sjeff                anafa 1             anafa 2
600219820Sjeffext port | 13 14 15 16 17 18 | 19 20 21 22 23 24
601219820Sjeffint port | 22 23 24 18 17 16 | 22 23 24 18 17 16
602219820Sjeffext port | 1  2  3  4  5  6  | 7  8  9  10 11 12
603219820Sjeffint port | 19 20 21 15 14 13 | 19 20 21 15 14 13
604219820Sjeff------------------------------------------------
605219820Sjeff
606219820SjeffModule : sLB-8
607219820Sjeff                anafa 1             anafa 2
608219820Sjeffext port | 13 14 15 16 17 18 | 19 20 21 22 23 24
609219820Sjeffint port | 24 23 22 18 17 16 | 24 23 22 18 17 16
610219820Sjeffext port | 1  2  3  4  5  6  | 7  8  9  10 11 12
611219820Sjeffint port | 21 20 19 15 14 13 | 21 20 19 15 14 13
612219820Sjeff
613219820Sjeff----------->
614219820Sjeff                anafa 1             anafa 2
615219820Sjeffext port | -  -  5  -  -  6  | -  -  7  -  -  8
616219820Sjeffint port | 24 23 22 18 17 16 | 24 23 22 18 17 16
617219820Sjeffext port | -  -  1  -  -  2  | -  -  3  -  -  4
618219820Sjeffint port | 21 20 19 15 14 13 | 21 20 19 15 14 13
619219820Sjeff------------------------------------------------
620219820Sjeff
621219820SjeffModule : sLB-2024
622219820Sjeff
623219820Sjeffext port | 13 14 15 16 17 18 19 20 21 22 23 24
624219820SjeffA1 int port| 13 14 15 16 17 18 19 20 21 22 23 24
625219820Sjeffext port | 1 2 3 4 5 6 7 8 9 10 11 12
626219820SjeffA2 int port| 13 14 15 16 17 18 19 20 21 22 23 24
627219820Sjeff---------------------------------------------------
628219820Sjeff
629219820Sjeff*/
630219820Sjeff
631219820Sjeffint int2ext_map_slb24[2][25] = {
632219820Sjeff					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, 4, 18, 17, 16, 1, 2, 3, 13, 14, 15 },
633219820Sjeff					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 11, 10, 24, 23, 22, 7, 8, 9, 19, 20, 21 }
634219820Sjeff				};
635219820Sjeffint int2ext_map_slb8[2][25] = {
636219820Sjeff					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 6, 6, 6, 1, 1, 1, 5, 5, 5 },
637219820Sjeff					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 8, 8, 8, 3, 3, 3, 7, 7, 7 }
638219820Sjeff				};
639219820Sjeffint int2ext_map_slb2024[2][25] = {
640219820Sjeff					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 },
641219820Sjeff					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }
642219820Sjeff				};
643219820Sjeff/*	reference			{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; */
644219820Sjeff
645219820Sjeff/*
646219820Sjeff	This function relevant only for line modules/chips
647219820Sjeff	Returns string with external port index
648219820Sjeff*/
649219820Sjeffchar *portmapstring(Port *port)
650219820Sjeff{
651219820Sjeff	static char mapping[OUT_BUFFER_SIZE];
652219820Sjeff	ChassisRecord *ch = port->node->chrecord;
653219820Sjeff	int portnum = port->portnum;
654219820Sjeff	int chipnum = 0;
655219820Sjeff	int pindex = 0;
656219820Sjeff	Node *node = port->node;
657219820Sjeff
658219820Sjeff	if (!ch || !is_line(node) || (portnum < 13 || portnum > 24))
659219820Sjeff		return NULL;
660219820Sjeff
661219820Sjeff	if (ch->anafanum < 1 || ch->anafanum > 2)
662219820Sjeff		return NULL;
663219820Sjeff
664219820Sjeff	memset(mapping, 0, sizeof(mapping));
665219820Sjeff
666219820Sjeff	chipnum = ch->anafanum - 1;
667219820Sjeff
668219820Sjeff	if (is_line_24(node))
669219820Sjeff		pindex = int2ext_map_slb24[chipnum][portnum];
670219820Sjeff	else if (is_line_2024(node))
671219820Sjeff		pindex = int2ext_map_slb2024[chipnum][portnum];
672219820Sjeff	else
673219820Sjeff		pindex = int2ext_map_slb8[chipnum][portnum];
674219820Sjeff
675219820Sjeff	sprintf(mapping, "[ext %d]", pindex);
676219820Sjeff
677219820Sjeff	return mapping;
678219820Sjeff}
679219820Sjeff
680219820Sjeffstatic void add_chassislist()
681219820Sjeff{
682219820Sjeff	if (!(mylist.current = calloc(1, sizeof(ChassisList))))
683219820Sjeff		IBPANIC("out of mem");
684219820Sjeff
685219820Sjeff	if (mylist.first == NULL) {
686219820Sjeff		mylist.first = mylist.current;
687219820Sjeff		mylist.last = mylist.current;
688219820Sjeff	} else {
689219820Sjeff		mylist.last->next = mylist.current;
690219820Sjeff		mylist.current->next = NULL;
691219820Sjeff		mylist.last = mylist.current;
692219820Sjeff	}
693219820Sjeff}
694219820Sjeff
695219820Sjeff/*
696219820Sjeff	Main grouping function
697219820Sjeff	Algorithm:
698219820Sjeff	1. pass on every Voltaire node
699219820Sjeff	2. catch spine chip for every Voltaire node
700219820Sjeff		2.1 build/interpolate chassis around this chip
701219820Sjeff		2.2 go to 1.
702219820Sjeff	3. pass on non Voltaire nodes (SystemImageGUID based grouping)
703219820Sjeff	4. now group non Voltaire nodes by SystemImageGUID
704219820Sjeff*/
705219820SjeffChassisList *group_nodes()
706219820Sjeff{
707219820Sjeff	Node *node;
708219820Sjeff	int dist;
709219820Sjeff	int chassisnum = 0;
710219820Sjeff	struct ChassisList *chassis;
711219820Sjeff
712219820Sjeff	mylist.first = NULL;
713219820Sjeff	mylist.current = NULL;
714219820Sjeff	mylist.last = NULL;
715219820Sjeff
716219820Sjeff	/* first pass on switches and build for every Voltaire node */
717219820Sjeff	/* an appropriate chassis record (slotnum and position) */
718219820Sjeff	/* according to internal connectivity */
719219820Sjeff	/* not very efficient but clear code so... */
720219820Sjeff	for (dist = 0; dist <= maxhops_discovered; dist++) {
721219820Sjeff		for (node = nodesdist[dist]; node; node = node->dnext) {
722219820Sjeff			if (node->vendid == VTR_VENDOR_ID)
723219820Sjeff				fill_chassis_record(node);
724219820Sjeff		}
725219820Sjeff	}
726219820Sjeff
727219820Sjeff	/* separate every Voltaire chassis from each other and build linked list of them */
728219820Sjeff	/* algorithm: catch spine and find all surrounding nodes */
729219820Sjeff	for (dist = 0; dist <= maxhops_discovered; dist++) {
730219820Sjeff		for (node = nodesdist[dist]; node; node = node->dnext) {
731219820Sjeff			if (node->vendid != VTR_VENDOR_ID)
732219820Sjeff				continue;
733219820Sjeff			if (!node->chrecord || node->chrecord->chassisnum || !is_spine(node))
734219820Sjeff				continue;
735219820Sjeff			add_chassislist();
736219820Sjeff			mylist.current->chassisnum = ++chassisnum;
737219820Sjeff			build_chassis(node, mylist.current);
738219820Sjeff		}
739219820Sjeff	}
740219820Sjeff
741219820Sjeff	/* now make pass on nodes for chassis which are not Voltaire */
742219820Sjeff	/* grouped by common SystemImageGUID */
743219820Sjeff	for (dist = 0; dist <= maxhops_discovered; dist++) {
744219820Sjeff		for (node = nodesdist[dist]; node; node = node->dnext) {
745219820Sjeff			if (node->vendid == VTR_VENDOR_ID)
746219820Sjeff				continue;
747219820Sjeff			if (node->sysimgguid) {
748219820Sjeff				chassis = find_chassisguid(node);
749219820Sjeff				if (chassis)
750219820Sjeff					chassis->nodecount++;
751219820Sjeff				else {
752219820Sjeff					/* Possible new chassis */
753219820Sjeff					add_chassislist();
754219820Sjeff					mylist.current->chassisguid = get_chassisguid(node);
755219820Sjeff					mylist.current->nodecount = 1;
756219820Sjeff				}
757219820Sjeff			}
758219820Sjeff		}
759219820Sjeff	}
760219820Sjeff
761219820Sjeff	/* now, make another pass to see which nodes are part of chassis */
762219820Sjeff	/* (defined as chassis->nodecount > 1) */
763219820Sjeff	for (dist = 0; dist <= MAXHOPS; ) {
764219820Sjeff		for (node = nodesdist[dist]; node; node = node->dnext) {
765219820Sjeff			if (node->vendid == VTR_VENDOR_ID)
766219820Sjeff				continue;
767219820Sjeff			if (node->sysimgguid) {
768219820Sjeff				chassis = find_chassisguid(node);
769219820Sjeff				if (chassis && chassis->nodecount > 1) {
770219820Sjeff					if (!chassis->chassisnum)
771219820Sjeff						chassis->chassisnum = ++chassisnum;
772219820Sjeff					if (!node->chrecord) {
773219820Sjeff						if (!(node->chrecord = calloc(1, sizeof(ChassisRecord))))
774219820Sjeff							IBPANIC("out of mem");
775219820Sjeff						node->chrecord->chassisnum = chassis->chassisnum;
776219820Sjeff					}
777219820Sjeff				}
778219820Sjeff			}
779219820Sjeff		}
780219820Sjeff		if (dist == maxhops_discovered)
781219820Sjeff			dist = MAXHOPS;	/* skip to CAs */
782219820Sjeff		else
783219820Sjeff			dist++;
784219820Sjeff	}
785219820Sjeff
786219820Sjeff	return (mylist.first);
787219820Sjeff}
788