Skip to content

Commit 1b7bde6

Browse files
Nimrod Andydavem330
authored andcommitted
net: fec: implement rx_copybreak to improve rx performance
- Copy short frames and keep the buffers mapped, re-allocate skb instead of memory copy for long frames. - Add support for setting/getting rx_copybreak using generic ethtool tunable Changes V3: * As Eric Dumazet's suggestion that removing the copybreak module parameter and only keep the ethtool API support for rx_copybreak. Changes V2: * Implements rx_copybreak * Rx_copybreak provides module parameter to change this value * Add tunable_ops support for rx_copybreak Signed-off-by: Fugang Duan <B38611@freescale.com> Signed-off-by: Frank Li <Frank.Li@freescale.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent ce1a4ea commit 1b7bde6

File tree

2 files changed

+145
-65
lines changed

2 files changed

+145
-65
lines changed

drivers/net/ethernet/freescale/fec.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,8 @@ struct fec_enet_private {
482482
unsigned int tx_pkts_itr;
483483
unsigned int tx_time_itr;
484484
unsigned int itr_clk_rate;
485+
486+
u32 rx_copybreak;
485487
};
486488

487489
void fec_ptp_init(struct platform_device *pdev);

drivers/net/ethernet/freescale/fec_main.c

Lines changed: 143 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
236236
#define FEC_PAUSE_FLAG_AUTONEG 0x1
237237
#define FEC_PAUSE_FLAG_ENABLE 0x2
238238

239+
#define COPYBREAK_DEFAULT 256
240+
239241
#define TSO_HEADER_SIZE 128
240242
/* Max number of allowed TCP segments for software TSO */
241243
#define FEC_MAX_TSO_SEGS 100
@@ -1322,6 +1324,50 @@ fec_enet_tx(struct net_device *ndev)
13221324
return;
13231325
}
13241326

1327+
static int
1328+
fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb)
1329+
{
1330+
struct fec_enet_private *fep = netdev_priv(ndev);
1331+
int off;
1332+
1333+
off = ((unsigned long)skb->data) & fep->rx_align;
1334+
if (off)
1335+
skb_reserve(skb, fep->rx_align + 1 - off);
1336+
1337+
bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
1338+
FEC_ENET_RX_FRSIZE - fep->rx_align,
1339+
DMA_FROM_DEVICE);
1340+
if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
1341+
if (net_ratelimit())
1342+
netdev_err(ndev, "Rx DMA memory map failed\n");
1343+
return -ENOMEM;
1344+
}
1345+
1346+
return 0;
1347+
}
1348+
1349+
static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb,
1350+
struct bufdesc *bdp, u32 length)
1351+
{
1352+
struct fec_enet_private *fep = netdev_priv(ndev);
1353+
struct sk_buff *new_skb;
1354+
1355+
if (length > fep->rx_copybreak)
1356+
return false;
1357+
1358+
new_skb = netdev_alloc_skb(ndev, length);
1359+
if (!new_skb)
1360+
return false;
1361+
1362+
dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr,
1363+
FEC_ENET_RX_FRSIZE - fep->rx_align,
1364+
DMA_FROM_DEVICE);
1365+
memcpy(new_skb->data, (*skb)->data, length);
1366+
*skb = new_skb;
1367+
1368+
return true;
1369+
}
1370+
13251371
/* During a receive, the cur_rx points to the current incoming buffer.
13261372
* When we update through the ring, if the next incoming buffer has
13271373
* not been given to the system, we just set the empty indicator,
@@ -1336,14 +1382,16 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
13361382
struct fec_enet_priv_rx_q *rxq;
13371383
struct bufdesc *bdp;
13381384
unsigned short status;
1339-
struct sk_buff *skb;
1385+
struct sk_buff *skb_new = NULL;
1386+
struct sk_buff *skb;
13401387
ushort pkt_len;
13411388
__u8 *data;
13421389
int pkt_received = 0;
13431390
struct bufdesc_ex *ebdp = NULL;
13441391
bool vlan_packet_rcvd = false;
13451392
u16 vlan_tag;
13461393
int index = 0;
1394+
bool is_copybreak;
13471395

13481396
#ifdef CONFIG_M532x
13491397
flush_cache_all();
@@ -1401,11 +1449,27 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
14011449
ndev->stats.rx_bytes += pkt_len;
14021450

14031451
index = fec_enet_get_bd_index(rxq->rx_bd_base, bdp, fep);
1404-
data = rxq->rx_skbuff[index]->data;
1405-
dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr,
1406-
FEC_ENET_RX_FRSIZE - fep->rx_align,
1407-
DMA_FROM_DEVICE);
1452+
skb = rxq->rx_skbuff[index];
14081453

1454+
/* The packet length includes FCS, but we don't want to
1455+
* include that when passing upstream as it messes up
1456+
* bridging applications.
1457+
*/
1458+
is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4);
1459+
if (!is_copybreak) {
1460+
skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
1461+
if (unlikely(!skb_new)) {
1462+
ndev->stats.rx_dropped++;
1463+
goto rx_processing_done;
1464+
}
1465+
dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
1466+
FEC_ENET_RX_FRSIZE - fep->rx_align,
1467+
DMA_FROM_DEVICE);
1468+
}
1469+
1470+
prefetch(skb->data - NET_IP_ALIGN);
1471+
skb_put(skb, pkt_len - 4);
1472+
data = skb->data;
14091473
if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
14101474
swap_buffer(data, pkt_len);
14111475

