1/*
2 * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2011 Mellanox Technologies LTD. All rights reserved.
4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5 * Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved.
6 * Copyright (c) 2009-2011 ZIH, TU Dresden, Federal Republic of Germany. 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/*
39 * Abstract:
40 *    Implementation of osm_link_mgr_t.
41 * This file implements the Link Manager object.
42 */
43
44#if HAVE_CONFIG_H
45#  include <config.h>
46#endif				/* HAVE_CONFIG_H */
47
48#include <string.h>
49#include <iba/ib_types.h>
50#include <complib/cl_debug.h>
51#include <opensm/osm_file_ids.h>
52#define FILE_ID OSM_FILE_LINK_MGR_C
53#include <opensm/osm_sm.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#include <opensm/osm_opensm.h>
59#include <opensm/osm_db_pack.h>
60
61static uint8_t link_mgr_get_smsl(IN osm_sm_t * sm, IN osm_physp_t * p_physp)
62{
63	osm_opensm_t *p_osm = sm->p_subn->p_osm;
64	struct osm_routing_engine *re = p_osm->routing_engine_used;
65	ib_net16_t slid;
66	ib_net16_t smlid;
67	uint8_t sl;
68
69	OSM_LOG_ENTER(sm->p_log);
70
71	if (!(re && re->path_sl &&
72	      (slid = osm_physp_get_base_lid(p_physp)))) {
73		/*
74		 * Use default SL if routing engine does not provide a
75		 * path SL lookup callback.
76		 */
77		OSM_LOG_EXIT(sm->p_log);
78		return sm->p_subn->opt.sm_sl;
79	}
80
81	smlid = sm->p_subn->sm_base_lid;
82
83	/* Call into routing engine to find proper SL */
84	sl = re->path_sl(re->context, sm->p_subn->opt.sm_sl,
85			 slid, smlid);
86
87	OSM_LOG_EXIT(sm->p_log);
88	return sl;
89}
90
91static int link_mgr_set_physp_pi(osm_sm_t * sm, IN osm_physp_t * p_physp,
92				 IN uint8_t port_state)
93{
94	uint8_t payload[IB_SMP_DATA_SIZE], payload2[IB_SMP_DATA_SIZE];
95	ib_port_info_t *p_pi = (ib_port_info_t *) payload;
96	ib_mlnx_ext_port_info_t *p_epi = (ib_mlnx_ext_port_info_t *) payload2;
97	const ib_port_info_t *p_old_pi;
98	const ib_mlnx_ext_port_info_t *p_old_epi;
99	osm_madw_context_t context;
100	osm_node_t *p_node;
101	ib_api_status_t status;
102	uint8_t port_num, mtu, op_vls, smsl = OSM_DEFAULT_SL;
103	boolean_t esp0 = FALSE, send_set = FALSE, send_set2 = FALSE;
104	osm_physp_t *p_remote_physp, *physp0 = NULL;
105	int issue_ext = 0, fdr10_change = 0;
106	int ret = 0;
107	ib_net32_t attr_mod, cap_mask;
108	boolean_t update_mkey = FALSE;
109	ib_net64_t m_key = 0;
110	osm_port_t *p_port;
111
112	OSM_LOG_ENTER(sm->p_log);
113
114	p_node = osm_physp_get_node_ptr(p_physp);
115
116	p_old_pi = &p_physp->port_info;
117
118	port_num = osm_physp_get_port_num(p_physp);
119
120	memcpy(payload, p_old_pi, sizeof(ib_port_info_t));
121
122	if (osm_node_get_type(p_node) != IB_NODE_TYPE_SWITCH ||
123	    port_num == 0) {
124		/* Need to make sure LID and SMLID fields in PortInfo are not 0 */
125		if (!p_pi->base_lid) {
126			p_port = osm_get_port_by_guid(sm->p_subn,
127						      osm_physp_get_port_guid(p_physp));
128			p_pi->base_lid = p_port->lid;
129			sm->lid_mgr.dirty = TRUE;
130			send_set = TRUE;
131		}
132
133		/* we are initializing the ports with our local sm_base_lid */
134		p_pi->master_sm_base_lid = sm->p_subn->sm_base_lid;
135		if (p_pi->master_sm_base_lid != p_old_pi->master_sm_base_lid)
136			send_set = TRUE;
137	}
138
139	if (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH)
140		physp0 = osm_node_get_physp_ptr(p_node, 0);
141
142	if (port_num == 0) {
143		/*
144		   CAs don't have a port 0, and for switch port 0,
145		   we need to check if this is enhanced or base port 0.
146		   For base port 0 the following parameters are not valid
147		   (IBA 1.2.1 p.830 table 146).
148		 */
149		if (!p_node->sw) {
150			OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 4201: "
151				"Cannot find switch by guid: 0x%" PRIx64 "\n",
152				cl_ntoh64(p_node->node_info.node_guid));
153			goto Exit;
154		}
155
156		if (ib_switch_info_is_enhanced_port0(&p_node->sw->switch_info)
157		    == FALSE) {
158
159			/* Even for base port 0 we might have to set smsl
160			   (if we are using lash routing) */
161			smsl = link_mgr_get_smsl(sm, p_physp);
162			if (smsl != ib_port_info_get_master_smsl(p_old_pi)) {
163				send_set = TRUE;
164				OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
165					"Setting SMSL to %d on port 0 GUID 0x%016"
166					PRIx64 "\n", smsl,
167					cl_ntoh64(osm_physp_get_port_guid
168						  (p_physp)));
169			/* Enter if base lid and master_sm_lid didn't change */
170			} else if (send_set == FALSE) {
171				/* This means the switch doesn't support
172				   enhanced port 0 and we don't need to
173				   change SMSL. Can skip it. */
174				OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
175					"Skipping port 0, GUID 0x%016" PRIx64
176					"\n",
177					cl_ntoh64(osm_physp_get_port_guid
178						  (p_physp)));
179				goto Exit;
180			}
181		} else
182			esp0 = TRUE;
183	}
184
185	/*
186	   Should never write back a value that is bigger then 3 in
187	   the PortPhysicalState field - so can not simply copy!
188
189	   Actually we want to write there:
190	   port physical state - no change,
191	   link down default state = polling
192	   port state - as requested.
193	 */
194	p_pi->state_info2 = 0x02;
195	ib_port_info_set_port_state(p_pi, port_state);
196
197	/* Determine ports' M_Key */
198	if (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH &&
199	    osm_physp_get_port_num(p_physp) != 0)
200		m_key = ib_port_info_get_m_key(&physp0->port_info);
201	else
202		m_key = ib_port_info_get_m_key(p_pi);
203
204	/* Check whether this is base port0 smsl handling only */
205	if (port_num == 0 && esp0 == FALSE) {
206		ib_port_info_set_master_smsl(p_pi, smsl);
207		goto Send;
208	}
209
210	/*
211	   PAST THIS POINT WE ARE HANDLING EITHER A NON PORT 0 OR ENHANCED PORT 0
212	 */
213
214	if (ib_port_info_get_link_down_def_state(p_pi) !=
215	    ib_port_info_get_link_down_def_state(p_old_pi))
216		send_set = TRUE;
217
218	/* didn't get PortInfo before */
219	if (!ib_port_info_get_port_state(p_old_pi))
220		send_set = TRUE;
221
222	/* we only change port fields if we do not change state */
223	if (port_state == IB_LINK_NO_CHANGE) {
224		/* The following fields are relevant only for CA port, router, or Enh. SP0 */
225		if (osm_node_get_type(p_node) != IB_NODE_TYPE_SWITCH ||
226		    port_num == 0) {
227			p_pi->m_key = sm->p_subn->opt.m_key;
228			if (memcmp(&p_pi->m_key, &p_old_pi->m_key,
229				   sizeof(p_pi->m_key))) {
230				update_mkey = TRUE;
231				send_set = TRUE;
232			}
233
234			p_pi->subnet_prefix = sm->p_subn->opt.subnet_prefix;
235			if (memcmp(&p_pi->subnet_prefix,
236				   &p_old_pi->subnet_prefix,
237				   sizeof(p_pi->subnet_prefix)))
238				send_set = TRUE;
239
240			smsl = link_mgr_get_smsl(sm, p_physp);
241			if (smsl != ib_port_info_get_master_smsl(p_old_pi)) {
242
243				ib_port_info_set_master_smsl(p_pi, smsl);
244
245				OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
246					"Setting SMSL to %d on GUID 0x%016"
247					PRIx64 ", port %d\n", smsl,
248					cl_ntoh64(osm_physp_get_port_guid
249						  (p_physp)), port_num);
250
251				send_set = TRUE;
252			}
253
254			p_pi->m_key_lease_period =
255			    sm->p_subn->opt.m_key_lease_period;
256			if (memcmp(&p_pi->m_key_lease_period,
257				   &p_old_pi->m_key_lease_period,
258				   sizeof(p_pi->m_key_lease_period)))
259				send_set = TRUE;
260
261			p_pi->mkey_lmc = 0;
262			ib_port_info_set_mpb(p_pi, sm->p_subn->opt.m_key_protect_bits);
263			if (esp0 == FALSE || sm->p_subn->opt.lmc_esp0)
264				ib_port_info_set_lmc(p_pi, sm->p_subn->opt.lmc);
265			if (ib_port_info_get_lmc(p_old_pi) !=
266			    ib_port_info_get_lmc(p_pi) ||
267			    ib_port_info_get_mpb(p_old_pi) !=
268			    ib_port_info_get_mpb(p_pi))
269				send_set = TRUE;
270
271			ib_port_info_set_timeout(p_pi,
272						 sm->p_subn->opt.
273						 subnet_timeout);
274			if (ib_port_info_get_timeout(p_pi) !=
275			    ib_port_info_get_timeout(p_old_pi))
276				send_set = TRUE;
277		}
278
279		/*
280		   Several timeout mechanisms:
281		 */
282		p_remote_physp = osm_physp_get_remote(p_physp);
283		if (port_num != 0 && p_remote_physp) {
284			if (osm_node_get_type(osm_physp_get_node_ptr(p_physp))
285			    == IB_NODE_TYPE_ROUTER) {
286				ib_port_info_set_hoq_lifetime(p_pi,
287							      sm->p_subn->
288							      opt.
289							      leaf_head_of_queue_lifetime);
290			} else
291			    if (osm_node_get_type
292				(osm_physp_get_node_ptr(p_physp)) ==
293				IB_NODE_TYPE_SWITCH) {
294				/* Is remote end CA or router (a leaf port) ? */
295				if (osm_node_get_type
296				    (osm_physp_get_node_ptr(p_remote_physp)) !=
297				    IB_NODE_TYPE_SWITCH) {
298					ib_port_info_set_hoq_lifetime(p_pi,
299								      sm->
300								      p_subn->
301								      opt.
302								      leaf_head_of_queue_lifetime);
303					ib_port_info_set_vl_stall_count(p_pi,
304									sm->
305									p_subn->
306									opt.
307									leaf_vl_stall_count);
308				} else {
309					ib_port_info_set_hoq_lifetime(p_pi,
310								      sm->
311								      p_subn->
312								      opt.
313								      head_of_queue_lifetime);
314					ib_port_info_set_vl_stall_count(p_pi,
315									sm->
316									p_subn->
317									opt.
318									vl_stall_count);
319				}
320			}
321			if (ib_port_info_get_hoq_lifetime(p_pi) !=
322			    ib_port_info_get_hoq_lifetime(p_old_pi) ||
323			    ib_port_info_get_vl_stall_count(p_pi) !=
324			    ib_port_info_get_vl_stall_count(p_old_pi))
325				send_set = TRUE;
326		}
327
328		ib_port_info_set_phy_and_overrun_err_thd(p_pi,
329							 sm->p_subn->opt.
330							 local_phy_errors_threshold,
331							 sm->p_subn->opt.
332							 overrun_errors_threshold);
333		if (p_pi->error_threshold != p_old_pi->error_threshold)
334			send_set = TRUE;
335
336		/*
337		   Set the easy common parameters for all port types,
338		   then determine the neighbor MTU.
339		 */
340		p_pi->link_width_enabled = p_old_pi->link_width_supported;
341		if (p_pi->link_width_enabled != p_old_pi->link_width_enabled)
342			send_set = TRUE;
343
344		if (sm->p_subn->opt.force_link_speed &&
345		    (sm->p_subn->opt.force_link_speed != 15 ||
346		     ib_port_info_get_link_speed_enabled(p_pi) !=
347		     ib_port_info_get_link_speed_sup(p_pi))) {
348			ib_port_info_set_link_speed_enabled(p_pi,
349							    sm->p_subn->opt.
350							    force_link_speed);
351			if (p_pi->link_speed != p_old_pi->link_speed)
352				send_set = TRUE;
353		}
354
355		if (sm->p_subn->opt.fdr10 &&
356		    p_physp->ext_port_info.link_speed_supported & FDR10) {
357			if (sm->p_subn->opt.fdr10 == 1) { /* enable */
358				if (!(p_physp->ext_port_info.link_speed_enabled & FDR10))
359					fdr10_change = 1;
360			} else {	/* disable */
361				if (p_physp->ext_port_info.link_speed_enabled & FDR10)
362					fdr10_change = 1;
363			}
364			if (fdr10_change) {
365				p_old_epi = &p_physp->ext_port_info;
366				memcpy(payload2, p_old_epi,
367				       sizeof(ib_mlnx_ext_port_info_t));
368				p_epi->state_change_enable = 0x01;
369				if (sm->p_subn->opt.fdr10 == 1)
370					p_epi->link_speed_enabled = FDR10;
371				else
372					p_epi->link_speed_enabled = 0;
373				send_set2 = TRUE;
374			}
375		}
376
377		if (osm_node_get_type(p_physp->p_node) == IB_NODE_TYPE_SWITCH &&
378		    osm_physp_get_port_num(p_physp) != 0) {
379			cap_mask = physp0->port_info.capability_mask;
380		} else
381			cap_mask = p_pi->capability_mask;
382
383		if (cap_mask & IB_PORT_CAP_HAS_EXT_SPEEDS)
384			issue_ext = 1;
385
386		/* Do peer ports support extended link speeds ? */
387		if (port_num != 0 && p_remote_physp) {
388			osm_physp_t *rphysp0;
389			ib_net32_t rem_cap_mask;
390
391			if (osm_node_get_type(p_remote_physp->p_node) ==
392			    IB_NODE_TYPE_SWITCH) {
393				rphysp0 = osm_node_get_physp_ptr(p_remote_physp->p_node, 0);
394				rem_cap_mask = rphysp0->port_info.capability_mask;
395			} else
396				rem_cap_mask = p_remote_physp->port_info.capability_mask;
397
398			if (cap_mask & IB_PORT_CAP_HAS_EXT_SPEEDS &&
399			    rem_cap_mask & IB_PORT_CAP_HAS_EXT_SPEEDS) {
400				if (sm->p_subn->opt.force_link_speed_ext &&
401				    (sm->p_subn->opt.force_link_speed_ext != IB_LINK_SPEED_EXT_SET_LSES ||
402				     p_pi->link_speed_ext_enabled !=
403				     ib_port_info_get_link_speed_ext_sup(p_pi))) {
404					p_pi->link_speed_ext_enabled = sm->p_subn->opt.force_link_speed_ext;
405					if (p_pi->link_speed_ext_enabled !=
406					    p_old_pi->link_speed_ext_enabled)
407						send_set = TRUE;
408				}
409			}
410		}
411
412		/* calc new op_vls and mtu */
413		op_vls =
414		    osm_physp_calc_link_op_vls(sm->p_log, sm->p_subn, p_physp,
415					       ib_port_info_get_op_vls(p_old_pi));
416		mtu = osm_physp_calc_link_mtu(sm->p_log, p_physp,
417					      ib_port_info_get_neighbor_mtu(p_old_pi));
418
419		ib_port_info_set_neighbor_mtu(p_pi, mtu);
420		if (ib_port_info_get_neighbor_mtu(p_pi) !=
421		    ib_port_info_get_neighbor_mtu(p_old_pi))
422			send_set = TRUE;
423
424		ib_port_info_set_op_vls(p_pi, op_vls);
425		if (ib_port_info_get_op_vls(p_pi) !=
426		    ib_port_info_get_op_vls(p_old_pi))
427			send_set = TRUE;
428
429		/* provide the vl_high_limit from the qos mgr */
430		if (sm->p_subn->opt.qos &&
431		    p_physp->vl_high_limit != p_old_pi->vl_high_limit) {
432			send_set = TRUE;
433			p_pi->vl_high_limit = p_physp->vl_high_limit;
434		}
435	}
436
437Send:
438	context.pi_context.active_transition = FALSE;
439	if (port_state != IB_LINK_NO_CHANGE &&
440	    port_state != ib_port_info_get_port_state(p_old_pi)) {
441		send_set = TRUE;
442		if (port_state == IB_LINK_ACTIVE)
443			context.pi_context.active_transition = TRUE;
444	}
445
446	context.pi_context.node_guid = osm_node_get_node_guid(p_node);
447	context.pi_context.port_guid = osm_physp_get_port_guid(p_physp);
448	context.pi_context.set_method = TRUE;
449	context.pi_context.light_sweep = FALSE;
450	context.pi_context.client_rereg = FALSE;
451
452	/* We need to send the PortInfoSet request with the new sm_lid
453	   in the following cases:
454	   1. There is a change in the values (send_set == TRUE)
455	   2. This is a switch external port (so it wasn't handled yet by
456	   osm_lid_mgr) and first_time_master_sweep flag on the subnet is TRUE,
457	   which means the SM just became master, and it then needs to send at
458	   PortInfoSet to every port.
459	 */
460	if (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH && port_num
461	    && sm->p_subn->first_time_master_sweep == TRUE)
462		send_set = TRUE;
463
464	if (!send_set)
465		goto SEND_EPI;
466
467	attr_mod = cl_hton32(port_num);
468	if (issue_ext)
469		attr_mod |= cl_hton32(1 << 31);	/* AM SMSupportExtendedSpeeds */
470	status = osm_req_set(sm, osm_physp_get_dr_path_ptr(p_physp),
471			     payload, sizeof(payload), IB_MAD_ATTR_PORT_INFO,
472			     attr_mod, FALSE, m_key,
473			     CL_DISP_MSGID_NONE, &context);
474	if (status)
475		ret = -1;
476
477	/* If we sent a new mkey above, update our guid2mkey map
478	   now, on the assumption that the SubnSet succeeds
479	 */
480	if (update_mkey)
481		osm_db_guid2mkey_set(sm->p_subn->p_g2m,
482				     cl_ntoh64(p_physp->port_guid),
483				     cl_ntoh64(p_pi->m_key));
484
485SEND_EPI:
486	if (send_set2) {
487		status = osm_req_set(sm, osm_physp_get_dr_path_ptr(p_physp),
488				     payload2, sizeof(payload2),
489				     IB_MAD_ATTR_MLNX_EXTENDED_PORT_INFO,
490				     cl_hton32(port_num), FALSE, m_key,
491				     CL_DISP_MSGID_NONE, &context);
492		if (status)
493			ret = -1;
494	}
495
496Exit:
497	OSM_LOG_EXIT(sm->p_log);
498	return ret;
499}
500
501static int link_mgr_process_node(osm_sm_t * sm, IN osm_node_t * p_node,
502				 IN const uint8_t link_state)
503{
504	osm_physp_t *p_physp, *p_physp_remote;
505	uint32_t i, num_physp;
506	int ret = 0;
507	uint8_t current_state;
508
509	OSM_LOG_ENTER(sm->p_log);
510
511	OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
512		"Node 0x%" PRIx64 " going to %s\n",
513		cl_ntoh64(osm_node_get_node_guid(p_node)),
514		ib_get_port_state_str(link_state));
515
516	/*
517	   Set the PortInfo for every Physical Port associated
518	   with this Port.  Start iterating with port 1, since the linkstate
519	   is not applicable to the management port on switches.
520	 */
521	num_physp = osm_node_get_num_physp(p_node);
522	for (i = 0; i < num_physp; i++) {
523		/*
524		   Don't bother doing anything if this Physical Port is not valid.
525		   or if the state of the port is already better then the
526		   specified state.
527		 */
528		p_physp = osm_node_get_physp_ptr(p_node, (uint8_t) i);
529		if (!p_physp)
530			continue;
531
532		current_state = osm_physp_get_port_state(p_physp);
533		if (current_state == IB_LINK_DOWN)
534			continue;
535
536		/*
537		    Set PortState to DOWN in case Remote Physical Port is
538		    unreachable. We have to check this for all ports, except
539		    port zero.
540		 */
541		p_physp_remote = osm_physp_get_remote(p_physp);
542		if ((i != 0) && (!p_physp_remote ||
543		    !osm_physp_is_valid(p_physp_remote))) {
544			if (current_state != IB_LINK_INIT)
545				link_mgr_set_physp_pi(sm, p_physp, IB_LINK_DOWN);
546			continue;
547		}
548
549		/*
550		   Normally we only send state update if state is lower
551		   then required state. However, we need to send update if
552		   no state change required.
553		 */
554		if (link_state != IB_LINK_NO_CHANGE &&
555		    link_state <= current_state)
556			OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
557				"Physical port %u already %s. Skipping\n",
558				p_physp->port_num,
559				ib_get_port_state_str(current_state));
560		else if (link_mgr_set_physp_pi(sm, p_physp, link_state))
561			ret = -1;
562	}
563
564	OSM_LOG_EXIT(sm->p_log);
565	return ret;
566}
567
568int osm_link_mgr_process(osm_sm_t * sm, IN const uint8_t link_state)
569{
570	cl_qmap_t *p_node_guid_tbl;
571	osm_node_t *p_node;
572	int ret = 0;
573
574	OSM_LOG_ENTER(sm->p_log);
575
576	p_node_guid_tbl = &sm->p_subn->node_guid_tbl;
577
578	CL_PLOCK_EXCL_ACQUIRE(sm->p_lock);
579
580	for (p_node = (osm_node_t *) cl_qmap_head(p_node_guid_tbl);
581	     p_node != (osm_node_t *) cl_qmap_end(p_node_guid_tbl);
582	     p_node = (osm_node_t *) cl_qmap_next(&p_node->map_item))
583		if (link_mgr_process_node(sm, p_node, link_state))
584			ret = -1;
585
586	CL_PLOCK_RELEASE(sm->p_lock);
587
588	OSM_LOG_EXIT(sm->p_log);
589	return ret;
590}
591