162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2020 Mellanox Technologies. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <rdma/ib_verbs.h> 762306a36Sopenharmony_ci#include <rdma/ib_cache.h> 862306a36Sopenharmony_ci#include <rdma/lag.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistatic struct sk_buff *rdma_build_skb(struct net_device *netdev, 1162306a36Sopenharmony_ci struct rdma_ah_attr *ah_attr, 1262306a36Sopenharmony_ci gfp_t flags) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci struct ipv6hdr *ip6h; 1562306a36Sopenharmony_ci struct sk_buff *skb; 1662306a36Sopenharmony_ci struct ethhdr *eth; 1762306a36Sopenharmony_ci struct iphdr *iph; 1862306a36Sopenharmony_ci struct udphdr *uh; 1962306a36Sopenharmony_ci u8 smac[ETH_ALEN]; 2062306a36Sopenharmony_ci bool is_ipv4; 2162306a36Sopenharmony_ci int hdr_len; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci is_ipv4 = ipv6_addr_v4mapped((struct in6_addr *)ah_attr->grh.dgid.raw); 2462306a36Sopenharmony_ci hdr_len = ETH_HLEN + sizeof(struct udphdr) + LL_RESERVED_SPACE(netdev); 2562306a36Sopenharmony_ci hdr_len += is_ipv4 ? sizeof(struct iphdr) : sizeof(struct ipv6hdr); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci skb = alloc_skb(hdr_len, flags); 2862306a36Sopenharmony_ci if (!skb) 2962306a36Sopenharmony_ci return NULL; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci skb->dev = netdev; 3262306a36Sopenharmony_ci skb_reserve(skb, hdr_len); 3362306a36Sopenharmony_ci skb_push(skb, sizeof(struct udphdr)); 3462306a36Sopenharmony_ci skb_reset_transport_header(skb); 3562306a36Sopenharmony_ci uh = udp_hdr(skb); 3662306a36Sopenharmony_ci uh->source = 3762306a36Sopenharmony_ci htons(rdma_flow_label_to_udp_sport(ah_attr->grh.flow_label)); 3862306a36Sopenharmony_ci uh->dest = htons(ROCE_V2_UDP_DPORT); 3962306a36Sopenharmony_ci uh->len = htons(sizeof(struct udphdr)); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (is_ipv4) { 4262306a36Sopenharmony_ci skb_push(skb, sizeof(struct iphdr)); 4362306a36Sopenharmony_ci skb_reset_network_header(skb); 4462306a36Sopenharmony_ci iph = ip_hdr(skb); 4562306a36Sopenharmony_ci iph->frag_off = 0; 4662306a36Sopenharmony_ci iph->version = 4; 4762306a36Sopenharmony_ci iph->protocol = IPPROTO_UDP; 4862306a36Sopenharmony_ci iph->ihl = 0x5; 4962306a36Sopenharmony_ci iph->tot_len = htons(sizeof(struct udphdr) + sizeof(struct 5062306a36Sopenharmony_ci iphdr)); 5162306a36Sopenharmony_ci memcpy(&iph->saddr, ah_attr->grh.sgid_attr->gid.raw + 12, 5262306a36Sopenharmony_ci sizeof(struct in_addr)); 5362306a36Sopenharmony_ci memcpy(&iph->daddr, ah_attr->grh.dgid.raw + 12, 5462306a36Sopenharmony_ci sizeof(struct in_addr)); 5562306a36Sopenharmony_ci } else { 5662306a36Sopenharmony_ci skb_push(skb, sizeof(struct ipv6hdr)); 5762306a36Sopenharmony_ci skb_reset_network_header(skb); 5862306a36Sopenharmony_ci ip6h = ipv6_hdr(skb); 5962306a36Sopenharmony_ci ip6h->version = 6; 6062306a36Sopenharmony_ci ip6h->nexthdr = IPPROTO_UDP; 6162306a36Sopenharmony_ci memcpy(&ip6h->flow_lbl, &ah_attr->grh.flow_label, 6262306a36Sopenharmony_ci sizeof(*ip6h->flow_lbl)); 6362306a36Sopenharmony_ci memcpy(&ip6h->saddr, ah_attr->grh.sgid_attr->gid.raw, 6462306a36Sopenharmony_ci sizeof(struct in6_addr)); 6562306a36Sopenharmony_ci memcpy(&ip6h->daddr, ah_attr->grh.dgid.raw, 6662306a36Sopenharmony_ci sizeof(struct in6_addr)); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci skb_push(skb, sizeof(struct ethhdr)); 7062306a36Sopenharmony_ci skb_reset_mac_header(skb); 7162306a36Sopenharmony_ci eth = eth_hdr(skb); 7262306a36Sopenharmony_ci skb->protocol = eth->h_proto = htons(is_ipv4 ? ETH_P_IP : ETH_P_IPV6); 7362306a36Sopenharmony_ci rdma_read_gid_l2_fields(ah_attr->grh.sgid_attr, NULL, smac); 7462306a36Sopenharmony_ci memcpy(eth->h_source, smac, ETH_ALEN); 7562306a36Sopenharmony_ci memcpy(eth->h_dest, ah_attr->roce.dmac, ETH_ALEN); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return skb; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic struct net_device *rdma_get_xmit_slave_udp(struct ib_device *device, 8162306a36Sopenharmony_ci struct net_device *master, 8262306a36Sopenharmony_ci struct rdma_ah_attr *ah_attr, 8362306a36Sopenharmony_ci gfp_t flags) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct net_device *slave; 8662306a36Sopenharmony_ci struct sk_buff *skb; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci skb = rdma_build_skb(master, ah_attr, flags); 8962306a36Sopenharmony_ci if (!skb) 9062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci rcu_read_lock(); 9362306a36Sopenharmony_ci slave = netdev_get_xmit_slave(master, skb, 9462306a36Sopenharmony_ci !!(device->lag_flags & 9562306a36Sopenharmony_ci RDMA_LAG_FLAGS_HASH_ALL_SLAVES)); 9662306a36Sopenharmony_ci if (slave) 9762306a36Sopenharmony_ci dev_hold(slave); 9862306a36Sopenharmony_ci rcu_read_unlock(); 9962306a36Sopenharmony_ci kfree_skb(skb); 10062306a36Sopenharmony_ci return slave; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_civoid rdma_lag_put_ah_roce_slave(struct net_device *xmit_slave) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci if (xmit_slave) 10662306a36Sopenharmony_ci dev_put(xmit_slave); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistruct net_device *rdma_lag_get_ah_roce_slave(struct ib_device *device, 11062306a36Sopenharmony_ci struct rdma_ah_attr *ah_attr, 11162306a36Sopenharmony_ci gfp_t flags) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct net_device *slave = NULL; 11462306a36Sopenharmony_ci struct net_device *master; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (!(ah_attr->type == RDMA_AH_ATTR_TYPE_ROCE && 11762306a36Sopenharmony_ci ah_attr->grh.sgid_attr->gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP && 11862306a36Sopenharmony_ci ah_attr->grh.flow_label)) 11962306a36Sopenharmony_ci return NULL; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci rcu_read_lock(); 12262306a36Sopenharmony_ci master = rdma_read_gid_attr_ndev_rcu(ah_attr->grh.sgid_attr); 12362306a36Sopenharmony_ci if (IS_ERR(master)) { 12462306a36Sopenharmony_ci rcu_read_unlock(); 12562306a36Sopenharmony_ci return master; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci dev_hold(master); 12862306a36Sopenharmony_ci rcu_read_unlock(); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (!netif_is_bond_master(master)) 13162306a36Sopenharmony_ci goto put; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci slave = rdma_get_xmit_slave_udp(device, master, ah_attr, flags); 13462306a36Sopenharmony_ciput: 13562306a36Sopenharmony_ci dev_put(master); 13662306a36Sopenharmony_ci return slave; 13762306a36Sopenharmony_ci} 138