514 lines · c
1/*2 * Copyright(c) 2017 Intel Corporation.3 *4 * This file is provided under a dual BSD/GPLv2 license. When using or5 * redistributing this file, you may do so under either license.6 *7 * GPL LICENSE SUMMARY8 *9 * This program is free software; you can redistribute it and/or modify10 * it under the terms of version 2 of the GNU General Public License as11 * published by the Free Software Foundation.12 *13 * This program is distributed in the hope that it will be useful, but14 * WITHOUT ANY WARRANTY; without even the implied warranty of15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU16 * General Public License for more details.17 *18 * BSD LICENSE19 *20 * Redistribution and use in source and binary forms, with or without21 * modification, are permitted provided that the following conditions22 * are met:23 *24 * - Redistributions of source code must retain the above copyright25 * notice, this list of conditions and the following disclaimer.26 * - Redistributions in binary form must reproduce the above copyright27 * notice, this list of conditions and the following disclaimer in28 * the documentation and/or other materials provided with the29 * distribution.30 * - Neither the name of Intel Corporation nor the names of its31 * contributors may be used to endorse or promote products derived32 * from this software without specific prior written permission.33 *34 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS35 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT36 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR37 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT38 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,39 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT40 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,41 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY42 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT43 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE44 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.45 *46 */47 48/*49 * This file contains OPA VNIC encapsulation/decapsulation function.50 */51 52#include <linux/if_ether.h>53#include <linux/if_vlan.h>54 55#include "opa_vnic_internal.h"56 57/* OPA 16B Header fields */58#define OPA_16B_LID_MASK 0xFFFFFull59#define OPA_16B_SLID_HIGH_SHFT 860#define OPA_16B_SLID_MASK 0xF00ull61#define OPA_16B_DLID_MASK 0xF000ull62#define OPA_16B_DLID_HIGH_SHFT 1263#define OPA_16B_LEN_SHFT 2064#define OPA_16B_SC_SHFT 2065#define OPA_16B_RC_SHFT 2566#define OPA_16B_PKEY_SHFT 1667 68#define OPA_VNIC_L4_HDR_SHFT 1669 70/* L2+L4 hdr len is 20 bytes (5 quad words) */71#define OPA_VNIC_HDR_QW_LEN 572 73static inline void opa_vnic_make_header(u8 *hdr, u32 slid, u32 dlid, u16 len,74 u16 pkey, u16 entropy, u8 sc, u8 rc,75 u8 l4_type, u16 l4_hdr)76{77 /* h[1]: LT=1, 16B L2=10 */78 u32 h[OPA_VNIC_HDR_QW_LEN] = {0, 0xc0000000, 0, 0, 0};79 80 h[2] = l4_type;81 h[3] = entropy;82 h[4] = l4_hdr << OPA_VNIC_L4_HDR_SHFT;83 84 /* Extract and set 4 upper bits and 20 lower bits of the lids */85 h[0] |= (slid & OPA_16B_LID_MASK);86 h[2] |= ((slid >> (20 - OPA_16B_SLID_HIGH_SHFT)) & OPA_16B_SLID_MASK);87 88 h[1] |= (dlid & OPA_16B_LID_MASK);89 h[2] |= ((dlid >> (20 - OPA_16B_DLID_HIGH_SHFT)) & OPA_16B_DLID_MASK);90 91 h[0] |= (len << OPA_16B_LEN_SHFT);92 h[1] |= (rc << OPA_16B_RC_SHFT);93 h[1] |= (sc << OPA_16B_SC_SHFT);94 h[2] |= ((u32)pkey << OPA_16B_PKEY_SHFT);95 96 memcpy(hdr, h, OPA_VNIC_HDR_LEN);97}98 99/*100 * Using a simple hash table for mac table implementation with the last octet101 * of mac address as a key.102 */103static void opa_vnic_free_mac_tbl(struct hlist_head *mactbl)104{105 struct opa_vnic_mac_tbl_node *node;106 struct hlist_node *tmp;107 int bkt;108 109 if (!mactbl)110 return;111 112 vnic_hash_for_each_safe(mactbl, bkt, tmp, node, hlist) {113 hash_del(&node->hlist);114 kfree(node);115 }116 kfree(mactbl);117}118 119static struct hlist_head *opa_vnic_alloc_mac_tbl(void)120{121 u32 size = sizeof(struct hlist_head) * OPA_VNIC_MAC_TBL_SIZE;122 struct hlist_head *mactbl;123 124 mactbl = kzalloc(size, GFP_KERNEL);125 if (!mactbl)126 return ERR_PTR(-ENOMEM);127 128 vnic_hash_init(mactbl);129 return mactbl;130}131 132/* opa_vnic_release_mac_tbl - empty and free the mac table */133void opa_vnic_release_mac_tbl(struct opa_vnic_adapter *adapter)134{135 struct hlist_head *mactbl;136 137 mutex_lock(&adapter->mactbl_lock);138 mactbl = rcu_access_pointer(adapter->mactbl);139 rcu_assign_pointer(adapter->mactbl, NULL);140 synchronize_rcu();141 opa_vnic_free_mac_tbl(mactbl);142 adapter->info.vport.mac_tbl_digest = 0;143 mutex_unlock(&adapter->mactbl_lock);144}145 146/*147 * opa_vnic_query_mac_tbl - query the mac table for a section148 *149 * This function implements query of specific function of the mac table.150 * The function also expects the requested range to be valid.151 */152void opa_vnic_query_mac_tbl(struct opa_vnic_adapter *adapter,153 struct opa_veswport_mactable *tbl)154{155 struct opa_vnic_mac_tbl_node *node;156 struct hlist_head *mactbl;157 int bkt;158 u16 loffset, lnum_entries;159 160 rcu_read_lock();161 mactbl = rcu_dereference(adapter->mactbl);162 if (!mactbl)163 goto get_mac_done;164 165 loffset = be16_to_cpu(tbl->offset);166 lnum_entries = be16_to_cpu(tbl->num_entries);167 168 vnic_hash_for_each(mactbl, bkt, node, hlist) {169 struct __opa_vnic_mactable_entry *nentry = &node->entry;170 struct opa_veswport_mactable_entry *entry;171 172 if ((node->index < loffset) ||173 (node->index >= (loffset + lnum_entries)))174 continue;175 176 /* populate entry in the tbl corresponding to the index */177 entry = &tbl->tbl_entries[node->index - loffset];178 memcpy(entry->mac_addr, nentry->mac_addr,179 ARRAY_SIZE(entry->mac_addr));180 memcpy(entry->mac_addr_mask, nentry->mac_addr_mask,181 ARRAY_SIZE(entry->mac_addr_mask));182 entry->dlid_sd = cpu_to_be32(nentry->dlid_sd);183 }184 tbl->mac_tbl_digest = cpu_to_be32(adapter->info.vport.mac_tbl_digest);185get_mac_done:186 rcu_read_unlock();187}188 189/*190 * opa_vnic_update_mac_tbl - update mac table section191 *192 * This function updates the specified section of the mac table.193 * The procedure includes following steps.194 * - Allocate a new mac (hash) table.195 * - Add the specified entries to the new table.196 * (except the ones that are requested to be deleted).197 * - Add all the other entries from the old mac table.198 * - If there is a failure, free the new table and return.199 * - Switch to the new table.200 * - Free the old table and return.201 *202 * The function also expects the requested range to be valid.203 */204int opa_vnic_update_mac_tbl(struct opa_vnic_adapter *adapter,205 struct opa_veswport_mactable *tbl)206{207 struct opa_vnic_mac_tbl_node *node, *new_node;208 struct hlist_head *new_mactbl, *old_mactbl;209 int i, bkt, rc = 0;210 u8 key;211 u16 loffset, lnum_entries;212 213 mutex_lock(&adapter->mactbl_lock);214 /* allocate new mac table */215 new_mactbl = opa_vnic_alloc_mac_tbl();216 if (IS_ERR(new_mactbl)) {217 mutex_unlock(&adapter->mactbl_lock);218 return PTR_ERR(new_mactbl);219 }220 221 loffset = be16_to_cpu(tbl->offset);222 lnum_entries = be16_to_cpu(tbl->num_entries);223 224 /* add updated entries to the new mac table */225 for (i = 0; i < lnum_entries; i++) {226 struct __opa_vnic_mactable_entry *nentry;227 struct opa_veswport_mactable_entry *entry =228 &tbl->tbl_entries[i];229 u8 *mac_addr = entry->mac_addr;230 u8 empty_mac[ETH_ALEN] = { 0 };231 232 v_dbg("new mac entry %4d: %02x:%02x:%02x:%02x:%02x:%02x %x\n",233 loffset + i, mac_addr[0], mac_addr[1], mac_addr[2],234 mac_addr[3], mac_addr[4], mac_addr[5],235 entry->dlid_sd);236 237 /* if the entry is being removed, do not add it */238 if (!memcmp(mac_addr, empty_mac, ARRAY_SIZE(empty_mac)))239 continue;240 241 node = kzalloc(sizeof(*node), GFP_KERNEL);242 if (!node) {243 rc = -ENOMEM;244 goto updt_done;245 }246 247 node->index = loffset + i;248 nentry = &node->entry;249 memcpy(nentry->mac_addr, entry->mac_addr,250 ARRAY_SIZE(nentry->mac_addr));251 memcpy(nentry->mac_addr_mask, entry->mac_addr_mask,252 ARRAY_SIZE(nentry->mac_addr_mask));253 nentry->dlid_sd = be32_to_cpu(entry->dlid_sd);254 key = node->entry.mac_addr[OPA_VNIC_MAC_HASH_IDX];255 vnic_hash_add(new_mactbl, &node->hlist, key);256 }257 258 /* add other entries from current mac table to new mac table */259 old_mactbl = rcu_access_pointer(adapter->mactbl);260 if (!old_mactbl)261 goto switch_tbl;262 263 vnic_hash_for_each(old_mactbl, bkt, node, hlist) {264 if ((node->index >= loffset) &&265 (node->index < (loffset + lnum_entries)))266 continue;267 268 new_node = kzalloc(sizeof(*new_node), GFP_KERNEL);269 if (!new_node) {270 rc = -ENOMEM;271 goto updt_done;272 }273 274 new_node->index = node->index;275 memcpy(&new_node->entry, &node->entry, sizeof(node->entry));276 key = new_node->entry.mac_addr[OPA_VNIC_MAC_HASH_IDX];277 vnic_hash_add(new_mactbl, &new_node->hlist, key);278 }279 280switch_tbl:281 /* switch to new table */282 rcu_assign_pointer(adapter->mactbl, new_mactbl);283 synchronize_rcu();284 285 adapter->info.vport.mac_tbl_digest = be32_to_cpu(tbl->mac_tbl_digest);286updt_done:287 /* upon failure, free the new table; otherwise, free the old table */288 if (rc)289 opa_vnic_free_mac_tbl(new_mactbl);290 else291 opa_vnic_free_mac_tbl(old_mactbl);292 293 mutex_unlock(&adapter->mactbl_lock);294 return rc;295}296 297/* opa_vnic_chk_mac_tbl - check mac table for dlid */298static uint32_t opa_vnic_chk_mac_tbl(struct opa_vnic_adapter *adapter,299 struct ethhdr *mac_hdr)300{301 struct opa_vnic_mac_tbl_node *node;302 struct hlist_head *mactbl;303 u32 dlid = 0;304 u8 key;305 306 rcu_read_lock();307 mactbl = rcu_dereference(adapter->mactbl);308 if (unlikely(!mactbl))309 goto chk_done;310 311 key = mac_hdr->h_dest[OPA_VNIC_MAC_HASH_IDX];312 vnic_hash_for_each_possible(mactbl, node, hlist, key) {313 struct __opa_vnic_mactable_entry *entry = &node->entry;314 315 /* if related to source mac, skip */316 if (unlikely(OPA_VNIC_DLID_SD_IS_SRC_MAC(entry->dlid_sd)))317 continue;318 319 if (!memcmp(node->entry.mac_addr, mac_hdr->h_dest,320 ARRAY_SIZE(node->entry.mac_addr))) {321 /* mac address found */322 dlid = OPA_VNIC_DLID_SD_GET_DLID(node->entry.dlid_sd);323 break;324 }325 }326 327chk_done:328 rcu_read_unlock();329 return dlid;330}331 332/* opa_vnic_get_dlid - find and return the DLID */333static uint32_t opa_vnic_get_dlid(struct opa_vnic_adapter *adapter,334 struct sk_buff *skb, u8 def_port)335{336 struct __opa_veswport_info *info = &adapter->info;337 struct ethhdr *mac_hdr = (struct ethhdr *)skb_mac_header(skb);338 u32 dlid;339 340 dlid = opa_vnic_chk_mac_tbl(adapter, mac_hdr);341 if (dlid)342 return dlid;343 344 if (is_multicast_ether_addr(mac_hdr->h_dest)) {345 dlid = info->vesw.u_mcast_dlid;346 } else {347 if (is_local_ether_addr(mac_hdr->h_dest)) {348 dlid = ((uint32_t)mac_hdr->h_dest[5] << 16) |349 ((uint32_t)mac_hdr->h_dest[4] << 8) |350 mac_hdr->h_dest[3];351 if (unlikely(!dlid))352 v_warn("Null dlid in MAC address\n");353 } else if (def_port != OPA_VNIC_INVALID_PORT) {354 if (def_port < OPA_VESW_MAX_NUM_DEF_PORT)355 dlid = info->vesw.u_ucast_dlid[def_port];356 }357 }358 359 return dlid;360}361 362/* opa_vnic_get_sc - return the service class */363static u8 opa_vnic_get_sc(struct __opa_veswport_info *info,364 struct sk_buff *skb)365{366 struct ethhdr *mac_hdr = (struct ethhdr *)skb_mac_header(skb);367 u16 vlan_tci;368 u8 sc;369 370 if (!__vlan_get_tag(skb, &vlan_tci)) {371 u8 pcp = OPA_VNIC_VLAN_PCP(vlan_tci);372 373 if (is_multicast_ether_addr(mac_hdr->h_dest))374 sc = info->vport.pcp_to_sc_mc[pcp];375 else376 sc = info->vport.pcp_to_sc_uc[pcp];377 } else {378 if (is_multicast_ether_addr(mac_hdr->h_dest))379 sc = info->vport.non_vlan_sc_mc;380 else381 sc = info->vport.non_vlan_sc_uc;382 }383 384 return sc;385}386 387u8 opa_vnic_get_vl(struct opa_vnic_adapter *adapter, struct sk_buff *skb)388{389 struct ethhdr *mac_hdr = (struct ethhdr *)skb_mac_header(skb);390 struct __opa_veswport_info *info = &adapter->info;391 u8 vl;392 393 if (skb_vlan_tag_present(skb)) {394 u8 pcp = skb_vlan_tag_get(skb) >> VLAN_PRIO_SHIFT;395 396 if (is_multicast_ether_addr(mac_hdr->h_dest))397 vl = info->vport.pcp_to_vl_mc[pcp];398 else399 vl = info->vport.pcp_to_vl_uc[pcp];400 } else {401 if (is_multicast_ether_addr(mac_hdr->h_dest))402 vl = info->vport.non_vlan_vl_mc;403 else404 vl = info->vport.non_vlan_vl_uc;405 }406 407 return vl;408}409 410/* opa_vnic_get_rc - return the routing control */411static u8 opa_vnic_get_rc(struct __opa_veswport_info *info,412 struct sk_buff *skb)413{414 u8 proto, rout_ctrl;415 416 switch (vlan_get_protocol(skb)) {417 case htons(ETH_P_IPV6):418 proto = ipv6_hdr(skb)->nexthdr;419 if (proto == IPPROTO_TCP)420 rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc,421 IPV6_TCP);422 else if (proto == IPPROTO_UDP)423 rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc,424 IPV6_UDP);425 else426 rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc, IPV6);427 break;428 case htons(ETH_P_IP):429 proto = ip_hdr(skb)->protocol;430 if (proto == IPPROTO_TCP)431 rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc,432 IPV4_TCP);433 else if (proto == IPPROTO_UDP)434 rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc,435 IPV4_UDP);436 else437 rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc, IPV4);438 break;439 default:440 rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc, DEFAULT);441 }442 443 return rout_ctrl;444}445 446/* opa_vnic_calc_entropy - calculate the packet entropy */447u8 opa_vnic_calc_entropy(struct sk_buff *skb)448{449 u32 hash = skb_get_hash(skb);450 451 /* store XOR of all bytes in lower 8 bits */452 hash ^= hash >> 8;453 hash ^= hash >> 16;454 455 /* return lower 8 bits as entropy */456 return (u8)(hash & 0xFF);457}458 459/* opa_vnic_get_def_port - get default port based on entropy */460static inline u8 opa_vnic_get_def_port(struct opa_vnic_adapter *adapter,461 u8 entropy)462{463 u8 flow_id;464 465 /* Add the upper and lower 4-bits of entropy to get the flow id */466 flow_id = ((entropy & 0xf) + (entropy >> 4));467 return adapter->flow_tbl[flow_id & (OPA_VNIC_FLOW_TBL_SIZE - 1)];468}469 470/* Calculate packet length including OPA header, crc and padding */471static inline int opa_vnic_wire_length(struct sk_buff *skb)472{473 u32 pad_len;474 475 /* padding for 8 bytes size alignment */476 pad_len = -(skb->len + OPA_VNIC_ICRC_TAIL_LEN) & 0x7;477 pad_len += OPA_VNIC_ICRC_TAIL_LEN;478 479 return (skb->len + pad_len) >> 3;480}481 482/* opa_vnic_encap_skb - encapsulate skb packet with OPA header and meta data */483void opa_vnic_encap_skb(struct opa_vnic_adapter *adapter, struct sk_buff *skb)484{485 struct __opa_veswport_info *info = &adapter->info;486 struct opa_vnic_skb_mdata *mdata;487 u8 def_port, sc, rc, entropy, *hdr;488 u16 len, l4_hdr;489 u32 dlid;490 491 hdr = skb_push(skb, OPA_VNIC_HDR_LEN);492 493 entropy = opa_vnic_calc_entropy(skb);494 def_port = opa_vnic_get_def_port(adapter, entropy);495 len = opa_vnic_wire_length(skb);496 dlid = opa_vnic_get_dlid(adapter, skb, def_port);497 sc = opa_vnic_get_sc(info, skb);498 rc = opa_vnic_get_rc(info, skb);499 l4_hdr = info->vesw.vesw_id;500 501 mdata = skb_push(skb, sizeof(*mdata));502 mdata->vl = opa_vnic_get_vl(adapter, skb);503 mdata->entropy = entropy;504 mdata->flags = 0;505 if (unlikely(!dlid)) {506 mdata->flags = OPA_VNIC_SKB_MDATA_ENCAP_ERR;507 return;508 }509 510 opa_vnic_make_header(hdr, info->vport.encap_slid, dlid, len,511 info->vesw.pkey, entropy, sc, rc,512 OPA_VNIC_L4_ETHR, l4_hdr);513}514