1219820Sjeff/* 2219820Sjeff * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved. 3219820Sjeff * Copyright (c) 2002-2006 Mellanox Technologies LTD. All rights reserved. 4219820Sjeff * Copyright (c) 1996-2003 Intel Corporation. All rights reserved. 5219820Sjeff * Copyright (c) 2008 Xsigo Systems Inc. All rights reserved. 6219820Sjeff * 7219820Sjeff * This software is available to you under a choice of one of two 8219820Sjeff * licenses. You may choose to be licensed under the terms of the GNU 9219820Sjeff * General Public License (GPL) Version 2, available from the file 10219820Sjeff * COPYING in the main directory of this source tree, or the 11219820Sjeff * OpenIB.org BSD license below: 12219820Sjeff * 13219820Sjeff * Redistribution and use in source and binary forms, with or 14219820Sjeff * without modification, are permitted provided that the following 15219820Sjeff * conditions are met: 16219820Sjeff * 17219820Sjeff * - Redistributions of source code must retain the above 18219820Sjeff * copyright notice, this list of conditions and the following 19219820Sjeff * disclaimer. 20219820Sjeff * 21219820Sjeff * - Redistributions in binary form must reproduce the above 22219820Sjeff * copyright notice, this list of conditions and the following 23219820Sjeff * disclaimer in the documentation and/or other materials 24219820Sjeff * provided with the distribution. 25219820Sjeff * 26219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 29219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 30219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 31219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 32219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33219820Sjeff * SOFTWARE. 34219820Sjeff * 35219820Sjeff */ 36219820Sjeff 37219820Sjeff/* 38219820Sjeff * Abstract: 39219820Sjeff * Implementation of osm_pr_rcv_t. 40219820Sjeff * This object represents the PathRecord Receiver object. 41219820Sjeff * This object is part of the opensm family of objects. 42219820Sjeff */ 43219820Sjeff 44219820Sjeff#if HAVE_CONFIG_H 45219820Sjeff# include <config.h> 46219820Sjeff#endif /* HAVE_CONFIG_H */ 47219820Sjeff 48219820Sjeff#include <string.h> 49219820Sjeff#include <arpa/inet.h> 50219820Sjeff#include <iba/ib_types.h> 51219820Sjeff#include <complib/cl_qmap.h> 52219820Sjeff#include <complib/cl_passivelock.h> 53219820Sjeff#include <complib/cl_debug.h> 54219820Sjeff#include <complib/cl_qlist.h> 55219820Sjeff#include <vendor/osm_vendor_api.h> 56219820Sjeff#include <opensm/osm_base.h> 57219820Sjeff#include <opensm/osm_port.h> 58219820Sjeff#include <opensm/osm_node.h> 59219820Sjeff#include <opensm/osm_switch.h> 60219820Sjeff#include <opensm/osm_helper.h> 61219820Sjeff#include <opensm/osm_pkey.h> 62219820Sjeff#include <opensm/osm_multicast.h> 63219820Sjeff#include <opensm/osm_partition.h> 64219820Sjeff#include <opensm/osm_opensm.h> 65219820Sjeff#include <opensm/osm_qos_policy.h> 66219820Sjeff#include <opensm/osm_sa.h> 67219820Sjeff#include <opensm/osm_router.h> 68219820Sjeff#include <opensm/osm_prefix_route.h> 69219820Sjeff 70219820Sjeff#include <sys/socket.h> 71219820Sjeff 72219820Sjeffextern uint8_t osm_get_lash_sl(osm_opensm_t * p_osm, 73219820Sjeff const osm_port_t * p_src_port, 74219820Sjeff const osm_port_t * p_dst_port); 75219820Sjeff 76219820Sjefftypedef struct osm_pr_item { 77219820Sjeff cl_list_item_t list_item; 78219820Sjeff ib_path_rec_t path_rec; 79219820Sjeff} osm_pr_item_t; 80219820Sjeff 81219820Sjefftypedef struct osm_path_parms { 82219820Sjeff ib_net16_t pkey; 83219820Sjeff uint8_t mtu; 84219820Sjeff uint8_t rate; 85219820Sjeff uint8_t sl; 86219820Sjeff uint8_t pkt_life; 87219820Sjeff boolean_t reversible; 88219820Sjeff} osm_path_parms_t; 89219820Sjeff 90219820Sjeffstatic const ib_gid_t zero_gid = { {0x00, 0x00, 0x00, 0x00, 91219820Sjeff 0x00, 0x00, 0x00, 0x00, 92219820Sjeff 0x00, 0x00, 0x00, 0x00, 93219820Sjeff 0x00, 0x00, 0x00, 0x00}, 94219820Sjeff}; 95219820Sjeff 96219820Sjeff/********************************************************************** 97219820Sjeff **********************************************************************/ 98219820Sjeffstatic inline boolean_t 99219820Sjeff__osm_sa_path_rec_is_tavor_port(IN const osm_port_t * const p_port) 100219820Sjeff{ 101219820Sjeff osm_node_t const *p_node; 102219820Sjeff ib_net32_t vend_id; 103219820Sjeff 104219820Sjeff p_node = p_port->p_node; 105219820Sjeff vend_id = ib_node_info_get_vendor_id(&p_node->node_info); 106219820Sjeff 107219820Sjeff return ((p_node->node_info.device_id == CL_HTON16(23108)) && 108219820Sjeff ((vend_id == CL_HTON32(OSM_VENDOR_ID_MELLANOX)) || 109219820Sjeff (vend_id == CL_HTON32(OSM_VENDOR_ID_TOPSPIN)) || 110219820Sjeff (vend_id == CL_HTON32(OSM_VENDOR_ID_SILVERSTORM)) || 111219820Sjeff (vend_id == CL_HTON32(OSM_VENDOR_ID_VOLTAIRE)))); 112219820Sjeff} 113219820Sjeff 114219820Sjeff/********************************************************************** 115219820Sjeff **********************************************************************/ 116219820Sjeffstatic boolean_t 117219820Sjeff__osm_sa_path_rec_apply_tavor_mtu_limit(IN const ib_path_rec_t * const p_pr, 118219820Sjeff IN const osm_port_t * const p_src_port, 119219820Sjeff IN const osm_port_t * const p_dest_port, 120219820Sjeff IN const ib_net64_t comp_mask) 121219820Sjeff{ 122219820Sjeff uint8_t required_mtu; 123219820Sjeff 124219820Sjeff /* only if at least one of the ports is a Tavor device */ 125219820Sjeff if (!__osm_sa_path_rec_is_tavor_port(p_src_port) && 126219820Sjeff !__osm_sa_path_rec_is_tavor_port(p_dest_port)) 127219820Sjeff return (FALSE); 128219820Sjeff 129219820Sjeff /* 130219820Sjeff we can apply the patch if either: 131219820Sjeff 1. No MTU required 132219820Sjeff 2. Required MTU < 133219820Sjeff 3. Required MTU = 1K or 512 or 256 134219820Sjeff 4. Required MTU > 256 or 512 135219820Sjeff */ 136219820Sjeff required_mtu = ib_path_rec_mtu(p_pr); 137219820Sjeff if ((comp_mask & IB_PR_COMPMASK_MTUSELEC) && 138219820Sjeff (comp_mask & IB_PR_COMPMASK_MTU)) { 139219820Sjeff switch (ib_path_rec_mtu_sel(p_pr)) { 140219820Sjeff case 0: /* must be greater than */ 141219820Sjeff case 2: /* exact match */ 142219820Sjeff if (IB_MTU_LEN_1024 < required_mtu) 143219820Sjeff return (FALSE); 144219820Sjeff break; 145219820Sjeff 146219820Sjeff case 1: /* must be less than */ 147219820Sjeff /* can't be disqualified by this one */ 148219820Sjeff break; 149219820Sjeff 150219820Sjeff case 3: /* largest available */ 151219820Sjeff /* the ULP intentionally requested */ 152219820Sjeff /* the largest MTU possible */ 153219820Sjeff return (FALSE); 154219820Sjeff break; 155219820Sjeff 156219820Sjeff default: 157219820Sjeff /* if we're here, there's a bug in ib_path_rec_mtu_sel() */ 158219820Sjeff CL_ASSERT(FALSE); 159219820Sjeff break; 160219820Sjeff } 161219820Sjeff } 162219820Sjeff 163219820Sjeff return (TRUE); 164219820Sjeff} 165219820Sjeff 166219820Sjeff/********************************************************************** 167219820Sjeff **********************************************************************/ 168219820Sjeffstatic ib_api_status_t 169219820Sjeff__osm_pr_rcv_get_path_parms(IN osm_sa_t * sa, 170219820Sjeff IN const ib_path_rec_t * const p_pr, 171219820Sjeff IN const osm_port_t * const p_src_port, 172219820Sjeff IN const osm_port_t * const p_dest_port, 173219820Sjeff IN const uint16_t dest_lid_ho, 174219820Sjeff IN const ib_net64_t comp_mask, 175219820Sjeff OUT osm_path_parms_t * const p_parms) 176219820Sjeff{ 177219820Sjeff const osm_node_t *p_node; 178219820Sjeff const osm_physp_t *p_physp; 179219820Sjeff const osm_physp_t *p_src_physp; 180219820Sjeff const osm_physp_t *p_dest_physp; 181219820Sjeff const osm_prtn_t *p_prtn = NULL; 182219820Sjeff osm_opensm_t *p_osm; 183219820Sjeff const ib_port_info_t *p_pi; 184219820Sjeff ib_api_status_t status = IB_SUCCESS; 185219820Sjeff ib_net16_t pkey; 186219820Sjeff uint8_t mtu; 187219820Sjeff uint8_t rate; 188219820Sjeff uint8_t pkt_life; 189219820Sjeff uint8_t required_mtu; 190219820Sjeff uint8_t required_rate; 191219820Sjeff uint8_t required_pkt_life; 192219820Sjeff uint8_t sl; 193219820Sjeff uint8_t in_port_num; 194219820Sjeff ib_net16_t dest_lid; 195219820Sjeff uint8_t i; 196219820Sjeff ib_slvl_table_t *p_slvl_tbl = NULL; 197219820Sjeff osm_qos_level_t *p_qos_level = NULL; 198219820Sjeff uint16_t valid_sl_mask = 0xffff; 199219820Sjeff int is_lash; 200219820Sjeff 201219820Sjeff OSM_LOG_ENTER(sa->p_log); 202219820Sjeff 203219820Sjeff dest_lid = cl_hton16(dest_lid_ho); 204219820Sjeff 205219820Sjeff p_dest_physp = p_dest_port->p_physp; 206219820Sjeff p_physp = p_src_port->p_physp; 207219820Sjeff p_src_physp = p_physp; 208219820Sjeff p_pi = &p_physp->port_info; 209219820Sjeff p_osm = sa->p_subn->p_osm; 210219820Sjeff 211219820Sjeff mtu = ib_port_info_get_mtu_cap(p_pi); 212219820Sjeff rate = ib_port_info_compute_rate(p_pi); 213219820Sjeff 214219820Sjeff /* 215219820Sjeff Mellanox Tavor device performance is better using 1K MTU. 216219820Sjeff If required MTU and MTU selector are such that 1K is OK 217219820Sjeff and at least one end of the path is Tavor we override the 218219820Sjeff port MTU with 1K. 219219820Sjeff */ 220219820Sjeff if (sa->p_subn->opt.enable_quirks && 221219820Sjeff __osm_sa_path_rec_apply_tavor_mtu_limit(p_pr, p_src_port, 222219820Sjeff p_dest_port, comp_mask)) 223219820Sjeff if (mtu > IB_MTU_LEN_1024) { 224219820Sjeff mtu = IB_MTU_LEN_1024; 225219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 226219820Sjeff "Optimized Path MTU to 1K for Mellanox Tavor device\n"); 227219820Sjeff } 228219820Sjeff 229219820Sjeff /* 230219820Sjeff Walk the subnet object from source to destination, 231219820Sjeff tracking the most restrictive rate and mtu values along the way... 232219820Sjeff 233219820Sjeff If source port node is a switch, then p_physp should 234219820Sjeff point to the port that routes the destination lid 235219820Sjeff */ 236219820Sjeff 237219820Sjeff p_node = osm_physp_get_node_ptr(p_physp); 238219820Sjeff 239219820Sjeff if (p_node->sw) { 240219820Sjeff /* 241219820Sjeff * Source node is a switch. 242219820Sjeff * Make sure that p_physp points to the out port of the 243219820Sjeff * switch that routes to the destination lid (dest_lid_ho) 244219820Sjeff */ 245219820Sjeff p_physp = osm_switch_get_route_by_lid(p_node->sw, dest_lid); 246219820Sjeff if (p_physp == 0) { 247219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F02: " 248219820Sjeff "Cannot find routing to LID %u from switch for GUID 0x%016" 249219820Sjeff PRIx64 "\n", dest_lid_ho, 250219820Sjeff cl_ntoh64(osm_node_get_node_guid(p_node))); 251219820Sjeff status = IB_NOT_FOUND; 252219820Sjeff goto Exit; 253219820Sjeff } 254219820Sjeff } 255219820Sjeff 256219820Sjeff if (sa->p_subn->opt.qos) { 257219820Sjeff 258219820Sjeff /* 259219820Sjeff * Whether this node is switch or CA, the IN port for 260219820Sjeff * the sl2vl table is 0, because this is a source node. 261219820Sjeff */ 262219820Sjeff p_slvl_tbl = osm_physp_get_slvl_tbl(p_physp, 0); 263219820Sjeff 264219820Sjeff /* update valid SLs that still exist on this route */ 265219820Sjeff for (i = 0; i < IB_MAX_NUM_VLS; i++) { 266219820Sjeff if (valid_sl_mask & (1 << i) && 267219820Sjeff ib_slvl_table_get(p_slvl_tbl, i) == IB_DROP_VL) 268219820Sjeff valid_sl_mask &= ~(1 << i); 269219820Sjeff } 270219820Sjeff if (!valid_sl_mask) { 271219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 272219820Sjeff "All the SLs lead to VL15 on this path\n"); 273219820Sjeff status = IB_NOT_FOUND; 274219820Sjeff goto Exit; 275219820Sjeff } 276219820Sjeff } 277219820Sjeff 278219820Sjeff /* 279219820Sjeff * Same as above 280219820Sjeff */ 281219820Sjeff p_node = osm_physp_get_node_ptr(p_dest_physp); 282219820Sjeff 283219820Sjeff if (p_node->sw) { 284219820Sjeff /* 285219820Sjeff * if destination is switch, we want p_dest_physp to point to port 0 286219820Sjeff */ 287219820Sjeff p_dest_physp = osm_switch_get_route_by_lid(p_node->sw, dest_lid); 288219820Sjeff 289219820Sjeff if (p_dest_physp == 0) { 290219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F03: " 291219820Sjeff "Cannot find routing to LID %u from switch for GUID 0x%016" 292219820Sjeff PRIx64 "\n", dest_lid_ho, 293219820Sjeff cl_ntoh64(osm_node_get_node_guid(p_node))); 294219820Sjeff status = IB_NOT_FOUND; 295219820Sjeff goto Exit; 296219820Sjeff } 297219820Sjeff 298219820Sjeff } 299219820Sjeff 300219820Sjeff /* 301219820Sjeff * Now go through the path step by step 302219820Sjeff */ 303219820Sjeff 304219820Sjeff while (p_physp != p_dest_physp) { 305219820Sjeff 306219820Sjeff p_node = osm_physp_get_node_ptr(p_physp); 307219820Sjeff p_physp = osm_physp_get_remote(p_physp); 308219820Sjeff 309219820Sjeff if (p_physp == 0) { 310219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F05: " 311219820Sjeff "Cannot find remote phys port when routing to LID %u from node GUID 0x%016" 312219820Sjeff PRIx64 "\n", dest_lid_ho, 313219820Sjeff cl_ntoh64(osm_node_get_node_guid(p_node))); 314219820Sjeff status = IB_ERROR; 315219820Sjeff goto Exit; 316219820Sjeff } 317219820Sjeff 318219820Sjeff in_port_num = osm_physp_get_port_num(p_physp); 319219820Sjeff 320219820Sjeff /* 321219820Sjeff This is point to point case (no switch in between) 322219820Sjeff */ 323219820Sjeff if (p_physp == p_dest_physp) 324219820Sjeff break; 325219820Sjeff 326219820Sjeff p_node = osm_physp_get_node_ptr(p_physp); 327219820Sjeff 328219820Sjeff if (!p_node->sw) { 329219820Sjeff /* 330219820Sjeff There is some sort of problem in the subnet object! 331219820Sjeff If this isn't a switch, we should have reached 332219820Sjeff the destination by now! 333219820Sjeff */ 334219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F06: " 335219820Sjeff "Internal error, bad path\n"); 336219820Sjeff status = IB_ERROR; 337219820Sjeff goto Exit; 338219820Sjeff } 339219820Sjeff 340219820Sjeff /* 341219820Sjeff Check parameters for the ingress port in this switch. 342219820Sjeff */ 343219820Sjeff p_pi = &p_physp->port_info; 344219820Sjeff 345219820Sjeff if (mtu > ib_port_info_get_mtu_cap(p_pi)) 346219820Sjeff mtu = ib_port_info_get_mtu_cap(p_pi); 347219820Sjeff 348219820Sjeff if (rate > ib_port_info_compute_rate(p_pi)) 349219820Sjeff rate = ib_port_info_compute_rate(p_pi); 350219820Sjeff 351219820Sjeff /* 352219820Sjeff Continue with the egress port on this switch. 353219820Sjeff */ 354219820Sjeff p_physp = osm_switch_get_route_by_lid(p_node->sw, dest_lid); 355219820Sjeff if (p_physp == 0) { 356219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F07: " 357219820Sjeff "Dead end on path to LID %u from switch for GUID 0x%016" 358219820Sjeff PRIx64 "\n", dest_lid_ho, 359219820Sjeff cl_ntoh64(osm_node_get_node_guid(p_node))); 360219820Sjeff status = IB_ERROR; 361219820Sjeff goto Exit; 362219820Sjeff } 363219820Sjeff 364219820Sjeff p_pi = &p_physp->port_info; 365219820Sjeff 366219820Sjeff if (mtu > ib_port_info_get_mtu_cap(p_pi)) 367219820Sjeff mtu = ib_port_info_get_mtu_cap(p_pi); 368219820Sjeff 369219820Sjeff if (rate > ib_port_info_compute_rate(p_pi)) 370219820Sjeff rate = ib_port_info_compute_rate(p_pi); 371219820Sjeff 372219820Sjeff if (sa->p_subn->opt.qos) { 373219820Sjeff /* 374219820Sjeff * Check SL2VL table of the switch and update valid SLs 375219820Sjeff */ 376219820Sjeff p_slvl_tbl = osm_physp_get_slvl_tbl(p_physp, in_port_num); 377219820Sjeff for (i = 0; i < IB_MAX_NUM_VLS; i++) { 378219820Sjeff if (valid_sl_mask & (1 << i) && 379219820Sjeff ib_slvl_table_get(p_slvl_tbl, i) == IB_DROP_VL) 380219820Sjeff valid_sl_mask &= ~(1 << i); 381219820Sjeff } 382219820Sjeff if (!valid_sl_mask) { 383219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "All the SLs " 384219820Sjeff "lead to VL15 on this path\n"); 385219820Sjeff status = IB_NOT_FOUND; 386219820Sjeff goto Exit; 387219820Sjeff } 388219820Sjeff } 389219820Sjeff } 390219820Sjeff 391219820Sjeff /* 392219820Sjeff p_physp now points to the destination 393219820Sjeff */ 394219820Sjeff p_pi = &p_physp->port_info; 395219820Sjeff 396219820Sjeff if (mtu > ib_port_info_get_mtu_cap(p_pi)) 397219820Sjeff mtu = ib_port_info_get_mtu_cap(p_pi); 398219820Sjeff 399219820Sjeff if (rate > ib_port_info_compute_rate(p_pi)) 400219820Sjeff rate = ib_port_info_compute_rate(p_pi); 401219820Sjeff 402219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 403219820Sjeff "Path min MTU = %u, min rate = %u\n", mtu, rate); 404219820Sjeff 405219820Sjeff /* 406219820Sjeff * Get QoS Level object according to the path request 407219820Sjeff * and adjust path parameters according to QoS settings 408219820Sjeff */ 409219820Sjeff if (sa->p_subn->opt.qos && 410219820Sjeff sa->p_subn->p_qos_policy && 411219820Sjeff (p_qos_level = 412219820Sjeff osm_qos_policy_get_qos_level_by_pr(sa->p_subn->p_qos_policy, 413219820Sjeff p_pr, p_src_physp, p_dest_physp, 414219820Sjeff comp_mask))) { 415219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 416219820Sjeff "PathRecord request matches QoS Level '%s' (%s)\n", 417219820Sjeff p_qos_level->name, p_qos_level->use ? 418219820Sjeff p_qos_level->use : "no description"); 419219820Sjeff 420219820Sjeff if (p_qos_level->mtu_limit_set 421219820Sjeff && (mtu > p_qos_level->mtu_limit)) 422219820Sjeff mtu = p_qos_level->mtu_limit; 423219820Sjeff 424219820Sjeff if (p_qos_level->rate_limit_set 425219820Sjeff && (rate > p_qos_level->rate_limit)) 426219820Sjeff rate = p_qos_level->rate_limit; 427219820Sjeff 428219820Sjeff if (p_qos_level->sl_set) { 429219820Sjeff sl = p_qos_level->sl; 430219820Sjeff if (!(valid_sl_mask & (1 << sl))) { 431219820Sjeff status = IB_NOT_FOUND; 432219820Sjeff goto Exit; 433219820Sjeff } 434219820Sjeff } 435219820Sjeff } 436219820Sjeff 437219820Sjeff /* 438219820Sjeff * Set packet lifetime. 439219820Sjeff * According to spec definition IBA 1.2 Table 205 440219820Sjeff * PacketLifeTime description, for loopback paths, 441219820Sjeff * packetLifeTime shall be zero. 442219820Sjeff */ 443219820Sjeff if (p_src_port == p_dest_port) 444219820Sjeff pkt_life = 0; 445219820Sjeff else if (p_qos_level && p_qos_level->pkt_life_set) 446219820Sjeff pkt_life = p_qos_level->pkt_life; 447219820Sjeff else 448219820Sjeff pkt_life = sa->p_subn->opt.subnet_timeout; 449219820Sjeff 450219820Sjeff /* 451219820Sjeff Determine if these values meet the user criteria 452219820Sjeff and adjust appropriately 453219820Sjeff */ 454219820Sjeff 455219820Sjeff /* we silently ignore cases where only the MTU selector is defined */ 456219820Sjeff if ((comp_mask & IB_PR_COMPMASK_MTUSELEC) && 457219820Sjeff (comp_mask & IB_PR_COMPMASK_MTU)) { 458219820Sjeff required_mtu = ib_path_rec_mtu(p_pr); 459219820Sjeff switch (ib_path_rec_mtu_sel(p_pr)) { 460219820Sjeff case 0: /* must be greater than */ 461219820Sjeff if (mtu <= required_mtu) 462219820Sjeff status = IB_NOT_FOUND; 463219820Sjeff break; 464219820Sjeff 465219820Sjeff case 1: /* must be less than */ 466219820Sjeff if (mtu >= required_mtu) { 467219820Sjeff /* adjust to use the highest mtu 468219820Sjeff lower then the required one */ 469219820Sjeff if (required_mtu > 1) 470219820Sjeff mtu = required_mtu - 1; 471219820Sjeff else 472219820Sjeff status = IB_NOT_FOUND; 473219820Sjeff } 474219820Sjeff break; 475219820Sjeff 476219820Sjeff case 2: /* exact match */ 477219820Sjeff if (mtu < required_mtu) 478219820Sjeff status = IB_NOT_FOUND; 479219820Sjeff else 480219820Sjeff mtu = required_mtu; 481219820Sjeff break; 482219820Sjeff 483219820Sjeff case 3: /* largest available */ 484219820Sjeff /* can't be disqualified by this one */ 485219820Sjeff break; 486219820Sjeff 487219820Sjeff default: 488219820Sjeff /* if we're here, there's a bug in ib_path_rec_mtu_sel() */ 489219820Sjeff CL_ASSERT(FALSE); 490219820Sjeff status = IB_ERROR; 491219820Sjeff break; 492219820Sjeff } 493219820Sjeff } 494219820Sjeff if (status != IB_SUCCESS) 495219820Sjeff goto Exit; 496219820Sjeff 497219820Sjeff /* we silently ignore cases where only the Rate selector is defined */ 498219820Sjeff if ((comp_mask & IB_PR_COMPMASK_RATESELEC) && 499219820Sjeff (comp_mask & IB_PR_COMPMASK_RATE)) { 500219820Sjeff required_rate = ib_path_rec_rate(p_pr); 501219820Sjeff switch (ib_path_rec_rate_sel(p_pr)) { 502219820Sjeff case 0: /* must be greater than */ 503219820Sjeff if (rate <= required_rate) 504219820Sjeff status = IB_NOT_FOUND; 505219820Sjeff break; 506219820Sjeff 507219820Sjeff case 1: /* must be less than */ 508219820Sjeff if (rate >= required_rate) { 509219820Sjeff /* adjust the rate to use the highest rate 510219820Sjeff lower then the required one */ 511219820Sjeff if (required_rate > 2) 512219820Sjeff rate = required_rate - 1; 513219820Sjeff else 514219820Sjeff status = IB_NOT_FOUND; 515219820Sjeff } 516219820Sjeff break; 517219820Sjeff 518219820Sjeff case 2: /* exact match */ 519219820Sjeff if (rate < required_rate) 520219820Sjeff status = IB_NOT_FOUND; 521219820Sjeff else 522219820Sjeff rate = required_rate; 523219820Sjeff break; 524219820Sjeff 525219820Sjeff case 3: /* largest available */ 526219820Sjeff /* can't be disqualified by this one */ 527219820Sjeff break; 528219820Sjeff 529219820Sjeff default: 530219820Sjeff /* if we're here, there's a bug in ib_path_rec_mtu_sel() */ 531219820Sjeff CL_ASSERT(FALSE); 532219820Sjeff status = IB_ERROR; 533219820Sjeff break; 534219820Sjeff } 535219820Sjeff } 536219820Sjeff if (status != IB_SUCCESS) 537219820Sjeff goto Exit; 538219820Sjeff 539219820Sjeff /* we silently ignore cases where only the PktLife selector is defined */ 540219820Sjeff if ((comp_mask & IB_PR_COMPMASK_PKTLIFETIMESELEC) && 541219820Sjeff (comp_mask & IB_PR_COMPMASK_PKTLIFETIME)) { 542219820Sjeff required_pkt_life = ib_path_rec_pkt_life(p_pr); 543219820Sjeff switch (ib_path_rec_pkt_life_sel(p_pr)) { 544219820Sjeff case 0: /* must be greater than */ 545219820Sjeff if (pkt_life <= required_pkt_life) 546219820Sjeff status = IB_NOT_FOUND; 547219820Sjeff break; 548219820Sjeff 549219820Sjeff case 1: /* must be less than */ 550219820Sjeff if (pkt_life >= required_pkt_life) { 551219820Sjeff /* adjust the lifetime to use the highest possible 552219820Sjeff lower then the required one */ 553219820Sjeff if (required_pkt_life > 1) 554219820Sjeff pkt_life = required_pkt_life - 1; 555219820Sjeff else 556219820Sjeff status = IB_NOT_FOUND; 557219820Sjeff } 558219820Sjeff break; 559219820Sjeff 560219820Sjeff case 2: /* exact match */ 561219820Sjeff if (pkt_life < required_pkt_life) 562219820Sjeff status = IB_NOT_FOUND; 563219820Sjeff else 564219820Sjeff pkt_life = required_pkt_life; 565219820Sjeff break; 566219820Sjeff 567219820Sjeff case 3: /* smallest available */ 568219820Sjeff /* can't be disqualified by this one */ 569219820Sjeff break; 570219820Sjeff 571219820Sjeff default: 572219820Sjeff /* if we're here, there's a bug in ib_path_rec_pkt_life_sel() */ 573219820Sjeff CL_ASSERT(FALSE); 574219820Sjeff status = IB_ERROR; 575219820Sjeff break; 576219820Sjeff } 577219820Sjeff } 578219820Sjeff 579219820Sjeff if (status != IB_SUCCESS) 580219820Sjeff goto Exit; 581219820Sjeff 582219820Sjeff /* 583219820Sjeff * set Pkey for this path record request 584219820Sjeff */ 585219820Sjeff 586219820Sjeff if ((comp_mask & IB_PR_COMPMASK_RAWTRAFFIC) && 587219820Sjeff (cl_ntoh32(p_pr->hop_flow_raw) & (1 << 31))) 588219820Sjeff pkey = osm_physp_find_common_pkey(p_src_physp, p_dest_physp); 589219820Sjeff 590219820Sjeff else if (comp_mask & IB_PR_COMPMASK_PKEY) { 591219820Sjeff /* 592219820Sjeff * PR request has a specific pkey: 593219820Sjeff * Check that source and destination share this pkey. 594219820Sjeff * If QoS level has pkeys, check that this pkey exists 595219820Sjeff * in the QoS level pkeys. 596219820Sjeff * PR returned pkey is the requested pkey. 597219820Sjeff */ 598219820Sjeff pkey = p_pr->pkey; 599219820Sjeff if (!osm_physp_share_this_pkey(p_src_physp, p_dest_physp, pkey)) { 600219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1A: " 601219820Sjeff "Ports 0x%016" PRIx64 " 0x%016" PRIx64 602219820Sjeff " do not share specified PKey 0x%04x\n", 603219820Sjeff cl_ntoh64(osm_physp_get_port_guid(p_src_physp)), 604219820Sjeff cl_ntoh64(osm_physp_get_port_guid(p_dest_physp)), 605219820Sjeff cl_ntoh16(pkey)); 606219820Sjeff status = IB_NOT_FOUND; 607219820Sjeff goto Exit; 608219820Sjeff } 609219820Sjeff if (p_qos_level && p_qos_level->pkey_range_len && 610219820Sjeff !osm_qos_level_has_pkey(p_qos_level, pkey)) { 611219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1D: " 612219820Sjeff "Ports do not share PKeys defined by QoS level\n"); 613219820Sjeff status = IB_NOT_FOUND; 614219820Sjeff goto Exit; 615219820Sjeff } 616219820Sjeff 617219820Sjeff } else if (p_qos_level && p_qos_level->pkey_range_len) { 618219820Sjeff /* 619219820Sjeff * PR request doesn't have a specific pkey, but QoS level 620219820Sjeff * has pkeys - get shared pkey from QoS level pkeys 621219820Sjeff */ 622219820Sjeff pkey = osm_qos_level_get_shared_pkey(p_qos_level, 623219820Sjeff p_src_physp, p_dest_physp); 624219820Sjeff if (!pkey) { 625219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1E: " 626219820Sjeff "Ports 0x%016" PRIx64 " 0x%016" PRIx64 627219820Sjeff " do not share PKeys defined by QoS level\n", 628219820Sjeff cl_ntoh64(osm_physp_get_port_guid(p_src_physp)), 629219820Sjeff cl_ntoh64(osm_physp_get_port_guid(p_dest_physp))); 630219820Sjeff status = IB_NOT_FOUND; 631219820Sjeff goto Exit; 632219820Sjeff } 633219820Sjeff } else { 634219820Sjeff /* 635219820Sjeff * Neither PR request nor QoS level have pkey. 636219820Sjeff * Just get any shared pkey. 637219820Sjeff */ 638219820Sjeff pkey = osm_physp_find_common_pkey(p_src_physp, p_dest_physp); 639219820Sjeff if (!pkey) { 640219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1B: " 641219820Sjeff "Ports 0x%016" PRIx64 " 0x%016" PRIx64 642219820Sjeff " do not have any shared PKeys\n", 643219820Sjeff cl_ntoh64(osm_physp_get_port_guid(p_src_physp)), 644219820Sjeff cl_ntoh64(osm_physp_get_port_guid(p_dest_physp))); 645219820Sjeff status = IB_NOT_FOUND; 646219820Sjeff goto Exit; 647219820Sjeff } 648219820Sjeff } 649219820Sjeff 650219820Sjeff if (pkey) { 651219820Sjeff p_prtn = 652219820Sjeff (osm_prtn_t *) cl_qmap_get(&sa->p_subn->prtn_pkey_tbl, 653219820Sjeff pkey & cl_hton16((uint16_t) ~ 654219820Sjeff 0x8000)); 655219820Sjeff if (p_prtn == 656219820Sjeff (osm_prtn_t *) cl_qmap_end(&sa->p_subn->prtn_pkey_tbl)) 657219820Sjeff p_prtn = NULL; 658219820Sjeff } 659219820Sjeff 660219820Sjeff /* 661219820Sjeff * Set PathRecord SL. 662219820Sjeff */ 663219820Sjeff 664219820Sjeff is_lash = (p_osm->routing_engine_used == OSM_ROUTING_ENGINE_TYPE_LASH); 665219820Sjeff 666219820Sjeff if (comp_mask & IB_PR_COMPMASK_SL) { 667219820Sjeff /* 668219820Sjeff * Specific SL was requested 669219820Sjeff */ 670219820Sjeff sl = ib_path_rec_sl(p_pr); 671219820Sjeff 672219820Sjeff if (p_qos_level && p_qos_level->sl_set 673219820Sjeff && (p_qos_level->sl != sl)) { 674219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1F: " 675219820Sjeff "QoS constaraints: required PathRecord SL (%u) " 676219820Sjeff "doesn't match QoS policy SL (%u)\n", sl, 677219820Sjeff p_qos_level->sl); 678219820Sjeff status = IB_NOT_FOUND; 679219820Sjeff goto Exit; 680219820Sjeff } 681219820Sjeff 682219820Sjeff if (is_lash 683219820Sjeff && osm_get_lash_sl(p_osm, p_src_port, p_dest_port) != sl) { 684219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F23: " 685219820Sjeff "Required PathRecord SL (%u) doesn't " 686219820Sjeff "match LASH SL\n", sl); 687219820Sjeff status = IB_NOT_FOUND; 688219820Sjeff goto Exit; 689219820Sjeff } 690219820Sjeff 691219820Sjeff } else if (is_lash) { 692219820Sjeff /* 693219820Sjeff * No specific SL in PathRecord request. 694219820Sjeff * If it's LASH routing - use its SL. 695219820Sjeff * slid and dest_lid are stored in network in lash. 696219820Sjeff */ 697219820Sjeff sl = osm_get_lash_sl(p_osm, p_src_port, p_dest_port); 698219820Sjeff } else if (p_qos_level && p_qos_level->sl_set) { 699219820Sjeff /* 700219820Sjeff * No specific SL was requested, and we're not in 701219820Sjeff * LASH routing, but there is an SL in QoS level. 702219820Sjeff */ 703219820Sjeff sl = p_qos_level->sl; 704219820Sjeff 705219820Sjeff if (pkey && p_prtn && p_prtn->sl != p_qos_level->sl) 706219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 707219820Sjeff "QoS level SL (%u) overrides partition SL (%u)\n", 708219820Sjeff p_qos_level->sl, p_prtn->sl); 709219820Sjeff 710219820Sjeff } else if (pkey) { 711219820Sjeff /* 712219820Sjeff * No specific SL in request or in QoS level - use partition SL 713219820Sjeff */ 714219820Sjeff if (!p_prtn) { 715219820Sjeff sl = OSM_DEFAULT_SL; 716219820Sjeff /* this may be possible when pkey tables are created somehow in 717219820Sjeff previous runs or things are going wrong here */ 718219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1C: " 719219820Sjeff "No partition found for PKey 0x%04x - using default SL %d\n", 720219820Sjeff cl_ntoh16(pkey), sl); 721219820Sjeff } else 722219820Sjeff sl = p_prtn->sl; 723219820Sjeff } else if (sa->p_subn->opt.qos) { 724219820Sjeff if (valid_sl_mask & (1 << OSM_DEFAULT_SL)) 725219820Sjeff sl = OSM_DEFAULT_SL; 726219820Sjeff else { 727219820Sjeff for (i = 0; i < IB_MAX_NUM_VLS; i++) 728219820Sjeff if (valid_sl_mask & (1 << i)) 729219820Sjeff break; 730219820Sjeff sl = i; 731219820Sjeff } 732219820Sjeff } else 733219820Sjeff sl = OSM_DEFAULT_SL; 734219820Sjeff 735219820Sjeff if (sa->p_subn->opt.qos && !(valid_sl_mask & (1 << sl))) { 736219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F24: " 737219820Sjeff "Selected SL (%u) leads to VL15\n", sl); 738219820Sjeff status = IB_NOT_FOUND; 739219820Sjeff goto Exit; 740219820Sjeff } 741219820Sjeff 742219820Sjeff /* reset pkey when raw traffic */ 743219820Sjeff if (comp_mask & IB_PR_COMPMASK_RAWTRAFFIC && 744219820Sjeff cl_ntoh32(p_pr->hop_flow_raw) & (1 << 31)) 745219820Sjeff pkey = 0; 746219820Sjeff 747219820Sjeff p_parms->mtu = mtu; 748219820Sjeff p_parms->rate = rate; 749219820Sjeff p_parms->pkt_life = pkt_life; 750219820Sjeff p_parms->pkey = pkey; 751219820Sjeff p_parms->sl = sl; 752219820Sjeff 753219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Path params: mtu = %u, rate = %u," 754219820Sjeff " packet lifetime = %u, pkey = 0x%04X, sl = %u\n", 755219820Sjeff mtu, rate, pkt_life, cl_ntoh16(pkey), sl); 756219820SjeffExit: 757219820Sjeff OSM_LOG_EXIT(sa->p_log); 758219820Sjeff return (status); 759219820Sjeff} 760219820Sjeff 761219820Sjeff/********************************************************************** 762219820Sjeff **********************************************************************/ 763219820Sjeffstatic void 764219820Sjeff__osm_pr_rcv_build_pr(IN osm_sa_t * sa, 765219820Sjeff IN const osm_port_t * const p_src_port, 766219820Sjeff IN const osm_port_t * const p_dest_port, 767219820Sjeff IN const ib_gid_t * const p_dgid, 768219820Sjeff IN const uint16_t src_lid_ho, 769219820Sjeff IN const uint16_t dest_lid_ho, 770219820Sjeff IN const uint8_t preference, 771219820Sjeff IN const osm_path_parms_t * const p_parms, 772219820Sjeff OUT ib_path_rec_t * const p_pr) 773219820Sjeff{ 774219820Sjeff const osm_physp_t *p_src_physp; 775219820Sjeff const osm_physp_t *p_dest_physp; 776219820Sjeff boolean_t is_nonzero_gid = 0; 777219820Sjeff 778219820Sjeff OSM_LOG_ENTER(sa->p_log); 779219820Sjeff 780219820Sjeff p_src_physp = p_src_port->p_physp; 781219820Sjeff 782219820Sjeff if (p_dgid) { 783219820Sjeff if (memcmp(p_dgid, &zero_gid, sizeof(*p_dgid))) 784219820Sjeff is_nonzero_gid = 1; 785219820Sjeff } 786219820Sjeff 787219820Sjeff if (is_nonzero_gid) 788219820Sjeff p_pr->dgid = *p_dgid; 789219820Sjeff else { 790219820Sjeff p_dest_physp = p_dest_port->p_physp; 791219820Sjeff 792219820Sjeff p_pr->dgid.unicast.prefix = 793219820Sjeff osm_physp_get_subnet_prefix(p_dest_physp); 794219820Sjeff p_pr->dgid.unicast.interface_id = 795219820Sjeff osm_physp_get_port_guid(p_dest_physp); 796219820Sjeff } 797219820Sjeff 798219820Sjeff p_pr->sgid.unicast.prefix = osm_physp_get_subnet_prefix(p_src_physp); 799219820Sjeff p_pr->sgid.unicast.interface_id = osm_physp_get_port_guid(p_src_physp); 800219820Sjeff 801219820Sjeff p_pr->dlid = cl_hton16(dest_lid_ho); 802219820Sjeff p_pr->slid = cl_hton16(src_lid_ho); 803219820Sjeff 804219820Sjeff p_pr->hop_flow_raw &= cl_hton32(1 << 31); 805219820Sjeff 806219820Sjeff /* Only set HopLimit if going through a router */ 807219820Sjeff if (is_nonzero_gid) 808219820Sjeff p_pr->hop_flow_raw |= cl_hton32(IB_HOPLIMIT_MAX); 809219820Sjeff 810219820Sjeff p_pr->pkey = p_parms->pkey; 811219820Sjeff ib_path_rec_set_sl(p_pr, p_parms->sl); 812219820Sjeff ib_path_rec_set_qos_class(p_pr, 0); 813219820Sjeff p_pr->mtu = (uint8_t) (p_parms->mtu | 0x80); 814219820Sjeff p_pr->rate = (uint8_t) (p_parms->rate | 0x80); 815219820Sjeff 816219820Sjeff /* According to 1.2 spec definition Table 205 PacketLifeTime description, 817219820Sjeff for loopback paths, packetLifeTime shall be zero. */ 818219820Sjeff if (p_src_port == p_dest_port) 819219820Sjeff p_pr->pkt_life = 0x80; /* loopback */ 820219820Sjeff else 821219820Sjeff p_pr->pkt_life = (uint8_t) (p_parms->pkt_life | 0x80); 822219820Sjeff 823219820Sjeff p_pr->preference = preference; 824219820Sjeff 825219820Sjeff /* always return num_path = 0 so this is only the reversible component */ 826219820Sjeff if (p_parms->reversible) 827219820Sjeff p_pr->num_path = 0x80; 828219820Sjeff 829219820Sjeff OSM_LOG_EXIT(sa->p_log); 830219820Sjeff} 831219820Sjeff 832219820Sjeff/********************************************************************** 833219820Sjeff **********************************************************************/ 834219820Sjeffstatic osm_pr_item_t * 835219820Sjeff__osm_pr_rcv_get_lid_pair_path(IN osm_sa_t * sa, 836219820Sjeff IN const ib_path_rec_t * const p_pr, 837219820Sjeff IN const osm_port_t * const p_src_port, 838219820Sjeff IN const osm_port_t * const p_dest_port, 839219820Sjeff IN const ib_gid_t * const p_dgid, 840219820Sjeff IN const uint16_t src_lid_ho, 841219820Sjeff IN const uint16_t dest_lid_ho, 842219820Sjeff IN const ib_net64_t comp_mask, 843219820Sjeff IN const uint8_t preference) 844219820Sjeff{ 845219820Sjeff osm_path_parms_t path_parms; 846219820Sjeff osm_path_parms_t rev_path_parms; 847219820Sjeff osm_pr_item_t *p_pr_item; 848219820Sjeff ib_api_status_t status, rev_path_status; 849219820Sjeff 850219820Sjeff OSM_LOG_ENTER(sa->p_log); 851219820Sjeff 852219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Src LID %u, Dest LID %u\n", 853219820Sjeff src_lid_ho, dest_lid_ho); 854219820Sjeff 855219820Sjeff p_pr_item = malloc(sizeof(*p_pr_item)); 856219820Sjeff if (p_pr_item == NULL) { 857219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F01: " 858219820Sjeff "Unable to allocate path record\n"); 859219820Sjeff goto Exit; 860219820Sjeff } 861219820Sjeff memset(p_pr_item, 0, sizeof(*p_pr_item)); 862219820Sjeff 863219820Sjeff status = __osm_pr_rcv_get_path_parms(sa, p_pr, p_src_port, 864219820Sjeff p_dest_port, dest_lid_ho, 865219820Sjeff comp_mask, &path_parms); 866219820Sjeff 867219820Sjeff if (status != IB_SUCCESS) { 868219820Sjeff free(p_pr_item); 869219820Sjeff p_pr_item = NULL; 870219820Sjeff goto Exit; 871219820Sjeff } 872219820Sjeff 873219820Sjeff /* now try the reversible path */ 874219820Sjeff rev_path_status = __osm_pr_rcv_get_path_parms(sa, p_pr, p_dest_port, 875219820Sjeff p_src_port, src_lid_ho, 876219820Sjeff comp_mask, 877219820Sjeff &rev_path_parms); 878219820Sjeff path_parms.reversible = (rev_path_status == IB_SUCCESS); 879219820Sjeff 880219820Sjeff /* did we get a Reversible Path compmask ? */ 881219820Sjeff /* 882219820Sjeff NOTE that if the reversible component = 0, it is a don't care 883219820Sjeff rather then requiring non-reversible paths ... 884219820Sjeff see Vol1 Ver1.2 p900 l16 885219820Sjeff */ 886219820Sjeff if (comp_mask & IB_PR_COMPMASK_REVERSIBLE) { 887219820Sjeff if ((!path_parms.reversible && (p_pr->num_path & 0x80))) { 888219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 889219820Sjeff "Requested reversible path but failed to get one\n"); 890219820Sjeff 891219820Sjeff free(p_pr_item); 892219820Sjeff p_pr_item = NULL; 893219820Sjeff goto Exit; 894219820Sjeff } 895219820Sjeff } 896219820Sjeff 897219820Sjeff __osm_pr_rcv_build_pr(sa, p_src_port, p_dest_port, p_dgid, 898219820Sjeff src_lid_ho, dest_lid_ho, preference, &path_parms, 899219820Sjeff &p_pr_item->path_rec); 900219820Sjeff 901219820SjeffExit: 902219820Sjeff OSM_LOG_EXIT(sa->p_log); 903219820Sjeff return (p_pr_item); 904219820Sjeff} 905219820Sjeff 906219820Sjeff/********************************************************************** 907219820Sjeff **********************************************************************/ 908219820Sjeffstatic void 909219820Sjeff__osm_pr_rcv_get_port_pair_paths(IN osm_sa_t * sa, 910219820Sjeff IN const osm_madw_t * const p_madw, 911219820Sjeff IN const osm_port_t * const p_req_port, 912219820Sjeff IN const osm_port_t * const p_src_port, 913219820Sjeff IN const osm_port_t * const p_dest_port, 914219820Sjeff IN const ib_gid_t * const p_dgid, 915219820Sjeff IN const ib_net64_t comp_mask, 916219820Sjeff IN cl_qlist_t * const p_list) 917219820Sjeff{ 918219820Sjeff const ib_path_rec_t *p_pr; 919219820Sjeff const ib_sa_mad_t *p_sa_mad; 920219820Sjeff osm_pr_item_t *p_pr_item; 921219820Sjeff uint16_t src_lid_min_ho; 922219820Sjeff uint16_t src_lid_max_ho; 923219820Sjeff uint16_t dest_lid_min_ho; 924219820Sjeff uint16_t dest_lid_max_ho; 925219820Sjeff uint16_t src_lid_ho; 926219820Sjeff uint16_t dest_lid_ho; 927219820Sjeff uint32_t path_num; 928219820Sjeff uint8_t preference; 929219820Sjeff uintn_t iterations; 930219820Sjeff uintn_t src_offset; 931219820Sjeff uintn_t dest_offset; 932219820Sjeff 933219820Sjeff OSM_LOG_ENTER(sa->p_log); 934219820Sjeff 935219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 936219820Sjeff "Src port 0x%016" PRIx64 ", Dst port 0x%016" PRIx64 "\n", 937219820Sjeff cl_ntoh64(osm_port_get_guid(p_src_port)), 938219820Sjeff cl_ntoh64(osm_port_get_guid(p_dest_port))); 939219820Sjeff 940219820Sjeff /* Check that the req_port, src_port and dest_port all share a 941219820Sjeff pkey. The check is done on the default physical port of the ports. */ 942219820Sjeff if (osm_port_share_pkey(sa->p_log, p_req_port, p_src_port) == FALSE 943219820Sjeff || osm_port_share_pkey(sa->p_log, p_req_port, 944219820Sjeff p_dest_port) == FALSE 945219820Sjeff || osm_port_share_pkey(sa->p_log, p_src_port, 946219820Sjeff p_dest_port) == FALSE) 947219820Sjeff /* One of the pairs doesn't share a pkey so the path is disqualified. */ 948219820Sjeff goto Exit; 949219820Sjeff 950219820Sjeff p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw); 951219820Sjeff p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad); 952219820Sjeff 953219820Sjeff /* 954219820Sjeff We shouldn't be here if the paths are disqualified in some way... 955219820Sjeff Thus, we assume every possible connection is valid. 956219820Sjeff 957219820Sjeff We desire to return high-quality paths first. 958219820Sjeff In OpenSM, higher quality means least overlap with other paths. 959219820Sjeff This is acheived in practice by returning paths with 960219820Sjeff different LID value on each end, which means these 961219820Sjeff paths are more redundant that paths with the same LID repeated 962219820Sjeff on one side. For example, in OpenSM the paths between two 963219820Sjeff endpoints with LMC = 1 might be as follows: 964219820Sjeff 965219820Sjeff Port A, LID 1 <-> Port B, LID 3 966219820Sjeff Port A, LID 1 <-> Port B, LID 4 967219820Sjeff Port A, LID 2 <-> Port B, LID 3 968219820Sjeff Port A, LID 2 <-> Port B, LID 4 969219820Sjeff 970219820Sjeff The OpenSM unicast routing algorithms attempt to disperse each path 971219820Sjeff to as varied a physical path as is reasonable. 1<->3 and 1<->4 have 972219820Sjeff more physical overlap (hence less redundancy) than 1<->3 and 2<->4. 973219820Sjeff 974219820Sjeff OpenSM ranks paths in three preference groups: 975219820Sjeff 976219820Sjeff Preference Value Description 977219820Sjeff ---------------- ------------------------------------------- 978219820Sjeff 0 Redundant in both directions with other 979219820Sjeff pref value = 0 paths 980219820Sjeff 981219820Sjeff 1 Redundant in one direction with other 982219820Sjeff pref value = 0 and pref value = 1 paths 983219820Sjeff 984219820Sjeff 2 Not redundant in either direction with 985219820Sjeff other paths 986219820Sjeff 987219820Sjeff 3-FF Unused 988219820Sjeff 989219820Sjeff SA clients don't need to know these details, only that the lower 990219820Sjeff preference paths are preferred, as stated in the spec. The paths 991219820Sjeff may not actually be physically redundant depending on the topology 992219820Sjeff of the subnet, but the point of LMC > 0 is to offer redundancy, 993219820Sjeff so it is assumed that the subnet is physically appropriate for the 994219820Sjeff specified LMC value. A more advanced implementation would inspect for 995219820Sjeff physical redundancy, but I'm not going to bother with that now. 996219820Sjeff */ 997219820Sjeff 998219820Sjeff /* 999219820Sjeff Refine our search if the client specified end-point LIDs 1000219820Sjeff */ 1001219820Sjeff if (comp_mask & IB_PR_COMPMASK_DLID) { 1002219820Sjeff dest_lid_min_ho = cl_ntoh16(p_pr->dlid); 1003219820Sjeff dest_lid_max_ho = cl_ntoh16(p_pr->dlid); 1004219820Sjeff } else 1005219820Sjeff osm_port_get_lid_range_ho(p_dest_port, &dest_lid_min_ho, 1006219820Sjeff &dest_lid_max_ho); 1007219820Sjeff 1008219820Sjeff if (comp_mask & IB_PR_COMPMASK_SLID) { 1009219820Sjeff src_lid_min_ho = cl_ntoh16(p_pr->slid); 1010219820Sjeff src_lid_max_ho = cl_ntoh16(p_pr->slid); 1011219820Sjeff } else 1012219820Sjeff osm_port_get_lid_range_ho(p_src_port, &src_lid_min_ho, 1013219820Sjeff &src_lid_max_ho); 1014219820Sjeff 1015219820Sjeff if (src_lid_min_ho == 0) { 1016219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F20:" 1017219820Sjeff "Obtained source LID of 0. No such LID possible\n"); 1018219820Sjeff goto Exit; 1019219820Sjeff } 1020219820Sjeff 1021219820Sjeff if (dest_lid_min_ho == 0) { 1022219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F21:" 1023219820Sjeff "Obtained destination LID of 0. No such LID possible\n"); 1024219820Sjeff goto Exit; 1025219820Sjeff } 1026219820Sjeff 1027219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, 1028219820Sjeff "Src LIDs [%u-%u], Dest LIDs [%u-%u]\n", 1029219820Sjeff src_lid_min_ho, src_lid_max_ho, 1030219820Sjeff dest_lid_min_ho, dest_lid_max_ho); 1031219820Sjeff 1032219820Sjeff src_lid_ho = src_lid_min_ho; 1033219820Sjeff dest_lid_ho = dest_lid_min_ho; 1034219820Sjeff 1035219820Sjeff /* 1036219820Sjeff Preferred paths come first in OpenSM 1037219820Sjeff */ 1038219820Sjeff preference = 0; 1039219820Sjeff path_num = 0; 1040219820Sjeff 1041219820Sjeff /* If SubnAdmGet, assume NumbPaths 1 (1.2 erratum) */ 1042219820Sjeff if (p_sa_mad->method != IB_MAD_METHOD_GET) 1043219820Sjeff if (comp_mask & IB_PR_COMPMASK_NUMBPATH) 1044219820Sjeff iterations = ib_path_rec_num_path(p_pr); 1045219820Sjeff else 1046219820Sjeff iterations = (uintn_t) (-1); 1047219820Sjeff else 1048219820Sjeff iterations = 1; 1049219820Sjeff 1050219820Sjeff while (path_num < iterations) { 1051219820Sjeff /* 1052219820Sjeff These paths are "fully redundant" 1053219820Sjeff */ 1054219820Sjeff 1055219820Sjeff p_pr_item = __osm_pr_rcv_get_lid_pair_path(sa, p_pr, 1056219820Sjeff p_src_port, 1057219820Sjeff p_dest_port, p_dgid, 1058219820Sjeff src_lid_ho, 1059219820Sjeff dest_lid_ho, 1060219820Sjeff comp_mask, 1061219820Sjeff preference); 1062219820Sjeff 1063219820Sjeff if (p_pr_item) { 1064219820Sjeff cl_qlist_insert_tail(p_list, &p_pr_item->list_item); 1065219820Sjeff ++path_num; 1066219820Sjeff } 1067219820Sjeff 1068219820Sjeff if (++src_lid_ho > src_lid_max_ho) 1069219820Sjeff break; 1070219820Sjeff 1071219820Sjeff if (++dest_lid_ho > dest_lid_max_ho) 1072219820Sjeff break; 1073219820Sjeff } 1074219820Sjeff 1075219820Sjeff /* 1076219820Sjeff Check if we've accumulated all the paths that the user cares to see 1077219820Sjeff */ 1078219820Sjeff if (path_num == iterations) 1079219820Sjeff goto Exit; 1080219820Sjeff 1081219820Sjeff /* 1082219820Sjeff Don't bother reporting preference 1 paths for now. 1083219820Sjeff It's more trouble than it's worth and can only occur 1084219820Sjeff if ports have different LMC values, which isn't supported 1085219820Sjeff by OpenSM right now anyway. 1086219820Sjeff */ 1087219820Sjeff preference = 2; 1088219820Sjeff src_lid_ho = src_lid_min_ho; 1089219820Sjeff dest_lid_ho = dest_lid_min_ho; 1090219820Sjeff src_offset = 0; 1091219820Sjeff dest_offset = 0; 1092219820Sjeff 1093219820Sjeff /* 1094219820Sjeff Iterate over the remaining paths 1095219820Sjeff */ 1096219820Sjeff while (path_num < iterations) { 1097219820Sjeff dest_offset++; 1098219820Sjeff dest_lid_ho++; 1099219820Sjeff 1100219820Sjeff if (dest_lid_ho > dest_lid_max_ho) { 1101219820Sjeff src_offset++; 1102219820Sjeff src_lid_ho++; 1103219820Sjeff 1104219820Sjeff if (src_lid_ho > src_lid_max_ho) 1105219820Sjeff break; /* done */ 1106219820Sjeff 1107219820Sjeff dest_offset = 0; 1108219820Sjeff dest_lid_ho = dest_lid_min_ho; 1109219820Sjeff } 1110219820Sjeff 1111219820Sjeff /* 1112219820Sjeff These paths are "fully non-redundant" with paths already 1113219820Sjeff identified above and consequently not of much value. 1114219820Sjeff 1115219820Sjeff Don't return paths we already identified above, as indicated 1116219820Sjeff by the offset values being equal. 1117219820Sjeff */ 1118219820Sjeff if (src_offset == dest_offset) 1119219820Sjeff continue; /* already reported */ 1120219820Sjeff 1121219820Sjeff p_pr_item = __osm_pr_rcv_get_lid_pair_path(sa, p_pr, 1122219820Sjeff p_src_port, 1123219820Sjeff p_dest_port, p_dgid, 1124219820Sjeff src_lid_ho, 1125219820Sjeff dest_lid_ho, 1126219820Sjeff comp_mask, 1127219820Sjeff preference); 1128219820Sjeff 1129219820Sjeff if (p_pr_item) { 1130219820Sjeff cl_qlist_insert_tail(p_list, &p_pr_item->list_item); 1131219820Sjeff ++path_num; 1132219820Sjeff } 1133219820Sjeff } 1134219820Sjeff 1135219820SjeffExit: 1136219820Sjeff OSM_LOG_EXIT(sa->p_log); 1137219820Sjeff} 1138219820Sjeff 1139219820Sjeff/********************************************************************** 1140219820Sjeff **********************************************************************/ 1141219820Sjeffstatic ib_net16_t 1142219820Sjeff__osm_pr_rcv_get_end_points(IN osm_sa_t * sa, 1143219820Sjeff IN const osm_madw_t * const p_madw, 1144219820Sjeff OUT const osm_port_t ** const pp_src_port, 1145219820Sjeff OUT const osm_port_t ** const pp_dest_port, 1146219820Sjeff OUT ib_gid_t * const p_dgid) 1147219820Sjeff{ 1148219820Sjeff const ib_path_rec_t *p_pr; 1149219820Sjeff const ib_sa_mad_t *p_sa_mad; 1150219820Sjeff ib_net64_t comp_mask; 1151219820Sjeff ib_net64_t dest_guid; 1152219820Sjeff ib_api_status_t status; 1153219820Sjeff ib_net16_t sa_status = IB_SA_MAD_STATUS_SUCCESS; 1154219820Sjeff osm_router_t *p_rtr; 1155219820Sjeff osm_port_t *p_rtr_port; 1156219820Sjeff 1157219820Sjeff OSM_LOG_ENTER(sa->p_log); 1158219820Sjeff 1159219820Sjeff /* 1160219820Sjeff Determine what fields are valid and then get a pointer 1161219820Sjeff to the source and destination port objects, if possible. 1162219820Sjeff */ 1163219820Sjeff 1164219820Sjeff p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw); 1165219820Sjeff p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad); 1166219820Sjeff 1167219820Sjeff comp_mask = p_sa_mad->comp_mask; 1168219820Sjeff 1169219820Sjeff /* 1170219820Sjeff Check a few easy disqualifying cases up front before getting 1171219820Sjeff into the endpoints. 1172219820Sjeff */ 1173219820Sjeff 1174219820Sjeff if (comp_mask & IB_PR_COMPMASK_SGID) { 1175219820Sjeff if (!ib_gid_is_link_local(&p_pr->sgid)) { 1176219820Sjeff if (ib_gid_get_subnet_prefix(&p_pr->sgid) != 1177219820Sjeff sa->p_subn->opt.subnet_prefix) { 1178219820Sjeff /* 1179219820Sjeff This 'error' is the client's fault (bad gid) 1180219820Sjeff so don't enter it as an error in our own log. 1181219820Sjeff Return an error response to the client. 1182219820Sjeff */ 1183219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_VERBOSE, 1184219820Sjeff "Non local SGID subnet prefix 0x%016" 1185219820Sjeff PRIx64 "\n", 1186219820Sjeff cl_ntoh64(p_pr->sgid.unicast.prefix)); 1187219820Sjeff 1188219820Sjeff sa_status = IB_SA_MAD_STATUS_INVALID_GID; 1189219820Sjeff goto Exit; 1190219820Sjeff } 1191219820Sjeff } 1192219820Sjeff 1193219820Sjeff *pp_src_port = osm_get_port_by_guid(sa->p_subn, 1194219820Sjeff p_pr->sgid.unicast. 1195219820Sjeff interface_id); 1196219820Sjeff if (!*pp_src_port) { 1197219820Sjeff /* 1198219820Sjeff This 'error' is the client's fault (bad gid) so 1199219820Sjeff don't enter it as an error in our own log. 1200219820Sjeff Return an error response to the client. 1201219820Sjeff */ 1202219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_VERBOSE, 1203219820Sjeff "No source port with GUID 0x%016" PRIx64 "\n", 1204219820Sjeff cl_ntoh64(p_pr->sgid.unicast.interface_id)); 1205219820Sjeff 1206219820Sjeff sa_status = IB_SA_MAD_STATUS_INVALID_GID; 1207219820Sjeff goto Exit; 1208219820Sjeff } 1209219820Sjeff } else { 1210219820Sjeff *pp_src_port = 0; 1211219820Sjeff if (comp_mask & IB_PR_COMPMASK_SLID) { 1212219820Sjeff status = cl_ptr_vector_at(&sa->p_subn->port_lid_tbl, 1213219820Sjeff cl_ntoh16(p_pr->slid), 1214219820Sjeff (void **)pp_src_port); 1215219820Sjeff 1216219820Sjeff if ((status != CL_SUCCESS) || (*pp_src_port == NULL)) { 1217219820Sjeff /* 1218219820Sjeff This 'error' is the client's fault (bad lid) so 1219219820Sjeff don't enter it as an error in our own log. 1220219820Sjeff Return an error response to the client. 1221219820Sjeff */ 1222219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_VERBOSE, 1223219820Sjeff "No source port with LID %u\n", 1224219820Sjeff cl_ntoh16(p_pr->slid)); 1225219820Sjeff 1226219820Sjeff sa_status = IB_SA_MAD_STATUS_NO_RECORDS; 1227219820Sjeff goto Exit; 1228219820Sjeff } 1229219820Sjeff } 1230219820Sjeff } 1231219820Sjeff 1232219820Sjeff if (p_dgid) 1233219820Sjeff memset(p_dgid, 0, sizeof(*p_dgid)); 1234219820Sjeff 1235219820Sjeff if (comp_mask & IB_PR_COMPMASK_DGID) { 1236219820Sjeff dest_guid = p_pr->dgid.unicast.interface_id; 1237219820Sjeff if (!ib_gid_is_link_local(&p_pr->dgid)) { 1238219820Sjeff if (!ib_gid_is_multicast(&p_pr->dgid) && 1239219820Sjeff ib_gid_get_subnet_prefix(&p_pr->dgid) != 1240219820Sjeff sa->p_subn->opt.subnet_prefix) { 1241219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_VERBOSE, 1242219820Sjeff "Non local DGID subnet prefix 0x%016" 1243219820Sjeff PRIx64 "\n", 1244219820Sjeff cl_ntoh64(p_pr->dgid.unicast.prefix)); 1245219820Sjeff 1246219820Sjeff /* Find the router port that is configured to 1247219820Sjeff handle this prefix, if any */ 1248219820Sjeff osm_prefix_route_t *route = NULL; 1249219820Sjeff osm_prefix_route_t *r = (osm_prefix_route_t *) 1250219820Sjeff cl_qlist_head(&sa->p_subn->prefix_routes_list); 1251219820Sjeff 1252219820Sjeff while (r != (osm_prefix_route_t *) 1253219820Sjeff cl_qlist_end(&sa->p_subn->prefix_routes_list)) 1254219820Sjeff { 1255219820Sjeff if (r->prefix == p_pr->dgid.unicast.prefix || 1256219820Sjeff r->prefix == 0) 1257219820Sjeff { 1258219820Sjeff route = r; 1259219820Sjeff break; 1260219820Sjeff } 1261219820Sjeff r = (osm_prefix_route_t *) cl_qlist_next(&r->list_item); 1262219820Sjeff } 1263219820Sjeff 1264219820Sjeff if (!route) { 1265219820Sjeff /* 1266219820Sjeff This 'error' is the client's fault (bad gid) so 1267219820Sjeff don't enter it as an error in our own log. 1268219820Sjeff Return an error response to the client. 1269219820Sjeff */ 1270219820Sjeff sa_status = IB_SA_MAD_STATUS_INVALID_GID; 1271219820Sjeff goto Exit; 1272219820Sjeff } else if (route->guid == 0) { 1273219820Sjeff /* first router */ 1274219820Sjeff p_rtr = (osm_router_t *) 1275219820Sjeff cl_qmap_head(&sa-> 1276219820Sjeff p_subn-> 1277219820Sjeff rtr_guid_tbl); 1278219820Sjeff } else { 1279219820Sjeff p_rtr = (osm_router_t *) 1280219820Sjeff cl_qmap_get(&sa-> 1281219820Sjeff p_subn-> 1282219820Sjeff rtr_guid_tbl, 1283219820Sjeff route->guid); 1284219820Sjeff } 1285219820Sjeff 1286219820Sjeff if (p_rtr == 1287219820Sjeff (osm_router_t *) cl_qmap_end(&sa-> 1288219820Sjeff p_subn-> 1289219820Sjeff rtr_guid_tbl)) 1290219820Sjeff { 1291219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, 1292219820Sjeff "ERR 1F22: " 1293219820Sjeff "Off subnet DGID but router not found\n"); 1294219820Sjeff sa_status = 1295219820Sjeff IB_SA_MAD_STATUS_INVALID_GID; 1296219820Sjeff goto Exit; 1297219820Sjeff } 1298219820Sjeff 1299219820Sjeff p_rtr_port = osm_router_get_port_ptr(p_rtr); 1300219820Sjeff dest_guid = osm_port_get_guid(p_rtr_port); 1301219820Sjeff if (p_dgid) 1302219820Sjeff *p_dgid = p_pr->dgid; 1303219820Sjeff } 1304219820Sjeff } 1305219820Sjeff 1306219820Sjeff *pp_dest_port = osm_get_port_by_guid(sa->p_subn, dest_guid); 1307219820Sjeff if (!*pp_dest_port) { 1308219820Sjeff /* 1309219820Sjeff This 'error' is the client's fault (bad gid) so 1310219820Sjeff don't enter it as an error in our own log. 1311219820Sjeff Return an error response to the client. 1312219820Sjeff */ 1313219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_VERBOSE, 1314219820Sjeff "No dest port with GUID 0x%016" PRIx64 "\n", 1315219820Sjeff cl_ntoh64(dest_guid)); 1316219820Sjeff 1317219820Sjeff sa_status = IB_SA_MAD_STATUS_INVALID_GID; 1318219820Sjeff goto Exit; 1319219820Sjeff } 1320219820Sjeff } else { 1321219820Sjeff *pp_dest_port = 0; 1322219820Sjeff if (comp_mask & IB_PR_COMPMASK_DLID) { 1323219820Sjeff status = cl_ptr_vector_at(&sa->p_subn->port_lid_tbl, 1324219820Sjeff cl_ntoh16(p_pr->dlid), 1325219820Sjeff (void **)pp_dest_port); 1326219820Sjeff 1327219820Sjeff if ((status != CL_SUCCESS) || (*pp_dest_port == NULL)) { 1328219820Sjeff /* 1329219820Sjeff This 'error' is the client's fault (bad lid) 1330219820Sjeff so don't enter it as an error in our own log. 1331219820Sjeff Return an error response to the client. 1332219820Sjeff */ 1333219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_VERBOSE, 1334219820Sjeff "No dest port with LID %u\n", 1335219820Sjeff cl_ntoh16(p_pr->dlid)); 1336219820Sjeff 1337219820Sjeff sa_status = IB_SA_MAD_STATUS_NO_RECORDS; 1338219820Sjeff goto Exit; 1339219820Sjeff } 1340219820Sjeff } 1341219820Sjeff } 1342219820Sjeff 1343219820SjeffExit: 1344219820Sjeff OSM_LOG_EXIT(sa->p_log); 1345219820Sjeff return (sa_status); 1346219820Sjeff} 1347219820Sjeff 1348219820Sjeff/********************************************************************** 1349219820Sjeff **********************************************************************/ 1350219820Sjeffstatic void 1351219820Sjeff__osm_pr_rcv_process_world(IN osm_sa_t * sa, 1352219820Sjeff IN const osm_madw_t * const p_madw, 1353219820Sjeff IN const osm_port_t * const requester_port, 1354219820Sjeff IN const ib_gid_t * const p_dgid, 1355219820Sjeff IN const ib_net64_t comp_mask, 1356219820Sjeff IN cl_qlist_t * const p_list) 1357219820Sjeff{ 1358219820Sjeff const cl_qmap_t *p_tbl; 1359219820Sjeff const osm_port_t *p_dest_port; 1360219820Sjeff const osm_port_t *p_src_port; 1361219820Sjeff 1362219820Sjeff OSM_LOG_ENTER(sa->p_log); 1363219820Sjeff 1364219820Sjeff /* 1365219820Sjeff Iterate the entire port space over itself. 1366219820Sjeff A path record from a port to itself is legit, so no 1367219820Sjeff need for a special case there. 1368219820Sjeff 1369219820Sjeff We compute both A -> B and B -> A, since we don't have 1370219820Sjeff any check to determine the reversability of the paths. 1371219820Sjeff */ 1372219820Sjeff p_tbl = &sa->p_subn->port_guid_tbl; 1373219820Sjeff 1374219820Sjeff p_dest_port = (osm_port_t *) cl_qmap_head(p_tbl); 1375219820Sjeff while (p_dest_port != (osm_port_t *) cl_qmap_end(p_tbl)) { 1376219820Sjeff p_src_port = (osm_port_t *) cl_qmap_head(p_tbl); 1377219820Sjeff while (p_src_port != (osm_port_t *) cl_qmap_end(p_tbl)) { 1378219820Sjeff __osm_pr_rcv_get_port_pair_paths(sa, p_madw, 1379219820Sjeff requester_port, 1380219820Sjeff p_src_port, 1381219820Sjeff p_dest_port, p_dgid, 1382219820Sjeff comp_mask, p_list); 1383219820Sjeff 1384219820Sjeff p_src_port = 1385219820Sjeff (osm_port_t *) cl_qmap_next(&p_src_port->map_item); 1386219820Sjeff } 1387219820Sjeff 1388219820Sjeff p_dest_port = 1389219820Sjeff (osm_port_t *) cl_qmap_next(&p_dest_port->map_item); 1390219820Sjeff } 1391219820Sjeff 1392219820Sjeff OSM_LOG_EXIT(sa->p_log); 1393219820Sjeff} 1394219820Sjeff 1395219820Sjeff/********************************************************************** 1396219820Sjeff **********************************************************************/ 1397219820Sjeffstatic void 1398219820Sjeff__osm_pr_rcv_process_half(IN osm_sa_t * sa, 1399219820Sjeff IN const osm_madw_t * const p_madw, 1400219820Sjeff IN const osm_port_t * const requester_port, 1401219820Sjeff IN const osm_port_t * const p_src_port, 1402219820Sjeff IN const osm_port_t * const p_dest_port, 1403219820Sjeff IN const ib_gid_t * const p_dgid, 1404219820Sjeff IN const ib_net64_t comp_mask, 1405219820Sjeff IN cl_qlist_t * const p_list) 1406219820Sjeff{ 1407219820Sjeff const cl_qmap_t *p_tbl; 1408219820Sjeff const osm_port_t *p_port; 1409219820Sjeff 1410219820Sjeff OSM_LOG_ENTER(sa->p_log); 1411219820Sjeff 1412219820Sjeff /* 1413219820Sjeff Iterate over every port, looking for matches... 1414219820Sjeff A path record from a port to itself is legit, so no 1415219820Sjeff need to special case that one. 1416219820Sjeff */ 1417219820Sjeff p_tbl = &sa->p_subn->port_guid_tbl; 1418219820Sjeff 1419219820Sjeff if (p_src_port) { 1420219820Sjeff /* 1421219820Sjeff The src port if fixed, so iterate over destination ports. 1422219820Sjeff */ 1423219820Sjeff p_port = (osm_port_t *) cl_qmap_head(p_tbl); 1424219820Sjeff while (p_port != (osm_port_t *) cl_qmap_end(p_tbl)) { 1425219820Sjeff __osm_pr_rcv_get_port_pair_paths(sa, p_madw, 1426219820Sjeff requester_port, 1427219820Sjeff p_src_port, p_port, 1428219820Sjeff p_dgid, comp_mask, 1429219820Sjeff p_list); 1430219820Sjeff p_port = (osm_port_t *) cl_qmap_next(&p_port->map_item); 1431219820Sjeff } 1432219820Sjeff } else { 1433219820Sjeff /* 1434219820Sjeff The dest port if fixed, so iterate over source ports. 1435219820Sjeff */ 1436219820Sjeff p_port = (osm_port_t *) cl_qmap_head(p_tbl); 1437219820Sjeff while (p_port != (osm_port_t *) cl_qmap_end(p_tbl)) { 1438219820Sjeff __osm_pr_rcv_get_port_pair_paths(sa, p_madw, 1439219820Sjeff requester_port, p_port, 1440219820Sjeff p_dest_port, p_dgid, 1441219820Sjeff comp_mask, p_list); 1442219820Sjeff p_port = (osm_port_t *) cl_qmap_next(&p_port->map_item); 1443219820Sjeff } 1444219820Sjeff } 1445219820Sjeff 1446219820Sjeff OSM_LOG_EXIT(sa->p_log); 1447219820Sjeff} 1448219820Sjeff 1449219820Sjeff/********************************************************************** 1450219820Sjeff **********************************************************************/ 1451219820Sjeffstatic void 1452219820Sjeff__osm_pr_rcv_process_pair(IN osm_sa_t * sa, 1453219820Sjeff IN const osm_madw_t * const p_madw, 1454219820Sjeff IN const osm_port_t * const requester_port, 1455219820Sjeff IN const osm_port_t * const p_src_port, 1456219820Sjeff IN const osm_port_t * const p_dest_port, 1457219820Sjeff IN const ib_gid_t * const p_dgid, 1458219820Sjeff IN const ib_net64_t comp_mask, 1459219820Sjeff IN cl_qlist_t * const p_list) 1460219820Sjeff{ 1461219820Sjeff OSM_LOG_ENTER(sa->p_log); 1462219820Sjeff 1463219820Sjeff __osm_pr_rcv_get_port_pair_paths(sa, p_madw, requester_port, 1464219820Sjeff p_src_port, p_dest_port, p_dgid, 1465219820Sjeff comp_mask, p_list); 1466219820Sjeff 1467219820Sjeff OSM_LOG_EXIT(sa->p_log); 1468219820Sjeff} 1469219820Sjeff 1470219820Sjeff/********************************************************************** 1471219820Sjeff **********************************************************************/ 1472219820Sjeffstatic osm_mgrp_t *pr_get_mgrp(IN osm_sa_t * sa, 1473219820Sjeff IN const osm_madw_t * const p_madw) 1474219820Sjeff{ 1475219820Sjeff ib_path_rec_t *p_pr; 1476219820Sjeff const ib_sa_mad_t *p_sa_mad; 1477219820Sjeff ib_net64_t comp_mask; 1478219820Sjeff osm_mgrp_t *mgrp = NULL; 1479219820Sjeff 1480219820Sjeff p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw); 1481219820Sjeff p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad); 1482219820Sjeff 1483219820Sjeff comp_mask = p_sa_mad->comp_mask; 1484219820Sjeff 1485219820Sjeff if ((comp_mask & IB_PR_COMPMASK_DGID) && 1486219820Sjeff !(mgrp = osm_get_mgrp_by_mgid(sa, &p_pr->dgid))) { 1487219820Sjeff char gid_str[INET6_ADDRSTRLEN]; 1488219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F09: " 1489219820Sjeff "No MC group found for PathRecord destination GID %s\n", 1490219820Sjeff inet_ntop(AF_INET6, p_pr->dgid.raw, gid_str, 1491219820Sjeff sizeof gid_str)); 1492219820Sjeff goto Exit; 1493219820Sjeff } 1494219820Sjeff 1495219820Sjeff if (comp_mask & IB_PR_COMPMASK_DLID) { 1496219820Sjeff if (mgrp) { 1497219820Sjeff /* check that the MLID in the MC group is */ 1498219820Sjeff /* the same as the DLID in the PathRecord */ 1499219820Sjeff if (mgrp->mlid != p_pr->dlid) { 1500219820Sjeff /* Note: perhaps this might be better indicated as an invalid request */ 1501219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F10: " 1502219820Sjeff "MC group MLID 0x%x does not match " 1503219820Sjeff "PathRecord destination LID 0x%x\n", 1504219820Sjeff mgrp->mlid, p_pr->dlid); 1505219820Sjeff mgrp = NULL; 1506219820Sjeff goto Exit; 1507219820Sjeff } 1508219820Sjeff } else if (!(mgrp = osm_get_mgrp_by_mlid(sa->p_subn, p_pr->dlid))) 1509219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F11: " 1510219820Sjeff "No MC group found for PathRecord " 1511219820Sjeff "destination LID 0x%x\n", p_pr->dlid); 1512219820Sjeff } 1513219820Sjeff 1514219820SjeffExit: 1515219820Sjeff return mgrp; 1516219820Sjeff} 1517219820Sjeff 1518219820Sjeff/********************************************************************** 1519219820Sjeff **********************************************************************/ 1520219820Sjeffstatic ib_api_status_t 1521219820Sjeff__osm_pr_match_mgrp_attributes(IN osm_sa_t * sa, 1522219820Sjeff IN const osm_madw_t * const p_madw, 1523219820Sjeff IN const osm_mgrp_t * const p_mgrp) 1524219820Sjeff{ 1525219820Sjeff const ib_path_rec_t *p_pr; 1526219820Sjeff const ib_sa_mad_t *p_sa_mad; 1527219820Sjeff ib_net64_t comp_mask; 1528219820Sjeff ib_api_status_t status = IB_ERROR; 1529219820Sjeff uint32_t flow_label; 1530219820Sjeff uint8_t sl; 1531219820Sjeff uint8_t hop_limit; 1532219820Sjeff 1533219820Sjeff OSM_LOG_ENTER(sa->p_log); 1534219820Sjeff 1535219820Sjeff p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw); 1536219820Sjeff p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad); 1537219820Sjeff 1538219820Sjeff comp_mask = p_sa_mad->comp_mask; 1539219820Sjeff 1540219820Sjeff /* If SGID and/or SLID specified, should validate as member of MC group */ 1541219820Sjeff /* Also, MTU, rate, packet lifetime, and raw traffic requested are not currently checked */ 1542219820Sjeff if (comp_mask & IB_PR_COMPMASK_PKEY) { 1543219820Sjeff if (p_pr->pkey != p_mgrp->mcmember_rec.pkey) 1544219820Sjeff goto Exit; 1545219820Sjeff } 1546219820Sjeff 1547219820Sjeff ib_member_get_sl_flow_hop(p_mgrp->mcmember_rec.sl_flow_hop, 1548219820Sjeff &sl, &flow_label, &hop_limit); 1549219820Sjeff 1550219820Sjeff if (comp_mask & IB_PR_COMPMASK_SL) { 1551219820Sjeff if (ib_path_rec_sl(p_pr) != sl) 1552219820Sjeff goto Exit; 1553219820Sjeff } 1554219820Sjeff 1555219820Sjeff /* If SubnAdmGet, assume NumbPaths of 1 (1.2 erratum) */ 1556219820Sjeff if ((comp_mask & IB_PR_COMPMASK_NUMBPATH) && 1557219820Sjeff (p_sa_mad->method != IB_MAD_METHOD_GET)) { 1558219820Sjeff if (ib_path_rec_num_path(p_pr) == 0) 1559219820Sjeff goto Exit; 1560219820Sjeff } 1561219820Sjeff 1562219820Sjeff if (comp_mask & IB_PR_COMPMASK_FLOWLABEL) { 1563219820Sjeff if (ib_path_rec_flow_lbl(p_pr) != flow_label) 1564219820Sjeff goto Exit; 1565219820Sjeff } 1566219820Sjeff 1567219820Sjeff if (comp_mask & IB_PR_COMPMASK_HOPLIMIT) { 1568219820Sjeff if (ib_path_rec_hop_limit(p_pr) != hop_limit) 1569219820Sjeff goto Exit; 1570219820Sjeff } 1571219820Sjeff 1572219820Sjeff if (comp_mask & IB_PR_COMPMASK_TCLASS) { 1573219820Sjeff if (p_pr->tclass != p_mgrp->mcmember_rec.tclass) 1574219820Sjeff goto Exit; 1575219820Sjeff } 1576219820Sjeff 1577219820Sjeff status = IB_SUCCESS; 1578219820Sjeff 1579219820SjeffExit: 1580219820Sjeff OSM_LOG_EXIT(sa->p_log); 1581219820Sjeff return (status); 1582219820Sjeff} 1583219820Sjeff 1584219820Sjeff/********************************************************************** 1585219820Sjeff **********************************************************************/ 1586219820Sjeffstatic int 1587219820Sjeff__osm_pr_rcv_check_mcast_dest(IN osm_sa_t * sa, 1588219820Sjeff IN const osm_madw_t * const p_madw) 1589219820Sjeff{ 1590219820Sjeff const ib_path_rec_t *p_pr; 1591219820Sjeff const ib_sa_mad_t *p_sa_mad; 1592219820Sjeff ib_net64_t comp_mask; 1593219820Sjeff int is_multicast = 0; 1594219820Sjeff 1595219820Sjeff OSM_LOG_ENTER(sa->p_log); 1596219820Sjeff 1597219820Sjeff p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw); 1598219820Sjeff p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad); 1599219820Sjeff 1600219820Sjeff comp_mask = p_sa_mad->comp_mask; 1601219820Sjeff 1602219820Sjeff if (comp_mask & IB_PR_COMPMASK_DGID) { 1603219820Sjeff is_multicast = ib_gid_is_multicast(&p_pr->dgid); 1604219820Sjeff if (!is_multicast) 1605219820Sjeff goto Exit; 1606219820Sjeff } 1607219820Sjeff 1608219820Sjeff if (comp_mask & IB_PR_COMPMASK_DLID) { 1609219820Sjeff if (cl_ntoh16(p_pr->dlid) >= IB_LID_MCAST_START_HO && 1610219820Sjeff cl_ntoh16(p_pr->dlid) <= IB_LID_MCAST_END_HO) 1611219820Sjeff is_multicast = 1; 1612219820Sjeff else if (is_multicast) { 1613219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F12: " 1614219820Sjeff "PathRecord request indicates MGID but not MLID\n"); 1615219820Sjeff is_multicast = -1; 1616219820Sjeff } 1617219820Sjeff } 1618219820Sjeff 1619219820SjeffExit: 1620219820Sjeff OSM_LOG_EXIT(sa->p_log); 1621219820Sjeff return (is_multicast); 1622219820Sjeff} 1623219820Sjeff 1624219820Sjeff/********************************************************************** 1625219820Sjeff **********************************************************************/ 1626219820Sjeffvoid osm_pr_rcv_process(IN void *context, IN void *data) 1627219820Sjeff{ 1628219820Sjeff osm_sa_t *sa = context; 1629219820Sjeff osm_madw_t *p_madw = data; 1630219820Sjeff const ib_path_rec_t *p_pr; 1631219820Sjeff const ib_sa_mad_t *p_sa_mad; 1632219820Sjeff const osm_port_t *p_src_port; 1633219820Sjeff const osm_port_t *p_dest_port; 1634219820Sjeff cl_qlist_t pr_list; 1635219820Sjeff ib_gid_t dgid; 1636219820Sjeff ib_net16_t sa_status; 1637219820Sjeff osm_port_t *requester_port; 1638219820Sjeff int ret; 1639219820Sjeff 1640219820Sjeff OSM_LOG_ENTER(sa->p_log); 1641219820Sjeff 1642219820Sjeff CL_ASSERT(p_madw); 1643219820Sjeff 1644219820Sjeff p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw); 1645219820Sjeff p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad); 1646219820Sjeff 1647219820Sjeff CL_ASSERT(p_sa_mad->attr_id == IB_MAD_ATTR_PATH_RECORD); 1648219820Sjeff 1649219820Sjeff /* we only support SubnAdmGet and SubnAdmGetTable methods */ 1650219820Sjeff if (p_sa_mad->method != IB_MAD_METHOD_GET && 1651219820Sjeff p_sa_mad->method != IB_MAD_METHOD_GETTABLE) { 1652219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F17: " 1653219820Sjeff "Unsupported Method (%s)\n", 1654219820Sjeff ib_get_sa_method_str(p_sa_mad->method)); 1655219820Sjeff osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR); 1656219820Sjeff goto Exit; 1657219820Sjeff } 1658219820Sjeff 1659219820Sjeff /* update the requester physical port. */ 1660219820Sjeff requester_port = osm_get_port_by_mad_addr(sa->p_log, sa->p_subn, 1661219820Sjeff osm_madw_get_mad_addr_ptr 1662219820Sjeff (p_madw)); 1663219820Sjeff if (requester_port == NULL) { 1664219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F16: " 1665219820Sjeff "Cannot find requester physical port\n"); 1666219820Sjeff goto Exit; 1667219820Sjeff } 1668219820Sjeff 1669219820Sjeff if (osm_log_is_active(sa->p_log, OSM_LOG_DEBUG)) 1670219820Sjeff osm_dump_path_record(sa->p_log, p_pr, OSM_LOG_DEBUG); 1671219820Sjeff 1672219820Sjeff cl_qlist_init(&pr_list); 1673219820Sjeff 1674219820Sjeff /* 1675219820Sjeff Most SA functions (including this one) are read-only on the 1676219820Sjeff subnet object, so we grab the lock non-exclusively. 1677219820Sjeff */ 1678219820Sjeff cl_plock_acquire(sa->p_lock); 1679219820Sjeff 1680219820Sjeff /* Handle multicast destinations separately */ 1681219820Sjeff if ((ret = __osm_pr_rcv_check_mcast_dest(sa, p_madw)) < 0) { 1682219820Sjeff /* Multicast DGID with unicast DLID */ 1683219820Sjeff cl_plock_release(sa->p_lock); 1684219820Sjeff osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_INVALID_FIELD); 1685219820Sjeff goto Exit; 1686219820Sjeff } 1687219820Sjeff 1688219820Sjeff if (ret > 0) 1689219820Sjeff goto McastDest; 1690219820Sjeff 1691219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Unicast destination requested\n"); 1692219820Sjeff 1693219820Sjeff sa_status = __osm_pr_rcv_get_end_points(sa, p_madw, 1694219820Sjeff &p_src_port, &p_dest_port, 1695219820Sjeff &dgid); 1696219820Sjeff 1697219820Sjeff if (sa_status == IB_SA_MAD_STATUS_SUCCESS) { 1698219820Sjeff /* 1699219820Sjeff What happens next depends on the type of endpoint information 1700219820Sjeff that was specified.... 1701219820Sjeff */ 1702219820Sjeff if (p_src_port) { 1703219820Sjeff if (p_dest_port) 1704219820Sjeff __osm_pr_rcv_process_pair(sa, p_madw, 1705219820Sjeff requester_port, 1706219820Sjeff p_src_port, 1707219820Sjeff p_dest_port, &dgid, 1708219820Sjeff p_sa_mad->comp_mask, 1709219820Sjeff &pr_list); 1710219820Sjeff else 1711219820Sjeff __osm_pr_rcv_process_half(sa, p_madw, 1712219820Sjeff requester_port, 1713219820Sjeff p_src_port, NULL, 1714219820Sjeff &dgid, 1715219820Sjeff p_sa_mad->comp_mask, 1716219820Sjeff &pr_list); 1717219820Sjeff } else { 1718219820Sjeff if (p_dest_port) 1719219820Sjeff __osm_pr_rcv_process_half(sa, p_madw, 1720219820Sjeff requester_port, NULL, 1721219820Sjeff p_dest_port, &dgid, 1722219820Sjeff p_sa_mad->comp_mask, 1723219820Sjeff &pr_list); 1724219820Sjeff else 1725219820Sjeff /* 1726219820Sjeff Katie, bar the door! 1727219820Sjeff */ 1728219820Sjeff __osm_pr_rcv_process_world(sa, p_madw, 1729219820Sjeff requester_port, 1730219820Sjeff &dgid, 1731219820Sjeff p_sa_mad->comp_mask, 1732219820Sjeff &pr_list); 1733219820Sjeff } 1734219820Sjeff } 1735219820Sjeff goto Unlock; 1736219820Sjeff 1737219820SjeffMcastDest: 1738219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Multicast destination requested\n"); 1739219820Sjeff { 1740219820Sjeff osm_mgrp_t *p_mgrp = NULL; 1741219820Sjeff ib_api_status_t status; 1742219820Sjeff osm_pr_item_t *p_pr_item; 1743219820Sjeff uint32_t flow_label; 1744219820Sjeff uint8_t sl; 1745219820Sjeff uint8_t hop_limit; 1746219820Sjeff 1747219820Sjeff /* First, get the MC info */ 1748219820Sjeff p_mgrp = pr_get_mgrp(sa, p_madw); 1749219820Sjeff 1750219820Sjeff if (!p_mgrp) 1751219820Sjeff goto Unlock; 1752219820Sjeff 1753219820Sjeff /* Make sure the rest of the PathRecord matches the MC group attributes */ 1754219820Sjeff status = __osm_pr_match_mgrp_attributes(sa, p_madw, p_mgrp); 1755219820Sjeff if (status != IB_SUCCESS) { 1756219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F19: " 1757219820Sjeff "MC group attributes don't match PathRecord request\n"); 1758219820Sjeff goto Unlock; 1759219820Sjeff } 1760219820Sjeff 1761219820Sjeff p_pr_item = malloc(sizeof(*p_pr_item)); 1762219820Sjeff if (p_pr_item == NULL) { 1763219820Sjeff OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F18: " 1764219820Sjeff "Unable to allocate path record for MC group\n"); 1765219820Sjeff goto Unlock; 1766219820Sjeff } 1767219820Sjeff memset(p_pr_item, 0, sizeof(*p_pr_item)); 1768219820Sjeff 1769219820Sjeff /* Copy PathRecord request into response */ 1770219820Sjeff p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw); 1771219820Sjeff p_pr = (ib_path_rec_t *) 1772219820Sjeff ib_sa_mad_get_payload_ptr(p_sa_mad); 1773219820Sjeff p_pr_item->path_rec = *p_pr; 1774219820Sjeff 1775219820Sjeff /* Now, use the MC info to cruft up the PathRecord response */ 1776219820Sjeff p_pr_item->path_rec.dgid = p_mgrp->mcmember_rec.mgid; 1777219820Sjeff p_pr_item->path_rec.dlid = p_mgrp->mcmember_rec.mlid; 1778219820Sjeff p_pr_item->path_rec.tclass = p_mgrp->mcmember_rec.tclass; 1779219820Sjeff p_pr_item->path_rec.num_path = 1; 1780219820Sjeff p_pr_item->path_rec.pkey = p_mgrp->mcmember_rec.pkey; 1781219820Sjeff 1782219820Sjeff /* MTU, rate, and packet lifetime should be exactly */ 1783219820Sjeff p_pr_item->path_rec.mtu = (2 << 6) | p_mgrp->mcmember_rec.mtu; 1784219820Sjeff p_pr_item->path_rec.rate = (2 << 6) | p_mgrp->mcmember_rec.rate; 1785219820Sjeff p_pr_item->path_rec.pkt_life = 1786219820Sjeff (2 << 6) | p_mgrp->mcmember_rec.pkt_life; 1787219820Sjeff 1788219820Sjeff /* SL, Hop Limit, and Flow Label */ 1789219820Sjeff ib_member_get_sl_flow_hop(p_mgrp->mcmember_rec.sl_flow_hop, 1790219820Sjeff &sl, &flow_label, &hop_limit); 1791219820Sjeff ib_path_rec_set_sl(&p_pr_item->path_rec, sl); 1792219820Sjeff ib_path_rec_set_qos_class(&p_pr_item->path_rec, 0); 1793219820Sjeff 1794219820Sjeff /* HopLimit is not yet set in non link local MC groups */ 1795219820Sjeff /* If it were, this would not be needed */ 1796219820Sjeff if (ib_mgid_get_scope(&p_mgrp->mcmember_rec.mgid) != IB_MC_SCOPE_LINK_LOCAL) 1797219820Sjeff hop_limit = IB_HOPLIMIT_MAX; 1798219820Sjeff 1799219820Sjeff p_pr_item->path_rec.hop_flow_raw = 1800219820Sjeff cl_hton32(hop_limit) | (flow_label << 8); 1801219820Sjeff 1802219820Sjeff cl_qlist_insert_tail(&pr_list, &p_pr_item->list_item); 1803219820Sjeff } 1804219820Sjeff 1805219820SjeffUnlock: 1806219820Sjeff cl_plock_release(sa->p_lock); 1807219820Sjeff 1808219820Sjeff /* Now, (finally) respond to the PathRecord request */ 1809219820Sjeff osm_sa_respond(sa, p_madw, sizeof(ib_path_rec_t), &pr_list); 1810219820Sjeff 1811219820SjeffExit: 1812219820Sjeff OSM_LOG_EXIT(sa->p_log); 1813219820Sjeff} 1814