1 /* 2 * Copyright (c) 2004-2009 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 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. 6 * 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: 12 * 13 * Redistribution and use in source and binary forms, with or 14 * without modification, are permitted provided that the following 15 * conditions are met: 16 * 17 * - Redistributions of source code must retain the above 18 * copyright notice, this list of conditions and the following 19 * disclaimer. 20 * 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. 25 * 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 33 * SOFTWARE. 34 * 35 */ 36 37 #if HAVE_CONFIG_H 38 # include <config.h> 39 #endif /* HAVE_CONFIG_H */ 40 41 #include <string.h> 42 #include <iba/ib_types.h> 43 #include <complib/cl_qmap.h> 44 #include <complib/cl_passivelock.h> 45 #include <complib/cl_debug.h> 46 #include <complib/cl_qlist.h> 47 #include <opensm/osm_file_ids.h> 48 #define FILE_ID OSM_FILE_SA_PKEY_RECORD_C 49 #include <vendor/osm_vendor_api.h> 50 #include <opensm/osm_port.h> 51 #include <opensm/osm_node.h> 52 #include <opensm/osm_helper.h> 53 #include <opensm/osm_pkey.h> 54 #include <opensm/osm_sa.h> 55 56 #define SA_PKEY_RESP_SIZE SA_ITEM_RESP_SIZE(pkey_rec) 57 58 typedef struct osm_pkey_search_ctxt { 59 const ib_pkey_table_record_t *p_rcvd_rec; 60 ib_net64_t comp_mask; 61 uint16_t block_num; 62 cl_qlist_t *p_list; 63 osm_sa_t *sa; 64 const osm_physp_t *p_req_physp; 65 } osm_pkey_search_ctxt_t; 66 67 static void sa_pkey_create(IN osm_sa_t * sa, IN osm_physp_t * p_physp, 68 IN osm_pkey_search_ctxt_t * p_ctxt, 69 IN uint16_t block) 70 { 71 osm_sa_item_t *p_rec_item; 72 uint16_t lid; 73 ib_pkey_table_t *tbl; 74 75 OSM_LOG_ENTER(sa->p_log); 76 77 p_rec_item = malloc(SA_PKEY_RESP_SIZE); 78 if (p_rec_item == NULL) { 79 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4602: " 80 "rec_item alloc failed\n"); 81 goto Exit; 82 } 83 84 if (p_physp->p_node->node_info.node_type != IB_NODE_TYPE_SWITCH) 85 lid = p_physp->port_info.base_lid; 86 else 87 lid = osm_node_get_base_lid(p_physp->p_node, 0); 88 89 OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 90 "New P_Key table for: port 0x%016" PRIx64 91 ", lid %u, port %u Block:%u\n", 92 cl_ntoh64(osm_physp_get_port_guid(p_physp)), 93 cl_ntoh16(lid), osm_physp_get_port_num(p_physp), block); 94 95 memset(p_rec_item, 0, SA_PKEY_RESP_SIZE); 96 97 p_rec_item->resp.pkey_rec.lid = lid; 98 p_rec_item->resp.pkey_rec.block_num = block; 99 p_rec_item->resp.pkey_rec.port_num = osm_physp_get_port_num(p_physp); 100 /* FIXME: There are ninf.PartitionCap or swinf.PartitionEnforcementCap 101 pkey entries so everything in that range is a valid block number 102 even if opensm is not using it. Return 0. However things outside 103 that range should return no entries. Not sure how to figure that 104 here? The range of pkey_tbl can be less than the cap, so 105 this falsely triggers. */ 106 tbl = osm_pkey_tbl_block_get(osm_physp_get_pkey_tbl(p_physp), block); 107 if (tbl) 108 p_rec_item->resp.pkey_rec.pkey_tbl = *tbl; 109 110 cl_qlist_insert_tail(p_ctxt->p_list, &p_rec_item->list_item); 111 112 Exit: 113 OSM_LOG_EXIT(sa->p_log); 114 } 115 116 static void sa_pkey_check_physp(IN osm_sa_t * sa, IN osm_physp_t * p_physp, 117 osm_pkey_search_ctxt_t * p_ctxt) 118 { 119 ib_net64_t comp_mask = p_ctxt->comp_mask; 120 uint16_t block, num_blocks; 121 122 OSM_LOG_ENTER(sa->p_log); 123 124 /* we got here with the phys port - all is left is to get the right block */ 125 if (comp_mask & IB_PKEY_COMPMASK_BLOCK) { 126 sa_pkey_create(sa, p_physp, p_ctxt, p_ctxt->block_num); 127 } else { 128 num_blocks = 129 osm_pkey_tbl_get_num_blocks(osm_physp_get_pkey_tbl 130 (p_physp)); 131 for (block = 0; block < num_blocks; block++) 132 sa_pkey_create(sa, p_physp, p_ctxt, block); 133 } 134 135 OSM_LOG_EXIT(sa->p_log); 136 } 137 138 static void sa_pkey_by_comp_mask(IN osm_sa_t * sa, IN const osm_port_t * p_port, 139 osm_pkey_search_ctxt_t * p_ctxt) 140 { 141 const ib_pkey_table_record_t *p_rcvd_rec; 142 ib_net64_t comp_mask; 143 osm_physp_t *p_physp; 144 uint8_t port_num; 145 uint8_t num_ports; 146 const osm_physp_t *p_req_physp; 147 148 OSM_LOG_ENTER(sa->p_log); 149 150 p_rcvd_rec = p_ctxt->p_rcvd_rec; 151 comp_mask = p_ctxt->comp_mask; 152 port_num = p_rcvd_rec->port_num; 153 p_req_physp = p_ctxt->p_req_physp; 154 155 /* if this is a switch port we can search all ports 156 otherwise we must be looking on port 0 */ 157 if (p_port->p_node->node_info.node_type != IB_NODE_TYPE_SWITCH) { 158 /* we put it in the comp mask and port num */ 159 port_num = p_port->p_physp->port_num; 160 OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 161 "Using Physical Default Port Number: 0x%X (for End Node)\n", 162 port_num); 163 comp_mask |= IB_PKEY_COMPMASK_PORT; 164 } 165 166 if (comp_mask & IB_PKEY_COMPMASK_PORT) { 167 if (port_num < osm_node_get_num_physp(p_port->p_node)) { 168 p_physp = 169 osm_node_get_physp_ptr(p_port->p_node, port_num); 170 /* Check that the p_physp is valid, and that is shares a pkey 171 with the p_req_physp. */ 172 if (p_physp && 173 osm_physp_share_pkey(sa->p_log, p_req_physp, 174 p_physp, sa->p_subn->opt.allow_both_pkeys)) 175 sa_pkey_check_physp(sa, p_physp, p_ctxt); 176 } else { 177 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4603: " 178 "Given Physical Port Number: 0x%X is out of range should be < 0x%X\n", 179 port_num, 180 osm_node_get_num_physp(p_port->p_node)); 181 goto Exit; 182 } 183 } else { 184 num_ports = osm_node_get_num_physp(p_port->p_node); 185 for (port_num = 0; port_num < num_ports; port_num++) { 186 p_physp = 187 osm_node_get_physp_ptr(p_port->p_node, port_num); 188 if (!p_physp) 189 continue; 190 191 /* if the requester and the p_physp don't share a pkey - 192 continue */ 193 if (!osm_physp_share_pkey 194 (sa->p_log, p_req_physp, p_physp, sa->p_subn->opt.allow_both_pkeys)) 195 continue; 196 197 sa_pkey_check_physp(sa, p_physp, p_ctxt); 198 } 199 } 200 Exit: 201 OSM_LOG_EXIT(sa->p_log); 202 } 203 204 static void sa_pkey_by_comp_mask_cb(IN cl_map_item_t * p_map_item, IN void *cxt) 205 { 206 const osm_port_t *p_port = (osm_port_t *) p_map_item; 207 osm_pkey_search_ctxt_t *p_ctxt = cxt; 208 209 sa_pkey_by_comp_mask(p_ctxt->sa, p_port, p_ctxt); 210 } 211 212 void osm_pkey_rec_rcv_process(IN void *ctx, IN void *data) 213 { 214 osm_sa_t *sa = ctx; 215 osm_madw_t *p_madw = data; 216 const ib_sa_mad_t *p_rcvd_mad; 217 const ib_pkey_table_record_t *p_rcvd_rec; 218 const osm_port_t *p_port = NULL; 219 cl_qlist_t rec_list; 220 osm_pkey_search_ctxt_t context; 221 ib_net64_t comp_mask; 222 osm_physp_t *p_req_physp; 223 224 CL_ASSERT(sa); 225 226 OSM_LOG_ENTER(sa->p_log); 227 228 CL_ASSERT(p_madw); 229 230 p_rcvd_mad = osm_madw_get_sa_mad_ptr(p_madw); 231 p_rcvd_rec = 232 (ib_pkey_table_record_t *) ib_sa_mad_get_payload_ptr(p_rcvd_mad); 233 comp_mask = p_rcvd_mad->comp_mask; 234 235 CL_ASSERT(p_rcvd_mad->attr_id == IB_MAD_ATTR_PKEY_TBL_RECORD); 236 237 /* we only support SubnAdmGet and SubnAdmGetTable methods */ 238 if (p_rcvd_mad->method != IB_MAD_METHOD_GET && 239 p_rcvd_mad->method != IB_MAD_METHOD_GETTABLE) { 240 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4605: " 241 "Unsupported Method (%s) for PKeyRecord request\n", 242 ib_get_sa_method_str(p_rcvd_mad->method)); 243 osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR); 244 goto Exit; 245 } 246 247 /* 248 p922 - P_KeyTableRecords shall only be provided in response 249 to trusted requests. 250 Check that the requester is a trusted one. 251 */ 252 if (p_rcvd_mad->sm_key != sa->p_subn->opt.sa_key) { 253 /* This is not a trusted requester! */ 254 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4608: " 255 "Ignoring PKeyRecord request from non-trusted requester" 256 " with SM_Key 0x%016" PRIx64 "\n", 257 cl_ntoh64(p_rcvd_mad->sm_key)); 258 osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID); 259 goto Exit; 260 } 261 262 cl_plock_acquire(sa->p_lock); 263 264 /* update the requester physical port */ 265 p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn, 266 osm_madw_get_mad_addr_ptr 267 (p_madw)); 268 if (p_req_physp == NULL) { 269 cl_plock_release(sa->p_lock); 270 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4604: " 271 "Cannot find requester physical port\n"); 272 goto Exit; 273 } 274 OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 275 "Requester port GUID 0x%" PRIx64 "\n", 276 cl_ntoh64(osm_physp_get_port_guid(p_req_physp))); 277 278 cl_qlist_init(&rec_list); 279 280 context.p_rcvd_rec = p_rcvd_rec; 281 context.p_list = &rec_list; 282 context.comp_mask = p_rcvd_mad->comp_mask; 283 context.sa = sa; 284 context.block_num = cl_ntoh16(p_rcvd_rec->block_num); 285 context.p_req_physp = p_req_physp; 286 287 OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 288 "Got Query Lid:%u(%02X), Block:0x%02X(%02X), Port:0x%02X(%02X)\n", 289 cl_ntoh16(p_rcvd_rec->lid), 290 (comp_mask & IB_PKEY_COMPMASK_LID) != 0, p_rcvd_rec->port_num, 291 (comp_mask & IB_PKEY_COMPMASK_PORT) != 0, context.block_num, 292 (comp_mask & IB_PKEY_COMPMASK_BLOCK) != 0); 293 294 /* 295 If the user specified a LID, it obviously narrows our 296 work load, since we don't have to search every port 297 */ 298 if (comp_mask & IB_PKEY_COMPMASK_LID) { 299 p_port = osm_get_port_by_lid(sa->p_subn, p_rcvd_rec->lid); 300 if (!p_port) 301 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 460B: " 302 "No port found with LID %u\n", 303 cl_ntoh16(p_rcvd_rec->lid)); 304 else 305 sa_pkey_by_comp_mask(sa, p_port, &context); 306 } else 307 cl_qmap_apply_func(&sa->p_subn->port_guid_tbl, 308 sa_pkey_by_comp_mask_cb, &context); 309 310 cl_plock_release(sa->p_lock); 311 312 osm_sa_respond(sa, p_madw, sizeof(ib_pkey_table_record_t), &rec_list); 313 314 Exit: 315 OSM_LOG_EXIT(sa->p_log); 316 } 317