]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ofed/management/opensm/opensm/osm_switch.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / ofed / management / opensm / opensm / osm_switch.c
1 /*
2  * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2008 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  *
6  * This software is available to you under a choice of one of two
7  * licenses.  You may choose to be licensed under the terms of the GNU
8  * General Public License (GPL) Version 2, available from the file
9  * COPYING in the main directory of this source tree, or the
10  * OpenIB.org BSD license below:
11  *
12  *     Redistribution and use in source and binary forms, with or
13  *     without modification, are permitted provided that the following
14  *     conditions are met:
15  *
16  *      - Redistributions of source code must retain the above
17  *        copyright notice, this list of conditions and the following
18  *        disclaimer.
19  *
20  *      - Redistributions in binary form must reproduce the above
21  *        copyright notice, this list of conditions and the following
22  *        disclaimer in the documentation and/or other materials
23  *        provided with the distribution.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32  * SOFTWARE.
33  *
34  */
35
36 /*
37  * Abstract:
38  *    Implementation of osm_switch_t.
39  * This object represents an Infiniband switch.
40  * This object is part of the opensm family of objects.
41  */
42
43 #if HAVE_CONFIG_H
44 #  include <config.h>
45 #endif                          /* HAVE_CONFIG_H */
46
47 #include <stdlib.h>
48 #include <string.h>
49 #include <complib/cl_math.h>
50 #include <iba/ib_types.h>
51 #include <opensm/osm_switch.h>
52
53 /**********************************************************************
54  **********************************************************************/
55 cl_status_t
56 osm_switch_set_hops(IN osm_switch_t * const p_sw,
57                     IN const uint16_t lid_ho,
58                     IN const uint8_t port_num, IN const uint8_t num_hops)
59 {
60         if (lid_ho > p_sw->max_lid_ho)
61                 return -1;
62         if (!p_sw->hops[lid_ho]) {
63                 p_sw->hops[lid_ho] = malloc(p_sw->num_ports);
64                 if (!p_sw->hops[lid_ho])
65                         return -1;
66                 memset(p_sw->hops[lid_ho], OSM_NO_PATH, p_sw->num_ports);
67         }
68
69         p_sw->hops[lid_ho][port_num] = num_hops;
70         if (p_sw->hops[lid_ho][0] > num_hops)
71                 p_sw->hops[lid_ho][0] = num_hops;
72
73         return 0;
74 }
75
76 /**********************************************************************
77  **********************************************************************/
78 static ib_api_status_t
79 osm_switch_init(IN osm_switch_t * const p_sw,
80                 IN osm_node_t * const p_node,
81                 IN const osm_madw_t * const p_madw)
82 {
83         ib_api_status_t status = IB_SUCCESS;
84         ib_switch_info_t *p_si;
85         ib_smp_t *p_smp;
86         uint8_t num_ports;
87         uint32_t port_num;
88
89         p_smp = osm_madw_get_smp_ptr(p_madw);
90         p_si = (ib_switch_info_t *) ib_smp_get_payload_ptr(p_smp);
91         num_ports = osm_node_get_num_physp(p_node);
92
93         CL_ASSERT(p_smp->attr_id == IB_MAD_ATTR_SWITCH_INFO);
94
95         p_sw->p_node = p_node;
96         p_sw->switch_info = *p_si;
97         p_sw->num_ports = num_ports;
98         p_sw->need_update = 2;
99
100         /* Initiate the linear forwarding table */
101
102         if (!p_si->lin_cap) {
103                 /* This switch does not support linear forwarding tables */
104                 status = IB_UNSUPPORTED;
105                 goto Exit;
106         }
107
108         p_sw->lft = malloc(IB_LID_UCAST_END_HO + 1);
109         if (!p_sw->lft) {
110                 status = IB_INSUFFICIENT_MEMORY;
111                 goto Exit;
112         }
113
114         /* Initialize the table to OSM_NO_PATH, which is "invalid port" */
115         memset(p_sw->lft, OSM_NO_PATH, IB_LID_UCAST_END_HO + 1);
116
117         p_sw->p_prof = malloc(sizeof(*p_sw->p_prof) * num_ports);
118         if (p_sw->p_prof == NULL) {
119                 status = IB_INSUFFICIENT_MEMORY;
120                 goto Exit;
121         }
122
123         memset(p_sw->p_prof, 0, sizeof(*p_sw->p_prof) * num_ports);
124
125         status = osm_mcast_tbl_init(&p_sw->mcast_tbl,
126                                     osm_node_get_num_physp(p_node),
127                                     cl_ntoh16(p_si->mcast_cap));
128         if (status != IB_SUCCESS)
129                 goto Exit;
130
131         for (port_num = 0; port_num < num_ports; port_num++)
132                 osm_port_prof_construct(&p_sw->p_prof[port_num]);
133
134 Exit:
135         return (status);
136 }
137
138 /**********************************************************************
139  **********************************************************************/
140 void osm_switch_delete(IN OUT osm_switch_t ** const pp_sw)
141 {
142         osm_switch_t *p_sw = *pp_sw;
143         unsigned i;
144
145         osm_mcast_tbl_destroy(&p_sw->mcast_tbl);
146         free(p_sw->p_prof);
147         if (p_sw->lft)
148                 free(p_sw->lft);
149         if (p_sw->new_lft)
150                 free(p_sw->new_lft);
151         if (p_sw->hops) {
152                 for (i = 0; i < p_sw->num_hops; i++)
153                         if (p_sw->hops[i])
154                                 free(p_sw->hops[i]);
155                 free(p_sw->hops);
156         }
157         free(*pp_sw);
158         *pp_sw = NULL;
159 }
160
161 /**********************************************************************
162  **********************************************************************/
163 osm_switch_t *osm_switch_new(IN osm_node_t * const p_node,
164                              IN const osm_madw_t * const p_madw)
165 {
166         ib_api_status_t status;
167         osm_switch_t *p_sw;
168
169         CL_ASSERT(p_madw);
170         CL_ASSERT(p_node);
171
172         p_sw = (osm_switch_t *) malloc(sizeof(*p_sw));
173         if (p_sw) {
174                 memset(p_sw, 0, sizeof(*p_sw));
175                 status = osm_switch_init(p_sw, p_node, p_madw);
176                 if (status != IB_SUCCESS)
177                         osm_switch_delete(&p_sw);
178         }
179
180         return (p_sw);
181 }
182
183 /**********************************************************************
184  **********************************************************************/
185 boolean_t
186 osm_switch_get_lft_block(IN const osm_switch_t * const p_sw,
187                          IN const uint16_t block_id,
188                          OUT uint8_t * const p_block)
189 {
190         uint16_t base_lid_ho = block_id * IB_SMP_DATA_SIZE;
191
192         CL_ASSERT(p_sw);
193         CL_ASSERT(p_block);
194
195         if (base_lid_ho > p_sw->max_lid_ho)
196                 return FALSE;
197
198         CL_ASSERT(base_lid_ho + IB_SMP_DATA_SIZE <= IB_LID_UCAST_END_HO);
199         memcpy(p_block, &(p_sw->lft[base_lid_ho]), IB_SMP_DATA_SIZE);
200         return TRUE;
201 }
202
203 /**********************************************************************
204  **********************************************************************/
205 static struct osm_remote_node *
206 osm_switch_find_guid_common(IN const osm_switch_t * const p_sw,
207                             IN struct osm_remote_guids_count *r,
208                             IN uint8_t port_num,
209                             IN int find_sys_guid,
210                             IN int find_node_guid)
211 {
212         struct osm_remote_node *p_remote_guid = NULL;
213         osm_physp_t *p_physp;
214         osm_physp_t *p_rem_physp;
215         osm_node_t *p_rem_node;
216         uint64_t sys_guid;
217         uint64_t node_guid;
218         int i;
219
220         CL_ASSERT(p_sw);
221
222         p_physp = osm_node_get_physp_ptr(p_sw->p_node, port_num);
223         p_rem_physp = osm_physp_get_remote(p_physp);
224         p_rem_node = osm_physp_get_node_ptr(p_rem_physp);
225         sys_guid = p_rem_node->node_info.sys_guid;
226         node_guid = p_rem_node->node_info.node_guid;
227
228         for (i = 0; i < r->count; i++) {
229                 if ((!find_sys_guid
230                      || r->guids[i].node->node_info.sys_guid == sys_guid)
231                     && (!find_node_guid
232                         || r->guids[i].node->node_info.node_guid == node_guid)) {
233                         p_remote_guid = &r->guids[i];
234                         break;
235                 }
236         }
237
238         return p_remote_guid;
239 }
240
241 static struct osm_remote_node *
242 osm_switch_find_sys_guid_count(IN const osm_switch_t * const p_sw,
243                                IN struct osm_remote_guids_count *r,
244                                IN uint8_t port_num)
245 {
246         return osm_switch_find_guid_common(p_sw, r, port_num, 1, 0);
247 }
248
249 static struct osm_remote_node *
250 osm_switch_find_node_guid_count(IN const osm_switch_t * const p_sw,
251                                 IN struct osm_remote_guids_count *r,
252                                 IN uint8_t port_num)
253 {
254         return osm_switch_find_guid_common(p_sw, r, port_num, 0, 1);
255 }
256
257 /**********************************************************************
258  **********************************************************************/
259 uint8_t
260 osm_switch_recommend_path(IN const osm_switch_t * const p_sw,
261                           IN osm_port_t * p_port,
262                           IN const uint16_t lid_ho,
263                           IN unsigned start_from,
264                           IN const boolean_t ignore_existing,
265                           IN const boolean_t dor)
266 {
267         /*
268            We support an enhanced LMC aware routing mode:
269            In the case of LMC > 0, we can track the remote side
270            system and node for all of the lids of the target
271            and try and avoid routing again through the same
272            system / node.
273
274            If this procedure is provided with the tracking array
275            and counter we can conduct this algorithm.
276          */
277         boolean_t routing_for_lmc = (p_port->priv != NULL);
278         uint16_t base_lid;
279         uint8_t hops;
280         uint8_t least_hops;
281         uint8_t port_num;
282         uint8_t num_ports;
283         uint32_t least_paths = 0xFFFFFFFF;
284         unsigned i;
285         /*
286            The follwing will track the least paths if the
287            route should go through a new system/node
288          */
289         uint32_t least_paths_other_sys = 0xFFFFFFFF;
290         uint32_t least_paths_other_nodes = 0xFFFFFFFF;
291         uint32_t least_forwarded_to = 0xFFFFFFFF;
292         uint32_t check_count;
293         uint8_t best_port = 0;
294         /*
295            These vars track the best port if it connects to
296            not used system/node.
297          */
298         uint8_t best_port_other_sys = 0;
299         uint8_t best_port_other_node = 0;
300         boolean_t port_found = FALSE;
301         osm_physp_t *p_physp;
302         osm_physp_t *p_rem_physp;
303         osm_node_t *p_rem_node;
304         osm_node_t *p_rem_node_first = NULL;
305         struct osm_remote_node *p_remote_guid = NULL;
306
307         CL_ASSERT(lid_ho > 0);
308
309         if (p_port->p_node->sw) {
310                 if (p_port->p_node->sw == p_sw)
311                         return 0;
312                 base_lid = osm_port_get_base_lid(p_port);
313         } else {
314                 p_physp = p_port->p_physp;
315                 if (!p_physp || !p_physp->p_remote_physp ||
316                     !p_physp->p_remote_physp->p_node->sw)
317                         return OSM_NO_PATH;
318
319                 if (p_physp->p_remote_physp->p_node->sw == p_sw)
320                         return p_physp->p_remote_physp->port_num;
321                 base_lid =
322                     osm_node_get_base_lid(p_physp->p_remote_physp->p_node, 0);
323         }
324         base_lid = cl_ntoh16(base_lid);
325
326         num_ports = p_sw->num_ports;
327
328         least_hops = osm_switch_get_least_hops(p_sw, base_lid);
329         if (least_hops == OSM_NO_PATH)
330                 return (OSM_NO_PATH);
331
332         /*
333            First, inquire with the forwarding table for an existing
334            route.  If one is found, honor it unless:
335            1. the ignore existing flag is set.
336            2. the physical port is not a valid one or not healthy
337            3. the physical port has a remote port (the link is up)
338            4. the port has min-hops to the target (avoid loops)
339          */
340         if (!ignore_existing) {
341                 port_num = osm_switch_get_port_by_lid(p_sw, lid_ho);
342
343                 if (port_num != OSM_NO_PATH) {
344                         CL_ASSERT(port_num < num_ports);
345
346                         p_physp =
347                             osm_node_get_physp_ptr(p_sw->p_node, port_num);
348                         /*
349                            Don't be too trusting of the current forwarding table!
350                            Verify that the port number is legal and that the
351                            LID is reachable through this port.
352                          */
353                         if (p_physp && osm_physp_is_healthy(p_physp) &&
354                             osm_physp_get_remote(p_physp)) {
355                                 hops =
356                                     osm_switch_get_hop_count(p_sw, base_lid,
357                                                              port_num);
358                                 /*
359                                    If we aren't using pre-defined user routes
360                                    function, then we need to make sure that the
361                                    current path is the minimum one. In case of
362                                    having such a user function - this check will
363                                    not be done, and the old routing will be used.
364                                    Note: This means that it is the user's job to
365                                    clean all data in the forwarding tables that
366                                    he wants to be overridden by the minimum
367                                    hop function.
368                                  */
369                                 if (hops == least_hops)
370                                         return (port_num);
371                         }
372                 }
373         }
374
375         /*
376            This algorithm selects a port based on a static load balanced
377            selection across equal hop-count ports.
378            There is lots of room for improved sophistication here,
379            possibly guided by user configuration info.
380          */
381
382         /*
383            OpenSM routing is "local" - not considering a full lid to lid
384            path. As such we can not guarantee a path will not loop if we
385            do not always follow least hops.
386            So we must abort if not least hops.
387          */
388
389         /* port number starts with one and num_ports is 1 + num phys ports */
390         for (i = start_from; i < start_from + num_ports; i++) {
391                 port_num = i%num_ports;
392                 if (!port_num ||
393                     osm_switch_get_hop_count(p_sw, base_lid, port_num) !=
394                     least_hops)
395                         continue;
396
397                 /* let us make sure it is not down or unhealthy */
398                 p_physp = osm_node_get_physp_ptr(p_sw->p_node, port_num);
399                 if (!p_physp || !osm_physp_is_healthy(p_physp) ||
400                     /*
401                        we require all - non sma ports to be linked
402                        to be routed through
403                      */
404                     !osm_physp_get_remote(p_physp))
405                         continue;
406
407                 /*
408                    We located a least-hop port, possibly one of many.
409                    For this port, check the running total count of
410                    the number of paths through this port.  Select
411                    the port routing the least number of paths.
412                  */
413                 check_count =
414                     osm_port_prof_path_count_get(&p_sw->p_prof[port_num]);
415
416                 /*
417                    Advanced LMC routing requires tracking of the
418                    best port by the node connected to the other side of
419                    it.
420                  */
421                 if (routing_for_lmc) {
422                         /* Is the sys guid already used ? */
423                         p_remote_guid = osm_switch_find_sys_guid_count(p_sw,
424                                                                        p_port->priv,
425                                                                        port_num);
426
427                         /* If not update the least hops for this case */
428                         if (!p_remote_guid) {
429                                 if (check_count < least_paths_other_sys) {
430                                         least_paths_other_sys = check_count;
431                                         best_port_other_sys = port_num;
432                                         least_forwarded_to = 0;
433                                 }
434                         } else {        /* same sys found - try node */
435                                 /* Else is the node guid already used ? */
436                                 p_remote_guid = osm_switch_find_node_guid_count(p_sw,
437                                                                                 p_port->priv,
438                                                                                 port_num);
439
440                                 /* If not update the least hops for this case */
441                                 if (!p_remote_guid
442                                     && check_count < least_paths_other_nodes) {
443                                         least_paths_other_nodes = check_count;
444                                         best_port_other_node = port_num;
445                                         least_forwarded_to = 0;
446                                 }
447                                 /* else prior sys and node guid already used */
448
449                         }       /* same sys found */
450                 }
451
452                 /* routing for LMC mode */
453                 /*
454                    the count is min but also lower then the max subscribed
455                  */
456                 if (check_count < least_paths) {
457                         if (dor) {
458                                 /* Get the Remote Node */
459                                 p_rem_physp = osm_physp_get_remote(p_physp);
460                                 p_rem_node =
461                                     osm_physp_get_node_ptr(p_rem_physp);
462                                 /* use the first dimension, but spread
463                                  * traffic out among the group of ports
464                                  * representing that dimension */
465                                 if (port_found) {
466                                         if (p_rem_node != p_rem_node_first)
467                                                 continue;
468                                 } else
469                                         p_rem_node_first = p_rem_node;
470                         }
471                         port_found = TRUE;
472                         best_port = port_num;
473                         least_paths = check_count;
474                         if (routing_for_lmc
475                             && p_remote_guid
476                             && p_remote_guid->forwarded_to < least_forwarded_to)
477                                 least_forwarded_to = p_remote_guid->forwarded_to;
478                 } else if (routing_for_lmc
479                            && p_remote_guid
480                            && check_count == least_paths
481                            && p_remote_guid->forwarded_to < least_forwarded_to) {
482                         least_forwarded_to = p_remote_guid->forwarded_to;
483                         best_port = port_num;
484                 }
485         }
486
487         if (port_found == FALSE)
488                 return (OSM_NO_PATH);
489
490         /*
491            if we are in enhanced routing mode and the best port is not
492            the local port 0
493          */
494         if (routing_for_lmc && best_port) {
495                 /* Select the least hop port of the non used sys first */
496                 if (best_port_other_sys)
497                         best_port = best_port_other_sys;
498                 else if (best_port_other_node)
499                         best_port = best_port_other_node;
500         }
501
502         return (best_port);
503 }
504
505 /**********************************************************************
506  **********************************************************************/
507 void osm_switch_clear_hops(IN osm_switch_t * p_sw)
508 {
509         unsigned i;
510
511         for (i = 0; i < p_sw->num_hops; i++)
512                 if (p_sw->hops[i])
513                         memset(p_sw->hops[i], OSM_NO_PATH, p_sw->num_ports);
514 }
515
516 /**********************************************************************
517  **********************************************************************/
518 int
519 osm_switch_prepare_path_rebuild(IN osm_switch_t * p_sw, IN uint16_t max_lids)
520 {
521         uint8_t **hops;
522         unsigned i;
523
524         for (i = 0; i < p_sw->num_ports; i++)
525                 osm_port_prof_construct(&p_sw->p_prof[i]);
526
527         osm_switch_clear_hops(p_sw);
528
529         if (!p_sw->new_lft &&
530             !(p_sw->new_lft = malloc(IB_LID_UCAST_END_HO + 1)))
531                 return IB_INSUFFICIENT_MEMORY;
532
533         memset(p_sw->new_lft, OSM_NO_PATH, IB_LID_UCAST_END_HO + 1);
534
535         if (!p_sw->hops) {
536                 hops = malloc((max_lids + 1) * sizeof(hops[0]));
537                 if (!hops)
538                         return -1;
539                 memset(hops, 0, (max_lids + 1) * sizeof(hops[0]));
540                 p_sw->hops = hops;
541                 p_sw->num_hops = max_lids + 1;
542         } else if (max_lids + 1 > p_sw->num_hops) {
543                 uint8_t **old_hops;
544
545                 hops = malloc((max_lids + 1) * sizeof(hops[0]));
546                 if (!hops)
547                         return -1;
548                 memcpy(hops, p_sw->hops, p_sw->num_hops * sizeof(hops[0]));
549                 memset(hops + p_sw->num_hops, 0,
550                        (max_lids + 1 - p_sw->num_hops) * sizeof(hops[0]));
551                 old_hops = p_sw->hops;
552                 p_sw->hops = hops;
553                 p_sw->num_hops = max_lids + 1;
554                 free(old_hops);
555         }
556         p_sw->max_lid_ho = max_lids;
557
558         return 0;
559 }
560
561 /**********************************************************************
562  **********************************************************************/
563 uint8_t
564 osm_switch_get_port_least_hops(IN const osm_switch_t * const p_sw,
565                                IN const osm_port_t * p_port)
566 {
567         uint16_t lid;
568
569         if (p_port->p_node->sw) {
570                 if (p_port->p_node->sw == p_sw)
571                         return 0;
572                 lid = osm_node_get_base_lid(p_port->p_node, 0);
573                 return osm_switch_get_least_hops(p_sw, cl_ntoh16(lid));
574         } else {
575                 osm_physp_t *p = p_port->p_physp;
576                 uint8_t hops;
577
578                 if (!p || !p->p_remote_physp || !p->p_remote_physp->p_node->sw)
579                         return OSM_NO_PATH;
580                 if (p->p_remote_physp->p_node->sw == p_sw)
581                         return 1;
582                 lid = osm_node_get_base_lid(p->p_remote_physp->p_node, 0);
583                 hops = osm_switch_get_least_hops(p_sw, cl_ntoh16(lid));
584                 return hops != OSM_NO_PATH ? hops + 1 : OSM_NO_PATH;
585         }
586 }
587
588 /**********************************************************************
589  **********************************************************************/
590 uint8_t
591 osm_switch_recommend_mcast_path(IN osm_switch_t * const p_sw,
592                                 IN osm_port_t * p_port,
593                                 IN uint16_t const mlid_ho,
594                                 IN boolean_t const ignore_existing)
595 {
596         uint16_t base_lid;
597         uint8_t hops;
598         uint8_t port_num;
599         uint8_t num_ports;
600         uint8_t least_hops;
601
602         CL_ASSERT(mlid_ho >= IB_LID_MCAST_START_HO);
603
604         if (p_port->p_node->sw) {
605                 if (p_port->p_node->sw == p_sw)
606                         return 0;
607                 base_lid = osm_port_get_base_lid(p_port);
608         } else {
609                 osm_physp_t *p_physp = p_port->p_physp;
610                 if (!p_physp || !p_physp->p_remote_physp ||
611                     !p_physp->p_remote_physp->p_node->sw)
612                         return OSM_NO_PATH;
613                 if (p_physp->p_remote_physp->p_node->sw == p_sw)
614                         return p_physp->p_remote_physp->port_num;
615                 base_lid =
616                     osm_node_get_base_lid(p_physp->p_remote_physp->p_node, 0);
617         }
618         base_lid = cl_ntoh16(base_lid);
619         num_ports = p_sw->num_ports;
620
621         /*
622            If the user wants us to ignore existing multicast routes,
623            then simply return the shortest hop count path to the
624            target port.
625
626            Otherwise, return the first port that has a path to the target,
627            picking from the ports that are already in the multicast group.
628          */
629         if (!ignore_existing) {
630                 for (port_num = 1; port_num < num_ports; port_num++) {
631                         if (!osm_mcast_tbl_is_port
632                             (&p_sw->mcast_tbl, mlid_ho, port_num))
633                                 continue;
634                         /*
635                            Don't be too trusting of the current forwarding table!
636                            Verify that the LID is reachable through this port.
637                          */
638                         hops =
639                             osm_switch_get_hop_count(p_sw, base_lid, port_num);
640                         if (hops != OSM_NO_PATH)
641                                 return (port_num);
642                 }
643         }
644
645         /*
646            Either no existing mcast paths reach this port or we are
647            ignoring existing paths.
648
649            Determine the best multicast path to the target.  Note that this
650            algorithm is slightly different from the one used for unicast route
651            recommendation.  In this case (multicast), we must NOT
652            perform any sort of load balancing.  We MUST take the FIRST
653            port found that has <= the lowest hop count path.  This prevents
654            more than one multicast path to the same remote switch which
655            prevents a multicast loop.  Multicast loops are bad since the same
656            multicast packet will go around and around, inevitably creating
657            a black hole that will destroy the Earth in a firey conflagration.
658          */
659         least_hops = osm_switch_get_least_hops(p_sw, base_lid);
660         for (port_num = 1; port_num < num_ports; port_num++)
661                 if (osm_switch_get_hop_count(p_sw, base_lid, port_num) ==
662                     least_hops)
663                         break;
664
665         CL_ASSERT(port_num < num_ports);
666         return (port_num);
667 }