aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2011-09-05 03:42:25 -0400
committerDavid S. Miller <davem@davemloft.net>2011-09-16 16:50:36 -0400
commita0c4faf5484b1fe38952d5b975f19e9f4b8f0f2b (patch)
tree5f39c2e01c48dc795cdccfb63abb3f96702dadef /drivers/net
parentb548f97684412b0969dc148e1706eb047151e356 (diff)
sfc: Correct reporting and validation of TX interrupt coalescing
The reported TX IRQ moderation is generated in a completely crazy way. Make it simple and correct. When channels are shared between RX and TX, TX IRQ moderation must be the same as RX IRQ moderation, but must be specified as 0! Allow it to be either specified as the same, or left at its previous value in which case it will be quietly overridden. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/ethernet/sfc/efx.c18
-rw-r--r--drivers/net/ethernet/sfc/efx.h2
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c67
3 files changed, 53 insertions, 34 deletions
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 097ed8b4a79a..e0157c03d313 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -1585,6 +1585,24 @@ void efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs,
1585 } 1585 }
1586} 1586}
1587 1587
1588void efx_get_irq_moderation(struct efx_nic *efx, unsigned int *tx_usecs,
1589 unsigned int *rx_usecs, bool *rx_adaptive)
1590{
1591 *rx_adaptive = efx->irq_rx_adaptive;
1592 *rx_usecs = efx->irq_rx_moderation * EFX_IRQ_MOD_RESOLUTION;
1593
1594 /* If channels are shared between RX and TX, so is IRQ
1595 * moderation. Otherwise, IRQ moderation is the same for all
1596 * TX channels and is not adaptive.
1597 */
1598 if (efx->tx_channel_offset == 0)
1599 *tx_usecs = *rx_usecs;
1600 else
1601 *tx_usecs =
1602 efx->channel[efx->tx_channel_offset]->irq_moderation *
1603 EFX_IRQ_MOD_RESOLUTION;
1604}
1605
1588/************************************************************************** 1606/**************************************************************************
1589 * 1607 *
1590 * Hardware monitor 1608 * Hardware monitor
diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h
index 8f5acae431b9..8ca68631fce5 100644
--- a/drivers/net/ethernet/sfc/efx.h
+++ b/drivers/net/ethernet/sfc/efx.h
@@ -113,6 +113,8 @@ extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok);
113extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); 113extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
114extern void efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs, 114extern void efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs,
115 unsigned int rx_usecs, bool rx_adaptive); 115 unsigned int rx_usecs, bool rx_adaptive);
116extern void efx_get_irq_moderation(struct efx_nic *efx, unsigned int *tx_usecs,
117 unsigned int *rx_usecs, bool *rx_adaptive);
116 118
117/* Dummy PHY ops for PHY drivers */ 119/* Dummy PHY ops for PHY drivers */
118extern int efx_port_dummy_op_int(struct efx_nic *efx); 120extern int efx_port_dummy_op_int(struct efx_nic *efx);
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c
index dedaa2c97e3c..1cb6ed0a255c 100644
--- a/drivers/net/ethernet/sfc/ethtool.c
+++ b/drivers/net/ethernet/sfc/ethtool.c
@@ -586,40 +586,37 @@ static int efx_ethtool_nway_reset(struct net_device *net_dev)
586 return mdio45_nway_restart(&efx->mdio); 586 return mdio45_nway_restart(&efx->mdio);
587} 587}
588 588
589/*
590 * Each channel has a single IRQ and moderation timer, started by any
591 * completion (or other event). Unless the module parameter
592 * separate_tx_channels is set, IRQs and moderation are therefore
593 * shared between RX and TX completions. In this case, when RX IRQ
594 * moderation is explicitly changed then TX IRQ moderation is
595 * automatically changed too, but otherwise we fail if the two values
596 * are requested to be different.
597 *
598 * We implement adaptive IRQ moderation, but use a different algorithm
599 * from that assumed in the definition of struct ethtool_coalesce.
600 * Therefore we do not use any of the adaptive moderation parameters
601 * in it.
602 */
603
589static int efx_ethtool_get_coalesce(struct net_device *net_dev, 604static int efx_ethtool_get_coalesce(struct net_device *net_dev,
590 struct ethtool_coalesce *coalesce) 605 struct ethtool_coalesce *coalesce)
591{ 606{
592 struct efx_nic *efx = netdev_priv(net_dev); 607 struct efx_nic *efx = netdev_priv(net_dev);
593 struct efx_channel *channel; 608 unsigned int tx_usecs, rx_usecs;
594 609 bool rx_adaptive;
595 memset(coalesce, 0, sizeof(*coalesce));
596
597 /* Find lowest IRQ moderation across all used TX queues */
598 coalesce->tx_coalesce_usecs_irq = ~((u32) 0);
599 efx_for_each_channel(channel, efx) {
600 if (!efx_channel_has_tx_queues(channel))
601 continue;
602 if (channel->irq_moderation < coalesce->tx_coalesce_usecs_irq) {
603 if (channel->channel < efx->n_rx_channels)
604 coalesce->tx_coalesce_usecs_irq =
605 channel->irq_moderation;
606 else
607 coalesce->tx_coalesce_usecs_irq = 0;
608 }
609 }
610 610
611 coalesce->use_adaptive_rx_coalesce = efx->irq_rx_adaptive; 611 efx_get_irq_moderation(efx, &tx_usecs, &rx_usecs, &rx_adaptive);
612 coalesce->rx_coalesce_usecs_irq = efx->irq_rx_moderation;
613 612
614 coalesce->tx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION; 613 coalesce->tx_coalesce_usecs_irq = tx_usecs;
615 coalesce->rx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION; 614 coalesce->rx_coalesce_usecs_irq = rx_usecs;
615 coalesce->use_adaptive_rx_coalesce = rx_adaptive;
616 616
617 return 0; 617 return 0;
618} 618}
619 619
620/* Set coalescing parameters
621 * The difficulties occur for shared channels
622 */
623static int efx_ethtool_set_coalesce(struct net_device *net_dev, 620static int efx_ethtool_set_coalesce(struct net_device *net_dev,
624 struct ethtool_coalesce *coalesce) 621 struct ethtool_coalesce *coalesce)
625{ 622{
@@ -637,20 +634,22 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev,
637 return -EINVAL; 634 return -EINVAL;
638 } 635 }
639 636
637 efx_get_irq_moderation(efx, &tx_usecs, &rx_usecs, &adaptive);
638
640 rx_usecs = coalesce->rx_coalesce_usecs_irq; 639 rx_usecs = coalesce->rx_coalesce_usecs_irq;
641 tx_usecs = coalesce->tx_coalesce_usecs_irq;
642 adaptive = coalesce->use_adaptive_rx_coalesce; 640 adaptive = coalesce->use_adaptive_rx_coalesce;
643 641
644 /* If the channel is shared only allow RX parameters to be set */ 642 /* If channels are shared, TX IRQ moderation can be quietly
645 efx_for_each_channel(channel, efx) { 643 * overridden unless it is changed from its old value.
646 if (efx_channel_has_rx_queue(channel) && 644 */
647 efx_channel_has_tx_queues(channel) && 645 if (efx->tx_channel_offset == 0 &&
648 tx_usecs) { 646 coalesce->tx_coalesce_usecs_irq != tx_usecs &&
649 netif_err(efx, drv, efx->net_dev, "Channel is shared. " 647 coalesce->tx_coalesce_usecs_irq != rx_usecs) {
650 "Only RX coalescing may be set\n"); 648 netif_err(efx, drv, efx->net_dev, "Channels are shared. "
651 return -EINVAL; 649 "RX and TX IRQ moderation must be equal\n");
652 } 650 return -EINVAL;
653 } 651 }
652 tx_usecs = coalesce->tx_coalesce_usecs_irq;
654 653
655 efx_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive); 654 efx_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive);
656 efx_for_each_channel(channel, efx) 655 efx_for_each_channel(channel, efx)