New opensm component
[mirror/winof/.git] / ulp / opensm / user / opensm / osm_switch.c
1 /*
2  * Copyright (c) 2004, 2005 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2005 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 the OpenIB.org BSD license
7  * below:
8  *
9  *     Redistribution and use in source and binary forms, with or
10  *     without modification, are permitted provided that the following
11  *     conditions are met:
12  *
13  *      - Redistributions of source code must retain the above
14  *        copyright notice, this list of conditions and the following
15  *        disclaimer.
16  *
17  *      - Redistributions in binary form must reproduce the above
18  *        copyright notice, this list of conditions and the following
19  *        disclaimer in the documentation and/or other materials
20  *        provided with the distribution.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
26  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
27  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29  * SOFTWARE.
30  *
31  * $Id$
32  */
33
34
35 /*
36  * Abstract:
37  *    Implementation of osm_switch_t.
38  * This object represents an Infiniband switch.
39  * This object is part of the opensm family of objects.
40  *
41  * Environment:
42  *    Linux User Mode
43  *
44  * $Revision: 1.13 $
45  */
46
47 #if HAVE_CONFIG_H
48 #  include <config.h>
49 #endif /* HAVE_CONFIG_H */
50
51 #include <complib/cl_memory.h>
52 #include <complib/cl_math.h>
53 #include <iba/ib_types.h>
54 #include <opensm/osm_switch.h>
55
56
57 /**********************************************************************
58  **********************************************************************/
59 void
60 osm_switch_construct(
61   IN osm_switch_t* const p_sw )
62 {
63   CL_ASSERT( p_sw );
64   cl_memclr( p_sw, sizeof(*p_sw) );
65   osm_lid_matrix_construct( &p_sw->lmx );
66 }
67
68 /**********************************************************************
69  **********************************************************************/
70 ib_api_status_t
71 osm_switch_init(
72   IN osm_switch_t* const p_sw,
73   IN osm_node_t* const p_node,
74   IN const osm_madw_t* const p_madw )
75 {
76   ib_api_status_t       status = IB_SUCCESS;
77   ib_switch_info_t      *p_si;
78   ib_smp_t           *p_smp;
79   uint8_t               num_ports;
80   uint32_t           port_num;
81
82   CL_ASSERT( p_sw );
83   CL_ASSERT( p_madw );
84   CL_ASSERT( p_node );
85
86   osm_switch_construct( p_sw );
87
88   p_smp = osm_madw_get_smp_ptr( p_madw );
89   p_si = (ib_switch_info_t*)ib_smp_get_payload_ptr( p_smp );
90   num_ports = osm_node_get_num_physp( p_node );
91
92   CL_ASSERT( p_smp->attr_id == IB_MAD_ATTR_SWITCH_INFO );
93
94   p_sw->p_node = p_node;
95   p_sw->switch_info = *p_si;
96
97   status = osm_lid_matrix_init( &p_sw->lmx, num_ports );
98   if( status != IB_SUCCESS )
99     goto Exit;
100
101   status = osm_fwd_tbl_init( &p_sw->fwd_tbl, p_si );
102
103   p_sw->p_pro = cl_zalloc( sizeof(*p_sw->p_pro) * num_ports );
104   if( p_sw->p_pro == NULL )
105   {
106     status = IB_INSUFFICIENT_MEMORY;
107     goto Exit;
108   }
109
110   status = osm_mcast_tbl_init( &p_sw->mcast_tbl,
111                                osm_node_get_num_physp( p_node ), cl_ntoh16( p_si->mcast_cap ) );
112   if( status != IB_SUCCESS )
113     goto Exit;
114
115   for( port_num = 0; port_num < num_ports; port_num++ )
116     osm_port_pro_construct( &p_sw->p_pro[port_num] );
117
118  Exit:
119   return( status );
120 }
121
122 /**********************************************************************
123  **********************************************************************/
124 void
125 osm_switch_destroy(
126   IN osm_switch_t* const p_sw )
127 {
128   /* free memory to avoid leaks */
129   osm_mcast_tbl_destroy( &p_sw->mcast_tbl );
130   cl_free( p_sw->p_pro );
131   osm_fwd_tbl_destroy( &p_sw->fwd_tbl );
132   osm_lid_matrix_destroy( &p_sw->lmx );
133 }
134
135 /**********************************************************************
136  **********************************************************************/
137 void
138 osm_switch_delete(
139   IN OUT osm_switch_t** const pp_sw )
140 {
141   osm_switch_destroy( *pp_sw );
142   cl_free( *pp_sw );
143   *pp_sw = NULL;
144 }
145
146 /**********************************************************************
147  **********************************************************************/
148 osm_switch_t*
149 osm_switch_new(
150   IN osm_node_t* const p_node,
151   IN const osm_madw_t* const p_madw )
152 {
153   ib_api_status_t status;
154   osm_switch_t *p_sw;
155
156   p_sw = (osm_switch_t*)cl_zalloc( sizeof(*p_sw) );
157   if( p_sw )
158   {
159     status = osm_switch_init( p_sw, p_node, p_madw );
160     if( status != IB_SUCCESS )
161       osm_switch_delete( &p_sw );
162   }
163
164   return( p_sw );
165 }
166
167 /**********************************************************************
168  **********************************************************************/
169 boolean_t
170 osm_switch_get_fwd_tbl_block(
171   IN const osm_switch_t* const p_sw,
172   IN const uint32_t block_id,
173   OUT uint8_t* const p_block )
174 {
175   uint16_t base_lid_ho;
176   uint16_t max_lid_ho;
177   uint16_t lid_ho;
178   uint16_t block_top_lid_ho;
179   uint32_t lids_per_block;
180   osm_fwd_tbl_t *p_tbl;
181   boolean_t return_flag = FALSE;
182
183   CL_ASSERT( p_sw );
184   CL_ASSERT( p_block );
185
186   p_tbl = osm_switch_get_fwd_tbl_ptr( p_sw );
187   max_lid_ho = osm_switch_get_max_lid_ho( p_sw );
188   lids_per_block = osm_fwd_tbl_get_lids_per_block( &p_sw->fwd_tbl );
189   base_lid_ho = (uint16_t)(block_id * lids_per_block);
190
191   if( base_lid_ho < max_lid_ho )
192   {
193     cl_memclr( p_block, IB_SMP_DATA_SIZE );
194     /*
195       Determine the range of LIDs we can return with this block.
196     */
197     block_top_lid_ho = (uint16_t)(base_lid_ho + lids_per_block - 1);
198     if( block_top_lid_ho > max_lid_ho )
199       block_top_lid_ho = max_lid_ho;
200
201     /*
202       Configure the forwarding table with the routing
203       information for the specified block of LIDs.
204     */
205     for( lid_ho = base_lid_ho; lid_ho <= block_top_lid_ho; lid_ho++ )
206     {
207       p_block[lid_ho - base_lid_ho] =  osm_fwd_tbl_get( p_tbl, lid_ho );
208     }
209
210     return_flag = TRUE;
211   }
212
213   return( return_flag );
214 }
215
216 /**********************************************************************
217  **********************************************************************/
218 uint8_t
219 osm_switch_recommend_path(
220   IN const osm_switch_t* const p_sw,
221   IN const uint16_t lid_ho,
222   IN const boolean_t ignore_existing,
223   IN OUT uint64_t *remote_sys_guids,
224   IN OUT uint16_t *p_num_used_sys,
225   IN OUT uint64_t *remote_node_guids,
226   IN OUT uint16_t *p_num_used_nodes,
227   IN const uint32_t max_routes_subscribed,
228   IN boolean_t      ui_ucast_fdb_assign_func_defined
229   )
230 {
231   /*
232     We support an enhanced LMC aware routing mode:
233     In case of LMC > 0 we can track the remote side
234     system and node for all of the lids of the target
235     and try and avoid routing again through the same
236     system / node.
237
238     If the procedure is provided with the tracking arrays
239     and counters we can conduct this algorithm.
240   */
241   boolean_t routing_for_lmc = remote_sys_guids && remote_node_guids &&
242     p_num_used_sys && p_num_used_nodes;
243   boolean_t sys_used, node_used;
244   uint16_t i;
245   uint8_t hops;
246   uint8_t least_hops;
247   uint8_t port_num;
248   uint8_t num_ports;
249   uint32_t least_paths = 0xFFFFFFFF;
250   /*
251     The follwing will track the least paths if the
252     route should go through a new system/node
253   */
254   uint32_t least_paths_other_sys = 0xFFFFFFFF;
255   uint32_t least_paths_other_nodes = 0xFFFFFFFF;
256   uint32_t check_count;
257   uint8_t best_port = 0;
258   /*
259     These vars track the best port if it connects to
260     not used system/node.
261   */
262   uint8_t best_port_other_sys = 0;
263   uint8_t best_port_other_node = 0;
264   boolean_t port_found = FALSE;
265   osm_physp_t *p_physp;
266   osm_physp_t *p_rem_physp;
267   osm_node_t *p_rem_node;
268
269   CL_ASSERT( lid_ho > 0 );
270
271   num_ports = osm_switch_get_num_ports( p_sw );
272
273   least_hops = osm_switch_get_least_hops( p_sw, lid_ho );
274
275   if ( least_hops == OSM_NO_PATH )
276     return (OSM_NO_PATH);
277
278   /*
279     First, enquire with the forwarding table for an existing
280     route.  If one is found, honor it unless:
281     1. the ignore existing flag is set.
282     2. the physical port is not a valid one or not healthy
283     3. the physical port has a remote port (the link is up)
284     4. the port has min-hops to the target (avoid loops)
285   */
286   if( !ignore_existing )
287   {
288     port_num = osm_fwd_tbl_get( &p_sw->fwd_tbl, lid_ho );
289
290     if (port_num != OSM_NO_PATH)
291     {
292       p_physp = osm_node_get_physp_ptr(p_sw->p_node, port_num);
293       /*
294         Don't be too trusting of the current forwarding table!
295         Verify that the port number is legal and that the
296         LID is reachable through this port.
297       */
298       if( (port_num < num_ports )  &&
299           osm_physp_is_valid(p_physp) &&
300           osm_physp_is_healthy(p_physp) &&
301           osm_physp_get_remote(p_physp) )
302       {
303         hops = osm_switch_get_hop_count( p_sw, lid_ho, port_num );
304         /* 
305            If we aren't using pre-defined user routes function, then
306            we need to make sure that the current path is the minimum one.
307            In case of having such a user function - this check will not
308            be done, and the old routing will be used.
309            Note: This means that it is the user's job to clean all data
310            in the forwarding tables that he wants to be overridden by the
311            minimum hop function. 
312         */
313         if ( hops == least_hops || ui_ucast_fdb_assign_func_defined )
314         {
315           return( port_num );
316         }
317       }
318     }
319   }
320
321   /*
322     This algorithm selects a port based on a static load balanced
323     selection across equal hop-count ports.
324     There is lots of room for improved sophistication here,
325     possibly guided by user configuration info.
326   */
327
328   /*
329     OpenSM routing is "local" - not considering a full lid to lid
330     path. As such we can not guarantee a path will not loop if we
331     do not always follow least hops.
332     So we must abort if not least hops.
333   */
334
335   /* port number starts with zero and num_ports is 1 + num phys ports */
336   for ( port_num = 0; port_num < num_ports; port_num++ )
337   {
338     if ( osm_switch_get_hop_count( p_sw, lid_ho, port_num ) == least_hops)
339     {
340       /* let us make sure it is not down or un-healthy */
341       p_physp = osm_node_get_physp_ptr(p_sw->p_node, port_num);
342       if (osm_physp_is_valid(p_physp) &&
343           osm_physp_is_healthy(p_physp) &&
344           /*
345             we require all - non sma ports to be linked
346             to be routed through
347           */
348           (! port_num || osm_physp_get_remote(p_physp)))
349       {
350
351         /*
352           We located a least-hop port, possibly one of many.
353           For this port, check the running total count of
354           the number of paths through this port.  Select
355           the port routing the least number of paths.
356         */
357         check_count = osm_port_pro_path_count_get(
358           &p_sw->p_pro[port_num] );
359
360         /*
361           Advanced LMC routing requires tracking of the
362           best port by the node connected to the other side of
363           it .
364         */
365         if (routing_for_lmc && port_num)
366         {
367           /* printf("LID:%u SYS:%d NODE:%d\n", lid_ho,*p_num_used_sys, *p_num_used_nodes); */
368
369           /* Get the Remote Node */
370           p_rem_physp = osm_physp_get_remote(p_physp);
371           p_rem_node = osm_physp_get_node_ptr(p_rem_physp);
372
373           /* Is the sys guid already used ? */
374           sys_used = FALSE;
375           for (i = 0; !sys_used && (i < *p_num_used_sys); i++)
376             if (!cl_memcmp(&p_rem_node->node_info.sys_guid,
377                            &remote_sys_guids[i],
378                            sizeof(uint64_t)))
379               sys_used = TRUE;
380
381           /* If not update the least hops for this case */
382           if (! sys_used)
383           {
384             if (check_count < least_paths_other_sys)
385             {
386               least_paths_other_sys = check_count;
387               best_port_other_sys = port_num;
388             }
389           }
390           else
391           { /* same sys found - try node */
392
393             /* Else Is the node guid already used ? */
394             node_used = FALSE;
395             for (i = 0; !node_used && (i < *p_num_used_nodes); i++)
396               if (!cl_memcmp(&p_rem_node->node_info.node_guid,
397                              &remote_node_guids[i],
398                              sizeof(uint64_t)))
399                 node_used = TRUE;
400
401
402             /* If not update the least hops for this case */
403             if (! node_used)
404             {
405               if (check_count < least_paths_other_nodes)
406               {
407                 least_paths_other_nodes = check_count;
408                 best_port_other_node = port_num;
409               }
410             }
411
412           } /* same sys found */
413         } /* routing for LMC mode */
414
415         /*
416           the count is min but also lower then the max subscribed
417         */
418         if( (check_count < least_paths) &&
419             (check_count <= max_routes_subscribed))
420         {
421           port_found = TRUE;
422           best_port = port_num;
423           least_paths = check_count;
424         }
425       }
426     }
427   }
428
429   if ( port_found == FALSE )
430     return (OSM_NO_PATH);
431
432   /*
433     if we are in enhanced routing mode and the best port is not
434     the local port 0
435   */
436   if (routing_for_lmc && best_port)
437   {
438     /* Select the least hop port of the non used sys first */
439     if (best_port_other_sys)
440       best_port = best_port_other_sys;
441     else if (best_port_other_node)
442       best_port = best_port_other_node;
443
444     /* track the remote node and system of the port used. */
445     p_physp = osm_node_get_physp_ptr(p_sw->p_node, best_port);
446     p_rem_physp = osm_physp_get_remote(p_physp);
447     p_rem_node = osm_physp_get_node_ptr(p_rem_physp);
448     cl_memcpy(&remote_node_guids[*p_num_used_nodes],
449               &(p_rem_node->node_info.node_guid),
450               sizeof(uint64_t));
451     (*p_num_used_nodes)++;
452     cl_memcpy(&remote_sys_guids[*p_num_used_sys],
453               &(p_rem_node->node_info.sys_guid),
454               sizeof(uint64_t));
455     (*p_num_used_sys)++;
456   }
457
458   return( best_port );
459 }
460
461 /**********************************************************************
462  **********************************************************************/
463 void
464 osm_switch_prepare_path_rebuild(
465   IN osm_switch_t* const p_sw )
466 {
467   uint8_t port_num;
468   uint8_t num_ports;
469
470   num_ports = osm_switch_get_num_ports( p_sw );
471   osm_lid_matrix_clear( &p_sw->lmx );
472   for( port_num = 0; port_num < num_ports; port_num++ )
473     osm_port_pro_construct( &p_sw->p_pro[port_num] );
474 }
475
476 /**********************************************************************
477  **********************************************************************/
478 uint8_t
479 osm_switch_recommend_mcast_path(
480   IN osm_switch_t*         const p_sw,
481   IN uint16_t              const lid_ho,
482   IN uint16_t              const mlid_ho,
483   IN boolean_t          const ignore_existing )
484 {
485   uint8_t                  hops;
486   uint8_t                  port_num;
487   uint8_t                  num_ports;
488   uint8_t                  least_hops;
489
490   CL_ASSERT( lid_ho > 0 );
491   CL_ASSERT( mlid_ho >= IB_LID_MCAST_START_HO );
492   num_ports = osm_switch_get_num_ports( p_sw );
493
494   /*
495     If the user wants us to ignore existing multicast routes,
496     then simply return the shortest hop count path to the
497     target port.
498
499     Otherwise, return the first port that has a path to the target,
500     picking from the ports that are already in the multicast group.
501   */
502   if( !ignore_existing )
503   {
504     for( port_num = 1; port_num < num_ports; port_num++ )
505     {
506       if( osm_mcast_tbl_is_port( &p_sw->mcast_tbl, mlid_ho, port_num ) )
507       {
508         /*
509           Don't be too trusting of the current forwarding table!
510           Verify that the LID is reachable through this port.
511         */
512         hops = osm_switch_get_hop_count( p_sw, lid_ho, port_num );
513         if( hops != OSM_NO_PATH )
514         {
515           return( port_num );
516         }
517       }
518     }
519   }
520
521   /*
522     Either no existing mcast paths reach this port or we are
523     ignoring existing paths.
524
525     Determine the best multicast path to the target.  Note that this
526     algorithm is slightly different from the one used for unicast route
527     recommendation.  In this case (multicast) we must NOT
528     perform any sort of load balancing.  We MUST take the FIRST
529     port found that has <= the lowest hop count path.  This prevents
530     more than one multicast path to the same remote switch which
531     prevents a multicast loop.  Multicast loops are bad since the same
532     multicast packet will go around and around, inevitably creating
533     a black hole that will destroy the Earth in a firey conflagration.
534   */
535   least_hops = osm_switch_get_least_hops( p_sw, lid_ho );
536   for( port_num = 0; port_num < num_ports; port_num++ )
537   {
538     if( osm_switch_get_hop_count( p_sw, lid_ho, port_num ) == least_hops )
539       break;
540   }
541
542   CL_ASSERT( port_num < num_ports );
543   return( port_num );
544 }