Skip to content

Commit 1629dd4

Browse files
Rafal Ozieblodavem330
authored andcommitted
cadence: Add LSO support.
New Cadence GEM hardware support Large Segment Offload (LSO): TCP segmentation offload (TSO) as well as UDP fragmentation offload (UFO). Support for those features was added to the driver. Signed-off-by: David S. Miller <[email protected]>
1 parent 0834899 commit 1629dd4

File tree

2 files changed

+140
-12
lines changed

2 files changed

+140
-12
lines changed

drivers/net/ethernet/cadence/macb.c

Lines changed: 126 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
#include <linux/of_gpio.h>
3333
#include <linux/of_mdio.h>
3434
#include <linux/of_net.h>
35-
35+
#include <linux/ip.h>
36+
#include <linux/udp.h>
37+
#include <linux/tcp.h>
3638
#include "macb.h"
3739

3840
#define MACB_RX_BUFFER_SIZE 128
@@ -60,10 +62,13 @@
6062
| MACB_BIT(TXERR))
6163
#define MACB_TX_INT_FLAGS (MACB_TX_ERR_FLAGS | MACB_BIT(TCOMP))
6264

63-
#define MACB_MAX_TX_LEN ((unsigned int)((1 << MACB_TX_FRMLEN_SIZE) - 1))
64-
#define GEM_MAX_TX_LEN ((unsigned int)((1 << GEM_TX_FRMLEN_SIZE) - 1))
65+
/* Max length of transmit frame must be a multiple of 8 bytes */
66+
#define MACB_TX_LEN_ALIGN 8
67+
#define MACB_MAX_TX_LEN ((unsigned int)((1 << MACB_TX_FRMLEN_SIZE) - 1) & ~((unsigned int)(MACB_TX_LEN_ALIGN - 1)))
68+
#define GEM_MAX_TX_LEN ((unsigned int)((1 << GEM_TX_FRMLEN_SIZE) - 1) & ~((unsigned int)(MACB_TX_LEN_ALIGN - 1)))
6569

6670
#define GEM_MTU_MIN_SIZE ETH_MIN_MTU
71+
#define MACB_NETIF_LSO (NETIF_F_TSO | NETIF_F_UFO)
6772

6873
#define MACB_WOL_HAS_MAGIC_PACKET (0x1 << 0)
6974
#define MACB_WOL_ENABLED (0x1 << 1)
@@ -1223,22 +1228,36 @@ static void macb_poll_controller(struct net_device *dev)
12231228

