diff options
author | David S. Miller <davem@davemloft.net> | 2011-02-22 13:21:36 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-02-22 13:21:36 -0500 |
commit | 2a3bcfdde613884ba7c5bf0e116cfd1991d4ba20 (patch) | |
tree | e853693868d15168382978c7b98e7d10c85d47b3 /drivers/net | |
parent | eaefd1105bc431ef329599e307a07f2a36ae7872 (diff) | |
parent | 64d8ad6d745bbb596bfce3c5d0157267feba7e26 (diff) |
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/bwh/sfc-next-2.6
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/sfc/efx.c | 49 | ||||
-rw-r--r-- | drivers/net/sfc/efx.h | 15 | ||||
-rw-r--r-- | drivers/net/sfc/filter.c | 117 | ||||
-rw-r--r-- | drivers/net/sfc/net_driver.h | 3 |
4 files changed, 178 insertions, 6 deletions
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index d4e04256730b..35b7bc52a2d1 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/ethtool.h> | 21 | #include <linux/ethtool.h> |
22 | #include <linux/topology.h> | 22 | #include <linux/topology.h> |
23 | #include <linux/gfp.h> | 23 | #include <linux/gfp.h> |
24 | #include <linux/cpu_rmap.h> | ||
24 | #include "net_driver.h" | 25 | #include "net_driver.h" |
25 | #include "efx.h" | 26 | #include "efx.h" |
26 | #include "nic.h" | 27 | #include "nic.h" |
@@ -307,6 +308,8 @@ static int efx_poll(struct napi_struct *napi, int budget) | |||
307 | channel->irq_mod_score = 0; | 308 | channel->irq_mod_score = 0; |
308 | } | 309 | } |
309 | 310 | ||
311 | efx_filter_rfs_expire(channel); | ||
312 | |||
310 | /* There is no race here; although napi_disable() will | 313 | /* There is no race here; although napi_disable() will |
311 | * only wait for napi_complete(), this isn't a problem | 314 | * only wait for napi_complete(), this isn't a problem |
312 | * since efx_channel_processed() will have no effect if | 315 | * since efx_channel_processed() will have no effect if |
@@ -1175,10 +1178,32 @@ static int efx_wanted_channels(void) | |||
1175 | return count; | 1178 | return count; |
1176 | } | 1179 | } |
1177 | 1180 | ||
1181 | static int | ||
1182 | efx_init_rx_cpu_rmap(struct efx_nic *efx, struct msix_entry *xentries) | ||
1183 | { | ||
1184 | #ifdef CONFIG_RFS_ACCEL | ||
1185 | int i, rc; | ||
1186 | |||
1187 | efx->net_dev->rx_cpu_rmap = alloc_irq_cpu_rmap(efx->n_rx_channels); | ||
1188 | if (!efx->net_dev->rx_cpu_rmap) | ||
1189 | return -ENOMEM; | ||
1190 | for (i = 0; i < efx->n_rx_channels; i++) { | ||
1191 | rc = irq_cpu_rmap_add(efx->net_dev->rx_cpu_rmap, | ||
1192 | xentries[i].vector); | ||
1193 | if (rc) { | ||
1194 | free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap); | ||
1195 | efx->net_dev->rx_cpu_rmap = NULL; | ||
1196 | return rc; | ||
1197 | } | ||
1198 | } | ||
1199 | #endif | ||
1200 | return 0; | ||
1201 | } | ||
1202 | |||
1178 | /* Probe the number and type of interrupts we are able to obtain, and | 1203 | /* Probe the number and type of interrupts we are able to obtain, and |
1179 | * the resulting numbers of channels and RX queues. | 1204 | * the resulting numbers of channels and RX queues. |
1180 | */ | 1205 | */ |
1181 | static void efx_probe_interrupts(struct efx_nic *efx) | 1206 | static int efx_probe_interrupts(struct efx_nic *efx) |
1182 | { | 1207 | { |
1183 | int max_channels = | 1208 | int max_channels = |
1184 | min_t(int, efx->type->phys_addr_channels, EFX_MAX_CHANNELS); | 1209 | min_t(int, efx->type->phys_addr_channels, EFX_MAX_CHANNELS); |
@@ -1220,6 +1245,11 @@ static void efx_probe_interrupts(struct efx_nic *efx) | |||
1220 | efx->n_tx_channels = efx->n_channels; | 1245 | efx->n_tx_channels = efx->n_channels; |
1221 | efx->n_rx_channels = efx->n_channels; | 1246 | efx->n_rx_channels = efx->n_channels; |
1222 | } | 1247 | } |
1248 | rc = efx_init_rx_cpu_rmap(efx, xentries); | ||
1249 | if (rc) { | ||
1250 | pci_disable_msix(efx->pci_dev); | ||
1251 | return rc; | ||
1252 | } | ||
1223 | for (i = 0; i < n_channels; i++) | 1253 | for (i = 0; i < n_channels; i++) |
1224 | efx_get_channel(efx, i)->irq = | 1254 | efx_get_channel(efx, i)->irq = |
1225 | xentries[i].vector; | 1255 | xentries[i].vector; |
@@ -1253,6 +1283,8 @@ static void efx_probe_interrupts(struct efx_nic *efx) | |||
1253 | efx->n_tx_channels = 1; | 1283 | efx->n_tx_channels = 1; |
1254 | efx->legacy_irq = efx->pci_dev->irq; | 1284 | efx->legacy_irq = efx->pci_dev->irq; |
1255 | } | 1285 | } |
1286 | |||
1287 | return 0; | ||
1256 | } | 1288 | } |
1257 | 1289 | ||
1258 | static void efx_remove_interrupts(struct efx_nic *efx) | 1290 | static void efx_remove_interrupts(struct efx_nic *efx) |
@@ -1289,7 +1321,9 @@ static int efx_probe_nic(struct efx_nic *efx) | |||
1289 | 1321 | ||
1290 | /* Determine the number of channels and queues by trying to hook | 1322 | /* Determine the number of channels and queues by trying to hook |
1291 | * in MSI-X interrupts. */ | 1323 | * in MSI-X interrupts. */ |
1292 | efx_probe_interrupts(efx); | 1324 | rc = efx_probe_interrupts(efx); |
1325 | if (rc) | ||
1326 | goto fail; | ||
1293 | 1327 | ||
1294 | if (efx->n_channels > 1) | 1328 | if (efx->n_channels > 1) |
1295 | get_random_bytes(&efx->rx_hash_key, sizeof(efx->rx_hash_key)); | 1329 | get_random_bytes(&efx->rx_hash_key, sizeof(efx->rx_hash_key)); |
@@ -1304,6 +1338,10 @@ static int efx_probe_nic(struct efx_nic *efx) | |||
1304 | efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec, true); | 1338 | efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec, true); |
1305 | 1339 | ||
1306 | return 0; | 1340 | return 0; |
1341 | |||
1342 | fail: | ||
1343 | efx->type->remove(efx); | ||
1344 | return rc; | ||
1307 | } | 1345 | } |
1308 | 1346 | ||
1309 | static void efx_remove_nic(struct efx_nic *efx) | 1347 | static void efx_remove_nic(struct efx_nic *efx) |
@@ -1837,6 +1875,9 @@ static const struct net_device_ops efx_netdev_ops = { | |||
1837 | .ndo_poll_controller = efx_netpoll, | 1875 | .ndo_poll_controller = efx_netpoll, |
1838 | #endif | 1876 | #endif |
1839 | .ndo_setup_tc = efx_setup_tc, | 1877 | .ndo_setup_tc = efx_setup_tc, |
1878 | #ifdef CONFIG_RFS_ACCEL | ||
1879 | .ndo_rx_flow_steer = efx_filter_rfs, | ||
1880 | #endif | ||
1840 | }; | 1881 | }; |
1841 | 1882 | ||
1842 | static void efx_update_name(struct efx_nic *efx) | 1883 | static void efx_update_name(struct efx_nic *efx) |
@@ -2274,6 +2315,10 @@ static void efx_fini_struct(struct efx_nic *efx) | |||
2274 | */ | 2315 | */ |
2275 | static void efx_pci_remove_main(struct efx_nic *efx) | 2316 | static void efx_pci_remove_main(struct efx_nic *efx) |
2276 | { | 2317 | { |
2318 | #ifdef CONFIG_RFS_ACCEL | ||
2319 | free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap); | ||
2320 | efx->net_dev->rx_cpu_rmap = NULL; | ||
2321 | #endif | ||
2277 | efx_nic_fini_interrupt(efx); | 2322 | efx_nic_fini_interrupt(efx); |
2278 | efx_fini_channels(efx); | 2323 | efx_fini_channels(efx); |
2279 | efx_fini_port(efx); | 2324 | efx_fini_port(efx); |
diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h index 0cb198a64a63..cbce62b9c996 100644 --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h | |||
@@ -76,6 +76,21 @@ extern int efx_filter_remove_filter(struct efx_nic *efx, | |||
76 | struct efx_filter_spec *spec); | 76 | struct efx_filter_spec *spec); |
77 | extern void efx_filter_clear_rx(struct efx_nic *efx, | 77 | extern void efx_filter_clear_rx(struct efx_nic *efx, |
78 | enum efx_filter_priority priority); | 78 | enum efx_filter_priority priority); |
79 | #ifdef CONFIG_RFS_ACCEL | ||
80 | extern int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, | ||
81 | u16 rxq_index, u32 flow_id); | ||
82 | extern bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned quota); | ||
83 | static inline void efx_filter_rfs_expire(struct efx_channel *channel) | ||
84 | { | ||
85 | if (channel->rfs_filters_added >= 60 && | ||
86 | __efx_filter_rfs_expire(channel->efx, 100)) | ||
87 | channel->rfs_filters_added -= 60; | ||
88 | } | ||
89 | #define efx_filter_rfs_enabled() 1 | ||
90 | #else | ||
91 | static inline void efx_filter_rfs_expire(struct efx_channel *channel) {} | ||
92 | #define efx_filter_rfs_enabled() 0 | ||
93 | #endif | ||
79 | 94 | ||
80 | /* Channels */ | 95 | /* Channels */ |
81 | extern void efx_process_channel_now(struct efx_channel *channel); | 96 | extern void efx_process_channel_now(struct efx_channel *channel); |
diff --git a/drivers/net/sfc/filter.c b/drivers/net/sfc/filter.c index d4722c41c4ce..95a980fd63d5 100644 --- a/drivers/net/sfc/filter.c +++ b/drivers/net/sfc/filter.c | |||
@@ -8,6 +8,7 @@ | |||
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <linux/in.h> | 10 | #include <linux/in.h> |
11 | #include <net/ip.h> | ||
11 | #include "efx.h" | 12 | #include "efx.h" |
12 | #include "filter.h" | 13 | #include "filter.h" |
13 | #include "io.h" | 14 | #include "io.h" |
@@ -27,6 +28,10 @@ | |||
27 | */ | 28 | */ |
28 | #define FILTER_CTL_SRCH_MAX 200 | 29 | #define FILTER_CTL_SRCH_MAX 200 |
29 | 30 | ||
31 | /* Don't try very hard to find space for performance hints, as this is | ||
32 | * counter-productive. */ | ||
33 | #define FILTER_CTL_SRCH_HINT_MAX 5 | ||
34 | |||
30 | enum efx_filter_table_id { | 35 | enum efx_filter_table_id { |
31 | EFX_FILTER_TABLE_RX_IP = 0, | 36 | EFX_FILTER_TABLE_RX_IP = 0, |
32 | EFX_FILTER_TABLE_RX_MAC, | 37 | EFX_FILTER_TABLE_RX_MAC, |
@@ -47,6 +52,10 @@ struct efx_filter_table { | |||
47 | struct efx_filter_state { | 52 | struct efx_filter_state { |
48 | spinlock_t lock; | 53 | spinlock_t lock; |
49 | struct efx_filter_table table[EFX_FILTER_TABLE_COUNT]; | 54 | struct efx_filter_table table[EFX_FILTER_TABLE_COUNT]; |
55 | #ifdef CONFIG_RFS_ACCEL | ||
56 | u32 *rps_flow_id; | ||
57 | unsigned rps_expire_index; | ||
58 | #endif | ||
50 | }; | 59 | }; |
51 | 60 | ||
52 | /* The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit | 61 | /* The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit |
@@ -325,15 +334,16 @@ static int efx_filter_search(struct efx_filter_table *table, | |||
325 | struct efx_filter_spec *spec, u32 key, | 334 | struct efx_filter_spec *spec, u32 key, |
326 | bool for_insert, int *depth_required) | 335 | bool for_insert, int *depth_required) |
327 | { | 336 | { |
328 | unsigned hash, incr, filter_idx, depth; | 337 | unsigned hash, incr, filter_idx, depth, depth_max; |
329 | struct efx_filter_spec *cmp; | 338 | struct efx_filter_spec *cmp; |
330 | 339 | ||
331 | hash = efx_filter_hash(key); | 340 | hash = efx_filter_hash(key); |
332 | incr = efx_filter_increment(key); | 341 | incr = efx_filter_increment(key); |
342 | depth_max = (spec->priority <= EFX_FILTER_PRI_HINT ? | ||
343 | FILTER_CTL_SRCH_HINT_MAX : FILTER_CTL_SRCH_MAX); | ||
333 | 344 | ||
334 | for (depth = 1, filter_idx = hash & (table->size - 1); | 345 | for (depth = 1, filter_idx = hash & (table->size - 1); |
335 | depth <= FILTER_CTL_SRCH_MAX && | 346 | depth <= depth_max && test_bit(filter_idx, table->used_bitmap); |
336 | test_bit(filter_idx, table->used_bitmap); | ||
337 | ++depth) { | 347 | ++depth) { |
338 | cmp = &table->spec[filter_idx]; | 348 | cmp = &table->spec[filter_idx]; |
339 | if (efx_filter_equal(spec, cmp)) | 349 | if (efx_filter_equal(spec, cmp)) |
@@ -342,7 +352,7 @@ static int efx_filter_search(struct efx_filter_table *table, | |||
342 | } | 352 | } |
343 | if (!for_insert) | 353 | if (!for_insert) |
344 | return -ENOENT; | 354 | return -ENOENT; |
345 | if (depth > FILTER_CTL_SRCH_MAX) | 355 | if (depth > depth_max) |
346 | return -EBUSY; | 356 | return -EBUSY; |
347 | found: | 357 | found: |
348 | *depth_required = depth; | 358 | *depth_required = depth; |
@@ -562,6 +572,13 @@ int efx_probe_filters(struct efx_nic *efx) | |||
562 | spin_lock_init(&state->lock); | 572 | spin_lock_init(&state->lock); |
563 | 573 | ||
564 | if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { | 574 | if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { |
575 | #ifdef CONFIG_RFS_ACCEL | ||
576 | state->rps_flow_id = kcalloc(FR_BZ_RX_FILTER_TBL0_ROWS, | ||
577 | sizeof(*state->rps_flow_id), | ||
578 | GFP_KERNEL); | ||
579 | if (!state->rps_flow_id) | ||
580 | goto fail; | ||
581 | #endif | ||
565 | table = &state->table[EFX_FILTER_TABLE_RX_IP]; | 582 | table = &state->table[EFX_FILTER_TABLE_RX_IP]; |
566 | table->id = EFX_FILTER_TABLE_RX_IP; | 583 | table->id = EFX_FILTER_TABLE_RX_IP; |
567 | table->offset = FR_BZ_RX_FILTER_TBL0; | 584 | table->offset = FR_BZ_RX_FILTER_TBL0; |
@@ -607,5 +624,97 @@ void efx_remove_filters(struct efx_nic *efx) | |||
607 | kfree(state->table[table_id].used_bitmap); | 624 | kfree(state->table[table_id].used_bitmap); |
608 | vfree(state->table[table_id].spec); | 625 | vfree(state->table[table_id].spec); |
609 | } | 626 | } |
627 | #ifdef CONFIG_RFS_ACCEL | ||
628 | kfree(state->rps_flow_id); | ||
629 | #endif | ||
610 | kfree(state); | 630 | kfree(state); |
611 | } | 631 | } |
632 | |||
633 | #ifdef CONFIG_RFS_ACCEL | ||
634 | |||
635 | int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, | ||
636 | u16 rxq_index, u32 flow_id) | ||
637 | { | ||
638 | struct efx_nic *efx = netdev_priv(net_dev); | ||
639 | struct efx_channel *channel; | ||
640 | struct efx_filter_state *state = efx->filter_state; | ||
641 | struct efx_filter_spec spec; | ||
642 | const struct iphdr *ip; | ||
643 | const __be16 *ports; | ||
644 | int nhoff; | ||
645 | int rc; | ||
646 | |||
647 | nhoff = skb_network_offset(skb); | ||
648 | |||
649 | if (skb->protocol != htons(ETH_P_IP)) | ||
650 | return -EPROTONOSUPPORT; | ||
651 | |||
652 | /* RFS must validate the IP header length before calling us */ | ||
653 | EFX_BUG_ON_PARANOID(!pskb_may_pull(skb, nhoff + sizeof(*ip))); | ||
654 | ip = (const struct iphdr *)(skb->data + nhoff); | ||
655 | if (ip->frag_off & htons(IP_MF | IP_OFFSET)) | ||
656 | return -EPROTONOSUPPORT; | ||
657 | EFX_BUG_ON_PARANOID(!pskb_may_pull(skb, nhoff + 4 * ip->ihl + 4)); | ||
658 | ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl); | ||
659 | |||
660 | efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT, 0, rxq_index); | ||
661 | rc = efx_filter_set_ipv4_full(&spec, ip->protocol, | ||
662 | ip->daddr, ports[1], ip->saddr, ports[0]); | ||
663 | if (rc) | ||
664 | return rc; | ||
665 | |||
666 | rc = efx_filter_insert_filter(efx, &spec, true); | ||
667 | if (rc < 0) | ||
668 | return rc; | ||
669 | |||
670 | /* Remember this so we can check whether to expire the filter later */ | ||
671 | state->rps_flow_id[rc] = flow_id; | ||
672 | channel = efx_get_channel(efx, skb_get_rx_queue(skb)); | ||
673 | ++channel->rfs_filters_added; | ||
674 | |||
675 | netif_info(efx, rx_status, efx->net_dev, | ||
676 | "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n", | ||
677 | (ip->protocol == IPPROTO_TCP) ? "TCP" : "UDP", | ||
678 | &ip->saddr, ntohs(ports[0]), &ip->daddr, ntohs(ports[1]), | ||
679 | rxq_index, flow_id, rc); | ||
680 | |||
681 | return rc; | ||
682 | } | ||
683 | |||
684 | bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned quota) | ||
685 | { | ||
686 | struct efx_filter_state *state = efx->filter_state; | ||
687 | struct efx_filter_table *table = &state->table[EFX_FILTER_TABLE_RX_IP]; | ||
688 | unsigned mask = table->size - 1; | ||
689 | unsigned index; | ||
690 | unsigned stop; | ||
691 | |||
692 | if (!spin_trylock_bh(&state->lock)) | ||
693 | return false; | ||
694 | |||
695 | index = state->rps_expire_index; | ||
696 | stop = (index + quota) & mask; | ||
697 | |||
698 | while (index != stop) { | ||
699 | if (test_bit(index, table->used_bitmap) && | ||
700 | table->spec[index].priority == EFX_FILTER_PRI_HINT && | ||
701 | rps_may_expire_flow(efx->net_dev, | ||
702 | table->spec[index].dmaq_id, | ||
703 | state->rps_flow_id[index], index)) { | ||
704 | netif_info(efx, rx_status, efx->net_dev, | ||
705 | "expiring filter %d [flow %u]\n", | ||
706 | index, state->rps_flow_id[index]); | ||
707 | efx_filter_table_clear_entry(efx, table, index); | ||
708 | } | ||
709 | index = (index + 1) & mask; | ||
710 | } | ||
711 | |||
712 | state->rps_expire_index = stop; | ||
713 | if (table->used == 0) | ||
714 | efx_filter_table_reset_search_depth(table); | ||
715 | |||
716 | spin_unlock_bh(&state->lock); | ||
717 | return true; | ||
718 | } | ||
719 | |||
720 | #endif /* CONFIG_RFS_ACCEL */ | ||
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 96e22ad34970..15b9068e5b87 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h | |||
@@ -362,6 +362,9 @@ struct efx_channel { | |||
362 | 362 | ||
363 | unsigned int irq_count; | 363 | unsigned int irq_count; |
364 | unsigned int irq_mod_score; | 364 | unsigned int irq_mod_score; |
365 | #ifdef CONFIG_RFS_ACCEL | ||
366 | unsigned int rfs_filters_added; | ||
367 | #endif | ||
365 | 368 | ||
366 | int rx_alloc_level; | 369 | int rx_alloc_level; |
367 | int rx_alloc_push_pages; | 370 | int rx_alloc_push_pages; |