Skip to content

Commit 6e90928

Browse files
Nimrod Andydavem330
authored andcommitted
net: fec: Add Scatter/gather support
Add Scatter/gather support for FEC. This feature allows to improve outbound throughput performance. Tested on imx6dl sabresd board: Running iperf tests shows a 55.4% improvement. $ ethtool -K eth0 sg off $ iperf -c 10.192.242.167 -t 3 & [ 3] local 10.192.242.108 port 52618 connected with 10.192.242.167 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0- 3.0 sec 99.5 MBytes 278 Mbits/sec $ ethtool -K eth0 sg on $ iperf -c 10.192.242.167 -t 3 & [ 3] local 10.192.242.108 port 52617 connected with 10.192.242.167 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0- 3.0 sec 154 MBytes 432 Mbits/sec CC: Li Frank <[email protected]> Signed-off-by: Fugang Duan <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 55d0218 commit 6e90928

File tree

2 files changed

+178
-62
lines changed

2 files changed

+178
-62
lines changed

drivers/net/ethernet/freescale/fec.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ struct bufdesc_ex {
221221
#define BD_ENET_TX_RCMASK ((ushort)0x003c)
222222
#define BD_ENET_TX_UN ((ushort)0x0002)
223223
#define BD_ENET_TX_CSL ((ushort)0x0001)
224-
#define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */
224+
#define BD_ENET_TX_STATS ((ushort)0x0fff) /* All status bits */
225225

226226
/*enhanced buffer descriptor control/status used by Ethernet transmit*/
227227
#define BD_ENET_TX_INT 0x40000000

drivers/net/ethernet/freescale/fec_main.c

Lines changed: 177 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,16 @@ static int fec_enet_get_bd_index(struct bufdesc *base, struct bufdesc *bdp,
289289
return ((const char *)bdp - (const char *)base) / fep->bufdesc_size;
290290
}
291291

292+
static int fec_enet_get_free_txdesc_num(struct fec_enet_private *fep)
293+
{
294+
int entries;
295+
296+
entries = ((const char *)fep->dirty_tx -
297+
(const char *)fep->cur_tx) / fep->bufdesc_size - 1;
298+
299+
return entries > 0 ? entries : entries + fep->tx_ring_size;
300+
}
301+
292302
static void *swap_buffer(void *bufaddr, int len)
293303
{
294304
int i;
@@ -316,103 +326,203 @@ fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
316326
return 0;
317327
}
318328

319-
static int txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
329+
static void
330+
fec_enet_submit_work(struct bufdesc *bdp, struct fec_enet_private *fep)
331+
{
332+
const struct platform_device_id *id_entry =
333+
platform_get_device_id(fep->pdev);
334+
struct bufdesc *bdp_pre;
335+
336+
bdp_pre = fec_enet_get_prevdesc(bdp, fep);
337+
if ((id_entry->driver_data & FEC_QUIRK_ERR006358) &&
338+
!(bdp_pre->cbd_sc & BD_ENET_TX_READY)) {
339+
fep->delay_work.trig_tx = true;
340+
schedule_delayed_work(&(fep->delay_work.delay_work),
341+
msecs_to_jiffies(1));
342+
}
343+
}
344+
345+
static int
346+
fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev)
320347
{
321348
struct fec_enet_private *fep = netdev_priv(ndev);
322349
const struct platform_device_id *id_entry =
323350
platform_get_device_id(fep->pdev);
324-
struct bufdesc *bdp, *bdp_pre;
325-
void *bufaddr;
326-
unsigned short status;
351+
struct bufdesc *bdp = fep->cur_tx;
352+
struct bufdesc_ex *ebdp;
353+
int nr_frags = skb_shinfo(skb)->nr_frags;
354+
int frag, frag_len;
355+
unsigned short status;
356+
unsigned int estatus = 0;
357+
skb_frag_t *this_frag;
327358
unsigned int index;
359+
void *bufaddr;
360+
int i;
328361

329-
/* Fill in a Tx ring entry */
362+
for (frag = 0; frag < nr_frags; frag++) {
363+
this_frag = &skb_shinfo(skb)->frags[frag];
364+
bdp = fec_enet_get_nextdesc(bdp, fep);
365+
ebdp = (struct bufdesc_ex *)bdp;
366+
367+
status = bdp->cbd_sc;
368+
status &= ~BD_ENET_TX_STATS;
369+
status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
370+
frag_len = skb_shinfo(skb)->frags[frag].size;
371+
372+
/* Handle the last BD specially */
373+
if (frag == nr_frags - 1) {
374+
status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
375+
if (fep->bufdesc_ex) {
376+
estatus |= BD_ENET_TX_INT;
377+
if (unlikely(skb_shinfo(skb)->tx_flags &
378+
SKBTX_HW_TSTAMP && fep->hwts_tx_en))
379+
estatus |= BD_ENET_TX_TS;
380+
}
381+
}
382+
383+
if (fep->bufdesc_ex) {
384+
if (skb->ip_summed == CHECKSUM_PARTIAL)
385+
estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
386+
ebdp->cbd_bdu = 0;
387+
ebdp->cbd_esc = estatus;
388+
}
389+
390+
bufaddr = page_address(this_frag->page.p) + this_frag->page_offset;
391+
392+
index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
393+
if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
394+
id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
395+
memcpy(fep->tx_bounce[index], bufaddr, frag_len);
396+
bufaddr = fep->tx_bounce[index];
397+
398+
if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
399+
swap_buffer(bufaddr, frag_len);
400+
}
401+
402+
bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
403+
frag_len, DMA_TO_DEVICE);
404+
if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
405+
dev_kfree_skb_any(skb);
406+
if (net_ratelimit())
407+
netdev_err(ndev, "Tx DMA memory map failed\n");
408+
goto dma_mapping_error;
409+
}
410+
411+
bdp->cbd_datlen = frag_len;
412+
bdp->cbd_sc = status;
413+
}
414+
415+
fep->cur_tx = bdp;
416+
417+
return 0;
418+
419+
dma_mapping_error:
330420
bdp = fep->cur_tx;
421+
for (i = 0; i < frag; i++) {
422+
bdp = fec_enet_get_nextdesc(bdp, fep);
423+
dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
424+
bdp->cbd_datlen, DMA_TO_DEVICE);
425+
}
426+
return NETDEV_TX_OK;
427+
}
331428

