Commit 44783d87 authored by David S. Miller's avatar David S. Miller

Merge branch 'fs_enet_napi'

Christophe Leroy says:

====================
net: fs_enet: Remove non NAPI RX and add NAPI for TX

When using a MPC8xx as a router, 'perf' shows a significant time spent in
fs_enet_interrupt() and fs_enet_start_xmit().
'perf annotate' shows that the time spent in fs_enet_start_xmit is indeed spent
between spin_unlock_irqrestore() and the following instruction, hence in
interrupt handling. This is due to the TX complete interrupt that fires after
each transmitted packet.
This patchset first remove all non NAPI handling as NAPI has become the only
mode for RX, then adds NAPI for handling TX complete.
This improves NAT TCP throughput by 21% on MPC885 with FEC.

Tested on MPC885 with FEC.

[PATCH 1/2] net: fs_enet: Remove non NAPI RX
[PATCH 2/2] net: fs_enet: Add NAPI TX
Signed-off-by: default avatarChristophe Leroy <christophe.leroy@c-s.fr>
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 935e2218 d43a396a
......@@ -215,139 +215,23 @@ static int fs_enet_rx_napi(struct napi_struct *napi, int budget)
return received;
}
/* non NAPI receive function */
static int fs_enet_rx_non_napi(struct net_device *dev)
static int fs_enet_tx_napi(struct napi_struct *napi, int budget)
{
struct fs_enet_private *fep = netdev_priv(dev);
const struct fs_platform_info *fpi = fep->fpi;
cbd_t __iomem *bdp;
struct sk_buff *skb, *skbn, *skbt;
int received = 0;
u16 pkt_len, sc;
int curidx;
/*
* First, grab all of the stats for the incoming packet.
* These get messed up if we get called due to a busy condition.
*/
bdp = fep->cur_rx;
while (((sc = CBDR_SC(bdp)) & BD_ENET_RX_EMPTY) == 0) {
curidx = bdp - fep->rx_bd_base;
/*
* Since we have allocated space to hold a complete frame,
* the last indicator should be set.
*/
if ((sc & BD_ENET_RX_LAST) == 0)
dev_warn(fep->dev, "rcv is not +last\n");
/*
* Check for errors.
*/
if (sc & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_CL |
BD_ENET_RX_NO | BD_ENET_RX_CR | BD_ENET_RX_OV)) {
fep->stats.rx_errors++;
/* Frame too long or too short. */
if (sc & (BD_ENET_RX_LG | BD_ENET_RX_SH))
fep->stats.rx_length_errors++;
/* Frame alignment */
if (sc & (BD_ENET_RX_NO | BD_ENET_RX_CL))
fep->stats.rx_frame_errors++;
/* CRC Error */
if (sc & BD_ENET_RX_CR)
fep->stats.rx_crc_errors++;
/* FIFO overrun */
if (sc & BD_ENET_RX_OV)
fep->stats.rx_crc_errors++;
skb = fep->rx_skbuff[curidx];
dma_unmap_single(fep->dev, CBDR_BUFADDR(bdp),
L1_CACHE_ALIGN(PKT_MAXBUF_SIZE),
DMA_FROM_DEVICE);
skbn = skb;
} else {
skb = fep->rx_skbuff[curidx];
dma_unmap_single(fep->dev, CBDR_BUFADDR(bdp),
L1_CACHE_ALIGN(PKT_MAXBUF_SIZE),
DMA_FROM_DEVICE);
/*
* Process the incoming frame.
*/
fep->stats.rx_packets++;
pkt_len = CBDR_DATLEN(bdp) - 4; /* remove CRC */
fep->stats.rx_bytes += pkt_len + 4;
if (pkt_len <= fpi->rx_copybreak) {
/* +2 to make IP header L1 cache aligned */
skbn = netdev_alloc_skb(dev, pkt_len + 2);
if (skbn != NULL) {
skb_reserve(skbn, 2); /* align IP header */
skb_copy_from_linear_data(skb,
skbn->data, pkt_len);
/* swap */
skbt = skb;
skb = skbn;
skbn = skbt;
}
} else {
skbn = netdev_alloc_skb(dev, ENET_RX_FRSIZE);
if (skbn)
skb_align(skbn, ENET_RX_ALIGN);
}
if (skbn != NULL) {
skb_put(skb, pkt_len); /* Make room */
skb->protocol = eth_type_trans(skb, dev);
received++;
netif_rx(skb);
} else {
fep->stats.rx_dropped++;
skbn = skb;
}
}
fep->rx_skbuff[curidx] = skbn;
CBDW_BUFADDR(bdp, dma_map_single(fep->dev, skbn->data,
L1_CACHE_ALIGN(PKT_MAXBUF_SIZE),
DMA_FROM_DEVICE));
CBDW_DATLEN(bdp, 0);
CBDW_SC(bdp, (sc & ~BD_ENET_RX_STATS) | BD_ENET_RX_EMPTY);
/*
* Update BD pointer to next entry.
*/
if ((sc & BD_ENET_RX_WRAP) == 0)
bdp++;
else
bdp = fep->rx_bd_base;
(*fep->ops->rx_bd_done)(dev);
}
fep->cur_rx = bdp;
return 0;
}
static void fs_enet_tx(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
struct fs_enet_private *fep = container_of(napi, struct fs_enet_private,
napi_tx);
struct net_device *dev = fep->ndev;
cbd_t __iomem *bdp;
struct sk_buff *skb;
int dirtyidx, do_wake, do_restart;
u16 sc;
int has_tx_work = 0;
spin_lock(&fep->tx_lock);
bdp = fep->dirty_tx;
/* clear TX status bits for napi*/
(*fep->ops->napi_clear_tx_event)(dev);
do_wake = do_restart = 0;
while (((sc = CBDR_SC(bdp)) & BD_ENET_TX_READY) == 0) {
dirtyidx = bdp - fep->tx_bd_base;
......@@ -400,7 +284,7 @@ static void fs_enet_tx(struct net_device *dev)
/*
* Free the sk buffer associated with this last transmit.
*/
dev_kfree_skb_irq(skb);
dev_kfree_skb(skb);
fep->tx_skbuff[dirtyidx] = NULL;
/*
......@@ -417,6 +301,7 @@ static void fs_enet_tx(struct net_device *dev)
*/
if (!fep->tx_free++)
do_wake = 1;
has_tx_work = 1;
}
fep->dirty_tx = bdp;
......@@ -424,10 +309,19 @@ static void fs_enet_tx(struct net_device *dev)
if (do_restart)
(*fep->ops->tx_restart)(dev);
if (!has_tx_work) {
napi_complete(napi);
(*fep->ops->napi_enable_tx)(dev);
}
spin_unlock(&fep->tx_lock);
if (do_wake)
netif_wake_queue(dev);
if (has_tx_work)
return budget;
return 0;
}
/*
......@@ -453,7 +347,6 @@ fs_enet_interrupt(int irq, void *dev_id)
nr++;
int_clr_events = int_events;
if (fpi->use_napi)
int_clr_events &= ~fep->ev_napi_rx;
(*fep->ops->clear_int_events)(dev, int_clr_events);
......@@ -462,9 +355,6 @@ fs_enet_interrupt(int irq, void *dev_id)
(*fep->ops->ev_error)(dev, int_events);
if (int_events & fep->ev_rx) {
if (!fpi->use_napi)
fs_enet_rx_non_napi(dev);
else {
napi_ok = napi_schedule_prep(&fep->napi);
(*fep->ops->napi_disable_rx)(dev);
......@@ -475,10 +365,18 @@ fs_enet_interrupt(int irq, void *dev_id)
if (napi_ok)
__napi_schedule(&fep->napi);
}
}
if (int_events & fep->ev_tx)
fs_enet_tx(dev);
if (int_events & fep->ev_tx) {
napi_ok = napi_schedule_prep(&fep->napi_tx);
(*fep->ops->napi_disable_tx)(dev);
(*fep->ops->clear_int_events)(dev, fep->ev_napi_tx);
/* NOTE: it is possible for FCCs in NAPI mode */
/* to submit a spurious interrupt while in poll */
if (napi_ok)
__napi_schedule(&fep->napi_tx);
}
}
handled = nr > 0;
......@@ -611,7 +509,6 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
cbd_t __iomem *bdp;
int curidx;
u16 sc;
unsigned long flags;
#ifdef CONFIG_FS_ENET_MPC5121_FEC
if (((unsigned long)skb->data) & 0x3) {
......@@ -626,7 +523,7 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
}
#endif
spin_lock_irqsave(&fep->tx_lock, flags);
spin_lock(&fep->tx_lock);
/*
* Fill in a Tx ring entry
......@@ -635,7 +532,7 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (!fep->tx_free || (CBDR_SC(bdp) & BD_ENET_TX_READY)) {
netif_stop_queue(dev);
spin_unlock_irqrestore(&fep->tx_lock, flags);
spin_unlock(&fep->tx_lock);
/*
* Ooops. All transmit buffers are full. Bail out.
......@@ -691,7 +588,7 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
(*fep->ops->tx_kickstart)(dev);
spin_unlock_irqrestore(&fep->tx_lock, flags);
spin_unlock(&fep->tx_lock);
return NETDEV_TX_OK;
}
......@@ -811,24 +708,24 @@ static int fs_enet_open(struct net_device *dev)
/* not doing this, will cause a crash in fs_enet_rx_napi */
fs_init_bds(fep->ndev);
if (fep->fpi->use_napi)
napi_enable(&fep->napi);
napi_enable(&fep->napi_tx);
/* Install our interrupt handler. */
r = request_irq(fep->interrupt, fs_enet_interrupt, IRQF_SHARED,
"fs_enet-mac", dev);
if (r != 0) {
dev_err(fep->dev, "Could not allocate FS_ENET IRQ!");
if (fep->fpi->use_napi)
napi_disable(&fep->napi);
napi_disable(&fep->napi_tx);
return -EINVAL;
}
err = fs_init_phy(dev);
if (err) {
free_irq(fep->interrupt, dev);
if (fep->fpi->use_napi)
napi_disable(&fep->napi);
napi_disable(&fep->napi_tx);
return err;
}
phy_start(fep->phydev);
......@@ -845,8 +742,8 @@ static int fs_enet_close(struct net_device *dev)
netif_stop_queue(dev);
netif_carrier_off(dev);
if (fep->fpi->use_napi)
napi_disable(&fep->napi);
napi_disable(&fep->napi_tx);
phy_stop(fep->phydev);
spin_lock_irqsave(&fep->lock, flags);
......@@ -1022,7 +919,6 @@ static int fs_enet_probe(struct platform_device *ofdev)
fpi->rx_ring = 32;
fpi->tx_ring = 32;
fpi->rx_copybreak = 240;
fpi->use_napi = 1;
fpi->napi_weight = 17;
fpi->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0);
if (!fpi->phy_node && of_phy_is_fixed_link(ofdev->dev.of_node)) {
......@@ -1102,9 +998,8 @@ static int fs_enet_probe(struct platform_device *ofdev)
ndev->netdev_ops = &fs_enet_netdev_ops;
ndev->watchdog_timeo = 2 * HZ;
if (fpi->use_napi)
netif_napi_add(ndev, &fep->napi, fs_enet_rx_napi,
fpi->napi_weight);
netif_napi_add(ndev, &fep->napi, fs_enet_rx_napi, fpi->napi_weight);
netif_napi_add(ndev, &fep->napi_tx, fs_enet_tx_napi, 2);
ndev->ethtool_ops = &fs_ethtool_ops;
......
......@@ -84,6 +84,9 @@ struct fs_ops {
void (*napi_clear_rx_event)(struct net_device *dev);
void (*napi_enable_rx)(struct net_device *dev);
void (*napi_disable_rx)(struct net_device *dev);
void (*napi_clear_tx_event)(struct net_device *dev);
void (*napi_enable_tx)(struct net_device *dev);
void (*napi_disable_tx)(struct net_device *dev);
void (*rx_bd_done)(struct net_device *dev);
void (*tx_kickstart)(struct net_device *dev);
u32 (*get_int_events)(struct net_device *dev);
......@@ -119,6 +122,7 @@ struct phy_info {
struct fs_enet_private {
struct napi_struct napi;
struct napi_struct napi_tx;
struct device *dev; /* pointer back to the device (must be initialized first) */
struct net_device *ndev;
spinlock_t lock; /* during all ops except TX pckt processing */
......@@ -149,6 +153,7 @@ struct fs_enet_private {
/* event masks */
u32 ev_napi_rx; /* mask of NAPI rx events */
u32 ev_napi_tx; /* mask of NAPI rx events */
u32 ev_rx; /* rx event mask */
u32 ev_tx; /* tx event mask */
u32 ev_err; /* error event mask */
......@@ -191,8 +196,8 @@ void fs_cleanup_bds(struct net_device *dev);
#define DRV_MODULE_NAME "fs_enet"
#define PFX DRV_MODULE_NAME ": "
#define DRV_MODULE_VERSION "1.0"
#define DRV_MODULE_RELDATE "Aug 8, 2005"
#define DRV_MODULE_VERSION "1.1"
#define DRV_MODULE_RELDATE "Sep 22, 2014"
/***************************************************************************/
......
......@@ -125,6 +125,7 @@ static int do_pd_setup(struct fs_enet_private *fep)
}
#define FCC_NAPI_RX_EVENT_MSK (FCC_ENET_RXF | FCC_ENET_RXB)
#define FCC_NAPI_TX_EVENT_MSK (FCC_ENET_TXF | FCC_ENET_TXB)
#define FCC_RX_EVENT (FCC_ENET_RXF)
#define FCC_TX_EVENT (FCC_ENET_TXB)
#define FCC_ERR_EVENT_MSK (FCC_ENET_TXE)
......@@ -137,6 +138,7 @@ static int setup_data(struct net_device *dev)
return -EINVAL;
fep->ev_napi_rx = FCC_NAPI_RX_EVENT_MSK;
fep->ev_napi_tx = FCC_NAPI_TX_EVENT_MSK;
fep->ev_rx = FCC_RX_EVENT;
fep->ev_tx = FCC_TX_EVENT;
fep->ev_err = FCC_ERR_EVENT_MSK;
......@@ -446,6 +448,30 @@ static void napi_disable_rx(struct net_device *dev)
C16(fccp, fcc_fccm, FCC_NAPI_RX_EVENT_MSK);
}
static void napi_clear_tx_event(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
fcc_t __iomem *fccp = fep->fcc.fccp;
W16(fccp, fcc_fcce, FCC_NAPI_TX_EVENT_MSK);
}
static void napi_enable_tx(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
fcc_t __iomem *fccp = fep->fcc.fccp;
S16(fccp, fcc_fccm, FCC_NAPI_TX_EVENT_MSK);
}
static void napi_disable_tx(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
fcc_t __iomem *fccp = fep->fcc.fccp;
C16(fccp, fcc_fccm, FCC_NAPI_TX_EVENT_MSK);
}
static void rx_bd_done(struct net_device *dev)
{
/* nothing */
......@@ -572,6 +598,9 @@ const struct fs_ops fs_fcc_ops = {
.napi_clear_rx_event = napi_clear_rx_event,
.napi_enable_rx = napi_enable_rx,
.napi_disable_rx = napi_disable_rx,
.napi_clear_tx_event = napi_clear_tx_event,
.napi_enable_tx = napi_enable_tx,
.napi_disable_tx = napi_disable_tx,
.rx_bd_done = rx_bd_done,
.tx_kickstart = tx_kickstart,
.get_int_events = get_int_events,
......
......@@ -110,6 +110,7 @@ static int do_pd_setup(struct fs_enet_private *fep)
}
#define FEC_NAPI_RX_EVENT_MSK (FEC_ENET_RXF | FEC_ENET_RXB)
#define FEC_NAPI_TX_EVENT_MSK (FEC_ENET_TXF | FEC_ENET_TXB)
#define FEC_RX_EVENT (FEC_ENET_RXF)
#define FEC_TX_EVENT (FEC_ENET_TXF)
#define FEC_ERR_EVENT_MSK (FEC_ENET_HBERR | FEC_ENET_BABR | \
......@@ -126,6 +127,7 @@ static int setup_data(struct net_device *dev)
fep->fec.htlo = 0;
fep->ev_napi_rx = FEC_NAPI_RX_EVENT_MSK;
fep->ev_napi_tx = FEC_NAPI_TX_EVENT_MSK;
fep->ev_rx = FEC_RX_EVENT;
fep->ev_tx = FEC_TX_EVENT;
fep->ev_err = FEC_ERR_EVENT_MSK;
......@@ -415,6 +417,30 @@ static void napi_disable_rx(struct net_device *dev)
FC(fecp, imask, FEC_NAPI_RX_EVENT_MSK);
}
static void napi_clear_tx_event(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
struct fec __iomem *fecp = fep->fec.fecp;
FW(fecp, ievent, FEC_NAPI_TX_EVENT_MSK);
}
static void napi_enable_tx(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
struct fec __iomem *fecp = fep->fec.fecp;
FS(fecp, imask, FEC_NAPI_TX_EVENT_MSK);
}
static void napi_disable_tx(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
struct fec __iomem *fecp = fep->fec.fecp;
FC(fecp, imask, FEC_NAPI_TX_EVENT_MSK);
}
static void rx_bd_done(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
......@@ -487,6 +513,9 @@ const struct fs_ops fs_fec_ops = {
.napi_clear_rx_event = napi_clear_rx_event,
.napi_enable_rx = napi_enable_rx,
.napi_disable_rx = napi_disable_rx,
.napi_clear_tx_event = napi_clear_tx_event,
.napi_enable_tx = napi_enable_tx,
.napi_disable_tx = napi_disable_tx,
.rx_bd_done = rx_bd_done,
.tx_kickstart = tx_kickstart,
.get_int_events = get_int_events,
......
......@@ -116,6 +116,7 @@ static int do_pd_setup(struct fs_enet_private *fep)
}
#define SCC_NAPI_RX_EVENT_MSK (SCCE_ENET_RXF | SCCE_ENET_RXB)
#define SCC_NAPI_TX_EVENT_MSK (SCCE_ENET_TXF | SCCE_ENET_TXB)
#define SCC_RX_EVENT (SCCE_ENET_RXF)
#define SCC_TX_EVENT (SCCE_ENET_TXB)
#define SCC_ERR_EVENT_MSK (SCCE_ENET_TXE | SCCE_ENET_BSY)
......@@ -130,6 +131,7 @@ static int setup_data(struct net_device *dev)
fep->scc.htlo = 0;
fep->ev_napi_rx = SCC_NAPI_RX_EVENT_MSK;
fep->ev_napi_tx = SCC_NAPI_TX_EVENT_MSK;
fep->ev_rx = SCC_RX_EVENT;
fep->ev_tx = SCC_TX_EVENT | SCCE_ENET_TXE;
fep->ev_err = SCC_ERR_EVENT_MSK;
......@@ -398,6 +400,30 @@ static void napi_disable_rx(struct net_device *dev)
C16(sccp, scc_sccm, SCC_NAPI_RX_EVENT_MSK);
}
static void napi_clear_tx_event(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
scc_t __iomem *sccp = fep->scc.sccp;
W16(sccp, scc_scce, SCC_NAPI_TX_EVENT_MSK);
}
static void napi_enable_tx(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
scc_t __iomem *sccp = fep->scc.sccp;
S16(sccp, scc_sccm, SCC_NAPI_TX_EVENT_MSK);
}
static void napi_disable_tx(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
scc_t __iomem *sccp = fep->scc.sccp;
C16(sccp, scc_sccm, SCC_NAPI_TX_EVENT_MSK);
}
static void rx_bd_done(struct net_device *dev)
{
/* nothing */
......@@ -471,6 +497,9 @@ const struct fs_ops fs_scc_ops = {
.napi_clear_rx_event = napi_clear_rx_event,
.napi_enable_rx = napi_enable_rx,
.napi_disable_rx = napi_disable_rx,
.napi_clear_tx_event = napi_clear_tx_event,
.napi_enable_tx = napi_enable_tx,
.napi_disable_tx = napi_disable_tx,
.rx_bd_done = rx_bd_done,
.tx_kickstart = tx_kickstart,
.get_int_events = get_int_events,
......
......@@ -139,7 +139,6 @@ struct fs_platform_info {
int rx_ring, tx_ring; /* number of buffers on rx */
__u8 macaddr[ETH_ALEN]; /* mac address */
int rx_copybreak; /* limit we copy small frames */
int use_napi; /* use NAPI */
int napi_weight; /* NAPI weight */
int use_rmii; /* use RMII mode */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment