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 multicast functions.
39 */
40
41#if HAVE_CONFIG_H
42#  include <config.h>
43#endif				/* HAVE_CONFIG_H */
44
45#include <stdlib.h>
46#include <string.h>
47#include <opensm/osm_multicast.h>
48#include <opensm/osm_mcm_port.h>
49#include <opensm/osm_mtree.h>
50#include <opensm/osm_inform.h>
51
52/**********************************************************************
53 **********************************************************************/
54void osm_mgrp_delete(IN osm_mgrp_t * const p_mgrp)
55{
56	osm_mcm_port_t *p_mcm_port;
57	osm_mcm_port_t *p_next_mcm_port;
58
59	CL_ASSERT(p_mgrp);
60
61	p_next_mcm_port =
62	    (osm_mcm_port_t *) cl_qmap_head(&p_mgrp->mcm_port_tbl);
63	while (p_next_mcm_port !=
64	       (osm_mcm_port_t *) cl_qmap_end(&p_mgrp->mcm_port_tbl)) {
65		p_mcm_port = p_next_mcm_port;
66		p_next_mcm_port =
67		    (osm_mcm_port_t *) cl_qmap_next(&p_mcm_port->map_item);
68		osm_mcm_port_delete(p_mcm_port);
69	}
70	/* destroy the mtree_node structure */
71	osm_mtree_destroy(p_mgrp->p_root);
72
73	free(p_mgrp);
74}
75
76/**********************************************************************
77 **********************************************************************/
78osm_mgrp_t *osm_mgrp_new(IN const ib_net16_t mlid)
79{
80	osm_mgrp_t *p_mgrp;
81
82	p_mgrp = (osm_mgrp_t *) malloc(sizeof(*p_mgrp));
83	if (!p_mgrp)
84		return NULL;
85
86	memset(p_mgrp, 0, sizeof(*p_mgrp));
87	cl_qmap_init(&p_mgrp->mcm_port_tbl);
88	p_mgrp->mlid = mlid;
89	p_mgrp->last_change_id = 0;
90	p_mgrp->last_tree_id = 0;
91	p_mgrp->to_be_deleted = FALSE;
92
93	return p_mgrp;
94}
95
96/**********************************************************************
97 **********************************************************************/
98static void mgrp_send_notice(osm_subn_t *subn, osm_log_t *log,
99			     osm_mgrp_t *mgrp, unsigned num)
100{
101	ib_mad_notice_attr_t notice;
102	ib_api_status_t status;
103
104	notice.generic_type = 0x83;	/* generic SubnMgt type */
105	ib_notice_set_prod_type_ho(&notice, 4);	/* A Class Manager generator */
106	notice.g_or_v.generic.trap_num = CL_HTON16(num);
107	/* The sm_base_lid is saved in network order already. */
108	notice.issuer_lid = subn->sm_base_lid;
109	/* following o14-12.1.11 and table 120 p726 */
110	/* we need to provide the MGID */
111	memcpy(&notice.data_details.ntc_64_67.gid,
112	       &mgrp->mcmember_rec.mgid, sizeof(ib_gid_t));
113
114	/* According to page 653 - the issuer gid in this case of trap
115	   is the SM gid, since the SM is the initiator of this trap. */
116	notice.issuer_gid.unicast.prefix = subn->opt.subnet_prefix;
117	notice.issuer_gid.unicast.interface_id = subn->sm_port_guid;
118
119	if ((status = osm_report_notice(log, subn, &notice)))
120		OSM_LOG(log, OSM_LOG_ERROR, "ERR 7601: "
121			"Error sending trap reports (%s)\n",
122			ib_get_err_str(status));
123}
124
125/**********************************************************************
126 **********************************************************************/
127osm_mcm_port_t *osm_mgrp_add_port(IN osm_subn_t *subn, osm_log_t *log,
128				  IN osm_mgrp_t * const p_mgrp,
129				  IN const ib_gid_t * const p_port_gid,
130				  IN const uint8_t join_state,
131				  IN boolean_t proxy_join)
132{
133	ib_net64_t port_guid;
134	osm_mcm_port_t *p_mcm_port;
135	cl_map_item_t *prev_item;
136	uint8_t prev_join_state = 0;
137	uint8_t prev_scope;
138
139	p_mcm_port = osm_mcm_port_new(p_port_gid, join_state, proxy_join);
140	if (!p_mcm_port)
141		return NULL;
142
143	port_guid = p_port_gid->unicast.interface_id;
144
145	/*
146	   prev_item = cl_qmap_insert(...)
147	   Pointer to the item in the map with the specified key.  If insertion
148	   was successful, this is the pointer to the item.  If an item with the
149	   specified key already exists in the map, the pointer to that item is
150	   returned.
151	 */
152	prev_item = cl_qmap_insert(&p_mgrp->mcm_port_tbl,
153				   port_guid, &p_mcm_port->map_item);
154
155	/* if already exists - revert the insertion and only update join state */
156	if (prev_item != &p_mcm_port->map_item) {
157		osm_mcm_port_delete(p_mcm_port);
158		p_mcm_port = (osm_mcm_port_t *) prev_item;
159
160		/*
161		   o15.0.1.11
162		   Join state of the end port should be the or of the
163		   previous setting with the current one
164		 */
165		ib_member_get_scope_state(p_mcm_port->scope_state, &prev_scope,
166					  &prev_join_state);
167		p_mcm_port->scope_state =
168		    ib_member_set_scope_state(prev_scope,
169					      prev_join_state | join_state);
170	} else {
171		/* track the fact we modified the group ports */
172		p_mgrp->last_change_id++;
173	}
174
175	if ((join_state & IB_JOIN_STATE_FULL) &&
176	    !(prev_join_state & IB_JOIN_STATE_FULL) &&
177	    (++p_mgrp->full_members == 1)) {
178		mgrp_send_notice(subn, log, p_mgrp, 66);
179		p_mgrp->to_be_deleted = 0;
180	}
181
182	return (p_mcm_port);
183}
184
185/**********************************************************************
186 **********************************************************************/
187int osm_mgrp_remove_port(osm_subn_t *subn, osm_log_t *log, osm_mgrp_t *mgrp,
188			 osm_mcm_port_t *mcm, uint8_t join_state)
189{
190	int ret;
191	uint8_t port_join_state;
192	uint8_t new_join_state;
193
194	/*
195	 * according to the same o15-0.1.14 we get the stored
196	 * JoinState and the request JoinState and they must be
197	 * opposite to leave - otherwise just update it
198	 */
199	port_join_state = mcm->scope_state & 0x0F;
200	new_join_state = port_join_state & ~join_state;
201
202	if (new_join_state) {
203		mcm->scope_state = new_join_state | (mcm->scope_state & 0xf0);
204		OSM_LOG(log, OSM_LOG_DEBUG,
205			"updating port 0x%" PRIx64 " JoinState 0x%x -> 0x%x\n",
206			cl_ntoh64(mcm->port_gid.unicast.interface_id),
207			port_join_state, new_join_state);
208		ret = 0;
209	} else {
210		cl_qmap_remove_item(&mgrp->mcm_port_tbl, &mcm->map_item);
211		OSM_LOG(log, OSM_LOG_DEBUG, "removing port 0x%" PRIx64 "\n",
212			cl_ntoh64(mcm->port_gid.unicast.interface_id));
213		osm_mcm_port_delete(mcm);
214		/* track the fact we modified the group */
215		mgrp->last_change_id++;
216		ret = 1;
217	}
218
219	/* no more full members so the group will be deleted after re-route
220	   but only if it is not a well known group */
221	if ((port_join_state & IB_JOIN_STATE_FULL) &&
222	    !(new_join_state & IB_JOIN_STATE_FULL) &&
223	    (--mgrp->full_members == 0)) {
224		mgrp_send_notice(subn, log, mgrp, 67);
225		if (!mgrp->well_known)
226			mgrp->to_be_deleted = 1;
227	}
228
229	return ret;
230}
231
232void osm_mgrp_delete_port(osm_subn_t *subn, osm_log_t *log, osm_mgrp_t *mgrp,
233			  ib_net64_t port_guid)
234{
235	cl_map_item_t *item = cl_qmap_get(&mgrp->mcm_port_tbl, port_guid);
236
237	if (item != cl_qmap_end(&mgrp->mcm_port_tbl))
238		osm_mgrp_remove_port(subn, log, mgrp, (osm_mcm_port_t *)item, 0xf);
239}
240
241/**********************************************************************
242 **********************************************************************/
243boolean_t
244osm_mgrp_is_port_present(IN const osm_mgrp_t * const p_mgrp,
245			 IN const ib_net64_t port_guid,
246			 OUT osm_mcm_port_t ** const pp_mcm_port)
247{
248	cl_map_item_t *p_map_item;
249
250	CL_ASSERT(p_mgrp);
251
252	p_map_item = cl_qmap_get(&p_mgrp->mcm_port_tbl, port_guid);
253
254	if (p_map_item != cl_qmap_end(&p_mgrp->mcm_port_tbl)) {
255		if (pp_mcm_port)
256			*pp_mcm_port = (osm_mcm_port_t *) p_map_item;
257		return TRUE;
258	}
259	if (pp_mcm_port)
260		*pp_mcm_port = NULL;
261	return FALSE;
262}
263
264/**********************************************************************
265 **********************************************************************/
266static void
267__osm_mgrp_apply_func_sub(const osm_mgrp_t * const p_mgrp,
268			  const osm_mtree_node_t * const p_mtn,
269			  osm_mgrp_func_t p_func, void *context)
270{
271	uint8_t i = 0;
272	uint8_t max_children;
273	osm_mtree_node_t *p_child_mtn;
274
275	/* Call the user, then recurse. */
276	p_func(p_mgrp, p_mtn, context);
277
278	max_children = osm_mtree_node_get_max_children(p_mtn);
279	for (i = 0; i < max_children; i++) {
280		p_child_mtn = osm_mtree_node_get_child(p_mtn, i);
281		if (p_child_mtn)
282			__osm_mgrp_apply_func_sub(p_mgrp, p_child_mtn, p_func,
283						  context);
284	}
285}
286
287/**********************************************************************
288 **********************************************************************/
289void
290osm_mgrp_apply_func(const osm_mgrp_t * const p_mgrp,
291		    osm_mgrp_func_t p_func, void *context)
292{
293	osm_mtree_node_t *p_mtn;
294
295	CL_ASSERT(p_mgrp);
296	CL_ASSERT(p_func);
297
298	p_mtn = p_mgrp->p_root;
299
300	if (p_mtn)
301		__osm_mgrp_apply_func_sub(p_mgrp, p_mtn, p_func, context);
302}
303