staging: et131x: Implement NAPI support
This implements NAPI support for et131x by: -adding a napi_struct to the private adapter struct -changing netfif_rx_skb() call to netif_receive_skb() -changing et131x_handle_recv_interrupt() to et131x_handle_recv_pkts() and taking a budget allocation. -changing et131x_handle_send_interrupt() to et131x_handle_send_pkts() -replacing bottom half workqueue with poll function which handles send & receive of skbs. -adding various other necessary standard napi calls. Also remove this item from the README TODO list. Signed-off-by: Mark Einon <mark.einon@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
df7b3b8a3a
commit
c2ebf58ba0
2 changed files with 50 additions and 63 deletions
|
@ -10,7 +10,6 @@ driver as they did not build properly at the time.
|
||||||
TODO:
|
TODO:
|
||||||
- Look at reducing the number of spinlocks
|
- Look at reducing the number of spinlocks
|
||||||
- Simplify code in nic_rx_pkts(), when determining multicast_pkts_rcvd
|
- Simplify code in nic_rx_pkts(), when determining multicast_pkts_rcvd
|
||||||
- Implement NAPI support
|
|
||||||
- In et131x_tx(), don't return NETDEV_TX_BUSY, just drop the packet with kfree_skb().
|
- In et131x_tx(), don't return NETDEV_TX_BUSY, just drop the packet with kfree_skb().
|
||||||
- Reduce the number of split lines by careful consideration of variable names etc.
|
- Reduce the number of split lines by careful consideration of variable names etc.
|
||||||
|
|
||||||
|
|
|
@ -470,7 +470,7 @@ struct et131x_adapter {
|
||||||
struct pci_dev *pdev;
|
struct pci_dev *pdev;
|
||||||
struct mii_bus *mii_bus;
|
struct mii_bus *mii_bus;
|
||||||
struct phy_device *phydev;
|
struct phy_device *phydev;
|
||||||
struct work_struct task;
|
struct napi_struct napi;
|
||||||
|
|
||||||
/* Flags that indicate current state of the adapter */
|
/* Flags that indicate current state of the adapter */
|
||||||
u32 flags;
|
u32 flags;
|
||||||
|
@ -2551,26 +2551,30 @@ static struct rfd *nic_rx_pkts(struct et131x_adapter *adapter)
|
||||||
|
|
||||||
skb->protocol = eth_type_trans(skb, adapter->netdev);
|
skb->protocol = eth_type_trans(skb, adapter->netdev);
|
||||||
skb->ip_summed = CHECKSUM_NONE;
|
skb->ip_summed = CHECKSUM_NONE;
|
||||||
netif_rx_ni(skb);
|
netif_receive_skb(skb);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
nic_return_rfd(adapter, rfd);
|
nic_return_rfd(adapter, rfd);
|
||||||
return rfd;
|
return rfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* et131x_handle_recv_interrupt - Interrupt handler for receive processing
|
/* et131x_handle_recv_pkts - Interrupt handler for receive processing
|
||||||
*
|
*
|
||||||
* Assumption, Rcv spinlock has been acquired.
|
* Assumption, Rcv spinlock has been acquired.
|
||||||
*/
|
*/
|
||||||
static void et131x_handle_recv_interrupt(struct et131x_adapter *adapter)
|
static int et131x_handle_recv_pkts(struct et131x_adapter *adapter, int budget)
|
||||||
{
|
{
|
||||||
struct rfd *rfd = NULL;
|
struct rfd *rfd = NULL;
|
||||||
u32 count = 0;
|
int count = 0;
|
||||||
|
int limit = budget;
|
||||||
bool done = true;
|
bool done = true;
|
||||||
struct rx_ring *rx_ring = &adapter->rx_ring;
|
struct rx_ring *rx_ring = &adapter->rx_ring;
|
||||||
|
|
||||||
|
if (budget > MAX_PACKETS_HANDLED)
|
||||||
|
limit = MAX_PACKETS_HANDLED;
|
||||||
|
|
||||||
/* Process up to available RFD's */
|
/* Process up to available RFD's */
|
||||||
while (count < MAX_PACKETS_HANDLED) {
|
while (count < limit) {
|
||||||
if (list_empty(&rx_ring->recv_list)) {
|
if (list_empty(&rx_ring->recv_list)) {
|
||||||
WARN_ON(rx_ring->num_ready_recv != 0);
|
WARN_ON(rx_ring->num_ready_recv != 0);
|
||||||
done = false;
|
done = false;
|
||||||
|
@ -2602,13 +2606,15 @@ static void et131x_handle_recv_interrupt(struct et131x_adapter *adapter)
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count == MAX_PACKETS_HANDLED || !done) {
|
if (count == limit || !done) {
|
||||||
rx_ring->unfinished_receives = true;
|
rx_ring->unfinished_receives = true;
|
||||||
writel(PARM_TX_TIME_INT_DEF * NANO_IN_A_MICRO,
|
writel(PARM_TX_TIME_INT_DEF * NANO_IN_A_MICRO,
|
||||||
&adapter->regs->global.watchdog_timer);
|
&adapter->regs->global.watchdog_timer);
|
||||||
} else
|
} else
|
||||||
/* Watchdog timer will disable itself if appropriate. */
|
/* Watchdog timer will disable itself if appropriate. */
|
||||||
rx_ring->unfinished_receives = false;
|
rx_ring->unfinished_receives = false;
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* et131x_tx_dma_memory_alloc
|
/* et131x_tx_dma_memory_alloc
|
||||||
|
@ -3094,14 +3100,14 @@ static void et131x_free_busy_send_packets(struct et131x_adapter *adapter)
|
||||||
tx_ring->used = 0;
|
tx_ring->used = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* et131x_handle_send_interrupt - Interrupt handler for sending processing
|
/* et131x_handle_send_pkts - Interrupt handler for sending processing
|
||||||
*
|
*
|
||||||
* Re-claim the send resources, complete sends and get more to send from
|
* Re-claim the send resources, complete sends and get more to send from
|
||||||
* the send wait queue.
|
* the send wait queue.
|
||||||
*
|
*
|
||||||
* Assumption - Send spinlock has been acquired
|
* Assumption - Send spinlock has been acquired
|
||||||
*/
|
*/
|
||||||
static void et131x_handle_send_interrupt(struct et131x_adapter *adapter)
|
static void et131x_handle_send_pkts(struct et131x_adapter *adapter)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u32 serviced;
|
u32 serviced;
|
||||||
|
@ -3720,9 +3726,9 @@ static void et131x_pci_remove(struct pci_dev *pdev)
|
||||||
struct et131x_adapter *adapter = netdev_priv(netdev);
|
struct et131x_adapter *adapter = netdev_priv(netdev);
|
||||||
|
|
||||||
unregister_netdev(netdev);
|
unregister_netdev(netdev);
|
||||||
|
netif_napi_del(&adapter->napi);
|
||||||
phy_disconnect(adapter->phydev);
|
phy_disconnect(adapter->phydev);
|
||||||
mdiobus_unregister(adapter->mii_bus);
|
mdiobus_unregister(adapter->mii_bus);
|
||||||
cancel_work_sync(&adapter->task);
|
|
||||||
kfree(adapter->mii_bus->irq);
|
kfree(adapter->mii_bus->irq);
|
||||||
mdiobus_free(adapter->mii_bus);
|
mdiobus_free(adapter->mii_bus);
|
||||||
|
|
||||||
|
@ -3802,6 +3808,7 @@ static irqreturn_t et131x_isr(int irq, void *dev_id)
|
||||||
bool handled = true;
|
bool handled = true;
|
||||||
struct net_device *netdev = (struct net_device *)dev_id;
|
struct net_device *netdev = (struct net_device *)dev_id;
|
||||||
struct et131x_adapter *adapter = netdev_priv(netdev);
|
struct et131x_adapter *adapter = netdev_priv(netdev);
|
||||||
|
struct address_map __iomem *iomem = adapter->regs;
|
||||||
struct rx_ring *rx_ring = &adapter->rx_ring;
|
struct rx_ring *rx_ring = &adapter->rx_ring;
|
||||||
struct tx_ring *tx_ring = &adapter->tx_ring;
|
struct tx_ring *tx_ring = &adapter->tx_ring;
|
||||||
u32 status;
|
u32 status;
|
||||||
|
@ -3838,7 +3845,6 @@ static irqreturn_t et131x_isr(int irq, void *dev_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is our interrupt, so process accordingly */
|
/* This is our interrupt, so process accordingly */
|
||||||
|
|
||||||
if (status & ET_INTR_WATCHDOG) {
|
if (status & ET_INTR_WATCHDOG) {
|
||||||
struct tcb *tcb = tx_ring->send_head;
|
struct tcb *tcb = tx_ring->send_head;
|
||||||
|
|
||||||
|
@ -3854,54 +3860,8 @@ static irqreturn_t et131x_isr(int irq, void *dev_id)
|
||||||
status &= ~ET_INTR_WATCHDOG;
|
status &= ~ET_INTR_WATCHDOG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!status) {
|
if (status & (ET_INTR_RXDMA_XFR_DONE | ET_INTR_TXDMA_ISR))
|
||||||
/* This interrupt has in some way been "handled" by
|
napi_schedule(&adapter->napi);
|
||||||
* the ISR. Either it was a spurious Rx interrupt, or
|
|
||||||
* it was a Tx interrupt that has been filtered by
|
|
||||||
* the ISR.
|
|
||||||
*/
|
|
||||||
et131x_enable_interrupts(adapter);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We need to save the interrupt status value for use in our
|
|
||||||
* DPC. We will clear the software copy of that in that
|
|
||||||
* routine.
|
|
||||||
*/
|
|
||||||
adapter->stats.interrupt_status = status;
|
|
||||||
|
|
||||||
/* Schedule the ISR handler as a bottom-half task in the
|
|
||||||
* kernel's tq_immediate queue, and mark the queue for
|
|
||||||
* execution
|
|
||||||
*/
|
|
||||||
schedule_work(&adapter->task);
|
|
||||||
out:
|
|
||||||
return IRQ_RETVAL(handled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* et131x_isr_handler - The ISR handler
|
|
||||||
*
|
|
||||||
* scheduled to run in a deferred context by the ISR. This is where the ISR's
|
|
||||||
* work actually gets done.
|
|
||||||
*/
|
|
||||||
static void et131x_isr_handler(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct et131x_adapter *adapter =
|
|
||||||
container_of(work, struct et131x_adapter, task);
|
|
||||||
u32 status = adapter->stats.interrupt_status;
|
|
||||||
struct address_map __iomem *iomem = adapter->regs;
|
|
||||||
|
|
||||||
/* These first two are by far the most common. Once handled, we clear
|
|
||||||
* their two bits in the status word. If the word is now zero, we
|
|
||||||
* exit.
|
|
||||||
*/
|
|
||||||
/* Handle all the completed Transmit interrupts */
|
|
||||||
if (status & ET_INTR_TXDMA_ISR)
|
|
||||||
et131x_handle_send_interrupt(adapter);
|
|
||||||
|
|
||||||
/* Handle all the completed Receives interrupts */
|
|
||||||
if (status & ET_INTR_RXDMA_XFR_DONE)
|
|
||||||
et131x_handle_recv_interrupt(adapter);
|
|
||||||
|
|
||||||
status &= ~(ET_INTR_TXDMA_ISR | ET_INTR_RXDMA_XFR_DONE);
|
status &= ~(ET_INTR_TXDMA_ISR | ET_INTR_RXDMA_XFR_DONE);
|
||||||
|
|
||||||
|
@ -4053,8 +4013,34 @@ static void et131x_isr_handler(struct work_struct *work)
|
||||||
* addressed module is in a power-down state and can't respond.
|
* addressed module is in a power-down state and can't respond.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
/* This interrupt has in some way been "handled" by
|
||||||
|
* the ISR. Either it was a spurious Rx interrupt, or
|
||||||
|
* it was a Tx interrupt that has been filtered by
|
||||||
|
* the ISR.
|
||||||
|
*/
|
||||||
|
et131x_enable_interrupts(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
et131x_enable_interrupts(adapter);
|
return IRQ_RETVAL(handled);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int et131x_poll(struct napi_struct *napi, int budget)
|
||||||
|
{
|
||||||
|
struct et131x_adapter *adapter =
|
||||||
|
container_of(napi, struct et131x_adapter, napi);
|
||||||
|
int work_done = et131x_handle_recv_pkts(adapter, budget);
|
||||||
|
|
||||||
|
et131x_handle_send_pkts(adapter);
|
||||||
|
|
||||||
|
if (work_done < budget) {
|
||||||
|
napi_complete(&adapter->napi);
|
||||||
|
et131x_enable_interrupts(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return work_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* et131x_stats - Return the current device statistics */
|
/* et131x_stats - Return the current device statistics */
|
||||||
|
@ -4123,6 +4109,8 @@ static int et131x_open(struct net_device *netdev)
|
||||||
|
|
||||||
adapter->flags |= FMP_ADAPTER_INTERRUPT_IN_USE;
|
adapter->flags |= FMP_ADAPTER_INTERRUPT_IN_USE;
|
||||||
|
|
||||||
|
napi_enable(&adapter->napi);
|
||||||
|
|
||||||
et131x_up(netdev);
|
et131x_up(netdev);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -4134,6 +4122,7 @@ static int et131x_close(struct net_device *netdev)
|
||||||
struct et131x_adapter *adapter = netdev_priv(netdev);
|
struct et131x_adapter *adapter = netdev_priv(netdev);
|
||||||
|
|
||||||
et131x_down(netdev);
|
et131x_down(netdev);
|
||||||
|
napi_disable(&adapter->napi);
|
||||||
|
|
||||||
adapter->flags &= ~FMP_ADAPTER_INTERRUPT_IN_USE;
|
adapter->flags &= ~FMP_ADAPTER_INTERRUPT_IN_USE;
|
||||||
free_irq(adapter->pdev->irq, netdev);
|
free_irq(adapter->pdev->irq, netdev);
|
||||||
|
@ -4514,8 +4503,7 @@ static int et131x_pci_setup(struct pci_dev *pdev,
|
||||||
/* Init send data structures */
|
/* Init send data structures */
|
||||||
et131x_init_send(adapter);
|
et131x_init_send(adapter);
|
||||||
|
|
||||||
/* Set up the task structure for the ISR's deferred handler */
|
netif_napi_add(netdev, &adapter->napi, et131x_poll, 64);
|
||||||
INIT_WORK(&adapter->task, et131x_isr_handler);
|
|
||||||
|
|
||||||
/* Copy address into the net_device struct */
|
/* Copy address into the net_device struct */
|
||||||
memcpy(netdev->dev_addr, adapter->addr, ETH_ALEN);
|
memcpy(netdev->dev_addr, adapter->addr, ETH_ALEN);
|
||||||
|
|
Loading…
Add table
Reference in a new issue