[PATCH 106/222] net:fec: better indexing for transmit descriptor ring
Russell King
rmk+kernel at arm.linux.org.uk
Fri Apr 25 04:39:58 PDT 2014
Maintaining the transmit ring position via pointers is inefficient,
especially when it involves complex pointer manipulation, and overlap
with the receive descriptor logic.
Re-implement this using indexes, and a single function which returns
the descriptor (appropriate to the descriptor type). As an additional
benefit, using a union allows cleaner access to the descriptor.
Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
---
drivers/net/ethernet/freescale/fec.h | 6 +-
drivers/net/ethernet/freescale/fec_main.c | 142 ++++++++++++++----------------
2 files changed, 67 insertions(+), 81 deletions(-)
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 30c683c0b480..c5a72c4bd07d 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -291,9 +291,9 @@ struct fec_enet_private {
union bufdesc_u *rx_bd_base;
union bufdesc_u *tx_bd_base;
/* The next free ring entry */
- union bufdesc_u *cur_rx, *cur_tx;
- /* The ring entries to be free()ed */
- union bufdesc_u *dirty_tx;
+ unsigned short tx_next;
+ unsigned short tx_dirty;
+ union bufdesc_u *cur_rx;
unsigned short tx_ring_size;
unsigned short rx_ring_size;
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 01e43a75089d..aec802b778f7 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -227,19 +227,28 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
static int mii_cnt;
+static union bufdesc_u *
+fec_enet_tx_get(unsigned index, struct fec_enet_private *fep)
+{
+ union bufdesc_u *base = fep->tx_bd_base;
+ union bufdesc_u *bdp;
+
+ if (fep->bufdesc_ex)
+ bdp = (union bufdesc_u *)(&base->ebd + index);
+ else
+ bdp = (union bufdesc_u *)(&base->bd + index);
+
+ return bdp;
+}
+
static inline
union bufdesc_u *fec_enet_get_nextdesc(union bufdesc_u *bdp, struct fec_enet_private *fep)
{
union bufdesc_u *base;
int ring_size;
- if (bdp >= fep->tx_bd_base) {
- base = fep->tx_bd_base;
- ring_size = fep->tx_ring_size;
- } else {
- base = fep->rx_bd_base;
- ring_size = fep->rx_ring_size;
- }
+ base = fep->rx_bd_base;
+ ring_size = fep->rx_ring_size;
if (fep->bufdesc_ex) {
struct bufdesc_ex *ebd = &bdp->ebd + 1;
@@ -258,13 +267,8 @@ union bufdesc_u *fec_enet_get_prevdesc(union bufdesc_u *bdp, struct fec_enet_pri
union bufdesc_u *base;
int ring_size;
- if (bdp >= fep->tx_bd_base) {
- base = fep->tx_bd_base;
- ring_size = fep->tx_ring_size;
- } else {
- base = fep->rx_bd_base;
- ring_size = fep->rx_ring_size;
- }
+ base = fep->rx_bd_base;
+ ring_size = fep->rx_ring_size;
if (fep->bufdesc_ex) {
struct bufdesc_ex *ebd = &bdp->ebd - 1;
@@ -291,23 +295,23 @@ static void *swap_buffer(void *bufaddr, int len)
static void fec_dump(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
- union bufdesc_u *bdp = fep->tx_bd_base;
+ union bufdesc_u *bdp;
unsigned index = 0;
netdev_info(ndev, "TX ring dump\n");
pr_info("Nr SC addr len SKB\n");
- do {
+ for (index = 0; index < fep->tx_ring_size; index++) {
+ bdp = fec_enet_tx_get(index, fep);
+
pr_info("%3u %c%c 0x%04x 0x%08lx %4u %p\n",
index,
- bdp == fep->cur_tx ? 'S' : ' ',
- bdp == fep->dirty_tx ? 'H' : ' ',
+ index == fep->tx_next ? 'S' : ' ',
+ index == fep->tx_dirty ? 'H' : ' ',
bdp->bd.cbd_sc, bdp->bd.cbd_bufaddr,
bdp->bd.cbd_datlen,
fep->tx_skbuff[index]);
- bdp = fec_enet_get_nextdesc(bdp, fep);
- index++;
- } while (bdp != fep->tx_bd_base);
+ }
}
static int
@@ -345,12 +349,13 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
union bufdesc_u *bdp;
void *bufaddr;
unsigned short status;
- unsigned int index;
+ unsigned index;
unsigned length;
dma_addr_t addr;
/* Fill in a Tx ring entry */
- bdp = fep->cur_tx;
+ index = fep->tx_next;
+ bdp = fec_enet_tx_get(index, fep);
status = bdp->bd.cbd_sc;
@@ -380,11 +385,6 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
* 4-byte boundaries. Use bounce buffers to copy data
* and get it aligned. Ugh.
*/
- if (fep->bufdesc_ex)
- index = &bdp->ebd - &fep->tx_bd_base->ebd;
- else
- index = &bdp->bd - &fep->tx_bd_base->bd;
-
if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
memcpy(fep->tx_bounce[index], skb->data, length);
bufaddr = fep->tx_bounce[index];
@@ -445,14 +445,14 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
| BD_ENET_TX_LAST | BD_ENET_TX_TC);
bdp->bd.cbd_sc = status;
- /* If this was the last BD in the ring, start at the beginning again. */
- bdp = fec_enet_get_nextdesc(bdp, fep);
-
skb_tx_timestamp(skb);
- fep->cur_tx = bdp;
+ if (++index >= fep->tx_ring_size)
+ index = 0;
- if (fep->cur_tx == fep->dirty_tx)
+ fep->tx_next = index;
+
+ if (fep->tx_next == fep->tx_dirty)
netif_stop_queue(ndev);
/* Trigger transmission start */
@@ -489,25 +489,24 @@ static void fec_enet_bd_init(struct net_device *dev)
fep->cur_rx = fep->rx_bd_base;
/* ...and the same for transmit */
- bdp = fep->tx_bd_base;
- fep->cur_tx = bdp;
for (i = 0; i < fep->tx_ring_size; i++) {
+ bdp = fec_enet_tx_get(i, fep);
/* Initialize the BD for every fragment in the page. */
- bdp->bd.cbd_sc = 0;
+ if (i == fep->tx_ring_size - 1)
+ bdp->bd.cbd_sc = BD_SC_WRAP;
+ else
+ bdp->bd.cbd_sc = 0;
if (bdp->bd.cbd_bufaddr)
fec_enet_tx_unmap(bdp, fep);
if (fep->tx_skbuff[i]) {
dev_kfree_skb_any(fep->tx_skbuff[i]);
fep->tx_skbuff[i] = NULL;
}
- bdp = fec_enet_get_nextdesc(bdp, fep);
}
- /* Set the last buffer to wrap */
- bdp = fec_enet_get_prevdesc(bdp, fep);
- bdp->bd.cbd_sc |= BD_SC_WRAP;
- fep->dirty_tx = bdp;
+ fep->tx_next = 0;
+ fep->tx_dirty = fep->tx_ring_size - 1;
}
/*
@@ -773,28 +772,25 @@ fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts,
static void
fec_enet_tx(struct net_device *ndev)
{
- struct fec_enet_private *fep;
+ struct fec_enet_private *fep = netdev_priv(ndev);
union bufdesc_u *bdp;
unsigned short status;
struct sk_buff *skb;
- int index = 0;
+ unsigned index = fep->tx_dirty;
- fep = netdev_priv(ndev);
- bdp = fep->dirty_tx;
-
- /* get next bdp of dirty_tx */
- bdp = fec_enet_get_nextdesc(bdp, fep);
-
- while (((status = bdp->bd.cbd_sc) & BD_ENET_TX_READY) == 0) {
+ do {
+ if (++index >= fep->tx_ring_size)
+ index = 0;
/* current queue is empty */
- if (bdp == fep->cur_tx)
+ if (index == fep->tx_next)
break;
- if (fep->bufdesc_ex)
- index = &bdp->ebd - &fep->tx_bd_base->ebd;
- else
- index = &bdp->bd - &fep->tx_bd_base->bd;
+ bdp = fec_enet_tx_get(index, fep);
+
+ status = bdp->bd.cbd_sc;
+ if (status & BD_ENET_TX_READY)
+ break;
fec_enet_tx_unmap(bdp, fep);
@@ -838,25 +834,17 @@ fec_enet_tx(struct net_device *ndev)
/* Free the sk buffer associated with this last transmit */
dev_kfree_skb_any(skb);
- fep->dirty_tx = bdp;
-
- /* Update pointer to next buffer descriptor to be transmitted */
- bdp = fec_enet_get_nextdesc(bdp, fep);
-
/* Since we have freed up a buffer, the ring is no longer full
*/
- if (fep->dirty_tx != fep->cur_tx) {
- if (netif_queue_stopped(ndev))
- netif_wake_queue(ndev);
- }
- }
+ if (netif_queue_stopped(ndev))
+ netif_wake_queue(ndev);
+
+ fep->tx_dirty = index;
+ } while (1);
/* ERR006538: Keep the transmitter going */
- if (fep->dirty_tx != fep->cur_tx &&
- readl(fep->hwp + FEC_X_DES_ACTIVE) == 0)
+ if (index != fep->tx_next && readl(fep->hwp + FEC_X_DES_ACTIVE) == 0)
writel(0, fep->hwp + FEC_X_DES_ACTIVE);
-
- return;
}
@@ -1737,8 +1725,8 @@ static void fec_enet_free_buffers(struct net_device *ndev)
bdp = fec_enet_get_nextdesc(bdp, fep);
}
- bdp = fep->tx_bd_base;
for (i = 0; i < fep->tx_ring_size; i++) {
+ bdp = fec_enet_tx_get(i, fep);
if (bdp->bd.cbd_bufaddr)
fec_enet_tx_unmap(bdp, fep);
kfree(fep->tx_bounce[i]);
@@ -1788,25 +1776,23 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
bdp = fec_enet_get_prevdesc(bdp, fep);
bdp->bd.cbd_sc |= BD_SC_WRAP;
- bdp = fep->tx_bd_base;
for (i = 0; i < fep->tx_ring_size; i++) {
+ bdp = fec_enet_tx_get(i, fep);
fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
if (!fep->tx_bounce[i])
goto err_alloc;
- bdp->bd.cbd_sc = 0;
+ /* Set the last buffer to wrap. */
+ if (i == fep->tx_ring_size - 1)
+ bdp->bd.cbd_sc = BD_SC_WRAP;
+ else
+ bdp->bd.cbd_sc = 0;
bdp->bd.cbd_bufaddr = 0;
if (fep->bufdesc_ex)
bdp->ebd.cbd_esc = BD_ENET_TX_INT;
-
- bdp = fec_enet_get_nextdesc(bdp, fep);
}
- /* Set the last buffer to wrap. */
- bdp = fec_enet_get_prevdesc(bdp, fep);
- bdp->bd.cbd_sc |= BD_SC_WRAP;
-
return 0;
err_alloc:
--
1.8.3.1
More information about the linux-arm-kernel
mailing list