]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ofed/management/opensm/opensm/osm_link_mgr.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / ofed / management / opensm / opensm / osm_link_mgr.c
1 /*
2  * Copyright (c) 2004-2008 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 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_link_mgr_t.
39  * This file implements the Link Manager object.
40  */
41
42 #if HAVE_CONFIG_H
43 #  include <config.h>
44 #endif                          /* HAVE_CONFIG_H */
45
46 #include <string.h>
47 #include <iba/ib_types.h>
48 #include <complib/cl_debug.h>
49 #include <opensm/osm_sm.h>
50 #include <opensm/osm_node.h>
51 #include <opensm/osm_switch.h>
52 #include <opensm/osm_helper.h>
53 #include <opensm/osm_msgdef.h>
54
55 /**********************************************************************
56  **********************************************************************/
57 static boolean_t
58 __osm_link_mgr_set_physp_pi(osm_sm_t * sm,
59                             IN osm_physp_t * const p_physp,
60                             IN uint8_t const port_state)
61 {
62         uint8_t payload[IB_SMP_DATA_SIZE];
63         ib_port_info_t *const p_pi = (ib_port_info_t *) payload;
64         const ib_port_info_t *p_old_pi;
65         osm_madw_context_t context;
66         osm_node_t *p_node;
67         ib_api_status_t status;
68         uint8_t port_num;
69         uint8_t mtu;
70         uint8_t op_vls;
71         boolean_t esp0 = FALSE;
72         boolean_t send_set = FALSE;
73         osm_physp_t *p_remote_physp;
74
75         OSM_LOG_ENTER(sm->p_log);
76
77         p_node = osm_physp_get_node_ptr(p_physp);
78
79         port_num = osm_physp_get_port_num(p_physp);
80
81         if (port_num == 0) {
82                 /*
83                    CAs don't have a port 0, and for switch port 0,
84                    we need to check if this is enhanced or base port 0.
85                    For base port 0 the following parameters are not valid (p822, table 145).
86                  */
87                 if (!p_node->sw) {
88                         OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 4201: "
89                                 "Cannot find switch by guid: 0x%" PRIx64 "\n",
90                                 cl_ntoh64(p_node->node_info.node_guid));
91                         goto Exit;
92                 }
93
94                 if (ib_switch_info_is_enhanced_port0(&p_node->sw->switch_info)
95                     == FALSE) {
96                         /* This means the switch doesn't support enhanced port 0.
97                            Can skip it. */
98                         OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
99                                 "Skipping port 0, GUID 0x%016" PRIx64 "\n",
100                                 cl_ntoh64(osm_physp_get_port_guid(p_physp)));
101                         goto Exit;
102                 }
103                 esp0 = TRUE;
104         }
105
106         /*
107            PAST THIS POINT WE ARE HANDLING EITHER A NON PORT 0 OR ENHANCED PORT 0
108          */
109
110         p_old_pi = &p_physp->port_info;
111
112         memset(payload, 0, IB_SMP_DATA_SIZE);
113         memcpy(payload, p_old_pi, sizeof(ib_port_info_t));
114
115         /*
116            Should never write back a value that is bigger then 3 in
117            the PortPhysicalState field - so can not simply copy!
118
119            Actually we want to write there:
120            port physical state - no change,
121            link down default state = polling
122            port state - as requested.
123          */
124         p_pi->state_info2 = 0x02;
125         ib_port_info_set_port_state(p_pi, port_state);
126
127         if (ib_port_info_get_link_down_def_state(p_pi) !=
128             ib_port_info_get_link_down_def_state(p_old_pi))
129                 send_set = TRUE;
130
131         /* didn't get PortInfo before */
132         if (!ib_port_info_get_port_state(p_old_pi))
133                 send_set = TRUE;
134
135         /* we only change port fields if we do not change state */
136         if (port_state == IB_LINK_NO_CHANGE) {
137                 /* The following fields are relevant only for CA port, router, or Enh. SP0 */
138                 if (osm_node_get_type(p_node) != IB_NODE_TYPE_SWITCH ||
139                     port_num == 0) {
140                         p_pi->m_key = sm->p_subn->opt.m_key;
141                         if (memcmp(&p_pi->m_key, &p_old_pi->m_key,
142                                    sizeof(p_pi->m_key)))
143                                 send_set = TRUE;
144
145                         p_pi->subnet_prefix = sm->p_subn->opt.subnet_prefix;
146                         if (memcmp(&p_pi->subnet_prefix,
147                                    &p_old_pi->subnet_prefix,
148                                    sizeof(p_pi->subnet_prefix)))
149                                 send_set = TRUE;
150
151                         p_pi->base_lid = osm_physp_get_base_lid(p_physp);
152                         if (memcmp(&p_pi->base_lid, &p_old_pi->base_lid,
153                                    sizeof(p_pi->base_lid)))
154                                 send_set = TRUE;
155
156                         /* we are initializing the ports with our local sm_base_lid */
157                         p_pi->master_sm_base_lid = sm->p_subn->sm_base_lid;
158                         if (memcmp(&p_pi->master_sm_base_lid,
159                                    &p_old_pi->master_sm_base_lid,
160                                    sizeof(p_pi->master_sm_base_lid)))
161                                 send_set = TRUE;
162
163                         p_pi->m_key_lease_period =
164                             sm->p_subn->opt.m_key_lease_period;
165                         if (memcmp(&p_pi->m_key_lease_period,
166                                    &p_old_pi->m_key_lease_period,
167                                    sizeof(p_pi->m_key_lease_period)))
168                                 send_set = TRUE;
169
170                         if (esp0 == FALSE)
171                                 p_pi->mkey_lmc = sm->p_subn->opt.lmc;
172                         else {
173                                 if (sm->p_subn->opt.lmc_esp0)
174                                         p_pi->mkey_lmc = sm->p_subn->opt.lmc;
175                                 else
176                                         p_pi->mkey_lmc = 0;
177                         }
178                         if (memcmp(&p_pi->mkey_lmc, &p_old_pi->mkey_lmc,
179                                    sizeof(p_pi->mkey_lmc)))
180                                 send_set = TRUE;
181
182                         ib_port_info_set_timeout(p_pi,
183                                                  sm->p_subn->opt.
184                                                  subnet_timeout);
185                         if (ib_port_info_get_timeout(p_pi) !=
186                             ib_port_info_get_timeout(p_old_pi))
187                                 send_set = TRUE;
188                 }
189
190                 /*
191                    Several timeout mechanisms:
192                  */
193                 p_remote_physp = osm_physp_get_remote(p_physp);
194                 if (port_num != 0 && p_remote_physp) {
195                         if (osm_node_get_type(osm_physp_get_node_ptr(p_physp))
196                             == IB_NODE_TYPE_ROUTER) {
197                                 ib_port_info_set_hoq_lifetime(p_pi,
198                                                               sm->p_subn->
199                                                               opt.
200                                                               leaf_head_of_queue_lifetime);
201                         } else
202                             if (osm_node_get_type
203                                 (osm_physp_get_node_ptr(p_physp)) ==
204                                 IB_NODE_TYPE_SWITCH) {
205                                 /* Is remote end CA or router (a leaf port) ? */
206                                 if (osm_node_get_type
207                                     (osm_physp_get_node_ptr(p_remote_physp)) !=
208                                     IB_NODE_TYPE_SWITCH) {
209                                         ib_port_info_set_hoq_lifetime(p_pi,
210                                                                       sm->
211                                                                       p_subn->
212                                                                       opt.
213                                                                       leaf_head_of_queue_lifetime);
214                                         ib_port_info_set_vl_stall_count(p_pi,
215                                                                         sm->
216                                                                         p_subn->
217                                                                         opt.
218                                                                         leaf_vl_stall_count);
219                                 } else {
220                                         ib_port_info_set_hoq_lifetime(p_pi,
221                                                                       sm->
222                                                                       p_subn->
223                                                                       opt.
224                                                                       head_of_queue_lifetime);
225                                         ib_port_info_set_vl_stall_count(p_pi,
226                                                                         sm->
227                                                                         p_subn->
228                                                                         opt.
229                                                                         vl_stall_count);
230                                 }
231                         }
232                         if (ib_port_info_get_hoq_lifetime(p_pi) !=
233                             ib_port_info_get_hoq_lifetime(p_old_pi) ||
234                             ib_port_info_get_vl_stall_count(p_pi) !=
235                             ib_port_info_get_vl_stall_count(p_old_pi))
236                                 send_set = TRUE;
237                 }
238
239                 ib_port_info_set_phy_and_overrun_err_thd(p_pi,
240                                                          sm->p_subn->opt.
241                                                          local_phy_errors_threshold,
242                                                          sm->p_subn->opt.
243                                                          overrun_errors_threshold);
244                 if (memcmp(&p_pi->error_threshold, &p_old_pi->error_threshold,
245                            sizeof(p_pi->error_threshold)))
246                         send_set = TRUE;
247
248                 /*
249                    Set the easy common parameters for all port types,
250                    then determine the neighbor MTU.
251                  */
252                 p_pi->link_width_enabled = p_old_pi->link_width_supported;
253                 if (memcmp(&p_pi->link_width_enabled,
254                            &p_old_pi->link_width_enabled,
255                            sizeof(p_pi->link_width_enabled)))
256                         send_set = TRUE;
257
258                 if (sm->p_subn->opt.force_link_speed &&
259                     (sm->p_subn->opt.force_link_speed != 15 ||
260                      ib_port_info_get_link_speed_enabled(p_pi) !=
261                      ib_port_info_get_link_speed_sup(p_pi))) {
262                         ib_port_info_set_link_speed_enabled(p_pi,
263                                                             sm->p_subn->opt.
264                                                             force_link_speed);
265                         if (memcmp(&p_pi->link_speed, &p_old_pi->link_speed,
266                                    sizeof(p_pi->link_speed)))
267                                 send_set = TRUE;
268                 }
269
270                 /* calc new op_vls and mtu */
271                 op_vls =
272                     osm_physp_calc_link_op_vls(sm->p_log, sm->p_subn, p_physp);
273                 mtu = osm_physp_calc_link_mtu(sm->p_log, p_physp);
274
275                 ib_port_info_set_neighbor_mtu(p_pi, mtu);
276                 if (ib_port_info_get_neighbor_mtu(p_pi) !=
277                     ib_port_info_get_neighbor_mtu(p_old_pi))
278                         send_set = TRUE;
279
280                 ib_port_info_set_op_vls(p_pi, op_vls);
281                 if (ib_port_info_get_op_vls(p_pi) !=
282                     ib_port_info_get_op_vls(p_old_pi))
283                         send_set = TRUE;
284
285                 /* provide the vl_high_limit from the qos mgr */
286                 if (sm->p_subn->opt.qos &&
287                     p_physp->vl_high_limit != p_old_pi->vl_high_limit) {
288                         send_set = TRUE;
289                         p_pi->vl_high_limit = p_physp->vl_high_limit;
290                 }
291         }
292
293         if (port_state != IB_LINK_NO_CHANGE &&
294             port_state != ib_port_info_get_port_state(p_old_pi)) {
295                 send_set = TRUE;
296                 if (port_state == IB_LINK_ACTIVE)
297                         context.pi_context.active_transition = TRUE;
298                 else
299                         context.pi_context.active_transition = FALSE;
300         }
301
302         context.pi_context.node_guid = osm_node_get_node_guid(p_node);
303         context.pi_context.port_guid = osm_physp_get_port_guid(p_physp);
304         context.pi_context.set_method = TRUE;
305         context.pi_context.light_sweep = FALSE;
306
307         /* We need to send the PortInfoSet request with the new sm_lid
308            in the following cases:
309            1. There is a change in the values (send_set == TRUE)
310            2. This is a switch external port (so it wasn't handled yet by
311            osm_lid_mgr) and first_time_master_sweep flag on the subnet is TRUE,
312            which means the SM just became master, and it then needs to send at
313            PortInfoSet to every port.
314          */
315         if (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH && port_num
316             && sm->p_subn->first_time_master_sweep == TRUE)
317                 send_set = TRUE;
318
319         if (send_set)
320                 status = osm_req_set(sm, osm_physp_get_dr_path_ptr(p_physp),
321                                      payload, sizeof(payload),
322                                      IB_MAD_ATTR_PORT_INFO,
323                                      cl_hton32(port_num),
324                                      CL_DISP_MSGID_NONE, &context);
325
326 Exit:
327         OSM_LOG_EXIT(sm->p_log);
328         return send_set;
329 }
330
331 /**********************************************************************
332  **********************************************************************/
333 static osm_signal_t
334 __osm_link_mgr_process_node(osm_sm_t * sm,
335                             IN osm_node_t * const p_node,
336                             IN const uint8_t link_state)
337 {
338         uint32_t i;
339         uint32_t num_physp;
340         osm_physp_t *p_physp;
341         uint8_t current_state;
342         osm_signal_t signal = OSM_SIGNAL_DONE;
343
344         OSM_LOG_ENTER(sm->p_log);
345
346         OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
347                 "Node 0x%" PRIx64 " going to %s\n",
348                 cl_ntoh64(osm_node_get_node_guid(p_node)),
349                 ib_get_port_state_str(link_state));
350
351         /*
352            Set the PortInfo for every Physical Port associated
353            with this Port.  Start iterating with port 1, since the linkstate
354            is not applicable to the management port on switches.
355          */
356         num_physp = osm_node_get_num_physp(p_node);
357         for (i = 0; i < num_physp; i++) {
358                 /*
359                    Don't bother doing anything if this Physical Port is not valid.
360                    or if the state of the port is already better then the
361                    specified state.
362                  */
363                 p_physp = osm_node_get_physp_ptr(p_node, (uint8_t) i);
364                 if (!p_physp)
365                         continue;
366
367                 current_state = osm_physp_get_port_state(p_physp);
368                 if (current_state == IB_LINK_DOWN)
369                         continue;
370
371                 /*
372                    Normally we only send state update if state is lower
373                    then required state. However, we need to send update if
374                    no state change required.
375                  */
376                 if (link_state != IB_LINK_NO_CHANGE &&
377                     link_state <= current_state)
378                         OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
379                                 "Physical port %u already %s. Skipping\n",
380                                 p_physp->port_num,
381                                 ib_get_port_state_str(current_state));
382                 else if (__osm_link_mgr_set_physp_pi(sm, p_physp, link_state))
383                         signal = OSM_SIGNAL_DONE_PENDING;
384         }
385
386         OSM_LOG_EXIT(sm->p_log);
387         return (signal);
388 }
389
390 /**********************************************************************
391  **********************************************************************/
392 osm_signal_t osm_link_mgr_process(osm_sm_t * sm, IN const uint8_t link_state)
393 {
394         cl_qmap_t *p_node_guid_tbl;
395         osm_node_t *p_node;
396         osm_signal_t signal = OSM_SIGNAL_DONE;
397
398         OSM_LOG_ENTER(sm->p_log);
399
400         p_node_guid_tbl = &sm->p_subn->node_guid_tbl;
401
402         CL_PLOCK_EXCL_ACQUIRE(sm->p_lock);
403
404         for (p_node = (osm_node_t *) cl_qmap_head(p_node_guid_tbl);
405              p_node != (osm_node_t *) cl_qmap_end(p_node_guid_tbl);
406              p_node = (osm_node_t *) cl_qmap_next(&p_node->map_item)) {
407                 if (__osm_link_mgr_process_node(sm, p_node, link_state) ==
408                     OSM_SIGNAL_DONE_PENDING)
409                         signal = OSM_SIGNAL_DONE_PENDING;
410         }
411
412         CL_PLOCK_RELEASE(sm->p_lock);
413
414         OSM_LOG_EXIT(sm->p_log);
415         return (signal);
416 }