diff options
author | Michael Reiss <michael.f.reiss@freescale.com> | 2007-04-13 02:26:11 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-04-28 11:01:04 -0400 |
commit | 702ff12ce7e9643084232a8d50b0b1eec26026ae (patch) | |
tree | d00d43218efdc3f6e5356c638438f2f0a2bc3774 /drivers/net/ucc_geth.c | |
parent | 728de4c927a3544b6d3da331b634035d4c75ca17 (diff) |
ucc_geth: NAPI-related bug fixes
Based partly on the gianfar driver, this patch fixes several
bugs which were causing NAPI to be completely unusable.
* An IRQ is still needed in NAPI, to kick off NAPI task,
and for Tx processing. Request the IRQ.
* If rx_work_limit = 0 we are not complete.
* While running Rx NAPI processing we must mask Rx events,
including Rx busy.
* ucc_geth_rx function does not need a lock.
Could lead to deadlock in NAPI case.
* There's no need to loop reading ucce multiple times in the ISR,
so while adding the call to schedule NAPI which was not there,
simplify the event processing into if-else format.
* Rx Busy now kicks off NAPI processing, while still
being counted as an error.
Signed-off-by: Michael Reiss <michael.f.reiss@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/ucc_geth.c')
-rw-r--r-- | drivers/net/ucc_geth.c | 98 |
1 files changed, 58 insertions, 40 deletions
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index d93cfde663e9..60be1e775380 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c | |||
@@ -3416,7 +3416,6 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit | |||
3416 | 3416 | ||
3417 | ugeth_vdbg("%s: IN", __FUNCTION__); | 3417 | ugeth_vdbg("%s: IN", __FUNCTION__); |
3418 | 3418 | ||
3419 | spin_lock(&ugeth->lock); | ||
3420 | /* collect received buffers */ | 3419 | /* collect received buffers */ |
3421 | bd = ugeth->rxBd[rxQ]; | 3420 | bd = ugeth->rxBd[rxQ]; |
3422 | 3421 | ||
@@ -3464,7 +3463,6 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit | |||
3464 | skb = get_new_skb(ugeth, bd); | 3463 | skb = get_new_skb(ugeth, bd); |
3465 | if (!skb) { | 3464 | if (!skb) { |
3466 | ugeth_warn("%s: No Rx Data Buffer", __FUNCTION__); | 3465 | ugeth_warn("%s: No Rx Data Buffer", __FUNCTION__); |
3467 | spin_unlock(&ugeth->lock); | ||
3468 | ugeth->stats.rx_dropped++; | 3466 | ugeth->stats.rx_dropped++; |
3469 | break; | 3467 | break; |
3470 | } | 3468 | } |
@@ -3485,7 +3483,6 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit | |||
3485 | } | 3483 | } |
3486 | 3484 | ||
3487 | ugeth->rxBd[rxQ] = bd; | 3485 | ugeth->rxBd[rxQ] = bd; |
3488 | spin_unlock(&ugeth->lock); | ||
3489 | return howmany; | 3486 | return howmany; |
3490 | } | 3487 | } |
3491 | 3488 | ||
@@ -3537,23 +3534,38 @@ static int ucc_geth_tx(struct net_device *dev, u8 txQ) | |||
3537 | static int ucc_geth_poll(struct net_device *dev, int *budget) | 3534 | static int ucc_geth_poll(struct net_device *dev, int *budget) |
3538 | { | 3535 | { |
3539 | struct ucc_geth_private *ugeth = netdev_priv(dev); | 3536 | struct ucc_geth_private *ugeth = netdev_priv(dev); |
3537 | struct ucc_geth_info *ug_info; | ||
3538 | struct ucc_fast_private *uccf; | ||
3540 | int howmany; | 3539 | int howmany; |
3541 | int rx_work_limit = *budget; | 3540 | u8 i; |
3542 | u8 rxQ = 0; | 3541 | int rx_work_limit; |
3542 | register u32 uccm; | ||
3543 | 3543 | ||
3544 | ug_info = ugeth->ug_info; | ||
3545 | |||
3546 | rx_work_limit = *budget; | ||
3544 | if (rx_work_limit > dev->quota) | 3547 | if (rx_work_limit > dev->quota) |
3545 | rx_work_limit = dev->quota; | 3548 | rx_work_limit = dev->quota; |
3546 | 3549 | ||
3547 | howmany = ucc_geth_rx(ugeth, rxQ, rx_work_limit); | 3550 | howmany = 0; |
3551 | |||
3552 | for (i = 0; i < ug_info->numQueuesRx; i++) { | ||
3553 | howmany += ucc_geth_rx(ugeth, i, rx_work_limit); | ||
3554 | } | ||
3548 | 3555 | ||
3549 | dev->quota -= howmany; | 3556 | dev->quota -= howmany; |
3550 | rx_work_limit -= howmany; | 3557 | rx_work_limit -= howmany; |
3551 | *budget -= howmany; | 3558 | *budget -= howmany; |
3552 | 3559 | ||
3553 | if (rx_work_limit >= 0) | 3560 | if (rx_work_limit > 0) { |
3554 | netif_rx_complete(dev); | 3561 | netif_rx_complete(dev); |
3562 | uccf = ugeth->uccf; | ||
3563 | uccm = in_be32(uccf->p_uccm); | ||
3564 | uccm |= UCCE_RX_EVENTS; | ||
3565 | out_be32(uccf->p_uccm, uccm); | ||
3566 | } | ||
3555 | 3567 | ||
3556 | return (rx_work_limit < 0) ? 1 : 0; | 3568 | return (rx_work_limit > 0) ? 0 : 1; |
3557 | } | 3569 | } |
3558 | #endif /* CONFIG_UGETH_NAPI */ | 3570 | #endif /* CONFIG_UGETH_NAPI */ |
3559 | 3571 | ||
@@ -3563,10 +3575,13 @@ static irqreturn_t ucc_geth_irq_handler(int irq, void *info) | |||
3563 | struct ucc_geth_private *ugeth = netdev_priv(dev); | 3575 | struct ucc_geth_private *ugeth = netdev_priv(dev); |
3564 | struct ucc_fast_private *uccf; | 3576 | struct ucc_fast_private *uccf; |
3565 | struct ucc_geth_info *ug_info; | 3577 | struct ucc_geth_info *ug_info; |
3566 | register u32 ucce = 0; | 3578 | register u32 ucce; |
3567 | register u32 bit_mask = UCCE_RXBF_SINGLE_MASK; | 3579 | register u32 uccm; |
3568 | register u32 tx_mask = UCCE_TXBF_SINGLE_MASK; | 3580 | #ifndef CONFIG_UGETH_NAPI |
3569 | register u8 i; | 3581 | register u32 rx_mask; |
3582 | #endif | ||
3583 | register u32 tx_mask; | ||
3584 | u8 i; | ||
3570 | 3585 | ||
3571 | ugeth_vdbg("%s: IN", __FUNCTION__); | 3586 | ugeth_vdbg("%s: IN", __FUNCTION__); |
3572 | 3587 | ||
@@ -3576,48 +3591,53 @@ static irqreturn_t ucc_geth_irq_handler(int irq, void *info) | |||
3576 | uccf = ugeth->uccf; | 3591 | uccf = ugeth->uccf; |
3577 | ug_info = ugeth->ug_info; | 3592 | ug_info = ugeth->ug_info; |
3578 | 3593 | ||
3579 | do { | 3594 | /* read and clear events */ |
3580 | ucce |= (u32) (in_be32(uccf->p_ucce) & in_be32(uccf->p_uccm)); | 3595 | ucce = (u32) in_be32(uccf->p_ucce); |
3581 | 3596 | uccm = (u32) in_be32(uccf->p_uccm); | |
3582 | /* clear event bits for next time */ | 3597 | ucce &= uccm; |
3583 | /* Side effect here is to mask ucce variable | 3598 | out_be32(uccf->p_ucce, ucce); |
3584 | for future processing below. */ | ||
3585 | out_be32(uccf->p_ucce, ucce); /* Clear with ones, | ||
3586 | but only bits in UCCM */ | ||
3587 | |||
3588 | /* We ignore Tx interrupts because Tx confirmation is | ||
3589 | done inside Tx routine */ | ||
3590 | 3599 | ||
3600 | /* check for receive events that require processing */ | ||
3601 | if (ucce & UCCE_RX_EVENTS) { | ||
3602 | #ifdef CONFIG_UGETH_NAPI | ||
3603 | if (netif_rx_schedule_prep(dev)) { | ||
3604 | uccm &= ~UCCE_RX_EVENTS; | ||
3605 | out_be32(uccf->p_uccm, uccm); | ||
3606 | __netif_rx_schedule(dev); | ||
3607 | } | ||
3608 | #else | ||
3609 | rx_mask = UCCE_RXBF_SINGLE_MASK; | ||
3591 | for (i = 0; i < ug_info->numQueuesRx; i++) { | 3610 | for (i = 0; i < ug_info->numQueuesRx; i++) { |
3592 | if (ucce & bit_mask) | 3611 | if (ucce & rx_mask) |
3593 | ucc_geth_rx(ugeth, i, | 3612 | ucc_geth_rx(ugeth, i, (int)ugeth->ug_info->bdRingLenRx[i]); |
3594 | (int)ugeth->ug_info-> | 3613 | ucce &= ~rx_mask; |
3595 | bdRingLenRx[i]); | 3614 | rx_mask <<= 1; |
3596 | ucce &= ~bit_mask; | ||
3597 | bit_mask <<= 1; | ||
3598 | } | 3615 | } |
3616 | #endif /* CONFIG_UGETH_NAPI */ | ||
3617 | } | ||
3599 | 3618 | ||
3619 | /* Tx event processing */ | ||
3620 | if (ucce & UCCE_TX_EVENTS) { | ||
3621 | spin_lock(&ugeth->lock); | ||
3622 | tx_mask = UCCE_TXBF_SINGLE_MASK; | ||
3600 | for (i = 0; i < ug_info->numQueuesTx; i++) { | 3623 | for (i = 0; i < ug_info->numQueuesTx; i++) { |
3601 | if (ucce & tx_mask) | 3624 | if (ucce & tx_mask) |
3602 | ucc_geth_tx(dev, i); | 3625 | ucc_geth_tx(dev, i); |
3603 | ucce &= ~tx_mask; | 3626 | ucce &= ~tx_mask; |
3604 | tx_mask <<= 1; | 3627 | tx_mask <<= 1; |
3605 | } | 3628 | } |
3629 | spin_unlock(&ugeth->lock); | ||
3630 | } | ||
3606 | 3631 | ||
3607 | /* Exceptions */ | 3632 | /* Errors and other events */ |
3633 | if (ucce & UCCE_OTHER) { | ||
3608 | if (ucce & UCCE_BSY) { | 3634 | if (ucce & UCCE_BSY) { |
3609 | ugeth_vdbg("Got BUSY irq!!!!"); | ||
3610 | ugeth->stats.rx_errors++; | 3635 | ugeth->stats.rx_errors++; |
3611 | ucce &= ~UCCE_BSY; | ||
3612 | } | 3636 | } |
3613 | if (ucce & UCCE_OTHER) { | 3637 | if (ucce & UCCE_TXE) { |
3614 | ugeth_vdbg("Got frame with error (ucce - 0x%08x)!!!!", | 3638 | ugeth->stats.tx_errors++; |
3615 | ucce); | ||
3616 | ugeth->stats.rx_errors++; | ||
3617 | ucce &= ~ucce; | ||
3618 | } | 3639 | } |
3619 | } | 3640 | } |
3620 | while (ucce); | ||
3621 | 3641 | ||
3622 | return IRQ_HANDLED; | 3642 | return IRQ_HANDLED; |
3623 | } | 3643 | } |
@@ -3677,7 +3697,6 @@ static int ucc_geth_open(struct net_device *dev) | |||
3677 | 3697 | ||
3678 | phy_start(ugeth->phydev); | 3698 | phy_start(ugeth->phydev); |
3679 | 3699 | ||
3680 | #ifndef CONFIG_UGETH_NAPI | ||
3681 | err = | 3700 | err = |
3682 | request_irq(ugeth->ug_info->uf_info.irq, ucc_geth_irq_handler, 0, | 3701 | request_irq(ugeth->ug_info->uf_info.irq, ucc_geth_irq_handler, 0, |
3683 | "UCC Geth", dev); | 3702 | "UCC Geth", dev); |
@@ -3687,7 +3706,6 @@ static int ucc_geth_open(struct net_device *dev) | |||
3687 | ucc_geth_stop(ugeth); | 3706 | ucc_geth_stop(ugeth); |
3688 | return err; | 3707 | return err; |
3689 | } | 3708 | } |
3690 | #endif /* CONFIG_UGETH_NAPI */ | ||
3691 | 3709 | ||
3692 | err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX); | 3710 | err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX); |
3693 | if (err) { | 3711 | if (err) { |