1/*
2 * Copyright 2009 Sandia Corporation.  Under the terms of Contract
3 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
4 * certain rights in this software.
5 * Copyright (c) 2009-2011 ZIH, TU Dresden, Federal Republic of Germany. All rights reserved.
6 * Copyright (c) 2010-2012 Mellanox Technologies LTD. All rights reserved.
7 *
8 * This software is available to you under a choice of one of two
9 * licenses.  You may choose to be licensed under the terms of the GNU
10 * General Public License (GPL) Version 2, available from the file
11 * COPYING in the main directory of this source tree, or the
12 * OpenIB.org BSD license below:
13 *
14 *     Redistribution and use in source and binary forms, with or
15 *     without modification, are permitted provided that the following
16 *     conditions are met:
17 *
18 *      - Redistributions of source code must retain the above
19 *        copyright notice, this list of conditions and the following
20 *        disclaimer.
21 *
22 *      - Redistributions in binary form must reproduce the above
23 *        copyright notice, this list of conditions and the following
24 *        disclaimer in the documentation and/or other materials
25 *        provided with the distribution.
26 *
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
31 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
32 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34 * SOFTWARE.
35 *
36 */
37
38#define	_WITH_GETLINE	/* for getline() */
39#include <stdint.h>
40#include <stdbool.h>
41#include <stdlib.h>
42#include <stdio.h>
43#include <unistd.h>
44#include <errno.h>
45#include <string.h>
46
47#if HAVE_CONFIG_H
48#  include <config.h>
49#endif				/* HAVE_CONFIG_H */
50
51#include <opensm/osm_file_ids.h>
52#define FILE_ID OSM_FILE_TORUS_C
53#include <opensm/osm_log.h>
54#include <opensm/osm_port.h>
55#include <opensm/osm_switch.h>
56#include <opensm/osm_node.h>
57#include <opensm/osm_opensm.h>
58
59#define TORUS_MAX_DIM        3
60#define PORTGRP_MAX_PORTS    16
61#define SWITCH_MAX_PORTGRPS  (1 + 2 * TORUS_MAX_DIM)
62#define DEFAULT_MAX_CHANGES  32
63
64#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
65
66typedef ib_net64_t guid_t;
67
68/*
69 * An endpoint terminates a link, and is one of three types:
70 *   UNKNOWN  - Uninitialized endpoint.
71 *   SRCSINK  - generates or consumes traffic, and thus has an associated LID;
72 *		  i.e. a CA or router port.
73 *   PASSTHRU - Has no associated LID; i.e. a switch port.
74 *
75 * If it is possible to communicate in-band with a switch, it will require
76 * a port with a GUID in the switch to source/sink that traffic, but there
77 * will be no attached link.  This code assumes there is only one such port.
78 *
79 * Here is an endpoint taxonomy:
80 *
81 *   type == SRCSINK
82 *   link == pointer to a valid struct link
83 *     ==> This endpoint is a CA or router port connected via a link to
84 *	     either a switch or another CA/router.  Thus:
85 *	   n_id ==> identifies the CA/router node GUID
86 *	   sw   ==> NULL
87 *	   port ==> identifies the port on the CA/router this endpoint uses
88 *	   pgrp ==> NULL
89 *
90 *   type == SRCSINK
91 *   link == NULL pointer
92 *     ==> This endpoint is the switch port used for in-band communication
93 *	     with the switch itself.  Thus:
94 *	   n_id ==> identifies the node GUID used to talk to the switch
95 *		      containing this endpoint
96 *	   sw   ==> pointer to valid struct switch containing this endpoint
97 *	   port ==> identifies the port on the switch this endpoint uses
98 *	   pgrp ==> NULL, or pointer to the valid struct port_grp holding
99 *		      the port in a t_switch.
100 *
101 *   type == PASSTHRU
102 *   link == pointer to valid struct link
103 *     ==> This endpoint is a switch port connected via a link to either
104 *	     another switch or a CA/router.  Thus:
105 *	   n_id ==> identifies the node GUID used to talk to the switch
106 *		      containing this endpoint - since each switch is assumed
107 *		      to have only one in-band communication port, this is a
108 *		      convenient unique name for the switch itself.
109 *	   sw   ==> pointer to valid struct switch containing this endpoint,
110 *		      or NULL, in the case of a fabric link that has been
111 *		      disconnected after being transferred to a torus link.
112 *	   port ==> identifies the port on the switch this endpoint uses.
113 *		      Note that in the special case of the coordinate direction
114 *		      links, the port value is -1, as those links aren't
115 *		      really connected to anything.
116 *	   pgrp ==> NULL, or pointer to the valid struct port_grp holding
117 *		      the port in a t_switch.
118 */
119enum endpt_type { UNKNOWN = 0, SRCSINK, PASSTHRU };
120struct torus;
121struct t_switch;
122struct port_grp;
123
124struct endpoint {
125	enum endpt_type type;
126	int port;
127	guid_t n_id;		/* IBA node GUID */
128	void *sw;		/* void* can point to either switch type */
129	struct link *link;
130	struct port_grp *pgrp;
131	void *tmp;
132	/*
133	 * Note: osm_port is only guaranteed to contain a valid pointer
134	 * when the call stack contains torus_build_lfts() or
135	 * osm_port_relink_endpoint().
136	 *
137	 * Otherwise, the opensm core could have deleted an osm_port object
138	 * without notifying us, invalidating the pointer we hold.
139	 *
140	 * When presented with a pointer to an osm_port_t, it is generally
141	 * safe and required to cast osm_port_t:priv to struct endpoint, and
142	 * check that the endpoint's osm_port is the same as the original
143	 * osm_port_t pointer.  Failure to do so means that invalidated
144	 * pointers will go undetected.
145	 */
146	struct osm_port *osm_port;
147};
148
149struct link {
150	struct endpoint end[2];
151};
152
153/*
154 * A port group is a collection of endpoints on a switch that share certain
155 * characteristics.  All the endpoints in a port group must have the same
156 * type.  Furthermore, if that type is PASSTHRU, then the connected links:
157 *   1) are parallel to a given coordinate direction
158 *   2) share the same two switches as endpoints.
159 *
160 * Torus-2QoS uses one master spanning tree for multicast, of which every
161 * multicast group spanning tree is a subtree.  to_stree_root is a pointer
162 * to the next port_grp on the path to the master spanning tree root.
163 * to_stree_tip is a pointer to the next port_grp on the path to a master
164 * spanning tree branch tip.
165 *
166 * Each t_switch can have at most one port_grp with a non-NULL to_stree_root.
167 * Exactly one t_switch in the fabric will have all port_grp objects with
168 * to_stree_root NULL; it is the master spanning tree root.
169 *
170 * A t_switch with all port_grp objects where to_stree_tip is NULL is at a
171 * master spanning tree branch tip.
172 */
173struct port_grp {
174	enum endpt_type type;
175	size_t port_cnt;	/* number of attached ports in group */
176	size_t port_grp;	/* what switch port_grp we're in */
177	unsigned sw_dlid_cnt;	/* switch dlids routed through this group */
178	unsigned ca_dlid_cnt;	/* CA dlids routed through this group */
179	struct t_switch *sw;	/* what switch we're attached to */
180	struct port_grp *to_stree_root;
181	struct port_grp *to_stree_tip;
182	struct endpoint **port;
183};
184
185/*
186 * A struct t_switch is used to represent a switch as placed in a torus.
187 *
188 * A t_switch used to build an N-dimensional torus will have 2N+1 port groups,
189 * used as follows, assuming 0 <= d < N:
190 *   port_grp[2d]   => links leaving in negative direction for coordinate d
191 *   port_grp[2d+1] => links leaving in positive direction for coordinate d
192 *   port_grp[2N]   => endpoints local to switch; i.e., hosts on switch
193 *
194 * struct link objects referenced by a t_switch are assumed to be oriented:
195 * traversing a link from link.end[0] to link.end[1] is always in the positive
196 * coordinate direction.
197 */
198struct t_switch {
199	guid_t n_id;		/* IBA node GUID */
200	int i, j, k;
201	unsigned port_cnt;	/* including management port */
202	struct torus *torus;
203	void *tmp;
204	/*
205	 * Note: osm_switch is only guaranteed to contain a valid pointer
206	 * when the call stack contains torus_build_lfts().
207	 *
208	 * Otherwise, the opensm core could have deleted an osm_switch object
209	 * without notifying us, invalidating the pointer we hold.
210	 *
211	 * When presented with a pointer to an osm_switch_t, it is generally
212	 * safe and required to cast osm_switch_t:priv to struct t_switch, and
213	 * check that the switch's osm_switch is the same as the original
214	 * osm_switch_t pointer.  Failure to do so means that invalidated
215	 * pointers will go undetected.
216	 */
217	struct osm_switch *osm_switch;
218
219	struct port_grp ptgrp[SWITCH_MAX_PORTGRPS];
220	struct endpoint **port;
221};
222
223/*
224 * We'd like to be able to discover the torus topology in a pile of switch
225 * links if we can.  We'll use a struct f_switch to store raw topology for a
226 * fabric description, then contruct the torus topology from struct t_switch
227 * objects as we process the fabric and recover it.
228 */
229struct f_switch {
230	guid_t n_id;		/* IBA node GUID */
231	unsigned port_cnt;	/* including management port */
232	void *tmp;
233	/*
234	 * Same rules apply here as for a struct t_switch member osm_switch.
235	 */
236	struct osm_switch *osm_switch;
237	struct endpoint **port;
238};
239
240struct fabric {
241	osm_opensm_t *osm;
242	unsigned ca_cnt;
243	unsigned link_cnt;
244	unsigned switch_cnt;
245
246	unsigned link_cnt_max;
247	unsigned switch_cnt_max;
248
249	struct link **link;
250	struct f_switch **sw;
251};
252
253struct coord_dirs {
254	/*
255	 * These links define the coordinate directions for the torus.
256	 * They are duplicates of links connected to switches.  Each of
257	 * these links must connect to a common switch.
258	 *
259	 * In the event that a failed switch was specified as one of these
260	 * link endpoints, our algorithm would not be able to find the
261	 * torus in the fabric.  So, we'll allow multiple instances of
262	 * this in the config file to allow improved resiliency.
263	 */
264	struct link xm_link, ym_link, zm_link;
265	struct link xp_link, yp_link, zp_link;
266	/*
267	 * A torus dimension has coordinate values 0, 1, ..., radix - 1.
268	 * The dateline, where we need to change VLs to avoid credit loops,
269	 * for a torus dimension is always between coordinate values
270	 * radix - 1 and 0.  The following specify the dateline location
271	 * relative to the coordinate links shared switch location.
272	 *
273	 * E.g. if the shared switch is at 0,0,0, the following are all
274	 * zero; if the shared switch is at 1,1,1, the following are all
275	 * -1, etc.
276	 *
277	 * Since our SL/VL assignment for a path depends on the position
278	 * of the path endpoints relative to the torus datelines, we need
279	 * this information to keep SL/VL assignment constant in the event
280	 * one of the switches used to specify coordinate directions fails.
281	 */
282	int x_dateline, y_dateline, z_dateline;
283};
284
285struct torus {
286	osm_opensm_t *osm;
287	unsigned ca_cnt;
288	unsigned link_cnt;
289	unsigned switch_cnt;
290	unsigned seed_cnt, seed_idx;
291	unsigned x_sz, y_sz, z_sz;
292
293	unsigned port_order[IB_NODE_NUM_PORTS_MAX+1];
294
295	unsigned sw_pool_sz;
296	unsigned link_pool_sz;
297	unsigned seed_sz;
298	unsigned portgrp_sz;	/* max ports for port groups in this torus */
299
300	struct fabric *fabric;
301	struct t_switch **sw_pool;
302	struct link *link_pool;
303
304	struct coord_dirs *seed;
305	struct t_switch ****sw;
306	struct t_switch *master_stree_root;
307
308	unsigned flags;
309	unsigned max_changes;
310	int debug;
311};
312
313/*
314 * Bits to use in torus.flags
315 */
316#define X_MESH (1U << 0)
317#define Y_MESH (1U << 1)
318#define Z_MESH (1U << 2)
319#define MSG_DEADLOCK (1U << 29)
320#define NOTIFY_CHANGES (1U << 30)
321
322#define ALL_MESH(flags) \
323	((flags & (X_MESH | Y_MESH | Z_MESH)) == (X_MESH | Y_MESH | Z_MESH))
324
325
326struct torus_context {
327	osm_opensm_t *osm;
328	struct torus *torus;
329	struct fabric fabric;
330};
331
332static
333void teardown_fabric(struct fabric *f)
334{
335	unsigned l, p, s;
336	struct endpoint *port;
337	struct f_switch *sw;
338
339	if (!f)
340		return;
341
342	if (f->sw) {
343		/*
344		 * Need to free switches, and also find/free the endpoints
345		 * we allocated for switch management ports.
346		 */
347		for (s = 0; s < f->switch_cnt; s++) {
348			sw = f->sw[s];
349			if (!sw)
350				continue;
351
352			for (p = 0; p < sw->port_cnt; p++) {
353				port = sw->port[p];
354				if (port && !port->link)
355					free(port);	/* management port */
356			}
357			free(sw);
358		}
359		free(f->sw);
360	}
361	if (f->link) {
362		for (l = 0; l < f->link_cnt; l++)
363			if (f->link[l])
364				free(f->link[l]);
365
366		free(f->link);
367	}
368	memset(f, 0, sizeof(*f));
369}
370
371void teardown_torus(struct torus *t)
372{
373	unsigned p, s;
374	struct endpoint *port;
375	struct t_switch *sw;
376
377	if (!t)
378		return;
379
380	if (t->sw_pool) {
381		/*
382		 * Need to free switches, and also find/free the endpoints
383		 * we allocated for switch management ports.
384		 */
385		for (s = 0; s < t->switch_cnt; s++) {
386			sw = t->sw_pool[s];
387			if (!sw)
388				continue;
389
390			for (p = 0; p < sw->port_cnt; p++) {
391				port = sw->port[p];
392				if (port && !port->link)
393					free(port);	/* management port */
394			}
395			free(sw);
396		}
397		free(t->sw_pool);
398	}
399	if (t->link_pool)
400		free(t->link_pool);
401
402	if (t->sw)
403		free(t->sw);
404
405	if (t->seed)
406		free(t->seed);
407
408	free(t);
409}
410
411static
412struct torus_context *torus_context_create(osm_opensm_t *osm)
413{
414	struct torus_context *ctx;
415
416	ctx = calloc(1, sizeof(*ctx));
417	if (ctx)
418		ctx->osm = osm;
419	else
420		OSM_LOG(&osm->log, OSM_LOG_ERROR,
421			"ERR 4E01: calloc: %s\n", strerror(errno));
422
423	return ctx;
424}
425
426static
427void torus_context_delete(void *context)
428{
429	struct torus_context *ctx = context;
430
431	teardown_fabric(&ctx->fabric);
432	if (ctx->torus)
433		teardown_torus(ctx->torus);
434	free(ctx);
435}
436
437static
438bool grow_seed_array(struct torus *t, int new_seeds)
439{
440	unsigned cnt;
441	void *ptr;
442
443	cnt = t->seed_cnt + new_seeds;
444	if (cnt > t->seed_sz) {
445		cnt += 2 + cnt / 2;
446		ptr = realloc(t->seed, cnt * sizeof(*t->seed));
447		if (!ptr)
448			return false;
449		t->seed = ptr;
450		t->seed_sz = cnt;
451		memset(&t->seed[t->seed_cnt], 0,
452		       (cnt - t->seed_cnt) * sizeof(*t->seed));
453	}
454	return true;
455}
456
457static
458struct f_switch *find_f_sw(struct fabric *f, guid_t sw_guid)
459{
460	unsigned s;
461	struct f_switch *sw;
462
463	if (f->sw) {
464		for (s = 0; s < f->switch_cnt; s++) {
465			sw = f->sw[s];
466			if (sw->n_id == sw_guid)
467				return sw;
468		}
469	}
470	return NULL;
471}
472
473static
474struct link *find_f_link(struct fabric *f,
475			 guid_t guid0, int port0, guid_t guid1, int port1)
476{
477	unsigned l;
478	struct link *link;
479
480	if (f->link) {
481		for (l = 0; l < f->link_cnt; l++) {
482			link = f->link[l];
483			if ((link->end[0].n_id == guid0 &&
484			     link->end[0].port == port0 &&
485			     link->end[1].n_id == guid1 &&
486			     link->end[1].port == port1) ||
487			    (link->end[0].n_id == guid1 &&
488			     link->end[0].port == port1 &&
489			     link->end[1].n_id == guid0 &&
490			     link->end[1].port == port0))
491				return link;
492		}
493	}
494	return NULL;
495}
496
497static
498struct f_switch *alloc_fswitch(struct fabric *f,
499			       guid_t sw_id, unsigned port_cnt)
500{
501	size_t new_sw_sz;
502	unsigned cnt_max;
503	struct f_switch *sw = NULL;
504	void *ptr;
505
506	if (f->switch_cnt >= f->switch_cnt_max) {
507
508		cnt_max = 16 + 5 * f->switch_cnt_max / 4;
509		ptr = realloc(f->sw, cnt_max * sizeof(*f->sw));
510		if (!ptr) {
511			OSM_LOG(&f->osm->log, OSM_LOG_ERROR,
512				"ERR 4E02: realloc: %s\n", strerror(errno));
513			goto out;
514		}
515		f->sw = ptr;
516		f->switch_cnt_max = cnt_max;
517		memset(&f->sw[f->switch_cnt], 0,
518		       (f->switch_cnt_max - f->switch_cnt)*sizeof(*f->sw));
519	}
520	new_sw_sz = sizeof(*sw) + port_cnt * sizeof(*sw->port);
521	sw = calloc(1, new_sw_sz);
522	if (!sw) {
523		OSM_LOG(&f->osm->log, OSM_LOG_ERROR,
524			"ERR 4E03: calloc: %s\n", strerror(errno));
525		goto out;
526	}
527	sw->port = (void *)(sw + 1);
528	sw->n_id = sw_id;
529	sw->port_cnt = port_cnt;
530	f->sw[f->switch_cnt++] = sw;
531out:
532	return sw;
533}
534
535static
536struct link *alloc_flink(struct fabric *f)
537{
538	unsigned cnt_max;
539	struct link *l = NULL;
540	void *ptr;
541
542	if (f->link_cnt >= f->link_cnt_max) {
543
544		cnt_max = 16 + 5 * f->link_cnt_max / 4;
545		ptr = realloc(f->link, cnt_max * sizeof(*f->link));
546		if (!ptr) {
547			OSM_LOG(&f->osm->log, OSM_LOG_ERROR,
548				"ERR 4E04: realloc: %s\n", strerror(errno));
549			goto out;
550		}
551		f->link = ptr;
552		f->link_cnt_max = cnt_max;
553		memset(&f->link[f->link_cnt], 0,
554		       (f->link_cnt_max - f->link_cnt) * sizeof(*f->link));
555	}
556	l = calloc(1, sizeof(*l));
557	if (!l) {
558		OSM_LOG(&f->osm->log, OSM_LOG_ERROR,
559			"ERR 4E05: calloc: %s\n", strerror(errno));
560		goto out;
561	}
562	f->link[f->link_cnt++] = l;
563out:
564	return l;
565}
566
567/*
568 * Caller must ensure osm_port points to a valid port which contains
569 * a valid osm_physp_t pointer for port 0, the switch management port.
570 */
571static
572bool build_sw_endpoint(struct fabric *f, osm_port_t *osm_port)
573{
574	int sw_port;
575	guid_t sw_guid;
576	struct osm_switch *osm_sw;
577	struct f_switch *sw;
578	struct endpoint *ep;
579	bool success = false;
580
581	sw_port = osm_physp_get_port_num(osm_port->p_physp);
582	sw_guid = osm_node_get_node_guid(osm_port->p_node);
583	osm_sw = osm_port->p_node->sw;
584
585	/*
586	 * The switch must already exist.
587	 */
588	sw = find_f_sw(f, sw_guid);
589	if (!sw) {
590		OSM_LOG(&f->osm->log, OSM_LOG_ERROR,
591			"ERR 4E06: missing switch w/GUID 0x%04"PRIx64"\n",
592			cl_ntoh64(sw_guid));
593		goto out;
594	}
595	/*
596	 * The endpoint may already exist.
597	 */
598	if (sw->port[sw_port]) {
599		if (sw->port[sw_port]->n_id == sw_guid) {
600			ep = sw->port[sw_port];
601			goto success;
602		} else
603			OSM_LOG(&f->osm->log, OSM_LOG_ERROR,
604				"ERR 4E07: switch port %d has id "
605				"0x%04"PRIx64", expected 0x%04"PRIx64"\n",
606				sw_port, cl_ntoh64(sw->port[sw_port]->n_id),
607				cl_ntoh64(sw_guid));
608		goto out;
609	}
610	ep = calloc(1, sizeof(*ep));
611	if (!ep) {
612		OSM_LOG(&f->osm->log, OSM_LOG_ERROR,
613			"ERR 4E08: allocating endpoint: %s\n", strerror(errno));
614		goto out;
615	}
616	ep->type = SRCSINK;
617	ep->port = sw_port;
618	ep->n_id = sw_guid;
619	ep->link = NULL;
620	ep->sw = sw;
621
622	sw->port[sw_port] = ep;
623
624success:
625	/*
626	 * Fabric objects are temporary, so don't set osm_sw/osm_port priv
627	 * pointers using them.  Wait until torus objects get constructed.
628	 */
629	sw->osm_switch = osm_sw;
630	ep->osm_port = osm_port;
631
632	success = true;
633out:
634	return success;
635}
636
637static
638bool build_ca_link(struct fabric *f,
639		   osm_port_t *osm_port_ca, guid_t sw_guid, int sw_port)
640{
641	int ca_port;
642	guid_t ca_guid;
643	struct link *l;
644	struct f_switch *sw;
645	bool success = false;
646
647	ca_port = osm_physp_get_port_num(osm_port_ca->p_physp);
648	ca_guid = osm_node_get_node_guid(osm_port_ca->p_node);
649
650	/*
651	 * The link may already exist.
652	 */
653	l = find_f_link(f, sw_guid, sw_port, ca_guid, ca_port);
654	if (l) {
655		success = true;
656		goto out;
657	}
658	/*
659	 * The switch must already exist.
660	 */
661	sw = find_f_sw(f, sw_guid);
662	if (!sw) {
663		OSM_LOG(&f->osm->log, OSM_LOG_ERROR,
664			"ERR 4E09: missing switch w/GUID 0x%04"PRIx64"\n",
665			cl_ntoh64(sw_guid));
666		goto out;
667	}
668	l = alloc_flink(f);
669	if (!l)
670		goto out;
671
672	l->end[0].type = PASSTHRU;
673	l->end[0].port = sw_port;
674	l->end[0].n_id = sw_guid;
675	l->end[0].sw = sw;
676	l->end[0].link = l;
677
678	sw->port[sw_port] = &l->end[0];
679
680	l->end[1].type = SRCSINK;
681	l->end[1].port = ca_port;
682	l->end[1].n_id = ca_guid;
683	l->end[1].sw = NULL;		/* Correct for a CA */
684	l->end[1].link = l;
685
686	/*
687	 * Fabric objects are temporary, so don't set osm_sw/osm_port priv
688	 * pointers using them.  Wait until torus objects get constructed.
689	 */
690	l->end[1].osm_port = osm_port_ca;
691
692	++f->ca_cnt;
693	success = true;
694out:
695	return success;
696}
697
698static
699bool build_link(struct fabric *f,
700		guid_t sw_guid0, int sw_port0, guid_t sw_guid1, int sw_port1)
701{
702	struct link *l;
703	struct f_switch *sw0, *sw1;
704	bool success = false;
705
706	/*
707	 * The link may already exist.
708	 */
709	l = find_f_link(f, sw_guid0, sw_port0, sw_guid1, sw_port1);
710	if (l) {
711		success = true;
712		goto out;
713	}
714	/*
715	 * The switches must already exist.
716	 */
717	sw0 = find_f_sw(f, sw_guid0);
718	if (!sw0) {
719		OSM_LOG(&f->osm->log, OSM_LOG_ERROR,
720			"ERR 4E0A: missing switch w/GUID 0x%04"PRIx64"\n",
721			cl_ntoh64(sw_guid0));
722		goto out;
723	}
724	sw1 = find_f_sw(f, sw_guid1);
725	if (!sw1) {
726		OSM_LOG(&f->osm->log, OSM_LOG_ERROR,
727			"ERR 4E0B: missing switch w/GUID 0x%04"PRIx64"\n",
728			cl_ntoh64(sw_guid1));
729		goto out;
730	}
731	l = alloc_flink(f);
732	if (!l)
733		goto out;
734
735	l->end[0].type = PASSTHRU;
736	l->end[0].port = sw_port0;
737	l->end[0].n_id = sw_guid0;
738	l->end[0].sw = sw0;
739	l->end[0].link = l;
740
741	sw0->port[sw_port0] = &l->end[0];
742
743	l->end[1].type = PASSTHRU;
744	l->end[1].port = sw_port1;
745	l->end[1].n_id = sw_guid1;
746	l->end[1].sw = sw1;
747	l->end[1].link = l;
748
749	sw1->port[sw_port1] = &l->end[1];
750
751	success = true;
752out:
753	return success;
754}
755
756static
757bool parse_size(unsigned *tsz, unsigned *tflags, unsigned mask,
758		const char *parse_sep)
759{
760	char *val, *nextchar;
761
762	val = strtok(NULL, parse_sep);
763	if (!val)
764		return false;
765	*tsz = strtoul(val, &nextchar, 0);
766	if (*tsz) {
767		if (*nextchar == 't' || *nextchar == 'T')
768			*tflags &= ~mask;
769		else if (*nextchar == 'm' || *nextchar == 'M')
770			*tflags |= mask;
771		/*
772		 * A torus of radix two is also a mesh of radix two
773		 * with multiple links between switches in that direction.
774		 *
775		 * Make it so always, otherwise the failure case routing
776		 * logic gets confused.
777		 */
778		if (*tsz == 2)
779			*tflags |= mask;
780	}
781	return true;
782}
783
784static
785bool parse_torus(struct torus *t, const char *parse_sep)
786{
787	unsigned i, j, k, cnt;
788	char *ptr;
789	bool success = false;
790
791	/*
792	 * There can be only one.  Ignore the imposters.
793	 */
794	if (t->sw_pool)
795		goto out;
796
797	if (!parse_size(&t->x_sz, &t->flags, X_MESH, parse_sep))
798		goto out;
799
800	if (!parse_size(&t->y_sz, &t->flags, Y_MESH, parse_sep))
801		goto out;
802
803	if (!parse_size(&t->z_sz, &t->flags, Z_MESH, parse_sep))
804		goto out;
805
806	/*
807	 * Set up a linear array of switch pointers big enough to hold
808	 * all expected switches.
809	 */
810	t->sw_pool_sz = t->x_sz * t->y_sz * t->z_sz;
811	t->sw_pool = calloc(t->sw_pool_sz, sizeof(*t->sw_pool));
812	if (!t->sw_pool) {
813		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
814			"ERR 4E0C: Torus switch array calloc: %s\n",
815			strerror(errno));
816		goto out;
817	}
818	/*
819	 * Set things up so that t->sw[i][j][k] can point to the i,j,k switch.
820	 */
821	cnt = t->x_sz * (1 + t->y_sz * (1 + t->z_sz));
822	t->sw = malloc(cnt * sizeof(void *));
823	if (!t->sw) {
824		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
825			"ERR 4E0D: Torus switch array malloc: %s\n",
826			strerror(errno));
827		goto out;
828	}
829	ptr = (void *)(t->sw);
830
831	ptr += t->x_sz * sizeof(void *);
832	for (i = 0; i < t->x_sz; i++) {
833		t->sw[i] = (void *)ptr;
834		ptr += t->y_sz * sizeof(void *);
835	}
836	for (i = 0; i < t->x_sz; i++)
837		for (j = 0; j < t->y_sz; j++) {
838			t->sw[i][j] = (void *)ptr;
839			ptr += t->z_sz * sizeof(void *);
840		}
841
842	for (i = 0; i < t->x_sz; i++)
843		for (j = 0; j < t->y_sz; j++)
844			for (k = 0; k < t->z_sz; k++)
845				t->sw[i][j][k] = NULL;
846
847	success = true;
848out:
849	return success;
850}
851
852static
853bool parse_unsigned(unsigned *result, const char *parse_sep)
854{
855	char *val, *nextchar;
856
857	val = strtok(NULL, parse_sep);
858	if (!val)
859		return false;
860	*result = strtoul(val, &nextchar, 0);
861	return true;
862}
863
864static
865bool parse_port_order(struct torus *t, const char *parse_sep)
866{
867	unsigned i, j, k, n;
868
869	for (i = 0; i < ARRAY_SIZE(t->port_order); i++) {
870		if (!parse_unsigned(&(t->port_order[i]), parse_sep))
871			break;
872
873		for (j = 0; j < i; j++) {
874			if (t->port_order[j] == t->port_order[i]) {
875				OSM_LOG(&t->osm->log, OSM_LOG_INFO,
876					"Ignored duplicate port %u in"
877					" port_order parsing\n",
878					t->port_order[j]);
879				i--;	/* Ignore duplicate port number */
880				break;
881			}
882		}
883	}
884
885	n = i;
886	for (j = 0; j < ARRAY_SIZE(t->port_order); j++) {
887		for (k = 0; k < i; k++)
888			if (t->port_order[k] == j)
889				break;
890		if (k >= i)
891			t->port_order[n++] = j;
892	}
893
894	return true;
895}
896
897static
898bool parse_guid(struct torus *t, guid_t *guid, const char *parse_sep)
899{
900	char *val;
901	bool success = false;
902
903	val = strtok(NULL, parse_sep);
904	if (!val)
905		goto out;
906	*guid = strtoull(val, NULL, 0);
907	*guid = cl_hton64(*guid);
908
909	success = true;
910out:
911	return success;
912}
913
914static
915bool parse_dir_link(int c_dir, struct torus *t, const char *parse_sep)
916{
917	guid_t sw_guid0, sw_guid1;
918	struct link *l;
919	bool success = false;
920
921	if (!parse_guid(t, &sw_guid0, parse_sep))
922		goto out;
923
924	if (!parse_guid(t, &sw_guid1, parse_sep))
925		goto out;
926
927	if (!t) {
928		success = true;
929		goto out;
930	}
931
932	switch (c_dir) {
933	case -1:
934		l = &t->seed[t->seed_cnt - 1].xm_link;
935		break;
936	case  1:
937		l = &t->seed[t->seed_cnt - 1].xp_link;
938		break;
939	case -2:
940		l = &t->seed[t->seed_cnt - 1].ym_link;
941		break;
942	case  2:
943		l = &t->seed[t->seed_cnt - 1].yp_link;
944		break;
945	case -3:
946		l = &t->seed[t->seed_cnt - 1].zm_link;
947		break;
948	case  3:
949		l = &t->seed[t->seed_cnt - 1].zp_link;
950		break;
951	default:
952		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
953			"ERR 4E0E: unknown link direction %d\n", c_dir);
954		goto out;
955	}
956	l->end[0].type = PASSTHRU;
957	l->end[0].port = -1;		/* We don't really connect. */
958	l->end[0].n_id = sw_guid0;
959	l->end[0].sw = NULL;		/* Fix this up later. */
960	l->end[0].link = NULL;		/* Fix this up later. */
961
962	l->end[1].type = PASSTHRU;
963	l->end[1].port = -1;		/* We don't really connect. */
964	l->end[1].n_id = sw_guid1;
965	l->end[1].sw = NULL;		/* Fix this up later. */
966	l->end[1].link = NULL;		/* Fix this up later. */
967
968	success = true;
969out:
970	return success;
971}
972
973static
974bool parse_dir_dateline(int c_dir, struct torus *t, const char *parse_sep)
975{
976	char *val;
977	int *dl, max_dl;
978	bool success = false;
979
980	val = strtok(NULL, parse_sep);
981	if (!val)
982		goto out;
983
984	if (!t) {
985		success = true;
986		goto out;
987	}
988
989	switch (c_dir) {
990	case  1:
991		dl = &t->seed[t->seed_cnt - 1].x_dateline;
992		max_dl = t->x_sz;
993		break;
994	case  2:
995		dl = &t->seed[t->seed_cnt - 1].y_dateline;
996		max_dl = t->y_sz;
997		break;
998	case  3:
999		dl = &t->seed[t->seed_cnt - 1].z_dateline;
1000		max_dl = t->z_sz;
1001		break;
1002	default:
1003		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1004			"ERR 4E0F: unknown dateline direction %d\n", c_dir);
1005		goto out;
1006	}
1007	*dl = strtol(val, NULL, 0);
1008
1009	if ((*dl < 0 && *dl <= -max_dl) || *dl >= max_dl)
1010		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1011			"ERR 4E10: dateline value for coordinate direction %d "
1012			"must be %d < dl < %d\n",
1013			c_dir, -max_dl, max_dl);
1014	else
1015		success = true;
1016out:
1017	return success;
1018}
1019
1020static
1021bool parse_config(const char *fn, struct fabric *f, struct torus *t)
1022{
1023	FILE *fp;
1024	unsigned i;
1025	char *keyword;
1026	char *line_buf = NULL;
1027	const char *parse_sep = " \n\t\015";
1028	size_t line_buf_sz = 0;
1029	size_t line_cntr = 0;
1030	ssize_t llen;
1031	bool kw_success, success = true;
1032
1033	if (!grow_seed_array(t, 2))
1034		return false;
1035
1036	for (i = 0; i < ARRAY_SIZE(t->port_order); i++)
1037		t->port_order[i] = i;
1038
1039	fp = fopen(fn, "r");
1040	if (!fp) {
1041		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1042			"ERR 4E11: Opening %s: %s\n", fn, strerror(errno));
1043		return false;
1044	}
1045	t->flags |= NOTIFY_CHANGES;
1046	t->portgrp_sz = PORTGRP_MAX_PORTS;
1047	t->max_changes = DEFAULT_MAX_CHANGES;
1048
1049next_line:
1050	llen = getline(&line_buf, &line_buf_sz, fp);
1051	if (llen < 0)
1052		goto out;
1053
1054	++line_cntr;
1055
1056	keyword = strtok(line_buf, parse_sep);
1057	if (!keyword)
1058		goto next_line;
1059
1060	if (strcmp("torus", keyword) == 0) {
1061		kw_success = parse_torus(t, parse_sep);
1062	} else if (strcmp("mesh", keyword) == 0) {
1063		t->flags |= X_MESH | Y_MESH | Z_MESH;
1064		kw_success = parse_torus(t, parse_sep);
1065	} else if (strcmp("port_order", keyword) == 0) {
1066		kw_success = parse_port_order(t, parse_sep);
1067	} else if (strcmp("next_seed", keyword) == 0) {
1068		kw_success = grow_seed_array(t, 1);
1069		t->seed_cnt++;
1070	} else if (strcmp("portgroup_max_ports", keyword) == 0) {
1071		kw_success = parse_unsigned(&t->portgrp_sz, parse_sep);
1072	} else if (strcmp("xp_link", keyword) == 0) {
1073		if (!t->seed_cnt)
1074			t->seed_cnt++;
1075		kw_success = parse_dir_link(1, t, parse_sep);
1076	} else if (strcmp("xm_link", keyword) == 0) {
1077		if (!t->seed_cnt)
1078			t->seed_cnt++;
1079		kw_success = parse_dir_link(-1, t, parse_sep);
1080	} else if (strcmp("x_dateline", keyword) == 0) {
1081		if (!t->seed_cnt)
1082			t->seed_cnt++;
1083		kw_success = parse_dir_dateline(1, t, parse_sep);
1084	} else if (strcmp("yp_link", keyword) == 0) {
1085		if (!t->seed_cnt)
1086			t->seed_cnt++;
1087		kw_success = parse_dir_link(2, t, parse_sep);
1088	} else if (strcmp("ym_link", keyword) == 0) {
1089		if (!t->seed_cnt)
1090			t->seed_cnt++;
1091		kw_success = parse_dir_link(-2, t, parse_sep);
1092	} else if (strcmp("y_dateline", keyword) == 0) {
1093		if (!t->seed_cnt)
1094			t->seed_cnt++;
1095		kw_success = parse_dir_dateline(2, t, parse_sep);
1096	} else if (strcmp("zp_link", keyword) == 0) {
1097		if (!t->seed_cnt)
1098			t->seed_cnt++;
1099		kw_success = parse_dir_link(3, t, parse_sep);
1100	} else if (strcmp("zm_link", keyword) == 0) {
1101		if (!t->seed_cnt)
1102			t->seed_cnt++;
1103		kw_success = parse_dir_link(-3, t, parse_sep);
1104	} else if (strcmp("z_dateline", keyword) == 0) {
1105		if (!t->seed_cnt)
1106			t->seed_cnt++;
1107		kw_success = parse_dir_dateline(3, t, parse_sep);
1108	} else if (strcmp("max_changes", keyword) == 0) {
1109		kw_success = parse_unsigned(&t->max_changes, parse_sep);
1110	} else if (keyword[0] == '#')
1111		goto next_line;
1112	else {
1113		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1114			"ERR 4E12: no keyword found: line %u\n",
1115			(unsigned)line_cntr);
1116		kw_success = false;
1117	}
1118	if (!kw_success) {
1119		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1120			"ERR 4E13: parsing '%s': line %u\n",
1121			keyword, (unsigned)line_cntr);
1122	}
1123	success = success && kw_success;
1124	goto next_line;
1125
1126out:
1127	if (line_buf)
1128		free(line_buf);
1129	fclose(fp);
1130	return success;
1131}
1132
1133static
1134bool capture_fabric(struct fabric *fabric)
1135{
1136	osm_subn_t *subnet = &fabric->osm->subn;
1137	osm_switch_t *osm_sw;
1138	osm_physp_t *lphysp, *rphysp;
1139	osm_port_t *lport;
1140	osm_node_t *osm_node;
1141	cl_map_item_t *item;
1142	uint8_t ltype, rtype;
1143	int p, port_cnt;
1144	guid_t sw_guid;
1145	bool success = true;
1146
1147	OSM_LOG_ENTER(&fabric->osm->log);
1148
1149	/*
1150	 * On OpenSM data structures:
1151	 *
1152	 * Apparently, every port in a fabric has an associated osm_physp_t,
1153	 * but not every port has an associated osm_port_t.  Apparently every
1154	 * osm_port_t has an associated osm_physp_t.
1155	 *
1156	 * So, in order to find the inter-switch links we need to walk the
1157	 * switch list and examine each port, via its osm_physp_t object.
1158	 *
1159	 * But, we need to associate our CA and switch management port
1160	 * endpoints with the corresponding osm_port_t objects, in order
1161	 * to simplify computation of LFT entries and perform SL lookup for
1162	 * path records. Since it is apparently difficult to locate the
1163	 * osm_port_t that corresponds to a given osm_physp_t, we also
1164	 * need to walk the list of ports indexed by GUID to get access
1165	 * to the appropriate osm_port_t objects.
1166	 *
1167	 * Need to allocate our switches before we do anything else.
1168	 */
1169	item = cl_qmap_head(&subnet->sw_guid_tbl);
1170	while (item != cl_qmap_end(&subnet->sw_guid_tbl)) {
1171
1172		osm_sw = (osm_switch_t *)item;
1173		item = cl_qmap_next(item);
1174		osm_sw->priv = NULL;  /* avoid stale pointer dereferencing */
1175		osm_node = osm_sw->p_node;
1176
1177		if (osm_node_get_type(osm_node) != IB_NODE_TYPE_SWITCH)
1178			continue;
1179
1180		port_cnt = osm_node_get_num_physp(osm_node);
1181		sw_guid = osm_node_get_node_guid(osm_node);
1182
1183		success = alloc_fswitch(fabric, sw_guid, port_cnt);
1184		if (!success)
1185			goto out;
1186	}
1187	/*
1188	 * Now build all our endpoints.
1189	 */
1190	item = cl_qmap_head(&subnet->port_guid_tbl);
1191	while (item != cl_qmap_end(&subnet->port_guid_tbl)) {
1192
1193		lport = (osm_port_t *)item;
1194		item = cl_qmap_next(item);
1195		lport->priv = NULL;  /* avoid stale pointer dereferencing */
1196
1197		lphysp = lport->p_physp;
1198		if (!(lphysp && osm_physp_is_valid(lphysp)))
1199			continue;
1200
1201		ltype = osm_node_get_type(lphysp->p_node);
1202		/*
1203		 * Switch management port is always port 0.
1204		 */
1205		if (lphysp->port_num == 0 && ltype == IB_NODE_TYPE_SWITCH) {
1206			success = build_sw_endpoint(fabric, lport);
1207			if (!success)
1208				goto out;
1209			continue;
1210		}
1211		rphysp = lphysp->p_remote_physp;
1212		if (!(rphysp && osm_physp_is_valid(rphysp)))
1213			continue;
1214
1215		rtype = osm_node_get_type(rphysp->p_node);
1216
1217		if ((ltype != IB_NODE_TYPE_CA &&
1218		     ltype != IB_NODE_TYPE_ROUTER) ||
1219		    rtype != IB_NODE_TYPE_SWITCH)
1220			continue;
1221
1222		success =
1223			build_ca_link(fabric, lport,
1224				      osm_node_get_node_guid(rphysp->p_node),
1225				      osm_physp_get_port_num(rphysp));
1226		if (!success)
1227			goto out;
1228	}
1229	/*
1230	 * Lastly, build all our interswitch links.
1231	 */
1232	item = cl_qmap_head(&subnet->sw_guid_tbl);
1233	while (item != cl_qmap_end(&subnet->sw_guid_tbl)) {
1234
1235		osm_sw = (osm_switch_t *)item;
1236		item = cl_qmap_next(item);
1237
1238		port_cnt = osm_node_get_num_physp(osm_sw->p_node);
1239		for (p = 0; p < port_cnt; p++) {
1240
1241			lphysp = osm_node_get_physp_ptr(osm_sw->p_node, p);
1242			if (!(lphysp && osm_physp_is_valid(lphysp)))
1243				continue;
1244
1245			rphysp = lphysp->p_remote_physp;
1246			if (!(rphysp && osm_physp_is_valid(rphysp)))
1247				continue;
1248
1249			if (lphysp == rphysp)
1250				continue;	/* ignore loopbacks */
1251
1252			ltype = osm_node_get_type(lphysp->p_node);
1253			rtype = osm_node_get_type(rphysp->p_node);
1254
1255			if (ltype != IB_NODE_TYPE_SWITCH ||
1256			    rtype != IB_NODE_TYPE_SWITCH)
1257				continue;
1258
1259			success =
1260				build_link(fabric,
1261					   osm_node_get_node_guid(lphysp->p_node),
1262					   osm_physp_get_port_num(lphysp),
1263					   osm_node_get_node_guid(rphysp->p_node),
1264					   osm_physp_get_port_num(rphysp));
1265			if (!success)
1266				goto out;
1267		}
1268	}
1269out:
1270	OSM_LOG_EXIT(&fabric->osm->log);
1271	return success;
1272}
1273
1274/*
1275 * diagnose_fabric() is just intended to report on fabric elements that
1276 * could not be placed into the torus.  We want to warn that there were
1277 * non-torus fabric elements, but they will be ignored for routing purposes.
1278 * Having them is not an error, and diagnose_fabric() thus has no return
1279 * value.
1280 */
1281static
1282void diagnose_fabric(struct fabric *f)
1283{
1284	struct link *l;
1285	struct endpoint *ep;
1286	unsigned k, p;
1287
1288	/*
1289	 * Report on any links that didn't get transferred to the torus.
1290	 */
1291	for (k = 0; k < f->link_cnt; k++) {
1292		l = f->link[k];
1293
1294		if (!(l->end[0].sw && l->end[1].sw))
1295			continue;
1296
1297		OSM_LOG(&f->osm->log, OSM_LOG_INFO,
1298			"Found non-torus fabric link:"
1299			" sw GUID 0x%04"PRIx64" port %d <->"
1300			" sw GUID 0x%04"PRIx64" port %d\n",
1301			cl_ntoh64(l->end[0].n_id), l->end[0].port,
1302			cl_ntoh64(l->end[1].n_id), l->end[1].port);
1303	}
1304	/*
1305	 * Report on any switches with ports using endpoints that didn't
1306	 * get transferred to the torus.
1307	 */
1308	for (k = 0; k < f->switch_cnt; k++)
1309		for (p = 0; p < f->sw[k]->port_cnt; p++) {
1310
1311			if (!f->sw[k]->port[p])
1312				continue;
1313
1314			ep = f->sw[k]->port[p];
1315
1316			/*
1317			 * We already reported on inter-switch links above.
1318			 */
1319			if (ep->type == PASSTHRU)
1320				continue;
1321
1322			OSM_LOG(&f->osm->log, OSM_LOG_INFO,
1323				"Found non-torus fabric port:"
1324				" sw GUID 0x%04"PRIx64" port %d\n",
1325				cl_ntoh64(f->sw[k]->n_id), p);
1326		}
1327}
1328
1329static
1330struct t_switch *alloc_tswitch(struct torus *t, struct f_switch *fsw)
1331{
1332	unsigned g;
1333	size_t new_sw_sz;
1334	struct t_switch *sw = NULL;
1335	void *ptr;
1336
1337	if (!fsw)
1338		goto out;
1339
1340	if (t->switch_cnt >= t->sw_pool_sz) {
1341		/*
1342		 * This should never happen, but occasionally a particularly
1343		 * pathological fabric can induce it.  So log an error.
1344		 */
1345		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1346			"ERR 4E14: unexpectedly requested too many switch "
1347			"structures!\n");
1348		goto out;
1349	}
1350	new_sw_sz = sizeof(*sw)
1351		+ fsw->port_cnt * sizeof(*sw->port)
1352		+ SWITCH_MAX_PORTGRPS * t->portgrp_sz * sizeof(*sw->ptgrp[0].port);
1353	sw = calloc(1, new_sw_sz);
1354	if (!sw) {
1355		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1356			"ERR 4E15: calloc: %s\n", strerror(errno));
1357		goto out;
1358	}
1359	sw->port = (void *)(sw + 1);
1360	sw->n_id = fsw->n_id;
1361	sw->port_cnt = fsw->port_cnt;
1362	sw->torus = t;
1363	sw->tmp = fsw;
1364
1365	ptr = &sw->port[sw->port_cnt];
1366
1367	for (g = 0; g < SWITCH_MAX_PORTGRPS; g++) {
1368		sw->ptgrp[g].port_grp = g;
1369		sw->ptgrp[g].sw = sw;
1370		sw->ptgrp[g].port = ptr;
1371		ptr = &sw->ptgrp[g].port[t->portgrp_sz];
1372	}
1373	t->sw_pool[t->switch_cnt++] = sw;
1374out:
1375	return sw;
1376}
1377
1378/*
1379 * install_tswitch() expects the switch coordinates i,j,k to be canonicalized
1380 * by caller.
1381 */
1382static
1383bool install_tswitch(struct torus *t,
1384		     int i, int j, int k, struct f_switch *fsw)
1385{
1386	struct t_switch **sw = &t->sw[i][j][k];
1387
1388	if (!*sw)
1389		*sw = alloc_tswitch(t, fsw);
1390
1391	if (*sw) {
1392		(*sw)->i = i;
1393		(*sw)->j = j;
1394		(*sw)->k = k;
1395	}
1396	return !!*sw;
1397}
1398
1399static
1400struct link *alloc_tlink(struct torus *t)
1401{
1402	if (t->link_cnt >= t->link_pool_sz) {
1403		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1404			"ERR 4E16: unexpectedly out of pre-allocated link "
1405			"structures!\n");
1406		return NULL;
1407	}
1408	return &t->link_pool[t->link_cnt++];
1409}
1410
1411static
1412int canonicalize(int v, int vmax)
1413{
1414	if (v >= 0 && v < vmax)
1415		return v;
1416
1417	if (v < 0)
1418		v += vmax * (1 - v/vmax);
1419
1420	return v % vmax;
1421}
1422
1423static
1424unsigned set_fp_bit(bool present, int i, int j, int k)
1425{
1426	return (unsigned)(!present) << (i + 2 * j + 4 * k);
1427}
1428
1429/*
1430 * Returns an 11-bit fingerprint of what switches are absent in a cube of
1431 * neighboring switches.  Each bit 0-7 corresponds to a corner of the cube;
1432 * if a bit is set the corresponding switch is absent.
1433 *
1434 * Bits 8-10 distinguish between 2D and 3D cases.  If bit 8+d is set,
1435 * for 0 <= d < 3;  the d dimension of the desired torus has radix greater
1436 * than 1. Thus, if all bits 8-10 are set, the desired torus is 3D.
1437 */
1438static
1439unsigned fingerprint(struct torus *t, int i, int j, int k)
1440{
1441	unsigned fp;
1442	int ip1, jp1, kp1;
1443	int x_sz_gt1, y_sz_gt1, z_sz_gt1;
1444
1445	x_sz_gt1 = t->x_sz > 1;
1446	y_sz_gt1 = t->y_sz > 1;
1447	z_sz_gt1 = t->z_sz > 1;
1448
1449	ip1 = canonicalize(i + 1, t->x_sz);
1450	jp1 = canonicalize(j + 1, t->y_sz);
1451	kp1 = canonicalize(k + 1, t->z_sz);
1452
1453	fp  = set_fp_bit(t->sw[i][j][k], 0, 0, 0);
1454	fp |= set_fp_bit(t->sw[ip1][j][k], x_sz_gt1, 0, 0);
1455	fp |= set_fp_bit(t->sw[i][jp1][k], 0, y_sz_gt1, 0);
1456	fp |= set_fp_bit(t->sw[ip1][jp1][k], x_sz_gt1, y_sz_gt1, 0);
1457	fp |= set_fp_bit(t->sw[i][j][kp1], 0, 0, z_sz_gt1);
1458	fp |= set_fp_bit(t->sw[ip1][j][kp1], x_sz_gt1, 0, z_sz_gt1);
1459	fp |= set_fp_bit(t->sw[i][jp1][kp1], 0, y_sz_gt1, z_sz_gt1);
1460	fp |= set_fp_bit(t->sw[ip1][jp1][kp1], x_sz_gt1, y_sz_gt1, z_sz_gt1);
1461
1462	fp |= x_sz_gt1 << 8;
1463	fp |= y_sz_gt1 << 9;
1464	fp |= z_sz_gt1 << 10;
1465
1466	return fp;
1467}
1468
1469static
1470bool connect_tlink(struct port_grp *pg0, struct endpoint *f_ep0,
1471		   struct port_grp *pg1, struct endpoint *f_ep1,
1472		   struct torus *t)
1473{
1474	struct link *l;
1475	bool success = false;
1476
1477	if (pg0->port_cnt == t->portgrp_sz) {
1478		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1479			"ERR 4E17: exceeded port group max "
1480			"port count (%d): switch GUID 0x%04"PRIx64"\n",
1481			t->portgrp_sz, cl_ntoh64(pg0->sw->n_id));
1482		goto out;
1483	}
1484	if (pg1->port_cnt == t->portgrp_sz) {
1485		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1486			"ERR 4E18: exceeded port group max "
1487			"port count (%d): switch GUID 0x%04"PRIx64"\n",
1488			t->portgrp_sz, cl_ntoh64(pg1->sw->n_id));
1489		goto out;
1490	}
1491	l = alloc_tlink(t);
1492	if (!l)
1493		goto out;
1494
1495	l->end[0].type = f_ep0->type;
1496	l->end[0].port = f_ep0->port;
1497	l->end[0].n_id = f_ep0->n_id;
1498	l->end[0].sw = pg0->sw;
1499	l->end[0].link = l;
1500	l->end[0].pgrp = pg0;
1501	pg0->port[pg0->port_cnt++] = &l->end[0];
1502	pg0->sw->port[f_ep0->port] = &l->end[0];
1503
1504	if (f_ep0->osm_port) {
1505		l->end[0].osm_port = f_ep0->osm_port;
1506		l->end[0].osm_port->priv = &l->end[0];
1507		f_ep0->osm_port = NULL;
1508	}
1509
1510	l->end[1].type = f_ep1->type;
1511	l->end[1].port = f_ep1->port;
1512	l->end[1].n_id = f_ep1->n_id;
1513	l->end[1].sw = pg1->sw;
1514	l->end[1].link = l;
1515	l->end[1].pgrp = pg1;
1516	pg1->port[pg1->port_cnt++] = &l->end[1];
1517	pg1->sw->port[f_ep1->port] = &l->end[1];
1518
1519	if (f_ep1->osm_port) {
1520		l->end[1].osm_port = f_ep1->osm_port;
1521		l->end[1].osm_port->priv = &l->end[1];
1522		f_ep1->osm_port = NULL;
1523	}
1524	/*
1525	 * Disconnect fabric link, so that later we can see if any were
1526	 * left unconnected in the torus.
1527	 */
1528	((struct f_switch *)f_ep0->sw)->port[f_ep0->port] = NULL;
1529	f_ep0->sw = NULL;
1530	f_ep0->port = -1;
1531
1532	((struct f_switch *)f_ep1->sw)->port[f_ep1->port] = NULL;
1533	f_ep1->sw = NULL;
1534	f_ep1->port = -1;
1535
1536	success = true;
1537out:
1538	return success;
1539}
1540
1541static
1542bool link_tswitches(struct torus *t, int cdir,
1543		    struct t_switch *t_sw0, struct t_switch *t_sw1)
1544{
1545	int p;
1546	struct port_grp *pg0, *pg1;
1547	struct f_switch *f_sw0, *f_sw1;
1548	const char *cdir_name = "unknown";
1549	unsigned port_cnt;
1550	int success = false;
1551
1552	/*
1553	 * If this is a 2D torus, it is possible for this function to be
1554	 * called with its two switch arguments being the same switch, in
1555	 * which case there are no links to install.
1556	 */
1557	if (t_sw0 == t_sw1 &&
1558	    ((cdir == 0 && t->x_sz == 1) ||
1559	     (cdir == 1 && t->y_sz == 1) ||
1560	     (cdir == 2 && t->z_sz == 1))) {
1561		success = true;
1562		goto out;
1563	}
1564	/*
1565	 * Ensure that t_sw1 is in the positive cdir direction wrt. t_sw0.
1566	 * ring_next_sw() relies on it.
1567	 */
1568	switch (cdir) {
1569	case 0:
1570		if (t->x_sz > 1 &&
1571		    canonicalize(t_sw0->i + 1, t->x_sz) != t_sw1->i) {
1572			cdir_name = "x";
1573			goto cdir_error;
1574		}
1575		break;
1576	case 1:
1577		if (t->y_sz > 1 &&
1578		    canonicalize(t_sw0->j + 1, t->y_sz) != t_sw1->j) {
1579			cdir_name = "y";
1580			goto cdir_error;
1581		}
1582		break;
1583	case 2:
1584		if (t->z_sz > 1 &&
1585		    canonicalize(t_sw0->k + 1, t->z_sz) != t_sw1->k) {
1586			cdir_name = "z";
1587			goto cdir_error;
1588		}
1589		break;
1590	default:
1591	cdir_error:
1592		OSM_LOG(&t->osm->log, OSM_LOG_ERROR, "ERR 4E19: "
1593			"sw 0x%04"PRIx64" (%d,%d,%d) <--> "
1594			"sw 0x%04"PRIx64" (%d,%d,%d) "
1595			"invalid torus %s link orientation\n",
1596			cl_ntoh64(t_sw0->n_id), t_sw0->i, t_sw0->j, t_sw0->k,
1597			cl_ntoh64(t_sw1->n_id), t_sw1->i, t_sw1->j, t_sw1->k,
1598			cdir_name);
1599		goto out;
1600	}
1601
1602	f_sw0 = t_sw0->tmp;
1603	f_sw1 = t_sw1->tmp;
1604
1605	if (!f_sw0 || !f_sw1) {
1606		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1607			"ERR 4E1A: missing fabric switches!\n"
1608			"  switch GUIDs: 0x%04"PRIx64" 0x%04"PRIx64"\n",
1609			cl_ntoh64(t_sw0->n_id), cl_ntoh64(t_sw1->n_id));
1610		goto out;
1611	}
1612	pg0 = &t_sw0->ptgrp[2*cdir + 1];
1613	pg0->type = PASSTHRU;
1614
1615	pg1 = &t_sw1->ptgrp[2*cdir];
1616	pg1->type = PASSTHRU;
1617
1618	port_cnt = f_sw0->port_cnt;
1619	/*
1620	 * Find all the links between these two switches.
1621	 */
1622	for (p = 0; p < port_cnt; p++) {
1623		struct endpoint *f_ep0 = NULL, *f_ep1 = NULL;
1624
1625		if (!f_sw0->port[p] || !f_sw0->port[p]->link)
1626			continue;
1627
1628		if (f_sw0->port[p]->link->end[0].n_id == t_sw0->n_id &&
1629		    f_sw0->port[p]->link->end[1].n_id == t_sw1->n_id) {
1630
1631			f_ep0 = &f_sw0->port[p]->link->end[0];
1632			f_ep1 = &f_sw0->port[p]->link->end[1];
1633		} else if (f_sw0->port[p]->link->end[1].n_id == t_sw0->n_id &&
1634			   f_sw0->port[p]->link->end[0].n_id == t_sw1->n_id) {
1635
1636			f_ep0 = &f_sw0->port[p]->link->end[1];
1637			f_ep1 = &f_sw0->port[p]->link->end[0];
1638		} else
1639			continue;
1640
1641		if (!(f_ep0->type == PASSTHRU && f_ep1->type == PASSTHRU)) {
1642			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1643				"ERR 4E1B: not interswitch "
1644				"link:\n  0x%04"PRIx64"/%d <-> 0x%04"PRIx64"/%d\n",
1645				cl_ntoh64(f_ep0->n_id), f_ep0->port,
1646				cl_ntoh64(f_ep1->n_id), f_ep1->port);
1647			goto out;
1648		}
1649		/*
1650		 * Skip over links that already have been established in the
1651		 * torus.
1652		 */
1653		if (!(f_ep0->sw && f_ep1->sw))
1654			continue;
1655
1656		if (!connect_tlink(pg0, f_ep0, pg1, f_ep1, t))
1657			goto out;
1658	}
1659	success = true;
1660out:
1661	return success;
1662}
1663
1664static
1665bool link_srcsink(struct torus *t, int i, int j, int k)
1666{
1667	struct endpoint *f_ep0;
1668	struct endpoint *f_ep1;
1669	struct t_switch *tsw;
1670	struct f_switch *fsw;
1671	struct port_grp *pg;
1672	struct link *fl, *tl;
1673	unsigned p, port_cnt;
1674	bool success = false;
1675
1676	i = canonicalize(i, t->x_sz);
1677	j = canonicalize(j, t->y_sz);
1678	k = canonicalize(k, t->z_sz);
1679
1680	tsw = t->sw[i][j][k];
1681	if (!tsw)
1682		return true;
1683
1684	fsw = tsw->tmp;
1685	/*
1686	 * link_srcsink is supposed to get called once for every switch in
1687	 * the fabric.  At this point every fsw we encounter must have a
1688	 * non-null osm_switch.  Otherwise something has gone horribly
1689	 * wrong with topology discovery; the most likely reason is that
1690	 * the fabric contains a radix-4 torus dimension, but the user gave
1691	 * a config that didn't say so, breaking all the checking in
1692	 * safe_x_perpendicular and friends.
1693	 */
1694	if (!(fsw && fsw->osm_switch)) {
1695		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1696			"ERR 4E1C: Invalid topology discovery. "
1697			"Verify torus-2QoS.conf contents.\n");
1698		return false;
1699	}
1700
1701	pg = &tsw->ptgrp[2 * TORUS_MAX_DIM];
1702	pg->type = SRCSINK;
1703	tsw->osm_switch = fsw->osm_switch;
1704	tsw->osm_switch->priv = tsw;
1705	fsw->osm_switch = NULL;
1706
1707	port_cnt = fsw->port_cnt;
1708	for (p = 0; p < port_cnt; p++) {
1709
1710		if (!fsw->port[p])
1711			continue;
1712
1713		if (fsw->port[p]->type == SRCSINK) {
1714			/*
1715			 * If the endpoint is the switch port used for in-band
1716			 * communication with the switch itself, move it to
1717			 * the torus.
1718			 */
1719			if (pg->port_cnt == t->portgrp_sz) {
1720				OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1721					"ERR 4E1D: exceeded port group max port "
1722					"count (%d): switch GUID 0x%04"PRIx64"\n",
1723					t->portgrp_sz, cl_ntoh64(tsw->n_id));
1724				goto out;
1725			}
1726			fsw->port[p]->sw = tsw;
1727			fsw->port[p]->pgrp = pg;
1728			tsw->port[p] = fsw->port[p];
1729			tsw->port[p]->osm_port->priv = tsw->port[p];
1730			pg->port[pg->port_cnt++] = fsw->port[p];
1731			fsw->port[p] = NULL;
1732
1733		} else if (fsw->port[p]->link &&
1734			   fsw->port[p]->type == PASSTHRU) {
1735			/*
1736			 * If the endpoint is a link to a CA, create a new link
1737			 * in the torus.  Disconnect the fabric link.
1738			 */
1739
1740			fl = fsw->port[p]->link;
1741
1742			if (fl->end[0].sw == fsw) {
1743				f_ep0 = &fl->end[0];
1744				f_ep1 = &fl->end[1];
1745			} else if (fl->end[1].sw == fsw) {
1746				f_ep1 = &fl->end[0];
1747				f_ep0 = &fl->end[1];
1748			} else
1749				continue;
1750
1751			if (f_ep1->type != SRCSINK)
1752				continue;
1753
1754			if (pg->port_cnt == t->portgrp_sz) {
1755				OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1756					"ERR 4E1E: exceeded port group max port "
1757					"count (%d): switch GUID 0x%04"PRIx64"\n",
1758					t->portgrp_sz, cl_ntoh64(tsw->n_id));
1759				goto out;
1760			}
1761			/*
1762			 * Switch ports connected to links don't get
1763			 * associated with osm_port_t objects; see
1764			 * capture_fabric().  So just check CA end.
1765			 */
1766			if (!f_ep1->osm_port) {
1767				OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
1768					"ERR 4E1F: NULL osm_port->priv port "
1769					"GUID 0x%04"PRIx64"\n",
1770					cl_ntoh64(f_ep1->n_id));
1771				goto out;
1772			}
1773			tl = alloc_tlink(t);
1774			if (!tl)
1775				continue;
1776
1777			tl->end[0].type = f_ep0->type;
1778			tl->end[0].port = f_ep0->port;
1779			tl->end[0].n_id = f_ep0->n_id;
1780			tl->end[0].sw = tsw;
1781			tl->end[0].link = tl;
1782			tl->end[0].pgrp = pg;
1783			pg->port[pg->port_cnt++] = &tl->end[0];
1784			pg->sw->port[f_ep0->port] =  &tl->end[0];
1785
1786			tl->end[1].type = f_ep1->type;
1787			tl->end[1].port = f_ep1->port;
1788			tl->end[1].n_id = f_ep1->n_id;
1789			tl->end[1].sw = NULL;	/* Correct for a CA */
1790			tl->end[1].link = tl;
1791			tl->end[1].pgrp = NULL;	/* Correct for a CA */
1792
1793			tl->end[1].osm_port = f_ep1->osm_port;
1794			tl->end[1].osm_port->priv = &tl->end[1];
1795			f_ep1->osm_port = NULL;
1796
1797			t->ca_cnt++;
1798			f_ep0->sw = NULL;
1799			f_ep0->port = -1;
1800			fsw->port[p] = NULL;
1801		}
1802	}
1803	success = true;
1804out:
1805	return success;
1806}
1807
1808static
1809struct f_switch *ffind_face_corner(struct f_switch *fsw0,
1810				   struct f_switch *fsw1,
1811				   struct f_switch *fsw2)
1812{
1813	int p0, p3;
1814	struct link *l;
1815	struct endpoint *far_end;
1816	struct f_switch *fsw, *fsw3 = NULL;
1817
1818	if (!(fsw0 && fsw1 && fsw2))
1819		goto out;
1820
1821	for (p0 = 0; p0 < fsw0->port_cnt; p0++) {
1822		/*
1823		 * Ignore everything except switch links that haven't
1824		 * been installed into the torus.
1825		 */
1826		if (!(fsw0->port[p0] && fsw0->port[p0]->sw &&
1827		      fsw0->port[p0]->type == PASSTHRU))
1828			continue;
1829
1830		l = fsw0->port[p0]->link;
1831
1832		if (l->end[0].n_id == fsw0->n_id)
1833			far_end = &l->end[1];
1834		else
1835			far_end = &l->end[0];
1836
1837		/*
1838		 * Ignore CAs
1839		 */
1840		if (!(far_end->type == PASSTHRU && far_end->sw))
1841			continue;
1842
1843		fsw3 = far_end->sw;
1844		if (fsw3->n_id == fsw1->n_id)	/* existing corner */
1845			continue;
1846
1847		for (p3 = 0; p3 < fsw3->port_cnt; p3++) {
1848			/*
1849			 * Ignore everything except switch links that haven't
1850			 * been installed into the torus.
1851			 */
1852			if (!(fsw3->port[p3] && fsw3->port[p3]->sw &&
1853			      fsw3->port[p3]->type == PASSTHRU))
1854				continue;
1855
1856			l = fsw3->port[p3]->link;
1857
1858			if (l->end[0].n_id == fsw3->n_id)
1859				far_end = &l->end[1];
1860			else
1861				far_end = &l->end[0];
1862
1863			/*
1864			 * Ignore CAs
1865			 */
1866			if (!(far_end->type == PASSTHRU && far_end->sw))
1867				continue;
1868
1869			fsw = far_end->sw;
1870			if (fsw->n_id == fsw2->n_id)
1871				goto out;
1872		}
1873	}
1874	fsw3 = NULL;
1875out:
1876	return fsw3;
1877}
1878
1879static
1880struct f_switch *tfind_face_corner(struct t_switch *tsw0,
1881				   struct t_switch *tsw1,
1882				   struct t_switch *tsw2)
1883{
1884	if (!(tsw0 && tsw1 && tsw2))
1885		return NULL;
1886
1887	return ffind_face_corner(tsw0->tmp, tsw1->tmp, tsw2->tmp);
1888}
1889
1890/*
1891 * This code can break on any torus with a dimension that has radix four.
1892 *
1893 * What is supposed to happen is that this code will find the
1894 * two faces whose shared edge is the desired perpendicular.
1895 *
1896 * What actually happens is while searching we send two connected
1897 * edges that are colinear in a torus dimension with radix four to
1898 * ffind_face_corner(), which tries to complete a face by finding a
1899 * 4-loop of edges.
1900 *
1901 * In the radix four torus case, it can find a 4-loop which is a ring in a
1902 * dimension with radix four, rather than the desired face.  It thus returns
1903 * true when it shouldn't, so the wrong edge is returned as the perpendicular.
1904 *
1905 * The appropriate instance of safe_N_perpendicular() (where N == x, y, z)
1906 * should be used to determine if it is safe to call ffind_perpendicular();
1907 * these functions will return false it there is a possibility of finding
1908 * a wrong perpendicular.
1909 */
1910struct f_switch *ffind_3d_perpendicular(struct f_switch *fsw0,
1911					struct f_switch *fsw1,
1912					struct f_switch *fsw2,
1913					struct f_switch *fsw3)
1914{
1915	int p1;
1916	struct link *l;
1917	struct endpoint *far_end;
1918	struct f_switch *fsw4 = NULL;
1919
1920	if (!(fsw0 && fsw1 && fsw2 && fsw3))
1921		goto out;
1922
1923	/*
1924	 * Look at all the ports on the switch, fsw1,  that is the base of
1925	 * the perpendicular.
1926	 */
1927	for (p1 = 0; p1 < fsw1->port_cnt; p1++) {
1928		/*
1929		 * Ignore everything except switch links that haven't
1930		 * been installed into the torus.
1931		 */
1932		if (!(fsw1->port[p1] && fsw1->port[p1]->sw &&
1933		      fsw1->port[p1]->type == PASSTHRU))
1934			continue;
1935
1936		l = fsw1->port[p1]->link;
1937
1938		if (l->end[0].n_id == fsw1->n_id)
1939			far_end = &l->end[1];
1940		else
1941			far_end = &l->end[0];
1942		/*
1943		 * Ignore CAs
1944		 */
1945		if (!(far_end->type == PASSTHRU && far_end->sw))
1946			continue;
1947
1948		fsw4 = far_end->sw;
1949		if (fsw4->n_id == fsw3->n_id)	/* wrong perpendicular */
1950			continue;
1951
1952		if (ffind_face_corner(fsw0, fsw1, fsw4) &&
1953		    ffind_face_corner(fsw2, fsw1, fsw4))
1954			goto out;
1955	}
1956	fsw4 = NULL;
1957out:
1958	return fsw4;
1959}
1960struct f_switch *ffind_2d_perpendicular(struct f_switch *fsw0,
1961					struct f_switch *fsw1,
1962					struct f_switch *fsw2)
1963{
1964	int p1;
1965	struct link *l;
1966	struct endpoint *far_end;
1967	struct f_switch *fsw3 = NULL;
1968
1969	if (!(fsw0 && fsw1 && fsw2))
1970		goto out;
1971
1972	/*
1973	 * Look at all the ports on the switch, fsw1,  that is the base of
1974	 * the perpendicular.
1975	 */
1976	for (p1 = 0; p1 < fsw1->port_cnt; p1++) {
1977		/*
1978		 * Ignore everything except switch links that haven't
1979		 * been installed into the torus.
1980		 */
1981		if (!(fsw1->port[p1] && fsw1->port[p1]->sw &&
1982		      fsw1->port[p1]->type == PASSTHRU))
1983			continue;
1984
1985		l = fsw1->port[p1]->link;
1986
1987		if (l->end[0].n_id == fsw1->n_id)
1988			far_end = &l->end[1];
1989		else
1990			far_end = &l->end[0];
1991		/*
1992		 * Ignore CAs
1993		 */
1994		if (!(far_end->type == PASSTHRU && far_end->sw))
1995			continue;
1996
1997		fsw3 = far_end->sw;
1998		if (fsw3->n_id == fsw2->n_id)	/* wrong perpendicular */
1999			continue;
2000
2001		if (ffind_face_corner(fsw0, fsw1, fsw3))
2002			goto out;
2003	}
2004	fsw3 = NULL;
2005out:
2006	return fsw3;
2007}
2008
2009static
2010struct f_switch *tfind_3d_perpendicular(struct t_switch *tsw0,
2011					struct t_switch *tsw1,
2012					struct t_switch *tsw2,
2013					struct t_switch *tsw3)
2014{
2015	if (!(tsw0 && tsw1 && tsw2 && tsw3))
2016		return NULL;
2017
2018	return ffind_3d_perpendicular(tsw0->tmp, tsw1->tmp,
2019				      tsw2->tmp, tsw3->tmp);
2020}
2021
2022static
2023struct f_switch *tfind_2d_perpendicular(struct t_switch *tsw0,
2024					struct t_switch *tsw1,
2025					struct t_switch *tsw2)
2026{
2027	if (!(tsw0 && tsw1 && tsw2))
2028		return NULL;
2029
2030	return ffind_2d_perpendicular(tsw0->tmp, tsw1->tmp, tsw2->tmp);
2031}
2032
2033static
2034bool safe_x_ring(struct torus *t, int i, int j, int k)
2035{
2036	int im1, ip1, ip2;
2037	bool success = true;
2038
2039	/*
2040	 * If this x-direction radix-4 ring has at least two links
2041	 * already installed into the torus,  then this ring does not
2042	 * prevent us from looking for y or z direction perpendiculars.
2043	 *
2044	 * It is easier to check for the appropriate switches being installed
2045	 * into the torus than it is to check for the links, so force the
2046	 * link installation if the appropriate switches are installed.
2047	 *
2048	 * Recall that canonicalize(n - 2, 4) == canonicalize(n + 2, 4).
2049	 */
2050	if (t->x_sz != 4 || t->flags & X_MESH)
2051		goto out;
2052
2053	im1 = canonicalize(i - 1, t->x_sz);
2054	ip1 = canonicalize(i + 1, t->x_sz);
2055	ip2 = canonicalize(i + 2, t->x_sz);
2056
2057	if (!!t->sw[im1][j][k] +
2058	    !!t->sw[ip1][j][k] + !!t->sw[ip2][j][k] < 2) {
2059		success = false;
2060		goto out;
2061	}
2062	if (t->sw[ip2][j][k] && t->sw[im1][j][k])
2063		success = link_tswitches(t, 0,
2064					 t->sw[ip2][j][k],
2065					 t->sw[im1][j][k])
2066			&& success;
2067
2068	if (t->sw[im1][j][k] && t->sw[i][j][k])
2069		success = link_tswitches(t, 0,
2070					 t->sw[im1][j][k],
2071					 t->sw[i][j][k])
2072			&& success;
2073
2074	if (t->sw[i][j][k] && t->sw[ip1][j][k])
2075		success = link_tswitches(t, 0,
2076					 t->sw[i][j][k],
2077					 t->sw[ip1][j][k])
2078			&& success;
2079
2080	if (t->sw[ip1][j][k] && t->sw[ip2][j][k])
2081		success = link_tswitches(t, 0,
2082					 t->sw[ip1][j][k],
2083					 t->sw[ip2][j][k])
2084			&& success;
2085out:
2086	return success;
2087}
2088
2089static
2090bool safe_y_ring(struct torus *t, int i, int j, int k)
2091{
2092	int jm1, jp1, jp2;
2093	bool success = true;
2094
2095	/*
2096	 * If this y-direction radix-4 ring has at least two links
2097	 * already installed into the torus,  then this ring does not
2098	 * prevent us from looking for x or z direction perpendiculars.
2099	 *
2100	 * It is easier to check for the appropriate switches being installed
2101	 * into the torus than it is to check for the links, so force the
2102	 * link installation if the appropriate switches are installed.
2103	 *
2104	 * Recall that canonicalize(n - 2, 4) == canonicalize(n + 2, 4).
2105	 */
2106	if (t->y_sz != 4 || (t->flags & Y_MESH))
2107		goto out;
2108
2109	jm1 = canonicalize(j - 1, t->y_sz);
2110	jp1 = canonicalize(j + 1, t->y_sz);
2111	jp2 = canonicalize(j + 2, t->y_sz);
2112
2113	if (!!t->sw[i][jm1][k] +
2114	    !!t->sw[i][jp1][k] + !!t->sw[i][jp2][k] < 2) {
2115		success = false;
2116		goto out;
2117	}
2118	if (t->sw[i][jp2][k] && t->sw[i][jm1][k])
2119		success = link_tswitches(t, 1,
2120					 t->sw[i][jp2][k],
2121					 t->sw[i][jm1][k])
2122			&& success;
2123
2124	if (t->sw[i][jm1][k] && t->sw[i][j][k])
2125		success = link_tswitches(t, 1,
2126					 t->sw[i][jm1][k],
2127					 t->sw[i][j][k])
2128			&& success;
2129
2130	if (t->sw[i][j][k] && t->sw[i][jp1][k])
2131		success = link_tswitches(t, 1,
2132					 t->sw[i][j][k],
2133					 t->sw[i][jp1][k])
2134			&& success;
2135
2136	if (t->sw[i][jp1][k] && t->sw[i][jp2][k])
2137		success = link_tswitches(t, 1,
2138					 t->sw[i][jp1][k],
2139					 t->sw[i][jp2][k])
2140			&& success;
2141out:
2142	return success;
2143}
2144
2145static
2146bool safe_z_ring(struct torus *t, int i, int j, int k)
2147{
2148	int km1, kp1, kp2;
2149	bool success = true;
2150
2151	/*
2152	 * If this z-direction radix-4 ring has at least two links
2153	 * already installed into the torus,  then this ring does not
2154	 * prevent us from looking for x or y direction perpendiculars.
2155	 *
2156	 * It is easier to check for the appropriate switches being installed
2157	 * into the torus than it is to check for the links, so force the
2158	 * link installation if the appropriate switches are installed.
2159	 *
2160	 * Recall that canonicalize(n - 2, 4) == canonicalize(n + 2, 4).
2161	 */
2162	if (t->z_sz != 4 || t->flags & Z_MESH)
2163		goto out;
2164
2165	km1 = canonicalize(k - 1, t->z_sz);
2166	kp1 = canonicalize(k + 1, t->z_sz);
2167	kp2 = canonicalize(k + 2, t->z_sz);
2168
2169	if (!!t->sw[i][j][km1] +
2170	    !!t->sw[i][j][kp1] + !!t->sw[i][j][kp2] < 2) {
2171		success = false;
2172		goto out;
2173	}
2174	if (t->sw[i][j][kp2] && t->sw[i][j][km1])
2175		success = link_tswitches(t, 2,
2176					 t->sw[i][j][kp2],
2177					 t->sw[i][j][km1])
2178			&& success;
2179
2180	if (t->sw[i][j][km1] && t->sw[i][j][k])
2181		success = link_tswitches(t, 2,
2182					 t->sw[i][j][km1],
2183					 t->sw[i][j][k])
2184			&& success;
2185
2186	if (t->sw[i][j][k] && t->sw[i][j][kp1])
2187		success = link_tswitches(t, 2,
2188					 t->sw[i][j][k],
2189					 t->sw[i][j][kp1])
2190			&& success;
2191
2192	if (t->sw[i][j][kp1] && t->sw[i][j][kp2])
2193		success = link_tswitches(t, 2,
2194					 t->sw[i][j][kp1],
2195					 t->sw[i][j][kp2])
2196			&& success;
2197out:
2198	return success;
2199}
2200
2201/*
2202 * These functions return true when it safe to call
2203 * tfind_3d_perpendicular()/ffind_3d_perpendicular().
2204 */
2205static
2206bool safe_x_perpendicular(struct torus *t, int i, int j, int k)
2207{
2208	/*
2209	 * If the dimensions perpendicular to the search direction are
2210	 * not radix 4 torus dimensions, it is always safe to search for
2211	 * a perpendicular.
2212	 *
2213	 * Here we are checking for enough appropriate links having been
2214	 * installed into the torus to prevent an incorrect link from being
2215	 * considered as a perpendicular candidate.
2216	 */
2217	return safe_y_ring(t, i, j, k) && safe_z_ring(t, i, j, k);
2218}
2219
2220static
2221bool safe_y_perpendicular(struct torus *t, int i, int j, int k)
2222{
2223	/*
2224	 * If the dimensions perpendicular to the search direction are
2225	 * not radix 4 torus dimensions, it is always safe to search for
2226	 * a perpendicular.
2227	 *
2228	 * Here we are checking for enough appropriate links having been
2229	 * installed into the torus to prevent an incorrect link from being
2230	 * considered as a perpendicular candidate.
2231	 */
2232	return safe_x_ring(t, i, j, k) && safe_z_ring(t, i, j, k);
2233}
2234
2235static
2236bool safe_z_perpendicular(struct torus *t, int i, int j, int k)
2237{
2238	/*
2239	 * If the dimensions perpendicular to the search direction are
2240	 * not radix 4 torus dimensions, it is always safe to search for
2241	 * a perpendicular.
2242	 *
2243	 * Implement this by checking for enough appropriate links having
2244	 * been installed into the torus to prevent an incorrect link from
2245	 * being considered as a perpendicular candidate.
2246	 */
2247	return safe_x_ring(t, i, j, k) && safe_y_ring(t, i, j, k);
2248}
2249
2250/*
2251 * Templates for determining 2D/3D case fingerprints. Recall that if
2252 * a fingerprint bit is set the corresponding switch is absent from
2253 * the all-switches-present template.
2254 *
2255 * I.e., for the 2D case where the x,y dimensions have a radix greater
2256 * than one, and the z dimension has radix 1, fingerprint bits 4-7 are
2257 * always zero.
2258 *
2259 * For the 2D case where the x,z dimensions have a radix greater than
2260 * one, and the y dimension has radix 1, fingerprint bits 2,3,6,7 are
2261 * always zero.
2262 *
2263 * For the 2D case where the y,z dimensions have a radix greater than
2264 * one, and the x dimension has radix 1, fingerprint bits 1,3,5,7 are
2265 * always zero.
2266 *
2267 * Recall also that bits 8-10 distinguish between 2D and 3D cases.
2268 * If bit 8+d is set, for 0 <= d < 3;  the d dimension of the desired
2269 * torus has radix greater than 1.
2270 */
2271
2272/*
2273 * 2D case 0x300
2274 *  b0: t->sw[i  ][j  ][0  ]
2275 *  b1: t->sw[i+1][j  ][0  ]
2276 *  b2: t->sw[i  ][j+1][0  ]
2277 *  b3: t->sw[i+1][j+1][0  ]
2278 *                                    O . . . . . O
2279 * 2D case 0x500                      .           .
2280 *  b0: t->sw[i  ][0  ][k  ]          .           .
2281 *  b1: t->sw[i+1][0  ][k  ]          .           .
2282 *  b4: t->sw[i  ][0  ][k+1]          .           .
2283 *  b5: t->sw[i+1][0  ][k+1]          .           .
2284 *                                    @ . . . . . O
2285 * 2D case 0x600
2286 *  b0: t->sw[0  ][j  ][k  ]
2287 *  b2: t->sw[0  ][j+1][k  ]
2288 *  b4: t->sw[0  ][j  ][k+1]
2289 *  b6: t->sw[0  ][j+1][k+1]
2290 */
2291
2292/*
2293 * 3D case 0x700:                           O
2294 *                                        . . .
2295 *  b0: t->sw[i  ][j  ][k  ]            .   .   .
2296 *  b1: t->sw[i+1][j  ][k  ]          .     .     .
2297 *  b2: t->sw[i  ][j+1][k  ]        .       .       .
2298 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
2299 *  b4: t->sw[i  ][j  ][k+1]      . .       O       . .
2300 *  b5: t->sw[i+1][j  ][k+1]      .   .   .   .   .   .
2301 *  b6: t->sw[i  ][j+1][k+1]      .     .       .     .
2302 *  b7: t->sw[i+1][j+1][k+1]      .   .   .   .   .   .
2303 *                                . .       O       . .
2304 *                                O         .         O
2305 *                                  .       .       .
2306 *                                    .     .     .
2307 *                                      .   .   .
2308 *                                        . . .
2309 *                                          @
2310 */
2311
2312static
2313void log_no_crnr(struct torus *t, unsigned n,
2314		 int case_i, int case_j, int case_k,
2315		 int crnr_i, int crnr_j, int crnr_k)
2316{
2317	if (t->debug)
2318		OSM_LOG(&t->osm->log, OSM_LOG_INFO, "Case 0x%03x "
2319			"@ %d %d %d: no corner @ %d %d %d\n",
2320			n, case_i, case_j, case_k, crnr_i, crnr_j, crnr_k);
2321}
2322
2323static
2324void log_no_perp(struct torus *t, unsigned n,
2325		 int case_i, int case_j, int case_k,
2326		 int perp_i, int perp_j, int perp_k)
2327{
2328	if (t->debug)
2329		OSM_LOG(&t->osm->log, OSM_LOG_INFO, "Case 0x%03x "
2330			"@ %d %d %d: no perpendicular @ %d %d %d\n",
2331			n, case_i, case_j, case_k, perp_i, perp_j, perp_k);
2332}
2333
2334/*
2335 * Handle the 2D cases with a single existing edge.
2336 *
2337 */
2338
2339/*
2340 * 2D case 0x30c
2341 *  b0: t->sw[i  ][j  ][0  ]
2342 *  b1: t->sw[i+1][j  ][0  ]
2343 *  b2:
2344 *  b3:
2345 *                                    O           O
2346 * 2D case 0x530
2347 *  b0: t->sw[i  ][0  ][k  ]
2348 *  b1: t->sw[i+1][0  ][k  ]
2349 *  b4:
2350 *  b5:
2351 *                                    @ . . . . . O
2352 * 2D case 0x650
2353 *  b0: t->sw[0  ][j  ][k  ]
2354 *  b2: t->sw[0  ][j+1][k  ]
2355 *  b4:
2356 *  b6:
2357 */
2358static
2359bool handle_case_0x30c(struct torus *t, int i, int j, int k)
2360{
2361	int ip1 = canonicalize(i + 1, t->x_sz);
2362	int jm1 = canonicalize(j - 1, t->y_sz);
2363	int jp1 = canonicalize(j + 1, t->y_sz);
2364
2365	if (safe_y_perpendicular(t, i, j, k) &&
2366	    install_tswitch(t, i, jp1, k,
2367			    tfind_2d_perpendicular(t->sw[ip1][j][k],
2368						   t->sw[i][j][k],
2369						   t->sw[i][jm1][k]))) {
2370		return true;
2371	}
2372	log_no_perp(t, 0x30c, i, j, k, i, j, k);
2373
2374	if (safe_y_perpendicular(t, ip1, j, k) &&
2375	    install_tswitch(t, ip1, jp1, k,
2376			    tfind_2d_perpendicular(t->sw[i][j][k],
2377						   t->sw[ip1][j][k],
2378						   t->sw[ip1][jm1][k]))) {
2379		return true;
2380	}
2381	log_no_perp(t, 0x30c, i, j, k, ip1, j, k);
2382	return false;
2383}
2384
2385static
2386bool handle_case_0x530(struct torus *t, int i, int j, int k)
2387{
2388	int ip1 = canonicalize(i + 1, t->x_sz);
2389	int km1 = canonicalize(k - 1, t->z_sz);
2390	int kp1 = canonicalize(k + 1, t->z_sz);
2391
2392	if (safe_z_perpendicular(t, i, j, k) &&
2393	    install_tswitch(t, i, j, kp1,
2394			    tfind_2d_perpendicular(t->sw[ip1][j][k],
2395						   t->sw[i][j][k],
2396						   t->sw[i][j][km1]))) {
2397		return true;
2398	}
2399	log_no_perp(t, 0x530, i, j, k, i, j, k);
2400
2401	if (safe_z_perpendicular(t, ip1, j, k) &&
2402	      install_tswitch(t, ip1, j, kp1,
2403			      tfind_2d_perpendicular(t->sw[i][j][k],
2404						     t->sw[ip1][j][k],
2405						     t->sw[ip1][j][km1]))) {
2406		return true;
2407	}
2408	log_no_perp(t, 0x530, i, j, k, ip1, j, k);
2409	return false;
2410}
2411
2412static
2413bool handle_case_0x650(struct torus *t, int i, int j, int k)
2414{
2415	int jp1 = canonicalize(j + 1, t->y_sz);
2416	int km1 = canonicalize(k - 1, t->z_sz);
2417	int kp1 = canonicalize(k + 1, t->z_sz);
2418
2419	if (safe_z_perpendicular(t, i, j, k) &&
2420	    install_tswitch(t, i, j, kp1,
2421			    tfind_2d_perpendicular(t->sw[i][jp1][k],
2422						   t->sw[i][j][k],
2423						   t->sw[i][j][km1]))) {
2424		return true;
2425	}
2426	log_no_perp(t, 0x650, i, j, k, i, j, k);
2427
2428	if (safe_z_perpendicular(t, i, jp1, k) &&
2429	    install_tswitch(t, i, jp1, kp1,
2430			    tfind_2d_perpendicular(t->sw[i][j][k],
2431						   t->sw[i][jp1][k],
2432						   t->sw[i][jp1][km1]))) {
2433		return true;
2434	}
2435	log_no_perp(t, 0x650, i, j, k, i, jp1, k);
2436	return false;
2437}
2438
2439/*
2440 * 2D case 0x305
2441 *  b0:
2442 *  b1: t->sw[i+1][j  ][0  ]
2443 *  b2:
2444 *  b3: t->sw[i+1][j+1][0  ]
2445 *                                    O           O
2446 * 2D case 0x511                                  .
2447 *  b0:                                           .
2448 *  b1: t->sw[i+1][0  ][k  ]                      .
2449 *  b4:                                           .
2450 *  b5: t->sw[i+1][0  ][k+1]                      .
2451 *                                    @           O
2452 * 2D case 0x611
2453 *  b0:
2454 *  b2: t->sw[0  ][j+1][k  ]
2455 *  b4:
2456 *  b6: t->sw[0  ][j+1][k+1]
2457 */
2458static
2459bool handle_case_0x305(struct torus *t, int i, int j, int k)
2460{
2461	int ip1 = canonicalize(i + 1, t->x_sz);
2462	int ip2 = canonicalize(i + 2, t->x_sz);
2463	int jp1 = canonicalize(j + 1, t->y_sz);
2464
2465	if (safe_x_perpendicular(t, ip1, j, k) &&
2466	    install_tswitch(t, i, j, k,
2467			    tfind_2d_perpendicular(t->sw[ip1][jp1][k],
2468						   t->sw[ip1][j][k],
2469						   t->sw[ip2][j][k]))) {
2470		return true;
2471	}
2472	log_no_perp(t, 0x305, i, j, k, ip1, j, k);
2473
2474	if (safe_x_perpendicular(t, ip1, jp1, k) &&
2475	    install_tswitch(t, i, jp1, k,
2476			    tfind_2d_perpendicular(t->sw[ip1][j][k],
2477						   t->sw[ip1][jp1][k],
2478						   t->sw[ip2][jp1][k]))) {
2479		return true;
2480	}
2481	log_no_perp(t, 0x305, i, j, k, ip1, jp1, k);
2482	return false;
2483}
2484
2485static
2486bool handle_case_0x511(struct torus *t, int i, int j, int k)
2487{
2488	int ip1 = canonicalize(i + 1, t->x_sz);
2489	int ip2 = canonicalize(i + 2, t->x_sz);
2490	int kp1 = canonicalize(k + 1, t->z_sz);
2491
2492	if (safe_x_perpendicular(t, ip1, j, k) &&
2493	    install_tswitch(t, i, j, k,
2494			    tfind_2d_perpendicular(t->sw[ip1][j][kp1],
2495						   t->sw[ip1][j][k],
2496						   t->sw[ip2][j][k]))) {
2497		return true;
2498	}
2499	log_no_perp(t, 0x511, i, j, k, ip1, j, k);
2500
2501	if (safe_x_perpendicular(t, ip1, j, kp1) &&
2502	    install_tswitch(t, i, j, kp1,
2503			    tfind_2d_perpendicular(t->sw[ip1][j][k],
2504						   t->sw[ip1][j][kp1],
2505						   t->sw[ip2][j][kp1]))) {
2506		return true;
2507	}
2508	log_no_perp(t, 0x511, i, j, k, ip1, j, kp1);
2509	return false;
2510}
2511
2512static
2513bool handle_case_0x611(struct torus *t, int i, int j, int k)
2514{
2515	int jp1 = canonicalize(j + 1, t->y_sz);
2516	int jp2 = canonicalize(j + 2, t->y_sz);
2517	int kp1 = canonicalize(k + 1, t->z_sz);
2518
2519	if (safe_y_perpendicular(t, i, jp1, k) &&
2520	    install_tswitch(t, i, j, k,
2521			    tfind_2d_perpendicular(t->sw[i][jp1][kp1],
2522						   t->sw[i][jp1][k],
2523						   t->sw[i][jp2][k]))) {
2524		return true;
2525	}
2526	log_no_perp(t, 0x611, i, j, k, i, jp1, k);
2527
2528	if (safe_y_perpendicular(t, i, jp1, kp1) &&
2529	    install_tswitch(t, i, j, kp1,
2530			    tfind_2d_perpendicular(t->sw[i][jp1][k],
2531						   t->sw[i][jp1][kp1],
2532						   t->sw[i][jp2][kp1]))) {
2533		return true;
2534	}
2535	log_no_perp(t, 0x611, i, j, k, i, jp1, kp1);
2536	return false;
2537}
2538
2539/*
2540 * 2D case 0x303
2541 *  b0:
2542 *  b1:
2543 *  b2: t->sw[i  ][j+1][0  ]
2544 *  b3: t->sw[i+1][j+1][0  ]
2545 *                                    O . . . . . O
2546 * 2D case 0x503
2547 *  b0:
2548 *  b1:
2549 *  b4: t->sw[i  ][0  ][k+1]
2550 *  b5: t->sw[i+1][0  ][k+1]
2551 *                                    @           O
2552 * 2D case 0x605
2553 *  b0:
2554 *  b2:
2555 *  b4: t->sw[0  ][j  ][k+1]
2556 *  b6: t->sw[0  ][j+1][k+1]
2557 */
2558static
2559bool handle_case_0x303(struct torus *t, int i, int j, int k)
2560{
2561	int ip1 = canonicalize(i + 1, t->x_sz);
2562	int jp1 = canonicalize(j + 1, t->y_sz);
2563	int jp2 = canonicalize(j + 2, t->y_sz);
2564
2565	if (safe_y_perpendicular(t, i, jp1, k) &&
2566	    install_tswitch(t, i, j, k,
2567			    tfind_2d_perpendicular(t->sw[ip1][jp1][k],
2568						   t->sw[i][jp1][k],
2569						   t->sw[i][jp2][k]))) {
2570		return true;
2571	}
2572	log_no_perp(t, 0x303, i, j, k, i, jp1, k);
2573
2574	if (safe_y_perpendicular(t, ip1, jp1, k) &&
2575	    install_tswitch(t, ip1, j, k,
2576			    tfind_2d_perpendicular(t->sw[i][jp1][k],
2577						   t->sw[ip1][jp1][k],
2578						   t->sw[ip1][jp2][k]))) {
2579		return true;
2580	}
2581	log_no_perp(t, 0x303, i, j, k, ip1, jp1, k);
2582	return false;
2583}
2584
2585static
2586bool handle_case_0x503(struct torus *t, int i, int j, int k)
2587{
2588	int ip1 = canonicalize(i + 1, t->x_sz);
2589	int kp1 = canonicalize(k + 1, t->z_sz);
2590	int kp2 = canonicalize(k + 2, t->z_sz);
2591
2592	if (safe_z_perpendicular(t, i, j, kp1) &&
2593	    install_tswitch(t, i, j, k,
2594			    tfind_2d_perpendicular(t->sw[ip1][j][kp1],
2595						   t->sw[i][j][kp1],
2596						   t->sw[i][j][kp2]))) {
2597		return true;
2598	}
2599	log_no_perp(t, 0x503, i, j, k, i, j, kp1);
2600
2601	if (safe_z_perpendicular(t, ip1, j, kp1) &&
2602	    install_tswitch(t, ip1, j, k,
2603			    tfind_2d_perpendicular(t->sw[i][j][kp1],
2604						   t->sw[ip1][j][kp1],
2605						   t->sw[ip1][j][kp2]))) {
2606		return true;
2607	}
2608	log_no_perp(t, 0x503, i, j, k, ip1, j, kp1);
2609	return false;
2610}
2611
2612static
2613bool handle_case_0x605(struct torus *t, int i, int j, int k)
2614{
2615	int jp1 = canonicalize(j + 1, t->y_sz);
2616	int kp1 = canonicalize(k + 1, t->z_sz);
2617	int kp2 = canonicalize(k + 2, t->z_sz);
2618
2619	if (safe_z_perpendicular(t, i, j, kp1) &&
2620	    install_tswitch(t, i, j, k,
2621			    tfind_2d_perpendicular(t->sw[i][jp1][kp1],
2622						   t->sw[i][j][kp1],
2623						   t->sw[i][j][kp2]))) {
2624		return true;
2625	}
2626	log_no_perp(t, 0x605, i, j, k, i, j, kp1);
2627
2628	if (safe_z_perpendicular(t, i, jp1, kp1) &&
2629	    install_tswitch(t, i, jp1, k,
2630			    tfind_2d_perpendicular(t->sw[i][j][kp1],
2631						   t->sw[i][jp1][kp1],
2632						   t->sw[i][jp1][kp2]))) {
2633		return true;
2634	}
2635	log_no_perp(t, 0x605, i, j, k, i, jp1, kp1);
2636	return false;
2637}
2638
2639/*
2640 * 2D case 0x30a
2641 *  b0: t->sw[i  ][j  ][0  ]
2642 *  b1:
2643 *  b2: t->sw[i  ][j+1][0  ]
2644 *  b3:
2645 *                                    O           O
2646 * 2D case 0x522                      .
2647 *  b0: t->sw[i  ][0  ][k  ]          .
2648 *  b1:                               .
2649 *  b4: t->sw[i  ][0  ][k+1]          .
2650 *  b5:                               .
2651 *                                    @           O
2652 * 2D case 0x644
2653 *  b0: t->sw[0  ][j  ][k  ]
2654 *  b2:
2655 *  b4: t->sw[0  ][j  ][k+1]
2656 *  b6:
2657 */
2658static
2659bool handle_case_0x30a(struct torus *t, int i, int j, int k)
2660{
2661	int im1 = canonicalize(i - 1, t->x_sz);
2662	int ip1 = canonicalize(i + 1, t->x_sz);
2663	int jp1 = canonicalize(j + 1, t->y_sz);
2664
2665	if (safe_x_perpendicular(t, i, j, k) &&
2666	    install_tswitch(t, ip1, j, k,
2667			    tfind_2d_perpendicular(t->sw[i][jp1][k],
2668						   t->sw[i][j][k],
2669						   t->sw[im1][j][k]))) {
2670		return true;
2671	}
2672	log_no_perp(t, 0x30a, i, j, k, i, j, k);
2673
2674	if (safe_x_perpendicular(t, i, jp1, k) &&
2675	    install_tswitch(t, ip1, jp1, k,
2676			    tfind_2d_perpendicular(t->sw[i][j][k],
2677						   t->sw[i][jp1][k],
2678						   t->sw[im1][jp1][k]))) {
2679		return true;
2680	}
2681	log_no_perp(t, 0x30a, i, j, k, i, jp1, k);
2682	return false;
2683}
2684
2685static
2686bool handle_case_0x522(struct torus *t, int i, int j, int k)
2687{
2688	int im1 = canonicalize(i - 1, t->x_sz);
2689	int ip1 = canonicalize(i + 1, t->x_sz);
2690	int kp1 = canonicalize(k + 1, t->z_sz);
2691
2692	if (safe_x_perpendicular(t, i, j, k) &&
2693	    install_tswitch(t, ip1, j, k,
2694			    tfind_2d_perpendicular(t->sw[i][j][kp1],
2695						   t->sw[i][j][k],
2696						   t->sw[im1][j][k]))) {
2697		return true;
2698	}
2699	log_no_perp(t, 0x522, i, j, k, i, j, k);
2700
2701	if (safe_x_perpendicular(t, i, j, kp1) &&
2702	    install_tswitch(t, ip1, j, kp1,
2703			    tfind_2d_perpendicular(t->sw[i][j][k],
2704						   t->sw[i][j][kp1],
2705						   t->sw[im1][j][kp1]))) {
2706		return true;
2707	}
2708	log_no_perp(t, 0x522, i, j, k, i, j, kp1);
2709	return false;
2710}
2711
2712static
2713bool handle_case_0x644(struct torus *t, int i, int j, int k)
2714{
2715	int jm1 = canonicalize(j - 1, t->y_sz);
2716	int jp1 = canonicalize(j + 1, t->y_sz);
2717	int kp1 = canonicalize(k + 1, t->z_sz);
2718
2719	if (safe_y_perpendicular(t, i, j, k) &&
2720	    install_tswitch(t, i, jp1, k,
2721			    tfind_2d_perpendicular(t->sw[i][j][kp1],
2722						   t->sw[i][j][k],
2723						   t->sw[i][jm1][k]))) {
2724		return true;
2725	}
2726	log_no_perp(t, 0x644, i, j, k, i, j, k);
2727
2728	if (safe_y_perpendicular(t, i, j, kp1) &&
2729	    install_tswitch(t, i, jp1, kp1,
2730			    tfind_2d_perpendicular(t->sw[i][j][k],
2731						   t->sw[i][j][kp1],
2732						   t->sw[i][jm1][kp1]))) {
2733		return true;
2734	}
2735	log_no_perp(t, 0x644, i, j, k, i, j, kp1);
2736	return false;
2737}
2738
2739/*
2740 * Handle the 2D cases where two existing edges meet at a corner.
2741 *
2742 */
2743
2744/*
2745 * 2D case 0x301
2746 *  b0:
2747 *  b1: t->sw[i+1][j  ][0  ]
2748 *  b2: t->sw[i  ][j+1][0  ]
2749 *  b3: t->sw[i+1][j+1][0  ]
2750 *                                    O . . . . . O
2751 * 2D case 0x501                                  .
2752 *  b0:                                           .
2753 *  b1: t->sw[i+1][0  ][k  ]                      .
2754 *  b4: t->sw[i  ][0  ][k+1]                      .
2755 *  b5: t->sw[i+1][0  ][k+1]                      .
2756 *                                    @           O
2757 * 2D case 0x601
2758 *  b0:
2759 *  b2: t->sw[0  ][j+1][k  ]
2760 *  b4: t->sw[0  ][j  ][k+1]
2761 *  b6: t->sw[0  ][j+1][k+1]
2762 */
2763static
2764bool handle_case_0x301(struct torus *t, int i, int j, int k)
2765{
2766	int ip1 = canonicalize(i + 1, t->x_sz);
2767	int jp1 = canonicalize(j + 1, t->y_sz);
2768
2769	if (install_tswitch(t, i, j, k,
2770			    tfind_face_corner(t->sw[ip1][j][k],
2771					      t->sw[ip1][jp1][k],
2772					      t->sw[i][jp1][k]))) {
2773		return true;
2774	}
2775	log_no_crnr(t, 0x301, i, j, k, i, j, k);
2776	return false;
2777}
2778
2779static
2780bool handle_case_0x501(struct torus *t, int i, int j, int k)
2781{
2782	int ip1 = canonicalize(i + 1, t->x_sz);
2783	int kp1 = canonicalize(k + 1, t->z_sz);
2784
2785	if (install_tswitch(t, i, j, k,
2786			    tfind_face_corner(t->sw[ip1][j][k],
2787					      t->sw[ip1][j][kp1],
2788					      t->sw[i][j][kp1]))) {
2789		return true;
2790	}
2791	log_no_crnr(t, 0x501, i, j, k, i, j, k);
2792	return false;
2793}
2794
2795static
2796bool handle_case_0x601(struct torus *t, int i, int j, int k)
2797{
2798	int jp1 = canonicalize(j + 1, t->y_sz);
2799	int kp1 = canonicalize(k + 1, t->z_sz);
2800
2801	if (install_tswitch(t, i, j, k,
2802			    tfind_face_corner(t->sw[i][jp1][k],
2803					      t->sw[i][jp1][kp1],
2804					      t->sw[i][j][kp1]))) {
2805		return true;
2806	}
2807	log_no_crnr(t, 0x601, i, j, k, i, j, k);
2808	return false;
2809}
2810
2811/*
2812 * 2D case 0x302
2813 *  b0: t->sw[i  ][j  ][0  ]
2814 *  b1:
2815 *  b2: t->sw[i  ][j+1][0  ]
2816 *  b3: t->sw[i+1][j+1][0  ]
2817 *                                    O . . . . . O
2818 * 2D case 0x502                      .
2819 *  b0: t->sw[i  ][0  ][k  ]          .
2820 *  b1:                               .
2821 *  b4: t->sw[i  ][0  ][k+1]          .
2822 *  b5: t->sw[i+1][0  ][k+1]          .
2823 *                                    @           O
2824 * 2D case 0x604
2825 *  b0: t->sw[0  ][j  ][k  ]
2826 *  b2:
2827 *  b4: t->sw[0  ][j  ][k+1]
2828 *  b6: t->sw[0  ][j+1][k+1]
2829 */
2830static
2831bool handle_case_0x302(struct torus *t, int i, int j, int k)
2832{
2833	int ip1 = canonicalize(i + 1, t->x_sz);
2834	int jp1 = canonicalize(j + 1, t->y_sz);
2835
2836	if (install_tswitch(t, ip1, j, k,
2837			    tfind_face_corner(t->sw[i][j][k],
2838					      t->sw[i][jp1][k],
2839					      t->sw[ip1][jp1][k]))) {
2840		return true;
2841	}
2842	log_no_crnr(t, 0x302, i, j, k, ip1, j, k);
2843	return false;
2844}
2845
2846static
2847bool handle_case_0x502(struct torus *t, int i, int j, int k)
2848{
2849	int ip1 = canonicalize(i + 1, t->x_sz);
2850	int kp1 = canonicalize(k + 1, t->z_sz);
2851
2852	if (install_tswitch(t, ip1, j, k,
2853			    tfind_face_corner(t->sw[i][j][k],
2854					      t->sw[i][j][kp1],
2855					      t->sw[ip1][j][kp1]))) {
2856		return true;
2857	}
2858	log_no_crnr(t, 0x502, i, j, k, ip1, j, k);
2859	return false;
2860}
2861
2862static
2863bool handle_case_0x604(struct torus *t, int i, int j, int k)
2864{
2865	int jp1 = canonicalize(j + 1, t->y_sz);
2866	int kp1 = canonicalize(k + 1, t->z_sz);
2867
2868	if (install_tswitch(t, i, jp1, k,
2869			    tfind_face_corner(t->sw[i][j][k],
2870					      t->sw[i][j][kp1],
2871					      t->sw[i][jp1][kp1]))) {
2872		return true;
2873	}
2874	log_no_crnr(t, 0x604, i, j, k, i, jp1, k);
2875	return false;
2876}
2877
2878
2879/*
2880 * 2D case 0x308
2881 *  b0: t->sw[i  ][j  ][0  ]
2882 *  b1: t->sw[i+1][j  ][0  ]
2883 *  b2: t->sw[i  ][j+1][0  ]
2884 *  b3:
2885 *                                    O           O
2886 * 2D case 0x520                      .
2887 *  b0: t->sw[i  ][0  ][k  ]          .
2888 *  b1: t->sw[i+1][0  ][k  ]          .
2889 *  b4: t->sw[i  ][0  ][k+1]          .
2890 *  b5:                               .
2891 *                                    @ . . . . . O
2892 * 2D case 0x640
2893 *  b0: t->sw[0  ][j  ][k  ]
2894 *  b2: t->sw[0  ][j+1][k  ]
2895 *  b4: t->sw[0  ][j  ][k+1]
2896 *  b6:
2897 */
2898static
2899bool handle_case_0x308(struct torus *t, int i, int j, int k)
2900{
2901	int ip1 = canonicalize(i + 1, t->x_sz);
2902	int jp1 = canonicalize(j + 1, t->y_sz);
2903
2904	if (install_tswitch(t, ip1, jp1, k,
2905			    tfind_face_corner(t->sw[ip1][j][k],
2906					      t->sw[i][j][k],
2907					      t->sw[i][jp1][k]))) {
2908		return true;
2909	}
2910	log_no_crnr(t, 0x308, i, j, k, ip1, jp1, k);
2911	return false;
2912}
2913
2914static
2915bool handle_case_0x520(struct torus *t, int i, int j, int k)
2916{
2917	int ip1 = canonicalize(i + 1, t->x_sz);
2918	int kp1 = canonicalize(k + 1, t->z_sz);
2919
2920	if (install_tswitch(t, ip1, j, kp1,
2921			    tfind_face_corner(t->sw[ip1][j][k],
2922					      t->sw[i][j][k],
2923					      t->sw[i][j][kp1]))) {
2924		return true;
2925	}
2926	log_no_crnr(t, 0x520, i, j, k, ip1, j, kp1);
2927	return false;
2928}
2929
2930static
2931bool handle_case_0x640(struct torus *t, int i, int j, int k)
2932{
2933	int jp1 = canonicalize(j + 1, t->y_sz);
2934	int kp1 = canonicalize(k + 1, t->z_sz);
2935
2936	if (install_tswitch(t, i, jp1, kp1,
2937			    tfind_face_corner(t->sw[i][jp1][k],
2938					      t->sw[i][j][k],
2939					      t->sw[i][j][kp1]))) {
2940		return true;
2941	}
2942	log_no_crnr(t, 0x640, i, j, k, i, jp1, kp1);
2943	return false;
2944}
2945
2946/*
2947 * 2D case 0x304
2948 *  b0: t->sw[i  ][j  ][0  ]
2949 *  b1: t->sw[i+1][j  ][0  ]
2950 *  b2:
2951 *  b3: t->sw[i+1][j+1][0  ]
2952 *                                    O           O
2953 * 2D case 0x510                                  .
2954 *  b0: t->sw[i  ][0  ][k  ]                      .
2955 *  b1: t->sw[i+1][0  ][k  ]                      .
2956 *  b4:                                           .
2957 *  b5: t->sw[i+1][0  ][k+1]                      .
2958 *                                    @ . . . . . O
2959 * 2D case 0x610
2960 *  b0: t->sw[0  ][j  ][k  ]
2961 *  b2: t->sw[0  ][j+1][k  ]
2962 *  b4:
2963 *  b6: t->sw[0  ][j+1][k+1]
2964 */
2965static
2966bool handle_case_0x304(struct torus *t, int i, int j, int k)
2967{
2968	int ip1 = canonicalize(i + 1, t->x_sz);
2969	int jp1 = canonicalize(j + 1, t->y_sz);
2970
2971	if (install_tswitch(t, i, jp1, k,
2972			    tfind_face_corner(t->sw[i][j][k],
2973					      t->sw[ip1][j][k],
2974					      t->sw[ip1][jp1][k]))) {
2975		return true;
2976	}
2977	log_no_crnr(t, 0x304, i, j, k, i, jp1, k);
2978	return false;
2979}
2980
2981static
2982bool handle_case_0x510(struct torus *t, int i, int j, int k)
2983{
2984	int ip1 = canonicalize(i + 1, t->x_sz);
2985	int kp1 = canonicalize(k + 1, t->z_sz);
2986
2987	if (install_tswitch(t, i, j, kp1,
2988			    tfind_face_corner(t->sw[i][j][k],
2989					      t->sw[ip1][j][k],
2990					      t->sw[ip1][j][kp1]))) {
2991		return true;
2992	}
2993	log_no_crnr(t, 0x510, i, j, k, i, j, kp1);
2994	return false;
2995}
2996
2997static
2998bool handle_case_0x610(struct torus *t, int i, int j, int k)
2999{
3000	int jp1 = canonicalize(j + 1, t->y_sz);
3001	int kp1 = canonicalize(k + 1, t->z_sz);
3002
3003	if (install_tswitch(t, i, j, kp1,
3004			    tfind_face_corner(t->sw[i][j][k],
3005					      t->sw[i][jp1][k],
3006					      t->sw[i][jp1][kp1]))) {
3007		return true;
3008	}
3009	log_no_crnr(t, 0x610, i, j, k, i, j, kp1);
3010	return false;
3011}
3012
3013/*
3014 * Handle the 3D cases where two existing edges meet at a corner.
3015 *
3016 */
3017
3018/*
3019 * 3D case 0x71f:                           O
3020 *                                        .   .
3021 *  b0:                                 .       .
3022 *  b1:                               .           .
3023 *  b2:                             .               .
3024 *  b3:                           O                   O
3025 *  b4:                                     O
3026 *  b5: t->sw[i+1][j  ][k+1]
3027 *  b6: t->sw[i  ][j+1][k+1]
3028 *  b7: t->sw[i+1][j+1][k+1]
3029 *                                          O
3030 *                                O                   O
3031 *
3032 *
3033 *
3034 *
3035 *                                          @
3036 */
3037static
3038bool handle_case_0x71f(struct torus *t, int i, int j, int k)
3039{
3040	int ip1 = canonicalize(i + 1, t->x_sz);
3041	int jp1 = canonicalize(j + 1, t->y_sz);
3042	int kp1 = canonicalize(k + 1, t->z_sz);
3043	int kp2 = canonicalize(k + 2, t->z_sz);
3044
3045	if (safe_z_perpendicular(t, ip1, jp1, kp1) &&
3046	    install_tswitch(t, ip1, jp1, k,
3047			    tfind_3d_perpendicular(t->sw[ip1][j][kp1],
3048						   t->sw[ip1][jp1][kp1],
3049						   t->sw[i][jp1][kp1],
3050						   t->sw[ip1][jp1][kp2]))) {
3051		return true;
3052	}
3053	log_no_perp(t, 0x71f, i, j, k, ip1, jp1, kp1);
3054	return false;
3055}
3056
3057/*
3058 * 3D case 0x72f:                           O
3059 *                                        .
3060 *  b0:                                 .
3061 *  b1:                               .
3062 *  b2:                             .
3063 *  b3:                           O                   O
3064 *  b4: t->sw[i  ][j  ][k+1]        .       O
3065 *  b5:                               .
3066 *  b6: t->sw[i  ][j+1][k+1]            .
3067 *  b7: t->sw[i+1][j+1][k+1]              .
3068 *                                          O
3069 *                                O                   O
3070 *
3071 *
3072 *
3073 *
3074 *                                          @
3075 */
3076static
3077bool handle_case_0x72f(struct torus *t, int i, int j, int k)
3078{
3079	int ip1 = canonicalize(i + 1, t->x_sz);
3080	int jp1 = canonicalize(j + 1, t->y_sz);
3081	int kp1 = canonicalize(k + 1, t->z_sz);
3082	int kp2 = canonicalize(k + 2, t->z_sz);
3083
3084	if (safe_z_perpendicular(t, i, jp1, kp1) &&
3085	    install_tswitch(t, i, jp1, k,
3086			    tfind_3d_perpendicular(t->sw[i][j][kp1],
3087						   t->sw[i][jp1][kp1],
3088						   t->sw[ip1][jp1][kp1],
3089						   t->sw[i][jp1][kp2]))) {
3090		return true;
3091	}
3092	log_no_perp(t, 0x72f, i, j, k, i, jp1, kp1);
3093	return false;
3094}
3095
3096/*
3097 * 3D case 0x737:                           O
3098 *                                        . .
3099 *  b0:                                 .   .
3100 *  b1:                               .     .
3101 *  b2:                             .       .
3102 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
3103 *  b4:                                     O
3104 *  b5:
3105 *  b6: t->sw[i  ][j+1][k+1]
3106 *  b7: t->sw[i+1][j+1][k+1]
3107 *                                          O
3108 *                                O                   O
3109 *
3110 *
3111 *
3112 *
3113 *                                          @
3114 */
3115static
3116bool handle_case_0x737(struct torus *t, int i, int j, int k)
3117{
3118	int ip1 = canonicalize(i + 1, t->x_sz);
3119	int jp1 = canonicalize(j + 1, t->y_sz);
3120	int jp2 = canonicalize(j + 2, t->y_sz);
3121	int kp1 = canonicalize(k + 1, t->z_sz);
3122
3123	if (safe_y_perpendicular(t, ip1, jp1, kp1) &&
3124	    install_tswitch(t, ip1, j, kp1,
3125			    tfind_3d_perpendicular(t->sw[i][jp1][kp1],
3126						   t->sw[ip1][jp1][kp1],
3127						   t->sw[ip1][jp1][k],
3128						   t->sw[ip1][jp2][kp1]))) {
3129		return true;
3130	}
3131	log_no_perp(t, 0x737, i, j, k, ip1, jp1, kp1);
3132	return false;
3133}
3134
3135/*
3136 * 3D case 0x73b:                           O
3137 *                                        .
3138 *  b0:                                 .
3139 *  b1:                               .
3140 *  b2: t->sw[i  ][j+1][k  ]        .
3141 *  b3:                           O                   O
3142 *  b4:                           .         O
3143 *  b5:                           .
3144 *  b6: t->sw[i  ][j+1][k+1]      .
3145 *  b7: t->sw[i+1][j+1][k+1]      .
3146 *                                .         O
3147 *                                O                   O
3148 *
3149 *
3150 *
3151 *
3152 *                                          @
3153 */
3154static
3155bool handle_case_0x73b(struct torus *t, int i, int j, int k)
3156{
3157	int ip1 = canonicalize(i + 1, t->x_sz);
3158	int jp1 = canonicalize(j + 1, t->y_sz);
3159	int jp2 = canonicalize(j + 2, t->y_sz);
3160	int kp1 = canonicalize(k + 1, t->z_sz);
3161
3162	if (safe_y_perpendicular(t, i, jp1, kp1) &&
3163	    install_tswitch(t, i, j, kp1,
3164			    tfind_3d_perpendicular(t->sw[i][jp1][k],
3165						   t->sw[i][jp1][kp1],
3166						   t->sw[ip1][jp1][kp1],
3167						   t->sw[i][jp2][kp1]))) {
3168		return true;
3169	}
3170	log_no_perp(t, 0x73b, i, j, k, i, jp1, kp1);
3171	return false;
3172}
3173
3174/*
3175 * 3D case 0x74f:                           O
3176 *                                            .
3177 *  b0:                                         .
3178 *  b1:                                           .
3179 *  b2:                                             .
3180 *  b3:                           O                   O
3181 *  b4: t->sw[i  ][j  ][k+1]                O       .
3182 *  b5: t->sw[i+1][j  ][k+1]                      .
3183 *  b6:                                         .
3184 *  b7: t->sw[i+1][j+1][k+1]                  .
3185 *                                          O
3186 *                                O                   O
3187 *
3188 *
3189 *
3190 *
3191 *                                          @
3192 */
3193static
3194bool handle_case_0x74f(struct torus *t, int i, int j, int k)
3195{
3196	int ip1 = canonicalize(i + 1, t->x_sz);
3197	int jp1 = canonicalize(j + 1, t->y_sz);
3198	int kp1 = canonicalize(k + 1, t->z_sz);
3199	int kp2 = canonicalize(k + 2, t->z_sz);
3200
3201	if (safe_z_perpendicular(t, ip1, j, kp1) &&
3202	    install_tswitch(t, ip1, j, k,
3203			    tfind_3d_perpendicular(t->sw[i][j][kp1],
3204						   t->sw[ip1][j][kp1],
3205						   t->sw[ip1][jp1][kp1],
3206						   t->sw[ip1][j][kp2]))) {
3207		return true;
3208	}
3209	log_no_perp(t, 0x74f, i, j, k, ip1, j, kp1);
3210	return false;
3211}
3212
3213/*
3214 * 3D case 0x757:                           O
3215 *                                          . .
3216 *  b0:                                     .   .
3217 *  b1:                                     .     .
3218 *  b2:                                     .       .
3219 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
3220 *  b4:                                     O
3221 *  b5: t->sw[i+1][j  ][k+1]
3222 *  b6:
3223 *  b7: t->sw[i+1][j+1][k+1]
3224 *                                          O
3225 *                                O                   O
3226 *
3227 *
3228 *
3229 *
3230 *                                          @
3231 */
3232static
3233bool handle_case_0x757(struct torus *t, int i, int j, int k)
3234{
3235	int ip1 = canonicalize(i + 1, t->x_sz);
3236	int ip2 = canonicalize(i + 2, t->x_sz);
3237	int jp1 = canonicalize(j + 1, t->y_sz);
3238	int kp1 = canonicalize(k + 1, t->z_sz);
3239
3240	if (safe_x_perpendicular(t, ip1, jp1, kp1) &&
3241	    install_tswitch(t, i, jp1, kp1,
3242			    tfind_3d_perpendicular(t->sw[ip1][j][kp1],
3243						   t->sw[ip1][jp1][kp1],
3244						   t->sw[ip1][jp1][k],
3245						   t->sw[ip2][jp1][kp1]))) {
3246		return true;
3247	}
3248	log_no_perp(t, 0x757, i, j, k, ip1, jp1, kp1);
3249	return false;
3250}
3251
3252/*
3253 * 3D case 0x75d:                           O
3254 *                                            .
3255 *  b0:                                         .
3256 *  b1: t->sw[i+1][j  ][k  ]                      .
3257 *  b2:                                             .
3258 *  b3:                           O                   O
3259 *  b4:                                     O         .
3260 *  b5: t->sw[i+1][j  ][k+1]                          .
3261 *  b6:                                               .
3262 *  b7: t->sw[i+1][j+1][k+1]                          .
3263 *                                          O         .
3264 *                                O                   O
3265 *
3266 *
3267 *
3268 *
3269 *                                          @
3270 */
3271static
3272bool handle_case_0x75d(struct torus *t, int i, int j, int k)
3273{
3274	int ip1 = canonicalize(i + 1, t->x_sz);
3275	int ip2 = canonicalize(i + 2, t->x_sz);
3276	int jp1 = canonicalize(j + 1, t->y_sz);
3277	int kp1 = canonicalize(k + 1, t->z_sz);
3278
3279	if (safe_x_perpendicular(t, ip1, j, kp1) &&
3280	    install_tswitch(t, i, j, kp1,
3281			    tfind_3d_perpendicular(t->sw[ip1][j][k],
3282						   t->sw[ip1][j][kp1],
3283						   t->sw[ip1][jp1][kp1],
3284						   t->sw[ip2][j][kp1]))) {
3285		return true;
3286	}
3287	log_no_perp(t, 0x75d, i, j, k, ip1, j, kp1);
3288	return false;
3289}
3290
3291/*
3292 * 3D case 0x773:                           O
3293 *                                          .
3294 *  b0:                                     .
3295 *  b1:                                     .
3296 *  b2: t->sw[i  ][j+1][k  ]                .
3297 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
3298 *  b4:                                     O
3299 *  b5:                                   .
3300 *  b6:                                 .
3301 *  b7: t->sw[i+1][j+1][k+1]          .
3302 *                                  .       O
3303 *                                O                   O
3304 *
3305 *
3306 *
3307 *
3308 *                                          @
3309 */
3310static
3311bool handle_case_0x773(struct torus *t, int i, int j, int k)
3312{
3313	int ip1 = canonicalize(i + 1, t->x_sz);
3314	int jp1 = canonicalize(j + 1, t->y_sz);
3315	int jp2 = canonicalize(j + 2, t->y_sz);
3316	int kp1 = canonicalize(k + 1, t->z_sz);
3317
3318	if (safe_y_perpendicular(t, ip1, jp1, k) &&
3319	    install_tswitch(t, ip1, j, k,
3320			    tfind_3d_perpendicular(t->sw[i][jp1][k],
3321						   t->sw[ip1][jp1][k],
3322						   t->sw[ip1][jp1][kp1],
3323						   t->sw[ip1][jp2][k]))) {
3324		return true;
3325	}
3326	log_no_perp(t, 0x773, i, j, k, ip1, jp1, k);
3327	return false;
3328}
3329
3330/*
3331 * 3D case 0x775:                           O
3332 *                                          .
3333 *  b0:                                     .
3334 *  b1: t->sw[i+1][j  ][k  ]                .
3335 *  b2:                                     .
3336 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
3337 *  b4:                                     O
3338 *  b5:                                       .
3339 *  b6:                                         .
3340 *  b7: t->sw[i+1][j+1][k+1]                      .
3341 *                                          O       .
3342 *                                O                   O
3343 *
3344 *
3345 *
3346 *
3347 *                                          @
3348 */
3349static
3350bool handle_case_0x775(struct torus *t, int i, int j, int k)
3351{
3352	int ip1 = canonicalize(i + 1, t->x_sz);
3353	int ip2 = canonicalize(i + 2, t->x_sz);
3354	int jp1 = canonicalize(j + 1, t->y_sz);
3355	int kp1 = canonicalize(k + 1, t->z_sz);
3356
3357	if (safe_x_perpendicular(t, ip1, jp1, k) &&
3358	    install_tswitch(t, i, jp1, k,
3359			    tfind_3d_perpendicular(t->sw[ip1][j][k],
3360						   t->sw[ip1][jp1][k],
3361						   t->sw[ip1][jp1][kp1],
3362						   t->sw[ip2][jp1][k]))) {
3363		return true;
3364	}
3365	log_no_perp(t, 0x775, i, j, k, ip1, jp1, k);
3366	return false;
3367}
3368
3369/*
3370 * 3D case 0x78f:                           O
3371 *
3372 *  b0:
3373 *  b1:
3374 *  b2:
3375 *  b3:                           O                   O
3376 *  b4: t->sw[i  ][j  ][k+1]        .       O       .
3377 *  b5: t->sw[i+1][j  ][k+1]          .           .
3378 *  b6: t->sw[i  ][j+1][k+1]            .       .
3379 *  b7:                                   .   .
3380 *                                          O
3381 *                                O                   O
3382 *
3383 *
3384 *
3385 *
3386 *                                          @
3387 */
3388static
3389bool handle_case_0x78f(struct torus *t, int i, int j, int k)
3390{
3391	int ip1 = canonicalize(i + 1, t->x_sz);
3392	int jp1 = canonicalize(j + 1, t->y_sz);
3393	int kp1 = canonicalize(k + 1, t->z_sz);
3394	int kp2 = canonicalize(k + 2, t->z_sz);
3395
3396	if (safe_z_perpendicular(t, i, j, kp1) &&
3397	    install_tswitch(t, i, j, k,
3398			    tfind_3d_perpendicular(t->sw[ip1][j][kp1],
3399						   t->sw[i][j][kp1],
3400						   t->sw[i][jp1][kp1],
3401						   t->sw[i][j][kp2]))) {
3402		return true;
3403	}
3404	log_no_perp(t, 0x78f, i, j, k, i, j, kp1);
3405	return false;
3406}
3407
3408/*
3409 * 3D case 0x7ab:                           O
3410 *
3411 *  b0:
3412 *  b1:
3413 *  b2: t->sw[i  ][j+1][k  ]
3414 *  b3:                           O                   O
3415 *  b4: t->sw[i  ][j  ][k+1]      . .       O
3416 *  b5:                           .   .
3417 *  b6: t->sw[i  ][j+1][k+1]      .     .
3418 *  b7:                           .       .
3419 *                                .         O
3420 *                                O                   O
3421 *
3422 *
3423 *
3424 *
3425 *                                          @
3426 */
3427static
3428bool handle_case_0x7ab(struct torus *t, int i, int j, int k)
3429{
3430	int im1 = canonicalize(i - 1, t->x_sz);
3431	int ip1 = canonicalize(i + 1, t->x_sz);
3432	int jp1 = canonicalize(j + 1, t->y_sz);
3433	int kp1 = canonicalize(k + 1, t->z_sz);
3434
3435	if (safe_x_perpendicular(t, i, jp1, kp1) &&
3436	    install_tswitch(t, ip1, jp1, kp1,
3437			    tfind_3d_perpendicular(t->sw[i][j][kp1],
3438						   t->sw[i][jp1][kp1],
3439						   t->sw[i][jp1][k],
3440						   t->sw[im1][jp1][kp1]))) {
3441		return true;
3442	}
3443	log_no_perp(t, 0x7ab, i, j, k, i, jp1, kp1);
3444	return false;
3445}
3446
3447/*
3448 * 3D case 0x7ae:                           O
3449 *
3450 *  b0: t->sw[i  ][j  ][k  ]
3451 *  b1:
3452 *  b2:
3453 *  b3:                           O                   O
3454 *  b4: t->sw[i  ][j  ][k+1]        .       O
3455 *  b5:                               .
3456 *  b6: t->sw[i  ][j+1][k+1]            .
3457 *  b7:                                   .
3458 *                                          O
3459 *                                O         .         O
3460 *                                          .
3461 *                                          .
3462 *                                          .
3463 *                                          .
3464 *                                          @
3465 */
3466static
3467bool handle_case_0x7ae(struct torus *t, int i, int j, int k)
3468{
3469	int im1 = canonicalize(i - 1, t->x_sz);
3470	int ip1 = canonicalize(i + 1, t->x_sz);
3471	int jp1 = canonicalize(j + 1, t->y_sz);
3472	int kp1 = canonicalize(k + 1, t->z_sz);
3473
3474	if (safe_x_perpendicular(t, i, j, kp1) &&
3475	    install_tswitch(t, ip1, j, kp1,
3476			    tfind_3d_perpendicular(t->sw[i][j][k],
3477						   t->sw[i][j][kp1],
3478						   t->sw[i][jp1][kp1],
3479						   t->sw[im1][j][kp1]))) {
3480		return true;
3481	}
3482	log_no_perp(t, 0x7ae, i, j, k, i, j, kp1);
3483	return false;
3484}
3485
3486/*
3487 * 3D case 0x7b3:                           O
3488 *
3489 *  b0:
3490 *  b1:
3491 *  b2: t->sw[i  ][j+1][k  ]
3492 *  b3: t->sw[i+1][j+1][k  ]      O                   O
3493 *  b4:                           .         O
3494 *  b5:                           .       .
3495 *  b6: t->sw[i  ][j+1][k+1]      .     .
3496 *  b7:                           .   .
3497 *                                . .       O
3498 *                                O                   O
3499 *
3500 *
3501 *
3502 *
3503 *                                          @
3504 */
3505static
3506bool handle_case_0x7b3(struct torus *t, int i, int j, int k)
3507{
3508	int ip1 = canonicalize(i + 1, t->x_sz);
3509	int jp1 = canonicalize(j + 1, t->y_sz);
3510	int jp2 = canonicalize(j + 2, t->y_sz);
3511	int kp1 = canonicalize(k + 1, t->z_sz);
3512
3513	if (safe_y_perpendicular(t, i, jp1, k) &&
3514	    install_tswitch(t, i, j, k,
3515			    tfind_3d_perpendicular(t->sw[i][jp1][kp1],
3516						   t->sw[i][jp1][k],
3517						   t->sw[ip1][jp1][k],
3518						   t->sw[i][jp2][k]))) {
3519		return true;
3520	}
3521	log_no_perp(t, 0x7b3, i, j, k, i, jp1, k);
3522	return false;
3523}
3524
3525/*
3526 * 3D case 0x7ba:                           O
3527 *
3528 *  b0: t->sw[i  ][j  ][k  ]
3529 *  b1:
3530 *  b2: t->sw[i  ][j+1][k  ]
3531 *  b3:                           O                   O
3532 *  b4:                           .         O
3533 *  b5:                           .
3534 *  b6: t->sw[i  ][j+1][k+1]      .
3535 *  b7:                           .
3536 *                                .         O
3537 *                                O                   O
3538 *                                  .
3539 *                                    .
3540 *                                      .
3541 *                                        .
3542 *                                          @
3543 */
3544static
3545bool handle_case_0x7ba(struct torus *t, int i, int j, int k)
3546{
3547	int im1 = canonicalize(i - 1, t->x_sz);
3548	int ip1 = canonicalize(i + 1, t->x_sz);
3549	int jp1 = canonicalize(j + 1, t->y_sz);
3550	int kp1 = canonicalize(k + 1, t->z_sz);
3551
3552	if (safe_x_perpendicular(t, i, jp1, k) &&
3553	    install_tswitch(t, ip1, jp1, k,
3554			    tfind_3d_perpendicular(t->sw[i][j][k],
3555						   t->sw[i][jp1][k],
3556						   t->sw[i][jp1][kp1],
3557						   t->sw[im1][jp1][k]))) {
3558		return true;
3559	}
3560	log_no_perp(t, 0x7ba, i, j, k, i, jp1, k);
3561	return false;
3562}
3563
3564/*
3565 * 3D case 0x7cd:                           O
3566 *
3567 *  b0:
3568 *  b1: t->sw[i+1][j  ][k  ]
3569 *  b2:
3570 *  b3:                           O                   O
3571 *  b4: t->sw[i  ][j  ][k+1]                O       . .
3572 *  b5: t->sw[i+1][j  ][k+1]                      .   .
3573 *  b6:                                         .     .
3574 *  b7:                                       .       .
3575 *                                          O         .
3576 *                                O                   O
3577 *
3578 *
3579 *
3580 *
3581 *                                          @
3582 */
3583static
3584bool handle_case_0x7cd(struct torus *t, int i, int j, int k)
3585{
3586	int ip1 = canonicalize(i + 1, t->x_sz);
3587	int jp1 = canonicalize(j + 1, t->y_sz);
3588	int jm1 = canonicalize(j - 1, t->y_sz);
3589	int kp1 = canonicalize(k + 1, t->z_sz);
3590
3591	if (safe_y_perpendicular(t, ip1, j, kp1) &&
3592	    install_tswitch(t, ip1, jp1, kp1,
3593			    tfind_3d_perpendicular(t->sw[i][j][kp1],
3594						   t->sw[ip1][j][kp1],
3595						   t->sw[ip1][j][k],
3596						   t->sw[ip1][jm1][kp1]))) {
3597		return true;
3598	}
3599	log_no_perp(t, 0x7cd, i, j, k, ip1, j, kp1);
3600	return false;
3601}
3602
3603/*
3604 * 3D case 0x7ce:                           O
3605 *
3606 *  b0: t->sw[i  ][j  ][k  ]
3607 *  b1:
3608 *  b2:
3609 *  b3:                           O                   O
3610 *  b4: t->sw[i  ][j  ][k+1]                O       .
3611 *  b5: t->sw[i+1][j  ][k+1]                      .
3612 *  b6:                                         .
3613 *  b7:                                       .
3614 *                                          O
3615 *                                O         .         O
3616 *                                          .
3617 *                                          .
3618 *                                          .
3619 *                                          .
3620 *                                          @
3621 */
3622static
3623bool handle_case_0x7ce(struct torus *t, int i, int j, int k)
3624{
3625	int ip1 = canonicalize(i + 1, t->x_sz);
3626	int jp1 = canonicalize(j + 1, t->y_sz);
3627	int jm1 = canonicalize(j - 1, t->y_sz);
3628	int kp1 = canonicalize(k + 1, t->z_sz);
3629
3630	if (safe_y_perpendicular(t, i, j, kp1) &&
3631	    install_tswitch(t, i, jp1, kp1,
3632			    tfind_3d_perpendicular(t->sw[i][j][k],
3633						   t->sw[i][j][kp1],
3634						   t->sw[ip1][j][kp1],
3635						   t->sw[i][jm1][kp1]))) {
3636		return true;
3637	}
3638	log_no_perp(t, 0x7ce, i, j, k, i, j, kp1);
3639	return false;
3640}
3641
3642/*
3643 * 3D case 0x7d5:                           O
3644 *
3645 *  b0:
3646 *  b1: t->sw[i+1][j  ][k  ]
3647 *  b2:
3648 *  b3: t->sw[i+1][j+1][k  ]      O                   O
3649 *  b4:                                     O         .
3650 *  b5: t->sw[i+1][j  ][k+1]                  .       .
3651 *  b6:                                         .     .
3652 *  b7:                                           .   .
3653 *                                          O       . .
3654 *                                O                   O
3655 *
3656 *
3657 *
3658 *
3659 *                                          @
3660 */
3661static
3662bool handle_case_0x7d5(struct torus *t, int i, int j, int k)
3663{
3664	int ip1 = canonicalize(i + 1, t->x_sz);
3665	int ip2 = canonicalize(i + 2, t->x_sz);
3666	int jp1 = canonicalize(j + 1, t->y_sz);
3667	int kp1 = canonicalize(k + 1, t->z_sz);
3668
3669	if (safe_x_perpendicular(t, ip1, j, k) &&
3670	    install_tswitch(t, i, j, k,
3671			    tfind_3d_perpendicular(t->sw[ip1][j][kp1],
3672						   t->sw[ip1][j][k],
3673						   t->sw[ip1][jp1][k],
3674						   t->sw[ip2][j][k]))) {
3675		return true;
3676	}
3677	log_no_perp(t, 0x7d5, i, j, k, ip1, j, k);
3678	return false;
3679}
3680
3681/*
3682 * 3D case 0x7dc:                           O
3683 *
3684 *  b0: t->sw[i  ][j  ][k  ]
3685 *  b1: t->sw[i+1][j  ][k  ]
3686 *  b2:
3687 *  b3:                           O                   O
3688 *  b4:                                     O         .
3689 *  b5: t->sw[i+1][j  ][k+1]                          .
3690 *  b6:                                               .
3691 *  b7:                                               .
3692 *                                          O         .
3693 *                                O                   O
3694 *                                                  .
3695 *                                                .
3696 *                                              .
3697 *                                            .
3698 *                                          @
3699 */
3700static
3701bool handle_case_0x7dc(struct torus *t, int i, int j, int k)
3702{
3703	int ip1 = canonicalize(i + 1, t->x_sz);
3704	int jp1 = canonicalize(j + 1, t->y_sz);
3705	int jm1 = canonicalize(j - 1, t->y_sz);
3706	int kp1 = canonicalize(k + 1, t->z_sz);
3707
3708	if (safe_y_perpendicular(t, ip1, j, k) &&
3709	    install_tswitch(t, ip1, jp1, k,
3710			    tfind_3d_perpendicular(t->sw[i][j][k],
3711						   t->sw[ip1][j][k],
3712						   t->sw[ip1][j][kp1],
3713						   t->sw[ip1][jm1][k]))) {
3714		return true;
3715	}
3716	log_no_perp(t, 0x7dc, i, j, k, ip1, j, k);
3717	return false;
3718}
3719
3720/*
3721 * 3D case 0x7ea:                           O
3722 *
3723 *  b0: t->sw[i  ][j  ][k  ]
3724 *  b1:
3725 *  b2: t->sw[i  ][j+1][k  ]
3726 *  b3:                            O                   O
3727 *  b4: t->sw[i  ][j  ][k+1]                 O
3728 *  b5:
3729 *  b6:
3730 *  b7:
3731 *                                          O
3732 *                                O         .         O
3733 *                                  .       .
3734 *                                    .     .
3735 *                                      .   .
3736 *                                        . .
3737 *                                          @
3738 */
3739static
3740bool handle_case_0x7ea(struct torus *t, int i, int j, int k)
3741{
3742	int im1 = canonicalize(i - 1, t->x_sz);
3743	int ip1 = canonicalize(i + 1, t->x_sz);
3744	int jp1 = canonicalize(j + 1, t->y_sz);
3745	int kp1 = canonicalize(k + 1, t->z_sz);
3746
3747	if (safe_x_perpendicular(t, i, j, k) &&
3748	    install_tswitch(t, ip1, j, k,
3749			    tfind_3d_perpendicular(t->sw[i][j][kp1],
3750						   t->sw[i][j][k],
3751						   t->sw[i][jp1][k],
3752						   t->sw[im1][j][k]))) {
3753		return true;
3754	}
3755	log_no_perp(t, 0x7ea, i, j, k, i, j, k);
3756	return false;
3757}
3758
3759/*
3760 * 3D case 0x7ec:                           O
3761 *
3762 *  b0: t->sw[i  ][j  ][k  ]
3763 *  b1: t->sw[i+1][j  ][k  ]
3764 *  b2:
3765 *  b3:                           O                   O
3766 *  b4: t->sw[i  ][j  ][k+1]                O
3767 *  b5:
3768 *  b6:
3769 *  b7:
3770 *                                          O
3771 *                                O         .         O
3772 *                                          .       .
3773 *                                          .     .
3774 *                                          .   .
3775 *                                          . .
3776 *                                          @
3777 */
3778static
3779bool handle_case_0x7ec(struct torus *t, int i, int j, int k)
3780{
3781	int ip1 = canonicalize(i + 1, t->x_sz);
3782	int jp1 = canonicalize(j + 1, t->y_sz);
3783	int jm1 = canonicalize(j - 1, t->y_sz);
3784	int kp1 = canonicalize(k + 1, t->z_sz);
3785
3786	if (safe_y_perpendicular(t, i, j, k) &&
3787	    install_tswitch(t, i, jp1, k,
3788			    tfind_3d_perpendicular(t->sw[i][j][kp1],
3789						   t->sw[i][j][k],
3790						   t->sw[ip1][j][k],
3791						   t->sw[i][jm1][k]))) {
3792		return true;
3793	}
3794	log_no_perp(t, 0x7ec, i, j, k, i, j, k);
3795	return false;
3796}
3797
3798/*
3799 * 3D case 0x7f1:                           O
3800 *
3801 *  b0:
3802 *  b1: t->sw[i+1][j  ][k  ]
3803 *  b2: t->sw[i  ][j+1][k  ]
3804 *  b3: t->sw[i+1][j+1][k  ]      O                   O
3805 *  b4:                                     O
3806 *  b5:                                   .   .
3807 *  b6:                                 .       .
3808 *  b7:                               .           .
3809 *                                  .       O       .
3810 *                                O                   O
3811 *
3812 *
3813 *
3814 *
3815 *                                          @
3816 */
3817static
3818bool handle_case_0x7f1(struct torus *t, int i, int j, int k)
3819{
3820	int ip1 = canonicalize(i + 1, t->x_sz);
3821	int jp1 = canonicalize(j + 1, t->y_sz);
3822	int km1 = canonicalize(k - 1, t->z_sz);
3823	int kp1 = canonicalize(k + 1, t->z_sz);
3824
3825	if (safe_z_perpendicular(t, ip1, jp1, k) &&
3826	    install_tswitch(t, ip1, jp1, kp1,
3827			    tfind_3d_perpendicular(t->sw[ip1][j][k],
3828						   t->sw[ip1][jp1][k],
3829						   t->sw[i][jp1][k],
3830						   t->sw[ip1][jp1][km1]))) {
3831		return true;
3832	}
3833	log_no_perp(t, 0x7f1, i, j, k, ip1, jp1, k);
3834	return false;
3835}
3836
3837/*
3838 * 3D case 0x7f2:                           O
3839 *
3840 *  b0: t->sw[i  ][j  ][k  ]
3841 *  b1:
3842 *  b2: t->sw[i  ][j+1][k  ]
3843 *  b3: t->sw[i+1][j+1][k  ]      O                   O
3844 *  b4:                                     O
3845 *  b5:                                   .
3846 *  b6:                                 .
3847 *  b7:                               .
3848 *                                  .       O
3849 *                                O                   O
3850 *                                  .
3851 *                                    .
3852 *                                      .
3853 *                                        .
3854 *                                          @
3855 */
3856static
3857bool handle_case_0x7f2(struct torus *t, int i, int j, int k)
3858{
3859	int ip1 = canonicalize(i + 1, t->x_sz);
3860	int jp1 = canonicalize(j + 1, t->y_sz);
3861	int km1 = canonicalize(k - 1, t->z_sz);
3862	int kp1 = canonicalize(k + 1, t->z_sz);
3863
3864	if (safe_z_perpendicular(t, i, jp1, k) &&
3865	    install_tswitch(t, i, jp1, kp1,
3866			    tfind_3d_perpendicular(t->sw[i][j][k],
3867						   t->sw[i][jp1][k],
3868						   t->sw[ip1][jp1][k],
3869						   t->sw[i][jp1][km1]))) {
3870		return true;
3871	}
3872	log_no_perp(t, 0x7f2, i, j, k, i, jp1, k);
3873	return false;
3874}
3875
3876/*
3877 * 3D case 0x7f4:                           O
3878 *
3879 *  b0: t->sw[i  ][j  ][k  ]
3880 *  b1: t->sw[i+1][j  ][k  ]
3881 *  b2:
3882 *  b3: t->sw[i+1][j+1][k  ]      O                   O
3883 *  b4:                                     O
3884 *  b5:                                       .
3885 *  b6:                                         .
3886 *  b7:                                           .
3887 *                                          O       .
3888 *                                O                   O
3889 *                                                  .
3890 *                                                .
3891 *                                              .
3892 *                                            .
3893 *                                          @
3894 */
3895static
3896bool handle_case_0x7f4(struct torus *t, int i, int j, int k)
3897{
3898	int ip1 = canonicalize(i + 1, t->x_sz);
3899	int jp1 = canonicalize(j + 1, t->y_sz);
3900	int km1 = canonicalize(k - 1, t->z_sz);
3901	int kp1 = canonicalize(k + 1, t->z_sz);
3902
3903	if (safe_z_perpendicular(t, ip1, j, k) &&
3904	    install_tswitch(t, ip1, j, kp1,
3905			    tfind_3d_perpendicular(t->sw[i][j][k],
3906						   t->sw[ip1][j][k],
3907						   t->sw[ip1][jp1][k],
3908						   t->sw[ip1][j][km1]))) {
3909		return true;
3910	}
3911	log_no_perp(t, 0x7f4, i, j, k, ip1, j, k);
3912	return false;
3913}
3914
3915/*
3916 * 3D case 0x7f8:                           O
3917 *
3918 *  b0: t->sw[i  ][j  ][k  ]
3919 *  b1: t->sw[i+1][j  ][k  ]
3920 *  b2: t->sw[i  ][j+1][k  ]
3921 *  b3:                           O                   O
3922 *  b4:                                     O
3923 *  b5:
3924 *  b6:
3925 *  b7:
3926 *                                          O
3927 *                                O                   O
3928 *                                  .               .
3929 *                                    .           .
3930 *                                      .       .
3931 *                                        .   .
3932 *                                          @
3933 */
3934static
3935bool handle_case_0x7f8(struct torus *t, int i, int j, int k)
3936{
3937	int ip1 = canonicalize(i + 1, t->x_sz);
3938	int jp1 = canonicalize(j + 1, t->y_sz);
3939	int km1 = canonicalize(k - 1, t->z_sz);
3940	int kp1 = canonicalize(k + 1, t->z_sz);
3941
3942	if (safe_z_perpendicular(t, i, j, k) &&
3943	    install_tswitch(t, i, j, kp1,
3944			    tfind_3d_perpendicular(t->sw[ip1][j][k],
3945						   t->sw[i][j][k],
3946						   t->sw[i][jp1][k],
3947						   t->sw[i][j][km1]))) {
3948		return true;
3949	}
3950	log_no_perp(t, 0x7f8, i, j, k, i, j, k);
3951	return false;
3952}
3953
3954/*
3955 * Handle the cases where three existing edges meet at a corner.
3956 */
3957
3958/*
3959 * 3D case 0x717:                           O
3960 *                                        . . .
3961 *  b0:                                 .   .   .
3962 *  b1:                               .     .     .
3963 *  b2:                             .       .       .
3964 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
3965 *  b4:                                     O
3966 *  b5: t->sw[i+1][j  ][k+1]
3967 *  b6: t->sw[i  ][j+1][k+1]
3968 *  b7: t->sw[i+1][j+1][k+1]
3969 *                                          O
3970 *                                O                   O
3971 *
3972 *
3973 *
3974 *
3975 *                                          @
3976 */
3977static
3978bool handle_case_0x717(struct torus *t, int i, int j, int k)
3979{
3980	int ip1 = canonicalize(i + 1, t->x_sz);
3981	int jp1 = canonicalize(j + 1, t->y_sz);
3982	int kp1 = canonicalize(k + 1, t->z_sz);
3983
3984	if (install_tswitch(t, i, j, kp1,
3985			    tfind_face_corner(t->sw[i][jp1][kp1],
3986					      t->sw[ip1][jp1][kp1],
3987					      t->sw[ip1][j][kp1]))) {
3988		return true;
3989	}
3990	log_no_crnr(t, 0x717, i, j, k, i, j, kp1);
3991
3992	if (install_tswitch(t, ip1, j, k,
3993			    tfind_face_corner(t->sw[ip1][jp1][k],
3994					      t->sw[ip1][jp1][kp1],
3995					      t->sw[ip1][j][kp1]))) {
3996		return true;
3997	}
3998	log_no_crnr(t, 0x717, i, j, k, ip1, j, k);
3999
4000	if (install_tswitch(t, i, jp1, k,
4001			    tfind_face_corner(t->sw[ip1][jp1][k],
4002					      t->sw[ip1][jp1][kp1],
4003					      t->sw[i][jp1][kp1]))) {
4004		return true;
4005	}
4006	log_no_crnr(t, 0x717, i, j, k, i, jp1, k);
4007	return false;
4008}
4009
4010/*
4011 * 3D case 0x72b:                           O
4012 *                                        .
4013 *  b0:                                 .
4014 *  b1:                               .
4015 *  b2: t->sw[i  ][j+1][k  ]        .
4016 *  b3:                           O                   O
4017 *  b4: t->sw[i  ][j  ][k+1]      . .       O
4018 *  b5:                           .   .
4019 *  b6: t->sw[i  ][j+1][k+1]      .     .
4020 *  b7: t->sw[i+1][j+1][k+1]      .       .
4021 *                                .         O
4022 *                                O                   O
4023 *
4024 *
4025 *
4026 *
4027 *                                          @
4028 */
4029static
4030bool handle_case_0x72b(struct torus *t, int i, int j, int k)
4031{
4032	int ip1 = canonicalize(i + 1, t->x_sz);
4033	int jp1 = canonicalize(j + 1, t->y_sz);
4034	int kp1 = canonicalize(k + 1, t->z_sz);
4035
4036	if (install_tswitch(t, ip1, j, kp1,
4037			    tfind_face_corner(t->sw[i][j][kp1],
4038					      t->sw[i][jp1][kp1],
4039					      t->sw[ip1][jp1][kp1]))) {
4040		return true;
4041	}
4042	log_no_crnr(t, 0x72b, i, j, k, ip1, j, kp1);
4043
4044	if (install_tswitch(t, i, j, k,
4045			    tfind_face_corner(t->sw[i][jp1][k],
4046					      t->sw[i][jp1][kp1],
4047					      t->sw[i][j][kp1]))) {
4048		return true;
4049	}
4050	log_no_crnr(t, 0x72b, i, j, k, i, j, k);
4051
4052	if (install_tswitch(t, ip1, jp1, k,
4053			    tfind_face_corner(t->sw[i][jp1][k],
4054					      t->sw[i][jp1][kp1],
4055					      t->sw[ip1][jp1][kp1]))) {
4056		return true;
4057	}
4058	log_no_crnr(t, 0x72b, i, j, k, ip1, jp1, k);
4059	return false;
4060}
4061
4062/*
4063 * 3D case 0x74d:                           O
4064 *                                            .
4065 *  b0:                                         .
4066 *  b1: t->sw[i+1][j  ][k  ]                      .
4067 *  b2:                                             .
4068 *  b3:                           O                   O
4069 *  b4: t->sw[i  ][j  ][k+1]                O       . .
4070 *  b5: t->sw[i+1][j  ][k+1]                      .   .
4071 *  b6:                                         .     .
4072 *  b7: t->sw[i+1][j+1][k+1]                  .       .
4073 *                                          O         .
4074 *                                O                   O
4075 *
4076 *
4077 *
4078 *
4079 *                                          @
4080 */
4081static
4082bool handle_case_0x74d(struct torus *t, int i, int j, int k)
4083{
4084	int ip1 = canonicalize(i + 1, t->x_sz);
4085	int jp1 = canonicalize(j + 1, t->y_sz);
4086	int kp1 = canonicalize(k + 1, t->z_sz);
4087
4088	if (install_tswitch(t, i, jp1, kp1,
4089			    tfind_face_corner(t->sw[i][j][kp1],
4090					      t->sw[ip1][j][kp1],
4091					      t->sw[ip1][jp1][kp1]))) {
4092		return true;
4093	}
4094	log_no_crnr(t, 0x74d, i, j, k, i, jp1, kp1);
4095
4096	if (install_tswitch(t, i, j, k,
4097			    tfind_face_corner(t->sw[ip1][j][k],
4098					      t->sw[ip1][j][kp1],
4099					      t->sw[i][j][kp1]))) {
4100		return true;
4101	}
4102	log_no_crnr(t, 0x74d, i, j, k, i, j, k);
4103
4104	if (install_tswitch(t, ip1, jp1, k,
4105			    tfind_face_corner(t->sw[ip1][j][k],
4106					      t->sw[ip1][j][kp1],
4107					      t->sw[ip1][jp1][kp1]))) {
4108		return true;
4109	}
4110	log_no_crnr(t, 0x74d, i, j, k, ip1, jp1, k);
4111	return false;
4112}
4113
4114/*
4115 * 3D case 0x771:                           O
4116 *                                          .
4117 *  b0:                                     .
4118 *  b1: t->sw[i+1][j  ][k  ]                .
4119 *  b2: t->sw[i  ][j+1][k  ]                .
4120 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
4121 *  b4:                                     O
4122 *  b5:                                   .   .
4123 *  b6:                                 .       .
4124 *  b7: t->sw[i+1][j+1][k+1]          .           .
4125 *                                  .       O       .
4126 *                                O                   O
4127 *
4128 *
4129 *
4130 *
4131 *                                          @
4132 */
4133static
4134bool handle_case_0x771(struct torus *t, int i, int j, int k)
4135{
4136	int ip1 = canonicalize(i + 1, t->x_sz);
4137	int jp1 = canonicalize(j + 1, t->y_sz);
4138	int kp1 = canonicalize(k + 1, t->z_sz);
4139
4140	if (install_tswitch(t, i, j, k,
4141			    tfind_face_corner(t->sw[i][jp1][k],
4142					      t->sw[ip1][jp1][k],
4143					      t->sw[ip1][j][k]))) {
4144		return true;
4145	}
4146	log_no_crnr(t, 0x771, i, j, k, i, j, k);
4147
4148	if (install_tswitch(t, ip1, j, kp1,
4149			    tfind_face_corner(t->sw[ip1][jp1][kp1],
4150					      t->sw[ip1][jp1][k],
4151					      t->sw[ip1][j][k]))) {
4152		return true;
4153	}
4154	log_no_crnr(t, 0x771, i, j, k, ip1, j, kp1);
4155
4156	if (install_tswitch(t, i, jp1, kp1,
4157			    tfind_face_corner(t->sw[ip1][jp1][kp1],
4158					      t->sw[ip1][jp1][k],
4159					      t->sw[i][jp1][k]))) {
4160		return true;
4161	}
4162	log_no_crnr(t, 0x771, i, j, k, i, jp1, kp1);
4163	return false;
4164}
4165
4166/*
4167 * 3D case 0x78e:                           O
4168 *
4169 *  b0: t->sw[i  ][j  ][k  ]
4170 *  b1:
4171 *  b2:
4172 *  b3:                           O                   O
4173 *  b4: t->sw[i  ][j  ][k+1]        .       O       .
4174 *  b5: t->sw[i+1][j  ][k+1]          .           .
4175 *  b6: t->sw[i  ][j+1][k+1]            .       .
4176 *  b7:                                   .   .
4177 *                                          O
4178 *                                O         .         O
4179 *                                          .
4180 *                                          .
4181 *                                          .
4182 *                                          .
4183 *                                          @
4184 */
4185static
4186bool handle_case_0x78e(struct torus *t, int i, int j, int k)
4187{
4188	int ip1 = canonicalize(i + 1, t->x_sz);
4189	int jp1 = canonicalize(j + 1, t->y_sz);
4190	int kp1 = canonicalize(k + 1, t->z_sz);
4191
4192	if (install_tswitch(t, ip1, jp1, kp1,
4193			    tfind_face_corner(t->sw[ip1][j][kp1],
4194					      t->sw[i][j][kp1],
4195					      t->sw[i][jp1][kp1]))) {
4196		return true;
4197	}
4198	log_no_crnr(t, 0x78e, i, j, k, ip1, jp1, kp1);
4199
4200	if (install_tswitch(t, ip1, j, k,
4201			    tfind_face_corner(t->sw[i][j][k],
4202					      t->sw[i][j][kp1],
4203					      t->sw[ip1][j][kp1]))) {
4204		return true;
4205	}
4206	log_no_crnr(t, 0x78e, i, j, k, ip1, j, k);
4207
4208	if (install_tswitch(t, i, jp1, k,
4209			    tfind_face_corner(t->sw[i][j][k],
4210					      t->sw[i][j][kp1],
4211					      t->sw[i][jp1][kp1]))) {
4212		return true;
4213	}
4214	log_no_crnr(t, 0x78e, i, j, k, i, jp1, k);
4215	return false;
4216}
4217
4218/*
4219 * 3D case 0x7b2:                           O
4220 *
4221 *  b0: t->sw[i  ][j  ][k  ]
4222 *  b1:
4223 *  b2: t->sw[i  ][j+1][k  ]
4224 *  b3: t->sw[i+1][j+1][k  ]      O                   O
4225 *  b4:                           .         O
4226 *  b5:                           .       .
4227 *  b6: t->sw[i  ][j+1][k+1]      .     .
4228 *  b7:                           .   .
4229 *                                . .       O
4230 *                                O                   O
4231 *                                  .
4232 *                                    .
4233 *                                      .
4234 *                                        .
4235 *                                          @
4236 */
4237static
4238bool handle_case_0x7b2(struct torus *t, int i, int j, int k)
4239{
4240	int ip1 = canonicalize(i + 1, t->x_sz);
4241	int jp1 = canonicalize(j + 1, t->y_sz);
4242	int kp1 = canonicalize(k + 1, t->z_sz);
4243
4244	if (install_tswitch(t, ip1, j, k,
4245			    tfind_face_corner(t->sw[i][j][k],
4246					      t->sw[i][jp1][k],
4247					      t->sw[ip1][jp1][k]))) {
4248		return true;
4249	}
4250	log_no_crnr(t, 0x7b2, i, j, k, ip1, j, k);
4251
4252	if (install_tswitch(t, ip1, jp1, kp1,
4253			    tfind_face_corner(t->sw[i][jp1][kp1],
4254					      t->sw[i][jp1][k],
4255					      t->sw[ip1][jp1][k]))) {
4256		return true;
4257	}
4258	log_no_crnr(t, 0x7b2, i, j, k, ip1, jp1, kp1);
4259
4260	if (install_tswitch(t, i, j, kp1,
4261			    tfind_face_corner(t->sw[i][jp1][kp1],
4262					      t->sw[i][jp1][k],
4263					      t->sw[i][j][k]))) {
4264		return true;
4265	}
4266	log_no_crnr(t, 0x7b2, i, j, k, i, j, kp1);
4267	return false;
4268}
4269
4270/*
4271 * 3D case 0x7d4:                           O
4272 *
4273 *  b0: t->sw[i  ][j  ][k  ]
4274 *  b1: t->sw[i+1][j  ][k  ]
4275 *  b2:
4276 *  b3: t->sw[i+1][j+1][k  ]      O                   O
4277 *  b4:                                     O         .
4278 *  b5: t->sw[i+1][j  ][k+1]                  .       .
4279 *  b6:                                         .     .
4280 *  b7:                                           .   .
4281 *                                          O       . .
4282 *                                O                   O
4283 *                                                  .
4284 *                                                .
4285 *                                              .
4286 *                                            .
4287 *                                          @
4288 */
4289static
4290bool handle_case_0x7d4(struct torus *t, int i, int j, int k)
4291{
4292	int ip1 = canonicalize(i + 1, t->x_sz);
4293	int jp1 = canonicalize(j + 1, t->y_sz);
4294	int kp1 = canonicalize(k + 1, t->z_sz);
4295
4296	if (install_tswitch(t, i, jp1, k,
4297			    tfind_face_corner(t->sw[i][j][k],
4298					      t->sw[ip1][j][k],
4299					      t->sw[ip1][jp1][k]))) {
4300		return true;
4301	}
4302	log_no_crnr(t, 0x7d4, i, j, k, i, jp1, k);
4303
4304	if (install_tswitch(t, i, j, kp1,
4305			    tfind_face_corner(t->sw[ip1][j][kp1],
4306					      t->sw[ip1][j][k],
4307					      t->sw[i][j][k]))) {
4308		return true;
4309	}
4310	log_no_crnr(t, 0x7d4, i, j, k, i, j, kp1);
4311
4312	if (install_tswitch(t, ip1, jp1, kp1,
4313			    tfind_face_corner(t->sw[ip1][j][kp1],
4314					      t->sw[ip1][j][k],
4315					      t->sw[ip1][jp1][k]))) {
4316		return true;
4317	}
4318	log_no_crnr(t, 0x7d4, i, j, k, ip1, jp1, kp1);
4319	return false;
4320}
4321
4322/*
4323 * 3D case 0x7e8:                           O
4324 *
4325 *  b0: t->sw[i  ][j  ][k  ]
4326 *  b1: t->sw[i+1][j  ][k  ]
4327 *  b2: t->sw[i  ][j+1][k  ]
4328 *  b3:                           O                   O
4329 *  b4: t->sw[i  ][j  ][k+1]                O
4330 *  b5:
4331 *  b6:
4332 *  b7:
4333 *                                          O
4334 *                                O         .         O
4335 *                                  .       .       .
4336 *                                    .     .     .
4337 *                                      .   .   .
4338 *                                        . . .
4339 *                                          @
4340 */
4341static
4342bool handle_case_0x7e8(struct torus *t, int i, int j, int k)
4343{
4344	int ip1 = canonicalize(i + 1, t->x_sz);
4345	int jp1 = canonicalize(j + 1, t->y_sz);
4346	int kp1 = canonicalize(k + 1, t->z_sz);
4347
4348	if (install_tswitch(t, ip1, jp1, k,
4349			    tfind_face_corner(t->sw[ip1][j][k],
4350					      t->sw[i][j][k],
4351					      t->sw[i][jp1][k]))) {
4352		return true;
4353	}
4354	log_no_crnr(t, 0x7e8, i, j, k, ip1, jp1, k);
4355
4356	if (install_tswitch(t, ip1, j, kp1,
4357			    tfind_face_corner(t->sw[ip1][j][k],
4358					      t->sw[i][j][k],
4359					      t->sw[i][j][kp1]))) {
4360		return true;
4361	}
4362	log_no_crnr(t, 0x7e8, i, j, k, ip1, j, kp1);
4363
4364	if (install_tswitch(t, i, jp1, kp1,
4365			    tfind_face_corner(t->sw[i][jp1][k],
4366					      t->sw[i][j][k],
4367					      t->sw[i][j][kp1]))) {
4368		return true;
4369	}
4370	log_no_crnr(t, 0x7e8, i, j, k, i, jp1, kp1);
4371	return false;
4372}
4373
4374/*
4375 * Handle the cases where four corners on a single face are missing.
4376 */
4377
4378/*
4379 * 3D case 0x70f:                           O
4380 *                                        .   .
4381 *  b0:                                 .       .
4382 *  b1:                               .           .
4383 *  b2:                             .               .
4384 *  b3:                           O                   O
4385 *  b4: t->sw[i  ][j  ][k+1]        .       O       .
4386 *  b5: t->sw[i+1][j  ][k+1]          .           .
4387 *  b6: t->sw[i  ][j+1][k+1]            .       .
4388 *  b7: t->sw[i+1][j+1][k+1]              .   .
4389 *                                          O
4390 *                                O                   O
4391 *
4392 *
4393 *
4394 *
4395 *                                          @
4396 */
4397static
4398bool handle_case_0x70f(struct torus *t, int i, int j, int k)
4399{
4400	if (handle_case_0x71f(t, i, j, k))
4401		return true;
4402
4403	if (handle_case_0x72f(t, i, j, k))
4404		return true;
4405
4406	if (handle_case_0x74f(t, i, j, k))
4407		return true;
4408
4409	return handle_case_0x78f(t, i, j, k);
4410}
4411
4412/*
4413 * 3D case 0x733:                           O
4414 *                                        . .
4415 *  b0:                                 .   .
4416 *  b1:                               .     .
4417 *  b2: t->sw[i  ][j+1][k  ]        .       .
4418 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
4419 *  b4:                           .         O
4420 *  b5:                           .       .
4421 *  b6: t->sw[i  ][j+1][k+1]      .     .
4422 *  b7: t->sw[i+1][j+1][k+1]      .   .
4423 *                                . .       O
4424 *                                O                   O
4425 *
4426 *
4427 *
4428 *
4429 *                                          @
4430 */
4431static
4432bool handle_case_0x733(struct torus *t, int i, int j, int k)
4433{
4434	if (handle_case_0x737(t, i, j, k))
4435		return true;
4436
4437	if (handle_case_0x73b(t, i, j, k))
4438		return true;
4439
4440	if (handle_case_0x773(t, i, j, k))
4441		return true;
4442
4443	return handle_case_0x7b3(t, i, j, k);
4444}
4445
4446/*
4447 * 3D case 0x755:                           O
4448 *                                          . .
4449 *  b0:                                     .   .
4450 *  b1: t->sw[i+1][j  ][k  ]                .     .
4451 *  b2:                                     .       .
4452 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
4453 *  b4:                                     O         .
4454 *  b5: t->sw[i+1][j  ][k+1]                  .       .
4455 *  b6:                                         .     .
4456 *  b7: t->sw[i+1][j+1][k+1]                      .   .
4457 *                                          O       . .
4458 *                                O                   O
4459 *
4460 *
4461 *
4462 *
4463 *                                          @
4464 */
4465static
4466bool handle_case_0x755(struct torus *t, int i, int j, int k)
4467{
4468	if (handle_case_0x757(t, i, j, k))
4469		return true;
4470
4471	if (handle_case_0x75d(t, i, j, k))
4472		return true;
4473
4474	if (handle_case_0x775(t, i, j, k))
4475		return true;
4476
4477	return handle_case_0x7d5(t, i, j, k);
4478}
4479
4480/*
4481 * 3D case 0x7aa:                           O
4482 *
4483 *  b0: t->sw[i  ][j  ][k  ]
4484 *  b1:
4485 *  b2: t->sw[i  ][j+1][k  ]
4486 *  b3:                           O                   O
4487 *  b4: t->sw[i  ][j  ][k+1]      . .       O
4488 *  b5:                           .   .
4489 *  b6: t->sw[i  ][j+1][k+1]      .     .
4490 *  b7:                           .       .
4491 *                                .         O
4492 *                                O         .         O
4493 *                                  .       .
4494 *                                    .     .
4495 *                                      .   .
4496 *                                        . .
4497 *                                          @
4498 */
4499static
4500bool handle_case_0x7aa(struct torus *t, int i, int j, int k)
4501{
4502	if (handle_case_0x7ab(t, i, j, k))
4503		return true;
4504
4505	if (handle_case_0x7ae(t, i, j, k))
4506		return true;
4507
4508	if (handle_case_0x7ba(t, i, j, k))
4509		return true;
4510
4511	return handle_case_0x7ea(t, i, j, k);
4512}
4513
4514/*
4515 * 3D case 0x7cc:                           O
4516 *
4517 *  b0: t->sw[i  ][j  ][k  ]
4518 *  b1: t->sw[i+1][j  ][k  ]
4519 *  b2:
4520 *  b3:                           O                   O
4521 *  b4: t->sw[i  ][j  ][k+1]                O       . .
4522 *  b5: t->sw[i+1][j  ][k+1]                      .   .
4523 *  b6:                                         .     .
4524 *  b7:                                       .       .
4525 *                                          O         .
4526 *                                O         .         O
4527 *                                          .       .
4528 *                                          .     .
4529 *                                          .   .
4530 *                                          . .
4531 *                                          @
4532 */
4533static
4534bool handle_case_0x7cc(struct torus *t, int i, int j, int k)
4535{
4536	if (handle_case_0x7cd(t, i, j, k))
4537		return true;
4538
4539	if (handle_case_0x7ce(t, i, j, k))
4540		return true;
4541
4542	if (handle_case_0x7dc(t, i, j, k))
4543		return true;
4544
4545	return handle_case_0x7ec(t, i, j, k);
4546}
4547
4548/*
4549 * 3D case 0x7f0:                           O
4550 *
4551 *  b0: t->sw[i  ][j  ][k  ]
4552 *  b1: t->sw[i+1][j  ][k  ]
4553 *  b2: t->sw[i  ][j+1][k  ]
4554 *  b3: t->sw[i+1][j+1][k  ]      O                   O
4555 *  b4:                                     O
4556 *  b5:                                   .   .
4557 *  b6:                                 .       .
4558 *  b7:                               .           .
4559 *                                  .       O       .
4560 *                                O                   O
4561 *                                  .               .
4562 *                                    .           .
4563 *                                      .       .
4564 *                                        .   .
4565 *                                          @
4566 */
4567static
4568bool handle_case_0x7f0(struct torus *t, int i, int j, int k)
4569{
4570	if (handle_case_0x7f1(t, i, j, k))
4571		return true;
4572
4573	if (handle_case_0x7f2(t, i, j, k))
4574		return true;
4575
4576	if (handle_case_0x7f4(t, i, j, k))
4577		return true;
4578
4579	return handle_case_0x7f8(t, i, j, k);
4580}
4581
4582/*
4583 * Handle the cases where three corners on a single face are missing.
4584 */
4585
4586
4587/*
4588 * 3D case 0x707:                           O
4589 *                                        . . .
4590 *  b0:                                 .   .   .
4591 *  b1:                               .     .     .
4592 *  b2:                             .       .       .
4593 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
4594 *  b4: t->sw[i  ][j  ][k+1]        .       O       .
4595 *  b5: t->sw[i+1][j  ][k+1]          .           .
4596 *  b6: t->sw[i  ][j+1][k+1]            .       .
4597 *  b7: t->sw[i+1][j+1][k+1]              .   .
4598 *                                          O
4599 *                                O                   O
4600 *
4601 *
4602 *
4603 *
4604 *                                          @
4605 */
4606static
4607bool handle_case_0x707(struct torus *t, int i, int j, int k)
4608{
4609	int ip1 = canonicalize(i + 1, t->x_sz);
4610	int jp1 = canonicalize(j + 1, t->y_sz);
4611	int kp1 = canonicalize(k + 1, t->z_sz);
4612
4613	if (install_tswitch(t, ip1, j, k,
4614			    tfind_face_corner(t->sw[ip1][jp1][k],
4615					      t->sw[ip1][jp1][kp1],
4616					      t->sw[ip1][j][kp1]))) {
4617		return true;
4618	}
4619	log_no_crnr(t, 0x707, i, j, k, ip1, j, k);
4620
4621	if (install_tswitch(t, i, jp1, k,
4622			    tfind_face_corner(t->sw[ip1][jp1][k],
4623					      t->sw[ip1][jp1][kp1],
4624					      t->sw[i][jp1][kp1]))) {
4625		return true;
4626	}
4627	log_no_crnr(t, 0x707, i, j, k, i, jp1, k);
4628	return false;
4629}
4630
4631/*
4632 * 3D case 0x70b:                           O
4633 *                                        .   .
4634 *  b0:                                 .       .
4635 *  b1:                               .           .
4636 *  b2: t->sw[i  ][j+1][k  ]        .               .
4637 *  b3:                           O                   O
4638 *  b4: t->sw[i  ][j  ][k+1]      . .       O       .
4639 *  b5: t->sw[i+1][j  ][k+1]      .   .           .
4640 *  b6: t->sw[i  ][j+1][k+1]      .     .       .
4641 *  b7: t->sw[i+1][j+1][k+1]      .       .   .
4642 *                                .         O
4643 *                                O                   O
4644 *
4645 *
4646 *
4647 *
4648 *                                          @
4649 */
4650static
4651bool handle_case_0x70b(struct torus *t, int i, int j, int k)
4652{
4653	int ip1 = canonicalize(i + 1, t->x_sz);
4654	int jp1 = canonicalize(j + 1, t->y_sz);
4655	int kp1 = canonicalize(k + 1, t->z_sz);
4656
4657	if (install_tswitch(t, i, j, k,
4658			    tfind_face_corner(t->sw[i][jp1][k],
4659					      t->sw[i][jp1][kp1],
4660					      t->sw[i][j][kp1]))) {
4661		return true;
4662	}
4663	log_no_crnr(t, 0x70b, i, j, k, i, j, k);
4664
4665	if (install_tswitch(t, ip1, jp1, k,
4666			    tfind_face_corner(t->sw[i][jp1][k],
4667					      t->sw[i][jp1][kp1],
4668					      t->sw[ip1][jp1][kp1]))) {
4669		return true;
4670	}
4671	log_no_crnr(t, 0x70b, i, j, k, ip1, jp1, k);
4672	return false;
4673}
4674
4675/*
4676 * 3D case 0x70d:                           O
4677 *                                        .   .
4678 *  b0:                                 .       .
4679 *  b1: t->sw[i+1][j  ][k  ]          .           .
4680 *  b2:                             .               .
4681 *  b3:                           O                   O
4682 *  b4: t->sw[i  ][j  ][k+1]        .       O       . .
4683 *  b5: t->sw[i+1][j  ][k+1]          .           .   .
4684 *  b6: t->sw[i  ][j+1][k+1]            .       .     .
4685 *  b7: t->sw[i+1][j+1][k+1]              .   .       .
4686 *                                          O         .
4687 *                                O                   O
4688 *
4689 *
4690 *
4691 *
4692 *                                          @
4693 */
4694static
4695bool handle_case_0x70d(struct torus *t, int i, int j, int k)
4696{
4697	int ip1 = canonicalize(i + 1, t->x_sz);
4698	int jp1 = canonicalize(j + 1, t->y_sz);
4699	int kp1 = canonicalize(k + 1, t->z_sz);
4700
4701	if (install_tswitch(t, i, j, k,
4702			    tfind_face_corner(t->sw[ip1][j][k],
4703					      t->sw[ip1][j][kp1],
4704					      t->sw[i][j][kp1]))) {
4705		return true;
4706	}
4707	log_no_crnr(t, 0x70d, i, j, k, i, j, k);
4708
4709	if (install_tswitch(t, ip1, jp1, k,
4710			    tfind_face_corner(t->sw[ip1][j][k],
4711					      t->sw[ip1][j][kp1],
4712					      t->sw[ip1][jp1][kp1]))) {
4713		return true;
4714	}
4715	log_no_crnr(t, 0x70d, i, j, k, ip1, jp1, k);
4716	return false;
4717}
4718
4719/*
4720 * 3D case 0x70e:                           O
4721 *                                        .   .
4722 *  b0: t->sw[i  ][j  ][k  ]            .       .
4723 *  b1:                               .           .
4724 *  b2:                             .               .
4725 *  b3:                           O                   O
4726 *  b4: t->sw[i  ][j  ][k+1]        .       O       .
4727 *  b5: t->sw[i+1][j  ][k+1]          .           .
4728 *  b6: t->sw[i  ][j+1][k+1]            .       .
4729 *  b7: t->sw[i+1][j+1][k+1]              .   .
4730 *                                          O
4731 *                                O         .         O
4732 *                                          .
4733 *                                          .
4734 *                                          .
4735 *                                          .
4736 *                                          @
4737 */
4738static
4739bool handle_case_0x70e(struct torus *t, int i, int j, int k)
4740{
4741	int ip1 = canonicalize(i + 1, t->x_sz);
4742	int jp1 = canonicalize(j + 1, t->y_sz);
4743	int kp1 = canonicalize(k + 1, t->z_sz);
4744
4745	if (install_tswitch(t, ip1, j, k,
4746			    tfind_face_corner(t->sw[i][j][k],
4747					      t->sw[i][j][kp1],
4748					      t->sw[ip1][j][kp1]))) {
4749		return true;
4750	}
4751	log_no_crnr(t, 0x70e, i, j, k, ip1, j, k);
4752
4753	if (install_tswitch(t, i, jp1, k,
4754			    tfind_face_corner(t->sw[i][j][k],
4755					      t->sw[i][j][kp1],
4756					      t->sw[i][jp1][kp1]))) {
4757		return true;
4758	}
4759	log_no_crnr(t, 0x70e, i, j, k, i, jp1, k);
4760	return false;
4761}
4762
4763/*
4764 * 3D case 0x713:                           O
4765 *                                        . . .
4766 *  b0:                                 .   .   .
4767 *  b1:                               .     .     .
4768 *  b2: t->sw[i  ][j+1][k  ]        .       .       .
4769 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
4770 *  b4:                           .         O
4771 *  b5: t->sw[i+1][j  ][k+1]      .       .
4772 *  b6: t->sw[i  ][j+1][k+1]      .     .
4773 *  b7: t->sw[i+1][j+1][k+1]      .   .
4774 *                                . .       O
4775 *                                O                   O
4776 *
4777 *
4778 *
4779 *
4780 *                                          @
4781 */
4782static
4783bool handle_case_0x713(struct torus *t, int i, int j, int k)
4784{
4785	int ip1 = canonicalize(i + 1, t->x_sz);
4786	int jp1 = canonicalize(j + 1, t->y_sz);
4787	int kp1 = canonicalize(k + 1, t->z_sz);
4788
4789	if (install_tswitch(t, ip1, j, k,
4790			    tfind_face_corner(t->sw[ip1][jp1][k],
4791					      t->sw[ip1][jp1][kp1],
4792					      t->sw[ip1][j][kp1]))) {
4793		return true;
4794	}
4795	log_no_crnr(t, 0x713, i, j, k, ip1, j, k);
4796
4797	if (install_tswitch(t, i, j, kp1,
4798			    tfind_face_corner(t->sw[ip1][j][kp1],
4799					      t->sw[ip1][jp1][kp1],
4800					      t->sw[i][jp1][kp1]))) {
4801		return true;
4802	}
4803	log_no_crnr(t, 0x713, i, j, k, i, j, kp1);
4804	return false;
4805}
4806
4807/*
4808 * 3D case 0x715:                           O
4809 *                                        . . .
4810 *  b0:                                 .   .   .
4811 *  b1: t->sw[i+1][j  ][k  ]          .     .     .
4812 *  b2:                             .       .       .
4813 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
4814 *  b4:                                     O         .
4815 *  b5: t->sw[i+1][j  ][k+1]                  .       .
4816 *  b6: t->sw[i  ][j+1][k+1]                    .     .
4817 *  b7: t->sw[i+1][j+1][k+1]                      .   .
4818 *                                          O       . .
4819 *                                O                   O
4820 *
4821 *
4822 *
4823 *
4824 *                                          @
4825 */
4826static
4827bool handle_case_0x715(struct torus *t, int i, int j, int k)
4828{
4829	int ip1 = canonicalize(i + 1, t->x_sz);
4830	int jp1 = canonicalize(j + 1, t->y_sz);
4831	int kp1 = canonicalize(k + 1, t->z_sz);
4832
4833	if (install_tswitch(t, i, jp1, k,
4834			    tfind_face_corner(t->sw[ip1][jp1][k],
4835					      t->sw[ip1][jp1][kp1],
4836					      t->sw[i][jp1][kp1]))) {
4837		return true;
4838	}
4839	log_no_crnr(t, 0x715, i, j, k, i, jp1, k);
4840
4841	if (install_tswitch(t, i, j, kp1,
4842			    tfind_face_corner(t->sw[ip1][j][kp1],
4843					      t->sw[ip1][jp1][kp1],
4844					      t->sw[i][jp1][kp1]))) {
4845		return true;
4846	}
4847	log_no_crnr(t, 0x715, i, j, k, i, j, kp1);
4848	return false;
4849}
4850
4851/*
4852 * 3D case 0x723:                           O
4853 *                                        . .
4854 *  b0:                                 .   .
4855 *  b1:                               .     .
4856 *  b2: t->sw[i  ][j+1][k  ]        .       .
4857 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
4858 *  b4: t->sw[i  ][j  ][k+1]      . .       O
4859 *  b5:                           .   .   .
4860 *  b6: t->sw[i  ][j+1][k+1]      .     .
4861 *  b7: t->sw[i+1][j+1][k+1]      .   .   .
4862 *                                . .       O
4863 *                                O                   O
4864 *
4865 *
4866 *
4867 *
4868 *                                          @
4869 */
4870static
4871bool handle_case_0x723(struct torus *t, int i, int j, int k)
4872{
4873	int ip1 = canonicalize(i + 1, t->x_sz);
4874	int jp1 = canonicalize(j + 1, t->y_sz);
4875	int kp1 = canonicalize(k + 1, t->z_sz);
4876
4877	if (install_tswitch(t, i, j, k,
4878			    tfind_face_corner(t->sw[i][jp1][k],
4879					      t->sw[i][jp1][kp1],
4880					      t->sw[i][j][kp1]))) {
4881		return true;
4882	}
4883	log_no_crnr(t, 0x723, i, j, k, i, j, k);
4884
4885	if (install_tswitch(t, ip1, j, kp1,
4886			    tfind_face_corner(t->sw[i][j][kp1],
4887					      t->sw[i][jp1][kp1],
4888					      t->sw[ip1][jp1][kp1]))) {
4889		return true;
4890	}
4891	log_no_crnr(t, 0x723, i, j, k, ip1, j, kp1);
4892	return false;
4893}
4894
4895/*
4896 * 3D case 0x72a:                           O
4897 *                                        .
4898 *  b0: t->sw[i  ][j  ][k  ]            .
4899 *  b1:                               .
4900 *  b2: t->sw[i  ][j+1][k  ]        .
4901 *  b3:                           O                   O
4902 *  b4: t->sw[i  ][j  ][k+1]      . .       O
4903 *  b5:                           .   .
4904 *  b6: t->sw[i  ][j+1][k+1]      .     .
4905 *  b7: t->sw[i+1][j+1][k+1]      .       .
4906 *                                .         O
4907 *                                O         .         O
4908 *                                  .       .
4909 *                                    .     .
4910 *                                      .   .
4911 *                                        . .
4912 *                                          @
4913 */
4914static
4915bool handle_case_0x72a(struct torus *t, int i, int j, int k)
4916{
4917	int ip1 = canonicalize(i + 1, t->x_sz);
4918	int jp1 = canonicalize(j + 1, t->y_sz);
4919	int kp1 = canonicalize(k + 1, t->z_sz);
4920
4921	if (install_tswitch(t, ip1, jp1, k,
4922			    tfind_face_corner(t->sw[i][jp1][k],
4923					      t->sw[i][jp1][kp1],
4924					      t->sw[ip1][jp1][kp1]))) {
4925		return true;
4926	}
4927	log_no_crnr(t, 0x72a, i, j, k, ip1, jp1, k);
4928
4929	if (install_tswitch(t, ip1, j, kp1,
4930			    tfind_face_corner(t->sw[i][j][kp1],
4931					      t->sw[i][jp1][kp1],
4932					      t->sw[ip1][jp1][kp1]))) {
4933		return true;
4934	}
4935	log_no_crnr(t, 0x72a, i, j, k, ip1, j, kp1);
4936	return false;
4937}
4938
4939/*
4940 * 3D case 0x731:                           O
4941 *                                        . .
4942 *  b0:                                 .   .
4943 *  b1: t->sw[i+1][j  ][k  ]          .     .
4944 *  b2: t->sw[i  ][j+1][k  ]        .       .
4945 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
4946 *  b4:                           .         O
4947 *  b5:                           .       .   .
4948 *  b6: t->sw[i  ][j+1][k+1]      .     .       .
4949 *  b7: t->sw[i+1][j+1][k+1]      .   .           .
4950 *                                . .       O       .
4951 *                                O                   O
4952 *
4953 *
4954 *
4955 *
4956 *                                          @
4957 */
4958static
4959bool handle_case_0x731(struct torus *t, int i, int j, int k)
4960{
4961	int ip1 = canonicalize(i + 1, t->x_sz);
4962	int jp1 = canonicalize(j + 1, t->y_sz);
4963	int kp1 = canonicalize(k + 1, t->z_sz);
4964
4965	if (install_tswitch(t, i, j, k,
4966			    tfind_face_corner(t->sw[ip1][j][k],
4967					      t->sw[ip1][jp1][k],
4968					      t->sw[i][jp1][k]))) {
4969		return true;
4970	}
4971	log_no_crnr(t, 0x731, i, j, k, i, j, k);
4972
4973	if (install_tswitch(t, ip1, j, kp1,
4974			    tfind_face_corner(t->sw[ip1][j][k],
4975					      t->sw[ip1][jp1][k],
4976					      t->sw[ip1][jp1][kp1]))) {
4977		return true;
4978	}
4979	log_no_crnr(t, 0x731, i, j, k, ip1, j, kp1);
4980	return false;
4981}
4982
4983/*
4984 * 3D case 0x732:                           O
4985 *                                        . .
4986 *  b0: t->sw[i  ][j  ][k  ]            .   .
4987 *  b1:                               .     .
4988 *  b2: t->sw[i  ][j+1][k  ]        .       .
4989 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
4990 *  b4:                           .         O
4991 *  b5:                           .       .
4992 *  b6: t->sw[i  ][j+1][k+1]      .     .
4993 *  b7: t->sw[i+1][j+1][k+1]      .   .
4994 *                                . .       O
4995 *                                O                   O
4996 *                                  .
4997 *                                    .
4998 *                                      .
4999 *                                        .
5000 *                                          @
5001 */
5002static
5003bool handle_case_0x732(struct torus *t, int i, int j, int k)
5004{
5005	int ip1 = canonicalize(i + 1, t->x_sz);
5006	int jp1 = canonicalize(j + 1, t->y_sz);
5007	int kp1 = canonicalize(k + 1, t->z_sz);
5008
5009	if (install_tswitch(t, ip1, j, k,
5010			    tfind_face_corner(t->sw[i][j][k],
5011					      t->sw[i][jp1][k],
5012					      t->sw[ip1][jp1][k]))) {
5013		return true;
5014	}
5015	log_no_crnr(t, 0x732, i, j, k, ip1, j, k);
5016
5017	if (install_tswitch(t, i, j, kp1,
5018			    tfind_face_corner(t->sw[i][j][k],
5019					      t->sw[i][jp1][k],
5020					      t->sw[i][jp1][kp1]))) {
5021		return true;
5022	}
5023	log_no_crnr(t, 0x732, i, j, k, i, j, kp1);
5024	return false;
5025}
5026
5027/*
5028 * 3D case 0x745:                           O
5029 *                                          . .
5030 *  b0:                                     .   .
5031 *  b1: t->sw[i+1][j  ][k  ]                .     .
5032 *  b2:                                     .       .
5033 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
5034 *  b4: t->sw[i  ][j  ][k+1]                O       . .
5035 *  b5: t->sw[i+1][j  ][k+1]                  .   .   .
5036 *  b6:                                         .     .
5037 *  b7: t->sw[i+1][j+1][k+1]                  .   .   .
5038 *                                          O       . .
5039 *                                O                   O
5040 *
5041 *
5042 *
5043 *
5044 *                                          @
5045 */
5046static
5047bool handle_case_0x745(struct torus *t, int i, int j, int k)
5048{
5049	int ip1 = canonicalize(i + 1, t->x_sz);
5050	int jp1 = canonicalize(j + 1, t->y_sz);
5051	int kp1 = canonicalize(k + 1, t->z_sz);
5052
5053	if (install_tswitch(t, i, j, k,
5054			    tfind_face_corner(t->sw[ip1][j][k],
5055					      t->sw[ip1][j][kp1],
5056					      t->sw[i][j][kp1]))) {
5057		return true;
5058	}
5059	log_no_crnr(t, 0x745, i, j, k, i, j, k);
5060
5061	if (install_tswitch(t, i, jp1, kp1,
5062			    tfind_face_corner(t->sw[i][j][kp1],
5063					      t->sw[ip1][j][kp1],
5064					      t->sw[ip1][jp1][kp1]))) {
5065		return true;
5066	}
5067	log_no_crnr(t, 0x745, i, j, k, i, jp1, kp1);
5068	return false;
5069}
5070
5071/*
5072 * 3D case 0x74c:                           O
5073 *                                            .
5074 *  b0: t->sw[i  ][j  ][k  ]                    .
5075 *  b1: t->sw[i+1][j  ][k  ]                      .
5076 *  b2:                                             .
5077 *  b3:                           O                   O
5078 *  b4: t->sw[i  ][j  ][k+1]                O       . .
5079 *  b5: t->sw[i+1][j  ][k+1]                      .   .
5080 *  b6:                                         .     .
5081 *  b7: t->sw[i+1][j+1][k+1]                  .       .
5082 *                                          O         .
5083 *                                O         .         O
5084 *                                          .       .
5085 *                                          .     .
5086 *                                          .   .
5087 *                                          . .
5088 *                                          @
5089 */
5090static
5091bool handle_case_0x74c(struct torus *t, int i, int j, int k)
5092{
5093	int ip1 = canonicalize(i + 1, t->x_sz);
5094	int jp1 = canonicalize(j + 1, t->y_sz);
5095	int kp1 = canonicalize(k + 1, t->z_sz);
5096
5097	if (install_tswitch(t, ip1, jp1, k,
5098			    tfind_face_corner(t->sw[ip1][j][k],
5099					      t->sw[ip1][j][kp1],
5100					      t->sw[ip1][jp1][kp1]))) {
5101		return true;
5102	}
5103	log_no_crnr(t, 0x74c, i, j, k, ip1, jp1, k);
5104
5105	if (install_tswitch(t, i, jp1, kp1,
5106			    tfind_face_corner(t->sw[i][j][kp1],
5107					      t->sw[ip1][j][kp1],
5108					      t->sw[ip1][jp1][kp1]))) {
5109		return true;
5110	}
5111	log_no_crnr(t, 0x74c, i, j, k, i, jp1, kp1);
5112	return false;
5113}
5114
5115/*
5116 * 3D case 0x751:                           O
5117 *                                          . .
5118 *  b0:                                     .   .
5119 *  b1: t->sw[i+1][j  ][k  ]                .     .
5120 *  b2: t->sw[i  ][j+1][k  ]                .       .
5121 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
5122 *  b4:                                     O         .
5123 *  b5: t->sw[i+1][j  ][k+1]              .   .       .
5124 *  b6:                                 .       .     .
5125 *  b7: t->sw[i+1][j+1][k+1]          .           .   .
5126 *                                  .       O       . .
5127 *                                O                   O
5128 *
5129 *
5130 *
5131 *
5132 *                                          @
5133 */
5134static
5135bool handle_case_0x751(struct torus *t, int i, int j, int k)
5136{
5137	int ip1 = canonicalize(i + 1, t->x_sz);
5138	int jp1 = canonicalize(j + 1, t->y_sz);
5139	int kp1 = canonicalize(k + 1, t->z_sz);
5140
5141	if (install_tswitch(t, i, j, k,
5142			    tfind_face_corner(t->sw[ip1][j][k],
5143					      t->sw[ip1][jp1][k],
5144					      t->sw[i][jp1][k]))) {
5145		return true;
5146	}
5147	log_no_crnr(t, 0x751, i, j, k, i, j, k);
5148
5149	if (install_tswitch(t, i, jp1, kp1,
5150			    tfind_face_corner(t->sw[i][jp1][k],
5151					      t->sw[ip1][jp1][k],
5152					      t->sw[ip1][jp1][kp1]))) {
5153		return true;
5154	}
5155	log_no_crnr(t, 0x751, i, j, k, i, jp1, kp1);
5156	return false;
5157}
5158
5159/*
5160 * 3D case 0x754:                           O
5161 *                                          . .
5162 *  b0: t->sw[i  ][j  ][k  ]                .   .
5163 *  b1: t->sw[i+1][j  ][k  ]                .     .
5164 *  b2:                                     .       .
5165 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
5166 *  b4:                                     O         .
5167 *  b5: t->sw[i+1][j  ][k+1]                  .       .
5168 *  b6:                                         .     .
5169 *  b7: t->sw[i+1][j+1][k+1]                      .   .
5170 *                                          O       . .
5171 *                                O                   O
5172 *                                                  .
5173 *                                                .
5174 *                                              .
5175 *                                            .
5176 *                                          @
5177 */
5178static
5179bool handle_case_0x754(struct torus *t, int i, int j, int k)
5180{
5181	int ip1 = canonicalize(i + 1, t->x_sz);
5182	int jp1 = canonicalize(j + 1, t->y_sz);
5183	int kp1 = canonicalize(k + 1, t->z_sz);
5184
5185	if (install_tswitch(t, i, jp1, k,
5186			    tfind_face_corner(t->sw[i][j][k],
5187					      t->sw[ip1][j][k],
5188					      t->sw[ip1][jp1][k]))) {
5189		return true;
5190	}
5191	log_no_crnr(t, 0x754, i, j, k, i, jp1, k);
5192
5193	if (install_tswitch(t, i, j, kp1,
5194			    tfind_face_corner(t->sw[i][j][k],
5195					      t->sw[ip1][j][k],
5196					      t->sw[ip1][j][kp1]))) {
5197		return true;
5198	}
5199	log_no_crnr(t, 0x754, i, j, k, i, j, kp1);
5200	return false;
5201}
5202
5203/*
5204 * 3D case 0x770:                           O
5205 *                                          .
5206 *  b0: t->sw[i  ][j  ][k  ]                .
5207 *  b1: t->sw[i+1][j  ][k  ]                .
5208 *  b2: t->sw[i  ][j+1][k  ]                .
5209 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
5210 *  b4:                                     O
5211 *  b5:                                   .   .
5212 *  b6:                                 .       .
5213 *  b7: t->sw[i+1][j+1][k+1]          .           .
5214 *                                  .       O       .
5215 *                                O                   O
5216 *                                  .               .
5217 *                                    .           .
5218 *                                      .       .
5219 *                                        .   .
5220 *                                          @
5221 */
5222static
5223bool handle_case_0x770(struct torus *t, int i, int j, int k)
5224{
5225	int ip1 = canonicalize(i + 1, t->x_sz);
5226	int jp1 = canonicalize(j + 1, t->y_sz);
5227	int kp1 = canonicalize(k + 1, t->z_sz);
5228
5229	if (install_tswitch(t, ip1, j, kp1,
5230			    tfind_face_corner(t->sw[ip1][j][k],
5231					      t->sw[ip1][jp1][k],
5232					      t->sw[ip1][jp1][kp1]))) {
5233		return true;
5234	}
5235	log_no_crnr(t, 0x770, i, j, k, ip1, j, kp1);
5236
5237	if (install_tswitch(t, i, jp1, kp1,
5238			    tfind_face_corner(t->sw[i][jp1][k],
5239					      t->sw[ip1][jp1][k],
5240					      t->sw[ip1][jp1][kp1]))) {
5241		return true;
5242	}
5243	log_no_crnr(t, 0x770, i, j, k, i, jp1, kp1);
5244	return false;
5245}
5246
5247/*
5248 * 3D case 0x78a:                           O
5249 *
5250 *  b0: t->sw[i  ][j  ][k  ]
5251 *  b1:
5252 *  b2: t->sw[i  ][j+1][k  ]
5253 *  b3:                           O                   O
5254 *  b4: t->sw[i  ][j  ][k+1]      . .       O       .
5255 *  b5: t->sw[i+1][j  ][k+1]      .   .           .
5256 *  b6: t->sw[i  ][j+1][k+1]      .     .       .
5257 *  b7:                           .       .   .
5258 *                                .         O
5259 *                                O         .         O
5260 *                                  .       .
5261 *                                    .     .
5262 *                                      .   .
5263 *                                        . .
5264 *                                          @
5265 */
5266static
5267bool handle_case_0x78a(struct torus *t, int i, int j, int k)
5268{
5269	int ip1 = canonicalize(i + 1, t->x_sz);
5270	int jp1 = canonicalize(j + 1, t->y_sz);
5271	int kp1 = canonicalize(k + 1, t->z_sz);
5272
5273	if (install_tswitch(t, ip1, j, k,
5274			    tfind_face_corner(t->sw[i][j][k],
5275					      t->sw[i][j][kp1],
5276					      t->sw[ip1][j][kp1]))) {
5277		return true;
5278	}
5279	log_no_crnr(t, 0x78a, i, j, k, ip1, j, k);
5280
5281	if (install_tswitch(t, ip1, jp1, kp1,
5282			    tfind_face_corner(t->sw[ip1][j][kp1],
5283					      t->sw[i][j][kp1],
5284					      t->sw[i][jp1][kp1]))) {
5285		return true;
5286	}
5287	log_no_crnr(t, 0x78a, i, j, k, ip1, jp1, kp1);
5288	return false;
5289}
5290
5291/*
5292 * 3D case 0x78c:                           O
5293 *
5294 *  b0: t->sw[i  ][j  ][k  ]
5295 *  b1: t->sw[i+1][j  ][k  ]
5296 *  b2:
5297 *  b3:                           O                   O
5298 *  b4: t->sw[i  ][j  ][k+1]        .       O       . .
5299 *  b5: t->sw[i+1][j  ][k+1]          .           .   .
5300 *  b6: t->sw[i  ][j+1][k+1]            .       .     .
5301 *  b7:                                   .   .       .
5302 *                                          O         .
5303 *                                O         .         O
5304 *                                          .       .
5305 *                                          .     .
5306 *                                          .   .
5307 *                                          . .
5308 *                                          @
5309 */
5310static
5311bool handle_case_0x78c(struct torus *t, int i, int j, int k)
5312{
5313	int ip1 = canonicalize(i + 1, t->x_sz);
5314	int jp1 = canonicalize(j + 1, t->y_sz);
5315	int kp1 = canonicalize(k + 1, t->z_sz);
5316
5317	if (install_tswitch(t, i, jp1, k,
5318			    tfind_face_corner(t->sw[i][j][k],
5319					      t->sw[i][j][kp1],
5320					      t->sw[i][jp1][kp1]))) {
5321		return true;
5322	}
5323	log_no_crnr(t, 0x78c, i, j, k, i, jp1, k);
5324
5325	if (install_tswitch(t, ip1, jp1, kp1,
5326			    tfind_face_corner(t->sw[ip1][j][kp1],
5327					      t->sw[i][j][kp1],
5328					      t->sw[i][jp1][kp1]))) {
5329		return true;
5330	}
5331	log_no_crnr(t, 0x78c, i, j, k, ip1, jp1, kp1);
5332	return false;
5333}
5334
5335/*
5336 * 3D case 0x7a2:                           O
5337 *
5338 *  b0: t->sw[i  ][j  ][k  ]
5339 *  b1:
5340 *  b2: t->sw[i  ][j+1][k  ]
5341 *  b3: t->sw[i+1][j+1][k  ]      O                   O
5342 *  b4: t->sw[i  ][j  ][k+1]      . .       O
5343 *  b5:                           .   .   .
5344 *  b6: t->sw[i  ][j+1][k+1]      .     .
5345 *  b7:                           .   .   .
5346 *                                . .       O
5347 *                                O         .         O
5348 *                                  .       .
5349 *                                    .     .
5350 *                                      .   .
5351 *                                        . .
5352 *                                          @
5353 */
5354static
5355bool handle_case_0x7a2(struct torus *t, int i, int j, int k)
5356{
5357	int ip1 = canonicalize(i + 1, t->x_sz);
5358	int jp1 = canonicalize(j + 1, t->y_sz);
5359	int kp1 = canonicalize(k + 1, t->z_sz);
5360
5361	if (install_tswitch(t, ip1, j, k,
5362			    tfind_face_corner(t->sw[i][j][k],
5363					      t->sw[i][jp1][k],
5364					      t->sw[ip1][jp1][k]))) {
5365		return true;
5366	}
5367	log_no_crnr(t, 0x7a2, i, j, k, ip1, j, k);
5368
5369	if (install_tswitch(t, ip1, jp1, kp1,
5370			    tfind_face_corner(t->sw[i][jp1][kp1],
5371					      t->sw[i][jp1][k],
5372					      t->sw[ip1][jp1][k]))) {
5373		return true;
5374	}
5375	log_no_crnr(t, 0x7a2, i, j, k, ip1, jp1, kp1);
5376	return false;
5377}
5378
5379/*
5380 * 3D case 0x7a8:                           O
5381 *
5382 *  b0: t->sw[i  ][j  ][k  ]
5383 *  b1: t->sw[ip1][j  ][k  ]
5384 *  b2: t->sw[i  ][j+1][k  ]
5385 *  b3:                           O                   O
5386 *  b4: t->sw[i  ][j  ][k+1]      . .       O
5387 *  b5:                           .   .
5388 *  b6: t->sw[i  ][j+1][k+1]      .     .
5389 *  b7:                           .       .
5390 *                                .         O
5391 *                                O         .         O
5392 *                                  .       .       .
5393 *                                    .     .     .
5394 *                                      .   .   .
5395 *                                        . . .
5396 *                                          @
5397 */
5398static
5399bool handle_case_0x7a8(struct torus *t, int i, int j, int k)
5400{
5401	int ip1 = canonicalize(i + 1, t->x_sz);
5402	int jp1 = canonicalize(j + 1, t->y_sz);
5403	int kp1 = canonicalize(k + 1, t->z_sz);
5404
5405	if (install_tswitch(t, ip1, jp1, k,
5406			    tfind_face_corner(t->sw[ip1][j][k],
5407					      t->sw[i][j][k],
5408					      t->sw[i][jp1][k]))) {
5409		return true;
5410	}
5411	log_no_crnr(t, 0x7a8, i, j, k, ip1, jp1, k);
5412
5413	if (install_tswitch(t, ip1, j, kp1,
5414			    tfind_face_corner(t->sw[i][j][kp1],
5415					      t->sw[i][j][k],
5416					      t->sw[ip1][j][k]))) {
5417		return true;
5418	}
5419	log_no_crnr(t, 0x7a8, i, j, k, ip1, j, kp1);
5420	return false;
5421}
5422
5423/*
5424 * 3D case 0x7b0:                           O
5425 *
5426 *  b0: t->sw[i  ][j  ][k  ]
5427 *  b1: t->sw[i+1][j  ][k  ]
5428 *  b2: t->sw[i  ][j+1][k  ]
5429 *  b3: t->sw[i+1][j+1][k  ]      O                   O
5430 *  b4:                           .         O
5431 *  b5:                           .       .   .
5432 *  b6: t->sw[i  ][j+1][k+1]      .     .       .
5433 *  b7:                           .   .           .
5434 *                                . .       O       .
5435 *                                O                   O
5436 *                                  .               .
5437 *                                    .           .
5438 *                                      .       .
5439 *                                        .   .
5440 *                                          @
5441 */
5442static
5443bool handle_case_0x7b0(struct torus *t, int i, int j, int k)
5444{
5445	int ip1 = canonicalize(i + 1, t->x_sz);
5446	int jp1 = canonicalize(j + 1, t->y_sz);
5447	int kp1 = canonicalize(k + 1, t->z_sz);
5448
5449	if (install_tswitch(t, i, j, kp1,
5450			    tfind_face_corner(t->sw[i][j][k],
5451					      t->sw[i][jp1][k],
5452					      t->sw[i][jp1][kp1]))) {
5453		return true;
5454	}
5455	log_no_crnr(t, 0x7b0, i, j, k, i, j, kp1);
5456
5457	if (install_tswitch(t, ip1, jp1, kp1,
5458			    tfind_face_corner(t->sw[i][jp1][kp1],
5459					      t->sw[i][jp1][k],
5460					      t->sw[ip1][jp1][k]))) {
5461		return true;
5462	}
5463	log_no_crnr(t, 0x7b0, i, j, k, ip1, jp1, kp1);
5464	return false;
5465}
5466
5467/*
5468 * 3D case 0x7c4:                           O
5469 *
5470 *  b0: t->sw[i  ][j  ][k  ]
5471 *  b1: t->sw[i+1][j  ][k  ]
5472 *  b2:
5473 *  b3: t->sw[i+1][j+1][k  ]      O                   O
5474 *  b4: t->sw[i  ][j  ][k+1]                O       . .
5475 *  b5: t->sw[i+1][j  ][k+1]                  .   .   .
5476 *  b6:                                         .     .
5477 *  b7:                                       .   .   .
5478 *                                          O       . .
5479 *                                O         .         O
5480 *                                          .       .
5481 *                                          .     .
5482 *                                          .   .
5483 *                                          . .
5484 *                                          @
5485 */
5486static
5487bool handle_case_0x7c4(struct torus *t, int i, int j, int k)
5488{
5489	int ip1 = canonicalize(i + 1, t->x_sz);
5490	int jp1 = canonicalize(j + 1, t->y_sz);
5491	int kp1 = canonicalize(k + 1, t->z_sz);
5492
5493	if (install_tswitch(t, i, jp1, k,
5494			    tfind_face_corner(t->sw[i][j][k],
5495					      t->sw[ip1][j][k],
5496					      t->sw[ip1][jp1][k]))) {
5497		return true;
5498	}
5499	log_no_crnr(t, 0x7c4, i, j, k, i, jp1, k);
5500
5501	if (install_tswitch(t, ip1, jp1, kp1,
5502			    tfind_face_corner(t->sw[ip1][j][kp1],
5503					      t->sw[ip1][j][k],
5504					      t->sw[ip1][jp1][k]))) {
5505		return true;
5506	}
5507	log_no_crnr(t, 0x7c4, i, j, k, ip1, jp1, kp1);
5508	return false;
5509}
5510
5511/*
5512 * 3D case 0x7c8:                           O
5513 *
5514 *  b0: t->sw[i  ][j  ][k  ]
5515 *  b1: t->sw[i+1][j  ][k  ]
5516 *  b2: t->sw[i  ][j+1][k  ]
5517 *  b3:                           O                   O
5518 *  b4: t->sw[i  ][j  ][k+1]                O       . .
5519 *  b5: t->sw[i+1][j  ][k+1]                      .   .
5520 *  b6:                                         .     .
5521 *  b7:                                       .       .
5522 *                                          O         .
5523 *                                O         .         O
5524 *                                  .       .       .
5525 *                                    .     .     .
5526 *                                      .   .   .
5527 *                                        . . .
5528 *                                          @
5529 */
5530static
5531bool handle_case_0x7c8(struct torus *t, int i, int j, int k)
5532{
5533	int ip1 = canonicalize(i + 1, t->x_sz);
5534	int jp1 = canonicalize(j + 1, t->y_sz);
5535	int kp1 = canonicalize(k + 1, t->z_sz);
5536
5537	if (install_tswitch(t, ip1, jp1, k,
5538			    tfind_face_corner(t->sw[ip1][j][k],
5539					      t->sw[i][j][k],
5540					      t->sw[i][jp1][k]))) {
5541		return true;
5542	}
5543	log_no_crnr(t, 0x7c8, i, j, k, ip1, jp1, k);
5544
5545	if (install_tswitch(t, i, jp1, kp1,
5546			    tfind_face_corner(t->sw[i][j][kp1],
5547					      t->sw[i][j][k],
5548					      t->sw[i][jp1][k]))) {
5549		return true;
5550	}
5551	log_no_crnr(t, 0x7c8, i, j, k, i, jp1, kp1);
5552	return false;
5553}
5554
5555/*
5556 * 3D case 0x7d0:                           O
5557 *
5558 *  b0: t->sw[i  ][j  ][k  ]
5559 *  b1: t->sw[i+1][j  ][k  ]
5560 *  b2: t->sw[i  ][j+1][k  ]
5561 *  b3: t->sw[i+1][j+1][k  ]      O                   O
5562 *  b4:                                     O         .
5563 *  b5: t->sw[i+1][j  ][k+1]              .   .       .
5564 *  b6:                                 .       .     .
5565 *  b7:                               .           .   .
5566 *                                  .       O       . .
5567 *                                O                   O
5568 *                                  .               .
5569 *                                    .           .
5570 *                                      .       .
5571 *                                        .   .
5572 *                                          @
5573 */
5574static
5575bool handle_case_0x7d0(struct torus *t, int i, int j, int k)
5576{
5577	int ip1 = canonicalize(i + 1, t->x_sz);
5578	int jp1 = canonicalize(j + 1, t->y_sz);
5579	int kp1 = canonicalize(k + 1, t->z_sz);
5580
5581	if (install_tswitch(t, i, j, kp1,
5582			    tfind_face_corner(t->sw[i][j][k],
5583					      t->sw[ip1][j][k],
5584					      t->sw[ip1][j][kp1]))) {
5585		return true;
5586	}
5587	log_no_crnr(t, 0x7d0, i, j, k, i, j, kp1);
5588
5589	if (install_tswitch(t, ip1, jp1, kp1,
5590			    tfind_face_corner(t->sw[ip1][j][kp1],
5591					      t->sw[ip1][j][k],
5592					      t->sw[ip1][jp1][k]))) {
5593		return true;
5594	}
5595	log_no_crnr(t, 0x7d0, i, j, k, ip1, jp1, kp1);
5596	return false;
5597}
5598
5599/*
5600 * 3D case 0x7e0:                           O
5601 *
5602 *  b0: t->sw[i  ][j  ][k  ]
5603 *  b1: t->sw[i+1][j  ][k  ]
5604 *  b2: t->sw[i  ][j+1][k  ]
5605 *  b3: t->sw[i+1][j+1][k  ]      O                   O
5606 *  b4: t->sw[i  ][j  ][k+1]                O
5607 *  b5:                                   .   .
5608 *  b6:                                 .       .
5609 *  b7:                               .           .
5610 *                                  .       O       .
5611 *                                O         .         O
5612 *                                  .       .       .
5613 *                                    .     .     .
5614 *                                      .   .   .
5615 *                                        . . .
5616 *                                          @
5617 */
5618static
5619bool handle_case_0x7e0(struct torus *t, int i, int j, int k)
5620{
5621	int ip1 = canonicalize(i + 1, t->x_sz);
5622	int jp1 = canonicalize(j + 1, t->y_sz);
5623	int kp1 = canonicalize(k + 1, t->z_sz);
5624
5625	if (install_tswitch(t, ip1, j, kp1,
5626			    tfind_face_corner(t->sw[i][j][kp1],
5627					      t->sw[i][j][k],
5628					      t->sw[ip1][j][k]))) {
5629		return true;
5630	}
5631	log_no_crnr(t, 0x7e0, i, j, k, ip1, j, kp1);
5632
5633	if (install_tswitch(t, i, jp1, kp1,
5634			    tfind_face_corner(t->sw[i][j][kp1],
5635					      t->sw[i][j][k],
5636					      t->sw[i][jp1][k]))) {
5637		return true;
5638	}
5639	log_no_crnr(t, 0x7e0, i, j, k, i, jp1, kp1);
5640	return false;
5641}
5642
5643/*
5644 * Handle the cases where two corners on a single edge are missing.
5645 */
5646
5647/*
5648 * 3D case 0x703:                           O
5649 *                                        . . .
5650 *  b0:                                 .   .   .
5651 *  b1:                               .     .     .
5652 *  b2: t->sw[i  ][j+1][k  ]        .       .       .
5653 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
5654 *  b4: t->sw[i  ][j  ][k+1]      . .       O       .
5655 *  b5: t->sw[i+1][j  ][k+1]      .   .   .       .
5656 *  b6: t->sw[i  ][j+1][k+1]      .     .       .
5657 *  b7: t->sw[i+1][j+1][k+1]      .   .   .   .
5658 *                                . .       O
5659 *                                O                   O
5660 *
5661 *
5662 *
5663 *
5664 *                                          @
5665 */
5666static
5667bool handle_case_0x703(struct torus *t, int i, int j, int k)
5668{
5669	int ip1 = canonicalize(i + 1, t->x_sz);
5670	int jp1 = canonicalize(j + 1, t->y_sz);
5671	int kp1 = canonicalize(k + 1, t->z_sz);
5672
5673	if (install_tswitch(t, i, j, k,
5674			    tfind_face_corner(t->sw[i][jp1][k],
5675					      t->sw[i][jp1][kp1],
5676					      t->sw[i][j][kp1]))) {
5677		return true;
5678	}
5679	log_no_crnr(t, 0x703, i, j, k, i, j, k);
5680
5681	if (install_tswitch(t, ip1, j, k,
5682			    tfind_face_corner(t->sw[ip1][jp1][k],
5683					      t->sw[ip1][jp1][kp1],
5684					      t->sw[ip1][j][kp1]))) {
5685		return true;
5686	}
5687	log_no_crnr(t, 0x703, i, j, k, ip1, j, k);
5688	return false;
5689}
5690
5691/*
5692 * 3D case 0x705:                           O
5693 *                                        . . .
5694 *  b0:                                 .   .   .
5695 *  b1: t->sw[i+1][j  ][k  ]          .     .     .
5696 *  b2:                             .       .       .
5697 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
5698 *  b4: t->sw[i  ][j  ][k+1]        .       O       . .
5699 *  b5: t->sw[i+1][j  ][k+1]          .       .   .   .
5700 *  b6: t->sw[i  ][j+1][k+1]            .       .     .
5701 *  b7: t->sw[i+1][j+1][k+1]              .   .   .   .
5702 *                                          O       . .
5703 *                                O                   O
5704 *
5705 *
5706 *
5707 *
5708 *                                          @
5709 */
5710static
5711bool handle_case_0x705(struct torus *t, int i, int j, int k)
5712{
5713	int ip1 = canonicalize(i + 1, t->x_sz);
5714	int jp1 = canonicalize(j + 1, t->y_sz);
5715	int kp1 = canonicalize(k + 1, t->z_sz);
5716
5717	if (install_tswitch(t, i, j, k,
5718			    tfind_face_corner(t->sw[ip1][j][k],
5719					      t->sw[ip1][j][kp1],
5720					      t->sw[i][j][kp1]))) {
5721		return true;
5722	}
5723	log_no_crnr(t, 0x705, i, j, k, i, j, k);
5724
5725	if (install_tswitch(t, i, jp1, k,
5726			    tfind_face_corner(t->sw[ip1][jp1][k],
5727					      t->sw[ip1][jp1][kp1],
5728					      t->sw[i][jp1][kp1]))) {
5729		return true;
5730	}
5731	log_no_crnr(t, 0x705, i, j, k, i, jp1, k);
5732	return false;
5733}
5734
5735/*
5736 * 3D case 0x70a:                           O
5737 *                                        . . .
5738 *  b0: t->sw[i  ][j  ][k  ]            .       .
5739 *  b1:                               .           .
5740 *  b2: t->sw[i  ][j+1][k  ]        .               .
5741 *  b3:                           O                   O
5742 *  b4: t->sw[i  ][j  ][k+1]      . .       O       .
5743 *  b5: t->sw[i+1][j  ][k+1]      .   .           .
5744 *  b6: t->sw[i  ][j+1][k+1]      .     .       .
5745 *  b7: t->sw[i+1][j+1][k+1]      .       .   .
5746 *                                .         O
5747 *                                O         .         O
5748 *                                  .       .
5749 *                                    .     .
5750 *                                      .   .
5751 *                                        . .
5752 *                                          @
5753 */
5754static
5755bool handle_case_0x70a(struct torus *t, int i, int j, int k)
5756{
5757	int ip1 = canonicalize(i + 1, t->x_sz);
5758	int jp1 = canonicalize(j + 1, t->y_sz);
5759	int kp1 = canonicalize(k + 1, t->z_sz);
5760
5761	if (install_tswitch(t, ip1, j, k,
5762			    tfind_face_corner(t->sw[i][j][k],
5763					      t->sw[i][j][kp1],
5764					      t->sw[ip1][j][kp1]))) {
5765		return true;
5766	}
5767	log_no_crnr(t, 0x70a, i, j, k, ip1, j, k);
5768
5769	if (install_tswitch(t, ip1, jp1, k,
5770			    tfind_face_corner(t->sw[i][jp1][k],
5771					      t->sw[i][jp1][kp1],
5772					      t->sw[ip1][jp1][kp1]))) {
5773		return true;
5774	}
5775	log_no_crnr(t, 0x70a, i, j, k, ip1, jp1, k);
5776	return false;
5777}
5778
5779/*
5780 * 3D case 0x70c:                           O
5781 *                                        .   .
5782 *  b0: t->sw[i  ][j  ][k  ]            .       .
5783 *  b1: t->sw[i+1][j  ][k  ]          .           .
5784 *  b2:                             .               .
5785 *  b3:                           O                   O
5786 *  b4: t->sw[i  ][j  ][k+1]        .       O       . .
5787 *  b5: t->sw[i+1][j  ][k+1]          .           .   .
5788 *  b6: t->sw[i  ][j+1][k+1]            .       .     .
5789 *  b7: t->sw[i+1][j+1][k+1]              .   .       .
5790 *                                          O         .
5791 *                                O         .         O
5792 *                                          .       .
5793 *                                          .     .
5794 *                                          .   .
5795 *                                          . .
5796 *                                          @
5797 */
5798static
5799bool handle_case_0x70c(struct torus *t, int i, int j, int k)
5800{
5801	int ip1 = canonicalize(i + 1, t->x_sz);
5802	int jp1 = canonicalize(j + 1, t->y_sz);
5803	int kp1 = canonicalize(k + 1, t->z_sz);
5804
5805	if (install_tswitch(t, i, jp1, k,
5806			    tfind_face_corner(t->sw[i][j][k],
5807					      t->sw[i][j][kp1],
5808					      t->sw[i][jp1][kp1]))) {
5809		return true;
5810	}
5811	log_no_crnr(t, 0x70c, i, j, k, i, jp1, k);
5812
5813	if (install_tswitch(t, ip1, jp1, k,
5814			    tfind_face_corner(t->sw[ip1][j][k],
5815					      t->sw[ip1][j][kp1],
5816					      t->sw[ip1][jp1][kp1]))) {
5817		return true;
5818	}
5819	log_no_crnr(t, 0x70c, i, j, k, ip1, jp1, k);
5820	return false;
5821}
5822
5823/*
5824 * 3D case 0x711:                           O
5825 *                                        . . .
5826 *  b0:                                 .   .   .
5827 *  b1: t->sw[i+1][j  ][k  ]          .     .     .
5828 *  b2: t->sw[i  ][j+1][k  ]        .       .       .
5829 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
5830 *  b4:                           .         O         .
5831 *  b5: t->sw[i+1][j  ][k+1]      .       .   .       .
5832 *  b6: t->sw[i  ][j+1][k+1]      .     .       .     .
5833 *  b7: t->sw[i+1][j+1][k+1]      .   .           .   .
5834 *                                . .       O       . .
5835 *                                O                   O
5836 *
5837 *
5838 *
5839 *
5840 *                                          @
5841 */
5842static
5843bool handle_case_0x711(struct torus *t, int i, int j, int k)
5844{
5845	int ip1 = canonicalize(i + 1, t->x_sz);
5846	int jp1 = canonicalize(j + 1, t->y_sz);
5847	int kp1 = canonicalize(k + 1, t->z_sz);
5848
5849	if (install_tswitch(t, i, j, k,
5850			    tfind_face_corner(t->sw[ip1][j][k],
5851					      t->sw[ip1][jp1][k],
5852					      t->sw[i][jp1][k]))) {
5853		return true;
5854	}
5855	log_no_crnr(t, 0x711, i, j, k, i, j, k);
5856
5857	if (install_tswitch(t, i, j, kp1,
5858			    tfind_face_corner(t->sw[ip1][j][kp1],
5859					      t->sw[ip1][jp1][kp1],
5860					      t->sw[i][jp1][kp1]))) {
5861		return true;
5862	}
5863	log_no_crnr(t, 0x711, i, j, k, i, j, kp1);
5864	return false;
5865}
5866
5867/*
5868 * 3D case 0x722:                           O
5869 *                                        . .
5870 *  b0: t->sw[i  ][j  ][k  ]            .   .
5871 *  b1:                               .     .
5872 *  b2: t->sw[i  ][j+1][k  ]        .       .
5873 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
5874 *  b4: t->sw[i  ][j  ][k+1]      . .       O
5875 *  b5:                           .   .   .
5876 *  b6: t->sw[i  ][j+1][k+1]      .     .
5877 *  b7: t->sw[i+1][j+1][k+1]      .   .   .
5878 *                                . .       O
5879 *                                O         .         O
5880 *                                  .       .
5881 *                                    .     .
5882 *                                      .   .
5883 *                                        . .
5884 *                                          @
5885 */
5886static
5887bool handle_case_0x722(struct torus *t, int i, int j, int k)
5888{
5889	int ip1 = canonicalize(i + 1, t->x_sz);
5890	int jp1 = canonicalize(j + 1, t->y_sz);
5891	int kp1 = canonicalize(k + 1, t->z_sz);
5892
5893	if (install_tswitch(t, ip1, j, k,
5894			    tfind_face_corner(t->sw[i][j][k],
5895					      t->sw[i][jp1][k],
5896					      t->sw[ip1][jp1][k]))) {
5897		return true;
5898	}
5899	log_no_crnr(t, 0x722, i, j, k, ip1, j, k);
5900
5901	if (install_tswitch(t, ip1, j, kp1,
5902			    tfind_face_corner(t->sw[i][j][kp1],
5903					      t->sw[i][jp1][kp1],
5904					      t->sw[ip1][jp1][kp1]))) {
5905		return true;
5906	}
5907	log_no_crnr(t, 0x722, i, j, k, ip1, j, kp1);
5908	return false;
5909}
5910
5911/*
5912 * 3D case 0x730:                           O
5913 *                                        . .
5914 *  b0: t->sw[i  ][j  ][k  ]            .   .
5915 *  b1: t->sw[i+1][j  ][k  ]          .     .
5916 *  b2: t->sw[i  ][j+1][k  ]        .       .
5917 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
5918 *  b4:                           .         O
5919 *  b5:                           .       .   .
5920 *  b6: t->sw[i  ][j+1][k+1]      .     .       .
5921 *  b7: t->sw[i+1][j+1][k+1]      .   .           .
5922 *                                . .       O       .
5923 *                                O                   O
5924 *                                  .               .
5925 *                                    .           .
5926 *                                      .       .
5927 *                                        .   .
5928 *                                          @
5929 */
5930static
5931bool handle_case_0x730(struct torus *t, int i, int j, int k)
5932{
5933	int ip1 = canonicalize(i + 1, t->x_sz);
5934	int jp1 = canonicalize(j + 1, t->y_sz);
5935	int kp1 = canonicalize(k + 1, t->z_sz);
5936
5937	if (install_tswitch(t, i, j, kp1,
5938			    tfind_face_corner(t->sw[i][j][k],
5939					      t->sw[i][jp1][k],
5940					      t->sw[i][jp1][kp1]))) {
5941		return true;
5942	}
5943	log_no_crnr(t, 0x730, i, j, k, i, j, kp1);
5944
5945	if (install_tswitch(t, ip1, j, kp1,
5946			    tfind_face_corner(t->sw[ip1][j][k],
5947					      t->sw[ip1][jp1][k],
5948					      t->sw[ip1][jp1][kp1]))) {
5949		return true;
5950	}
5951	log_no_crnr(t, 0x730, i, j, k, ip1, j, kp1);
5952	return false;
5953}
5954
5955/*
5956 * 3D case 0x744:                           O
5957 *                                          . .
5958 *  b0: t->sw[i  ][j  ][k  ]                .   .
5959 *  b1: t->sw[i+1][j  ][k  ]                .     .
5960 *  b2:                                     .       .
5961 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
5962 *  b4: t->sw[i  ][j  ][k+1]                O       . .
5963 *  b5: t->sw[i+1][j  ][k+1]                  .   .   .
5964 *  b6:                                         .     .
5965 *  b7: t->sw[i+1][j+1][k+1]                  .   .   .
5966 *                                          O       . .
5967 *                                O         .         O
5968 *                                          .       .
5969 *                                          .     .
5970 *                                          .   .
5971 *                                          . .
5972 *                                          @
5973 */
5974static
5975bool handle_case_0x744(struct torus *t, int i, int j, int k)
5976{
5977	int ip1 = canonicalize(i + 1, t->x_sz);
5978	int jp1 = canonicalize(j + 1, t->y_sz);
5979	int kp1 = canonicalize(k + 1, t->z_sz);
5980
5981	if (install_tswitch(t, i, jp1, k,
5982			    tfind_face_corner(t->sw[i][j][k],
5983					      t->sw[ip1][j][k],
5984					      t->sw[ip1][jp1][k]))) {
5985		return true;
5986	}
5987	log_no_crnr(t, 0x744, i, j, k, i, jp1, k);
5988
5989	if (install_tswitch(t, i, jp1, kp1,
5990			    tfind_face_corner(t->sw[i][j][kp1],
5991					      t->sw[ip1][j][kp1],
5992					      t->sw[ip1][jp1][kp1]))) {
5993		return true;
5994	}
5995	log_no_crnr(t, 0x744, i, j, k, i, jp1, kp1);
5996	return false;
5997}
5998
5999/*
6000 * 3D case 0x750:                           O
6001 *                                          . .
6002 *  b0: t->sw[i  ][j  ][k  ]                .   .
6003 *  b1: t->sw[i+1][j  ][k  ]                .     .
6004 *  b2: t->sw[i  ][j+1][k  ]                .       .
6005 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
6006 *  b4:                                     O         .
6007 *  b5: t->sw[i+1][j  ][k+1]              .   .       .
6008 *  b6:                                 .       .     .
6009 *  b7: t->sw[i+1][j+1][k+1]          .           .   .
6010 *                                  .       O       . .
6011 *                                O                   O
6012 *                                  .               .
6013 *                                    .           .
6014 *                                      .       .
6015 *                                        .   .
6016 *                                          @
6017 */
6018static
6019bool handle_case_0x750(struct torus *t, int i, int j, int k)
6020{
6021	int ip1 = canonicalize(i + 1, t->x_sz);
6022	int jp1 = canonicalize(j + 1, t->y_sz);
6023	int kp1 = canonicalize(k + 1, t->z_sz);
6024
6025	if (install_tswitch(t, i, j, kp1,
6026			    tfind_face_corner(t->sw[i][j][k],
6027					      t->sw[ip1][j][k],
6028					      t->sw[ip1][j][kp1]))) {
6029		return true;
6030	}
6031	log_no_crnr(t, 0x750, i, j, k, i, j, kp1);
6032
6033	if (install_tswitch(t, i, jp1, kp1,
6034			    tfind_face_corner(t->sw[i][jp1][k],
6035					      t->sw[ip1][jp1][k],
6036					      t->sw[ip1][jp1][kp1]))) {
6037		return true;
6038	}
6039	log_no_crnr(t, 0x750, i, j, k, i, jp1, kp1);
6040	return false;
6041}
6042
6043/*
6044 * 3D case 0x788:                           O
6045 *
6046 *  b0: t->sw[i  ][j  ][k  ]
6047 *  b1: t->sw[ip1][j  ][k  ]
6048 *  b2: t->sw[i  ][j+1][k  ]
6049 *  b3:                           O                   O
6050 *  b4: t->sw[i  ][j  ][k+1]      . .       O       . .
6051 *  b5: t->sw[i+1][j  ][k+1]      .   .           .   .
6052 *  b6: t->sw[i  ][j+1][k+1]      .     .       .     .
6053 *  b7:                           .       .   .       .
6054 *                                .         O         .
6055 *                                O         .         O
6056 *                                  .       .       .
6057 *                                    .     .     .
6058 *                                      .   .   .
6059 *                                        . . .
6060 *                                          @
6061 */
6062static
6063bool handle_case_0x788(struct torus *t, int i, int j, int k)
6064{
6065	int ip1 = canonicalize(i + 1, t->x_sz);
6066	int jp1 = canonicalize(j + 1, t->y_sz);
6067	int kp1 = canonicalize(k + 1, t->z_sz);
6068
6069	if (install_tswitch(t, ip1, jp1, k,
6070			    tfind_face_corner(t->sw[ip1][j][k],
6071					      t->sw[i][j][k],
6072					      t->sw[i][jp1][k]))) {
6073		return true;
6074	}
6075	log_no_crnr(t, 0x788, i, j, k, ip1, jp1, k);
6076
6077	if (install_tswitch(t, ip1, jp1, kp1,
6078			    tfind_face_corner(t->sw[ip1][j][kp1],
6079					      t->sw[i][j][kp1],
6080					      t->sw[i][jp1][kp1]))) {
6081		return true;
6082	}
6083	log_no_crnr(t, 0x788, i, j, k, ip1, jp1, kp1);
6084	return false;
6085}
6086
6087/*
6088 * 3D case 0x7a0:                           O
6089 *
6090 *  b0: t->sw[i  ][j  ][k  ]
6091 *  b1: t->sw[i+1][j  ][k  ]
6092 *  b2: t->sw[i  ][j+1][k  ]
6093 *  b3: t->sw[i+1][j+1][k  ]      O                   O
6094 *  b4: t->sw[i  ][j  ][k+1]      . .       O
6095 *  b5:                           .   .   .   .
6096 *  b6: t->sw[i  ][j+1][k+1]      .     .       .
6097 *  b7:                           .   .   .       .
6098 *                                . .       O       .
6099 *                                O         .         O
6100 *                                  .       .       .
6101 *                                    .     .     .
6102 *                                      .   .   .
6103 *                                        . . .
6104 *                                          @
6105 */
6106static
6107bool handle_case_0x7a0(struct torus *t, int i, int j, int k)
6108{
6109	int ip1 = canonicalize(i + 1, t->x_sz);
6110	int jp1 = canonicalize(j + 1, t->y_sz);
6111	int kp1 = canonicalize(k + 1, t->z_sz);
6112
6113	if (install_tswitch(t, ip1, j, kp1,
6114			    tfind_face_corner(t->sw[i][j][kp1],
6115					      t->sw[i][j][k],
6116					      t->sw[ip1][j][k]))) {
6117		return true;
6118	}
6119	log_no_crnr(t, 0x7a0, i, j, k, ip1, j, kp1);
6120
6121	if (install_tswitch(t, ip1, jp1, kp1,
6122			    tfind_face_corner(t->sw[i][jp1][kp1],
6123					      t->sw[i][jp1][k],
6124					      t->sw[ip1][jp1][k]))) {
6125		return true;
6126	}
6127	log_no_crnr(t, 0x7a0, i, j, k, ip1, jp1, kp1);
6128	return false;
6129}
6130
6131/*
6132 * 3D case 0x7c0:                           O
6133 *
6134 *  b0: t->sw[i  ][j  ][k  ]
6135 *  b1: t->sw[i+1][j  ][k  ]
6136 *  b2: t->sw[i  ][j+1][k  ]
6137 *  b3: t->sw[i+1][j+1][k  ]      O                   O
6138 *  b4: t->sw[i  ][j  ][k+1]                O       . .
6139 *  b5: t->sw[i+1][j  ][k+1]              .   .   .   .
6140 *  b6:                                 .       .     .
6141 *  b7:                               .       .   .   .
6142 *                                  .       O       . .
6143 *                                O         .         O
6144 *                                  .       .       .
6145 *                                    .     .     .
6146 *                                      .   .   .
6147 *                                        . . .
6148 *                                          @
6149 */
6150static
6151bool handle_case_0x7c0(struct torus *t, int i, int j, int k)
6152{
6153	int ip1 = canonicalize(i + 1, t->x_sz);
6154	int jp1 = canonicalize(j + 1, t->y_sz);
6155	int kp1 = canonicalize(k + 1, t->z_sz);
6156
6157	if (install_tswitch(t, i, jp1, kp1,
6158			    tfind_face_corner(t->sw[i][j][kp1],
6159					      t->sw[i][j][k],
6160					      t->sw[i][jp1][k]))) {
6161		return true;
6162	}
6163	log_no_crnr(t, 0x7c0, i, j, k, i, jp1, kp1);
6164
6165	if (install_tswitch(t, ip1, jp1, kp1,
6166			    tfind_face_corner(t->sw[ip1][j][kp1],
6167					      t->sw[ip1][j][k],
6168					      t->sw[ip1][jp1][k]))) {
6169		return true;
6170	}
6171	log_no_crnr(t, 0x7c0, i, j, k, ip1, jp1, kp1);
6172	return false;
6173}
6174
6175/*
6176 * Handle the cases where a single corner is missing.
6177 */
6178
6179/*
6180 * 3D case 0x701:                           O
6181 *                                        . . .
6182 *  b0:                                     .   .   .
6183 *  b1: t->sw[i+1][j  ][k  ]          .     .     .
6184 *  b2: t->sw[i  ][j+1][k  ]        .       .       .
6185 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
6186 *  b4: t->sw[i  ][j  ][k+1]      . .       O       . .
6187 *  b5: t->sw[i+1][j  ][k+1]      .   .   .   .   .   .
6188 *  b6: t->sw[i  ][j+1][k+1]      .     .       .     .
6189 *  b7: t->sw[i+1][j+1][k+1]      .   .   .   .   .   .
6190 *                                . .       O       . .
6191 *                                O                   O
6192 *
6193 *
6194 *
6195 *
6196 *                                          @
6197 */
6198static
6199bool handle_case_0x701(struct torus *t, int i, int j, int k)
6200{
6201	int ip1 = canonicalize(i + 1, t->x_sz);
6202	int jp1 = canonicalize(j + 1, t->y_sz);
6203
6204	if (install_tswitch(t, i, j, k,
6205			    tfind_face_corner(t->sw[i][jp1][k],
6206					      t->sw[ip1][jp1][k],
6207					      t->sw[ip1][j][k]))) {
6208		return true;
6209	}
6210	log_no_crnr(t, 0x701, i, j, k, i, j, k);
6211	return false;
6212}
6213
6214/*
6215 * 3D case 0x702:                           O
6216 *                                        . . .
6217 *  b0: t->sw[i  ][j  ][k  ]            .   .   .
6218 *  b1:                               .     .     .
6219 *  b2: t->sw[i  ][j+1][k  ]        .       .       .
6220 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
6221 *  b4: t->sw[i  ][j  ][k+1]      . .       O       .
6222 *  b5: t->sw[i+1][j  ][k+1]      .   .   .       .
6223 *  b6: t->sw[i  ][j+1][k+1]      .     .       .
6224 *  b7: t->sw[i+1][j+1][k+1]      .   .   .   .
6225 *                                . .       O
6226 *                                O         .         O
6227 *                                  .       .
6228 *                                    .     .
6229 *                                      .   .
6230 *                                        . .
6231 *                                          @
6232 */
6233static
6234bool handle_case_0x702(struct torus *t, int i, int j, int k)
6235{
6236	int ip1 = canonicalize(i + 1, t->x_sz);
6237	int kp1 = canonicalize(k + 1, t->z_sz);
6238
6239	if (install_tswitch(t, ip1, j, k,
6240			    tfind_face_corner(t->sw[i][j][k],
6241					      t->sw[i][j][kp1],
6242					      t->sw[ip1][j][kp1]))) {
6243		return true;
6244	}
6245	log_no_crnr(t, 0x702, i, j, k, ip1, j, k);
6246	return false;
6247}
6248
6249/*
6250 * 3D case 0x704:                           O
6251 *                                        . . .
6252 *  b0: t->sw[i  ][j  ][k  ]            .   .   .
6253 *  b1: t->sw[i+1][j  ][k  ]          .     .     .
6254 *  b2:                             .       .       .
6255 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
6256 *  b4: t->sw[i  ][j  ][k+1]        .       O       . .
6257 *  b5: t->sw[i+1][j  ][k+1]          .       .   .   .
6258 *  b6: t->sw[i  ][j+1][k+1]            .       .     .
6259 *  b7: t->sw[i+1][j+1][k+1]              .   .   .   .
6260 *                                          O       . .
6261 *                                O         .         O
6262 *                                          .       .
6263 *                                          .     .
6264 *                                          .   .
6265 *                                          . .
6266 *                                          @
6267 */
6268static
6269bool handle_case_0x704(struct torus *t, int i, int j, int k)
6270{
6271	int jp1 = canonicalize(j + 1, t->y_sz);
6272	int kp1 = canonicalize(k + 1, t->z_sz);
6273
6274	if (install_tswitch(t, i, jp1, k,
6275			    tfind_face_corner(t->sw[i][j][k],
6276					      t->sw[i][j][kp1],
6277					      t->sw[i][jp1][kp1]))) {
6278		return true;
6279	}
6280	log_no_crnr(t, 0x704, i, j, k, i, jp1, k);
6281	return false;
6282}
6283
6284/*
6285 * 3D case 0x708:                           O
6286 *                                        .   .
6287 *  b0: t->sw[i  ][j  ][k  ]            .       .
6288 *  b1: t->sw[i+1][j  ][k  ]          .           .
6289 *  b2: t->sw[i  ][j+1][k  ]        .               .
6290 *  b3:                           O                   O
6291 *  b4: t->sw[i  ][j  ][k+1]      . .       O       . .
6292 *  b5: t->sw[i+1][j  ][k+1]      .   .           .   .
6293 *  b6: t->sw[i  ][j+1][k+1]      .     .       .     .
6294 *  b7: t->sw[i+1][j+1][k+1]      .       .   .       .
6295 *                                .         O         .
6296 *                                O         .         O
6297 *                                  .       .       .
6298 *                                    .     .     .
6299 *                                      .   .   .
6300 *                                        . . .
6301 *                                          @
6302 */
6303static
6304bool handle_case_0x708(struct torus *t, int i, int j, int k)
6305{
6306	int ip1 = canonicalize(i + 1, t->x_sz);
6307	int jp1 = canonicalize(j + 1, t->y_sz);
6308
6309	if (install_tswitch(t, ip1, jp1, k,
6310			    tfind_face_corner(t->sw[i][jp1][k],
6311					      t->sw[i][j][k],
6312					      t->sw[ip1][j][k]))) {
6313		return true;
6314	}
6315	log_no_crnr(t, 0x708, i, j, k, ip1, jp1, k);
6316	return false;
6317}
6318
6319/*
6320 * 3D case 0x710:                           O
6321 *                                        . . .
6322 *  b0: t->sw[i  ][j  ][k  ]            .   .   .
6323 *  b1: t->sw[i+1][j  ][k  ]          .     .     .
6324 *  b2: t->sw[i  ][j+1][k  ]        .       .       .
6325 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
6326 *  b4:                           .         O         .
6327 *  b5: t->sw[i+1][j  ][k+1]      .       .   .       .
6328 *  b6: t->sw[i  ][j+1][k+1]      .     .       .     .
6329 *  b7: t->sw[i+1][j+1][k+1]      .   .           .   .
6330 *                                . .       O       . .
6331 *                                O                   O
6332 *                                  .               .
6333 *                                    .           .
6334 *                                      .       .
6335 *                                        .   .
6336 *                                          @
6337 */
6338static
6339bool handle_case_0x710(struct torus *t, int i, int j, int k)
6340{
6341	int ip1 = canonicalize(i + 1, t->x_sz);
6342	int kp1 = canonicalize(k + 1, t->z_sz);
6343
6344	if (install_tswitch(t, i, j, kp1,
6345			    tfind_face_corner(t->sw[i][j][k],
6346					      t->sw[ip1][j][k],
6347					      t->sw[ip1][j][kp1]))) {
6348		return true;
6349	}
6350	log_no_crnr(t, 0x710, i, j, k, i, j, kp1);
6351	return false;
6352}
6353
6354/*
6355 * 3D case 0x720:                           O
6356 *                                        . .
6357 *  b0: t->sw[i  ][j  ][k  ]            .   .
6358 *  b1: t->sw[i+1][j  ][k  ]          .     .
6359 *  b2: t->sw[i  ][j+1][k  ]        .       .
6360 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
6361 *  b4: t->sw[i  ][j  ][k+1]      . .       O
6362 *  b5:                           .   .   .   .
6363 *  b6: t->sw[i  ][j+1][k+1]      .     .       .
6364 *  b7: t->sw[i+1][j+1][k+1]      .   .   .       .
6365 *                                . .       O       .
6366 *                                O         .         O
6367 *                                  .       .       .
6368 *                                    .     .     .
6369 *                                      .   .   .
6370 *                                        . . .
6371 *                                          @
6372 */
6373static
6374bool handle_case_0x720(struct torus *t, int i, int j, int k)
6375{
6376	int ip1 = canonicalize(i + 1, t->x_sz);
6377	int kp1 = canonicalize(k + 1, t->z_sz);
6378
6379	if (install_tswitch(t, ip1, j, kp1,
6380			    tfind_face_corner(t->sw[ip1][j][k],
6381					      t->sw[i][j][k],
6382					      t->sw[i][j][kp1]))) {
6383		return true;
6384	}
6385	log_no_crnr(t, 0x720, i, j, k, ip1, j, kp1);
6386	return false;
6387}
6388
6389/*
6390 * 3D case 0x740:                           O
6391 *                                          . .
6392 *  b0: t->sw[i  ][j  ][k  ]                .   .
6393 *  b1: t->sw[i+1][j  ][k  ]                .     .
6394 *  b2: t->sw[i  ][j+1][k  ]                .       .
6395 *  b3: t->sw[i+1][j+1][k  ]      O         .         O
6396 *  b4: t->sw[i  ][j  ][k+1]                O       . .
6397 *  b5: t->sw[i+1][j  ][k+1]              .   .   .   .
6398 *  b6:                                 .       .     .
6399 *  b7: t->sw[i+1][j+1][k+1]          .       .   .   .
6400 *                                  .       O       . .
6401 *                                O         .         O
6402 *                                  .       .       .
6403 *                                    .     .     .
6404 *                                      .   .   .
6405 *                                        . . .
6406 *                                          @
6407 */
6408static
6409bool handle_case_0x740(struct torus *t, int i, int j, int k)
6410{
6411	int jp1 = canonicalize(j + 1, t->y_sz);
6412	int kp1 = canonicalize(k + 1, t->z_sz);
6413
6414	if (install_tswitch(t, i, jp1, kp1,
6415			    tfind_face_corner(t->sw[i][jp1][k],
6416					      t->sw[i][j][k],
6417					      t->sw[i][j][kp1]))) {
6418		return true;
6419	}
6420	log_no_crnr(t, 0x740, i, j, k, i, jp1, kp1);
6421	return false;
6422}
6423
6424/*
6425 * 3D case 0x780:                           O
6426 *
6427 *  b0: t->sw[i  ][j  ][k  ]
6428 *  b1: t->sw[i+1][j  ][k  ]
6429 *  b2: t->sw[i  ][j+1][k  ]
6430 *  b3: t->sw[i+1][j+1][k  ]      O                   O
6431 *  b4: t->sw[i  ][j  ][k+1]      . .       O       . .
6432 *  b5: t->sw[i+1][j  ][k+1]      .   .   .   .   .   .
6433 *  b6: t->sw[i  ][j+1][k+1]      .     .       .     .
6434 *  b7:                           .   .   .   .   .   .
6435 *                                . .       O       . .
6436 *                                O         .         O
6437 *                                  .       .       .
6438 *                                    .     .     .
6439 *                                      .   .   .
6440 *                                        . . .
6441 *                                          @
6442 */
6443static
6444bool handle_case_0x780(struct torus *t, int i, int j, int k)
6445{
6446	int ip1 = canonicalize(i + 1, t->x_sz);
6447	int jp1 = canonicalize(j + 1, t->y_sz);
6448	int kp1 = canonicalize(k + 1, t->z_sz);
6449
6450	if (install_tswitch(t, ip1, jp1, kp1,
6451			    tfind_face_corner(t->sw[i][jp1][kp1],
6452					      t->sw[i][j][kp1],
6453					      t->sw[ip1][j][kp1]))) {
6454		return true;
6455	}
6456	log_no_crnr(t, 0x780, i, j, k, ip1, jp1, kp1);
6457	return false;
6458}
6459
6460/*
6461 * Make sure links between all known torus/mesh switches are installed.
6462 *
6463 * We don't have to worry about links that wrap on a mesh coordinate, as
6464 * there shouldn't be any; if there are it indicates an input error.
6465 */
6466static
6467void check_tlinks(struct torus *t, int i, int j, int k)
6468{
6469	struct t_switch ****sw = t->sw;
6470	int ip1 = canonicalize(i + 1, t->x_sz);
6471	int jp1 = canonicalize(j + 1, t->y_sz);
6472	int kp1 = canonicalize(k + 1, t->z_sz);
6473
6474	/*
6475	 * Don't waste time/code checking return status of link_tswitches()
6476	 * here.  It is unlikely to fail, and the result of any failure here
6477	 * will be caught elsewhere anyway.
6478	 */
6479	if (sw[i][j][k] && sw[ip1][j][k])
6480		link_tswitches(t, 0, sw[i][j][k], sw[ip1][j][k]);
6481
6482	if (sw[i][jp1][k] && sw[ip1][jp1][k])
6483		link_tswitches(t, 0, sw[i][jp1][k], sw[ip1][jp1][k]);
6484
6485	if (sw[i][j][kp1] && sw[ip1][j][kp1])
6486		link_tswitches(t, 0, sw[i][j][kp1], sw[ip1][j][kp1]);
6487
6488	if (sw[i][jp1][kp1] && sw[ip1][jp1][kp1])
6489		link_tswitches(t, 0, sw[i][jp1][kp1], sw[ip1][jp1][kp1]);
6490
6491
6492	if (sw[i][j][k] && sw[i][jp1][k])
6493		link_tswitches(t, 1, sw[i][j][k], sw[i][jp1][k]);
6494
6495	if (sw[ip1][j][k] && sw[ip1][jp1][k])
6496		link_tswitches(t, 1, sw[ip1][j][k], sw[ip1][jp1][k]);
6497
6498	if (sw[i][j][kp1] && sw[i][jp1][kp1])
6499		link_tswitches(t, 1, sw[i][j][kp1], sw[i][jp1][kp1]);
6500
6501	if (sw[ip1][j][kp1] && sw[ip1][jp1][kp1])
6502		link_tswitches(t, 1, sw[ip1][j][kp1], sw[ip1][jp1][kp1]);
6503
6504
6505	if (sw[i][j][k] && sw[i][j][kp1])
6506		link_tswitches(t, 2, sw[i][j][k], sw[i][j][kp1]);
6507
6508	if (sw[ip1][j][k] && sw[ip1][j][kp1])
6509		link_tswitches(t, 2, sw[ip1][j][k], sw[ip1][j][kp1]);
6510
6511	if (sw[i][jp1][k] && sw[i][jp1][kp1])
6512		link_tswitches(t, 2, sw[i][jp1][k], sw[i][jp1][kp1]);
6513
6514	if (sw[ip1][jp1][k] && sw[ip1][jp1][kp1])
6515		link_tswitches(t, 2, sw[ip1][jp1][k], sw[ip1][jp1][kp1]);
6516}
6517
6518static
6519void locate_sw(struct torus *t, int i, int j, int k)
6520{
6521	unsigned fp;
6522	bool success;
6523
6524	i = canonicalize(i, t->x_sz);
6525	j = canonicalize(j, t->y_sz);
6526	k = canonicalize(k, t->z_sz);
6527
6528	/*
6529	 * By definition, if a coordinate direction is meshed, we don't
6530	 * allow it to wrap to zero.
6531	 */
6532	if (t->flags & X_MESH) {
6533		int ip1 = canonicalize(i + 1, t->x_sz);
6534		if (ip1 < i)
6535			goto out;
6536	}
6537	if (t->flags & Y_MESH) {
6538		int jp1 = canonicalize(j + 1, t->y_sz);
6539		if (jp1 < j)
6540			goto out;
6541	}
6542	if (t->flags & Z_MESH) {
6543		int kp1 = canonicalize(k + 1, t->z_sz);
6544		if (kp1 < k)
6545			goto out;
6546	}
6547	/*
6548	 * There are various reasons that the links are not installed between
6549	 * known torus switches.  These include cases where the search for
6550	 * new switches only partially succeeds due to missing switches, and
6551	 * cases where we haven't processed this position yet, but processing
6552	 * of multiple independent neighbor positions has installed switches
6553	 * into corners of our case.
6554	 *
6555	 * In any event, the topology assumptions made in handling the
6556	 * fingerprint for this position require that all links be installed
6557	 * between installed switches for this position.
6558	 */
6559again:
6560	check_tlinks(t, i, j, k);
6561	fp = fingerprint(t, i, j, k);
6562
6563	switch (fp) {
6564	/*
6565	 * When all switches are present, we are done.  Otherwise, one of
6566	 * the cases below will be unsuccessful, and we'll be done also.
6567	 *
6568	 * Note that check_tlinks() above will ensure all links that are
6569	 * present are connected, in the event that all our switches are
6570	 * present due to successful case handling in the surrounding
6571	 * torus/mesh.
6572	 */
6573	case 0x300:
6574	case 0x500:
6575	case 0x600:
6576	case 0x700:
6577		goto out;
6578	/*
6579	 * Ignore the 2D cases where there isn't enough information to uniquely
6580	 * locate/place a switch into the cube.
6581	 */
6582	case 0x30f: 	/* 0 corners available */
6583	case 0x533: 	/* 0 corners available */
6584	case 0x655: 	/* 0 corners available */
6585	case 0x30e:	/* 1 corner available */
6586	case 0x532:	/* 1 corner available */
6587	case 0x654:	/* 1 corner available */
6588	case 0x30d:	/* 1 corner available */
6589	case 0x531:	/* 1 corner available */
6590	case 0x651:	/* 1 corner available */
6591	case 0x30b:	/* 1 corner available */
6592	case 0x523:	/* 1 corner available */
6593	case 0x645:	/* 1 corner available */
6594	case 0x307:	/* 1 corner available */
6595	case 0x513:	/* 1 corner available */
6596	case 0x615:	/* 1 corner available */
6597		goto out;
6598	/*
6599	 * Handle the 2D cases with a single existing edge.
6600	 *
6601	 */
6602	case 0x30c:
6603		success = handle_case_0x30c(t, i, j, k);
6604		break;
6605	case 0x303:
6606		success = handle_case_0x303(t, i, j, k);
6607		break;
6608	case 0x305:
6609		success = handle_case_0x305(t, i, j, k);
6610		break;
6611	case 0x30a:
6612		success = handle_case_0x30a(t, i, j, k);
6613		break;
6614	case 0x503:
6615		success = handle_case_0x503(t, i, j, k);
6616		break;
6617	case 0x511:
6618		success = handle_case_0x511(t, i, j, k);
6619		break;
6620	case 0x522:
6621		success = handle_case_0x522(t, i, j, k);
6622		break;
6623	case 0x530:
6624		success = handle_case_0x530(t, i, j, k);
6625		break;
6626	case 0x605:
6627		success = handle_case_0x605(t, i, j, k);
6628		break;
6629	case 0x611:
6630		success = handle_case_0x611(t, i, j, k);
6631		break;
6632	case 0x644:
6633		success = handle_case_0x644(t, i, j, k);
6634		break;
6635	case 0x650:
6636		success = handle_case_0x650(t, i, j, k);
6637		break;
6638	/*
6639	 * Handle the 2D cases where two existing edges meet at a corner.
6640	 */
6641	case 0x301:
6642		success = handle_case_0x301(t, i, j, k);
6643		break;
6644	case 0x302:
6645		success = handle_case_0x302(t, i, j, k);
6646		break;
6647	case 0x304:
6648		success = handle_case_0x304(t, i, j, k);
6649		break;
6650	case 0x308:
6651		success = handle_case_0x308(t, i, j, k);
6652		break;
6653	case 0x501:
6654		success = handle_case_0x501(t, i, j, k);
6655		break;
6656	case 0x502:
6657		success = handle_case_0x502(t, i, j, k);
6658		break;
6659	case 0x520:
6660		success = handle_case_0x520(t, i, j, k);
6661		break;
6662	case 0x510:
6663		success = handle_case_0x510(t, i, j, k);
6664		break;
6665	case 0x601:
6666		success = handle_case_0x601(t, i, j, k);
6667		break;
6668	case 0x604:
6669		success = handle_case_0x604(t, i, j, k);
6670		break;
6671	case 0x610:
6672		success = handle_case_0x610(t, i, j, k);
6673		break;
6674	case 0x640:
6675		success = handle_case_0x640(t, i, j, k);
6676		break;
6677	/*
6678	 * Ignore the 3D cases where there isn't enough information to uniquely
6679	 * locate/place a switch into the cube.
6680	 */
6681	case 0x7ff:	/* 0 corners available */
6682	case 0x7fe:	/* 1 corner available */
6683	case 0x7fd:	/* 1 corner available */
6684	case 0x7fb:	/* 1 corner available */
6685	case 0x7f7:	/* 1 corner available */
6686	case 0x7ef:	/* 1 corner available */
6687	case 0x7df:	/* 1 corner available */
6688	case 0x7bf:	/* 1 corner available */
6689	case 0x77f:	/* 1 corner available */
6690	case 0x7fc:	/* 2 adj corners available */
6691	case 0x7fa:	/* 2 adj corners available */
6692	case 0x7f5:	/* 2 adj corners available */
6693	case 0x7f3:	/* 2 adj corners available */
6694	case 0x7cf:	/* 2 adj corners available */
6695	case 0x7af:	/* 2 adj corners available */
6696	case 0x75f:	/* 2 adj corners available */
6697	case 0x73f:	/* 2 adj corners available */
6698	case 0x7ee:	/* 2 adj corners available */
6699	case 0x7dd:	/* 2 adj corners available */
6700	case 0x7bb:	/* 2 adj corners available */
6701	case 0x777:	/* 2 adj corners available */
6702		goto out;
6703	/*
6704	 * Handle the 3D cases where two existing edges meet at a corner.
6705	 *
6706	 */
6707	case 0x71f:
6708		success = handle_case_0x71f(t, i, j, k);
6709		break;
6710	case 0x72f:
6711		success = handle_case_0x72f(t, i, j, k);
6712		break;
6713	case 0x737:
6714		success = handle_case_0x737(t, i, j, k);
6715		break;
6716	case 0x73b:
6717		success = handle_case_0x73b(t, i, j, k);
6718		break;
6719	case 0x74f:
6720		success = handle_case_0x74f(t, i, j, k);
6721		break;
6722	case 0x757:
6723		success = handle_case_0x757(t, i, j, k);
6724		break;
6725	case 0x75d:
6726		success = handle_case_0x75d(t, i, j, k);
6727		break;
6728	case 0x773:
6729		success = handle_case_0x773(t, i, j, k);
6730		break;
6731	case 0x775:
6732		success = handle_case_0x775(t, i, j, k);
6733		break;
6734	case 0x78f:
6735		success = handle_case_0x78f(t, i, j, k);
6736		break;
6737	case 0x7ab:
6738		success = handle_case_0x7ab(t, i, j, k);
6739		break;
6740	case 0x7ae:
6741		success = handle_case_0x7ae(t, i, j, k);
6742		break;
6743	case 0x7b3:
6744		success = handle_case_0x7b3(t, i, j, k);
6745		break;
6746	case 0x7ba:
6747		success = handle_case_0x7ba(t, i, j, k);
6748		break;
6749	case 0x7cd:
6750		success = handle_case_0x7cd(t, i, j, k);
6751		break;
6752	case 0x7ce:
6753		success = handle_case_0x7ce(t, i, j, k);
6754		break;
6755	case 0x7d5:
6756		success = handle_case_0x7d5(t, i, j, k);
6757		break;
6758	case 0x7dc:
6759		success = handle_case_0x7dc(t, i, j, k);
6760		break;
6761	case 0x7ea:
6762		success = handle_case_0x7ea(t, i, j, k);
6763		break;
6764	case 0x7ec:
6765		success = handle_case_0x7ec(t, i, j, k);
6766		break;
6767	case 0x7f1:
6768		success = handle_case_0x7f1(t, i, j, k);
6769		break;
6770	case 0x7f2:
6771		success = handle_case_0x7f2(t, i, j, k);
6772		break;
6773	case 0x7f4:
6774		success = handle_case_0x7f4(t, i, j, k);
6775		break;
6776	case 0x7f8:
6777		success = handle_case_0x7f8(t, i, j, k);
6778		break;
6779	/*
6780	 * Handle the cases where three existing edges meet at a corner.
6781	 *
6782	 */
6783	case 0x717:
6784		success = handle_case_0x717(t, i, j, k);
6785		break;
6786	case 0x72b:
6787		success = handle_case_0x72b(t, i, j, k);
6788		break;
6789	case 0x74d:
6790		success = handle_case_0x74d(t, i, j, k);
6791		break;
6792	case 0x771:
6793		success = handle_case_0x771(t, i, j, k);
6794		break;
6795	case 0x78e:
6796		success = handle_case_0x78e(t, i, j, k);
6797		break;
6798	case 0x7b2:
6799		success = handle_case_0x7b2(t, i, j, k);
6800		break;
6801	case 0x7d4:
6802		success = handle_case_0x7d4(t, i, j, k);
6803		break;
6804	case 0x7e8:
6805		success = handle_case_0x7e8(t, i, j, k);
6806		break;
6807	/*
6808	 * Handle the cases where four corners on a single face are missing.
6809	 */
6810	case 0x70f:
6811		success = handle_case_0x70f(t, i, j, k);
6812		break;
6813	case 0x733:
6814		success = handle_case_0x733(t, i, j, k);
6815		break;
6816	case 0x755:
6817		success = handle_case_0x755(t, i, j, k);
6818		break;
6819	case 0x7aa:
6820		success = handle_case_0x7aa(t, i, j, k);
6821		break;
6822	case 0x7cc:
6823		success = handle_case_0x7cc(t, i, j, k);
6824		break;
6825	case 0x7f0:
6826		success = handle_case_0x7f0(t, i, j, k);
6827		break;
6828	/*
6829	 * Handle the cases where three corners on a single face are missing.
6830	 */
6831	case 0x707:
6832		success = handle_case_0x707(t, i, j, k);
6833		break;
6834	case 0x70b:
6835		success = handle_case_0x70b(t, i, j, k);
6836		break;
6837	case 0x70d:
6838		success = handle_case_0x70d(t, i, j, k);
6839		break;
6840	case 0x70e:
6841		success = handle_case_0x70e(t, i, j, k);
6842		break;
6843	case 0x713:
6844		success = handle_case_0x713(t, i, j, k);
6845		break;
6846	case 0x715:
6847		success = handle_case_0x715(t, i, j, k);
6848		break;
6849	case 0x723:
6850		success = handle_case_0x723(t, i, j, k);
6851		break;
6852	case 0x72a:
6853		success = handle_case_0x72a(t, i, j, k);
6854		break;
6855	case 0x731:
6856		success = handle_case_0x731(t, i, j, k);
6857		break;
6858	case 0x732:
6859		success = handle_case_0x732(t, i, j, k);
6860		break;
6861	case 0x745:
6862		success = handle_case_0x745(t, i, j, k);
6863		break;
6864	case 0x74c:
6865		success = handle_case_0x74c(t, i, j, k);
6866		break;
6867	case 0x751:
6868		success = handle_case_0x751(t, i, j, k);
6869		break;
6870	case 0x754:
6871		success = handle_case_0x754(t, i, j, k);
6872		break;
6873	case 0x770:
6874		success = handle_case_0x770(t, i, j, k);
6875		break;
6876	case 0x78a:
6877		success = handle_case_0x78a(t, i, j, k);
6878		break;
6879	case 0x78c:
6880		success = handle_case_0x78c(t, i, j, k);
6881		break;
6882	case 0x7a2:
6883		success = handle_case_0x7a2(t, i, j, k);
6884		break;
6885	case 0x7a8:
6886		success = handle_case_0x7a8(t, i, j, k);
6887		break;
6888	case 0x7b0:
6889		success = handle_case_0x7b0(t, i, j, k);
6890		break;
6891	case 0x7c4:
6892		success = handle_case_0x7c4(t, i, j, k);
6893		break;
6894	case 0x7c8:
6895		success = handle_case_0x7c8(t, i, j, k);
6896		break;
6897	case 0x7d0:
6898		success = handle_case_0x7d0(t, i, j, k);
6899		break;
6900	case 0x7e0:
6901		success = handle_case_0x7e0(t, i, j, k);
6902		break;
6903	/*
6904	 * Handle the cases where two corners on a single edge are missing.
6905	 */
6906	case 0x703:
6907		success = handle_case_0x703(t, i, j, k);
6908		break;
6909	case 0x705:
6910		success = handle_case_0x705(t, i, j, k);
6911		break;
6912	case 0x70a:
6913		success = handle_case_0x70a(t, i, j, k);
6914		break;
6915	case 0x70c:
6916		success = handle_case_0x70c(t, i, j, k);
6917		break;
6918	case 0x711:
6919		success = handle_case_0x711(t, i, j, k);
6920		break;
6921	case 0x722:
6922		success = handle_case_0x722(t, i, j, k);
6923		break;
6924	case 0x730:
6925		success = handle_case_0x730(t, i, j, k);
6926		break;
6927	case 0x744:
6928		success = handle_case_0x744(t, i, j, k);
6929		break;
6930	case 0x750:
6931		success = handle_case_0x750(t, i, j, k);
6932		break;
6933	case 0x788:
6934		success = handle_case_0x788(t, i, j, k);
6935		break;
6936	case 0x7a0:
6937		success = handle_case_0x7a0(t, i, j, k);
6938		break;
6939	case 0x7c0:
6940		success = handle_case_0x7c0(t, i, j, k);
6941		break;
6942	/*
6943	 * Handle the cases where a single corner is missing.
6944	 */
6945	case 0x701:
6946		success = handle_case_0x701(t, i, j, k);
6947		break;
6948	case 0x702:
6949		success = handle_case_0x702(t, i, j, k);
6950		break;
6951	case 0x704:
6952		success = handle_case_0x704(t, i, j, k);
6953		break;
6954	case 0x708:
6955		success = handle_case_0x708(t, i, j, k);
6956		break;
6957	case 0x710:
6958		success = handle_case_0x710(t, i, j, k);
6959		break;
6960	case 0x720:
6961		success = handle_case_0x720(t, i, j, k);
6962		break;
6963	case 0x740:
6964		success = handle_case_0x740(t, i, j, k);
6965		break;
6966	case 0x780:
6967		success = handle_case_0x780(t, i, j, k);
6968		break;
6969
6970	default:
6971		/*
6972		 * There's lots of unhandled cases still, but it's not clear
6973		 * we care.  Let debugging show us what they are so we can
6974		 * learn if we care.
6975		 */
6976		if (t->debug)
6977			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
6978				"Unhandled fingerprint 0x%03x @ %d %d %d\n",
6979				fp, i, j, k);
6980		goto out;
6981	}
6982	/*
6983	 * If we successfully handled a case, we may be able to make more
6984	 * progress at this position, so try again.  Otherwise, even though
6985	 * we didn't successfully handle a case, we may have installed a
6986	 * switch into the torus/mesh, so try to install links as well.
6987	 * Then we'll have another go at the next position.
6988	 */
6989	if (success) {
6990		if (t->debug)
6991			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
6992				"Success on fingerprint 0x%03x @ %d %d %d\n",
6993				fp, i, j, k);
6994		goto again;
6995	} else {
6996		check_tlinks(t, i, j, k);
6997		if (t->debug)
6998			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
6999				"Failed on fingerprint 0x%03x @ %d %d %d\n",
7000				fp, i, j, k);
7001	}
7002out:
7003	return;
7004}
7005
7006#define LINK_ERR_STR " direction link required for topology seed configuration since radix == 4! See torus-2QoS.conf(5).\n"
7007#define LINK_ERR2_STR " direction link required for topology seed configuration! See torus-2QoS.conf(5).\n"
7008#define SEED_ERR_STR " direction links for topology seed do not share a common switch! See torus-2QoS.conf(5).\n"
7009
7010static
7011bool verify_setup(struct torus *t, struct fabric *f)
7012{
7013	struct coord_dirs *o;
7014	struct f_switch *sw;
7015	unsigned p, s, n = 0;
7016	bool success = false;
7017	bool all_sw_present, need_seed = true;
7018
7019	if (!(t->x_sz && t->y_sz && t->z_sz)) {
7020		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7021			"ERR 4E20: missing required torus size specification!\n");
7022		goto out;
7023	}
7024	if (t->osm->subn.min_sw_data_vls < 2) {
7025		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7026			"ERR 4E48: Too few data VLs to support torus routing "
7027			"without credit loops (have switchport %d need 2)\n",
7028			(int)t->osm->subn.min_sw_data_vls);
7029		goto out;
7030	}
7031	if (t->osm->subn.min_sw_data_vls < 4)
7032		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7033			"Warning: Too few data VLs to support torus routing "
7034			"with a failed switch without credit loops "
7035			"(have switchport %d need 4)\n",
7036			(int)t->osm->subn.min_sw_data_vls);
7037	if (t->osm->subn.min_sw_data_vls < 8)
7038		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7039			"Warning: Too few data VLs to support torus routing "
7040			"with two QoS levels (have switchport %d need 8)\n",
7041			(int)t->osm->subn.min_sw_data_vls);
7042	if (t->osm->subn.min_data_vls < 2)
7043		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7044			"Warning: Too few data VLs to support torus routing "
7045			"with two QoS levels (have endport %d need 2)\n",
7046			(int)t->osm->subn.min_data_vls);
7047	/*
7048	 * Be sure all the switches in the torus support the port
7049	 * ordering that might have been configured.
7050	 */
7051	for (s = 0; s < f->switch_cnt; s++) {
7052		sw = f->sw[s];
7053		for (p = 0; p < sw->port_cnt; p++) {
7054			if (t->port_order[p] >= sw->port_cnt) {
7055				OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7056					"ERR 4E21: port_order configured using "
7057					"port %u, but only %u ports in "
7058					"switch w/ GUID 0x%04"PRIx64"\n",
7059					t->port_order[p], sw->port_cnt - 1,
7060					cl_ntoh64(sw->n_id));
7061				goto out;
7062			}
7063		}
7064	}
7065	/*
7066	 * Unfortunately, there is a problem with non-unique topology for any
7067	 * torus dimension which has radix four.  This problem requires extra
7068	 * input, in the form of specifying both the positive and negative
7069	 * coordinate directions from a common switch, for any torus dimension
7070	 * with radix four (see also build_torus()).
7071	 *
7072	 * Do the checking required to ensure that the required information
7073	 * is present, but more than the needed information is not required.
7074	 *
7075	 * So, verify that we learned the coordinate directions correctly for
7076	 * the fabric.  The coordinate direction links get an invalid port
7077	 * set on their ends when parsed.
7078	 */
7079again:
7080	all_sw_present = true;
7081	o = &t->seed[n];
7082
7083	if (t->x_sz == 4 && !(t->flags & X_MESH)) {
7084		if (o->xp_link.end[0].port >= 0) {
7085			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7086				"ERR 4E22: Positive x" LINK_ERR_STR);
7087			goto out;
7088		}
7089		if (o->xm_link.end[0].port >= 0) {
7090			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7091				"ERR 4E23: Negative x" LINK_ERR_STR);
7092			goto out;
7093		}
7094		if (o->xp_link.end[0].n_id != o->xm_link.end[0].n_id) {
7095			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7096				"ERR 4E24: Positive/negative x" SEED_ERR_STR);
7097			goto out;
7098		}
7099	}
7100	if (t->y_sz == 4 && !(t->flags & Y_MESH)) {
7101		if (o->yp_link.end[0].port >= 0) {
7102			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7103				"ERR 4E25: Positive y" LINK_ERR_STR);
7104			goto out;
7105		}
7106		if (o->ym_link.end[0].port >= 0) {
7107			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7108				"ERR 4E26: Negative y" LINK_ERR_STR);
7109			goto out;
7110		}
7111		if (o->yp_link.end[0].n_id != o->ym_link.end[0].n_id) {
7112			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7113				"ERR 4E27: Positive/negative y" SEED_ERR_STR);
7114			goto out;
7115		}
7116	}
7117	if (t->z_sz == 4 && !(t->flags & Z_MESH)) {
7118		if (o->zp_link.end[0].port >= 0) {
7119			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7120				"ERR 4E28: Positive z" LINK_ERR_STR);
7121			goto out;
7122		}
7123		if (o->zm_link.end[0].port >= 0) {
7124			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7125				"ERR 4E29: Negative z" LINK_ERR_STR);
7126			goto out;
7127		}
7128		if (o->zp_link.end[0].n_id != o->zm_link.end[0].n_id) {
7129			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7130				"ERR 4E2A: Positive/negative z" SEED_ERR_STR);
7131			goto out;
7132		}
7133	}
7134	if (t->x_sz > 1) {
7135		if (o->xp_link.end[0].port >= 0 &&
7136		    o->xm_link.end[0].port >= 0) {
7137			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7138				"ERR 4E2B: Positive or negative x" LINK_ERR2_STR);
7139			goto out;
7140		}
7141		if (o->xp_link.end[0].port < 0 &&
7142		    !find_f_sw(f, o->xp_link.end[0].n_id))
7143			all_sw_present = false;
7144
7145		if (o->xp_link.end[1].port < 0 &&
7146		    !find_f_sw(f, o->xp_link.end[1].n_id))
7147			all_sw_present = false;
7148
7149		if (o->xm_link.end[0].port < 0 &&
7150		    !find_f_sw(f, o->xm_link.end[0].n_id))
7151			all_sw_present = false;
7152
7153		if (o->xm_link.end[1].port < 0 &&
7154		    !find_f_sw(f, o->xm_link.end[1].n_id))
7155			all_sw_present = false;
7156	}
7157	if (t->z_sz > 1) {
7158		if (o->zp_link.end[0].port >= 0 &&
7159		    o->zm_link.end[0].port >= 0) {
7160			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7161				"ERR 4E2C: Positive or negative z" LINK_ERR2_STR);
7162			goto out;
7163		}
7164		if ((o->xp_link.end[0].port < 0 &&
7165		     o->zp_link.end[0].port < 0 &&
7166		     o->zp_link.end[0].n_id != o->xp_link.end[0].n_id) ||
7167
7168		    (o->xp_link.end[0].port < 0 &&
7169		     o->zm_link.end[0].port < 0 &&
7170		     o->zm_link.end[0].n_id != o->xp_link.end[0].n_id) ||
7171
7172		    (o->xm_link.end[0].port < 0 &&
7173		     o->zp_link.end[0].port < 0 &&
7174		     o->zp_link.end[0].n_id != o->xm_link.end[0].n_id) ||
7175
7176		    (o->xm_link.end[0].port < 0 &&
7177		     o->zm_link.end[0].port < 0 &&
7178		     o->zm_link.end[0].n_id != o->xm_link.end[0].n_id)) {
7179
7180			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7181				"ERR 4E2D: x and z" SEED_ERR_STR);
7182			goto out;
7183		}
7184		if (o->zp_link.end[0].port < 0 &&
7185		    !find_f_sw(f, o->zp_link.end[0].n_id))
7186			all_sw_present = false;
7187
7188		if (o->zp_link.end[1].port < 0 &&
7189		    !find_f_sw(f, o->zp_link.end[1].n_id))
7190			all_sw_present = false;
7191
7192		if (o->zm_link.end[0].port < 0 &&
7193		    !find_f_sw(f, o->zm_link.end[0].n_id))
7194			all_sw_present = false;
7195
7196		if (o->zm_link.end[1].port < 0 &&
7197		    !find_f_sw(f, o->zm_link.end[1].n_id))
7198			all_sw_present = false;
7199	}
7200	if (t->y_sz > 1) {
7201		if (o->yp_link.end[0].port >= 0 &&
7202		    o->ym_link.end[0].port >= 0) {
7203			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7204				"ERR 4E2E: Positive or negative y" LINK_ERR2_STR);
7205			goto out;
7206		}
7207		if ((o->xp_link.end[0].port < 0 &&
7208		     o->yp_link.end[0].port < 0 &&
7209		     o->yp_link.end[0].n_id != o->xp_link.end[0].n_id) ||
7210
7211		    (o->xp_link.end[0].port < 0 &&
7212		     o->ym_link.end[0].port < 0 &&
7213		     o->ym_link.end[0].n_id != o->xp_link.end[0].n_id) ||
7214
7215		    (o->xm_link.end[0].port < 0 &&
7216		     o->yp_link.end[0].port < 0 &&
7217		     o->yp_link.end[0].n_id != o->xm_link.end[0].n_id) ||
7218
7219		    (o->xm_link.end[0].port < 0 &&
7220		     o->ym_link.end[0].port < 0 &&
7221		     o->ym_link.end[0].n_id != o->xm_link.end[0].n_id)) {
7222
7223			OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7224				"ERR 4E2F: x and y" SEED_ERR_STR);
7225			goto out;
7226		}
7227		if (o->yp_link.end[0].port < 0 &&
7228		    !find_f_sw(f, o->yp_link.end[0].n_id))
7229			all_sw_present = false;
7230
7231		if (o->yp_link.end[1].port < 0 &&
7232		    !find_f_sw(f, o->yp_link.end[1].n_id))
7233			all_sw_present = false;
7234
7235		if (o->ym_link.end[0].port < 0 &&
7236		    !find_f_sw(f, o->ym_link.end[0].n_id))
7237			all_sw_present = false;
7238
7239		if (o->ym_link.end[1].port < 0 &&
7240		    !find_f_sw(f, o->ym_link.end[1].n_id))
7241			all_sw_present = false;
7242	}
7243	if (all_sw_present && need_seed) {
7244		t->seed_idx = n;
7245		need_seed = false;
7246	}
7247	if (++n < t->seed_cnt)
7248		goto again;
7249
7250	if (need_seed)
7251		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7252			"ERR 4E30: Every configured torus seed has at "
7253			"least one switch missing in fabric! See "
7254			"torus-2QoS.conf(5) and TORUS TOPOLOGY DISCOVERY "
7255			"in torus-2QoS(8)\n");
7256	else
7257		success = true;
7258out:
7259	return success;
7260}
7261
7262static
7263bool build_torus(struct fabric *f, struct torus *t)
7264{
7265	int i, j, k;
7266	int im1, jm1, km1;
7267	int ip1, jp1, kp1;
7268	unsigned nlink;
7269	struct coord_dirs *o;
7270	struct f_switch *fsw0, *fsw1;
7271	struct t_switch ****sw = t->sw;
7272	bool success = true;
7273
7274	t->link_pool_sz = f->link_cnt;
7275	t->link_pool = calloc(1, t->link_pool_sz * sizeof(*t->link_pool));
7276	if (!t->link_pool) {
7277		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7278			"ERR 4E31: Allocating torus link pool: %s\n",
7279			strerror(errno));
7280		goto out;
7281	}
7282	t->fabric = f;
7283
7284	/*
7285	 * Get things started by locating the up to seven switches that
7286	 * define the torus "seed", coordinate directions, and datelines.
7287	 */
7288	o = &t->seed[t->seed_idx];
7289
7290	i = canonicalize(-o->x_dateline, t->x_sz);
7291	j = canonicalize(-o->y_dateline, t->y_sz);
7292	k = canonicalize(-o->z_dateline, t->z_sz);
7293
7294	if (o->xp_link.end[0].port < 0) {
7295		ip1 = canonicalize(1 - o->x_dateline, t->x_sz);
7296		fsw0 = find_f_sw(f, o->xp_link.end[0].n_id);
7297		fsw1 = find_f_sw(f, o->xp_link.end[1].n_id);
7298		success =
7299			install_tswitch(t, i, j, k, fsw0) &&
7300			install_tswitch(t, ip1, j, k, fsw1) && success;
7301	}
7302	if (o->xm_link.end[0].port < 0) {
7303		im1 = canonicalize(-1 - o->x_dateline, t->x_sz);
7304		fsw0 = find_f_sw(f, o->xm_link.end[0].n_id);
7305		fsw1 = find_f_sw(f, o->xm_link.end[1].n_id);
7306		success =
7307			install_tswitch(t, i, j, k, fsw0) &&
7308			install_tswitch(t, im1, j, k, fsw1) && success;
7309	}
7310	if (o->yp_link.end[0].port < 0) {
7311		jp1 = canonicalize(1 - o->y_dateline, t->y_sz);
7312		fsw0 = find_f_sw(f, o->yp_link.end[0].n_id);
7313		fsw1 = find_f_sw(f, o->yp_link.end[1].n_id);
7314		success =
7315			install_tswitch(t, i, j, k, fsw0) &&
7316			install_tswitch(t, i, jp1, k, fsw1) && success;
7317	}
7318	if (o->ym_link.end[0].port < 0) {
7319		jm1 = canonicalize(-1 - o->y_dateline, t->y_sz);
7320		fsw0 = find_f_sw(f, o->ym_link.end[0].n_id);
7321		fsw1 = find_f_sw(f, o->ym_link.end[1].n_id);
7322		success =
7323			install_tswitch(t, i, j, k, fsw0) &&
7324			install_tswitch(t, i, jm1, k, fsw1) && success;
7325	}
7326	if (o->zp_link.end[0].port < 0) {
7327		kp1 = canonicalize(1 - o->z_dateline, t->z_sz);
7328		fsw0 = find_f_sw(f, o->zp_link.end[0].n_id);
7329		fsw1 = find_f_sw(f, o->zp_link.end[1].n_id);
7330		success =
7331			install_tswitch(t, i, j, k, fsw0) &&
7332			install_tswitch(t, i, j, kp1, fsw1) && success;
7333	}
7334	if (o->zm_link.end[0].port < 0) {
7335		km1 = canonicalize(-1 - o->z_dateline, t->z_sz);
7336		fsw0 = find_f_sw(f, o->zm_link.end[0].n_id);
7337		fsw1 = find_f_sw(f, o->zm_link.end[1].n_id);
7338		success =
7339			install_tswitch(t, i, j, k, fsw0) &&
7340			install_tswitch(t, i, j, km1, fsw1) && success;
7341	}
7342	if (!success)
7343		goto out;
7344
7345	if (!t->seed_idx)
7346		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7347			"Using torus seed configured as default "
7348			"(seed sw %d,%d,%d GUID 0x%04"PRIx64").\n",
7349			i, j, k, cl_ntoh64(sw[i][j][k]->n_id));
7350	else
7351		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7352			"Using torus seed configured as backup #%u "
7353			"(seed sw %d,%d,%d GUID 0x%04"PRIx64").\n",
7354			t->seed_idx, i, j, k, cl_ntoh64(sw[i][j][k]->n_id));
7355
7356	/*
7357	 * Search the fabric and construct the expected torus topology.
7358	 *
7359	 * The algorithm is to consider the "cube" formed by eight switch
7360	 * locations bounded by the corners i, j, k and i+1, j+1, k+1.
7361	 * For each such cube look at the topology of the switches already
7362	 * placed in the torus, and deduce which new switches can be placed
7363	 * into their proper locations in the torus.  Examine each cube
7364	 * multiple times, until the number of links moved into the torus
7365	 * topology does not change.
7366	 */
7367again:
7368	nlink = t->link_cnt;
7369
7370	for (k = 0; k < (int)t->z_sz; k++)
7371		for (j = 0; j < (int)t->y_sz; j++)
7372			for (i = 0; i < (int)t->x_sz; i++)
7373				locate_sw(t, i, j, k);
7374
7375	if (t->link_cnt != nlink)
7376		goto again;
7377
7378	/*
7379	 * Move all other endpoints into torus/mesh.
7380	 */
7381	for (k = 0; k < (int)t->z_sz; k++)
7382		for (j = 0; j < (int)t->y_sz; j++)
7383			for (i = 0; i < (int)t->x_sz; i++)
7384				if (!link_srcsink(t, i, j, k)) {
7385					success = false;
7386					goto out;
7387				}
7388out:
7389	return success;
7390}
7391
7392/*
7393 * Returns a count of differences between old and new switches.
7394 */
7395static
7396unsigned tsw_changes(struct t_switch *nsw, struct t_switch *osw)
7397{
7398	unsigned p, cnt = 0, port_cnt;
7399	struct endpoint *npt, *opt;
7400	struct endpoint *rnpt, *ropt;
7401
7402	if (nsw && !osw) {
7403		cnt++;
7404		OSM_LOG(&nsw->torus->osm->log, OSM_LOG_INFO,
7405			"New torus switch %d,%d,%d GUID 0x%04"PRIx64"\n",
7406			nsw->i, nsw->j, nsw->k, cl_ntoh64(nsw->n_id));
7407		goto out;
7408	}
7409	if (osw && !nsw) {
7410		cnt++;
7411		OSM_LOG(&osw->torus->osm->log, OSM_LOG_INFO,
7412			"Lost torus switch %d,%d,%d GUID 0x%04"PRIx64"\n",
7413			osw->i, osw->j, osw->k, cl_ntoh64(osw->n_id));
7414		goto out;
7415	}
7416	if (!(nsw && osw))
7417		goto out;
7418
7419	if (nsw->n_id != osw->n_id) {
7420		cnt++;
7421		OSM_LOG(&nsw->torus->osm->log, OSM_LOG_INFO,
7422			"Torus switch %d,%d,%d GUID "
7423			"was 0x%04"PRIx64", now 0x%04"PRIx64"\n",
7424			nsw->i, nsw->j, nsw->k,
7425			cl_ntoh64(osw->n_id), cl_ntoh64(nsw->n_id));
7426	}
7427
7428	if (nsw->port_cnt != osw->port_cnt) {
7429		cnt++;
7430		OSM_LOG(&nsw->torus->osm->log, OSM_LOG_INFO,
7431			"Torus switch %d,%d,%d GUID 0x%04"PRIx64" "
7432			"had %d ports, now has %d\n",
7433			nsw->i, nsw->j, nsw->k, cl_ntoh64(nsw->n_id),
7434			osw->port_cnt, nsw->port_cnt);
7435	}
7436	port_cnt = nsw->port_cnt;
7437	if (port_cnt > osw->port_cnt)
7438		port_cnt = osw->port_cnt;
7439
7440	for (p = 0; p < port_cnt; p++) {
7441		npt = nsw->port[p];
7442		opt = osw->port[p];
7443
7444		if (npt && npt->link) {
7445			if (&npt->link->end[0] == npt)
7446				rnpt = &npt->link->end[1];
7447			else
7448				rnpt = &npt->link->end[0];
7449		} else
7450			rnpt = NULL;
7451
7452		if (opt && opt->link) {
7453			if (&opt->link->end[0] == opt)
7454				ropt = &opt->link->end[1];
7455			else
7456				ropt = &opt->link->end[0];
7457		} else
7458			ropt = NULL;
7459
7460		if (rnpt && !ropt) {
7461			++cnt;
7462			OSM_LOG(&nsw->torus->osm->log, OSM_LOG_INFO,
7463				"Torus switch %d,%d,%d GUID 0x%04"PRIx64"[%d] "
7464				"remote now %s GUID 0x%04"PRIx64"[%d], "
7465				"was missing\n",
7466				nsw->i, nsw->j, nsw->k, cl_ntoh64(nsw->n_id),
7467				p, rnpt->type == PASSTHRU ? "sw" : "node",
7468				cl_ntoh64(rnpt->n_id), rnpt->port);
7469			continue;
7470		}
7471		if (ropt && !rnpt) {
7472			++cnt;
7473			OSM_LOG(&nsw->torus->osm->log, OSM_LOG_INFO,
7474				"Torus switch %d,%d,%d GUID 0x%04"PRIx64"[%d] "
7475				"remote now missing, "
7476				"was %s GUID 0x%04"PRIx64"[%d]\n",
7477				osw->i, osw->j, osw->k, cl_ntoh64(nsw->n_id),
7478				p, ropt->type == PASSTHRU ? "sw" : "node",
7479				cl_ntoh64(ropt->n_id), ropt->port);
7480			continue;
7481		}
7482		if (!(rnpt && ropt))
7483			continue;
7484
7485		if (rnpt->n_id != ropt->n_id) {
7486			++cnt;
7487			OSM_LOG(&nsw->torus->osm->log, OSM_LOG_INFO,
7488				"Torus switch %d,%d,%d GUID 0x%04"PRIx64"[%d] "
7489				"remote now %s GUID 0x%04"PRIx64"[%d], "
7490				"was %s GUID 0x%04"PRIx64"[%d]\n",
7491				nsw->i, nsw->j, nsw->k, cl_ntoh64(nsw->n_id),
7492				p, rnpt->type == PASSTHRU ? "sw" : "node",
7493				cl_ntoh64(rnpt->n_id), rnpt->port,
7494				ropt->type == PASSTHRU ? "sw" : "node",
7495				cl_ntoh64(ropt->n_id), ropt->port);
7496			continue;
7497		}
7498	}
7499out:
7500	return cnt;
7501}
7502
7503static
7504void dump_torus(struct torus *t)
7505{
7506	unsigned i, j, k;
7507	unsigned x_sz = t->x_sz;
7508	unsigned y_sz = t->y_sz;
7509	unsigned z_sz = t->z_sz;
7510	char path[1024];
7511	FILE *file;
7512
7513	snprintf(path, sizeof(path), "%s/%s", t->osm->subn.opt.dump_files_dir,
7514		 "opensm-torus.dump");
7515	file = fopen(path, "w");
7516	if (!file) {
7517		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7518			"ERR 4E47: cannot create file \'%s\'\n", path);
7519		return;
7520	}
7521
7522	for (k = 0; k < z_sz; k++)
7523		for (j = 0; j < y_sz; j++)
7524			for (i = 0; i < x_sz; i++)
7525				if (t->sw[i][j][k])
7526					fprintf(file, "switch %u,%u,%u GUID 0x%04"
7527						PRIx64 " (%s)\n",
7528						i, j, k,
7529						cl_ntoh64(t->sw[i][j][k]->n_id),
7530						t->sw[i][j][k]->osm_switch->p_node->print_desc);
7531	fclose(file);
7532}
7533
7534static
7535void report_torus_changes(struct torus *nt, struct torus *ot)
7536{
7537	unsigned cnt = 0;
7538	unsigned i, j, k;
7539	unsigned x_sz = nt->x_sz;
7540	unsigned y_sz = nt->y_sz;
7541	unsigned z_sz = nt->z_sz;
7542	unsigned max_changes = nt->max_changes;
7543
7544	if (OSM_LOG_IS_ACTIVE_V2(&nt->osm->log, OSM_LOG_ROUTING))
7545		dump_torus(nt);
7546
7547	if (!ot)
7548		return;
7549
7550	if (x_sz != ot->x_sz) {
7551		cnt++;
7552		OSM_LOG(&nt->osm->log, OSM_LOG_INFO,
7553			"Torus x radix was %d now %d\n",
7554			ot->x_sz, nt->x_sz);
7555		if (x_sz > ot->x_sz)
7556			x_sz = ot->x_sz;
7557	}
7558	if (y_sz != ot->y_sz) {
7559		cnt++;
7560		OSM_LOG(&nt->osm->log, OSM_LOG_INFO,
7561			"Torus y radix was %d now %d\n",
7562			ot->y_sz, nt->y_sz);
7563		if (y_sz > ot->y_sz)
7564			y_sz = ot->y_sz;
7565	}
7566	if (z_sz != ot->z_sz) {
7567		cnt++;
7568		OSM_LOG(&nt->osm->log, OSM_LOG_INFO,
7569			"Torus z radix was %d now %d\n",
7570			ot->z_sz, nt->z_sz);
7571		if (z_sz > ot->z_sz)
7572			z_sz = ot->z_sz;
7573	}
7574
7575	for (k = 0; k < z_sz; k++)
7576		for (j = 0; j < y_sz; j++)
7577			for (i = 0; i < x_sz; i++) {
7578				cnt += tsw_changes(nt->sw[i][j][k],
7579						   ot->sw[i][j][k]);
7580				/*
7581				 * Booting a big fabric will cause lots of
7582				 * changes as hosts come up, so don't spew.
7583				 * We want to log changes to learn more about
7584				 * bouncing links, etc, so they can be fixed.
7585				 */
7586				if (cnt > max_changes) {
7587					OSM_LOG(&nt->osm->log, OSM_LOG_INFO,
7588						"Too many torus changes; "
7589						"stopping reporting early\n");
7590					return;
7591				}
7592			}
7593}
7594
7595static
7596void rpt_torus_missing(struct torus *t, int i, int j, int k,
7597		       struct t_switch *sw, int *missing_z)
7598{
7599	uint64_t guid_ho;
7600
7601	if (!sw) {
7602		/*
7603		 * We can have multiple missing switches without deadlock
7604		 * if and only if they are adajacent in the Z direction.
7605		 */
7606		if ((t->switch_cnt + 1) < t->sw_pool_sz) {
7607			if (t->sw[i][j][canonicalize(k - 1, t->z_sz)] &&
7608			    t->sw[i][j][canonicalize(k + 1, t->z_sz)])
7609				t->flags |= MSG_DEADLOCK;
7610		}
7611		/*
7612		 * There can be only one such Z-column of missing switches.
7613		 */
7614		if (*missing_z < 0)
7615			*missing_z = i + j * t->x_sz;
7616		else if (*missing_z != i + j * t->x_sz)
7617			t->flags |= MSG_DEADLOCK;
7618
7619		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7620			"Missing torus switch at %d,%d,%d\n", i, j, k);
7621		return;
7622	}
7623	guid_ho = cl_ntoh64(sw->n_id);
7624
7625	if (!(sw->ptgrp[0].port_cnt || (t->x_sz == 1) ||
7626	      ((t->flags & X_MESH) && i == 0)))
7627		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7628			"Missing torus -x link on "
7629			"switch %d,%d,%d GUID 0x%04"PRIx64"\n",
7630			i, j, k, guid_ho);
7631	if (!(sw->ptgrp[1].port_cnt || (t->x_sz == 1) ||
7632	      ((t->flags & X_MESH) && (i + 1) == t->x_sz)))
7633		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7634			"Missing torus +x link on "
7635			"switch %d,%d,%d GUID 0x%04"PRIx64"\n",
7636			i, j, k, guid_ho);
7637	if (!(sw->ptgrp[2].port_cnt || (t->y_sz == 1) ||
7638	      ((t->flags & Y_MESH) && j == 0)))
7639		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7640			"Missing torus -y link on "
7641			"switch %d,%d,%d GUID 0x%04"PRIx64"\n",
7642			i, j, k, guid_ho);
7643	if (!(sw->ptgrp[3].port_cnt || (t->y_sz == 1) ||
7644	      ((t->flags & Y_MESH) && (j + 1) == t->y_sz)))
7645		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7646			"Missing torus +y link on "
7647			"switch %d,%d,%d GUID 0x%04"PRIx64"\n",
7648			i, j, k, guid_ho);
7649	if (!(sw->ptgrp[4].port_cnt || (t->z_sz == 1) ||
7650	      ((t->flags & Z_MESH) && k == 0)))
7651		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7652			"Missing torus -z link on "
7653			"switch %d,%d,%d GUID 0x%04"PRIx64"\n",
7654			i, j, k, guid_ho);
7655	if (!(sw->ptgrp[5].port_cnt || (t->z_sz == 1) ||
7656	      ((t->flags & Z_MESH) && (k + 1) == t->z_sz)))
7657		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7658			"Missing torus +z link on "
7659			"switch %d,%d,%d GUID 0x%04"PRIx64"\n",
7660			i, j, k, guid_ho);
7661}
7662
7663/*
7664 * Returns true if the torus can be successfully routed, false otherwise.
7665 */
7666static
7667bool routable_torus(struct torus *t, struct fabric *f)
7668{
7669	int i, j, k, tmp = -1;
7670	unsigned b2g_cnt, g2b_cnt;
7671	bool success = true;
7672
7673	t->flags &= ~MSG_DEADLOCK;
7674
7675	if (t->link_cnt != f->link_cnt || t->switch_cnt != f->switch_cnt)
7676		OSM_LOG(&t->osm->log, OSM_LOG_INFO,
7677			"Warning: Could not construct torus using all "
7678			"known fabric switches and/or links.\n");
7679
7680	for (k = 0; k < (int)t->z_sz; k++)
7681		for (j = 0; j < (int)t->y_sz; j++)
7682			for (i = 0; i < (int)t->x_sz; i++)
7683				rpt_torus_missing(t, i, j, k,
7684						  t->sw[i][j][k], &tmp);
7685	/*
7686	 * Check for multiple failures that create disjoint regions on a ring.
7687	 */
7688	for (k = 0; k < (int)t->z_sz; k++)
7689		for (j = 0; j < (int)t->y_sz; j++) {
7690			b2g_cnt = 0;
7691			g2b_cnt = 0;
7692			for (i = 0; i < (int)t->x_sz; i++) {
7693
7694				if (!t->sw[i][j][k])
7695					continue;
7696
7697				if (!t->sw[i][j][k]->ptgrp[0].port_cnt)
7698					b2g_cnt++;
7699				if (!t->sw[i][j][k]->ptgrp[1].port_cnt)
7700					g2b_cnt++;
7701			}
7702			if (b2g_cnt != g2b_cnt) {
7703				OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7704					"ERR 4E32: strange failures in "
7705					"x ring at y=%d  z=%d"
7706					" b2g_cnt %u g2b_cnt %u\n",
7707					j, k, b2g_cnt, g2b_cnt);
7708				success = false;
7709			}
7710			if (b2g_cnt > 1) {
7711				OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7712					"ERR 4E33: disjoint failures in "
7713					"x ring at y=%d  z=%d\n", j, k);
7714				success = false;
7715			}
7716		}
7717
7718	for (i = 0; i < (int)t->x_sz; i++)
7719		for (k = 0; k < (int)t->z_sz; k++) {
7720			b2g_cnt = 0;
7721			g2b_cnt = 0;
7722			for (j = 0; j < (int)t->y_sz; j++) {
7723
7724				if (!t->sw[i][j][k])
7725					continue;
7726
7727				if (!t->sw[i][j][k]->ptgrp[2].port_cnt)
7728					b2g_cnt++;
7729				if (!t->sw[i][j][k]->ptgrp[3].port_cnt)
7730					g2b_cnt++;
7731			}
7732			if (b2g_cnt != g2b_cnt) {
7733				OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7734					"ERR 4E34: strange failures in "
7735					"y ring at x=%d  z=%d"
7736					" b2g_cnt %u g2b_cnt %u\n",
7737					i, k, b2g_cnt, g2b_cnt);
7738				success = false;
7739			}
7740			if (b2g_cnt > 1) {
7741				OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7742					"ERR 4E35: disjoint failures in "
7743					"y ring at x=%d  z=%d\n", i, k);
7744				success = false;
7745			}
7746		}
7747
7748	for (j = 0; j < (int)t->y_sz; j++)
7749		for (i = 0; i < (int)t->x_sz; i++) {
7750			b2g_cnt = 0;
7751			g2b_cnt = 0;
7752			for (k = 0; k < (int)t->z_sz; k++) {
7753
7754				if (!t->sw[i][j][k])
7755					continue;
7756
7757				if (!t->sw[i][j][k]->ptgrp[4].port_cnt)
7758					b2g_cnt++;
7759				if (!t->sw[i][j][k]->ptgrp[5].port_cnt)
7760					g2b_cnt++;
7761			}
7762			if (b2g_cnt != g2b_cnt) {
7763				OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7764					"ERR 4E36: strange failures in "
7765					"z ring at x=%d  y=%d"
7766					" b2g_cnt %u g2b_cnt %u\n",
7767					i, j, b2g_cnt, g2b_cnt);
7768				success = false;
7769			}
7770			if (b2g_cnt > 1) {
7771				OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7772					"ERR 4E37: disjoint failures in "
7773					"z ring at x=%d  y=%d\n", i, j);
7774				success = false;
7775			}
7776		}
7777
7778	if (t->flags & MSG_DEADLOCK) {
7779		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
7780			"ERR 4E38: missing switch topology "
7781			"==> message deadlock!\n");
7782		success = false;
7783	}
7784	return success;
7785}
7786
7787/*
7788 * Use this function to re-establish the pointers between a torus endpoint
7789 * and an opensm osm_port_t.
7790 *
7791 * Typically this is only needed when "opensm --ucast-cache" is used, and
7792 * a CA link bounces.  When the CA port goes away, the osm_port_t object
7793 * is destroyed, invalidating the endpoint osm_port_t pointer.  When the
7794 * link comes back, a new osm_port_t object is created with a NULL priv
7795 * member.  Thus, when osm_get_torus_sl() is called it is missing the data
7796 * needed to do its work.  Use this function to fix things up.
7797 */
7798static
7799struct endpoint *osm_port_relink_endpoint(const osm_port_t *osm_port)
7800{
7801	guid_t node_guid;
7802	uint8_t port_num, r_port_num;
7803	struct t_switch *sw;
7804	struct endpoint *ep = NULL;
7805	osm_switch_t *osm_sw;
7806	osm_physp_t *osm_physp;
7807	osm_node_t *osm_node, *r_osm_node;
7808
7809	/*
7810	 * We need to find the torus endpoint that has the same GUID as
7811	 * the osm_port.  Rather than search the entire set of endpoints,
7812	 * we'll try to follow pointers.
7813	 */
7814	osm_physp = osm_port->p_physp;
7815	osm_node = osm_port->p_node;
7816	port_num = osm_physp_get_port_num(osm_physp);
7817	node_guid = osm_node_get_node_guid(osm_node);
7818	/*
7819	 * Switch management port?
7820	 */
7821	if (port_num == 0 &&
7822	    osm_node_get_type(osm_node) == IB_NODE_TYPE_SWITCH) {
7823
7824		osm_sw = osm_node->sw;
7825		if (osm_sw && osm_sw->priv) {
7826			sw = osm_sw->priv;
7827			if (sw->osm_switch == osm_sw &&
7828			    sw->port[0]->n_id == node_guid) {
7829
7830				ep = sw->port[0];
7831				goto relink_priv;
7832			}
7833		}
7834	}
7835	/*
7836	 * CA port?  Try other end of link.  This should also catch a
7837	 * router port if it is connected to a switch.
7838	 */
7839	r_osm_node = osm_node_get_remote_node(osm_node, port_num, &r_port_num);
7840	if (!r_osm_node)
7841		goto out;
7842
7843	osm_sw = r_osm_node->sw;
7844	if (!osm_sw)
7845		goto out;
7846
7847	sw = osm_sw->priv;
7848	if (!(sw && sw->osm_switch == osm_sw))
7849		goto out;
7850
7851	ep = sw->port[r_port_num];
7852	if (!(ep && ep->link))
7853		goto out;
7854
7855	if (ep->link->end[0].n_id == node_guid) {
7856		ep = &ep->link->end[0];
7857		goto relink_priv;
7858	}
7859	if (ep->link->end[1].n_id == node_guid) {
7860		ep = &ep->link->end[1];
7861		goto relink_priv;
7862	}
7863	ep = NULL;
7864	goto out;
7865
7866relink_priv:
7867	/* FIXME:
7868	 * Unfortunately, we need to cast away const to rebuild the links
7869	 * between the torus endpoint and the osm_port_t.
7870	 *
7871	 * What is really needed is to check whether pr_rcv_get_path_parms()
7872	 * needs its port objects to be const.  If so, why, and whether
7873	 * anything can be done about it.
7874	 */
7875	((osm_port_t *)osm_port)->priv = ep;
7876	ep->osm_port = (osm_port_t *)osm_port;
7877out:
7878	return ep;
7879}
7880
7881/*
7882 * Computing LFT entries and path SL values:
7883 *
7884 * For a pristine torus, we compute LFT entries using XYZ DOR, and select
7885 * which direction to route on a ring (i.e., the 1-D torus for the coordinate
7886 * in question) based on shortest path.  We compute the SL to use for the
7887 * path based on whether we crossed a dateline (where a ring coordinate
7888 * wraps to zero) for each coordinate.
7889 *
7890 * When there is a link/switch failure, we want to compute LFT entries
7891 * to route around the failure, without changing the path SL.  I.e., we
7892 * want the SL to reach a given destination from a given source to be
7893 * independent of the presence or number of failed components in the fabric.
7894 *
7895 * In order to make this feasible, we will assume that no ring is broken
7896 * into disjoint pieces by multiple failures
7897 *
7898 * We handle failure by attempting to take the long way around any ring
7899 * with connectivity interrupted by failed components, unless the path
7900 * requires a turn on a failed switch.
7901 *
7902 * For paths that require a turn on a failed switch, we head towards the
7903 * failed switch, then turn when progress is blocked by a failure, using a
7904 * turn allowed under XYZ DOR.  However, such a path will also require a turn
7905 * that is not a legal XYZ DOR turn, so we construct the SL2VL mapping tables
7906 * such that XYZ DOR turns use one set of VLs and ZYX DOR turns use a
7907 * separate set of VLs.
7908 *
7909 * Under these rules the algorithm guarantees credit-loop-free routing for a
7910 * single failed switch, without any change in path SL values.  We can also
7911 * guarantee credit-loop-free routing for failures of multiple switches, if
7912 * they are adjacent in the last DOR direction.  Since we use XYZ-DOR,
7913 * that means failed switches at i,j,k and i,j,k+1 will not cause credit
7914 * loops.
7915 *
7916 * These failure routing rules are intended to prevent paths that cross any
7917 * coordinate dateline twice (over and back), so we don't need to worry about
7918 * any ambiguity over which SL to use for such a case.  Also, we cannot have
7919 * a ring deadlock when a ring is broken by failure and we route the long
7920 * way around, so we don't need to worry about the impact of such routing
7921 * on SL choice.
7922 */
7923
7924/*
7925 * Functions to set our SL bit encoding for routing/QoS info.  Combine the
7926 * resuts of these functions with bitwise or to get final SL.
7927 *
7928 * SL bits 0-2 encode whether we "looped" in a given direction
7929 * on the torus on the path from source to destination.
7930 *
7931 * SL bit 3 encodes the QoS level.  We only support two QoS levels.
7932 *
7933 * Below we assume TORUS_MAX_DIM == 3 and 0 <= coord_dir < TORUS_MAX_DIM.
7934 */
7935static inline
7936unsigned sl_set_use_loop_vl(bool use_loop_vl, unsigned coord_dir)
7937{
7938	return (coord_dir < TORUS_MAX_DIM)
7939		? ((unsigned)use_loop_vl << coord_dir) : 0;
7940}
7941
7942static inline
7943unsigned sl_set_qos(unsigned qos)
7944{
7945	return (unsigned)(!!qos) << TORUS_MAX_DIM;
7946}
7947
7948/*
7949 * Functions to crack our SL bit encoding for routing/QoS info.
7950 */
7951static inline
7952bool sl_get_use_loop_vl(unsigned sl, unsigned coord_dir)
7953{
7954	return (coord_dir < TORUS_MAX_DIM)
7955		? (sl >> coord_dir) & 0x1 : false;
7956}
7957
7958static inline
7959unsigned sl_get_qos(unsigned sl)
7960{
7961	return (sl >> TORUS_MAX_DIM) & 0x1;
7962}
7963
7964/*
7965 * Functions to encode routing/QoS info into VL bits.  Combine the resuts of
7966 * these functions with bitwise or to get final VL.
7967 *
7968 * For interswitch links:
7969 * VL bit 0 encodes whether we need to leave on the "loop" VL.
7970 *
7971 * VL bit 1 encodes whether turn is XYZ DOR or ZYX DOR. A 3d mesh/torus
7972 * has 6 turn types: x-y, y-z, x-z, y-x, z-y, z-x.  The first three are
7973 * legal XYZ DOR turns, and the second three are legal ZYX DOR turns.
7974 * Straight-through (x-x, y-y, z-z) paths are legal in both DOR variants,
7975 * so we'll assign them to XYZ DOR VLs.
7976 *
7977 * Note that delivery to switch-local ports (i.e. those that source/sink
7978 * traffic, rather than forwarding it) cannot cause a deadlock, so that
7979 * can also use either XYZ or ZYX DOR.
7980 *
7981 * VL bit 2 encodes QoS level.
7982 *
7983 * For end port links:
7984 * VL bit 0 encodes QoS level.
7985 *
7986 * Note that if VL bit encodings are changed here, the available fabric VL
7987 * verification in verify_setup() needs to be updated as well.
7988 */
7989static inline
7990unsigned vl_set_loop_vl(bool use_loop_vl)
7991{
7992	return use_loop_vl;
7993}
7994
7995static inline
7996unsigned vl_set_qos_vl(unsigned qos)
7997{
7998	return (qos & 0x1) << 2;
7999}
8000
8001static inline
8002unsigned vl_set_ca_qos_vl(unsigned qos)
8003{
8004	return qos & 0x1;
8005}
8006
8007static inline
8008unsigned vl_set_turn_vl(unsigned in_coord_dir, unsigned out_coord_dir)
8009{
8010	unsigned vl = 0;
8011
8012	if (in_coord_dir != TORUS_MAX_DIM &&
8013	    out_coord_dir != TORUS_MAX_DIM)
8014		vl = (in_coord_dir > out_coord_dir)
8015			? 0x1 << 1 : 0;
8016
8017	return vl;
8018}
8019
8020static
8021unsigned sl2vl_entry(struct torus *t, struct t_switch *sw,
8022		     int input_pt, int output_pt, unsigned sl)
8023{
8024	unsigned id, od, vl, data_vls;
8025
8026	if (sw && sw->port[input_pt])
8027		id = sw->port[input_pt]->pgrp->port_grp / 2;
8028	else
8029		id = TORUS_MAX_DIM;
8030
8031	if (sw && sw->port[output_pt])
8032		od = sw->port[output_pt]->pgrp->port_grp / 2;
8033	else
8034		od = TORUS_MAX_DIM;
8035
8036	if (sw)
8037		data_vls = t->osm->subn.min_sw_data_vls;
8038	else
8039		data_vls = t->osm->subn.min_data_vls;
8040
8041	vl = 0;
8042	if (sw && od != TORUS_MAX_DIM) {
8043		if (data_vls >= 2)
8044			vl |= vl_set_loop_vl(sl_get_use_loop_vl(sl, od));
8045		if (data_vls >= 4)
8046			vl |= vl_set_turn_vl(id, od);
8047		if (data_vls >= 8)
8048			vl |= vl_set_qos_vl(sl_get_qos(sl));
8049	} else {
8050		if (data_vls >= 2)
8051			vl |= vl_set_ca_qos_vl(sl_get_qos(sl));
8052	}
8053	return vl;
8054}
8055
8056static
8057void torus_update_osm_sl2vl(void *context, osm_physp_t *osm_phys_port,
8058			    uint8_t iport_num, uint8_t oport_num,
8059			    ib_slvl_table_t *osm_oport_sl2vl)
8060{
8061	osm_node_t *node = osm_physp_get_node_ptr(osm_phys_port);
8062	struct torus_context *ctx = context;
8063	struct t_switch *sw = NULL;
8064	int sl, vl;
8065
8066	if (node->sw) {
8067		sw = node->sw->priv;
8068		if (sw && sw->osm_switch != node->sw) {
8069			osm_log_t *log = &ctx->osm->log;
8070			guid_t guid;
8071
8072			guid = osm_node_get_node_guid(node);
8073			OSM_LOG(log, OSM_LOG_INFO,
8074				"Note: osm_switch (GUID 0x%04"PRIx64") "
8075				"not in torus fabric description\n",
8076				cl_ntoh64(guid));
8077			return;
8078		}
8079	}
8080	for (sl = 0; sl < 16; sl++) {
8081		vl = sl2vl_entry(ctx->torus, sw, iport_num, oport_num, sl);
8082		ib_slvl_table_set(osm_oport_sl2vl, sl, vl);
8083	}
8084}
8085
8086static
8087void torus_update_osm_vlarb(void *context, osm_physp_t *osm_phys_port,
8088			    uint8_t port_num, ib_vl_arb_table_t *block,
8089			    unsigned block_length, unsigned block_num)
8090{
8091	osm_node_t *node = osm_physp_get_node_ptr(osm_phys_port);
8092	struct torus_context *ctx = context;
8093	struct t_switch *sw = NULL;
8094	unsigned i, next;
8095
8096	if (node->sw) {
8097		sw = node->sw->priv;
8098		if (sw && sw->osm_switch != node->sw) {
8099			osm_log_t *log = &ctx->osm->log;
8100			guid_t guid;
8101
8102			guid = osm_node_get_node_guid(node);
8103			OSM_LOG(log, OSM_LOG_INFO,
8104				"Note: osm_switch (GUID 0x%04"PRIx64") "
8105				"not in torus fabric description\n",
8106				cl_ntoh64(guid));
8107			return;
8108		}
8109	}
8110
8111	/*
8112	 * If osm_phys_port is a switch port that connects to a CA, then
8113	 * we're using at most VL 0 (for QoS level 0) and VL 1 (for QoS
8114	 * level 1).  We've been passed the VLarb values for a switch
8115	 * external port, so we need to fix them up to avoid unexpected
8116	 * results depending on how the switch handles VLarb values for
8117	 * unprogrammed VLs.
8118	 *
8119	 * For inter-switch links torus-2QoS uses VLs 0-3 to implement
8120	 * QoS level 0, and VLs 4-7 to implement QoS level 1.
8121	 *
8122	 * So, leave VL 0 alone, remap VL 4 to VL 1, zero out the rest,
8123	 * and compress out the zero entries to the end.
8124	 */
8125	if (!sw || !port_num || !sw->port[port_num] ||
8126	    sw->port[port_num]->pgrp->port_grp != 2 * TORUS_MAX_DIM)
8127		return;
8128
8129	next = 0;
8130	for (i = 0; i < block_length; i++) {
8131		switch (block->vl_entry[i].vl) {
8132		case 4:
8133			block->vl_entry[i].vl = 1;
8134			/* fall through */
8135		case 0:
8136			block->vl_entry[next].vl = block->vl_entry[i].vl;
8137			block->vl_entry[next].weight = block->vl_entry[i].weight;
8138			next++;
8139			/*
8140			 * If we didn't update vl_entry[i] in place,
8141			 * fall through to zero it out.
8142			 */
8143			if (next > i)
8144				break;
8145		default:
8146			block->vl_entry[i].vl = 0;
8147			block->vl_entry[i].weight = 0;
8148			break;
8149		}
8150	}
8151}
8152
8153/*
8154 * Computes the path lengths *vl0_len and *vl1_len to get from src
8155 * to dst on a ring with count switches.
8156 *
8157 * *vl0_len is the path length for a direct path; it corresponds to a path
8158 * that should be assigned to use VL0 in a switch.  *vl1_len is the path
8159 * length for a path that wraps aroung the ring, i.e. where the ring index
8160 * goes from count to zero or from zero to count.  It corresponds to the path
8161 * that should be assigned to use VL1 in a switch.
8162 */
8163static
8164void get_pathlen(unsigned src, unsigned dst, unsigned count,
8165		 unsigned *vl0_len, unsigned *vl1_len)
8166{
8167	unsigned s, l;		/* assume s < l */
8168
8169	if (dst > src) {
8170		s = src;
8171		l = dst;
8172	} else {
8173		s = dst;
8174		l = src;
8175	}
8176	*vl0_len = l - s;
8177	*vl1_len = s + count - l;
8178}
8179
8180/*
8181 * Returns a positive number if we should take the "positive" ring direction
8182 * to reach dst from src, a negative number if we should take the "negative"
8183 * ring direction, and 0 if src and dst are the same.  The choice is strictly
8184 * based on which path is shorter.
8185 */
8186static
8187int ring_dir_idx(unsigned src, unsigned dst, unsigned count)
8188{
8189	int r;
8190	unsigned vl0_len, vl1_len;
8191
8192	if (dst == src)
8193		return 0;
8194
8195	get_pathlen(src, dst, count, &vl0_len, &vl1_len);
8196
8197	if (dst > src)
8198		r = vl0_len <= vl1_len ? 1 : -1;
8199	else
8200		r = vl0_len <= vl1_len ? -1 : 1;
8201
8202	return r;
8203}
8204
8205/*
8206 * Returns true if the VL1 path should be used to reach src from dst on a
8207 * ring, based on which path is shorter.
8208 */
8209static
8210bool use_vl1(unsigned src, unsigned dst, unsigned count)
8211{
8212	unsigned vl0_len, vl1_len;
8213
8214	get_pathlen(src, dst, count, &vl0_len, &vl1_len);
8215
8216	return vl0_len <= vl1_len ? false : true;
8217}
8218
8219/*
8220 * Returns the next switch in the ring of switches along coordinate direction
8221 * cdir, in the positive ring direction if rdir is positive, and in the
8222 * negative ring direction if rdir is negative.
8223 *
8224 * Returns NULL if rdir is zero, or there is no next switch.
8225 */
8226static
8227struct t_switch *ring_next_sw(struct t_switch *sw, unsigned cdir, int rdir)
8228{
8229	unsigned pt_grp, far_end = 0;
8230
8231	if (!rdir)
8232		return NULL;
8233	/*
8234	 * Recall that links are installed into the torus so that their 1 end
8235	 * is in the "positive" coordinate direction relative to their 0 end
8236	 * (see link_tswitches() and connect_tlink()).  Recall also that for
8237	 * interswitch links, all links in a given switch port group have the
8238	 * same endpoints, so we just need to look at the first link.
8239	 */
8240	pt_grp = 2 * cdir;
8241	if (rdir > 0) {
8242		pt_grp++;
8243		far_end = 1;
8244	}
8245
8246	if (!sw->ptgrp[pt_grp].port_cnt)
8247		return NULL;
8248
8249	return sw->ptgrp[pt_grp].port[0]->link->end[far_end].sw;
8250}
8251
8252/*
8253 * Returns a positive number if we should take the "positive" ring direction
8254 * to reach dsw from ssw, a negative number if we should take the "negative"
8255 * ring direction, and 0 if src and dst are the same, or if dsw is not
8256 * reachable from ssw because the path is interrupted by failure.
8257 */
8258static
8259int ring_dir_path(struct torus *t, unsigned cdir,
8260		  struct t_switch *ssw, struct t_switch *dsw)
8261{
8262	int d = 0;
8263	struct t_switch *sw;
8264
8265	switch (cdir) {
8266	case 0:
8267		d = ring_dir_idx(ssw->i, dsw->i, t->x_sz);
8268		break;
8269	case 1:
8270		d = ring_dir_idx(ssw->j, dsw->j, t->y_sz);
8271		break;
8272	case 2:
8273		d = ring_dir_idx(ssw->k, dsw->k, t->z_sz);
8274		break;
8275	default:
8276		break;
8277	}
8278	if (!d)
8279		goto out;
8280
8281	sw = ssw;
8282	while (sw) {
8283		sw = ring_next_sw(sw, cdir, d);
8284		if (sw == dsw)
8285			goto out;
8286	}
8287	d *= -1;
8288	sw = ssw;
8289	while (sw) {
8290		sw = ring_next_sw(sw, cdir, d);
8291		if (sw == dsw)
8292			goto out;
8293	}
8294	d = 0;
8295out:
8296	return d;
8297}
8298
8299/*
8300 * Returns true, and sets *pt_grp to the port group index to use for the
8301 * next hop, if it is possible to make progress from ssw to dsw along the
8302 * coordinate direction cdir, taking into account whether there are
8303 * interruptions in the path.
8304 *
8305 * This next hop result can be used without worrying about ring deadlocks -
8306 * if we don't choose the shortest path it is because there is a failure in
8307 * the ring, which removes the possibilility of a ring deadlock on that ring.
8308 */
8309static
8310bool next_hop_path(struct torus *t, unsigned cdir,
8311		   struct t_switch *ssw, struct t_switch *dsw,
8312		   unsigned *pt_grp)
8313{
8314	struct t_switch *tsw = NULL;
8315	bool success = false;
8316	int d;
8317
8318	/*
8319	 * If the path from ssw to dsw turns, this is the switch where the
8320	 * turn happens.
8321	 */
8322	switch (cdir) {
8323	case 0:
8324		tsw = t->sw[dsw->i][ssw->j][ssw->k];
8325		break;
8326	case 1:
8327		tsw = t->sw[ssw->i][dsw->j][ssw->k];
8328		break;
8329	case 2:
8330		tsw = t->sw[ssw->i][ssw->j][dsw->k];
8331		break;
8332	default:
8333		goto out;
8334	}
8335	if (tsw) {
8336		d = ring_dir_path(t, cdir, ssw, tsw);
8337		cdir *= 2;
8338		if (d > 0)
8339			*pt_grp = cdir + 1;
8340		else if (d < 0)
8341			*pt_grp = cdir;
8342		else
8343			goto out;
8344		success = true;
8345	}
8346out:
8347	return success;
8348}
8349
8350/*
8351 * Returns true, and sets *pt_grp to the port group index to use for the
8352 * next hop, if it is possible to make progress from ssw to dsw along the
8353 * coordinate direction cdir.  This decision is made strictly on a
8354 * shortest-path basis without regard for path availability.
8355 */
8356static
8357bool next_hop_idx(struct torus *t, unsigned cdir,
8358		  struct t_switch *ssw, struct t_switch *dsw,
8359		  unsigned *pt_grp)
8360{
8361	int d;
8362	unsigned g;
8363	bool success = false;
8364
8365	switch (cdir) {
8366	case 0:
8367		d = ring_dir_idx(ssw->i, dsw->i, t->x_sz);
8368		break;
8369	case 1:
8370		d = ring_dir_idx(ssw->j, dsw->j, t->y_sz);
8371		break;
8372	case 2:
8373		d = ring_dir_idx(ssw->k, dsw->k, t->z_sz);
8374		break;
8375	default:
8376		goto out;
8377	}
8378
8379	cdir *= 2;
8380	if (d > 0)
8381		g = cdir + 1;
8382	else if (d < 0)
8383		g = cdir;
8384	else
8385		goto out;
8386
8387	if (!ssw->ptgrp[g].port_cnt)
8388		goto out;
8389
8390	*pt_grp = g;
8391	success = true;
8392out:
8393	return success;
8394}
8395
8396static
8397void warn_on_routing(const char *msg,
8398		     struct t_switch *sw, struct t_switch *dsw)
8399{
8400	OSM_LOG(&sw->torus->osm->log, OSM_LOG_ERROR,
8401		"%s from sw 0x%04"PRIx64" (%d,%d,%d) "
8402		"to sw 0x%04"PRIx64" (%d,%d,%d)\n",
8403		msg, cl_ntoh64(sw->n_id), sw->i, sw->j, sw->k,
8404		cl_ntoh64(dsw->n_id), dsw->i, dsw->j, dsw->k);
8405}
8406
8407static
8408bool next_hop_x(struct torus *t,
8409		struct t_switch *ssw, struct t_switch *dsw, unsigned *pt_grp)
8410{
8411	if (t->sw[dsw->i][ssw->j][ssw->k])
8412		/*
8413		 * The next turning switch on this path is available,
8414		 * so head towards it by the shortest available path.
8415		 */
8416		return next_hop_path(t, 0, ssw, dsw, pt_grp);
8417	else
8418		/*
8419		 * The next turning switch on this path is not
8420		 * available, so head towards it in the shortest
8421		 * path direction.
8422		 */
8423		return next_hop_idx(t, 0, ssw, dsw, pt_grp);
8424}
8425
8426static
8427bool next_hop_y(struct torus *t,
8428		struct t_switch *ssw, struct t_switch *dsw, unsigned *pt_grp)
8429{
8430	if (t->sw[ssw->i][dsw->j][ssw->k])
8431		/*
8432		 * The next turning switch on this path is available,
8433		 * so head towards it by the shortest available path.
8434		 */
8435		return next_hop_path(t, 1, ssw, dsw, pt_grp);
8436	else
8437		/*
8438		 * The next turning switch on this path is not
8439		 * available, so head towards it in the shortest
8440		 * path direction.
8441		 */
8442		return next_hop_idx(t, 1, ssw, dsw, pt_grp);
8443}
8444
8445static
8446bool next_hop_z(struct torus *t,
8447		struct t_switch *ssw, struct t_switch *dsw, unsigned *pt_grp)
8448{
8449	return next_hop_path(t, 2, ssw, dsw, pt_grp);
8450}
8451
8452/*
8453 * Returns the port number on *sw to use to reach *dsw, or -1 if unable to
8454 * route.
8455 */
8456static
8457int lft_port(struct torus *t,
8458	     struct t_switch *sw, struct t_switch *dsw,
8459	     bool update_port_cnt, bool ca)
8460{
8461	unsigned g, p;
8462	struct port_grp *pg;
8463
8464	/*
8465	 * The IBA does not provide a way to preserve path history for
8466	 * routing decisions and VL assignment, and the only mechanism to
8467	 * provide global fabric knowledge to the routing engine is via
8468	 * the four SL bits.  This severely constrains the ability to deal
8469	 * with missing/dead switches.
8470	 *
8471	 * Also, if routing a torus with XYZ-DOR, the only way to route
8472	 * around a missing/dead switch is to introduce a turn that is
8473	 * illegal under XYZ-DOR.
8474	 *
8475	 * But here's what we can do:
8476	 *
8477	 * We have a VL bit we use to flag illegal turns, thus putting the
8478	 * hop directly after an illegal turn on a separate set of VLs.
8479	 * Unfortunately, since there is no path history,  the _second_
8480	 * and subsequent hops after an illegal turn use the standard
8481	 * XYZ-DOR VL set.  This is enough to introduce credit loops in
8482	 * many cases.
8483	 *
8484	 * To minimize the number of cases such illegal turns can introduce
8485	 * credit loops, we try to introduce the illegal turn as late in a
8486	 * path as possible.
8487	 *
8488	 * Define a turning switch as a switch where a path turns from one
8489	 * coordinate direction onto another.  If a turning switch in a path
8490	 * is missing, construct the LFT entries so that the path progresses
8491	 * as far as possible on the shortest path to the turning switch.
8492	 * When progress is not possible, turn onto the next coordinate
8493	 * direction.
8494	 *
8495	 * The next turn after that will be an illegal turn, after which
8496	 * point the path will continue to use a standard XYZ-DOR path.
8497	 */
8498	if (dsw->i != sw->i) {
8499
8500		if (next_hop_x(t, sw, dsw, &g))
8501			goto done;
8502		/*
8503		 * This path has made as much progress in this direction as
8504		 * is possible, so turn it now.
8505		 */
8506		if (dsw->j != sw->j && next_hop_y(t, sw, dsw, &g))
8507			goto done;
8508
8509		if (dsw->k != sw->k && next_hop_z(t, sw, dsw, &g))
8510			goto done;
8511
8512		warn_on_routing("Error: unable to route", sw, dsw);
8513		goto no_route;
8514	} else if (dsw->j != sw->j) {
8515
8516		if (next_hop_y(t, sw, dsw, &g))
8517			goto done;
8518
8519		if (dsw->k != sw->k && next_hop_z(t, sw, dsw, &g))
8520			goto done;
8521
8522		warn_on_routing("Error: unable to route", sw, dsw);
8523		goto no_route;
8524	} else {
8525		if (dsw->k == sw->k)
8526			warn_on_routing("Warning: bad routing", sw, dsw);
8527
8528		if (next_hop_z(t, sw, dsw, &g))
8529			goto done;
8530
8531		warn_on_routing("Error: unable to route", sw, dsw);
8532		goto no_route;
8533	}
8534done:
8535	pg = &sw->ptgrp[g];
8536	if (!pg->port_cnt)
8537		goto no_route;
8538
8539	if (update_port_cnt) {
8540		if (ca)
8541			p = pg->ca_dlid_cnt++ % pg->port_cnt;
8542		else
8543			p = pg->sw_dlid_cnt++ % pg->port_cnt;
8544	} else {
8545		/*
8546		 * If we're not updating port counts, then we're just running
8547		 * routes for SL path checking, and it doesn't matter which
8548		 * of several parallel links we use.  Use the first one.
8549		 */
8550		p = 0;
8551	}
8552	p = pg->port[p]->port;
8553
8554	return p;
8555
8556no_route:
8557	/*
8558	 * We can't get there from here.
8559	 */
8560	OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
8561		"ERR 4E39: routing on sw 0x%04"PRIx64": sending "
8562		"traffic for dest sw 0x%04"PRIx64" to port %u\n",
8563		cl_ntoh64(sw->n_id), cl_ntoh64(dsw->n_id), OSM_NO_PATH);
8564	return -1;
8565}
8566
8567static
8568bool get_lid(struct port_grp *pg, unsigned p,
8569	     uint16_t *dlid_base, uint8_t *dlid_lmc, bool *ca)
8570{
8571	struct endpoint *ep;
8572	osm_port_t *osm_port;
8573
8574	if (p >= pg->port_cnt) {
8575		OSM_LOG(&pg->sw->torus->osm->log, OSM_LOG_ERROR,
8576			"ERR 4E3A: Port group index %u too large: sw "
8577			"0x%04"PRIx64" pt_grp %u pt_grp_cnt %u\n",
8578			p, cl_ntoh64(pg->sw->n_id),
8579			(unsigned)pg->port_grp, (unsigned)pg->port_cnt);
8580		return false;
8581	}
8582	if (pg->port[p]->type == SRCSINK) {
8583		ep = pg->port[p];
8584		if (ca)
8585			*ca = false;
8586	} else if (pg->port[p]->type == PASSTHRU &&
8587		   pg->port[p]->link->end[1].type == SRCSINK) {
8588		/*
8589		 * If this port is connected via a link to a CA, then we
8590		 * know link->end[0] is the switch end and link->end[1] is
8591		 * the CA end; see build_ca_link() and link_srcsink().
8592		 */
8593		ep = &pg->port[p]->link->end[1];
8594		if (ca)
8595			*ca = true;
8596	} else {
8597		OSM_LOG(&pg->sw->torus->osm->log, OSM_LOG_ERROR,
8598			"ERR 4E3B: Switch 0x%04"PRIx64" port %d improperly connected\n",
8599			cl_ntoh64(pg->sw->n_id), pg->port[p]->port);
8600		return false;
8601	}
8602	osm_port = ep->osm_port;
8603	if (!(osm_port && osm_port->priv == ep)) {
8604		OSM_LOG(&pg->sw->torus->osm->log, OSM_LOG_ERROR,
8605			"ERR 4E3C: ep->osm_port->priv != ep "
8606			"for sw 0x%04"PRIx64" port %d\n",
8607			cl_ntoh64(((struct t_switch *)(ep->sw))->n_id), ep->port);
8608		return false;
8609	}
8610	*dlid_base = cl_ntoh16(osm_physp_get_base_lid(osm_port->p_physp));
8611	*dlid_lmc = osm_physp_get_lmc(osm_port->p_physp);
8612
8613	return true;
8614}
8615
8616static
8617bool torus_lft(struct torus *t, struct t_switch *sw)
8618{
8619	bool success = true;
8620	int dp;
8621	unsigned p, s;
8622	uint16_t l, dlid_base;
8623	uint8_t dlid_lmc;
8624	bool ca;
8625	struct port_grp *pgrp;
8626	struct t_switch *dsw;
8627	osm_switch_t *osm_sw;
8628	uint8_t order[IB_NODE_NUM_PORTS_MAX+1];
8629
8630	if (!(sw->osm_switch && sw->osm_switch->priv == sw)) {
8631		OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
8632			"ERR 4E3D: sw->osm_switch->priv != sw "
8633			"for sw 0x%04"PRIx64"\n", cl_ntoh64(sw->n_id));
8634		return false;
8635	}
8636	osm_sw = sw->osm_switch;
8637	memset(osm_sw->new_lft, OSM_NO_PATH, osm_sw->lft_size);
8638
8639	for (s = 0; s < t->switch_cnt; s++) {
8640
8641		dsw = t->sw_pool[s];
8642		pgrp = &dsw->ptgrp[2 * TORUS_MAX_DIM];
8643
8644		memset(order, IB_INVALID_PORT_NUM, sizeof(order));
8645		for (p = 0; p < pgrp->port_cnt; p++)
8646			order[pgrp->port[p]->port] = p;
8647
8648		for (p = 0; p < ARRAY_SIZE(order); p++) {
8649
8650			uint8_t px = order[t->port_order[p]];
8651
8652			if (px == IB_INVALID_PORT_NUM)
8653				continue;
8654
8655			if (!get_lid(pgrp, px, &dlid_base, &dlid_lmc, &ca))
8656				return false;
8657
8658			if (sw->n_id == dsw->n_id)
8659				dp = pgrp->port[px]->port;
8660			else
8661				dp = lft_port(t, sw, dsw, true, ca);
8662			/*
8663			 * LMC > 0 doesn't really make sense for torus-2QoS.
8664			 * So, just make sure traffic gets delivered if
8665			 * non-zero LMC is used.
8666			 */
8667			if (dp >= 0)
8668				for (l = 0; l < (1U << dlid_lmc); l++)
8669					osm_sw->new_lft[dlid_base + l] = dp;
8670			else
8671				success = false;
8672		}
8673	}
8674	return success;
8675}
8676
8677static
8678osm_mtree_node_t *mcast_stree_branch(struct t_switch *sw, osm_switch_t *osm_sw,
8679				     osm_mgrp_box_t *mgb, unsigned depth,
8680				     unsigned *port_cnt, unsigned *max_depth)
8681{
8682	osm_mtree_node_t *mtn = NULL;
8683	osm_mcast_tbl_t *mcast_tbl, *ds_mcast_tbl;
8684	osm_node_t *ds_node;
8685	struct t_switch *ds_sw;
8686	struct port_grp *ptgrp;
8687	struct link *link;
8688	struct endpoint *port;
8689	unsigned g, p;
8690	unsigned mcast_fwd_ports = 0, mcast_end_ports = 0;
8691
8692	depth++;
8693
8694	if (osm_sw->priv != sw) {
8695		OSM_LOG(&sw->torus->osm->log, OSM_LOG_ERROR,
8696			"ERR 4E3E: osm_sw (GUID 0x%04"PRIx64") "
8697			"not in torus fabric description\n",
8698			cl_ntoh64(osm_node_get_node_guid(osm_sw->p_node)));
8699		goto out;
8700	}
8701	if (!osm_switch_supports_mcast(osm_sw)) {
8702		OSM_LOG(&sw->torus->osm->log, OSM_LOG_ERROR,
8703			"ERR 4E3F: osm_sw (GUID 0x%04"PRIx64") "
8704			"does not support multicast\n",
8705			cl_ntoh64(osm_node_get_node_guid(osm_sw->p_node)));
8706		goto out;
8707	}
8708	mtn = osm_mtree_node_new(osm_sw);
8709	if (!mtn) {
8710		OSM_LOG(&sw->torus->osm->log, OSM_LOG_ERROR,
8711			"ERR 4E46: Insufficient memory to build multicast tree\n");
8712		goto out;
8713	}
8714	mcast_tbl = osm_switch_get_mcast_tbl_ptr(osm_sw);
8715	/*
8716	 * Recurse to downstream switches, i.e. those closer to master
8717	 * spanning tree branch tips.
8718	 *
8719	 * Note that if there are multiple ports in this port group, i.e.,
8720	 * multiple parallel links, we can pick any one of them to use for
8721	 * any individual MLID without causing loops.  Pick one based on MLID
8722	 * for now, until someone turns up evidence we need to be smarter.
8723	 *
8724	 * Also, it might be we got called in a window between a switch getting
8725	 * removed from the fabric, and torus-2QoS getting to rebuild its
8726	 * fabric representation.  If that were to happen, our next hop
8727	 * osm_switch pointer might be stale.  Look it up via opensm's fabric
8728	 * description to be sure it's not.
8729	 */
8730	for (g = 0; g < 2 * TORUS_MAX_DIM; g++) {
8731		ptgrp = &sw->ptgrp[g];
8732		if (!ptgrp->to_stree_tip)
8733			continue;
8734
8735		p = mgb->mlid % ptgrp->port_cnt;/* port # in port group */
8736		p = ptgrp->port[p]->port;	/* now port # in switch */
8737
8738		ds_node = osm_node_get_remote_node(osm_sw->p_node, p, NULL);
8739		ds_sw = ptgrp->to_stree_tip->sw;
8740
8741		if (!(ds_node && ds_node->sw &&
8742		      ds_sw->osm_switch == ds_node->sw)) {
8743			OSM_LOG(&sw->torus->osm->log, OSM_LOG_ERROR,
8744				"ERR 4E40: stale pointer to osm_sw "
8745				"(GUID 0x%04"PRIx64")\n", cl_ntoh64(ds_sw->n_id));
8746			continue;
8747		}
8748		mtn->child_array[p] =
8749			mcast_stree_branch(ds_sw, ds_node->sw, mgb,
8750					   depth, port_cnt, max_depth);
8751		if (!mtn->child_array[p])
8752			continue;
8753
8754		osm_mcast_tbl_set(mcast_tbl, mgb->mlid, p);
8755		mcast_fwd_ports++;
8756		/*
8757		 * Since we forward traffic for this multicast group on this
8758		 * port, cause the switch on the other end of the link
8759		 * to forward traffic back to us.  Do it now since have at
8760		 * hand the link used; otherwise it'll be hard to figure out
8761		 * later, and if we get it wrong we get a MC routing loop.
8762		 */
8763		link = sw->port[p]->link;
8764		ds_mcast_tbl = osm_switch_get_mcast_tbl_ptr(ds_node->sw);
8765
8766		if (&link->end[0] == sw->port[p])
8767			osm_mcast_tbl_set(ds_mcast_tbl, mgb->mlid,
8768					  link->end[1].port);
8769		else
8770			osm_mcast_tbl_set(ds_mcast_tbl, mgb->mlid,
8771					  link->end[0].port);
8772	}
8773	/*
8774	 * Add any host ports marked as in mcast group into spanning tree.
8775	 */
8776	ptgrp = &sw->ptgrp[2 * TORUS_MAX_DIM];
8777	for (p = 0; p < ptgrp->port_cnt; p++) {
8778		port = ptgrp->port[p];
8779		if (port->tmp) {
8780			port->tmp = NULL;
8781			mtn->child_array[port->port] = OSM_MTREE_LEAF;
8782			osm_mcast_tbl_set(mcast_tbl, mgb->mlid, port->port);
8783			mcast_end_ports++;
8784		}
8785	}
8786	if (!(mcast_end_ports || mcast_fwd_ports)) {
8787		osm_mtree_destroy(mtn);
8788		mtn = NULL;
8789	} else if (depth > *max_depth)
8790		*max_depth = depth;
8791
8792	*port_cnt += mcast_end_ports;
8793out:
8794	return mtn;
8795}
8796
8797static
8798osm_port_t *next_mgrp_box_port(osm_mgrp_box_t *mgb,
8799			       cl_list_item_t **list_iterator,
8800			       cl_map_item_t **map_iterator)
8801{
8802	osm_mgrp_t *mgrp;
8803	osm_mcm_port_t *mcm_port;
8804	osm_port_t *osm_port = NULL;
8805	cl_map_item_t *m_item = *map_iterator;
8806	cl_list_item_t *l_item = *list_iterator;
8807
8808next_mgrp:
8809	if (!l_item)
8810		l_item = cl_qlist_head(&mgb->mgrp_list);
8811	if (l_item == cl_qlist_end(&mgb->mgrp_list)) {
8812		l_item = NULL;
8813		goto out;
8814	}
8815	mgrp = cl_item_obj(l_item, mgrp, list_item);
8816
8817	if (!m_item)
8818		m_item = cl_qmap_head(&mgrp->mcm_port_tbl);
8819	if (m_item == cl_qmap_end(&mgrp->mcm_port_tbl)) {
8820		m_item = NULL;
8821		l_item = cl_qlist_next(l_item);
8822		goto next_mgrp;
8823	}
8824	mcm_port = cl_item_obj(m_item, mcm_port, map_item);
8825	m_item = cl_qmap_next(m_item);
8826	osm_port = mcm_port->port;
8827out:
8828	*list_iterator = l_item;
8829	*map_iterator = m_item;
8830	return osm_port;
8831}
8832
8833static
8834ib_api_status_t torus_mcast_stree(void *context, osm_mgrp_box_t *mgb)
8835{
8836	struct torus_context *ctx = context;
8837	struct torus *t = ctx->torus;
8838	cl_map_item_t *m_item = NULL;
8839	cl_list_item_t *l_item = NULL;
8840	osm_port_t *osm_port;
8841	osm_switch_t *osm_sw;
8842	struct endpoint *port;
8843	unsigned port_cnt = 0, max_depth = 0;
8844
8845	osm_purge_mtree(&ctx->osm->sm, mgb);
8846
8847	/*
8848	 * Build a spanning tree for a multicast group by first marking
8849	 * the torus endpoints that are participating in the group.
8850	 * Then do a depth-first search of the torus master spanning
8851	 * tree to build up the spanning tree specific to this group.
8852	 *
8853	 * Since the torus master spanning tree is constructed specifically
8854	 * to guarantee that multicast will not deadlock against unicast
8855	 * when they share VLs, we can be sure that any multicast group
8856	 * spanning tree constructed this way has the same property.
8857	 */
8858	while ((osm_port = next_mgrp_box_port(mgb, &l_item, &m_item))) {
8859		port = osm_port->priv;
8860		if (!(port && port->osm_port == osm_port)) {
8861			port = osm_port_relink_endpoint(osm_port);
8862			if (!port) {
8863				guid_t id;
8864				id = osm_node_get_node_guid(osm_port->p_node);
8865				OSM_LOG(&ctx->osm->log, OSM_LOG_ERROR,
8866					"ERR 4E41: osm_port (GUID 0x%04"PRIx64") "
8867					"not in torus fabric description\n",
8868					cl_ntoh64(id));
8869				continue;
8870			}
8871		}
8872		/*
8873		 * If this is a CA port, mark the switch port at the
8874		 * other end of this port's link.
8875		 *
8876		 * By definition, a CA port is connected to end[1] of a link,
8877		 * and the switch port is end[0].  See build_ca_link() and
8878		 * link_srcsink().
8879		 */
8880		if (port->link)
8881			port = &port->link->end[0];
8882		port->tmp = osm_port;
8883	}
8884	/*
8885	 * It might be we got called in a window between a switch getting
8886	 * removed from the fabric, and torus-2QoS getting to rebuild its
8887	 * fabric representation.  If that were to happen, our
8888	 * master_stree_root->osm_switch pointer might be stale.  Look up
8889	 * the osm_switch by GUID to be sure it's not.
8890	 *
8891	 * Also, call into mcast_stree_branch with depth = -1, because
8892	 * depth at root switch needs to be 0.
8893	 */
8894	osm_sw = (osm_switch_t *)cl_qmap_get(&ctx->osm->subn.sw_guid_tbl,
8895					     t->master_stree_root->n_id);
8896	if (!(osm_sw && t->master_stree_root->osm_switch == osm_sw)) {
8897		OSM_LOG(&ctx->osm->log, OSM_LOG_ERROR,
8898			"ERR 4E42: stale pointer to osm_sw (GUID 0x%04"PRIx64")\n",
8899			cl_ntoh64(t->master_stree_root->n_id));
8900		return IB_ERROR;
8901	}
8902	mgb->root = mcast_stree_branch(t->master_stree_root, osm_sw,
8903				       mgb, -1, &port_cnt, &max_depth);
8904
8905	OSM_LOG(&ctx->osm->log, OSM_LOG_VERBOSE,
8906		"Configured MLID 0x%X for %u ports, max tree depth = %u\n",
8907		mgb->mlid, port_cnt, max_depth);
8908
8909	return IB_SUCCESS;
8910}
8911
8912static
8913bool good_xy_ring(struct torus *t, const int x, const int y, const int z)
8914{
8915	struct t_switch ****sw = t->sw;
8916	bool good_ring = true;
8917	int x_tst, y_tst;
8918
8919	for (x_tst = 0; x_tst < t->x_sz && good_ring; x_tst++)
8920		good_ring = sw[x_tst][y][z];
8921
8922	for (y_tst = 0; y_tst < t->y_sz && good_ring; y_tst++)
8923		good_ring = sw[x][y_tst][z];
8924
8925	return good_ring;
8926}
8927
8928static
8929struct t_switch *find_plane_mid(struct torus *t, const int z)
8930{
8931	int x, dx, xm = t->x_sz / 2;
8932	int y, dy, ym = t->y_sz / 2;
8933	struct t_switch ****sw = t->sw;
8934
8935	if (good_xy_ring(t, xm, ym, z))
8936		return sw[xm][ym][z];
8937
8938	for (dx = 1, dy = 1; dx <= xm && dy <= ym; dx++, dy++) {
8939
8940		x = canonicalize(xm - dx, t->x_sz);
8941		y = canonicalize(ym - dy, t->y_sz);
8942		if (good_xy_ring(t, x, y, z))
8943			return sw[x][y][z];
8944
8945		x = canonicalize(xm + dx, t->x_sz);
8946		y = canonicalize(ym + dy, t->y_sz);
8947		if (good_xy_ring(t, x, y, z))
8948			return sw[x][y][z];
8949	}
8950	return NULL;
8951}
8952
8953static
8954struct t_switch *find_stree_root(struct torus *t)
8955{
8956	int x, y, z, dz, zm = t->z_sz / 2;
8957	struct t_switch ****sw = t->sw;
8958	struct t_switch *root;
8959	bool good_plane;
8960
8961	/*
8962	 * Look for a switch near the "center" (wrt. the datelines) of the
8963	 * torus, as that will be the most optimum spanning tree root.  Use
8964	 * a search that is not exhaustive, on the theory that this routing
8965	 * engine isn't useful anyway if too many switches are missing.
8966	 *
8967	 * Also, want to pick an x-y plane with no missing switches, so that
8968	 * the master spanning tree construction algorithm doesn't have to
8969	 * deal with needing a turn on a missing switch.
8970	 */
8971	for (dz = 0; dz <= zm; dz++) {
8972
8973		z = canonicalize(zm - dz, t->z_sz);
8974		good_plane = true;
8975		for (y = 0; y < t->y_sz && good_plane; y++)
8976			for (x = 0; x < t->x_sz && good_plane; x++)
8977				good_plane = sw[x][y][z];
8978
8979		if (good_plane) {
8980			root = find_plane_mid(t, z);
8981			if (root)
8982				goto out;
8983		}
8984		if (!dz)
8985			continue;
8986
8987		z = canonicalize(zm + dz, t->z_sz);
8988		good_plane = true;
8989		for (y = 0; y < t->y_sz && good_plane; y++)
8990			for (x = 0; x < t->x_sz && good_plane; x++)
8991				good_plane = sw[x][y][z];
8992
8993		if (good_plane) {
8994			root = find_plane_mid(t, z);
8995			if (root)
8996				goto out;
8997		}
8998	}
8999	/*
9000	 * Note that torus-2QoS can route a torus that is missing an entire
9001	 * column (switches with x,y constant, for all z values) without
9002	 * deadlocks.
9003	 *
9004	 * if we've reached this point, we must have a column of missing
9005	 * switches, as routable_torus() would have returned false for
9006	 * any other configuration of missing switches that made it through
9007	 * the above.
9008	 *
9009	 * So any switch in the mid-z plane will do as the root.
9010	 */
9011	root = find_plane_mid(t, zm);
9012out:
9013	return root;
9014}
9015
9016static
9017bool sw_in_master_stree(struct t_switch *sw)
9018{
9019	int g;
9020	bool connected;
9021
9022	connected = sw == sw->torus->master_stree_root;
9023	for (g = 0; g < 2 * TORUS_MAX_DIM; g++)
9024		connected = connected || sw->ptgrp[g].to_stree_root;
9025
9026	return connected;
9027}
9028
9029static
9030void grow_master_stree_branch(struct t_switch *root, struct t_switch *tip,
9031			      unsigned to_root_pg, unsigned to_tip_pg)
9032{
9033	root->ptgrp[to_tip_pg].to_stree_tip = &tip->ptgrp[to_root_pg];
9034	tip->ptgrp[to_root_pg].to_stree_root = &root->ptgrp[to_tip_pg];
9035}
9036
9037static
9038void build_master_stree_branch(struct t_switch *branch_root, int cdir)
9039{
9040	struct t_switch *sw, *n_sw, *p_sw;
9041	unsigned l, idx, cnt, pg, ng;
9042
9043	switch (cdir) {
9044	case 0:
9045		idx = branch_root->i;
9046		cnt = branch_root->torus->x_sz;
9047		break;
9048	case 1:
9049		idx = branch_root->j;
9050		cnt = branch_root->torus->y_sz;
9051		break;
9052	case 2:
9053		idx = branch_root->k;
9054		cnt = branch_root->torus->z_sz;
9055		break;
9056	default:
9057		goto out;
9058	}
9059	/*
9060	 * This algorithm intends that a spanning tree branch never crosses
9061	 * a dateline unless the 1-D ring for which we're building the branch
9062	 * is interrupted by failure.  We need that guarantee to prevent
9063	 * multicast/unicast credit loops.
9064	 */
9065	n_sw = branch_root;		/* tip of negative cdir branch */
9066	ng = 2 * cdir;			/* negative cdir port group index */
9067	p_sw = branch_root;		/* tip of positive cdir branch */
9068	pg = 2 * cdir + 1;		/* positive cdir port group index */
9069
9070	for (l = idx; n_sw && l >= 1; l--) {
9071		sw = ring_next_sw(n_sw, cdir, -1);
9072		if (sw && !sw_in_master_stree(sw)) {
9073			grow_master_stree_branch(n_sw, sw, pg, ng);
9074			n_sw = sw;
9075		} else
9076			n_sw = NULL;
9077	}
9078	for (l = idx; p_sw && l < (cnt - 1); l++) {
9079		sw = ring_next_sw(p_sw, cdir, 1);
9080		if (sw && !sw_in_master_stree(sw)) {
9081			grow_master_stree_branch(p_sw, sw, ng, pg);
9082			p_sw = sw;
9083		} else
9084			p_sw = NULL;
9085	}
9086	if (n_sw && p_sw)
9087		goto out;
9088	/*
9089	 * At least one branch couldn't grow to the dateline for this ring.
9090	 * That means it is acceptable to grow the branch by crossing the
9091	 * dateline.
9092	 */
9093	for (l = 0; l < cnt; l++) {
9094		if (n_sw) {
9095			sw = ring_next_sw(n_sw, cdir, -1);
9096			if (sw && !sw_in_master_stree(sw)) {
9097				grow_master_stree_branch(n_sw, sw, pg, ng);
9098				n_sw = sw;
9099			} else
9100				n_sw = NULL;
9101		}
9102		if (p_sw) {
9103			sw = ring_next_sw(p_sw, cdir, 1);
9104			if (sw && !sw_in_master_stree(sw)) {
9105				grow_master_stree_branch(p_sw, sw, ng, pg);
9106				p_sw = sw;
9107			} else
9108				p_sw = NULL;
9109		}
9110		if (!(n_sw || p_sw))
9111			break;
9112	}
9113out:
9114	return;
9115}
9116
9117static
9118bool torus_master_stree(struct torus *t)
9119{
9120	int i, j, k;
9121	bool success = false;
9122	struct t_switch *stree_root = find_stree_root(t);
9123
9124	if (stree_root)
9125		build_master_stree_branch(stree_root, 0);
9126	else
9127		goto out;
9128
9129	k = stree_root->k;
9130	for (i = 0; i < t->x_sz; i++) {
9131		j = stree_root->j;
9132		if (t->sw[i][j][k])
9133			build_master_stree_branch(t->sw[i][j][k], 1);
9134
9135		for (j = 0; j < t->y_sz; j++)
9136			if (t->sw[i][j][k])
9137				build_master_stree_branch(t->sw[i][j][k], 2);
9138	}
9139	t->master_stree_root = stree_root;
9140	/*
9141	 * At this point we should have a master spanning tree that contains
9142	 * every present switch, for all fabrics that torus-2QoS can route
9143	 * without deadlocks.  Make sure this is the case; otherwise warn
9144	 * and return failure so we get bug reports.
9145	 */
9146	success = true;
9147	for (i = 0; i < t->x_sz; i++)
9148		for (j = 0; j < t->y_sz; j++)
9149			for (k = 0; k < t->z_sz; k++) {
9150				struct t_switch *sw = t->sw[i][j][k];
9151				if (!sw || sw_in_master_stree(sw))
9152					continue;
9153
9154				success = false;
9155				OSM_LOG(&t->osm->log, OSM_LOG_ERROR,
9156					"ERR 4E43: sw 0x%04"PRIx64" (%d,%d,%d) not in "
9157					"torus multicast master spanning tree\n",
9158					cl_ntoh64(sw->n_id), i, j, k);
9159			}
9160out:
9161	return success;
9162}
9163
9164int route_torus(struct torus *t)
9165{
9166	int s;
9167	bool success = true;
9168
9169	for (s = 0; s < (int)t->switch_cnt; s++)
9170		success = torus_lft(t, t->sw_pool[s]) && success;
9171
9172	success = success && torus_master_stree(t);
9173
9174	return success ? 0 : -1;
9175}
9176
9177uint8_t torus_path_sl(void *context, uint8_t path_sl_hint,
9178		      const ib_net16_t slid, const ib_net16_t dlid)
9179{
9180	struct torus_context *ctx = context;
9181	osm_opensm_t *p_osm = ctx->osm;
9182	osm_log_t *log = &p_osm->log;
9183	osm_port_t *osm_sport, *osm_dport;
9184	struct endpoint *sport, *dport;
9185	struct t_switch *ssw, *dsw;
9186	struct torus *t;
9187	guid_t guid;
9188	unsigned sl = 0;
9189
9190	osm_sport = osm_get_port_by_lid(&p_osm->subn, slid);
9191	if (!osm_sport)
9192		goto out;
9193
9194	osm_dport = osm_get_port_by_lid(&p_osm->subn, dlid);
9195	if (!osm_dport)
9196		goto out;
9197
9198	sport = osm_sport->priv;
9199	if (!(sport && sport->osm_port == osm_sport)) {
9200		sport = osm_port_relink_endpoint(osm_sport);
9201		if (!sport) {
9202			guid = osm_node_get_node_guid(osm_sport->p_node);
9203			OSM_LOG(log, OSM_LOG_INFO,
9204				"Note: osm_sport (GUID 0x%04"PRIx64") "
9205				"not in torus fabric description\n",
9206				cl_ntoh64(guid));
9207			goto out;
9208		}
9209	}
9210	dport = osm_dport->priv;
9211	if (!(dport && dport->osm_port == osm_dport)) {
9212		dport = osm_port_relink_endpoint(osm_dport);
9213		if (!dport) {
9214			guid = osm_node_get_node_guid(osm_dport->p_node);
9215			OSM_LOG(log, OSM_LOG_INFO,
9216				"Note: osm_dport (GUID 0x%04"PRIx64") "
9217				"not in torus fabric description\n",
9218				cl_ntoh64(guid));
9219			goto out;
9220		}
9221	}
9222	/*
9223	 * We're only supposed to be called for CA ports, and maybe
9224	 * switch management ports.
9225	 */
9226	if (sport->type != SRCSINK) {
9227		guid = osm_node_get_node_guid(osm_sport->p_node);
9228		OSM_LOG(log, OSM_LOG_INFO,
9229			"Error: osm_sport (GUID 0x%04"PRIx64") "
9230			"not a data src/sink port\n", cl_ntoh64(guid));
9231		goto out;
9232	}
9233	if (dport->type != SRCSINK) {
9234		guid = osm_node_get_node_guid(osm_dport->p_node);
9235		OSM_LOG(log, OSM_LOG_INFO,
9236			"Error: osm_dport (GUID 0x%04"PRIx64") "
9237			"not a data src/sink port\n", cl_ntoh64(guid));
9238		goto out;
9239	}
9240	/*
9241	 * By definition, a CA port is connected to end[1] of a link, and
9242	 * the switch port is end[0].  See build_ca_link() and link_srcsink().
9243	 */
9244	if (sport->link) {
9245		ssw = sport->link->end[0].sw;
9246	} else {
9247		ssw = sport->sw;
9248	}
9249	if (dport->link)
9250		dsw = dport->link->end[0].sw;
9251	else
9252		dsw = dport->sw;
9253
9254	t = ssw->torus;
9255
9256	sl  = sl_set_use_loop_vl(use_vl1(ssw->i, dsw->i, t->x_sz), 0);
9257	sl |= sl_set_use_loop_vl(use_vl1(ssw->j, dsw->j, t->y_sz), 1);
9258	sl |= sl_set_use_loop_vl(use_vl1(ssw->k, dsw->k, t->z_sz), 2);
9259	sl |= sl_set_qos(sl_get_qos(path_sl_hint));
9260out:
9261	return sl;
9262}
9263
9264static
9265void sum_vlarb_weights(const char *vlarb_str,
9266		       unsigned total_weight[IB_MAX_NUM_VLS])
9267{
9268	unsigned i = 0, v, vl = 0;
9269	char *end;
9270
9271	while (*vlarb_str && i++ < 2 * IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK) {
9272		v = strtoul(vlarb_str, &end, 0);
9273		if (*end)
9274			end++;
9275		vlarb_str = end;
9276		if (i & 0x1)
9277			vl = v & 0xf;
9278		else
9279			total_weight[vl] += v & 0xff;
9280	}
9281}
9282
9283static
9284int uniform_vlarb_weight_value(unsigned *weight, unsigned count)
9285{
9286	int i, v = weight[0];
9287
9288	for (i = 1; i < count; i++) {
9289		if (v != weight[i])
9290			return -1;
9291	}
9292	return v;
9293}
9294
9295static
9296void check_vlarb_config(const char *vlarb_str, bool is_default,
9297			const char *str, const char *pri, osm_log_t *log)
9298{
9299	unsigned total_weight[IB_MAX_NUM_VLS] = {0,};
9300
9301	sum_vlarb_weights(vlarb_str, total_weight);
9302	if (!(uniform_vlarb_weight_value(&total_weight[0], 4) >= 0 &&
9303	      uniform_vlarb_weight_value(&total_weight[4], 4) >= 0))
9304		OSM_LOG(log, OSM_LOG_INFO,
9305			"Warning: torus-2QoS requires same VLarb weights for "
9306			"VLs 0-3; also for VLs 4-7: not true for %s "
9307			"%s_vlarb_%s\n",
9308			(is_default ? "default" : "configured"), str, pri);
9309}
9310
9311/*
9312 * Use this to check the qos_config for switch external ports.
9313 */
9314static
9315void check_qos_swe_config(osm_qos_options_t *opt,
9316			  osm_qos_options_t *def, osm_log_t *log)
9317{
9318	const char *vlarb_str, *tstr;
9319	bool is_default;
9320	unsigned max_vls;
9321
9322	max_vls = def->max_vls;
9323	if (opt->max_vls > 0)
9324		max_vls = opt->max_vls;
9325
9326	if (max_vls > 0 && max_vls < 8)
9327		OSM_LOG(log, OSM_LOG_INFO,
9328			"Warning: full torus-2QoS functionality not available "
9329			"for configured %s_max_vls = %d\n",
9330			(opt->max_vls > 0 ? "qos_swe" : "qos"), opt->max_vls);
9331
9332	vlarb_str = opt->vlarb_high;
9333	is_default = false;
9334	tstr = "qos_swe";
9335	if (!vlarb_str) {
9336		vlarb_str = def->vlarb_high;
9337		tstr = "qos";
9338	}
9339	if (!vlarb_str) {
9340		vlarb_str = OSM_DEFAULT_QOS_VLARB_HIGH;
9341		is_default = true;
9342	}
9343	check_vlarb_config(vlarb_str, is_default, tstr, "high", log);
9344
9345	vlarb_str = opt->vlarb_low;
9346	is_default = false;
9347	tstr = "qos_swe";
9348	if (!vlarb_str) {
9349		vlarb_str = def->vlarb_low;
9350		tstr = "qos";
9351	}
9352	if (!vlarb_str) {
9353		vlarb_str = OSM_DEFAULT_QOS_VLARB_LOW;
9354		is_default = true;
9355	}
9356	check_vlarb_config(vlarb_str, is_default, tstr, "low", log);
9357
9358	if (opt->sl2vl)
9359		OSM_LOG(log, OSM_LOG_INFO,
9360			"Warning: torus-2QoS must override configured "
9361			"qos_swe_sl2vl to generate deadlock-free routes\n");
9362}
9363
9364static
9365void check_ep_vlarb_config(const char *vlarb_str,
9366			   bool is_default, bool is_specific,
9367			   const char *str, const char *pri, osm_log_t *log)
9368{
9369	unsigned i, total_weight[IB_MAX_NUM_VLS] = {0,};
9370	int val = 0;
9371
9372	sum_vlarb_weights(vlarb_str, total_weight);
9373	for (i = 2; i < 8; i++) {
9374		val += total_weight[i];
9375	}
9376	if (!val)
9377		return;
9378
9379	if (is_specific)
9380		OSM_LOG(log, OSM_LOG_INFO,
9381			"Warning: torus-2QoS recommends 0 VLarb weights"
9382			" for VLs 2-7 on endpoint links; not true for "
9383			" configured %s_vlarb_%s\n", str, pri);
9384	else
9385		OSM_LOG(log, OSM_LOG_INFO,
9386			"Warning: torus-2QoS recommends 0 VLarb weights "
9387			"for VLs 2-7 on endpoint links; not true for %s "
9388			"qos_vlarb_%s values used for %s_vlarb_%s\n",
9389			(is_default ? "default" : "configured"), pri, str, pri);
9390}
9391
9392/*
9393 * Use this to check the qos_config for endports
9394 */
9395static
9396void check_qos_ep_config(osm_qos_options_t *opt, osm_qos_options_t *def,
9397			 const char *str, osm_log_t *log)
9398{
9399	const char *vlarb_str;
9400	bool is_default, is_specific;
9401	unsigned max_vls;
9402
9403	max_vls = def->max_vls;
9404	if (opt->max_vls > 0)
9405		max_vls = opt->max_vls;
9406
9407	if (max_vls > 0 && max_vls < 2)
9408		OSM_LOG(log, OSM_LOG_INFO,
9409			"Warning: full torus-2QoS functionality not available "
9410			"for configured %s_max_vls = %d\n",
9411			(opt->max_vls > 0 ? str : "qos"), opt->max_vls);
9412
9413	vlarb_str = opt->vlarb_high;
9414	is_default = false;
9415	is_specific = true;
9416	if (!vlarb_str) {
9417		vlarb_str = def->vlarb_high;
9418		is_specific = false;
9419	}
9420	if (!vlarb_str) {
9421		vlarb_str = OSM_DEFAULT_QOS_VLARB_HIGH;
9422		is_default = true;
9423	}
9424	check_ep_vlarb_config(vlarb_str, is_default, is_specific,
9425			      str, "high", log);
9426
9427	vlarb_str = opt->vlarb_low;
9428	is_default = false;
9429	is_specific = true;
9430	if (!vlarb_str) {
9431		vlarb_str = def->vlarb_low;
9432		is_specific = false;
9433	}
9434	if (!vlarb_str) {
9435		vlarb_str = OSM_DEFAULT_QOS_VLARB_LOW;
9436		is_default = true;
9437	}
9438	check_ep_vlarb_config(vlarb_str, is_default, is_specific,
9439			      str, "low", log);
9440
9441	if (opt->sl2vl)
9442		OSM_LOG(log, OSM_LOG_INFO,
9443			"Warning: torus-2QoS must override configured "
9444			"%s_sl2vl to generate deadlock-free routes\n", str);
9445}
9446
9447static
9448int torus_build_lfts(void *context)
9449{
9450	int status = -1;
9451	struct torus_context *ctx = context;
9452	struct fabric *fabric;
9453	struct torus *torus;
9454
9455	if (!ctx->osm->subn.opt.qos) {
9456		OSM_LOG(&ctx->osm->log, OSM_LOG_ERROR,
9457			"ERR 4E44: Routing engine list contains torus-2QoS. "
9458			"Enable QoS for correct operation "
9459			"(-Q or 'qos TRUE' in opensm.conf).\n");
9460		return status;
9461	}
9462
9463	fabric = &ctx->fabric;
9464	teardown_fabric(fabric);
9465
9466	torus = calloc(1, sizeof(*torus));
9467	if (!torus) {
9468		OSM_LOG(&ctx->osm->log, OSM_LOG_ERROR,
9469			"ERR 4E45: allocating torus: %s\n", strerror(errno));
9470		goto out;
9471	}
9472	torus->osm = ctx->osm;
9473	fabric->osm = ctx->osm;
9474
9475	if (!parse_config(ctx->osm->subn.opt.torus_conf_file,
9476			  fabric, torus))
9477		goto out;
9478
9479	if (!capture_fabric(fabric))
9480		goto out;
9481
9482	OSM_LOG(&torus->osm->log, OSM_LOG_INFO,
9483		"Found fabric w/ %d links, %d switches, %d CA ports, "
9484		"minimum data VLs: endport %d, switchport %d\n",
9485		(int)fabric->link_cnt, (int)fabric->switch_cnt,
9486		(int)fabric->ca_cnt, (int)ctx->osm->subn.min_data_vls,
9487		(int)ctx->osm->subn.min_sw_data_vls);
9488
9489	if (!verify_setup(torus, fabric))
9490		goto out;
9491
9492	OSM_LOG(&torus->osm->log, OSM_LOG_INFO,
9493		"Looking for %d x %d x %d %s\n",
9494		(int)torus->x_sz, (int)torus->y_sz, (int)torus->z_sz,
9495		(ALL_MESH(torus->flags) ? "mesh" : "torus"));
9496
9497	if (!build_torus(fabric, torus)) {
9498		OSM_LOG(&torus->osm->log, OSM_LOG_ERROR, "ERR 4E57: "
9499			"build_torus finished with errors\n");
9500		goto out;
9501	}
9502
9503	OSM_LOG(&torus->osm->log, OSM_LOG_INFO,
9504		"Built %d x %d x %d %s w/ %d links, %d switches, %d CA ports\n",
9505		(int)torus->x_sz, (int)torus->y_sz, (int)torus->z_sz,
9506		(ALL_MESH(torus->flags) ? "mesh" : "torus"),
9507		(int)torus->link_cnt, (int)torus->switch_cnt,
9508		(int)torus->ca_cnt);
9509
9510	diagnose_fabric(fabric);
9511	/*
9512	 * Since we found some sort of torus fabric, report on any topology
9513	 * changes vs. the last torus we found.
9514	 */
9515	if (torus->flags & NOTIFY_CHANGES)
9516		report_torus_changes(torus, ctx->torus);
9517
9518	if (routable_torus(torus, fabric))
9519		status = route_torus(torus);
9520
9521out:
9522	if (status) {		/* bad torus!! */
9523		if (torus)
9524			teardown_torus(torus);
9525	} else {
9526		osm_subn_opt_t *opt = &torus->osm->subn.opt;
9527		osm_log_t *log = &torus->osm->log;
9528
9529		if (ctx->torus)
9530			teardown_torus(ctx->torus);
9531		ctx->torus = torus;
9532
9533		check_qos_swe_config(&opt->qos_swe_options, &opt->qos_options,
9534				     log);
9535
9536		check_qos_ep_config(&opt->qos_ca_options,
9537				    &opt->qos_options, "qos_ca", log);
9538		check_qos_ep_config(&opt->qos_sw0_options,
9539				    &opt->qos_options, "qos_sw0", log);
9540		check_qos_ep_config(&opt->qos_rtr_options,
9541				    &opt->qos_options, "qos_rtr", log);
9542	}
9543	teardown_fabric(fabric);
9544	return status;
9545}
9546
9547int osm_ucast_torus2QoS_setup(struct osm_routing_engine *r,
9548			      osm_opensm_t *osm)
9549{
9550	struct torus_context *ctx;
9551
9552	ctx = torus_context_create(osm);
9553	if (!ctx)
9554		return -1;
9555
9556	r->context = ctx;
9557	r->ucast_build_fwd_tables = torus_build_lfts;
9558	r->build_lid_matrices = ucast_dummy_build_lid_matrices;
9559	r->update_sl2vl = torus_update_osm_sl2vl;
9560	r->update_vlarb = torus_update_osm_vlarb;
9561	r->path_sl = torus_path_sl;
9562	r->mcast_build_stree = torus_mcast_stree;
9563	r->destroy = torus_context_delete;
9564	return 0;
9565}
9566