diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2009-03-20 09:30:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-03-21 22:06:58 -0400 |
commit | 6fb70fd1b57707a5c7b9fb167b7790b2cba13f04 (patch) | |
tree | 311192bd4de07e1da571f1a6ac6a3ad123ab3f50 /drivers/net/sfc | |
parent | 85451a951b9511605475fadcc0a8d3aeccefded8 (diff) |
sfc: Implement adaptive IRQ moderation
Calculate a score for each 1000 IRQs:
- TX completions are worth 1 point
- RX completions are worth 4 if merged using LRO or 2 otherwise
Reduce moderation if the score is less than 10000, down to a minimum
of 5 us. Increase moderation if the score is more than 20000, up to
the specified maximum.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sfc')
-rw-r--r-- | drivers/net/sfc/efx.c | 46 | ||||
-rw-r--r-- | drivers/net/sfc/efx.h | 2 | ||||
-rw-r--r-- | drivers/net/sfc/ethtool.c | 19 | ||||
-rw-r--r-- | drivers/net/sfc/falcon.c | 16 | ||||
-rw-r--r-- | drivers/net/sfc/falcon.h | 2 | ||||
-rw-r--r-- | drivers/net/sfc/net_driver.h | 9 |
6 files changed, 73 insertions, 21 deletions
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 8fa68d82c022..6eff9ca6c6c8 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c | |||
@@ -133,6 +133,16 @@ static int phy_flash_cfg; | |||
133 | module_param(phy_flash_cfg, int, 0644); | 133 | module_param(phy_flash_cfg, int, 0644); |
134 | MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially"); | 134 | MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially"); |
135 | 135 | ||
136 | static unsigned irq_adapt_low_thresh = 10000; | ||
137 | module_param(irq_adapt_low_thresh, uint, 0644); | ||
138 | MODULE_PARM_DESC(irq_adapt_low_thresh, | ||
139 | "Threshold score for reducing IRQ moderation"); | ||
140 | |||
141 | static unsigned irq_adapt_high_thresh = 20000; | ||
142 | module_param(irq_adapt_high_thresh, uint, 0644); | ||
143 | MODULE_PARM_DESC(irq_adapt_high_thresh, | ||
144 | "Threshold score for increasing IRQ moderation"); | ||
145 | |||
136 | /************************************************************************** | 146 | /************************************************************************** |
137 | * | 147 | * |
138 | * Utility functions and prototypes | 148 | * Utility functions and prototypes |
@@ -223,6 +233,35 @@ static int efx_poll(struct napi_struct *napi, int budget) | |||
223 | rx_packets = efx_process_channel(channel, budget); | 233 | rx_packets = efx_process_channel(channel, budget); |
224 | 234 | ||
225 | if (rx_packets < budget) { | 235 | if (rx_packets < budget) { |
236 | struct efx_nic *efx = channel->efx; | ||
237 | |||
238 | if (channel->used_flags & EFX_USED_BY_RX && | ||
239 | efx->irq_rx_adaptive && | ||
240 | unlikely(++channel->irq_count == 1000)) { | ||
241 | unsigned old_irq_moderation = channel->irq_moderation; | ||
242 | |||
243 | if (unlikely(channel->irq_mod_score < | ||
244 | irq_adapt_low_thresh)) { | ||
245 | channel->irq_moderation = | ||
246 | max_t(int, | ||
247 | channel->irq_moderation - | ||
248 | FALCON_IRQ_MOD_RESOLUTION, | ||
249 | FALCON_IRQ_MOD_RESOLUTION); | ||
250 | } else if (unlikely(channel->irq_mod_score > | ||
251 | irq_adapt_high_thresh)) { | ||
252 | channel->irq_moderation = | ||
253 | min(channel->irq_moderation + | ||
254 | FALCON_IRQ_MOD_RESOLUTION, | ||
255 | efx->irq_rx_moderation); | ||
256 | } | ||
257 | |||
258 | if (channel->irq_moderation != old_irq_moderation) | ||
259 | falcon_set_int_moderation(channel); | ||
260 | |||
261 | channel->irq_count = 0; | ||
262 | channel->irq_mod_score = 0; | ||
263 | } | ||
264 | |||
226 | /* There is no race here; although napi_disable() will | 265 | /* There is no race here; although napi_disable() will |
227 | * only wait for napi_complete(), this isn't a problem | 266 | * only wait for napi_complete(), this isn't a problem |
228 | * since efx_channel_processed() will have no effect if | 267 | * since efx_channel_processed() will have no effect if |
@@ -991,7 +1030,7 @@ static int efx_probe_nic(struct efx_nic *efx) | |||
991 | efx_set_channels(efx); | 1030 | efx_set_channels(efx); |
992 | 1031 | ||
993 | /* Initialise the interrupt moderation settings */ | 1032 | /* Initialise the interrupt moderation settings */ |
994 | efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec); | 1033 | efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec, true); |
995 | 1034 | ||
996 | return 0; | 1035 | return 0; |
997 | } | 1036 | } |
@@ -1188,7 +1227,8 @@ void efx_flush_queues(struct efx_nic *efx) | |||
1188 | **************************************************************************/ | 1227 | **************************************************************************/ |
1189 | 1228 | ||
1190 | /* Set interrupt moderation parameters */ | 1229 | /* Set interrupt moderation parameters */ |
1191 | void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs) | 1230 | void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs, |
1231 | bool rx_adaptive) | ||
1192 | { | 1232 | { |
1193 | struct efx_tx_queue *tx_queue; | 1233 | struct efx_tx_queue *tx_queue; |
1194 | struct efx_rx_queue *rx_queue; | 1234 | struct efx_rx_queue *rx_queue; |
@@ -1198,6 +1238,8 @@ void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs) | |||
1198 | efx_for_each_tx_queue(tx_queue, efx) | 1238 | efx_for_each_tx_queue(tx_queue, efx) |
1199 | tx_queue->channel->irq_moderation = tx_usecs; | 1239 | tx_queue->channel->irq_moderation = tx_usecs; |
1200 | 1240 | ||
1241 | efx->irq_rx_adaptive = rx_adaptive; | ||
1242 | efx->irq_rx_moderation = rx_usecs; | ||
1201 | efx_for_each_rx_queue(rx_queue, efx) | 1243 | efx_for_each_rx_queue(rx_queue, efx) |
1202 | rx_queue->channel->irq_moderation = rx_usecs; | 1244 | rx_queue->channel->irq_moderation = rx_usecs; |
1203 | } | 1245 | } |
diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h index 8bde1d2a21db..da157aa74b83 100644 --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h | |||
@@ -52,7 +52,7 @@ extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); | |||
52 | extern void efx_suspend(struct efx_nic *efx); | 52 | extern void efx_suspend(struct efx_nic *efx); |
53 | extern void efx_resume(struct efx_nic *efx); | 53 | extern void efx_resume(struct efx_nic *efx); |
54 | extern void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, | 54 | extern void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, |
55 | int rx_usecs); | 55 | int rx_usecs, bool rx_adaptive); |
56 | extern int efx_request_power(struct efx_nic *efx, int mw, const char *name); | 56 | extern int efx_request_power(struct efx_nic *efx, int mw, const char *name); |
57 | extern void efx_hex_dump(const u8 *, unsigned int, const char *); | 57 | extern void efx_hex_dump(const u8 *, unsigned int, const char *); |
58 | 58 | ||
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 589d13292969..64309f4e8b19 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c | |||
@@ -604,7 +604,6 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev, | |||
604 | { | 604 | { |
605 | struct efx_nic *efx = netdev_priv(net_dev); | 605 | struct efx_nic *efx = netdev_priv(net_dev); |
606 | struct efx_tx_queue *tx_queue; | 606 | struct efx_tx_queue *tx_queue; |
607 | struct efx_rx_queue *rx_queue; | ||
608 | struct efx_channel *channel; | 607 | struct efx_channel *channel; |
609 | 608 | ||
610 | memset(coalesce, 0, sizeof(*coalesce)); | 609 | memset(coalesce, 0, sizeof(*coalesce)); |
@@ -622,14 +621,8 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev, | |||
622 | } | 621 | } |
623 | } | 622 | } |
624 | 623 | ||
625 | /* Find lowest IRQ moderation across all used RX queues */ | 624 | coalesce->use_adaptive_rx_coalesce = efx->irq_rx_adaptive; |
626 | coalesce->rx_coalesce_usecs_irq = ~((u32) 0); | 625 | coalesce->rx_coalesce_usecs_irq = efx->irq_rx_moderation; |
627 | efx_for_each_rx_queue(rx_queue, efx) { | ||
628 | channel = rx_queue->channel; | ||
629 | if (channel->irq_moderation < coalesce->rx_coalesce_usecs_irq) | ||
630 | coalesce->rx_coalesce_usecs_irq = | ||
631 | channel->irq_moderation; | ||
632 | } | ||
633 | 626 | ||
634 | return 0; | 627 | return 0; |
635 | } | 628 | } |
@@ -643,10 +636,9 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev, | |||
643 | struct efx_nic *efx = netdev_priv(net_dev); | 636 | struct efx_nic *efx = netdev_priv(net_dev); |
644 | struct efx_channel *channel; | 637 | struct efx_channel *channel; |
645 | struct efx_tx_queue *tx_queue; | 638 | struct efx_tx_queue *tx_queue; |
646 | unsigned tx_usecs, rx_usecs; | 639 | unsigned tx_usecs, rx_usecs, adaptive; |
647 | 640 | ||
648 | if (coalesce->use_adaptive_rx_coalesce || | 641 | if (coalesce->use_adaptive_tx_coalesce) |
649 | coalesce->use_adaptive_tx_coalesce) | ||
650 | return -EOPNOTSUPP; | 642 | return -EOPNOTSUPP; |
651 | 643 | ||
652 | if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) { | 644 | if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) { |
@@ -657,6 +649,7 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev, | |||
657 | 649 | ||
658 | rx_usecs = coalesce->rx_coalesce_usecs_irq; | 650 | rx_usecs = coalesce->rx_coalesce_usecs_irq; |
659 | tx_usecs = coalesce->tx_coalesce_usecs_irq; | 651 | tx_usecs = coalesce->tx_coalesce_usecs_irq; |
652 | adaptive = coalesce->use_adaptive_rx_coalesce; | ||
660 | 653 | ||
661 | /* If the channel is shared only allow RX parameters to be set */ | 654 | /* If the channel is shared only allow RX parameters to be set */ |
662 | efx_for_each_tx_queue(tx_queue, efx) { | 655 | efx_for_each_tx_queue(tx_queue, efx) { |
@@ -668,7 +661,7 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev, | |||
668 | } | 661 | } |
669 | } | 662 | } |
670 | 663 | ||
671 | efx_init_irq_moderation(efx, tx_usecs, rx_usecs); | 664 | efx_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive); |
672 | 665 | ||
673 | /* Reset channel to pick up new moderation value. Note that | 666 | /* Reset channel to pick up new moderation value. Note that |
674 | * this may change the value of the irq_moderation field | 667 | * this may change the value of the irq_moderation field |
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index f42fc60d1df4..23a1b148d5b2 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c | |||
@@ -729,6 +729,9 @@ static void falcon_handle_tx_event(struct efx_channel *channel, | |||
729 | tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, TX_EV_DESC_PTR); | 729 | tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, TX_EV_DESC_PTR); |
730 | tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL); | 730 | tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL); |
731 | tx_queue = &efx->tx_queue[tx_ev_q_label]; | 731 | tx_queue = &efx->tx_queue[tx_ev_q_label]; |
732 | channel->irq_mod_score += | ||
733 | (tx_ev_desc_ptr - tx_queue->read_count) & | ||
734 | efx->type->txd_ring_mask; | ||
732 | efx_xmit_done(tx_queue, tx_ev_desc_ptr); | 735 | efx_xmit_done(tx_queue, tx_ev_desc_ptr); |
733 | } else if (EFX_QWORD_FIELD(*event, TX_EV_WQ_FF_FULL)) { | 736 | } else if (EFX_QWORD_FIELD(*event, TX_EV_WQ_FF_FULL)) { |
734 | /* Rewrite the FIFO write pointer */ | 737 | /* Rewrite the FIFO write pointer */ |
@@ -898,6 +901,8 @@ static void falcon_handle_rx_event(struct efx_channel *channel, | |||
898 | discard = true; | 901 | discard = true; |
899 | } | 902 | } |
900 | 903 | ||
904 | channel->irq_mod_score += 2; | ||
905 | |||
901 | /* Handle received packet */ | 906 | /* Handle received packet */ |
902 | efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, | 907 | efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, |
903 | checksummed, discard); | 908 | checksummed, discard); |
@@ -1075,14 +1080,15 @@ void falcon_set_int_moderation(struct efx_channel *channel) | |||
1075 | * program is based at 0. So actual interrupt moderation | 1080 | * program is based at 0. So actual interrupt moderation |
1076 | * achieved is ((x + 1) * res). | 1081 | * achieved is ((x + 1) * res). |
1077 | */ | 1082 | */ |
1078 | unsigned int res = 5; | 1083 | channel->irq_moderation -= (channel->irq_moderation % |
1079 | channel->irq_moderation -= (channel->irq_moderation % res); | 1084 | FALCON_IRQ_MOD_RESOLUTION); |
1080 | if (channel->irq_moderation < res) | 1085 | if (channel->irq_moderation < FALCON_IRQ_MOD_RESOLUTION) |
1081 | channel->irq_moderation = res; | 1086 | channel->irq_moderation = FALCON_IRQ_MOD_RESOLUTION; |
1082 | EFX_POPULATE_DWORD_2(timer_cmd, | 1087 | EFX_POPULATE_DWORD_2(timer_cmd, |
1083 | TIMER_MODE, TIMER_MODE_INT_HLDOFF, | 1088 | TIMER_MODE, TIMER_MODE_INT_HLDOFF, |
1084 | TIMER_VAL, | 1089 | TIMER_VAL, |
1085 | (channel->irq_moderation / res) - 1); | 1090 | channel->irq_moderation / |
1091 | FALCON_IRQ_MOD_RESOLUTION - 1); | ||
1086 | } else { | 1092 | } else { |
1087 | EFX_POPULATE_DWORD_2(timer_cmd, | 1093 | EFX_POPULATE_DWORD_2(timer_cmd, |
1088 | TIMER_MODE, TIMER_MODE_DIS, | 1094 | TIMER_MODE, TIMER_MODE_DIS, |
diff --git a/drivers/net/sfc/falcon.h b/drivers/net/sfc/falcon.h index 7869c3d74383..77f2e0db7ca1 100644 --- a/drivers/net/sfc/falcon.h +++ b/drivers/net/sfc/falcon.h | |||
@@ -85,6 +85,8 @@ extern void falcon_set_int_moderation(struct efx_channel *channel); | |||
85 | extern void falcon_disable_interrupts(struct efx_nic *efx); | 85 | extern void falcon_disable_interrupts(struct efx_nic *efx); |
86 | extern void falcon_fini_interrupt(struct efx_nic *efx); | 86 | extern void falcon_fini_interrupt(struct efx_nic *efx); |
87 | 87 | ||
88 | #define FALCON_IRQ_MOD_RESOLUTION 5 | ||
89 | |||
88 | /* Global Resources */ | 90 | /* Global Resources */ |
89 | extern int falcon_probe_nic(struct efx_nic *efx); | 91 | extern int falcon_probe_nic(struct efx_nic *efx); |
90 | extern int falcon_probe_resources(struct efx_nic *efx); | 92 | extern int falcon_probe_resources(struct efx_nic *efx); |
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index b81fc727dfff..e169e5dcd1e6 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h | |||
@@ -336,6 +336,8 @@ enum efx_rx_alloc_method { | |||
336 | * @eventq_read_ptr: Event queue read pointer | 336 | * @eventq_read_ptr: Event queue read pointer |
337 | * @last_eventq_read_ptr: Last event queue read pointer value. | 337 | * @last_eventq_read_ptr: Last event queue read pointer value. |
338 | * @eventq_magic: Event queue magic value for driver-generated test events | 338 | * @eventq_magic: Event queue magic value for driver-generated test events |
339 | * @irq_count: Number of IRQs since last adaptive moderation decision | ||
340 | * @irq_mod_score: IRQ moderation score | ||
339 | * @rx_alloc_level: Watermark based heuristic counter for pushing descriptors | 341 | * @rx_alloc_level: Watermark based heuristic counter for pushing descriptors |
340 | * and diagnostic counters | 342 | * and diagnostic counters |
341 | * @rx_alloc_push_pages: RX allocation method currently in use for pushing | 343 | * @rx_alloc_push_pages: RX allocation method currently in use for pushing |
@@ -364,6 +366,9 @@ struct efx_channel { | |||
364 | unsigned int last_eventq_read_ptr; | 366 | unsigned int last_eventq_read_ptr; |
365 | unsigned int eventq_magic; | 367 | unsigned int eventq_magic; |
366 | 368 | ||
369 | unsigned int irq_count; | ||
370 | unsigned int irq_mod_score; | ||
371 | |||
367 | int rx_alloc_level; | 372 | int rx_alloc_level; |
368 | int rx_alloc_push_pages; | 373 | int rx_alloc_push_pages; |
369 | 374 | ||
@@ -703,6 +708,8 @@ union efx_multicast_hash { | |||
703 | * @membase: Memory BAR value | 708 | * @membase: Memory BAR value |
704 | * @biu_lock: BIU (bus interface unit) lock | 709 | * @biu_lock: BIU (bus interface unit) lock |
705 | * @interrupt_mode: Interrupt mode | 710 | * @interrupt_mode: Interrupt mode |
711 | * @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues | ||
712 | * @irq_rx_moderation: IRQ moderation time for RX event queues | ||
706 | * @i2c_adap: I2C adapter | 713 | * @i2c_adap: I2C adapter |
707 | * @board_info: Board-level information | 714 | * @board_info: Board-level information |
708 | * @state: Device state flag. Serialised by the rtnl_lock. | 715 | * @state: Device state flag. Serialised by the rtnl_lock. |
@@ -784,6 +791,8 @@ struct efx_nic { | |||
784 | void __iomem *membase; | 791 | void __iomem *membase; |
785 | spinlock_t biu_lock; | 792 | spinlock_t biu_lock; |
786 | enum efx_int_mode interrupt_mode; | 793 | enum efx_int_mode interrupt_mode; |
794 | bool irq_rx_adaptive; | ||
795 | unsigned int irq_rx_moderation; | ||
787 | 796 | ||
788 | struct i2c_adapter i2c_adap; | 797 | struct i2c_adapter i2c_adap; |
789 | struct efx_board board_info; | 798 | struct efx_board board_info; |