12241229
static unsigned int macb_tx_map(struct macb *bp,
12251230
struct macb_queue *queue,
1226-
struct sk_buff *skb)
1231+
struct sk_buff *skb,
1232+
unsigned int hdrlen)
12271233
{
12281234
dma_addr_t mapping;
12291235
unsigned int len, entry, i, tx_head = queue->tx_head;
12301236
struct macb_tx_skb *tx_skb = NULL;
12311237
struct macb_dma_desc *desc;
12321238
unsigned int offset, size, count = 0;
12331239
unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags;
1234-
unsigned int eof = 1;
1235-
u32 ctrl;
1240+
unsigned int eof = 1, mss_mfs = 0;
1241+
u32 ctrl, lso_ctrl = 0, seq_ctrl = 0;
1242+
1243+
/* LSO */
1244+
if (skb_shinfo(skb)->gso_size != 0) {
1245+
if (ip_hdr(skb)->protocol == IPPROTO_UDP)
1246+
/* UDP - UFO */
1247+
lso_ctrl = MACB_LSO_UFO_ENABLE;
1248+
else
1249+
/* TCP - TSO */
1250+
lso_ctrl = MACB_LSO_TSO_ENABLE;
1251+
}
12361252

12371253
/* First, map non-paged data */
12381254
len = skb_headlen(skb);
1255+
1256+
/* first buffer length */
1257+
size = hdrlen;
1258+
12391259
offset = 0;
12401260
while (len) {
1241-
size = min(len, bp->max_tx_length);
12421261
entry = macb_tx_ring_wrap(bp, tx_head);
12431262
tx_skb = &queue->tx_skb[entry];
12441263

@@ -1258,6 +1277,8 @@ static unsigned int macb_tx_map(struct macb *bp,
12581277
offset += size;
12591278
count++;
12601279
tx_head++;
1280+
1281+
size = min(len, bp->max_tx_length);
12611282
}
12621283

12631284
/* Then, map paged data from fragments */
@@ -1311,6 +1332,21 @@ static unsigned int macb_tx_map(struct macb *bp,
13111332
desc = &queue->tx_ring[entry];
13121333
desc->ctrl = ctrl;
13131334

1335+
if (lso_ctrl) {
1336+
if (lso_ctrl == MACB_LSO_UFO_ENABLE)
1337+
/* include header and FCS in value given to h/w */
1338+
mss_mfs = skb_shinfo(skb)->gso_size +
1339+
skb_transport_offset(skb) +
1340+
ETH_FCS_LEN;
1341+
else /* TSO */ {
1342+
mss_mfs = skb_shinfo(skb)->gso_size;
1343+
/* TCP Sequence Number Source Select
1344+
* can be set only for TSO
1345+
*/
1346+
seq_ctrl = 0;
1347+
}
1348+
}
1349+
13141350
do {
13151351
i--;
13161352
entry = macb_tx_ring_wrap(bp, i);
@@ -1325,6 +1361,16 @@ static unsigned int macb_tx_map(struct macb *bp,
13251361
if (unlikely(entry == (bp->tx_ring_size - 1)))
13261362
ctrl |= MACB_BIT(TX_WRAP);
13271363

1364+
/* First descriptor is header descriptor */
1365+
if (i == queue->tx_head) {
1366+
ctrl |= MACB_BF(TX_LSO, lso_ctrl);
1367+
ctrl |= MACB_BF(TX_TCP_SEQ_SRC, seq_ctrl);
1368+
} else
1369+
/* Only set MSS/MFS on payload descriptors
1370+
* (second or later descriptor)
1371+
*/
1372+
ctrl |= MACB_BF(MSS_MFS, mss_mfs);
1373+
13281374
/* Set TX buffer descriptor */
13291375
macb_set_addr(desc, tx_skb->mapping);
13301376
/* desc->addr must be visible to hardware before clearing
@@ -1350,6 +1396,43 @@ static unsigned int macb_tx_map(struct macb *bp,
13501396
return 0;
13511397
}
13521398

1399+
static netdev_features_t macb_features_check(struct sk_buff *skb,
1400+
struct net_device *dev,
1401+
netdev_features_t features)
1402+
{
1403+
unsigned int nr_frags, f;
1404+
unsigned int hdrlen;
1405+
1406+
/* Validate LSO compatibility */
1407+
1408+
/* there is only one buffer */
1409+
if (!skb_is_nonlinear(skb))
1410+
return features;
1411+
1412+
/* length of header */
1413+
hdrlen = skb_transport_offset(skb);
1414+
if (ip_hdr(skb)->protocol == IPPROTO_TCP)
1415+
hdrlen += tcp_hdrlen(skb);
1416+
1417+
/* For LSO:
1418+
* When software supplies two or more payload buffers all payload buffers
1419+
* apart from the last must be a multiple of 8 bytes in size.
1420+
*/
1421+
if (!IS_ALIGNED(skb_headlen(skb) - hdrlen, MACB_TX_LEN_ALIGN))
1422+
return features & ~MACB_NETIF_LSO;
1423+
1424+
nr_frags = skb_shinfo(skb)->nr_frags;
1425+
/* No need to check last fragment */
1426+
nr_frags--;
1427+
for (f = 0; f < nr_frags; f++) {
1428+
const skb_frag_t *frag = &skb_shinfo(skb)->frags[f];
1429+
1430+
if (!IS_ALIGNED(skb_frag_size(frag), MACB_TX_LEN_ALIGN))
1431+
return features & ~MACB_NETIF_LSO;
1432+
}
1433+
return features;
1434+
}
1435+
13531436
static inline int macb_clear_csum(struct sk_buff *skb)
13541437
{
13551438
/* no change for packets without checksum offloading */
@@ -1374,7 +1457,28 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
13741457
struct macb *bp = netdev_priv(dev);
13751458
struct macb_queue *queue = &bp->queues[queue_index];
13761459
unsigned long flags;
1377-
unsigned int count, nr_frags, frag_size, f;
1460+
unsigned int desc_cnt, nr_frags, frag_size, f;
1461+
unsigned int hdrlen;
1462+
bool is_lso, is_udp = 0;
1463+
1464+
is_lso = (skb_shinfo(skb)->gso_size != 0);
1465+
1466+
if (is_lso) {
1467+
is_udp = !!(ip_hdr(skb)->protocol == IPPROTO_UDP);
1468+
1469+
/* length of headers */
1470+
if (is_udp)
1471+
/* only queue eth + ip headers separately for UDP */
1472+
hdrlen = skb_transport_offset(skb);
1473+
else
1474+
hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb);
1475+
if (skb_headlen(skb) < hdrlen) {
1476+
netdev_err(bp->dev, "Error - LSO headers fragmented!!!\n");
1477+
/* if this is required, would need to copy to single buffer */
1478+
return NETDEV_TX_BUSY;
1479+
}
1480+
} else
1481+
hdrlen = min(skb_headlen(skb), bp->max_tx_length);
13781482

13791483
#if defined(DEBUG) && defined(VERBOSE_DEBUG)
13801484
netdev_vdbg(bp->dev,
@@ -1389,18 +1493,22 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
13891493
* socket buffer: skb fragments of jumbo frames may need to be
13901494
* split into many buffer descriptors.
13911495
*/
1392-
count = DIV_ROUND_UP(skb_headlen(skb), bp->max_tx_length);
1496+
if (is_lso && (skb_headlen(skb) > hdrlen))
1497+
/* extra header descriptor if also payload in first buffer */
1498+
desc_cnt = DIV_ROUND_UP((skb_headlen(skb) - hdrlen), bp->max_tx_length) + 1;
1499+
else
1500+
desc_cnt = DIV_ROUND_UP(skb_headlen(skb), bp->max_tx_length);
13931501
nr_frags = skb_shinfo(skb)->nr_frags;
13941502
for (f = 0; f < nr_frags; f++) {
13951503
frag_size = skb_frag_size(&skb_shinfo(skb)->frags[f]);
1396-
count += DIV_ROUND_UP(frag_size, bp->max_tx_length);
1504+
desc_cnt += DIV_ROUND_UP(frag_size, bp->max_tx_length);
13971505
}
13981506

13991507
spin_lock_irqsave(&bp->lock, flags);
14001508

14011509
/* This is a hard error, log it. */
14021510
if (CIRC_SPACE(queue->tx_head, queue->tx_tail,
1403-
bp->tx_ring_size) < count) {
1511+
bp->tx_ring_size) < desc_cnt) {
14041512
netif_stop_subqueue(dev, queue_index);
14051513
spin_unlock_irqrestore(&bp->lock, flags);
14061514
netdev_dbg(bp->dev, "tx_head = %u, tx_tail = %u\n",
@@ -1414,7 +1522,7 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
14141522
}
14151523

14161524
/* Map socket buffer for DMA transfer */
1417-
if (!macb_tx_map(bp, queue, skb)) {
1525+
if (!macb_tx_map(bp, queue, skb, hdrlen)) {
14181526
dev_kfree_skb_any(skb);
14191527
goto unlock;
14201528
}
@@ -2354,6 +2462,7 @@ static const struct net_device_ops macb_netdev_ops = {
23542462
.ndo_poll_controller = macb_poll_controller,
23552463
#endif
23562464
.ndo_set_features = macb_set_features,
2465+
.ndo_features_check = macb_features_check,
23572466
};
23582467

23592468
/* Configure peripheral capabilities according to device tree
@@ -2560,6 +2669,11 @@ static int macb_init(struct platform_device *pdev)
25602669

25612670
/* Set features */
25622671
dev->hw_features = NETIF_F_SG;
2672+
2673+
/* Check LSO capability */
2674+
if (GEM_BFEXT(PBUF_LSO, gem_readl(bp, DCFG6)))
2675+
dev->hw_features |= MACB_NETIF_LSO;
2676+
25632677
/* Checksum offload is only available on gem with packet buffer */
25642678
if (macb_is_gem(bp) && !(bp->caps & MACB_CAPS_FIFO_MODE))
25652679
dev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;

drivers/net/ethernet/cadence/macb.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,10 @@
382382
#define GEM_TX_PKT_BUFF_OFFSET 21
383383
#define GEM_TX_PKT_BUFF_SIZE 1
384384

385+
/* Bitfields in DCFG6. */
386+
#define GEM_PBUF_LSO_OFFSET 27
387+
#define GEM_PBUF_LSO_SIZE 1
388+
385389
/* Constants for CLK */
386390
#define MACB_CLK_DIV8 0
387391
#define MACB_CLK_DIV16 1
@@ -414,6 +418,10 @@
414418
#define MACB_CAPS_SG_DISABLED 0x40000000
415419
#define MACB_CAPS_MACB_IS_GEM 0x80000000
416420

421+
/* LSO settings */
422+
#define MACB_LSO_UFO_ENABLE 0x01
423+
#define MACB_LSO_TSO_ENABLE 0x02
424+
417425
/* Bit manipulation macros */
418426
#define MACB_BIT(name) \
419427
(1 << MACB_##name##_OFFSET)
@@ -545,6 +553,12 @@ struct macb_dma_desc {
545553
#define MACB_TX_LAST_SIZE 1
546554
#define MACB_TX_NOCRC_OFFSET 16
547555
#define MACB_TX_NOCRC_SIZE 1
556+
#define MACB_MSS_MFS_OFFSET 16
557+
#define MACB_MSS_MFS_SIZE 14
558+
#define MACB_TX_LSO_OFFSET 17
559+
#define MACB_TX_LSO_SIZE 2
560+
#define MACB_TX_TCP_SEQ_SRC_OFFSET 19
561+
#define MACB_TX_TCP_SEQ_SRC_SIZE 1
548562
#define MACB_TX_BUF_EXHAUSTED_OFFSET 27
549563
#define MACB_TX_BUF_EXHAUSTED_SIZE 1
550564
#define MACB_TX_UNDERRUN_OFFSET 28

0 commit comments

Comments
 (0)