diff options
author | Nimrod Andy <B38611@freescale.com> | 2014-09-29 21:28:05 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-10-01 21:28:21 -0400 |
commit | 1b7bde6d659d30f171259cc2dfba8e5dab34e735 (patch) | |
tree | 93990ae75315127b3c75a5e4441923889268e78f /drivers/net/ethernet/freescale/fec_main.c | |
parent | ce1a4ea3f125863bfbcb1afb76590ee2b7b93fbf (diff) |
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>
Diffstat (limited to 'drivers/net/ethernet/freescale/fec_main.c')
-rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 208 |
1 files changed, 143 insertions, 65 deletions
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 2c7343473e1c..1f07db891ee8 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c | |||
@@ -236,6 +236,8 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); | |||
236 | #define FEC_PAUSE_FLAG_AUTONEG 0x1 | 236 | #define FEC_PAUSE_FLAG_AUTONEG 0x1 |
237 | #define FEC_PAUSE_FLAG_ENABLE 0x2 | 237 | #define FEC_PAUSE_FLAG_ENABLE 0x2 |
238 | 238 | ||
239 | #define COPYBREAK_DEFAULT 256 | ||
240 | |||
239 | #define TSO_HEADER_SIZE 128 | 241 | #define TSO_HEADER_SIZE 128 |
240 | /* Max number of allowed TCP segments for software TSO */ | 242 | /* Max number of allowed TCP segments for software TSO */ |
241 | #define FEC_MAX_TSO_SEGS 100 | 243 | #define FEC_MAX_TSO_SEGS 100 |
@@ -1322,6 +1324,50 @@ fec_enet_tx(struct net_device *ndev) | |||
1322 | return; | 1324 | return; |
1323 | } | 1325 | } |
1324 | 1326 | ||
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 | |||
1325 | /* During a receive, the cur_rx points to the current incoming buffer. | 1371 | /* During a receive, the cur_rx points to the current incoming buffer. |
1326 | * When we update through the ring, if the next incoming buffer has | 1372 | * When we update through the ring, if the next incoming buffer has |
1327 | * not been given to the system, we just set the empty indicator, | 1373 | * not been given to the system, we just set the empty indicator, |
@@ -1336,7 +1382,8 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) | |||
1336 | struct fec_enet_priv_rx_q *rxq; | 1382 | struct fec_enet_priv_rx_q *rxq; |
1337 | struct bufdesc *bdp; | 1383 | struct bufdesc *bdp; |
1338 | unsigned short status; | 1384 | unsigned short status; |
1339 | struct sk_buff *skb; | 1385 | struct sk_buff *skb_new = NULL; |
1386 | struct sk_buff *skb; | ||
1340 | ushort pkt_len; | 1387 | ushort pkt_len; |
1341 | __u8 *data; | 1388 | __u8 *data; |
1342 | int pkt_received = 0; | 1389 | int pkt_received = 0; |
@@ -1344,6 +1391,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) | |||
1344 | bool vlan_packet_rcvd = false; | 1391 | bool vlan_packet_rcvd = false; |
1345 | u16 vlan_tag; | 1392 | u16 vlan_tag; |
1346 | int index = 0; | 1393 | int index = 0; |
1394 | bool is_copybreak; | ||
1347 | 1395 | ||
1348 | #ifdef CONFIG_M532x | 1396 | #ifdef CONFIG_M532x |
1349 | flush_cache_all(); | 1397 | flush_cache_all(); |
@@ -1401,11 +1449,27 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) | |||
1401 | ndev->stats.rx_bytes += pkt_len; | 1449 | ndev->stats.rx_bytes += pkt_len; |
1402 | 1450 | ||
1403 | index = fec_enet_get_bd_index(rxq->rx_bd_base, bdp, fep); | 1451 | index = fec_enet_get_bd_index(rxq->rx_bd_base, bdp, fep); |
1404 | data = rxq->rx_skbuff[index]->data; | 1452 | skb = rxq->rx_skbuff[index]; |
1405 | dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr, | ||
1406 | FEC_ENET_RX_FRSIZE - fep->rx_align, | ||
1407 | DMA_FROM_DEVICE); | ||
1408 | 1453 | ||
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; | ||
1409 | if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) | 1473 | if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) |
1410 | swap_buffer(data, pkt_len); | 1474 | swap_buffer(data, pkt_len); |
1411 | 1475 | ||
@@ -1422,62 +1486,48 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) | |||
1422 | struct vlan_hdr *vlan_header = | 1486 | struct vlan_hdr *vlan_header = |
1423 | (struct vlan_hdr *) (data + ETH_HLEN); | 1487 | (struct vlan_hdr *) (data + ETH_HLEN); |
1424 | vlan_tag = ntohs(vlan_header->h_vlan_TCI); | 1488 | vlan_tag = ntohs(vlan_header->h_vlan_TCI); |
1425 | pkt_len -= VLAN_HLEN; | ||
1426 | 1489 | ||
1427 | vlan_packet_rcvd = true; | 1490 | 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); | ||
1428 | } | 1495 | } |
1429 | 1496 | ||
1430 | /* This does 16 byte alignment, exactly what we need. | 1497 | skb->protocol = eth_type_trans(skb, ndev); |
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); | ||
1436 | 1498 | ||
1437 | if (unlikely(!skb)) { | 1499 | /* Get receive timestamp from the skb */ |
1438 | ndev->stats.rx_dropped++; | 1500 | if (fep->hwts_rx_en && fep->bufdesc_ex) |
1439 | } else { | 1501 | fec_enet_hwtstamp(fep, ebdp->ts, |
1440 | int payload_offset = (2 * ETH_ALEN); | 1502 | skb_hwtstamps(skb)); |
1441 | skb_reserve(skb, NET_IP_ALIGN); | 1503 | |
1442 | skb_put(skb, pkt_len - 4); /* Make room */ | 1504 | if (fep->bufdesc_ex && |
1443 | 1505 | (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { | |
1444 | /* Extract the frame data without the VLAN header. */ | 1506 | if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { |
1445 | skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN)); | 1507 | /* don't check it */ |
1446 | if (vlan_packet_rcvd) | 1508 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
1447 | payload_offset = (2 * ETH_ALEN) + VLAN_HLEN; | 1509 | } else { |
1448 | skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN), | 1510 | skb_checksum_none_assert(skb); |
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 | } | ||
1467 | } | 1511 | } |
1512 | } | ||
1468 | 1513 | ||
1469 | /* Handle received VLAN packets */ | 1514 | /* Handle received VLAN packets */ |
1470 | if (vlan_packet_rcvd) | 1515 | if (vlan_packet_rcvd) |
1471 | __vlan_hwaccel_put_tag(skb, | 1516 | __vlan_hwaccel_put_tag(skb, |
1472 | htons(ETH_P_8021Q), | 1517 | htons(ETH_P_8021Q), |
1473 | vlan_tag); | 1518 | vlan_tag); |
1474 | 1519 | ||
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); | ||
1476 | } | 1529 | } |
1477 | 1530 | ||
1478 | dma_sync_single_for_device(&fep->pdev->dev, bdp->cbd_bufaddr, | ||
1479 | FEC_ENET_RX_FRSIZE - fep->rx_align, | ||
1480 | DMA_FROM_DEVICE); | ||
1481 | rx_processing_done: | 1531 | rx_processing_done: |
1482 | /* Clear the status flags for this buffer */ | 1532 | /* Clear the status flags for this buffer */ |
1483 | status &= ~BD_ENET_RX_STATS; | 1533 | status &= ~BD_ENET_RX_STATS; |
@@ -2392,6 +2442,44 @@ static void fec_enet_itr_coal_init(struct net_device *ndev) | |||
2392 | fec_enet_set_coalesce(ndev, &ec); | 2442 | fec_enet_set_coalesce(ndev, &ec); |
2393 | } | 2443 | } |
2394 | 2444 | ||
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 | |||
2395 | static const struct ethtool_ops fec_enet_ethtool_ops = { | 2483 | static const struct ethtool_ops fec_enet_ethtool_ops = { |
2396 | .get_settings = fec_enet_get_settings, | 2484 | .get_settings = fec_enet_get_settings, |
2397 | .set_settings = fec_enet_set_settings, | 2485 | .set_settings = fec_enet_set_settings, |
@@ -2408,6 +2496,8 @@ static const struct ethtool_ops fec_enet_ethtool_ops = { | |||
2408 | .get_sset_count = fec_enet_get_sset_count, | 2496 | .get_sset_count = fec_enet_get_sset_count, |
2409 | #endif | 2497 | #endif |
2410 | .get_ts_info = fec_enet_get_ts_info, | 2498 | .get_ts_info = fec_enet_get_ts_info, |
2499 | .get_tunable = fec_enet_get_tunable, | ||
2500 | .set_tunable = fec_enet_set_tunable, | ||
2411 | }; | 2501 | }; |
2412 | 2502 | ||
2413 | static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) | 2503 | 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) | |||
2553 | struct sk_buff *skb; | 2643 | struct sk_buff *skb; |
2554 | struct bufdesc *bdp; | 2644 | struct bufdesc *bdp; |
2555 | struct fec_enet_priv_rx_q *rxq; | 2645 | struct fec_enet_priv_rx_q *rxq; |
2556 | unsigned int off; | ||
2557 | 2646 | ||
2558 | rxq = fep->rx_queue[queue]; | 2647 | rxq = fep->rx_queue[queue]; |
2559 | bdp = rxq->rx_bd_base; | 2648 | bdp = rxq->rx_bd_base; |
2560 | for (i = 0; i < rxq->rx_ring_size; i++) { | 2649 | for (i = 0; i < rxq->rx_ring_size; i++) { |
2561 | dma_addr_t addr; | ||
2562 | |||
2563 | skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); | 2650 | skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); |
2564 | if (!skb) | 2651 | if (!skb) |
2565 | goto err_alloc; | 2652 | goto err_alloc; |
2566 | 2653 | ||
2567 | off = ((unsigned long)skb->data) & fep->rx_align; | 2654 | if (fec_enet_new_rxbdp(ndev, bdp, skb)) { |
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)) { | ||
2575 | dev_kfree_skb(skb); | 2655 | dev_kfree_skb(skb); |
2576 | if (net_ratelimit()) | ||
2577 | netdev_err(ndev, "Rx DMA memory map failed\n"); | ||
2578 | goto err_alloc; | 2656 | goto err_alloc; |
2579 | } | 2657 | } |
2580 | 2658 | ||
2581 | rxq->rx_skbuff[i] = skb; | 2659 | rxq->rx_skbuff[i] = skb; |
2582 | bdp->cbd_bufaddr = addr; | ||
2583 | bdp->cbd_sc = BD_ENET_RX_EMPTY; | 2660 | bdp->cbd_sc = BD_ENET_RX_EMPTY; |
2584 | 2661 | ||
2585 | if (fep->bufdesc_ex) { | 2662 | if (fep->bufdesc_ex) { |
@@ -3240,6 +3317,7 @@ fec_probe(struct platform_device *pdev) | |||
3240 | if (fep->bufdesc_ex && fep->ptp_clock) | 3317 | if (fep->bufdesc_ex && fep->ptp_clock) |
3241 | netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); | 3318 | netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); |
3242 | 3319 | ||
3320 | fep->rx_copybreak = COPYBREAK_DEFAULT; | ||
3243 | INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); | 3321 | INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); |
3244 | return 0; | 3322 | return 0; |
3245 | 3323 | ||