2 * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2015 Mellanox Technologies LTD. All rights reserved.
4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
7 * This software is available to you under a choice of one of two
8 * licenses. You may choose to be licensed under the terms of the GNU
9 * General Public License (GPL) Version 2, available from the file
10 * COPYING in the main directory of this source tree, or the
11 * OpenIB.org BSD license below:
13 * Redistribution and use in source and binary forms, with or
14 * without modification, are permitted provided that the following
17 * - Redistributions of source code must retain the above
18 * copyright notice, this list of conditions and the following
21 * - Redistributions in binary form must reproduce the above
22 * copyright notice, this list of conditions and the following
23 * disclaimer in the documentation and/or other materials
24 * provided with the distribution.
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39 * Implementation of multicast functions.
44 #endif /* HAVE_CONFIG_H */
48 #include <arpa/inet.h>
49 #include <sys/socket.h>
50 #include <opensm/osm_file_ids.h>
51 #define FILE_ID OSM_FILE_MULTICAST_C
52 #include <opensm/osm_multicast.h>
53 #include <opensm/osm_mcm_port.h>
54 #include <opensm/osm_mtree.h>
55 #include <opensm/osm_inform.h>
56 #include <opensm/osm_opensm.h>
58 static osm_mgrp_box_t *mgrp_box_new(uint16_t mlid)
60 osm_mgrp_box_t *mbox = malloc(sizeof(*mbox));
64 memset(mbox, 0, sizeof(*mbox));
66 cl_qlist_init(&mbox->mgrp_list);
71 void mgrp_box_delete(osm_mgrp_box_t *mbox)
73 osm_mtree_destroy(mbox->root);
77 void mgrp_delete(IN osm_mgrp_t * p_mgrp)
79 osm_mcm_alias_guid_t *p_mcm_alias_guid, *p_next_mcm_alias_guid;
80 osm_mcm_port_t *p_mcm_port, *p_next_mcm_port;
84 p_next_mcm_alias_guid =
85 (osm_mcm_alias_guid_t *) cl_qmap_head(&p_mgrp->mcm_alias_port_tbl);
86 while (p_next_mcm_alias_guid !=
87 (osm_mcm_alias_guid_t *) cl_qmap_end(&p_mgrp->mcm_alias_port_tbl)) {
88 p_mcm_alias_guid = p_next_mcm_alias_guid;
89 p_next_mcm_alias_guid =
90 (osm_mcm_alias_guid_t *) cl_qmap_next(&p_mcm_alias_guid->map_item);
91 osm_mcm_alias_guid_delete(&p_mcm_alias_guid);
95 (osm_mcm_port_t *) cl_qmap_head(&p_mgrp->mcm_port_tbl);
96 while (p_next_mcm_port !=
97 (osm_mcm_port_t *) cl_qmap_end(&p_mgrp->mcm_port_tbl)) {
98 p_mcm_port = p_next_mcm_port;
100 (osm_mcm_port_t *) cl_qmap_next(&p_mcm_port->map_item);
101 osm_mcm_port_delete(p_mcm_port);
107 void osm_mgrp_box_delete(osm_mgrp_box_t *mbox)
111 while (cl_qlist_count(&mbox->mgrp_list)) {
112 mgrp = cl_item_obj(cl_qlist_remove_head(&mbox->mgrp_list),
116 mgrp_box_delete(mbox);
119 osm_mgrp_t *osm_mgrp_new(IN osm_subn_t * subn, IN ib_net16_t mlid,
120 IN ib_member_rec_t * mcmr)
123 osm_mgrp_box_t *mbox;
125 p_mgrp = (osm_mgrp_t *) malloc(sizeof(*p_mgrp));
129 memset(p_mgrp, 0, sizeof(*p_mgrp));
130 cl_qmap_init(&p_mgrp->mcm_port_tbl);
131 cl_qmap_init(&p_mgrp->mcm_alias_port_tbl);
133 p_mgrp->mcmember_rec = *mcmr;
135 mbox = osm_get_mbox_by_mlid(subn, p_mgrp->mlid);
136 if (!mbox && !(mbox = mgrp_box_new(cl_ntoh16(p_mgrp->mlid)))) {
141 cl_qlist_insert_tail(&mbox->mgrp_list, &p_mgrp->list_item);
142 subn->mboxes[mbox->mlid - IB_LID_MCAST_START_HO] = mbox;
144 cl_fmap_insert(&subn->mgrp_mgid_tbl, &p_mgrp->mcmember_rec.mgid,
147 subn->p_osm->sa.dirty = TRUE;
151 void osm_mgrp_cleanup(osm_subn_t * subn, osm_mgrp_t * mgrp)
153 osm_mgrp_box_t *mbox;
154 osm_mcm_alias_guid_t *mcm_alias_guid;
155 osm_mcm_port_t *mcm_port;
157 if (mgrp->full_members)
160 while (cl_qmap_count(&mgrp->mcm_alias_port_tbl)) {
161 mcm_alias_guid = (osm_mcm_alias_guid_t *) cl_qmap_head(&mgrp->mcm_alias_port_tbl);
162 cl_qmap_remove_item(&mgrp->mcm_alias_port_tbl, &mcm_alias_guid->map_item);
163 osm_mcm_alias_guid_delete(&mcm_alias_guid);
166 while (cl_qmap_count(&mgrp->mcm_port_tbl)) {
167 mcm_port = (osm_mcm_port_t *) cl_qmap_head(&mgrp->mcm_port_tbl);
168 cl_qmap_remove_item(&mgrp->mcm_port_tbl, &mcm_port->map_item);
169 cl_qlist_remove_item(&mcm_port->port->mcm_list,
170 &mcm_port->list_item);
171 osm_mcm_port_delete(mcm_port);
174 if (mgrp->well_known)
177 cl_fmap_remove_item(&subn->mgrp_mgid_tbl, &mgrp->map_item);
179 mbox = osm_get_mbox_by_mlid(subn, mgrp->mlid);
180 cl_qlist_remove_item(&mbox->mgrp_list, &mgrp->list_item);
181 if (cl_is_qlist_empty(&mbox->mgrp_list)) {
182 subn->mboxes[cl_ntoh16(mgrp->mlid) - IB_LID_MCAST_START_HO] = NULL;
183 mgrp_box_delete(mbox);
187 subn->p_osm->sa.dirty = TRUE;
190 static void mgrp_send_notice(osm_subn_t * subn, osm_log_t * log,
191 osm_mgrp_t * mgrp, unsigned num)
193 ib_mad_notice_attr_t notice;
194 ib_api_status_t status;
196 notice.generic_type = 0x80 | IB_NOTICE_TYPE_SUBN_MGMT; /* is generic subn mgt type */
197 ib_notice_set_prod_type_ho(¬ice, 4); /* A Class Manager generator */
198 notice.g_or_v.generic.trap_num = CL_HTON16(num);
199 /* The sm_base_lid is saved in network order already. */
200 notice.issuer_lid = subn->sm_base_lid;
201 /* following o14-12.1.11 and table 120 p726 */
202 /* we need to provide the MGID */
203 memcpy(¬ice.data_details.ntc_64_67.gid,
204 &mgrp->mcmember_rec.mgid, sizeof(ib_gid_t));
206 /* According to page 653 - the issuer gid in this case of trap
207 is the SM gid, since the SM is the initiator of this trap. */
208 notice.issuer_gid.unicast.prefix = subn->opt.subnet_prefix;
209 notice.issuer_gid.unicast.interface_id = subn->sm_port_guid;
211 if ((status = osm_report_notice(log, subn, ¬ice)))
212 OSM_LOG(log, OSM_LOG_ERROR, "ERR 7601: "
213 "Error sending trap reports (%s)\n",
214 ib_get_err_str(status));
217 static boolean_t is_qmap_empty_for_port(IN const cl_qmap_t * const p_map,
218 IN const osm_port_t *port)
222 osm_mcm_alias_guid_t *mcm_alias_guid;
224 for (item = cl_qmap_head(p_map); item != cl_qmap_end(p_map);
225 item = cl_qmap_next(item)) {
226 mcm_alias_guid = (osm_mcm_alias_guid_t *) item;
227 if (mcm_alias_guid->p_base_mcm_port->port == port) {
236 static boolean_t is_qmap_empty_for_mcm_port(IN const cl_qmap_t * const p_map,
237 IN const osm_mcm_port_t *mcm_port)
241 osm_mcm_alias_guid_t *mcm_alias_guid;
243 for (item = cl_qmap_head(p_map); item != cl_qmap_end(p_map);
244 item = cl_qmap_next(item)) {
245 mcm_alias_guid = (osm_mcm_alias_guid_t *) item;
246 if (mcm_alias_guid->p_base_mcm_port == mcm_port) {
254 static osm_mcm_alias_guid_t *insert_alias_guid(IN osm_mgrp_t * mgrp,
255 IN osm_mcm_alias_guid_t * p_mcm_alias_guid)
257 osm_mcm_alias_guid_t *p_mcm_alias_guid_check;
259 /* insert into mcm alias guid table */
260 p_mcm_alias_guid_check =
261 (osm_mcm_alias_guid_t *) cl_qmap_insert(&mgrp->mcm_alias_port_tbl,
262 p_mcm_alias_guid->alias_guid,
263 &p_mcm_alias_guid->map_item);
264 if (p_mcm_alias_guid_check != (osm_mcm_alias_guid_t *) &p_mcm_alias_guid->map_item) {
265 /* alias GUID is a duplicate */
266 osm_mcm_alias_guid_delete(&p_mcm_alias_guid);
267 return p_mcm_alias_guid_check;
272 osm_mcm_port_t *osm_mgrp_add_port(IN osm_subn_t * subn, osm_log_t * log,
273 IN osm_mgrp_t * mgrp, osm_port_t *port,
274 IN ib_member_rec_t *mcmr, IN boolean_t proxy)
276 osm_mcm_port_t *mcm_port;
277 osm_mcm_alias_guid_t *p_mcm_alias_guid, *p_mcm_alias_guid_check;
278 cl_map_item_t *prev_item;
279 uint8_t prev_join_state = 0, join_state = mcmr->scope_state;
282 if (OSM_LOG_IS_ACTIVE_V2(log, OSM_LOG_VERBOSE)) {
283 char gid_str[INET6_ADDRSTRLEN];
284 OSM_LOG(log, OSM_LOG_VERBOSE, "GUID 0x%016" PRIx64
285 " Port 0x%016" PRIx64 " joining "
286 "MC group %s (mlid 0x%x)\n",
287 cl_ntoh64(mcmr->port_gid.unicast.interface_id),
288 cl_ntoh64(port->guid),
289 inet_ntop(AF_INET6, mgrp->mcmember_rec.mgid.raw,
290 gid_str, sizeof(gid_str)),
291 cl_ntoh16(mgrp->mlid));
294 mcm_port = osm_mcm_port_new(port, mgrp);
298 p_mcm_alias_guid = osm_mcm_alias_guid_new(mcm_port, mcmr, proxy);
299 if (!p_mcm_alias_guid) {
300 osm_mcm_port_delete(mcm_port);
305 prev_item = cl_qmap_insert(...)
306 Pointer to the item in the map with the specified key. If insertion
307 was successful, this is the pointer to the item. If an item with the
308 specified key already exists in the map, the pointer to that item is
311 prev_item = cl_qmap_insert(&mgrp->mcm_port_tbl, port->guid,
312 &mcm_port->map_item);
314 if (prev_item != &mcm_port->map_item) { /* mcm port already exists */
315 osm_mcm_port_delete(mcm_port);
316 mcm_port = (osm_mcm_port_t *) prev_item;
318 p_mcm_alias_guid->p_base_mcm_port = (osm_mcm_port_t *) prev_item;
319 p_mcm_alias_guid_check = insert_alias_guid(mgrp, p_mcm_alias_guid);
320 if (p_mcm_alias_guid_check) { /* alias GUID already exists */
321 p_mcm_alias_guid = p_mcm_alias_guid_check;
322 ib_member_get_scope_state(p_mcm_alias_guid->scope_state,
323 &prev_scope, &prev_join_state);
324 p_mcm_alias_guid->scope_state =
325 ib_member_set_scope_state(prev_scope,
326 prev_join_state | join_state);
329 insert_alias_guid(mgrp, p_mcm_alias_guid);
330 cl_qlist_insert_tail(&port->mcm_list, &mcm_port->list_item);
331 osm_sm_reroute_mlid(&subn->p_osm->sm, mgrp->mlid);
334 /* o15.0.1.11: copy the join state */
335 mcmr->scope_state = p_mcm_alias_guid->scope_state;
337 if ((join_state & IB_JOIN_STATE_FULL) &&
338 !(prev_join_state & IB_JOIN_STATE_FULL) &&
339 ++mgrp->full_members == 1)
340 mgrp_send_notice(subn, log, mgrp, SM_MGID_CREATED_TRAP); /* 66 */
342 subn->p_osm->sa.dirty = TRUE;
346 boolean_t osm_mgrp_remove_port(osm_subn_t * subn, osm_log_t * log, osm_mgrp_t * mgrp,
347 osm_mcm_alias_guid_t * mcm_alias_guid,
348 ib_member_rec_t *mcmr)
350 uint8_t join_state = mcmr->scope_state & 0xf;
351 uint8_t port_join_state, new_join_state;
352 boolean_t mgrp_deleted = FALSE;
355 * according to the same o15-0.1.14 we get the stored
356 * JoinState and the request JoinState and they must be
357 * opposite to leave - otherwise just update it
359 port_join_state = mcm_alias_guid->scope_state & 0x0F;
360 new_join_state = port_join_state & ~join_state;
362 if (OSM_LOG_IS_ACTIVE_V2(log, OSM_LOG_VERBOSE)) {
363 char gid_str[INET6_ADDRSTRLEN];
364 OSM_LOG(log, OSM_LOG_VERBOSE,
365 "GUID 0x%" PRIx64 " Port 0x%" PRIx64
366 " leaving MC group %s (mlid 0x%x)\n",
367 cl_ntoh64(mcm_alias_guid->alias_guid),
368 cl_ntoh64(mcm_alias_guid->p_base_mcm_port->port->guid),
369 inet_ntop(AF_INET6, mgrp->mcmember_rec.mgid.raw,
370 gid_str, sizeof(gid_str)),
371 cl_ntoh16(mgrp->mlid));
374 if (new_join_state & IB_JOIN_STATE_FULL ||
376 (mgrp->full_members > (port_join_state & IB_JOIN_STATE_FULL) ? 1 : 0))) {
377 mcm_alias_guid->scope_state =
378 new_join_state | (mcm_alias_guid->scope_state & 0xf0);
379 OSM_LOG(log, OSM_LOG_DEBUG,
380 "updating GUID 0x%" PRIx64 " port 0x%" PRIx64
381 " JoinState 0x%x -> 0x%x\n",
382 cl_ntoh64(mcm_alias_guid->alias_guid),
383 cl_ntoh64(mcm_alias_guid->p_base_mcm_port->port->guid),
384 port_join_state, new_join_state);
385 mcmr->scope_state = mcm_alias_guid->scope_state;
387 mcmr->scope_state = mcm_alias_guid->scope_state & 0xf0;
388 OSM_LOG(log, OSM_LOG_DEBUG, "removing alias GUID 0x%" PRIx64 "\n",
389 cl_ntoh64(mcm_alias_guid->alias_guid));
390 cl_qmap_remove_item(&mgrp->mcm_alias_port_tbl,
391 &mcm_alias_guid->map_item);
392 if (is_qmap_empty_for_port(&mgrp->mcm_alias_port_tbl,
393 mcm_alias_guid->p_base_mcm_port->port)) { /* last alias in mcast group for this port */
394 OSM_LOG(log, OSM_LOG_DEBUG, "removing port 0x%" PRIx64 "\n",
395 cl_ntoh64(mcm_alias_guid->p_base_mcm_port->port->guid));
396 cl_qmap_remove_item(&mgrp->mcm_port_tbl,
397 &mcm_alias_guid->p_base_mcm_port->map_item);
398 cl_qlist_remove_item(&mcm_alias_guid->p_base_mcm_port->port->mcm_list,
399 &mcm_alias_guid->p_base_mcm_port->list_item);
400 if (is_qmap_empty_for_mcm_port(&mgrp->mcm_alias_port_tbl,
401 mcm_alias_guid->p_base_mcm_port)) /* last alias in mcast group for this mcm port */
402 osm_mcm_port_delete(mcm_alias_guid->p_base_mcm_port);
403 osm_sm_reroute_mlid(&subn->p_osm->sm, mgrp->mlid);
405 osm_mcm_alias_guid_delete(&mcm_alias_guid);
408 /* no more full members so the group will be deleted after re-route
409 but only if it is not a well known group */
410 if ((port_join_state & IB_JOIN_STATE_FULL) &&
411 !(new_join_state & IB_JOIN_STATE_FULL) &&
412 --mgrp->full_members == 0) {
413 mgrp_send_notice(subn, log, mgrp, SM_MGID_DESTROYED_TRAP); /* 67 */
414 osm_mgrp_cleanup(subn, mgrp);
418 subn->p_osm->sa.dirty = TRUE;
420 return (mgrp_deleted);
423 void osm_mgrp_delete_port(osm_subn_t * subn, osm_log_t * log, osm_mgrp_t * mgrp,
426 osm_mcm_alias_guid_t *mcm_alias_guid, *next_mcm_alias_guid;
427 ib_member_rec_t mcmrec;
428 boolean_t mgrp_deleted = FALSE;
430 next_mcm_alias_guid = (osm_mcm_alias_guid_t *) cl_qmap_head(&mgrp->mcm_alias_port_tbl);
431 while (next_mcm_alias_guid != (osm_mcm_alias_guid_t *) cl_qmap_end(&mgrp->mcm_alias_port_tbl) &&
433 mcm_alias_guid = next_mcm_alias_guid;
434 next_mcm_alias_guid = (osm_mcm_alias_guid_t *) cl_qmap_next(&next_mcm_alias_guid->map_item);
435 if (mcm_alias_guid->p_base_mcm_port->port == port) {
436 mcmrec.scope_state = 0xf;
437 mgrp_deleted = osm_mgrp_remove_port(subn, log, mgrp, mcm_alias_guid,
443 osm_mcm_port_t *osm_mgrp_get_mcm_port(IN const osm_mgrp_t * p_mgrp,
444 IN ib_net64_t port_guid)
446 cl_map_item_t *item = cl_qmap_get(&p_mgrp->mcm_port_tbl, port_guid);
447 if (item != cl_qmap_end(&p_mgrp->mcm_port_tbl))
448 return (osm_mcm_port_t *) item;
452 osm_mcm_alias_guid_t *osm_mgrp_get_mcm_alias_guid(IN const osm_mgrp_t * p_mgrp,
453 IN ib_net64_t port_guid)
455 cl_map_item_t *item = cl_qmap_get(&p_mgrp->mcm_alias_port_tbl,
457 if (item != cl_qmap_end(&p_mgrp->mcm_alias_port_tbl))
458 return (osm_mcm_alias_guid_t *) item;