diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2010-09-20 04:43:07 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-09-21 17:58:00 -0400 |
commit | 64eebcfd899a5d2ebe211a593ec13ec24630f1a3 (patch) | |
tree | b147a37d3b4a586464908221b9ea18f80c34ee46 /drivers | |
parent | a77f5db361ed9953b5b749353ea2c7fed2bf8d93 (diff) |
sfc: Add filter table management
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/sfc/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/sfc/efx.c | 11 | ||||
-rw-r--r-- | drivers/net/sfc/efx.h | 14 | ||||
-rw-r--r-- | drivers/net/sfc/filter.c | 445 | ||||
-rw-r--r-- | drivers/net/sfc/filter.h | 189 | ||||
-rw-r--r-- | drivers/net/sfc/net_driver.h | 4 | ||||
-rw-r--r-- | drivers/net/sfc/regs.h | 14 |
7 files changed, 678 insertions, 1 deletions
diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile index 1047b19c60a5..fd9272b5873a 100644 --- a/drivers/net/sfc/Makefile +++ b/drivers/net/sfc/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | sfc-y += efx.o nic.o falcon.o siena.o tx.o rx.o \ | 1 | sfc-y += efx.o nic.o falcon.o siena.o tx.o rx.o filter.o \ |
2 | falcon_gmac.o falcon_xmac.o mcdi_mac.o \ | 2 | falcon_gmac.o falcon_xmac.o mcdi_mac.o \ |
3 | selftest.o ethtool.o qt202x_phy.o mdio_10g.o \ | 3 | selftest.o ethtool.o qt202x_phy.o mdio_10g.o \ |
4 | tenxpress.o falcon_boards.o mcdi.o mcdi_phy.o | 4 | tenxpress.o falcon_boards.o mcdi.o mcdi_phy.o |
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index f702f1fb63b6..4a1c93f165e6 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c | |||
@@ -1357,8 +1357,17 @@ static int efx_probe_all(struct efx_nic *efx) | |||
1357 | if (rc) | 1357 | if (rc) |
1358 | goto fail3; | 1358 | goto fail3; |
1359 | 1359 | ||
1360 | rc = efx_probe_filters(efx); | ||
1361 | if (rc) { | ||
1362 | netif_err(efx, probe, efx->net_dev, | ||
1363 | "failed to create filter tables\n"); | ||
1364 | goto fail4; | ||
1365 | } | ||
1366 | |||
1360 | return 0; | 1367 | return 0; |
1361 | 1368 | ||
1369 | fail4: | ||
1370 | efx_remove_channels(efx); | ||
1362 | fail3: | 1371 | fail3: |
1363 | efx_remove_port(efx); | 1372 | efx_remove_port(efx); |
1364 | fail2: | 1373 | fail2: |
@@ -1489,6 +1498,7 @@ static void efx_stop_all(struct efx_nic *efx) | |||
1489 | 1498 | ||
1490 | static void efx_remove_all(struct efx_nic *efx) | 1499 | static void efx_remove_all(struct efx_nic *efx) |
1491 | { | 1500 | { |
1501 | efx_remove_filters(efx); | ||
1492 | efx_remove_channels(efx); | 1502 | efx_remove_channels(efx); |
1493 | efx_remove_port(efx); | 1503 | efx_remove_port(efx); |
1494 | efx_remove_nic(efx); | 1504 | efx_remove_nic(efx); |
@@ -2002,6 +2012,7 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok) | |||
2002 | efx->mac_op->reconfigure(efx); | 2012 | efx->mac_op->reconfigure(efx); |
2003 | 2013 | ||
2004 | efx_init_channels(efx); | 2014 | efx_init_channels(efx); |
2015 | efx_restore_filters(efx); | ||
2005 | 2016 | ||
2006 | mutex_unlock(&efx->spi_lock); | 2017 | mutex_unlock(&efx->spi_lock); |
2007 | mutex_unlock(&efx->mac_lock); | 2018 | mutex_unlock(&efx->mac_lock); |
diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h index e783c0fedfd8..f502b14eb22c 100644 --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h | |||
@@ -12,6 +12,7 @@ | |||
12 | #define EFX_EFX_H | 12 | #define EFX_EFX_H |
13 | 13 | ||
14 | #include "net_driver.h" | 14 | #include "net_driver.h" |
15 | #include "filter.h" | ||
15 | 16 | ||
16 | /* PCI IDs */ | 17 | /* PCI IDs */ |
17 | #define EFX_VENDID_SFC 0x1924 | 18 | #define EFX_VENDID_SFC 0x1924 |
@@ -64,6 +65,19 @@ extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue); | |||
64 | * skb. Falcon/A1 may require up to three descriptors per skb_frag. */ | 65 | * skb. Falcon/A1 may require up to three descriptors per skb_frag. */ |
65 | #define EFX_MIN_RING_SIZE (roundup_pow_of_two(2 * 3 * MAX_SKB_FRAGS)) | 66 | #define EFX_MIN_RING_SIZE (roundup_pow_of_two(2 * 3 * MAX_SKB_FRAGS)) |
66 | 67 | ||
68 | /* Filters */ | ||
69 | extern int efx_probe_filters(struct efx_nic *efx); | ||
70 | extern void efx_restore_filters(struct efx_nic *efx); | ||
71 | extern void efx_remove_filters(struct efx_nic *efx); | ||
72 | extern int efx_filter_insert_filter(struct efx_nic *efx, | ||
73 | struct efx_filter_spec *spec, | ||
74 | bool replace); | ||
75 | extern int efx_filter_remove_filter(struct efx_nic *efx, | ||
76 | struct efx_filter_spec *spec); | ||
77 | extern void efx_filter_table_clear(struct efx_nic *efx, | ||
78 | enum efx_filter_table_id table_id, | ||
79 | enum efx_filter_priority priority); | ||
80 | |||
67 | /* Channels */ | 81 | /* Channels */ |
68 | extern void efx_process_channel_now(struct efx_channel *channel); | 82 | extern void efx_process_channel_now(struct efx_channel *channel); |
69 | extern int | 83 | extern int |
diff --git a/drivers/net/sfc/filter.c b/drivers/net/sfc/filter.c new file mode 100644 index 000000000000..abc884d09d57 --- /dev/null +++ b/drivers/net/sfc/filter.c | |||
@@ -0,0 +1,445 @@ | |||
1 | /**************************************************************************** | ||
2 | * Driver for Solarflare Solarstorm network controllers and boards | ||
3 | * Copyright 2005-2010 Solarflare Communications Inc. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published | ||
7 | * by the Free Software Foundation, incorporated herein by reference. | ||
8 | */ | ||
9 | |||
10 | #include "efx.h" | ||
11 | #include "filter.h" | ||
12 | #include "io.h" | ||
13 | #include "nic.h" | ||
14 | #include "regs.h" | ||
15 | |||
16 | /* "Fudge factors" - difference between programmed value and actual depth. | ||
17 | * Due to pipelined implementation we need to program H/W with a value that | ||
18 | * is larger than the hop limit we want. | ||
19 | */ | ||
20 | #define FILTER_CTL_SRCH_FUDGE_WILD 3 | ||
21 | #define FILTER_CTL_SRCH_FUDGE_FULL 1 | ||
22 | |||
23 | struct efx_filter_table { | ||
24 | u32 offset; /* address of table relative to BAR */ | ||
25 | unsigned size; /* number of entries */ | ||
26 | unsigned step; /* step between entries */ | ||
27 | unsigned used; /* number currently used */ | ||
28 | unsigned long *used_bitmap; | ||
29 | struct efx_filter_spec *spec; | ||
30 | }; | ||
31 | |||
32 | struct efx_filter_state { | ||
33 | spinlock_t lock; | ||
34 | struct efx_filter_table table[EFX_FILTER_TABLE_COUNT]; | ||
35 | unsigned search_depth[EFX_FILTER_TYPE_COUNT]; | ||
36 | }; | ||
37 | |||
38 | /* The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit | ||
39 | * key derived from the n-tuple. The initial LFSR state is 0xffff. */ | ||
40 | static u16 efx_filter_hash(u32 key) | ||
41 | { | ||
42 | u16 tmp; | ||
43 | |||
44 | /* First 16 rounds */ | ||
45 | tmp = 0x1fff ^ key >> 16; | ||
46 | tmp = tmp ^ tmp >> 3 ^ tmp >> 6; | ||
47 | tmp = tmp ^ tmp >> 9; | ||
48 | /* Last 16 rounds */ | ||
49 | tmp = tmp ^ tmp << 13 ^ key; | ||
50 | tmp = tmp ^ tmp >> 3 ^ tmp >> 6; | ||
51 | return tmp ^ tmp >> 9; | ||
52 | } | ||
53 | |||
54 | /* To allow for hash collisions, filter search continues at these | ||
55 | * increments from the first possible entry selected by the hash. */ | ||
56 | static u16 efx_filter_increment(u32 key) | ||
57 | { | ||
58 | return key * 2 - 1; | ||
59 | } | ||
60 | |||
61 | static enum efx_filter_table_id | ||
62 | efx_filter_type_table_id(enum efx_filter_type type) | ||
63 | { | ||
64 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_TCP_FULL >> 2)); | ||
65 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_TCP_WILD >> 2)); | ||
66 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_UDP_FULL >> 2)); | ||
67 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_UDP_WILD >> 2)); | ||
68 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_RX_MAC_FULL >> 2)); | ||
69 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_RX_MAC_WILD >> 2)); | ||
70 | return type >> 2; | ||
71 | } | ||
72 | |||
73 | static void | ||
74 | efx_filter_table_reset_search_depth(struct efx_filter_state *state, | ||
75 | enum efx_filter_table_id table_id) | ||
76 | { | ||
77 | memset(state->search_depth + (table_id << 2), 0, | ||
78 | sizeof(state->search_depth[0]) << 2); | ||
79 | } | ||
80 | |||
81 | static void efx_filter_push_rx_limits(struct efx_nic *efx) | ||
82 | { | ||
83 | struct efx_filter_state *state = efx->filter_state; | ||
84 | efx_oword_t filter_ctl; | ||
85 | |||
86 | efx_reado(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL); | ||
87 | |||
88 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_FULL_SRCH_LIMIT, | ||
89 | state->search_depth[EFX_FILTER_RX_TCP_FULL] + | ||
90 | FILTER_CTL_SRCH_FUDGE_FULL); | ||
91 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_WILD_SRCH_LIMIT, | ||
92 | state->search_depth[EFX_FILTER_RX_TCP_WILD] + | ||
93 | FILTER_CTL_SRCH_FUDGE_WILD); | ||
94 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_FULL_SRCH_LIMIT, | ||
95 | state->search_depth[EFX_FILTER_RX_UDP_FULL] + | ||
96 | FILTER_CTL_SRCH_FUDGE_FULL); | ||
97 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_WILD_SRCH_LIMIT, | ||
98 | state->search_depth[EFX_FILTER_RX_UDP_WILD] + | ||
99 | FILTER_CTL_SRCH_FUDGE_WILD); | ||
100 | |||
101 | if (state->table[EFX_FILTER_TABLE_RX_MAC].size) { | ||
102 | EFX_SET_OWORD_FIELD( | ||
103 | filter_ctl, FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT, | ||
104 | state->search_depth[EFX_FILTER_RX_MAC_FULL] + | ||
105 | FILTER_CTL_SRCH_FUDGE_FULL); | ||
106 | EFX_SET_OWORD_FIELD( | ||
107 | filter_ctl, FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT, | ||
108 | state->search_depth[EFX_FILTER_RX_MAC_WILD] + | ||
109 | FILTER_CTL_SRCH_FUDGE_WILD); | ||
110 | } | ||
111 | |||
112 | efx_writeo(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL); | ||
113 | } | ||
114 | |||
115 | /* Build a filter entry and return its n-tuple key. */ | ||
116 | static u32 efx_filter_build(efx_oword_t *filter, struct efx_filter_spec *spec) | ||
117 | { | ||
118 | u32 data3; | ||
119 | |||
120 | switch (efx_filter_type_table_id(spec->type)) { | ||
121 | case EFX_FILTER_TABLE_RX_IP: { | ||
122 | bool is_udp = (spec->type == EFX_FILTER_RX_UDP_FULL || | ||
123 | spec->type == EFX_FILTER_RX_UDP_WILD); | ||
124 | EFX_POPULATE_OWORD_7( | ||
125 | *filter, | ||
126 | FRF_BZ_RSS_EN, | ||
127 | !!(spec->flags & EFX_FILTER_FLAG_RX_RSS), | ||
128 | FRF_BZ_SCATTER_EN, | ||
129 | !!(spec->flags & EFX_FILTER_FLAG_RX_SCATTER), | ||
130 | FRF_BZ_TCP_UDP, is_udp, | ||
131 | FRF_BZ_RXQ_ID, spec->dmaq_id, | ||
132 | EFX_DWORD_2, spec->data[2], | ||
133 | EFX_DWORD_1, spec->data[1], | ||
134 | EFX_DWORD_0, spec->data[0]); | ||
135 | data3 = is_udp; | ||
136 | break; | ||
137 | } | ||
138 | |||
139 | case EFX_FILTER_TABLE_RX_MAC: { | ||
140 | bool is_wild = spec->type == EFX_FILTER_RX_MAC_WILD; | ||
141 | EFX_POPULATE_OWORD_8( | ||
142 | *filter, | ||
143 | FRF_CZ_RMFT_RSS_EN, | ||
144 | !!(spec->flags & EFX_FILTER_FLAG_RX_RSS), | ||
145 | FRF_CZ_RMFT_SCATTER_EN, | ||
146 | !!(spec->flags & EFX_FILTER_FLAG_RX_SCATTER), | ||
147 | FRF_CZ_RMFT_IP_OVERRIDE, | ||
148 | !!(spec->flags & EFX_FILTER_FLAG_RX_OVERRIDE_IP), | ||
149 | FRF_CZ_RMFT_RXQ_ID, spec->dmaq_id, | ||
150 | FRF_CZ_RMFT_WILDCARD_MATCH, is_wild, | ||
151 | FRF_CZ_RMFT_DEST_MAC_HI, spec->data[2], | ||
152 | FRF_CZ_RMFT_DEST_MAC_LO, spec->data[1], | ||
153 | FRF_CZ_RMFT_VLAN_ID, spec->data[0]); | ||
154 | data3 = is_wild; | ||
155 | break; | ||
156 | } | ||
157 | |||
158 | default: | ||
159 | BUG(); | ||
160 | } | ||
161 | |||
162 | return spec->data[0] ^ spec->data[1] ^ spec->data[2] ^ data3; | ||
163 | } | ||
164 | |||
165 | static bool efx_filter_equal(const struct efx_filter_spec *left, | ||
166 | const struct efx_filter_spec *right) | ||
167 | { | ||
168 | if (left->type != right->type || | ||
169 | memcmp(left->data, right->data, sizeof(left->data))) | ||
170 | return false; | ||
171 | |||
172 | return true; | ||
173 | } | ||
174 | |||
175 | static int efx_filter_search(struct efx_filter_table *table, | ||
176 | struct efx_filter_spec *spec, u32 key, | ||
177 | bool for_insert, int *depth_required) | ||
178 | { | ||
179 | unsigned hash, incr, filter_idx, depth; | ||
180 | struct efx_filter_spec *cmp; | ||
181 | |||
182 | hash = efx_filter_hash(key); | ||
183 | incr = efx_filter_increment(key); | ||
184 | |||
185 | for (depth = 1, filter_idx = hash & (table->size - 1); | ||
186 | test_bit(filter_idx, table->used_bitmap); | ||
187 | ++depth) { | ||
188 | cmp = &table->spec[filter_idx]; | ||
189 | if (efx_filter_equal(spec, cmp)) | ||
190 | goto found; | ||
191 | filter_idx = (filter_idx + incr) & (table->size - 1); | ||
192 | } | ||
193 | if (!for_insert) | ||
194 | return -ENOENT; | ||
195 | found: | ||
196 | *depth_required = depth; | ||
197 | return filter_idx; | ||
198 | } | ||
199 | |||
200 | /** | ||
201 | * efx_filter_insert_filter - add or replace a filter | ||
202 | * @efx: NIC in which to insert the filter | ||
203 | * @spec: Specification for the filter | ||
204 | * @replace: Flag for whether the specified filter may replace a filter | ||
205 | * with an identical match expression and equal or lower priority | ||
206 | * | ||
207 | * On success, return the filter index within its table. | ||
208 | * On failure, return a negative error code. | ||
209 | */ | ||
210 | int efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec, | ||
211 | bool replace) | ||
212 | { | ||
213 | struct efx_filter_state *state = efx->filter_state; | ||
214 | enum efx_filter_table_id table_id = | ||
215 | efx_filter_type_table_id(spec->type); | ||
216 | struct efx_filter_table *table = &state->table[table_id]; | ||
217 | struct efx_filter_spec *saved_spec; | ||
218 | efx_oword_t filter; | ||
219 | int filter_idx, depth; | ||
220 | u32 key; | ||
221 | int rc; | ||
222 | |||
223 | if (table->size == 0) | ||
224 | return -EINVAL; | ||
225 | |||
226 | key = efx_filter_build(&filter, spec); | ||
227 | |||
228 | netif_vdbg(efx, hw, efx->net_dev, | ||
229 | "%s: type %d search_depth=%d", __func__, spec->type, | ||
230 | state->search_depth[spec->type]); | ||
231 | |||
232 | spin_lock_bh(&state->lock); | ||
233 | |||
234 | rc = efx_filter_search(table, spec, key, true, &depth); | ||
235 | if (rc < 0) | ||
236 | goto out; | ||
237 | filter_idx = rc; | ||
238 | BUG_ON(filter_idx >= table->size); | ||
239 | saved_spec = &table->spec[filter_idx]; | ||
240 | |||
241 | if (test_bit(filter_idx, table->used_bitmap)) { | ||
242 | /* Should we replace the existing filter? */ | ||
243 | if (!replace) { | ||
244 | rc = -EEXIST; | ||
245 | goto out; | ||
246 | } | ||
247 | if (spec->priority < saved_spec->priority) { | ||
248 | rc = -EPERM; | ||
249 | goto out; | ||
250 | } | ||
251 | } else { | ||
252 | __set_bit(filter_idx, table->used_bitmap); | ||
253 | ++table->used; | ||
254 | } | ||
255 | *saved_spec = *spec; | ||
256 | |||
257 | if (state->search_depth[spec->type] < depth) { | ||
258 | state->search_depth[spec->type] = depth; | ||
259 | efx_filter_push_rx_limits(efx); | ||
260 | } | ||
261 | |||
262 | efx_writeo(efx, &filter, table->offset + table->step * filter_idx); | ||
263 | |||
264 | netif_vdbg(efx, hw, efx->net_dev, | ||
265 | "%s: filter type %d index %d rxq %u set", | ||
266 | __func__, spec->type, filter_idx, spec->dmaq_id); | ||
267 | |||
268 | out: | ||
269 | spin_unlock_bh(&state->lock); | ||
270 | return rc; | ||
271 | } | ||
272 | |||
273 | static void efx_filter_table_clear_entry(struct efx_nic *efx, | ||
274 | struct efx_filter_table *table, | ||
275 | int filter_idx) | ||
276 | { | ||
277 | static efx_oword_t filter; | ||
278 | |||
279 | if (test_bit(filter_idx, table->used_bitmap)) { | ||
280 | __clear_bit(filter_idx, table->used_bitmap); | ||
281 | --table->used; | ||
282 | memset(&table->spec[filter_idx], 0, sizeof(table->spec[0])); | ||
283 | |||
284 | efx_writeo(efx, &filter, | ||
285 | table->offset + table->step * filter_idx); | ||
286 | } | ||
287 | } | ||
288 | |||
289 | /** | ||
290 | * efx_filter_remove_filter - remove a filter by specification | ||
291 | * @efx: NIC from which to remove the filter | ||
292 | * @spec: Specification for the filter | ||
293 | * | ||
294 | * On success, return zero. | ||
295 | * On failure, return a negative error code. | ||
296 | */ | ||
297 | int efx_filter_remove_filter(struct efx_nic *efx, struct efx_filter_spec *spec) | ||
298 | { | ||
299 | struct efx_filter_state *state = efx->filter_state; | ||
300 | enum efx_filter_table_id table_id = | ||
301 | efx_filter_type_table_id(spec->type); | ||
302 | struct efx_filter_table *table = &state->table[table_id]; | ||
303 | struct efx_filter_spec *saved_spec; | ||
304 | efx_oword_t filter; | ||
305 | int filter_idx, depth; | ||
306 | u32 key; | ||
307 | int rc; | ||
308 | |||
309 | key = efx_filter_build(&filter, spec); | ||
310 | |||
311 | spin_lock_bh(&state->lock); | ||
312 | |||
313 | rc = efx_filter_search(table, spec, key, false, &depth); | ||
314 | if (rc < 0) | ||
315 | goto out; | ||
316 | filter_idx = rc; | ||
317 | saved_spec = &table->spec[filter_idx]; | ||
318 | |||
319 | if (spec->priority < saved_spec->priority) { | ||
320 | rc = -EPERM; | ||
321 | goto out; | ||
322 | } | ||
323 | |||
324 | efx_filter_table_clear_entry(efx, table, filter_idx); | ||
325 | if (table->used == 0) | ||
326 | efx_filter_table_reset_search_depth(state, table_id); | ||
327 | rc = 0; | ||
328 | |||
329 | out: | ||
330 | spin_unlock_bh(&state->lock); | ||
331 | return rc; | ||
332 | } | ||
333 | |||
334 | /** | ||
335 | * efx_filter_table_clear - remove filters from a table by priority | ||
336 | * @efx: NIC from which to remove the filters | ||
337 | * @table_id: Table from which to remove the filters | ||
338 | * @priority: Maximum priority to remove | ||
339 | */ | ||
340 | void efx_filter_table_clear(struct efx_nic *efx, | ||
341 | enum efx_filter_table_id table_id, | ||
342 | enum efx_filter_priority priority) | ||
343 | { | ||
344 | struct efx_filter_state *state = efx->filter_state; | ||
345 | struct efx_filter_table *table = &state->table[table_id]; | ||
346 | int filter_idx; | ||
347 | |||
348 | spin_lock_bh(&state->lock); | ||
349 | |||
350 | for (filter_idx = 0; filter_idx < table->size; ++filter_idx) | ||
351 | if (table->spec[filter_idx].priority <= priority) | ||
352 | efx_filter_table_clear_entry(efx, table, filter_idx); | ||
353 | if (table->used == 0) | ||
354 | efx_filter_table_reset_search_depth(state, table_id); | ||
355 | |||
356 | spin_unlock_bh(&state->lock); | ||
357 | } | ||
358 | |||
359 | /* Restore filter stater after reset */ | ||
360 | void efx_restore_filters(struct efx_nic *efx) | ||
361 | { | ||
362 | struct efx_filter_state *state = efx->filter_state; | ||
363 | enum efx_filter_table_id table_id; | ||
364 | struct efx_filter_table *table; | ||
365 | efx_oword_t filter; | ||
366 | int filter_idx; | ||
367 | |||
368 | spin_lock_bh(&state->lock); | ||
369 | |||
370 | for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) { | ||
371 | table = &state->table[table_id]; | ||
372 | for (filter_idx = 0; filter_idx < table->size; filter_idx++) { | ||
373 | if (!test_bit(filter_idx, table->used_bitmap)) | ||
374 | continue; | ||
375 | efx_filter_build(&filter, &table->spec[filter_idx]); | ||
376 | efx_writeo(efx, &filter, | ||
377 | table->offset + table->step * filter_idx); | ||
378 | } | ||
379 | } | ||
380 | |||
381 | efx_filter_push_rx_limits(efx); | ||
382 | |||
383 | spin_unlock_bh(&state->lock); | ||
384 | } | ||
385 | |||
386 | int efx_probe_filters(struct efx_nic *efx) | ||
387 | { | ||
388 | struct efx_filter_state *state; | ||
389 | struct efx_filter_table *table; | ||
390 | unsigned table_id; | ||
391 | |||
392 | state = kzalloc(sizeof(*efx->filter_state), GFP_KERNEL); | ||
393 | if (!state) | ||
394 | return -ENOMEM; | ||
395 | efx->filter_state = state; | ||
396 | |||
397 | spin_lock_init(&state->lock); | ||
398 | |||
399 | if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { | ||
400 | table = &state->table[EFX_FILTER_TABLE_RX_IP]; | ||
401 | table->offset = FR_BZ_RX_FILTER_TBL0; | ||
402 | table->size = FR_BZ_RX_FILTER_TBL0_ROWS; | ||
403 | table->step = FR_BZ_RX_FILTER_TBL0_STEP; | ||
404 | } | ||
405 | |||
406 | if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) { | ||
407 | table = &state->table[EFX_FILTER_TABLE_RX_MAC]; | ||
408 | table->offset = FR_CZ_RX_MAC_FILTER_TBL0; | ||
409 | table->size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS; | ||
410 | table->step = FR_CZ_RX_MAC_FILTER_TBL0_STEP; | ||
411 | } | ||
412 | |||
413 | for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) { | ||
414 | table = &state->table[table_id]; | ||
415 | if (table->size == 0) | ||
416 | continue; | ||
417 | table->used_bitmap = kcalloc(BITS_TO_LONGS(table->size), | ||
418 | sizeof(unsigned long), | ||
419 | GFP_KERNEL); | ||
420 | if (!table->used_bitmap) | ||
421 | goto fail; | ||
422 | table->spec = vmalloc(table->size * sizeof(*table->spec)); | ||
423 | if (!table->spec) | ||
424 | goto fail; | ||
425 | memset(table->spec, 0, table->size * sizeof(*table->spec)); | ||
426 | } | ||
427 | |||
428 | return 0; | ||
429 | |||
430 | fail: | ||
431 | efx_remove_filters(efx); | ||
432 | return -ENOMEM; | ||
433 | } | ||
434 | |||
435 | void efx_remove_filters(struct efx_nic *efx) | ||
436 | { | ||
437 | struct efx_filter_state *state = efx->filter_state; | ||
438 | enum efx_filter_table_id table_id; | ||
439 | |||
440 | for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) { | ||
441 | kfree(state->table[table_id].used_bitmap); | ||
442 | vfree(state->table[table_id].spec); | ||
443 | } | ||
444 | kfree(state); | ||
445 | } | ||
diff --git a/drivers/net/sfc/filter.h b/drivers/net/sfc/filter.h new file mode 100644 index 000000000000..a53319ded79c --- /dev/null +++ b/drivers/net/sfc/filter.h | |||
@@ -0,0 +1,189 @@ | |||
1 | /**************************************************************************** | ||
2 | * Driver for Solarflare Solarstorm network controllers and boards | ||
3 | * Copyright 2005-2010 Solarflare Communications Inc. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published | ||
7 | * by the Free Software Foundation, incorporated herein by reference. | ||
8 | */ | ||
9 | |||
10 | #ifndef EFX_FILTER_H | ||
11 | #define EFX_FILTER_H | ||
12 | |||
13 | #include <linux/types.h> | ||
14 | |||
15 | enum efx_filter_table_id { | ||
16 | EFX_FILTER_TABLE_RX_IP = 0, | ||
17 | EFX_FILTER_TABLE_RX_MAC, | ||
18 | EFX_FILTER_TABLE_COUNT, | ||
19 | }; | ||
20 | |||
21 | /** | ||
22 | * enum efx_filter_type - type of hardware filter | ||
23 | * @EFX_FILTER_RX_TCP_FULL: RX, matching TCP/IPv4 4-tuple | ||
24 | * @EFX_FILTER_RX_TCP_WILD: RX, matching TCP/IPv4 destination (host, port) | ||
25 | * @EFX_FILTER_RX_UDP_FULL: RX, matching UDP/IPv4 4-tuple | ||
26 | * @EFX_FILTER_RX_UDP_WILD: RX, matching UDP/IPv4 destination (host, port) | ||
27 | * @EFX_FILTER_RX_MAC_FULL: RX, matching Ethernet destination MAC address, VID | ||
28 | * @EFX_FILTER_RX_MAC_WILD: RX, matching Ethernet destination MAC address | ||
29 | * | ||
30 | * Falcon NICs only support the RX TCP/IPv4 and UDP/IPv4 filter types. | ||
31 | */ | ||
32 | enum efx_filter_type { | ||
33 | EFX_FILTER_RX_TCP_FULL = 0, | ||
34 | EFX_FILTER_RX_TCP_WILD, | ||
35 | EFX_FILTER_RX_UDP_FULL, | ||
36 | EFX_FILTER_RX_UDP_WILD, | ||
37 | EFX_FILTER_RX_MAC_FULL = 4, | ||
38 | EFX_FILTER_RX_MAC_WILD, | ||
39 | EFX_FILTER_TYPE_COUNT, | ||
40 | }; | ||
41 | |||
42 | /** | ||
43 | * enum efx_filter_priority - priority of a hardware filter specification | ||
44 | * @EFX_FILTER_PRI_HINT: Performance hint | ||
45 | * @EFX_FILTER_PRI_MANUAL: Manually configured filter | ||
46 | * @EFX_FILTER_PRI_REQUIRED: Required for correct behaviour | ||
47 | */ | ||
48 | enum efx_filter_priority { | ||
49 | EFX_FILTER_PRI_HINT = 0, | ||
50 | EFX_FILTER_PRI_MANUAL, | ||
51 | EFX_FILTER_PRI_REQUIRED, | ||
52 | }; | ||
53 | |||
54 | /** | ||
55 | * enum efx_filter_flags - flags for hardware filter specifications | ||
56 | * @EFX_FILTER_FLAG_RX_RSS: Use RSS to spread across multiple queues. | ||
57 | * By default, matching packets will be delivered only to the | ||
58 | * specified queue. If this flag is set, they will be delivered | ||
59 | * to a range of queues offset from the specified queue number | ||
60 | * according to the indirection table. | ||
61 | * @EFX_FILTER_FLAG_RX_SCATTER: Enable DMA scatter on the receiving | ||
62 | * queue. | ||
63 | * @EFX_FILTER_FLAG_RX_OVERRIDE_IP: Enables a MAC filter to override | ||
64 | * any IP filter that matches the same packet. By default, IP | ||
65 | * filters take precedence. | ||
66 | * | ||
67 | * Currently, no flags are defined for TX filters. | ||
68 | */ | ||
69 | enum efx_filter_flags { | ||
70 | EFX_FILTER_FLAG_RX_RSS = 0x01, | ||
71 | EFX_FILTER_FLAG_RX_SCATTER = 0x02, | ||
72 | EFX_FILTER_FLAG_RX_OVERRIDE_IP = 0x04, | ||
73 | }; | ||
74 | |||
75 | /** | ||
76 | * struct efx_filter_spec - specification for a hardware filter | ||
77 | * @type: Type of match to be performed, from &enum efx_filter_type | ||
78 | * @priority: Priority of the filter, from &enum efx_filter_priority | ||
79 | * @flags: Miscellaneous flags, from &enum efx_filter_flags | ||
80 | * @dmaq_id: Source/target queue index | ||
81 | * @data: Match data (type-dependent) | ||
82 | * | ||
83 | * Use the efx_filter_set_*() functions to initialise the @type and | ||
84 | * @data fields. | ||
85 | */ | ||
86 | struct efx_filter_spec { | ||
87 | u8 type:4; | ||
88 | u8 priority:4; | ||
89 | u8 flags; | ||
90 | u16 dmaq_id; | ||
91 | u32 data[3]; | ||
92 | }; | ||
93 | |||
94 | /** | ||
95 | * efx_filter_set_rx_tcp_full - specify RX filter with TCP/IPv4 full match | ||
96 | * @spec: Specification to initialise | ||
97 | * @shost: Source host address (host byte order) | ||
98 | * @sport: Source port (host byte order) | ||
99 | * @dhost: Destination host address (host byte order) | ||
100 | * @dport: Destination port (host byte order) | ||
101 | */ | ||
102 | static inline void | ||
103 | efx_filter_set_rx_tcp_full(struct efx_filter_spec *spec, | ||
104 | u32 shost, u16 sport, u32 dhost, u16 dport) | ||
105 | { | ||
106 | spec->type = EFX_FILTER_RX_TCP_FULL; | ||
107 | spec->data[0] = sport | shost << 16; | ||
108 | spec->data[1] = dport << 16 | shost >> 16; | ||
109 | spec->data[2] = dhost; | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * efx_filter_set_rx_tcp_wild - specify RX filter with TCP/IPv4 wildcard match | ||
114 | * @spec: Specification to initialise | ||
115 | * @dhost: Destination host address (host byte order) | ||
116 | * @dport: Destination port (host byte order) | ||
117 | */ | ||
118 | static inline void | ||
119 | efx_filter_set_rx_tcp_wild(struct efx_filter_spec *spec, u32 dhost, u16 dport) | ||
120 | { | ||
121 | spec->type = EFX_FILTER_RX_TCP_WILD; | ||
122 | spec->data[0] = 0; | ||
123 | spec->data[1] = dport << 16; | ||
124 | spec->data[2] = dhost; | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * efx_filter_set_rx_udp_full - specify RX filter with UDP/IPv4 full match | ||
129 | * @spec: Specification to initialise | ||
130 | * @shost: Source host address (host byte order) | ||
131 | * @sport: Source port (host byte order) | ||
132 | * @dhost: Destination host address (host byte order) | ||
133 | * @dport: Destination port (host byte order) | ||
134 | */ | ||
135 | static inline void | ||
136 | efx_filter_set_rx_udp_full(struct efx_filter_spec *spec, | ||
137 | u32 shost, u16 sport, u32 dhost, u16 dport) | ||
138 | { | ||
139 | spec->type = EFX_FILTER_RX_UDP_FULL; | ||
140 | spec->data[0] = sport | shost << 16; | ||
141 | spec->data[1] = dport << 16 | shost >> 16; | ||
142 | spec->data[2] = dhost; | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * efx_filter_set_rx_udp_wild - specify RX filter with UDP/IPv4 wildcard match | ||
147 | * @spec: Specification to initialise | ||
148 | * @dhost: Destination host address (host byte order) | ||
149 | * @dport: Destination port (host byte order) | ||
150 | */ | ||
151 | static inline void | ||
152 | efx_filter_set_rx_udp_wild(struct efx_filter_spec *spec, u32 dhost, u16 dport) | ||
153 | { | ||
154 | spec->type = EFX_FILTER_RX_UDP_WILD; | ||
155 | spec->data[0] = dport; | ||
156 | spec->data[1] = 0; | ||
157 | spec->data[2] = dhost; | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * efx_filter_set_rx_mac_full - specify RX filter with MAC full match | ||
162 | * @spec: Specification to initialise | ||
163 | * @vid: VLAN ID | ||
164 | * @addr: Destination MAC address | ||
165 | */ | ||
166 | static inline void efx_filter_set_rx_mac_full(struct efx_filter_spec *spec, | ||
167 | u16 vid, const u8 *addr) | ||
168 | { | ||
169 | spec->type = EFX_FILTER_RX_MAC_FULL; | ||
170 | spec->data[0] = vid; | ||
171 | spec->data[1] = addr[2] << 24 | addr[3] << 16 | addr[4] << 8 | addr[5]; | ||
172 | spec->data[2] = addr[0] << 8 | addr[1]; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * efx_filter_set_rx_mac_full - specify RX filter with MAC wildcard match | ||
177 | * @spec: Specification to initialise | ||
178 | * @addr: Destination MAC address | ||
179 | */ | ||
180 | static inline void efx_filter_set_rx_mac_wild(struct efx_filter_spec *spec, | ||
181 | const u8 *addr) | ||
182 | { | ||
183 | spec->type = EFX_FILTER_RX_MAC_WILD; | ||
184 | spec->data[0] = 0; | ||
185 | spec->data[1] = addr[2] << 24 | addr[3] << 16 | addr[4] << 8 | addr[5]; | ||
186 | spec->data[2] = addr[0] << 8 | addr[1]; | ||
187 | } | ||
188 | |||
189 | #endif /* EFX_FILTER_H */ | ||
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 152342dbff29..ea6691c83fba 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h | |||
@@ -619,6 +619,8 @@ union efx_multicast_hash { | |||
619 | efx_oword_t oword[EFX_MCAST_HASH_ENTRIES / sizeof(efx_oword_t) / 8]; | 619 | efx_oword_t oword[EFX_MCAST_HASH_ENTRIES / sizeof(efx_oword_t) / 8]; |
620 | }; | 620 | }; |
621 | 621 | ||
622 | struct efx_filter_state; | ||
623 | |||
622 | /** | 624 | /** |
623 | * struct efx_nic - an Efx NIC | 625 | * struct efx_nic - an Efx NIC |
624 | * @name: Device name (net device name or bus id before net device registered) | 626 | * @name: Device name (net device name or bus id before net device registered) |
@@ -799,6 +801,8 @@ struct efx_nic { | |||
799 | u64 loopback_modes; | 801 | u64 loopback_modes; |
800 | 802 | ||
801 | void *loopback_selftest; | 803 | void *loopback_selftest; |
804 | |||
805 | struct efx_filter_state *filter_state; | ||
802 | }; | 806 | }; |
803 | 807 | ||
804 | static inline int efx_dev_registered(struct efx_nic *efx) | 808 | static inline int efx_dev_registered(struct efx_nic *efx) |
diff --git a/drivers/net/sfc/regs.h b/drivers/net/sfc/regs.h index 18a3be428348..96430ed81c36 100644 --- a/drivers/net/sfc/regs.h +++ b/drivers/net/sfc/regs.h | |||
@@ -2893,6 +2893,20 @@ | |||
2893 | #define FRF_AB_XX_FORCE_SIG_WIDTH 8 | 2893 | #define FRF_AB_XX_FORCE_SIG_WIDTH 8 |
2894 | #define FFE_AB_XX_FORCE_SIG_ALL_LANES 0xff | 2894 | #define FFE_AB_XX_FORCE_SIG_ALL_LANES 0xff |
2895 | 2895 | ||
2896 | /* RX_MAC_FILTER_TBL0 */ | ||
2897 | /* RMFT_DEST_MAC is wider than 32 bits */ | ||
2898 | #define FRF_CZ_RMFT_DEST_MAC_LO_LBN 12 | ||
2899 | #define FRF_CZ_RMFT_DEST_MAC_LO_WIDTH 32 | ||
2900 | #define FRF_CZ_RMFT_DEST_MAC_HI_LBN 44 | ||
2901 | #define FRF_CZ_RMFT_DEST_MAC_HI_WIDTH 16 | ||
2902 | |||
2903 | /* TX_MAC_FILTER_TBL0 */ | ||
2904 | /* TMFT_SRC_MAC is wider than 32 bits */ | ||
2905 | #define FRF_CZ_TMFT_SRC_MAC_LO_LBN 12 | ||
2906 | #define FRF_CZ_TMFT_SRC_MAC_LO_WIDTH 32 | ||
2907 | #define FRF_CZ_TMFT_SRC_MAC_HI_LBN 44 | ||
2908 | #define FRF_CZ_TMFT_SRC_MAC_HI_WIDTH 16 | ||
2909 | |||
2896 | /* DRIVER_EV */ | 2910 | /* DRIVER_EV */ |
2897 | /* Sub-fields of an RX flush completion event */ | 2911 | /* Sub-fields of an RX flush completion event */ |
2898 | #define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_LBN 12 | 2912 | #define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_LBN 12 |