Skip to content

Commit a43e8e9

Browse files
haiyangzPaolo Abeni
authored andcommitted
net: mana: Fix oversized sge0 for GSO packets
Handle the case when GSO SKB linear length is too large. MANA NIC requires GSO packets to put only the header part to SGE0, otherwise the TX queue may stop at the HW level. So, use 2 SGEs for the skb linear part which contains more than the packet header. Fixes: ca9c54d ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Signed-off-by: Haiyang Zhang <[email protected]> Reviewed-by: Simon Horman <[email protected]> Reviewed-by: Shradha Gupta <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent 7a54de9 commit a43e8e9

File tree

2 files changed

+138
-58
lines changed

2 files changed

+138
-58
lines changed

drivers/net/ethernet/microsoft/mana/mana_en.c

Lines changed: 135 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -91,63 +91,137 @@ static unsigned int mana_checksum_info(struct sk_buff *skb)
9191
return 0;
9292
}
9393

94+
static void mana_add_sge(struct mana_tx_package *tp, struct mana_skb_head *ash,
95+
int sg_i, dma_addr_t da, int sge_len, u32 gpa_mkey)
96+
{
97+
ash->dma_handle[sg_i] = da;
98+
ash->size[sg_i] = sge_len;
99+
100+
tp->wqe_req.sgl[sg_i].address = da;
101+
tp->wqe_req.sgl[sg_i].mem_key = gpa_mkey;
102+
tp->wqe_req.sgl[sg_i].size = sge_len;
103+
}
104+
94105
static int mana_map_skb(struct sk_buff *skb, struct mana_port_context *apc,
95-
struct mana_tx_package *tp)
106+
struct mana_tx_package *tp, int gso_hs)
96107
{
97108
struct mana_skb_head *ash = (struct mana_skb_head *)skb->head;
109+
int hsg = 1; /* num of SGEs of linear part */
98110
struct gdma_dev *gd = apc->ac->gdma_dev;
111+
int skb_hlen = skb_headlen(skb);
112+
int sge0_len, sge1_len = 0;
99113
struct gdma_context *gc;
100114
struct device *dev;
101115
skb_frag_t *frag;
102116
dma_addr_t da;
117+
int sg_i;
103118
int i;
104119

105120
gc = gd->gdma_context;
106121
dev = gc->dev;
107-
da = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
108122

123+
if (gso_hs && gso_hs < skb_hlen) {
124+
sge0_len = gso_hs;
125+
sge1_len = skb_hlen - gso_hs;
126+
} else {
127+
sge0_len = skb_hlen;
128+
}
129+
130+
da = dma_map_single(dev, skb->data, sge0_len, DMA_TO_DEVICE);
109131
if (dma_mapping_error(dev, da))
110132
return -ENOMEM;
111133

112-
ash->dma_handle[0] = da;
113-
ash->size[0] = skb_headlen(skb);
134+
mana_add_sge(tp, ash, 0, da, sge0_len, gd->gpa_mkey);
114135

115-
tp->wqe_req.sgl[0].address = ash->dma_handle[0];
116-
tp->wqe_req.sgl[0].mem_key = gd->gpa_mkey;
117-
tp->wqe_req.sgl[0].size = ash->size[0];
136+
if (sge1_len) {
137+
sg_i = 1;
138+
da = dma_map_single(dev, skb->data + sge0_len, sge1_len,
139+
DMA_TO_DEVICE);
140+
if (dma_mapping_error(dev, da))
141+
goto frag_err;
142+
143+
mana_add_sge(tp, ash, sg_i, da, sge1_len, gd->gpa_mkey);
144+
hsg = 2;
145+
}
118146

119147
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
148+
sg_i = hsg + i;
149+
120150
frag = &skb_shinfo(skb)->frags[i];
121151
da = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
122152
DMA_TO_DEVICE);
123-
124153
if (dma_mapping_error(dev, da))
125154
goto frag_err;
126155

127-
ash->dma_handle[i + 1] = da;
128-
ash->size[i + 1] = skb_frag_size(frag);
129-
130-
tp->wqe_req.sgl[i + 1].address = ash->dma_handle[i + 1];
131-
tp->wqe_req.sgl[i + 1].mem_key = gd->gpa_mkey;
132-
tp->wqe_req.sgl[i + 1].size = ash->size[i + 1];
156+
mana_add_sge(tp, ash, sg_i, da, skb_frag_size(frag),
157+
gd->gpa_mkey);
133158
}
134159

135160
return 0;
136161