332-
status = bdp->cbd_sc;
429+
static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
430+
{
431+
struct fec_enet_private *fep = netdev_priv(ndev);
432+
const struct platform_device_id *id_entry =
433+
platform_get_device_id(fep->pdev);
434+
int nr_frags = skb_shinfo(skb)->nr_frags;
435+
struct bufdesc *bdp, *last_bdp;
436+
void *bufaddr;
437+
unsigned short status;
438+
unsigned short buflen;
439+
unsigned int estatus = 0;
440+
unsigned int index;
441+
int ret;
333442

334443
/* Protocol checksum off-load for TCP and UDP. */
335444
if (fec_enet_clear_csum(skb, ndev)) {
336445
dev_kfree_skb_any(skb);
337446
return NETDEV_TX_OK;
338447
}
339448

340-
/* Clear all of the status flags */
449+
/* Fill in a Tx ring entry */
450+
bdp = fep->cur_tx;
451+
status = bdp->cbd_sc;
341452
status &= ~BD_ENET_TX_STATS;
342453

343454
/* Set buffer length and buffer pointer */
344455
bufaddr = skb->data;
345-
bdp->cbd_datlen = skb->len;
456+
buflen = skb_headlen(skb);
346457

347458
index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
348-
349-
if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
350-
memcpy(fep->tx_bounce[index], skb->data, skb->len);
459+
if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
460+
id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
461+
memcpy(fep->tx_bounce[index], skb->data, buflen);
351462
bufaddr = fep->tx_bounce[index];
352-
}
353463