@@ -1422,62 +1486,48 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
14221486
struct vlan_hdr *vlan_header =
14231487
(struct vlan_hdr *) (data + ETH_HLEN);
14241488
vlan_tag = ntohs(vlan_header->h_vlan_TCI);
1425-
pkt_len -= VLAN_HLEN;
14261489

14271490
vlan_packet_rcvd = true;
1491+
1492+
skb_copy_to_linear_data_offset(skb, VLAN_HLEN,
1493+
data, (2 * ETH_ALEN));
1494+
skb_pull(skb, VLAN_HLEN);
14281495
}
14291496

1430-
/* This does 16 byte alignment, exactly what we need.
1431-
* The packet length includes FCS, but we don't want to
1432-
* include that when passing upstream as it messes up
1433-
* bridging applications.
1434-
*/
1435-
skb = netdev_alloc_skb(ndev, pkt_len - 4 + NET_IP_ALIGN);
1497+
skb->protocol = eth_type_trans(skb, ndev);
14361498

1437-
if (unlikely(!skb)) {
1438-
ndev->stats.rx_dropped++;
1439-
} else {
1440-
int payload_offset = (2 * ETH_ALEN);
1441-
skb_reserve(skb, NET_IP_ALIGN);
1442-
skb_put(skb, pkt_len - 4); /* Make room */
1443-
1444-
/* Extract the frame data without the VLAN header. */
1445-
skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN));
1446-
if (vlan_packet_rcvd)
1447-
payload_offset = (2 * ETH_ALEN) + VLAN_HLEN;
1448-
skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN),
1449-
data + payload_offset,
1450-
pkt_len - 4 - (2 * ETH_ALEN));
1451-
1452-
skb->protocol = eth_type_trans(skb, ndev);
1453-
1454-
/* Get receive timestamp from the skb */
1455-
if (fep->hwts_rx_en && fep->bufdesc_ex)
1456-
fec_enet_hwtstamp(fep, ebdp->ts,
1457-
skb_hwtstamps(skb));
1458-
1459-
if (fep->bufdesc_ex &&
1460-
(fep->csum_flags & FLAG_RX_CSUM_ENABLED)) {
1461-
if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) {
1462-
/* don't check it */
1463-
skb->ip_summed = CHECKSUM_UNNECESSARY;
1464-
} else {
1465-
skb_checksum_none_assert(skb);
1466-
}
1499+
/* Get receive timestamp from the skb */
1500+
if (fep->hwts_rx_en && fep->bufdesc_ex)
1501+
fec_enet_hwtstamp(fep, ebdp->ts,
1502+
skb_hwtstamps(skb));
1503+
1504+
if (fep->bufdesc_ex &&
1505+
(fep->csum_flags & FLAG_RX_CSUM_ENABLED)) {
1506+
if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) {
1507+
/* don't check it */
1508+
skb->ip_summed = CHECKSUM_UNNECESSARY;
1509+
} else {
1510+
skb_checksum_none_assert(skb);
14671511
}
1512+
}
14681513

1469-
/* Handle received VLAN packets */
1470-
if (vlan_packet_rcvd)
1471-
__vlan_hwaccel_put_tag(skb,
1472-
htons(ETH_P_8021Q),
1473-
vlan_tag);
1514+
/* Handle received VLAN packets */
1515+
if (vlan_packet_rcvd)
1516+
__vlan_hwaccel_put_tag(skb,
1517+
htons(ETH_P_8021Q),
1518+
vlan_tag);
14741519

1475-
napi_gro_receive(&fep->napi, skb);
1520+
napi_gro_receive(&fep->napi, skb);
1521+
1522+
if (is_copybreak) {
1523+
dma_sync_single_for_device(&fep->pdev->dev, bdp->cbd_bufaddr,
1524+
FEC_ENET_RX_FRSIZE - fep->rx_align,
1525+
DMA_FROM_DEVICE);
1526+
} else {
1527+
rxq->rx_skbuff[index] = skb_new;
1528+
fec_enet_new_rxbdp(ndev, bdp, skb_new);
14761529
}
14771530