137162
frag_err:
138-
for (i = i - 1; i >= 0; i--)
139-
dma_unmap_page(dev, ash->dma_handle[i + 1], ash->size[i + 1],
163+
for (i = sg_i - 1; i >= hsg; i--)
164+
dma_unmap_page(dev, ash->dma_handle[i], ash->size[i],
140165
DMA_TO_DEVICE);
141166

142-
dma_unmap_single(dev, ash->dma_handle[0], ash->size[0], DMA_TO_DEVICE);
167+
for (i = hsg - 1; i >= 0; i--)
168+
dma_unmap_single(dev, ash->dma_handle[i], ash->size[i],
169+
DMA_TO_DEVICE);
143170

144171
return -ENOMEM;
145172
}
146173

174+
/* Handle the case when GSO SKB linear length is too large.
175+
* MANA NIC requires GSO packets to put only the packet header to SGE0.
176+
* So, we need 2 SGEs for the skb linear part which contains more than the
177+
* header.
178+
* Return a positive value for the number of SGEs, or a negative value
179+
* for an error.
180+
*/
181+
static int mana_fix_skb_head(struct net_device *ndev, struct sk_buff *skb,
182+
int gso_hs)
183+
{
184+
int num_sge = 1 + skb_shinfo(skb)->nr_frags;
185+
int skb_hlen = skb_headlen(skb);
186+
187+
if (gso_hs < skb_hlen) {
188+
num_sge++;
189+
} else if (gso_hs > skb_hlen) {
190+
if (net_ratelimit())
191+
netdev_err(ndev,
192+
"TX nonlinear head: hs:%d, skb_hlen:%d\n",
193+
gso_hs, skb_hlen);
194+
195+
return -EINVAL;
196+
}
197+
198+
return num_sge;
199+
}
200+
201+
/* Get the GSO packet's header size */
202+
static int mana_get_gso_hs(struct sk_buff *skb)
203+
{
204+
int gso_hs;
205+
206+
if (skb->encapsulation) {
207+
gso_hs = skb_inner_tcp_all_headers(skb);
208+
} else {
209+
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) {
210+
gso_hs = skb_transport_offset(skb) +
211+
sizeof(struct udphdr);
212+
} else {
213+
gso_hs = skb_tcp_all_headers(skb);
214+
}
215+
}
216+
217+
return gso_hs;
218+
}
219+
147220
netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
148221
{
149222
enum mana_tx_pkt_format pkt_fmt = MANA_SHORT_PKT_FMT;
150223
struct mana_port_context *apc = netdev_priv(ndev);
224+
int gso_hs = 0; /* zero for non-GSO pkts */
151225
u16 txq_idx = skb_get_queue_mapping(skb);
152226
struct gdma_dev *gd = apc->ac->gdma_dev;
153227
bool ipv4 = false, ipv6 = false;
@@ -159,7 +233,6 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
159233
struct mana_txq *txq;
160234
struct mana_cq *cq;
161235
int err, len;
162-
u16 ihs;
163236

164237
if (unlikely(!apc->port_is_up))
165238
goto tx_drop;
@@ -209,26 +282,33 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
209282
pkg.wqe_req.client_data_unit = 0;
210283

211284
pkg.wqe_req.num_sge = 1 + skb_shinfo(skb)->nr_frags;
212-
WARN_ON_ONCE(pkg.wqe_req.num_sge > MAX_TX_WQE_SGL_ENTRIES);
213-
214-
if (pkg.wqe_req.num_sge <= ARRAY_SIZE(pkg.sgl_array)) {
215-
pkg.wqe_req.sgl = pkg.sgl_array;
216-
} else {
217-
pkg.sgl_ptr = kmalloc_array(pkg.wqe_req.num_sge,
218-
sizeof(struct gdma_sge),
219-
GFP_ATOMIC);
220-
if (!pkg.sgl_ptr)
221-
goto tx_drop_count;
222-
223-
pkg.wqe_req.sgl = pkg.sgl_ptr;
224-
}
225285

226286
if (skb->protocol == htons(ETH_P_IP))
227287
ipv4 = true;
228288
else if (skb->protocol == htons(ETH_P_IPV6))
229289
ipv6 = true;
230290

231291
if (skb_is_gso(skb)) {
292+
int num_sge;
293+
294+
gso_hs = mana_get_gso_hs(skb);
295+
296+
num_sge = mana_fix_skb_head(ndev, skb, gso_hs);
297+
if (num_sge > 0)
298+
pkg.wqe_req.num_sge = num_sge;
299+
else
300+
goto tx_drop_count;
301+
302+
u64_stats_update_begin(&tx_stats->syncp);
303+
if (skb->encapsulation) {
304+
tx_stats->tso_inner_packets++;
305+
tx_stats->tso_inner_bytes += skb->len - gso_hs;
306+
} else {
307+
tx_stats->tso_packets++;
308+
tx_stats->tso_bytes += skb->len - gso_hs;
309+
}
310+
u64_stats_update_end(&tx_stats->syncp);
311+
232312
pkg.tx_oob.s_oob.is_outer_ipv4 = ipv4;
233313
pkg.tx_oob.s_oob.is_outer_ipv6 = ipv6;
234314

@@ -252,26 +332,6 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
252332
&ipv6_hdr(skb)->daddr, 0,
253333
IPPROTO_TCP, 0);
254334
}
255-
256-
if (skb->encapsulation) {
257-
ihs = skb_inner_tcp_all_headers(skb);
258-
u64_stats_update_begin(&tx_stats->syncp);
259-
tx_stats->tso_inner_packets++;
260-
tx_stats->tso_inner_bytes += skb->len - ihs;
261-
u64_stats_update_end(&tx_stats->syncp);
262-
} else {
263-
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) {
264-
ihs = skb_transport_offset(skb) + sizeof(struct udphdr);
265-
} else {
266-
ihs = skb_tcp_all_headers(skb);
267-
}
268-
269-
u64_stats_update_begin(&tx_stats->syncp);
270-
tx_stats->tso_packets++;
271-
tx_stats->tso_bytes += skb->len - ihs;
272-
u64_stats_update_end(&tx_stats->syncp);
273-
}
274-
275335
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
276336
csum_type = mana_checksum_info(skb);
277337

