xref: /OK3568_Linux_fs/kernel/drivers/infiniband/core/lag.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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