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(¬ice, 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(¬ice.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, ¬ice))) 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