162306a36Sopenharmony_ci/* This file is part of the Emulex RoCE Device Driver for 262306a36Sopenharmony_ci * RoCE (RDMA over Converged Ethernet) adapters. 362306a36Sopenharmony_ci * Copyright (C) 2012-2015 Emulex. All rights reserved. 462306a36Sopenharmony_ci * EMULEX and SLI are trademarks of Emulex. 562306a36Sopenharmony_ci * www.emulex.com 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This software is available to you under a choice of one of two licenses. 862306a36Sopenharmony_ci * You may choose to be licensed under the terms of the GNU General Public 962306a36Sopenharmony_ci * License (GPL) Version 2, available from the file COPYING in the main 1062306a36Sopenharmony_ci * directory of this source tree, or the BSD license below: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 1362306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 1462306a36Sopenharmony_ci * are met: 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * - Redistributions of source code must retain the above copyright notice, 1762306a36Sopenharmony_ci * this list of conditions and the following disclaimer. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above copyright 2062306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 2162306a36Sopenharmony_ci * the documentation and/or other materials provided with the distribution. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2462306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,THE 2562306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2662306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 2762306a36Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2862306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2962306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 3062306a36Sopenharmony_ci * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 3162306a36Sopenharmony_ci * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 3262306a36Sopenharmony_ci * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 3362306a36Sopenharmony_ci * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Contact Information: 3662306a36Sopenharmony_ci * linux-drivers@emulex.com 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Emulex 3962306a36Sopenharmony_ci * 3333 Susan Street 4062306a36Sopenharmony_ci * Costa Mesa, CA 92626 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include <net/neighbour.h> 4462306a36Sopenharmony_ci#include <net/netevent.h> 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#include <rdma/ib_addr.h> 4762306a36Sopenharmony_ci#include <rdma/ib_mad.h> 4862306a36Sopenharmony_ci#include <rdma/ib_cache.h> 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include "ocrdma.h" 5162306a36Sopenharmony_ci#include "ocrdma_verbs.h" 5262306a36Sopenharmony_ci#include "ocrdma_ah.h" 5362306a36Sopenharmony_ci#include "ocrdma_hw.h" 5462306a36Sopenharmony_ci#include "ocrdma_stats.h" 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define OCRDMA_VID_PCP_SHIFT 0xD 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic u16 ocrdma_hdr_type_to_proto_num(int devid, u8 hdr_type) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci switch (hdr_type) { 6162306a36Sopenharmony_ci case OCRDMA_L3_TYPE_IB_GRH: 6262306a36Sopenharmony_ci return (u16)ETH_P_IBOE; 6362306a36Sopenharmony_ci case OCRDMA_L3_TYPE_IPV4: 6462306a36Sopenharmony_ci return (u16)0x0800; 6562306a36Sopenharmony_ci case OCRDMA_L3_TYPE_IPV6: 6662306a36Sopenharmony_ci return (u16)0x86dd; 6762306a36Sopenharmony_ci default: 6862306a36Sopenharmony_ci pr_err("ocrdma%d: Invalid network header\n", devid); 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic inline int set_av_attr(struct ocrdma_dev *dev, struct ocrdma_ah *ah, 7462306a36Sopenharmony_ci struct rdma_ah_attr *attr, const union ib_gid *sgid, 7562306a36Sopenharmony_ci int pdid, bool *isvlan, u16 vlan_tag) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci int status; 7862306a36Sopenharmony_ci struct ocrdma_eth_vlan eth; 7962306a36Sopenharmony_ci struct ocrdma_grh grh; 8062306a36Sopenharmony_ci int eth_sz; 8162306a36Sopenharmony_ci u16 proto_num = 0; 8262306a36Sopenharmony_ci u8 nxthdr = 0x11; 8362306a36Sopenharmony_ci struct iphdr ipv4; 8462306a36Sopenharmony_ci const struct ib_global_route *ib_grh; 8562306a36Sopenharmony_ci union { 8662306a36Sopenharmony_ci struct sockaddr_in _sockaddr_in; 8762306a36Sopenharmony_ci struct sockaddr_in6 _sockaddr_in6; 8862306a36Sopenharmony_ci } sgid_addr, dgid_addr; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci memset(ð, 0, sizeof(eth)); 9162306a36Sopenharmony_ci memset(&grh, 0, sizeof(grh)); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Protocol Number */ 9462306a36Sopenharmony_ci proto_num = ocrdma_hdr_type_to_proto_num(dev->id, ah->hdr_type); 9562306a36Sopenharmony_ci if (!proto_num) 9662306a36Sopenharmony_ci return -EINVAL; 9762306a36Sopenharmony_ci nxthdr = (proto_num == ETH_P_IBOE) ? 0x1b : 0x11; 9862306a36Sopenharmony_ci /* VLAN */ 9962306a36Sopenharmony_ci if (!vlan_tag || (vlan_tag > 0xFFF)) 10062306a36Sopenharmony_ci vlan_tag = dev->pvid; 10162306a36Sopenharmony_ci if (vlan_tag || dev->pfc_state) { 10262306a36Sopenharmony_ci if (!vlan_tag) { 10362306a36Sopenharmony_ci pr_err("ocrdma%d:Using VLAN with PFC is recommended\n", 10462306a36Sopenharmony_ci dev->id); 10562306a36Sopenharmony_ci pr_err("ocrdma%d:Using VLAN 0 for this connection\n", 10662306a36Sopenharmony_ci dev->id); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci eth.eth_type = cpu_to_be16(0x8100); 10962306a36Sopenharmony_ci eth.roce_eth_type = cpu_to_be16(proto_num); 11062306a36Sopenharmony_ci vlan_tag |= (dev->sl & 0x07) << OCRDMA_VID_PCP_SHIFT; 11162306a36Sopenharmony_ci eth.vlan_tag = cpu_to_be16(vlan_tag); 11262306a36Sopenharmony_ci eth_sz = sizeof(struct ocrdma_eth_vlan); 11362306a36Sopenharmony_ci *isvlan = true; 11462306a36Sopenharmony_ci } else { 11562306a36Sopenharmony_ci eth.eth_type = cpu_to_be16(proto_num); 11662306a36Sopenharmony_ci eth_sz = sizeof(struct ocrdma_eth_basic); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci /* MAC */ 11962306a36Sopenharmony_ci memcpy(ð.smac[0], &dev->nic_info.mac_addr[0], ETH_ALEN); 12062306a36Sopenharmony_ci status = ocrdma_resolve_dmac(dev, attr, ð.dmac[0]); 12162306a36Sopenharmony_ci if (status) 12262306a36Sopenharmony_ci return status; 12362306a36Sopenharmony_ci ib_grh = rdma_ah_read_grh(attr); 12462306a36Sopenharmony_ci ah->sgid_index = ib_grh->sgid_index; 12562306a36Sopenharmony_ci /* Eth HDR */ 12662306a36Sopenharmony_ci memcpy(&ah->av->eth_hdr, ð, eth_sz); 12762306a36Sopenharmony_ci if (ah->hdr_type == RDMA_NETWORK_IPV4) { 12862306a36Sopenharmony_ci *((__be16 *)&ipv4) = htons((4 << 12) | (5 << 8) | 12962306a36Sopenharmony_ci ib_grh->traffic_class); 13062306a36Sopenharmony_ci ipv4.id = cpu_to_be16(pdid); 13162306a36Sopenharmony_ci ipv4.frag_off = htons(IP_DF); 13262306a36Sopenharmony_ci ipv4.tot_len = htons(0); 13362306a36Sopenharmony_ci ipv4.ttl = ib_grh->hop_limit; 13462306a36Sopenharmony_ci ipv4.protocol = nxthdr; 13562306a36Sopenharmony_ci rdma_gid2ip((struct sockaddr *)&sgid_addr, sgid); 13662306a36Sopenharmony_ci ipv4.saddr = sgid_addr._sockaddr_in.sin_addr.s_addr; 13762306a36Sopenharmony_ci rdma_gid2ip((struct sockaddr*)&dgid_addr, &ib_grh->dgid); 13862306a36Sopenharmony_ci ipv4.daddr = dgid_addr._sockaddr_in.sin_addr.s_addr; 13962306a36Sopenharmony_ci memcpy((u8 *)ah->av + eth_sz, &ipv4, sizeof(struct iphdr)); 14062306a36Sopenharmony_ci } else { 14162306a36Sopenharmony_ci memcpy(&grh.sgid[0], sgid->raw, sizeof(union ib_gid)); 14262306a36Sopenharmony_ci grh.tclass_flow = cpu_to_be32((6 << 28) | 14362306a36Sopenharmony_ci (ib_grh->traffic_class << 24) | 14462306a36Sopenharmony_ci ib_grh->flow_label); 14562306a36Sopenharmony_ci memcpy(&grh.dgid[0], ib_grh->dgid.raw, 14662306a36Sopenharmony_ci sizeof(ib_grh->dgid.raw)); 14762306a36Sopenharmony_ci grh.pdid_hoplimit = cpu_to_be32((pdid << 16) | 14862306a36Sopenharmony_ci (nxthdr << 8) | 14962306a36Sopenharmony_ci ib_grh->hop_limit); 15062306a36Sopenharmony_ci memcpy((u8 *)ah->av + eth_sz, &grh, sizeof(struct ocrdma_grh)); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci if (*isvlan) 15362306a36Sopenharmony_ci ah->av->valid |= OCRDMA_AV_VLAN_VALID; 15462306a36Sopenharmony_ci ah->av->valid = cpu_to_le32(ah->av->valid); 15562306a36Sopenharmony_ci return status; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciint ocrdma_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr, 15962306a36Sopenharmony_ci struct ib_udata *udata) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci u32 *ahid_addr; 16262306a36Sopenharmony_ci int status; 16362306a36Sopenharmony_ci struct ocrdma_ah *ah = get_ocrdma_ah(ibah); 16462306a36Sopenharmony_ci bool isvlan = false; 16562306a36Sopenharmony_ci u16 vlan_tag = 0xffff; 16662306a36Sopenharmony_ci const struct ib_gid_attr *sgid_attr; 16762306a36Sopenharmony_ci struct ocrdma_pd *pd = get_ocrdma_pd(ibah->pd); 16862306a36Sopenharmony_ci struct rdma_ah_attr *attr = init_attr->ah_attr; 16962306a36Sopenharmony_ci struct ocrdma_dev *dev = get_ocrdma_dev(ibah->device); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if ((attr->type != RDMA_AH_ATTR_TYPE_ROCE) || 17262306a36Sopenharmony_ci !(rdma_ah_get_ah_flags(attr) & IB_AH_GRH)) 17362306a36Sopenharmony_ci return -EINVAL; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (atomic_cmpxchg(&dev->update_sl, 1, 0)) 17662306a36Sopenharmony_ci ocrdma_init_service_level(dev); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci sgid_attr = attr->grh.sgid_attr; 17962306a36Sopenharmony_ci status = rdma_read_gid_l2_fields(sgid_attr, &vlan_tag, NULL); 18062306a36Sopenharmony_ci if (status) 18162306a36Sopenharmony_ci return status; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci status = ocrdma_alloc_av(dev, ah); 18462306a36Sopenharmony_ci if (status) 18562306a36Sopenharmony_ci goto av_err; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Get network header type for this GID */ 18862306a36Sopenharmony_ci ah->hdr_type = rdma_gid_attr_network_type(sgid_attr); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci status = set_av_attr(dev, ah, attr, &sgid_attr->gid, pd->id, 19162306a36Sopenharmony_ci &isvlan, vlan_tag); 19262306a36Sopenharmony_ci if (status) 19362306a36Sopenharmony_ci goto av_conf_err; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* if pd is for the user process, pass the ah_id to user space */ 19662306a36Sopenharmony_ci if ((pd->uctx) && (pd->uctx->ah_tbl.va)) { 19762306a36Sopenharmony_ci ahid_addr = pd->uctx->ah_tbl.va + rdma_ah_get_dlid(attr); 19862306a36Sopenharmony_ci *ahid_addr = 0; 19962306a36Sopenharmony_ci *ahid_addr |= ah->id & OCRDMA_AH_ID_MASK; 20062306a36Sopenharmony_ci if (ocrdma_is_udp_encap_supported(dev)) { 20162306a36Sopenharmony_ci *ahid_addr |= ((u32)ah->hdr_type & 20262306a36Sopenharmony_ci OCRDMA_AH_L3_TYPE_MASK) << 20362306a36Sopenharmony_ci OCRDMA_AH_L3_TYPE_SHIFT; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci if (isvlan) 20662306a36Sopenharmony_ci *ahid_addr |= (OCRDMA_AH_VLAN_VALID_MASK << 20762306a36Sopenharmony_ci OCRDMA_AH_VLAN_VALID_SHIFT); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciav_conf_err: 21362306a36Sopenharmony_ci ocrdma_free_av(dev, ah); 21462306a36Sopenharmony_ciav_err: 21562306a36Sopenharmony_ci return status; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ciint ocrdma_destroy_ah(struct ib_ah *ibah, u32 flags) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct ocrdma_ah *ah = get_ocrdma_ah(ibah); 22162306a36Sopenharmony_ci struct ocrdma_dev *dev = get_ocrdma_dev(ibah->device); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ocrdma_free_av(dev, ah); 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ciint ocrdma_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *attr) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct ocrdma_ah *ah = get_ocrdma_ah(ibah); 23062306a36Sopenharmony_ci struct ocrdma_av *av = ah->av; 23162306a36Sopenharmony_ci struct ocrdma_grh *grh; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci attr->type = ibah->type; 23462306a36Sopenharmony_ci if (ah->av->valid & OCRDMA_AV_VALID) { 23562306a36Sopenharmony_ci grh = (struct ocrdma_grh *)((u8 *)ah->av + 23662306a36Sopenharmony_ci sizeof(struct ocrdma_eth_vlan)); 23762306a36Sopenharmony_ci rdma_ah_set_sl(attr, be16_to_cpu(av->eth_hdr.vlan_tag) >> 13); 23862306a36Sopenharmony_ci } else { 23962306a36Sopenharmony_ci grh = (struct ocrdma_grh *)((u8 *)ah->av + 24062306a36Sopenharmony_ci sizeof(struct ocrdma_eth_basic)); 24162306a36Sopenharmony_ci rdma_ah_set_sl(attr, 0); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci rdma_ah_set_grh(attr, NULL, 24462306a36Sopenharmony_ci be32_to_cpu(grh->tclass_flow) & 0xffffffff, 24562306a36Sopenharmony_ci ah->sgid_index, 24662306a36Sopenharmony_ci be32_to_cpu(grh->pdid_hoplimit) & 0xff, 24762306a36Sopenharmony_ci be32_to_cpu(grh->tclass_flow) >> 24); 24862306a36Sopenharmony_ci rdma_ah_set_dgid_raw(attr, &grh->dgid[0]); 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciint ocrdma_process_mad(struct ib_device *ibdev, int process_mad_flags, 25362306a36Sopenharmony_ci u32 port_num, const struct ib_wc *in_wc, 25462306a36Sopenharmony_ci const struct ib_grh *in_grh, const struct ib_mad *in, 25562306a36Sopenharmony_ci struct ib_mad *out, size_t *out_mad_size, 25662306a36Sopenharmony_ci u16 *out_mad_pkey_index) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci int status = IB_MAD_RESULT_SUCCESS; 25962306a36Sopenharmony_ci struct ocrdma_dev *dev; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (in->mad_hdr.mgmt_class == IB_MGMT_CLASS_PERF_MGMT) { 26262306a36Sopenharmony_ci dev = get_ocrdma_dev(ibdev); 26362306a36Sopenharmony_ci ocrdma_pma_counters(dev, out); 26462306a36Sopenharmony_ci status |= IB_MAD_RESULT_REPLY; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return status; 26862306a36Sopenharmony_ci} 269