1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2020 Mellanox Technologies. All rights reserved.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <rdma/ib_verbs.h>
7*4882a593Smuzhiyun #include <rdma/ib_cache.h>
8*4882a593Smuzhiyun #include <rdma/lag.h>
9*4882a593Smuzhiyun
rdma_build_skb(struct ib_device * device,struct net_device * netdev,struct rdma_ah_attr * ah_attr,gfp_t flags)10*4882a593Smuzhiyun static struct sk_buff *rdma_build_skb(struct ib_device *device,
11*4882a593Smuzhiyun struct net_device *netdev,
12*4882a593Smuzhiyun struct rdma_ah_attr *ah_attr,
13*4882a593Smuzhiyun gfp_t flags)
14*4882a593Smuzhiyun {
15*4882a593Smuzhiyun struct ipv6hdr *ip6h;
16*4882a593Smuzhiyun struct sk_buff *skb;
17*4882a593Smuzhiyun struct ethhdr *eth;
18*4882a593Smuzhiyun struct iphdr *iph;
19*4882a593Smuzhiyun struct udphdr *uh;
20*4882a593Smuzhiyun u8 smac[ETH_ALEN];
21*4882a593Smuzhiyun bool is_ipv4;
22*4882a593Smuzhiyun int hdr_len;
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun is_ipv4 = ipv6_addr_v4mapped((struct in6_addr *)ah_attr->grh.dgid.raw);
25*4882a593Smuzhiyun hdr_len = ETH_HLEN + sizeof(struct udphdr) + LL_RESERVED_SPACE(netdev);
26*4882a593Smuzhiyun hdr_len += is_ipv4 ? sizeof(struct iphdr) : sizeof(struct ipv6hdr);
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun skb = alloc_skb(hdr_len, flags);
29*4882a593Smuzhiyun if (!skb)
30*4882a593Smuzhiyun return NULL;
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun skb->dev = netdev;
33*4882a593Smuzhiyun skb_reserve(skb, hdr_len);
34*4882a593Smuzhiyun skb_push(skb, sizeof(struct udphdr));
35*4882a593Smuzhiyun skb_reset_transport_header(skb);
36*4882a593Smuzhiyun uh = udp_hdr(skb);
37*4882a593Smuzhiyun uh->source =
38*4882a593Smuzhiyun htons(rdma_flow_label_to_udp_sport(ah_attr->grh.flow_label));
39*4882a593Smuzhiyun uh->dest = htons(ROCE_V2_UDP_DPORT);
40*4882a593Smuzhiyun uh->len = htons(sizeof(struct udphdr));
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun if (is_ipv4) {
43*4882a593Smuzhiyun skb_push(skb, sizeof(struct iphdr));
44*4882a593Smuzhiyun skb_reset_network_header(skb);
45*4882a593Smuzhiyun iph = ip_hdr(skb);
46*4882a593Smuzhiyun iph->frag_off = 0;
47*4882a593Smuzhiyun iph->version = 4;
48*4882a593Smuzhiyun iph->protocol = IPPROTO_UDP;
49*4882a593Smuzhiyun iph->ihl = 0x5;
50*4882a593Smuzhiyun iph->tot_len = htons(sizeof(struct udphdr) + sizeof(struct
51*4882a593Smuzhiyun iphdr));
52*4882a593Smuzhiyun memcpy(&iph->saddr, ah_attr->grh.sgid_attr->gid.raw + 12,
53*4882a593Smuzhiyun sizeof(struct in_addr));
54*4882a593Smuzhiyun memcpy(&iph->daddr, ah_attr->grh.dgid.raw + 12,
55*4882a593Smuzhiyun sizeof(struct in_addr));
56*4882a593Smuzhiyun } else {
57*4882a593Smuzhiyun skb_push(skb, sizeof(struct ipv6hdr));
58*4882a593Smuzhiyun skb_reset_network_header(skb);
59*4882a593Smuzhiyun ip6h = ipv6_hdr(skb);
60*4882a593Smuzhiyun ip6h->version = 6;
61*4882a593Smuzhiyun ip6h->nexthdr = IPPROTO_UDP;
62*4882a593Smuzhiyun memcpy(&ip6h->flow_lbl, &ah_attr->grh.flow_label,
63*4882a593Smuzhiyun sizeof(*ip6h->flow_lbl));
64*4882a593Smuzhiyun memcpy(&ip6h->saddr, ah_attr->grh.sgid_attr->gid.raw,
65*4882a593Smuzhiyun sizeof(struct in6_addr));
66*4882a593Smuzhiyun memcpy(&ip6h->daddr, ah_attr->grh.dgid.raw,
67*4882a593Smuzhiyun sizeof(struct in6_addr));
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun skb_push(skb, sizeof(struct ethhdr));
71*4882a593Smuzhiyun skb_reset_mac_header(skb);
72*4882a593Smuzhiyun eth = eth_hdr(skb);
73*4882a593Smuzhiyun skb->protocol = eth->h_proto = htons(is_ipv4 ? ETH_P_IP : ETH_P_IPV6);
74*4882a593Smuzhiyun rdma_read_gid_l2_fields(ah_attr->grh.sgid_attr, NULL, smac);
75*4882a593Smuzhiyun memcpy(eth->h_source, smac, ETH_ALEN);
76*4882a593Smuzhiyun memcpy(eth->h_dest, ah_attr->roce.dmac, ETH_ALEN);
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun return skb;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
rdma_get_xmit_slave_udp(struct ib_device * device,struct net_device * master,struct rdma_ah_attr * ah_attr,gfp_t flags)81*4882a593Smuzhiyun static struct net_device *rdma_get_xmit_slave_udp(struct ib_device *device,
82*4882a593Smuzhiyun struct net_device *master,
83*4882a593Smuzhiyun struct rdma_ah_attr *ah_attr,
84*4882a593Smuzhiyun gfp_t flags)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun struct net_device *slave;
87*4882a593Smuzhiyun struct sk_buff *skb;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun skb = rdma_build_skb(device, master, ah_attr, flags);
90*4882a593Smuzhiyun if (!skb)
91*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun rcu_read_lock();
94*4882a593Smuzhiyun slave = netdev_get_xmit_slave(master, skb,
95*4882a593Smuzhiyun !!(device->lag_flags &
96*4882a593Smuzhiyun RDMA_LAG_FLAGS_HASH_ALL_SLAVES));
97*4882a593Smuzhiyun if (slave)
98*4882a593Smuzhiyun dev_hold(slave);
99*4882a593Smuzhiyun rcu_read_unlock();
100*4882a593Smuzhiyun kfree_skb(skb);
101*4882a593Smuzhiyun return slave;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
rdma_lag_put_ah_roce_slave(struct net_device * xmit_slave)104*4882a593Smuzhiyun void rdma_lag_put_ah_roce_slave(struct net_device *xmit_slave)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun if (xmit_slave)
107*4882a593Smuzhiyun dev_put(xmit_slave);
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun
rdma_lag_get_ah_roce_slave(struct ib_device * device,struct rdma_ah_attr * ah_attr,gfp_t flags)110*4882a593Smuzhiyun struct net_device *rdma_lag_get_ah_roce_slave(struct ib_device *device,
111*4882a593Smuzhiyun struct rdma_ah_attr *ah_attr,
112*4882a593Smuzhiyun gfp_t flags)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun struct net_device *slave = NULL;
115*4882a593Smuzhiyun struct net_device *master;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun if (!(ah_attr->type == RDMA_AH_ATTR_TYPE_ROCE &&
118*4882a593Smuzhiyun ah_attr->grh.sgid_attr->gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP &&
119*4882a593Smuzhiyun ah_attr->grh.flow_label))
120*4882a593Smuzhiyun return NULL;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun rcu_read_lock();
123*4882a593Smuzhiyun master = rdma_read_gid_attr_ndev_rcu(ah_attr->grh.sgid_attr);
124*4882a593Smuzhiyun if (IS_ERR(master)) {
125*4882a593Smuzhiyun rcu_read_unlock();
126*4882a593Smuzhiyun return master;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun dev_hold(master);
129*4882a593Smuzhiyun rcu_read_unlock();
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun if (!netif_is_bond_master(master))
132*4882a593Smuzhiyun goto put;
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun slave = rdma_get_xmit_slave_udp(device, master, ah_attr, flags);
135*4882a593Smuzhiyun put:
136*4882a593Smuzhiyun dev_put(master);
137*4882a593Smuzhiyun return slave;
138*4882a593Smuzhiyun }
139