diff options
-rw-r--r-- | drivers/net/ethernet/sfc/efx.c | 18 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/efx.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/ethtool.c | 67 |
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 | ||
1588 | void 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); | |||
113 | extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); | 113 | extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); |
114 | extern void efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs, | 114 | extern 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); |
116 | extern 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 */ |
118 | extern int efx_port_dummy_op_int(struct efx_nic *efx); | 120 | extern 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 | |||
589 | static int efx_ethtool_get_coalesce(struct net_device *net_dev, | 604 | static 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 | */ | ||
623 | static int efx_ethtool_set_coalesce(struct net_device *net_dev, | 620 | static 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) |