In osm_ucast_mgr, where osm_req_set is called for SwitchInfo,
[mirror/winof/.git] / ulp / opensm / user / opensm / osm_link_mgr.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_link_mgr_t.
38  * This file implements the Link Manager object.
39  *
40  * Environment:
41  *    Linux User Mode
42  *
43  * $Revision: 1.15 $
44  */
45
46 #if HAVE_CONFIG_H
47 #  include <config.h>
48 #endif /* HAVE_CONFIG_H */
49
50 #include <iba/ib_types.h>
51 #include <complib/cl_memory.h>
52 #include <complib/cl_debug.h>
53 #include <opensm/osm_link_mgr.h>
54 #include <opensm/osm_node.h>
55 #include <opensm/osm_switch.h>
56 #include <opensm/osm_helper.h>
57 #include <opensm/osm_msgdef.h>
58
59
60 /**********************************************************************
61  **********************************************************************/
62 void
63 osm_link_mgr_construct(
64   IN osm_link_mgr_t* const p_mgr )
65 {
66   cl_memclr( p_mgr, sizeof(*p_mgr) );
67 }
68
69 /**********************************************************************
70  **********************************************************************/
71 void
72 osm_link_mgr_destroy(
73   IN osm_link_mgr_t* const p_mgr )
74 {
75   OSM_LOG_ENTER( p_mgr->p_log, osm_link_mgr_destroy );
76
77
78   OSM_LOG_EXIT( p_mgr->p_log );
79 }
80
81
82 /**********************************************************************
83  **********************************************************************/
84 ib_api_status_t
85 osm_link_mgr_init(
86   IN osm_link_mgr_t* const p_mgr,
87   IN osm_req_t* const p_req,
88   IN osm_subn_t* const p_subn,
89   IN osm_log_t* const p_log,
90   IN cl_plock_t* const p_lock )
91 {
92   ib_api_status_t status = IB_SUCCESS;
93
94   OSM_LOG_ENTER( p_log, osm_link_mgr_init );
95
96   CL_ASSERT( p_req );
97   CL_ASSERT( p_subn );
98   CL_ASSERT( p_lock );
99
100   osm_link_mgr_construct( p_mgr );
101
102   p_mgr->p_log = p_log;
103   p_mgr->p_subn = p_subn;
104   p_mgr->p_lock = p_lock;
105   p_mgr->p_req = p_req;
106
107   OSM_LOG_EXIT( p_mgr->p_log );
108   return( status );
109 }
110
111 /**********************************************************************
112  **********************************************************************/
113 void
114 osm_link_mgr_set_physp_pi(
115   IN osm_link_mgr_t*       const p_mgr,
116   IN osm_physp_t*          const p_physp,
117   IN uint8_t               const port_state )
118 {
119   uint8_t                  payload[IB_SMP_DATA_SIZE];
120   ib_port_info_t*          const p_pi = (ib_port_info_t*)payload;
121   const ib_port_info_t*    p_old_pi;
122   osm_madw_context_t       context;
123   osm_node_t*              p_node;
124   ib_api_status_t          status;
125   uint8_t                  port_num;
126   uint8_t                  mtu;
127   uint8_t                  op_vls;
128   boolean_t                send_set = FALSE;
129   osm_physp_t             *p_remote_physp;
130
131   OSM_LOG_ENTER( p_mgr->p_log, osm_link_mgr_set_physp_pi );
132
133   CL_ASSERT( p_physp );
134   CL_ASSERT( osm_physp_is_valid( p_physp ) );
135
136   p_node = osm_physp_get_node_ptr( p_physp );
137
138   port_num = osm_physp_get_port_num( p_physp );
139
140   if( port_num == 0 )
141   {
142     osm_switch_t *p_switch;
143     ib_switch_info_t* p_sw_info;
144     /*
145       HCA's don't have a port 0, and for switch port 0,
146       we need to check if this is enhanced port 0 or base port 0.
147       For base port 0 the following parameters are not valid. (p734, table132)
148     */
149     p_switch = osm_get_switch_by_guid( p_mgr->p_subn, p_node->node_info.node_guid );
150     if (! p_switch )
151     {
152       osm_log( p_mgr->p_log, OSM_LOG_ERROR,
153                "osm_link_mgr_set_physp_pi: ERR 4201: "
154                "Cannot find switch by guid: 0x%" PRIx64 "\n",
155                cl_ntoh64( p_node->node_info.node_guid ) );
156       goto Exit;
157     }
158      
159     p_sw_info = osm_switch_get_si_ptr( p_switch );
160     if (ib_switch_info_is_enhanced_port_0( p_sw_info ) == FALSE)
161     {
162       /* This means the switch doesn't support enhanced port zero. 
163          Can skip it. */
164       if( osm_log_is_active( p_mgr->p_log, OSM_LOG_DEBUG ) )
165       {
166         osm_log( p_mgr->p_log, OSM_LOG_DEBUG,
167                  "osm_link_mgr_set_physp_pi: "
168                  "Skipping port 0, GUID = 0x%016" PRIx64 ".\n",
169                  cl_ntoh64( osm_physp_get_port_guid( p_physp ) ) );
170       }
171       goto Exit;
172     }
173   }
174
175   /*
176     PAST THIS POINT WE ARE HANDLING EITHER A NON PORT 0 OR ENHANCED PORT 0
177   */
178
179   p_node = osm_physp_get_node_ptr( p_physp );
180   p_old_pi = osm_physp_get_port_info_ptr( p_physp );
181
182   cl_memclr( payload, IB_SMP_DATA_SIZE );
183
184   /* Correction by FUJITSU */
185   cl_memcpy( payload, p_old_pi, sizeof(ib_port_info_t) );
186
187   /*
188     Correction following a bug injected by the previous
189     FUJITSU line:
190
191     Should never write back a value that is bigger then 3 in
192     the PortPhysicalState field - so can not simply copy!
193
194     Actually we want to write there:
195     port physical state - no change,
196     link down default state = polling
197     port state - no change
198   */
199   p_pi->state_info2 = 0x02;
200   ib_port_info_set_port_state( p_pi, IB_LINK_NO_CHANGE );
201   if ( ib_port_info_get_link_down_def_state(p_pi) != 
202        ib_port_info_get_link_down_def_state(p_old_pi) )
203     send_set = TRUE;
204
205   /* we only change port fields if we do not change state */
206   if (port_state == IB_LINK_NO_CHANGE)
207   {
208     /* The following fields are relevant only for CA port or Enh.SP0 */
209     if( osm_node_get_type( p_node ) != IB_NODE_TYPE_SWITCH ||
210         port_num == 0 )
211     {
212       p_pi->m_key = p_mgr->p_subn->opt.m_key;
213       if (cl_memcmp( &p_pi->m_key, &p_old_pi->m_key, sizeof(p_pi->m_key) ))
214         send_set = TRUE;
215
216       p_pi->subnet_prefix = p_mgr->p_subn->opt.subnet_prefix;
217       if (cl_memcmp( &p_pi->subnet_prefix, &p_old_pi->subnet_prefix, 
218                      sizeof(p_pi->subnet_prefix) ))
219         send_set = TRUE;
220
221       p_pi->base_lid = osm_physp_get_base_lid( p_physp );
222       if (cl_memcmp( &p_pi->base_lid, &p_old_pi->base_lid, 
223                      sizeof(p_pi->base_lid) ))
224         send_set = TRUE;
225
226       /*  we are initializing the ports with our local sm_base_lid */
227       p_pi->master_sm_base_lid = p_mgr->p_subn->sm_base_lid;
228       if (cl_memcmp( &p_pi->master_sm_base_lid, &p_old_pi->master_sm_base_lid, 
229                      sizeof(p_pi->master_sm_base_lid) ))
230         send_set = TRUE;
231
232       p_pi->m_key_lease_period = p_mgr->p_subn->opt.m_key_lease_period;
233       if (cl_memcmp( &p_pi->m_key_lease_period, &p_old_pi->m_key_lease_period, 
234                      sizeof(p_pi->m_key_lease_period) ))
235         send_set = TRUE;
236
237       p_pi->mkey_lmc = p_mgr->p_subn->opt.lmc;
238       if (cl_memcmp( &p_pi->mkey_lmc, &p_old_pi->mkey_lmc, sizeof(p_pi->mkey_lmc) ))
239         send_set = TRUE;
240
241       ib_port_info_set_timeout( p_pi, p_mgr->p_subn->opt.subnet_timeout );
242       if (ib_port_info_get_timeout( p_pi ) != ib_port_info_get_timeout( p_old_pi ))
243         send_set = TRUE;
244     }
245
246     /*
247       Several timeout mechanisms:
248     */
249     p_remote_physp = osm_physp_get_remote( p_physp );
250     
251     if (p_remote_physp && 
252         osm_physp_is_valid(p_remote_physp) &&
253         (osm_node_get_type( osm_physp_get_node_ptr(p_remote_physp) ) != 
254          IB_NODE_TYPE_SWITCH))
255     {
256       /* we drive an HCA port so we need to set stall-count to 1 and
257          use leaf hoq value */
258       ib_port_info_set_hoq_lifetime(
259         p_pi, p_mgr->p_subn->opt.leaf_head_of_queue_lifetime);
260       ib_port_info_set_vl_stall_count(
261         p_pi, OSM_DEFAULT_LEAF_VL_STALL_COUNT);
262     }
263     else
264     {
265       ib_port_info_set_hoq_lifetime(
266         p_pi, p_mgr->p_subn->opt.head_of_queue_lifetime);
267     }
268     if ( ib_port_info_get_hoq_lifetime(p_pi) !=
269          ib_port_info_get_hoq_lifetime(p_old_pi) )
270       send_set = TRUE;
271
272     ib_port_info_set_phy_and_overrun_err_thd(
273       p_pi,
274       p_mgr->p_subn->opt.local_phy_errors_threshold,
275       p_mgr->p_subn->opt.overrun_errors_threshold);
276     if (cl_memcmp( &p_pi->error_threshold, &p_old_pi->error_threshold, 
277                    sizeof(p_pi->error_threshold) ))
278       send_set = TRUE;
279
280     /*
281       Set the easy common parameters for all port types,
282       then determine the neighbor MTU.
283     */
284     p_pi->link_width_enabled = p_old_pi->link_width_supported;
285     if (cl_memcmp( &p_pi->link_width_enabled, &p_old_pi->link_width_enabled, 
286                     sizeof(p_pi->link_width_enabled) ))
287       send_set = TRUE;
288
289     /* calc new op_vls and mtu */
290     op_vls =
291       osm_physp_calc_link_op_vls(p_mgr->p_log, p_mgr->p_subn, p_physp );
292     mtu =  osm_physp_calc_link_mtu(p_mgr->p_log, p_physp );
293
294     ib_port_info_set_neighbor_mtu( p_pi, mtu );
295     if ( ib_port_info_get_neighbor_mtu(p_pi) !=
296          ib_port_info_get_neighbor_mtu(p_old_pi) )
297       send_set = TRUE;
298
299     ib_port_info_set_op_vls( p_pi, op_vls );
300     if ( ib_port_info_get_op_vls(p_pi) != 
301          ib_port_info_get_op_vls(p_old_pi) )
302       send_set = TRUE;
303
304     /* also the context can flag the need to check for errors. */
305     context.pi_context.ignore_errors = FALSE;
306   }
307   else
308   {
309     /*
310       Since the only change we try to do is to modify the port
311       state we can ignore the errors that might becaused by a
312       race in setting the state and the actual state the port is
313       in.
314     */
315     context.pi_context.ignore_errors = FALSE;
316   }
317
318   ib_port_info_set_port_state( p_pi, port_state );
319   if (port_state != IB_LINK_NO_CHANGE &&
320       ib_port_info_get_port_state(p_pi) != 
321       ib_port_info_get_port_state(p_old_pi) )
322     send_set = TRUE;
323
324   context.pi_context.node_guid = osm_node_get_node_guid( p_node );
325   context.pi_context.port_guid = osm_physp_get_port_guid( p_physp );
326   context.pi_context.set_method = TRUE;
327   context.pi_context.update_master_sm_base_lid = FALSE;
328   context.pi_context.light_sweep = FALSE;
329
330   /* We need to send the PortInfoSet request with the new sm_lid
331      in the following cases:
332      1. There is a change in the values (send_set == TRUE)
333      2. This is an hca port or a switch port zero and got_set_resp is FALSE 
334         (in this case we sent a PortInfoSet in the osm_lid_mgr, but for some reason we 
335         didn't get a response) - try and re-send.
336      3. This is a switch port and:
337         a. first_time_master_sweep flag on the subnet is TRUE. This means the
338            SM just became master, and it then needs to send at PortInfoSet to
339            every port (and this is the first time we can send a PortInfoSet to switch 
340            external ports).
341         b. got_set_resp on the physical port is FALSE. This means we haven't seen
342            this port before - need to send PortInfoSet to it.
343   */
344   if (send_set || 
345       (osm_node_get_type(p_node) != IB_NODE_TYPE_SWITCH && p_physp->got_set_resp == FALSE) ||
346       (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH && port_num == 0 && 
347        p_physp->got_set_resp == FALSE) ||
348       (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH && port_num != 0 &&
349        (p_mgr->p_subn->first_time_master_sweep == TRUE || p_physp->got_set_resp == FALSE)))
350   {
351     p_mgr->send_set_reqs = TRUE;
352     status = osm_req_set( p_mgr->p_req,
353                           osm_physp_get_dr_path_ptr( p_physp ),
354                           payload,
355                           sizeof(payload),
356                           IB_MAD_ATTR_PORT_INFO,
357                           cl_hton32(port_num),
358                           CL_DISP_MSGID_NONE,
359                           &context );
360   }
361
362  Exit:
363   OSM_LOG_EXIT( p_mgr->p_log );
364 }
365
366 /**********************************************************************
367  **********************************************************************/
368 osm_signal_t
369 __osm_link_mgr_process_port(
370   IN osm_link_mgr_t* const p_mgr,
371   IN osm_port_t* const p_port,
372   IN const uint8_t link_state )
373 {
374   uint32_t i;
375   uint32_t num_physp;
376   osm_physp_t *p_physp;
377   uint8_t current_state;
378   osm_signal_t signal = OSM_SIGNAL_DONE;
379
380   OSM_LOG_ENTER( p_mgr->p_log, __osm_link_mgr_process_port );
381
382   if( osm_log_is_active( p_mgr->p_log, OSM_LOG_DEBUG ) )
383   {
384     osm_log( p_mgr->p_log, OSM_LOG_DEBUG,
385              "__osm_link_mgr_process_port: "
386              "Port 0x%" PRIx64 " going to %s.\n",
387              cl_ntoh64( osm_port_get_guid( p_port ) ),
388              ib_get_port_state_str( link_state ) );
389   }
390
391   /*
392     Set the PortInfo for every Physical Port associated
393     with this Port.  Start iterating with port 1, since the linkstate
394     is not applicable to the management port on switches.
395   */
396   num_physp = osm_port_get_num_physp( p_port );
397   for( i = 0; i < num_physp; i ++ )
398   {
399     /*
400       Don't bother doing anything if this Physical Port is not valid.
401       or if the state of the port is already better then the
402       specified state.
403     */
404     p_physp = osm_port_get_phys_ptr( p_port, (uint8_t)i );
405     if( p_physp && osm_physp_is_valid( p_physp ) )
406     {
407       current_state = osm_physp_get_port_state( p_physp );
408
409       if( current_state == IB_LINK_DOWN )
410         continue;
411
412       /*
413         Normally we only send state update if state is lower
414         then required state. However, we need to send update if
415         no state change required.
416       */
417       if( (link_state == IB_LINK_NO_CHANGE) ||
418           (current_state < link_state) )
419       {
420         p_mgr->send_set_reqs = FALSE;
421         osm_link_mgr_set_physp_pi(
422           p_mgr,
423           p_physp,
424           link_state );
425
426         if ( p_mgr->send_set_reqs == TRUE )
427           signal = OSM_SIGNAL_DONE_PENDING;
428       }
429       else
430       {
431         if( osm_log_is_active( p_mgr->p_log, OSM_LOG_DEBUG ) )
432         {
433           osm_log( p_mgr->p_log, OSM_LOG_DEBUG,
434                    "__osm_link_mgr_process_port: "
435                    "Physical port 0x%X already %s.  Skipping.\n",
436                    osm_physp_get_port_num( p_physp ),
437                    ib_get_port_state_str( current_state ) );
438         }
439       }
440     }
441   }
442
443   OSM_LOG_EXIT( p_mgr->p_log );
444   return( signal );
445 }
446
447 /**********************************************************************
448  **********************************************************************/
449 osm_signal_t
450 osm_link_mgr_process(
451   IN osm_link_mgr_t* const p_mgr,
452   IN const uint8_t link_state )
453 {
454   cl_qmap_t *p_port_guid_tbl;
455   osm_port_t *p_port;
456   osm_signal_t signal = OSM_SIGNAL_DONE;
457
458   OSM_LOG_ENTER( p_mgr->p_log, osm_link_mgr_process );
459
460   p_port_guid_tbl = &p_mgr->p_subn->port_guid_tbl;
461
462   CL_PLOCK_EXCL_ACQUIRE( p_mgr->p_lock );
463
464   for( p_port = (osm_port_t*)cl_qmap_head( p_port_guid_tbl );
465        p_port != (osm_port_t*)cl_qmap_end( p_port_guid_tbl );
466        p_port = (osm_port_t*)cl_qmap_next( &p_port->map_item ) )
467   {
468     if( __osm_link_mgr_process_port( p_mgr, p_port, link_state ) ==
469         OSM_SIGNAL_DONE_PENDING )
470       signal = OSM_SIGNAL_DONE_PENDING;
471   }
472
473   CL_PLOCK_RELEASE( p_mgr->p_lock );
474
475   OSM_LOG_EXIT( p_mgr->p_log );
476   return( signal );
477 }