354-
/*
355-
* Some design made an incorrect assumption on endian mode of
356-
* the system that it's running on. As the result, driver has to
357-
* swap every frame going to and coming from the controller.
358-
*/
359-
if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
360-
swap_buffer(bufaddr, skb->len);
361-
362-
/* Save skb pointer */
363-
fep->tx_skbuff[index] = skb;
464+
if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
465+
swap_buffer(bufaddr, buflen);
466+
}
364467

365468
/* Push the data cache so the CPM does not get stale memory
366469
* data.
367470
*/
368471
bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
369-
skb->len, DMA_TO_DEVICE);
472+
buflen, DMA_TO_DEVICE);
370473
if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
371-
bdp->cbd_bufaddr = 0;
372-
fep->tx_skbuff[index] = NULL;
373474
dev_kfree_skb_any(skb);
374475
if (net_ratelimit())
375476
netdev_err(ndev, "Tx DMA memory map failed\n");
376477
return NETDEV_TX_OK;
377478
}
378479

480+
if (nr_frags) {
481+
ret = fec_enet_txq_submit_frag_skb(skb, ndev);
482+
if (ret)
483+
return ret;
484+
} else {
485+
status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
486+
if (fep->bufdesc_ex) {
487+
estatus = BD_ENET_TX_INT;
488+
if (unlikely(skb_shinfo(skb)->tx_flags &
489+
SKBTX_HW_TSTAMP && fep->hwts_tx_en))
490+
estatus |= BD_ENET_TX_TS;
491+
}
492+
}
493+
379494
if (fep->bufdesc_ex) {
380495

381496
struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
382-
ebdp->cbd_bdu = 0;
497+
383498
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
384-
fep->hwts_tx_en)) {
385-
ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT);
499+
fep->hwts_tx_en))
386500
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
387-
} else {
388-
ebdp->cbd_esc = BD_ENET_TX_INT;
389501

390-
/* Enable protocol checksum flags
391-
* We do not bother with the IP Checksum bits as they
392-
* are done by the kernel
393-
*/
394-
if (skb->ip_summed == CHECKSUM_PARTIAL)
395-
ebdp->cbd_esc |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
396-
}
502+
if (skb->ip_summed == CHECKSUM_PARTIAL)
503+
estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
504+
505+
ebdp->cbd_bdu = 0;
506+
ebdp->cbd_esc = estatus;
397507
}
398508

509+
last_bdp = fep->cur_tx;
510+
index = fec_enet_get_bd_index(fep->tx_bd_base, last_bdp, fep);
511+
/* Save skb pointer */
512+
fep->tx_skbuff[index] = skb;
513+
514+
bdp->cbd_datlen = buflen;
515+
399516
/* Send it on its way. Tell FEC it's ready, interrupt when done,
400517
* it's the last BD of the frame, and to put the CRC on the end.
401518
*/
402-
status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
403-
| BD_ENET_TX_LAST | BD_ENET_TX_TC);
519+
status |= (BD_ENET_TX_READY | BD_ENET_TX_TC);
404520
bdp->cbd_sc = status;
405521

406-
bdp_pre = fec_enet_get_prevdesc(bdp, fep);
407-
if ((id_entry->driver_data & FEC_QUIRK_ERR006358) &&
408-
!(bdp_pre->cbd_sc & BD_ENET_TX_READY)) {
409-
fep->delay_work.trig_tx = true;
410-
schedule_delayed_work(&(fep->delay_work.delay_work),
411-
msecs_to_jiffies(1));
412-
}
522+
fec_enet_submit_work(bdp, fep);
413523

414524
/* If this was the last BD in the ring, start at the beginning again. */
415-
bdp = fec_enet_get_nextdesc(bdp, fep);
525+
bdp = fec_enet_get_nextdesc(last_bdp, fep);
416526

417527
skb_tx_timestamp(skb);
418528