@@ -294,11 +354,25 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
294354
} else {
295355
/* Can't do offload of this type of checksum */
296356
if (skb_checksum_help(skb))
297-
goto free_sgl_ptr;
357+
goto tx_drop_count;
298358
}
299359
}
300360

301-
if (mana_map_skb(skb, apc, &pkg)) {
361+
WARN_ON_ONCE(pkg.wqe_req.num_sge > MAX_TX_WQE_SGL_ENTRIES);
362+
363+
if (pkg.wqe_req.num_sge <= ARRAY_SIZE(pkg.sgl_array)) {
364+
pkg.wqe_req.sgl = pkg.sgl_array;
365+
} else {
366+
pkg.sgl_ptr = kmalloc_array(pkg.wqe_req.num_sge,
367+
sizeof(struct gdma_sge),
368+
GFP_ATOMIC);
369+
if (!pkg.sgl_ptr)
370+
goto tx_drop_count;
371+
372+
pkg.wqe_req.sgl = pkg.sgl_ptr;
373+
}
374+
375+
if (mana_map_skb(skb, apc, &pkg, gso_hs)) {
302376
u64_stats_update_begin(&tx_stats->syncp);
303377
tx_stats->mana_map_err++;
304378
u64_stats_update_end(&tx_stats->syncp);
@@ -1256,11 +1330,16 @@ static void mana_unmap_skb(struct sk_buff *skb, struct mana_port_context *apc)
12561330
struct mana_skb_head *ash = (struct mana_skb_head *)skb->head;
12571331
struct gdma_context *gc = apc->ac->gdma_dev->gdma_context;
12581332
struct device *dev = gc->dev;
1259-
int i;
1333+
int hsg, i;
1334+
1335+
/* Number of SGEs of linear part */
1336+
hsg = (skb_is_gso(skb) && skb_headlen(skb) > ash->size[0]) ? 2 : 1;
12601337

1261-
dma_unmap_single(dev, ash->dma_handle[0], ash->size[0], DMA_TO_DEVICE);
1338+
for (i = 0; i < hsg; i++)
1339+
dma_unmap_single(dev, ash->dma_handle[i], ash->size[i],
1340+
DMA_TO_DEVICE);
12621341

1263-
for (i = 1; i < skb_shinfo(skb)->nr_frags + 1; i++)
1342+
for (i = hsg; i < skb_shinfo(skb)->nr_frags + hsg; i++)
12641343
dma_unmap_page(dev, ash->dma_handle[i], ash->size[i],
12651344
DMA_TO_DEVICE);
12661345
}

include/net/mana/mana.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,10 @@ struct mana_txq {
103103

104104
/* skb data and frags dma mappings */
105105
struct mana_skb_head {
106-
dma_addr_t dma_handle[MAX_SKB_FRAGS + 1];
106+
/* GSO pkts may have 2 SGEs for the linear part*/
107+
dma_addr_t dma_handle[MAX_SKB_FRAGS + 2];
107108

108-
u32 size[MAX_SKB_FRAGS + 1];
109+
u32 size[MAX_SKB_FRAGS + 2];
109110
};
110111

111112
#define MANA_HEADROOM sizeof(struct mana_skb_head)

0 commit comments

Comments
 (0)