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
66 typedef 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 */
119 enum endpt_type { UNKNOWN = 0, SRCSINK, PASSTHRU };
120 struct torus;
121 struct t_switch;
122 struct port_grp;
123
124 struct 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
149 struct 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 */
173 struct 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 */
198 struct 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 */
229 struct 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
240 struct 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
253 struct 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
285 struct 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
326 struct torus_context {
327 osm_opensm_t *osm;
328 struct torus *torus;
329 struct fabric fabric;
330 };
331
332 static
teardown_fabric(struct fabric * f)333 void 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
teardown_torus(struct torus * t)371 void 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
411 static
torus_context_create(osm_opensm_t * osm)412 struct 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
426 static
torus_context_delete(void * context)427 void 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
437 static
grow_seed_array(struct torus * t,int new_seeds)438 bool 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
457 static
find_f_sw(struct fabric * f,guid_t sw_guid)458 struct 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
473 static
find_f_link(struct fabric * f,guid_t guid0,int port0,guid_t guid1,int port1)474 struct 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
497 static
alloc_fswitch(struct fabric * f,guid_t sw_id,unsigned port_cnt)498 struct 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;
531 out:
532 return sw;
533 }
534
535 static
alloc_flink(struct fabric * f)536 struct 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;
563 out:
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 */
571 static
build_sw_endpoint(struct fabric * f,osm_port_t * osm_port)572 bool 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
624 success:
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;
633 out:
634 return success;
635 }
636
637 static
build_ca_link(struct fabric * f,osm_port_t * osm_port_ca,guid_t sw_guid,int sw_port)638 bool 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;
694 out:
695 return success;
696 }
697
698 static
build_link(struct fabric * f,guid_t sw_guid0,int sw_port0,guid_t sw_guid1,int sw_port1)699 bool 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;
752 out:
753 return success;
754 }
755
756 static
parse_size(unsigned * tsz,unsigned * tflags,unsigned mask,const char * parse_sep)757 bool 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
784 static
parse_torus(struct torus * t,const char * parse_sep)785 bool 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;
848 out:
849 return success;
850 }
851
852 static
parse_unsigned(unsigned * result,const char * parse_sep)853 bool 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
864 static
parse_port_order(struct torus * t,const char * parse_sep)865 bool 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
897 static
parse_guid(struct torus * t,guid_t * guid,const char * parse_sep)898 bool 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;
910 out:
911 return success;
912 }
913
914 static
parse_dir_link(int c_dir,struct torus * t,const char * parse_sep)915 bool 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;
969 out:
970 return success;
971 }
972
973 static
parse_dir_dateline(int c_dir,struct torus * t,const char * parse_sep)974 bool 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;
1016 out:
1017 return success;
1018 }
1019
1020 static
parse_config(const char * fn,struct fabric * f,struct torus * t)1021 bool 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
1049 next_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
1126 out:
1127 if (line_buf)
1128 free(line_buf);
1129 fclose(fp);
1130 return success;
1131 }
1132
1133 static
capture_fabric(struct fabric * fabric)1134 bool 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 }
1269 out:
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 */
1281 static
diagnose_fabric(struct fabric * f)1282 void 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
1329 static
alloc_tswitch(struct torus * t,struct f_switch * fsw)1330 struct 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;
1374 out:
1375 return sw;
1376 }
1377
1378 /*
1379 * install_tswitch() expects the switch coordinates i,j,k to be canonicalized
1380 * by caller.
1381 */
1382 static
install_tswitch(struct torus * t,int i,int j,int k,struct f_switch * fsw)1383 bool 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
1399 static
alloc_tlink(struct torus * t)1400 struct 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
1411 static
canonicalize(int v,int vmax)1412 int 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
1423 static
set_fp_bit(bool present,int i,int j,int k)1424 unsigned 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 */
1438 static
fingerprint(struct torus * t,int i,int j,int k)1439 unsigned 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
1469 static
connect_tlink(struct port_grp * pg0,struct endpoint * f_ep0,struct port_grp * pg1,struct endpoint * f_ep1,struct torus * t)1470 bool 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;
1537 out:
1538 return success;
1539 }
1540
1541 static
link_tswitches(struct torus * t,int cdir,struct t_switch * t_sw0,struct t_switch * t_sw1)1542 bool 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;
1660 out:
1661 return success;
1662 }
1663
1664 static
link_srcsink(struct torus * t,int i,int j,int k)1665 bool 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;
1804 out:
1805 return success;
1806 }
1807
1808 static
ffind_face_corner(struct f_switch * fsw0,struct f_switch * fsw1,struct f_switch * fsw2)1809 struct 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;
1875 out:
1876 return fsw3;
1877 }
1878
1879 static
tfind_face_corner(struct t_switch * tsw0,struct t_switch * tsw1,struct t_switch * tsw2)1880 struct 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 */
ffind_3d_perpendicular(struct f_switch * fsw0,struct f_switch * fsw1,struct f_switch * fsw2,struct f_switch * fsw3)1910 struct 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;
1957 out:
1958 return fsw4;
1959 }
ffind_2d_perpendicular(struct f_switch * fsw0,struct f_switch * fsw1,struct f_switch * fsw2)1960 struct 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;
2005 out:
2006 return fsw3;
2007 }
2008
2009 static
tfind_3d_perpendicular(struct t_switch * tsw0,struct t_switch * tsw1,struct t_switch * tsw2,struct t_switch * tsw3)2010 struct 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
2022 static
tfind_2d_perpendicular(struct t_switch * tsw0,struct t_switch * tsw1,struct t_switch * tsw2)2023 struct 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
2033 static
safe_x_ring(struct torus * t,int i,int j,int k)2034 bool 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;
2085 out:
2086 return success;
2087 }
2088
2089 static
safe_y_ring(struct torus * t,int i,int j,int k)2090 bool 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;
2141 out:
2142 return success;
2143 }
2144
2145 static
safe_z_ring(struct torus * t,int i,int j,int k)2146 bool 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;
2197 out:
2198 return success;
2199 }
2200
2201 /*
2202 * These functions return true when it safe to call
2203 * tfind_3d_perpendicular()/ffind_3d_perpendicular().
2204 */
2205 static
safe_x_perpendicular(struct torus * t,int i,int j,int k)2206 bool 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
2220 static
safe_y_perpendicular(struct torus * t,int i,int j,int k)2221 bool 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
2235 static
safe_z_perpendicular(struct torus * t,int i,int j,int k)2236 bool 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
2312 static
log_no_crnr(struct torus * t,unsigned n,int case_i,int case_j,int case_k,int crnr_i,int crnr_j,int crnr_k)2313 void 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
2323 static
log_no_perp(struct torus * t,unsigned n,int case_i,int case_j,int case_k,int perp_i,int perp_j,int perp_k)2324 void 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 */
2358 static
handle_case_0x30c(struct torus * t,int i,int j,int k)2359 bool 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
2385 static
handle_case_0x530(struct torus * t,int i,int j,int k)2386 bool 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
2412 static
handle_case_0x650(struct torus * t,int i,int j,int k)2413 bool 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 */
2458 static
handle_case_0x305(struct torus * t,int i,int j,int k)2459 bool 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
2485 static
handle_case_0x511(struct torus * t,int i,int j,int k)2486 bool 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
2512 static
handle_case_0x611(struct torus * t,int i,int j,int k)2513 bool 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 */
2558 static
handle_case_0x303(struct torus * t,int i,int j,int k)2559 bool 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
2585 static
handle_case_0x503(struct torus * t,int i,int j,int k)2586 bool 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
2612 static
handle_case_0x605(struct torus * t,int i,int j,int k)2613 bool 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 */
2658 static
handle_case_0x30a(struct torus * t,int i,int j,int k)2659 bool 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
2685 static
handle_case_0x522(struct torus * t,int i,int j,int k)2686 bool 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
2712 static
handle_case_0x644(struct torus * t,int i,int j,int k)2713 bool 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 */
2763 static
handle_case_0x301(struct torus * t,int i,int j,int k)2764 bool 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
2779 static
handle_case_0x501(struct torus * t,int i,int j,int k)2780 bool 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
2795 static
handle_case_0x601(struct torus * t,int i,int j,int k)2796 bool 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 */
2830 static
handle_case_0x302(struct torus * t,int i,int j,int k)2831 bool 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
2846 static
handle_case_0x502(struct torus * t,int i,int j,int k)2847 bool 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
2862 static
handle_case_0x604(struct torus * t,int i,int j,int k)2863 bool 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 */
2898 static
handle_case_0x308(struct torus * t,int i,int j,int k)2899 bool 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
2914 static
handle_case_0x520(struct torus * t,int i,int j,int k)2915 bool 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
2930 static
handle_case_0x640(struct torus * t,int i,int j,int k)2931 bool 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 */
2965 static
handle_case_0x304(struct torus * t,int i,int j,int k)2966 bool 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
2981 static
handle_case_0x510(struct torus * t,int i,int j,int k)2982 bool 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
2997 static
handle_case_0x610(struct torus * t,int i,int j,int k)2998 bool 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 */
3037 static
handle_case_0x71f(struct torus * t,int i,int j,int k)3038 bool 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 */
3076 static
handle_case_0x72f(struct torus * t,int i,int j,int k)3077 bool 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 */
3115 static
handle_case_0x737(struct torus * t,int i,int j,int k)3116 bool 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 */
3154 static
handle_case_0x73b(struct torus * t,int i,int j,int k)3155 bool 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 */
3193 static
handle_case_0x74f(struct torus * t,int i,int j,int k)3194 bool 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 */
3232 static
handle_case_0x757(struct torus * t,int i,int j,int k)3233 bool 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 */
3271 static
handle_case_0x75d(struct torus * t,int i,int j,int k)3272 bool 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 */
3310 static
handle_case_0x773(struct torus * t,int i,int j,int k)3311 bool 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 */
3349 static
handle_case_0x775(struct torus * t,int i,int j,int k)3350 bool 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 */
3388 static
handle_case_0x78f(struct torus * t,int i,int j,int k)3389 bool 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 */
3427 static
handle_case_0x7ab(struct torus * t,int i,int j,int k)3428 bool 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 */
3466 static
handle_case_0x7ae(struct torus * t,int i,int j,int k)3467 bool 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 */
3505 static
handle_case_0x7b3(struct torus * t,int i,int j,int k)3506 bool 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 */
3544 static
handle_case_0x7ba(struct torus * t,int i,int j,int k)3545 bool 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 */
3583 static
handle_case_0x7cd(struct torus * t,int i,int j,int k)3584 bool 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 */
3622 static
handle_case_0x7ce(struct torus * t,int i,int j,int k)3623 bool 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 */
3661 static
handle_case_0x7d5(struct torus * t,int i,int j,int k)3662 bool 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 */
3700 static
handle_case_0x7dc(struct torus * t,int i,int j,int k)3701 bool 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 */
3739 static
handle_case_0x7ea(struct torus * t,int i,int j,int k)3740 bool 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 */
3778 static
handle_case_0x7ec(struct torus * t,int i,int j,int k)3779 bool 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 */
3817 static
handle_case_0x7f1(struct torus * t,int i,int j,int k)3818 bool 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 */
3856 static
handle_case_0x7f2(struct torus * t,int i,int j,int k)3857 bool 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 */
3895 static
handle_case_0x7f4(struct torus * t,int i,int j,int k)3896 bool 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 */
3934 static
handle_case_0x7f8(struct torus * t,int i,int j,int k)3935 bool 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 */
3977 static
handle_case_0x717(struct torus * t,int i,int j,int k)3978 bool 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 */
4029 static
handle_case_0x72b(struct torus * t,int i,int j,int k)4030 bool 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 */
4081 static
handle_case_0x74d(struct torus * t,int i,int j,int k)4082 bool 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 */
4133 static
handle_case_0x771(struct torus * t,int i,int j,int k)4134 bool 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 */
4185 static
handle_case_0x78e(struct torus * t,int i,int j,int k)4186 bool 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 */
4237 static
handle_case_0x7b2(struct torus * t,int i,int j,int k)4238 bool 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 */
4289 static
handle_case_0x7d4(struct torus * t,int i,int j,int k)4290 bool 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 */
4341 static
handle_case_0x7e8(struct torus * t,int i,int j,int k)4342 bool 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 */
4397 static
handle_case_0x70f(struct torus * t,int i,int j,int k)4398 bool 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 */
4431 static
handle_case_0x733(struct torus * t,int i,int j,int k)4432 bool 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 */
4465 static
handle_case_0x755(struct torus * t,int i,int j,int k)4466 bool 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 */
4499 static
handle_case_0x7aa(struct torus * t,int i,int j,int k)4500 bool 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 */
4533 static
handle_case_0x7cc(struct torus * t,int i,int j,int k)4534 bool 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 */
4567 static
handle_case_0x7f0(struct torus * t,int i,int j,int k)4568 bool 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 */
4606 static
handle_case_0x707(struct torus * t,int i,int j,int k)4607 bool 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 */
4650 static
handle_case_0x70b(struct torus * t,int i,int j,int k)4651 bool 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 */
4694 static
handle_case_0x70d(struct torus * t,int i,int j,int k)4695 bool 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 */
4738 static
handle_case_0x70e(struct torus * t,int i,int j,int k)4739 bool 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 */
4782 static
handle_case_0x713(struct torus * t,int i,int j,int k)4783 bool 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 */
4826 static
handle_case_0x715(struct torus * t,int i,int j,int k)4827 bool 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 */
4870 static
handle_case_0x723(struct torus * t,int i,int j,int k)4871 bool 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 */
4914 static
handle_case_0x72a(struct torus * t,int i,int j,int k)4915 bool 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 */
4958 static
handle_case_0x731(struct torus * t,int i,int j,int k)4959 bool 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 */
5002 static
handle_case_0x732(struct torus * t,int i,int j,int k)5003 bool 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 */
5046 static
handle_case_0x745(struct torus * t,int i,int j,int k)5047 bool 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 */
5090 static
handle_case_0x74c(struct torus * t,int i,int j,int k)5091 bool 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 */
5134 static
handle_case_0x751(struct torus * t,int i,int j,int k)5135 bool 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 */
5178 static
handle_case_0x754(struct torus * t,int i,int j,int k)5179 bool 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 */
5222 static
handle_case_0x770(struct torus * t,int i,int j,int k)5223 bool 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 */
5266 static
handle_case_0x78a(struct torus * t,int i,int j,int k)5267 bool 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 */
5310 static
handle_case_0x78c(struct torus * t,int i,int j,int k)5311 bool 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 */
5354 static
handle_case_0x7a2(struct torus * t,int i,int j,int k)5355 bool 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 */
5398 static
handle_case_0x7a8(struct torus * t,int i,int j,int k)5399 bool 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 */
5442 static
handle_case_0x7b0(struct torus * t,int i,int j,int k)5443 bool 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 */
5486 static
handle_case_0x7c4(struct torus * t,int i,int j,int k)5487 bool 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 */
5530 static
handle_case_0x7c8(struct torus * t,int i,int j,int k)5531 bool 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 */
5574 static
handle_case_0x7d0(struct torus * t,int i,int j,int k)5575 bool 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 */
5618 static
handle_case_0x7e0(struct torus * t,int i,int j,int k)5619 bool 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 */
5666 static
handle_case_0x703(struct torus * t,int i,int j,int k)5667 bool 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 */
5710 static
handle_case_0x705(struct torus * t,int i,int j,int k)5711 bool 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 */
5754 static
handle_case_0x70a(struct torus * t,int i,int j,int k)5755 bool 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 */
5798 static
handle_case_0x70c(struct torus * t,int i,int j,int k)5799 bool 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 */
5842 static
handle_case_0x711(struct torus * t,int i,int j,int k)5843 bool 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 */
5886 static
handle_case_0x722(struct torus * t,int i,int j,int k)5887 bool 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 */
5930 static
handle_case_0x730(struct torus * t,int i,int j,int k)5931 bool 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 */
5974 static
handle_case_0x744(struct torus * t,int i,int j,int k)5975 bool 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 */
6018 static
handle_case_0x750(struct torus * t,int i,int j,int k)6019 bool 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 */
6062 static
handle_case_0x788(struct torus * t,int i,int j,int k)6063 bool 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 */
6106 static
handle_case_0x7a0(struct torus * t,int i,int j,int k)6107 bool 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 */
6150 static
handle_case_0x7c0(struct torus * t,int i,int j,int k)6151 bool 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 */
6198 static
handle_case_0x701(struct torus * t,int i,int j,int k)6199 bool 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 */
6233 static
handle_case_0x702(struct torus * t,int i,int j,int k)6234 bool 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 */
6268 static
handle_case_0x704(struct torus * t,int i,int j,int k)6269 bool 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 */
6303 static
handle_case_0x708(struct torus * t,int i,int j,int k)6304 bool 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 */
6338 static
handle_case_0x710(struct torus * t,int i,int j,int k)6339 bool 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 */
6373 static
handle_case_0x720(struct torus * t,int i,int j,int k)6374 bool 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 */
6408 static
handle_case_0x740(struct torus * t,int i,int j,int k)6409 bool 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 */
6443 static
handle_case_0x780(struct torus * t,int i,int j,int k)6444 bool 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 */
6466 static
check_tlinks(struct torus * t,int i,int j,int k)6467 void 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
6518 static
locate_sw(struct torus * t,int i,int j,int k)6519 void 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 */
6559 again:
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 }
7002 out:
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
7010 static
verify_setup(struct torus * t,struct fabric * f)7011 bool 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 */
7079 again:
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;
7258 out:
7259 return success;
7260 }
7261
7262 static
build_torus(struct fabric * f,struct torus * t)7263 bool 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 */
7367 again:
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 }
7388 out:
7389 return success;
7390 }
7391
7392 /*
7393 * Returns a count of differences between old and new switches.
7394 */
7395 static
tsw_changes(struct t_switch * nsw,struct t_switch * osw)7396 unsigned 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 }
7499 out:
7500 return cnt;
7501 }
7502
7503 static
dump_torus(struct torus * t)7504 void 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
7534 static
report_torus_changes(struct torus * nt,struct torus * ot)7535 void 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
7595 static
rpt_torus_missing(struct torus * t,int i,int j,int k,struct t_switch * sw,int * missing_z)7596 void 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 */
7666 static
routable_torus(struct torus * t,struct fabric * f)7667 bool 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 */
7798 static
osm_port_relink_endpoint(const osm_port_t * osm_port)7799 struct 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
7866 relink_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;
7877 out:
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 */
7935 static inline
sl_set_use_loop_vl(bool use_loop_vl,unsigned coord_dir)7936 unsigned 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
7942 static inline
sl_set_qos(unsigned qos)7943 unsigned 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 */
7951 static inline
sl_get_use_loop_vl(unsigned sl,unsigned coord_dir)7952 bool 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
7958 static inline
sl_get_qos(unsigned sl)7959 unsigned 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 */
7989 static inline
vl_set_loop_vl(bool use_loop_vl)7990 unsigned vl_set_loop_vl(bool use_loop_vl)
7991 {
7992 return use_loop_vl;
7993 }
7994
7995 static inline
vl_set_qos_vl(unsigned qos)7996 unsigned vl_set_qos_vl(unsigned qos)
7997 {
7998 return (qos & 0x1) << 2;
7999 }
8000
8001 static inline
vl_set_ca_qos_vl(unsigned qos)8002 unsigned vl_set_ca_qos_vl(unsigned qos)
8003 {
8004 return qos & 0x1;
8005 }
8006
8007 static inline
vl_set_turn_vl(unsigned in_coord_dir,unsigned out_coord_dir)8008 unsigned 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
8020 static
sl2vl_entry(struct torus * t,struct t_switch * sw,int input_pt,int output_pt,unsigned sl)8021 unsigned 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
8056 static
torus_update_osm_sl2vl(void * context,osm_physp_t * osm_phys_port,uint8_t iport_num,uint8_t oport_num,ib_slvl_table_t * osm_oport_sl2vl)8057 void 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
8086 static
torus_update_osm_vlarb(void * context,osm_physp_t * osm_phys_port,uint8_t port_num,ib_vl_arb_table_t * block,unsigned block_length,unsigned block_num)8087 void 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 */
8163 static
get_pathlen(unsigned src,unsigned dst,unsigned count,unsigned * vl0_len,unsigned * vl1_len)8164 void 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 */
8186 static
ring_dir_idx(unsigned src,unsigned dst,unsigned count)8187 int 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 */
8209 static
use_vl1(unsigned src,unsigned dst,unsigned count)8210 bool 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 */
8226 static
ring_next_sw(struct t_switch * sw,unsigned cdir,int rdir)8227 struct 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 */
8258 static
ring_dir_path(struct torus * t,unsigned cdir,struct t_switch * ssw,struct t_switch * dsw)8259 int 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;
8295 out:
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 */
8309 static
next_hop_path(struct torus * t,unsigned cdir,struct t_switch * ssw,struct t_switch * dsw,unsigned * pt_grp)8310 bool 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 }
8346 out:
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 */
8356 static
next_hop_idx(struct torus * t,unsigned cdir,struct t_switch * ssw,struct t_switch * dsw,unsigned * pt_grp)8357 bool 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;
8392 out:
8393 return success;
8394 }
8395
8396 static
warn_on_routing(const char * msg,struct t_switch * sw,struct t_switch * dsw)8397 void 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
8407 static
next_hop_x(struct torus * t,struct t_switch * ssw,struct t_switch * dsw,unsigned * pt_grp)8408 bool 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
8426 static
next_hop_y(struct torus * t,struct t_switch * ssw,struct t_switch * dsw,unsigned * pt_grp)8427 bool 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
8445 static
next_hop_z(struct torus * t,struct t_switch * ssw,struct t_switch * dsw,unsigned * pt_grp)8446 bool 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 */
8456 static
lft_port(struct torus * t,struct t_switch * sw,struct t_switch * dsw,bool update_port_cnt,bool ca)8457 int 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 }
8534 done:
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
8556 no_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
8567 static
get_lid(struct port_grp * pg,unsigned p,uint16_t * dlid_base,uint8_t * dlid_lmc,bool * ca)8568 bool 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
8616 static
torus_lft(struct torus * t,struct t_switch * sw)8617 bool 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
8677 static
mcast_stree_branch(struct t_switch * sw,osm_switch_t * osm_sw,osm_mgrp_box_t * mgb,unsigned depth,unsigned * port_cnt,unsigned * max_depth)8678 osm_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;
8793 out:
8794 return mtn;
8795 }
8796
8797 static
next_mgrp_box_port(osm_mgrp_box_t * mgb,cl_list_item_t ** list_iterator,cl_map_item_t ** map_iterator)8798 osm_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
8808 next_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;
8827 out:
8828 *list_iterator = l_item;
8829 *map_iterator = m_item;
8830 return osm_port;
8831 }
8832
8833 static
torus_mcast_stree(void * context,osm_mgrp_box_t * mgb)8834 ib_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
8912 static
good_xy_ring(struct torus * t,const int x,const int y,const int z)8913 bool 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
8928 static
find_plane_mid(struct torus * t,const int z)8929 struct 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
8953 static
find_stree_root(struct torus * t)8954 struct 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);
9012 out:
9013 return root;
9014 }
9015
9016 static
sw_in_master_stree(struct t_switch * sw)9017 bool 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
9029 static
grow_master_stree_branch(struct t_switch * root,struct t_switch * tip,unsigned to_root_pg,unsigned to_tip_pg)9030 void 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
9037 static
build_master_stree_branch(struct t_switch * branch_root,int cdir)9038 void 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 }
9113 out:
9114 return;
9115 }
9116
9117 static
torus_master_stree(struct torus * t)9118 bool 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 }
9160 out:
9161 return success;
9162 }
9163
route_torus(struct torus * t)9164 int 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
torus_path_sl(void * context,uint8_t path_sl_hint,const ib_net16_t slid,const ib_net16_t dlid)9177 uint8_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));
9260 out:
9261 return sl;
9262 }
9263
9264 static
sum_vlarb_weights(const char * vlarb_str,unsigned total_weight[IB_MAX_NUM_VLS])9265 void 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
9283 static
uniform_vlarb_weight_value(unsigned * weight,unsigned count)9284 int 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
9295 static
check_vlarb_config(const char * vlarb_str,bool is_default,const char * str,const char * pri,osm_log_t * log)9296 void 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 */
9314 static
check_qos_swe_config(osm_qos_options_t * opt,osm_qos_options_t * def,osm_log_t * log)9315 void 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
9364 static
check_ep_vlarb_config(const char * vlarb_str,bool is_default,bool is_specific,const char * str,const char * pri,osm_log_t * log)9365 void 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 */
9395 static
check_qos_ep_config(osm_qos_options_t * opt,osm_qos_options_t * def,const char * str,osm_log_t * log)9396 void 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
9447 static
torus_build_lfts(void * context)9448 int 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
9521 out:
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
osm_ucast_torus2QoS_setup(struct osm_routing_engine * r,osm_opensm_t * osm)9547 int 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