@@ -421,7 +531,7 @@ static int txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
421531
/* Trigger transmission start */
422532
writel(0, fep->hwp + FEC_X_DES_ACTIVE);
423533

424-
return NETDEV_TX_OK;
534+
return 0;
425535
}
426536

427537
static netdev_tx_t
@@ -430,6 +540,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
430540
struct fec_enet_private *fep = netdev_priv(ndev);
431541
struct bufdesc *bdp;
432542
unsigned short status;
543+
int entries_free;
433544
int ret;
434545

435546
/* Fill in a Tx ring entry */
@@ -441,15 +552,17 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
441552
/* Ooops. All transmit buffers are full. Bail out.
442553
* This should not happen, since ndev->tbusy should be set.
443554
*/
444-
netdev_err(ndev, "tx queue full!\n");
555+
if (net_ratelimit())
556+
netdev_err(ndev, "tx queue full!\n");
445557
return NETDEV_TX_BUSY;
446558
}
447559

448-
ret = txq_submit_skb(skb, ndev);
449-
if (ret == -EBUSY)
450-
return NETDEV_TX_BUSY;
560+
ret = fec_enet_txq_submit_skb(skb, ndev);
561+
if (ret)
562+
return ret;
451563

452-
if (fep->cur_tx == fep->dirty_tx)
564+
entries_free = fec_enet_get_free_txdesc_num(fep);
565+
if (entries_free < MAX_SKB_FRAGS + 1)
453566
netif_stop_queue(ndev);
454567

455568
return NETDEV_TX_OK;
@@ -770,6 +883,7 @@ fec_enet_tx(struct net_device *ndev)
770883
unsigned short status;
771884
struct sk_buff *skb;
772885
int index = 0;
886+
int entries;
773887

774888
fep = netdev_priv(ndev);
775889
bdp = fep->dirty_tx;
@@ -786,9 +900,13 @@ fec_enet_tx(struct net_device *ndev)
786900
index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
787901

788902
skb = fep->tx_skbuff[index];
789-
dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, skb->len,
903+
dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, bdp->cbd_datlen,
790904
DMA_TO_DEVICE);
791905
bdp->cbd_bufaddr = 0;
906+
if (!skb) {
907+
bdp = fec_enet_get_nextdesc(bdp, fep);
908+
continue;
909+
}
792910

793911
/* Check for errors. */
794912
if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
@@ -807,7 +925,7 @@ fec_enet_tx(struct net_device *ndev)
807925
ndev->stats.tx_carrier_errors++;
808926
} else {
809927
ndev->stats.tx_packets++;
810-
ndev->stats.tx_bytes += bdp->cbd_datlen;
928+
ndev->stats.tx_bytes += skb->len;
811929
}
812930

813931
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
@@ -844,15 +962,13 @@ fec_enet_tx(struct net_device *ndev)
844962

845963
/* Since we have freed up a buffer, the ring is no longer full
846964
*/
847-
if (fep->dirty_tx != fep->cur_tx) {
848-
if (netif_queue_stopped(ndev))
849-
netif_wake_queue(ndev);
850-
}
965+
entries = fec_enet_get_free_txdesc_num(fep);
966+
if (entries >= MAX_SKB_FRAGS + 1 && netif_queue_stopped(ndev))
967+
netif_wake_queue(ndev);
851968
}
852969
return;
853970
}
854971

855-
856972
/* During a receive, the cur_rx points to the current incoming buffer.
857973
* When we update through the ring, if the next incoming buffer has
858974
* not been given to the system, we just set the empty indicator,
@@ -2095,7 +2211,7 @@ static int fec_enet_init(struct net_device *ndev)
20952211
if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) {
20962212
/* enable hw accelerator */
20972213
ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
2098-
| NETIF_F_RXCSUM);
2214+
| NETIF_F_RXCSUM | NETIF_F_SG);
20992215
fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
21002216
}
21012217

0 commit comments

Comments
 (0)