1478-
dma_sync_single_for_device(&fep->pdev->dev, bdp->cbd_bufaddr,
1479-
FEC_ENET_RX_FRSIZE - fep->rx_align,
1480-
DMA_FROM_DEVICE);
14811531
rx_processing_done:
14821532
/* Clear the status flags for this buffer */
14831533
status &= ~BD_ENET_RX_STATS;
@@ -2392,6 +2442,44 @@ static void fec_enet_itr_coal_init(struct net_device *ndev)
23922442
fec_enet_set_coalesce(ndev, &ec);
23932443
}
23942444

2445+
static int fec_enet_get_tunable(struct net_device *netdev,
2446+
const struct ethtool_tunable *tuna,
2447+
void *data)
2448+
{
2449+
struct fec_enet_private *fep = netdev_priv(netdev);
2450+
int ret = 0;
2451+
2452+
switch (tuna->id) {
2453+
case ETHTOOL_RX_COPYBREAK:
2454+
*(u32 *)data = fep->rx_copybreak;
2455+
break;
2456+
default:
2457+
ret = -EINVAL;
2458+
break;
2459+
}
2460+
2461+
return ret;
2462+
}
2463+
2464+
static int fec_enet_set_tunable(struct net_device *netdev,
2465+
const struct ethtool_tunable *tuna,
2466+
const void *data)
2467+
{
2468+
struct fec_enet_private *fep = netdev_priv(netdev);
2469+
int ret = 0;
2470+
2471+
switch (tuna->id) {
2472+
case ETHTOOL_RX_COPYBREAK:
2473+
fep->rx_copybreak = *(u32 *)data;
2474+
break;
2475+
default:
2476+
ret = -EINVAL;
2477+
break;
2478+
}
2479+
2480+
return ret;
2481+
}
2482+
23952483
static const struct ethtool_ops fec_enet_ethtool_ops = {
23962484
.get_settings = fec_enet_get_settings,
23972485
.set_settings = fec_enet_set_settings,
@@ -2408,6 +2496,8 @@ static const struct ethtool_ops fec_enet_ethtool_ops = {
24082496
.get_sset_count = fec_enet_get_sset_count,
24092497
#endif
24102498
.get_ts_info = fec_enet_get_ts_info,
2499+
.get_tunable = fec_enet_get_tunable,
2500+
.set_tunable = fec_enet_set_tunable,
24112501
};
24122502

24132503
static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
@@ -2553,33 +2643,20 @@ fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue)
25532643
struct sk_buff *skb;
25542644
struct bufdesc *bdp;
25552645
struct fec_enet_priv_rx_q *rxq;
2556-
unsigned int off;
25572646

25582647
rxq = fep->rx_queue[queue];
25592648
bdp = rxq->rx_bd_base;
25602649
for (i = 0; i < rxq->rx_ring_size; i++) {
2561-
dma_addr_t addr;
2562-
25632650
skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
25642651
if (!skb)
25652652
goto err_alloc;
25662653

2567-
off = ((unsigned long)skb->data) & fep->rx_align;
2568-
if (off)
2569-
skb_reserve(skb, fep->rx_align + 1 - off);
2570-
2571-
addr = dma_map_single(&fep->pdev->dev, skb->data,
2572-
FEC_ENET_RX_FRSIZE - fep->rx_align, DMA_FROM_DEVICE);
2573-
2574-
if (dma_mapping_error(&fep->pdev->dev, addr)) {
2654+
if (fec_enet_new_rxbdp(ndev, bdp, skb)) {
25752655
dev_kfree_skb(skb);
2576-
if (net_ratelimit())
2577-
netdev_err(ndev, "Rx DMA memory map failed\n");
25782656
goto err_alloc;
25792657
}
25802658

25812659
rxq->rx_skbuff[i] = skb;
2582-
bdp->cbd_bufaddr = addr;
25832660
bdp->cbd_sc = BD_ENET_RX_EMPTY;
25842661

25852662
if (fep->bufdesc_ex) {
@@ -3240,6 +3317,7 @@ fec_probe(struct platform_device *pdev)
32403317
if (fep->bufdesc_ex && fep->ptp_clock)
32413318
netdev_info(ndev, "registered PHC device %d\n", fep->dev_id);
32423319

3320+
fep->rx_copybreak = COPYBREAK_DEFAULT;
32433321
INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work);
32443322
return 0;
32453323

0 commit comments

Comments
 (0)