Commit | Line | Data |
---|---|---|
64eebcfd BH |
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 | ||
993284df BH |
23 | /* Hard maximum hop limit. Hardware will time-out beyond 200-something. |
24 | * We also need to avoid infinite loops in efx_filter_search() when the | |
25 | * table is full. | |
26 | */ | |
27 | #define FILTER_CTL_SRCH_MAX 200 | |
28 | ||
64eebcfd BH |
29 | struct efx_filter_table { |
30 | u32 offset; /* address of table relative to BAR */ | |
31 | unsigned size; /* number of entries */ | |
32 | unsigned step; /* step between entries */ | |
33 | unsigned used; /* number currently used */ | |
34 | unsigned long *used_bitmap; | |
35 | struct efx_filter_spec *spec; | |
36 | }; | |
37 | ||
38 | struct efx_filter_state { | |
39 | spinlock_t lock; | |
40 | struct efx_filter_table table[EFX_FILTER_TABLE_COUNT]; | |
41 | unsigned search_depth[EFX_FILTER_TYPE_COUNT]; | |
42 | }; | |
43 | ||
44 | /* The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit | |
45 | * key derived from the n-tuple. The initial LFSR state is 0xffff. */ | |
46 | static u16 efx_filter_hash(u32 key) | |
47 | { | |
48 | u16 tmp; | |
49 | ||
50 | /* First 16 rounds */ | |
51 | tmp = 0x1fff ^ key >> 16; | |
52 | tmp = tmp ^ tmp >> 3 ^ tmp >> 6; | |
53 | tmp = tmp ^ tmp >> 9; | |
54 | /* Last 16 rounds */ | |
55 | tmp = tmp ^ tmp << 13 ^ key; | |
56 | tmp = tmp ^ tmp >> 3 ^ tmp >> 6; | |
57 | return tmp ^ tmp >> 9; | |
58 | } | |
59 | ||
60 | /* To allow for hash collisions, filter search continues at these | |
61 | * increments from the first possible entry selected by the hash. */ | |
62 | static u16 efx_filter_increment(u32 key) | |
63 | { | |
64 | return key * 2 - 1; | |
65 | } | |
66 | ||
67 | static enum efx_filter_table_id | |
68 | efx_filter_type_table_id(enum efx_filter_type type) | |
69 | { | |
70 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_TCP_FULL >> 2)); | |
71 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_TCP_WILD >> 2)); | |
72 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_UDP_FULL >> 2)); | |
73 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_UDP_WILD >> 2)); | |
74 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_RX_MAC_FULL >> 2)); | |
75 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_RX_MAC_WILD >> 2)); | |
76 | return type >> 2; | |
77 | } | |
78 | ||
79 | static void | |
80 | efx_filter_table_reset_search_depth(struct efx_filter_state *state, | |
81 | enum efx_filter_table_id table_id) | |
82 | { | |
83 | memset(state->search_depth + (table_id << 2), 0, | |
84 | sizeof(state->search_depth[0]) << 2); | |
85 | } | |
86 | ||
87 | static void efx_filter_push_rx_limits(struct efx_nic *efx) | |
88 | { | |
89 | struct efx_filter_state *state = efx->filter_state; | |
90 | efx_oword_t filter_ctl; | |
91 | ||
92 | efx_reado(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL); | |
93 | ||
94 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_FULL_SRCH_LIMIT, | |
95 | state->search_depth[EFX_FILTER_RX_TCP_FULL] + | |
96 | FILTER_CTL_SRCH_FUDGE_FULL); | |
97 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_WILD_SRCH_LIMIT, | |
98 | state->search_depth[EFX_FILTER_RX_TCP_WILD] + | |
99 | FILTER_CTL_SRCH_FUDGE_WILD); | |
100 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_FULL_SRCH_LIMIT, | |
101 | state->search_depth[EFX_FILTER_RX_UDP_FULL] + | |
102 | FILTER_CTL_SRCH_FUDGE_FULL); | |
103 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_WILD_SRCH_LIMIT, | |
104 | state->search_depth[EFX_FILTER_RX_UDP_WILD] + | |
105 | FILTER_CTL_SRCH_FUDGE_WILD); | |
106 | ||
107 | if (state->table[EFX_FILTER_TABLE_RX_MAC].size) { | |
108 | EFX_SET_OWORD_FIELD( | |
109 | filter_ctl, FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT, | |
110 | state->search_depth[EFX_FILTER_RX_MAC_FULL] + | |
111 | FILTER_CTL_SRCH_FUDGE_FULL); | |
112 | EFX_SET_OWORD_FIELD( | |
113 | filter_ctl, FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT, | |
114 | state->search_depth[EFX_FILTER_RX_MAC_WILD] + | |
115 | FILTER_CTL_SRCH_FUDGE_WILD); | |
116 | } | |
117 | ||
118 | efx_writeo(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL); | |
119 | } | |
120 | ||
121 | /* Build a filter entry and return its n-tuple key. */ | |
122 | static u32 efx_filter_build(efx_oword_t *filter, struct efx_filter_spec *spec) | |
123 | { | |
124 | u32 data3; | |
125 | ||
126 | switch (efx_filter_type_table_id(spec->type)) { | |
127 | case EFX_FILTER_TABLE_RX_IP: { | |
128 | bool is_udp = (spec->type == EFX_FILTER_RX_UDP_FULL || | |
129 | spec->type == EFX_FILTER_RX_UDP_WILD); | |
130 | EFX_POPULATE_OWORD_7( | |
131 | *filter, | |
132 | FRF_BZ_RSS_EN, | |
133 | !!(spec->flags & EFX_FILTER_FLAG_RX_RSS), | |
134 | FRF_BZ_SCATTER_EN, | |
135 | !!(spec->flags & EFX_FILTER_FLAG_RX_SCATTER), | |
136 | FRF_BZ_TCP_UDP, is_udp, | |
137 | FRF_BZ_RXQ_ID, spec->dmaq_id, | |
138 | EFX_DWORD_2, spec->data[2], | |
139 | EFX_DWORD_1, spec->data[1], | |
140 | EFX_DWORD_0, spec->data[0]); | |
141 | data3 = is_udp; | |
142 | break; | |
143 | } | |
144 | ||
145 | case EFX_FILTER_TABLE_RX_MAC: { | |
146 | bool is_wild = spec->type == EFX_FILTER_RX_MAC_WILD; | |
147 | EFX_POPULATE_OWORD_8( | |
148 | *filter, | |
149 | FRF_CZ_RMFT_RSS_EN, | |
150 | !!(spec->flags & EFX_FILTER_FLAG_RX_RSS), | |
151 | FRF_CZ_RMFT_SCATTER_EN, | |
152 | !!(spec->flags & EFX_FILTER_FLAG_RX_SCATTER), | |
153 | FRF_CZ_RMFT_IP_OVERRIDE, | |
154 | !!(spec->flags & EFX_FILTER_FLAG_RX_OVERRIDE_IP), | |
155 | FRF_CZ_RMFT_RXQ_ID, spec->dmaq_id, | |
156 | FRF_CZ_RMFT_WILDCARD_MATCH, is_wild, | |
157 | FRF_CZ_RMFT_DEST_MAC_HI, spec->data[2], | |
158 | FRF_CZ_RMFT_DEST_MAC_LO, spec->data[1], | |
159 | FRF_CZ_RMFT_VLAN_ID, spec->data[0]); | |
160 | data3 = is_wild; | |
161 | break; | |
162 | } | |
163 | ||
164 | default: | |
165 | BUG(); | |
166 | } | |
167 | ||
168 | return spec->data[0] ^ spec->data[1] ^ spec->data[2] ^ data3; | |
169 | } | |
170 | ||
171 | static bool efx_filter_equal(const struct efx_filter_spec *left, | |
172 | const struct efx_filter_spec *right) | |
173 | { | |
174 | if (left->type != right->type || | |
175 | memcmp(left->data, right->data, sizeof(left->data))) | |
176 | return false; | |
177 | ||
178 | return true; | |
179 | } | |
180 | ||
181 | static int efx_filter_search(struct efx_filter_table *table, | |
182 | struct efx_filter_spec *spec, u32 key, | |
183 | bool for_insert, int *depth_required) | |
184 | { | |
185 | unsigned hash, incr, filter_idx, depth; | |
186 | struct efx_filter_spec *cmp; | |
187 | ||
188 | hash = efx_filter_hash(key); | |
189 | incr = efx_filter_increment(key); | |
190 | ||
191 | for (depth = 1, filter_idx = hash & (table->size - 1); | |
993284df BH |
192 | depth <= FILTER_CTL_SRCH_MAX && |
193 | test_bit(filter_idx, table->used_bitmap); | |
64eebcfd BH |
194 | ++depth) { |
195 | cmp = &table->spec[filter_idx]; | |
196 | if (efx_filter_equal(spec, cmp)) | |
197 | goto found; | |
198 | filter_idx = (filter_idx + incr) & (table->size - 1); | |
199 | } | |
200 | if (!for_insert) | |
201 | return -ENOENT; | |
993284df BH |
202 | if (depth > FILTER_CTL_SRCH_MAX) |
203 | return -EBUSY; | |
64eebcfd BH |
204 | found: |
205 | *depth_required = depth; | |
206 | return filter_idx; | |
207 | } | |
208 | ||
209 | /** | |
210 | * efx_filter_insert_filter - add or replace a filter | |
211 | * @efx: NIC in which to insert the filter | |
212 | * @spec: Specification for the filter | |
213 | * @replace: Flag for whether the specified filter may replace a filter | |
214 | * with an identical match expression and equal or lower priority | |
215 | * | |
216 | * On success, return the filter index within its table. | |
217 | * On failure, return a negative error code. | |
218 | */ | |
219 | int efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec, | |
220 | bool replace) | |
221 | { | |
222 | struct efx_filter_state *state = efx->filter_state; | |
223 | enum efx_filter_table_id table_id = | |
224 | efx_filter_type_table_id(spec->type); | |
225 | struct efx_filter_table *table = &state->table[table_id]; | |
226 | struct efx_filter_spec *saved_spec; | |
227 | efx_oword_t filter; | |
228 | int filter_idx, depth; | |
229 | u32 key; | |
230 | int rc; | |
231 | ||
232 | if (table->size == 0) | |
233 | return -EINVAL; | |
234 | ||
235 | key = efx_filter_build(&filter, spec); | |
236 | ||
237 | netif_vdbg(efx, hw, efx->net_dev, | |
238 | "%s: type %d search_depth=%d", __func__, spec->type, | |
239 | state->search_depth[spec->type]); | |
240 | ||
241 | spin_lock_bh(&state->lock); | |
242 | ||
243 | rc = efx_filter_search(table, spec, key, true, &depth); | |
244 | if (rc < 0) | |
245 | goto out; | |
246 | filter_idx = rc; | |
247 | BUG_ON(filter_idx >= table->size); | |
248 | saved_spec = &table->spec[filter_idx]; | |
249 | ||
250 | if (test_bit(filter_idx, table->used_bitmap)) { | |
251 | /* Should we replace the existing filter? */ | |
252 | if (!replace) { | |
253 | rc = -EEXIST; | |
254 | goto out; | |
255 | } | |
256 | if (spec->priority < saved_spec->priority) { | |
257 | rc = -EPERM; | |
258 | goto out; | |
259 | } | |
260 | } else { | |
261 | __set_bit(filter_idx, table->used_bitmap); | |
262 | ++table->used; | |
263 | } | |
264 | *saved_spec = *spec; | |
265 | ||
266 | if (state->search_depth[spec->type] < depth) { | |
267 | state->search_depth[spec->type] = depth; | |
268 | efx_filter_push_rx_limits(efx); | |
269 | } | |
270 | ||
271 | efx_writeo(efx, &filter, table->offset + table->step * filter_idx); | |
272 | ||
273 | netif_vdbg(efx, hw, efx->net_dev, | |
274 | "%s: filter type %d index %d rxq %u set", | |
275 | __func__, spec->type, filter_idx, spec->dmaq_id); | |
276 | ||
277 | out: | |
278 | spin_unlock_bh(&state->lock); | |
279 | return rc; | |
280 | } | |
281 | ||
282 | static void efx_filter_table_clear_entry(struct efx_nic *efx, | |
283 | struct efx_filter_table *table, | |
284 | int filter_idx) | |
285 | { | |
286 | static efx_oword_t filter; | |
287 | ||
288 | if (test_bit(filter_idx, table->used_bitmap)) { | |
289 | __clear_bit(filter_idx, table->used_bitmap); | |
290 | --table->used; | |
291 | memset(&table->spec[filter_idx], 0, sizeof(table->spec[0])); | |
292 | ||
293 | efx_writeo(efx, &filter, | |
294 | table->offset + table->step * filter_idx); | |
295 | } | |
296 | } | |
297 | ||
298 | /** | |
299 | * efx_filter_remove_filter - remove a filter by specification | |
300 | * @efx: NIC from which to remove the filter | |
301 | * @spec: Specification for the filter | |
302 | * | |
303 | * On success, return zero. | |
304 | * On failure, return a negative error code. | |
305 | */ | |
306 | int efx_filter_remove_filter(struct efx_nic *efx, struct efx_filter_spec *spec) | |
307 | { | |
308 | struct efx_filter_state *state = efx->filter_state; | |
309 | enum efx_filter_table_id table_id = | |
310 | efx_filter_type_table_id(spec->type); | |
311 | struct efx_filter_table *table = &state->table[table_id]; | |
312 | struct efx_filter_spec *saved_spec; | |
313 | efx_oword_t filter; | |
314 | int filter_idx, depth; | |
315 | u32 key; | |
316 | int rc; | |
317 | ||
318 | key = efx_filter_build(&filter, spec); | |
319 | ||
320 | spin_lock_bh(&state->lock); | |
321 | ||
322 | rc = efx_filter_search(table, spec, key, false, &depth); | |
323 | if (rc < 0) | |
324 | goto out; | |
325 | filter_idx = rc; | |
326 | saved_spec = &table->spec[filter_idx]; | |
327 | ||
328 | if (spec->priority < saved_spec->priority) { | |
329 | rc = -EPERM; | |
330 | goto out; | |
331 | } | |
332 | ||
333 | efx_filter_table_clear_entry(efx, table, filter_idx); | |
334 | if (table->used == 0) | |
335 | efx_filter_table_reset_search_depth(state, table_id); | |
336 | rc = 0; | |
337 | ||
338 | out: | |
339 | spin_unlock_bh(&state->lock); | |
340 | return rc; | |
341 | } | |
342 | ||
343 | /** | |
344 | * efx_filter_table_clear - remove filters from a table by priority | |
345 | * @efx: NIC from which to remove the filters | |
346 | * @table_id: Table from which to remove the filters | |
347 | * @priority: Maximum priority to remove | |
348 | */ | |
349 | void efx_filter_table_clear(struct efx_nic *efx, | |
350 | enum efx_filter_table_id table_id, | |
351 | enum efx_filter_priority priority) | |
352 | { | |
353 | struct efx_filter_state *state = efx->filter_state; | |
354 | struct efx_filter_table *table = &state->table[table_id]; | |
355 | int filter_idx; | |
356 | ||
357 | spin_lock_bh(&state->lock); | |
358 | ||
359 | for (filter_idx = 0; filter_idx < table->size; ++filter_idx) | |
360 | if (table->spec[filter_idx].priority <= priority) | |
361 | efx_filter_table_clear_entry(efx, table, filter_idx); | |
362 | if (table->used == 0) | |
363 | efx_filter_table_reset_search_depth(state, table_id); | |
364 | ||
365 | spin_unlock_bh(&state->lock); | |
366 | } | |
367 | ||
368 | /* Restore filter stater after reset */ | |
369 | void efx_restore_filters(struct efx_nic *efx) | |
370 | { | |
371 | struct efx_filter_state *state = efx->filter_state; | |
372 | enum efx_filter_table_id table_id; | |
373 | struct efx_filter_table *table; | |
374 | efx_oword_t filter; | |
375 | int filter_idx; | |
376 | ||
377 | spin_lock_bh(&state->lock); | |
378 | ||
379 | for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) { | |
380 | table = &state->table[table_id]; | |
381 | for (filter_idx = 0; filter_idx < table->size; filter_idx++) { | |
382 | if (!test_bit(filter_idx, table->used_bitmap)) | |
383 | continue; | |
384 | efx_filter_build(&filter, &table->spec[filter_idx]); | |
385 | efx_writeo(efx, &filter, | |
386 | table->offset + table->step * filter_idx); | |
387 | } | |
388 | } | |
389 | ||
390 | efx_filter_push_rx_limits(efx); | |
391 | ||
392 | spin_unlock_bh(&state->lock); | |
393 | } | |
394 | ||
395 | int efx_probe_filters(struct efx_nic *efx) | |
396 | { | |
397 | struct efx_filter_state *state; | |
398 | struct efx_filter_table *table; | |
399 | unsigned table_id; | |
400 | ||
401 | state = kzalloc(sizeof(*efx->filter_state), GFP_KERNEL); | |
402 | if (!state) | |
403 | return -ENOMEM; | |
404 | efx->filter_state = state; | |
405 | ||
406 | spin_lock_init(&state->lock); | |
407 | ||
408 | if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { | |
409 | table = &state->table[EFX_FILTER_TABLE_RX_IP]; | |
410 | table->offset = FR_BZ_RX_FILTER_TBL0; | |
411 | table->size = FR_BZ_RX_FILTER_TBL0_ROWS; | |
412 | table->step = FR_BZ_RX_FILTER_TBL0_STEP; | |
413 | } | |
414 | ||
415 | if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) { | |
416 | table = &state->table[EFX_FILTER_TABLE_RX_MAC]; | |
417 | table->offset = FR_CZ_RX_MAC_FILTER_TBL0; | |
418 | table->size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS; | |
419 | table->step = FR_CZ_RX_MAC_FILTER_TBL0_STEP; | |
420 | } | |
421 | ||
422 | for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) { | |
423 | table = &state->table[table_id]; | |
424 | if (table->size == 0) | |
425 | continue; | |
426 | table->used_bitmap = kcalloc(BITS_TO_LONGS(table->size), | |
427 | sizeof(unsigned long), | |
428 | GFP_KERNEL); | |
429 | if (!table->used_bitmap) | |
430 | goto fail; | |
431 | table->spec = vmalloc(table->size * sizeof(*table->spec)); | |
432 | if (!table->spec) | |
433 | goto fail; | |
434 | memset(table->spec, 0, table->size * sizeof(*table->spec)); | |
435 | } | |
436 | ||
437 | return 0; | |
438 | ||
439 | fail: | |
440 | efx_remove_filters(efx); | |
441 | return -ENOMEM; | |
442 | } | |
443 | ||
444 | void efx_remove_filters(struct efx_nic *efx) | |
445 | { | |
446 | struct efx_filter_state *state = efx->filter_state; | |
447 | enum efx_filter_table_id table_id; | |
448 | ||
449 | for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) { | |
450 | kfree(state->table[table_id].used_bitmap); | |
451 | vfree(state->table[table_id].spec); | |
452 | } | |
453 | kfree(state); | |
454 | } |