diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2011-06-24 15:26:44 -0400 |
---|---|---|
committer | Ben Hutchings <bhutchings@solarflare.com> | 2011-06-24 19:43:48 -0400 |
commit | 4017dbdc14af1903dc9fcba4d08b89c02325069d (patch) | |
tree | 33c61638e1fb98ebf27f710e99f443aed9c2f738 /drivers/net/sfc | |
parent | 22c8c9343258feda9ea9ebb9e5f8cbb727b69454 (diff) |
sfc: Fix loop condition for efx_filter_search() when !for_insert
efx_filter_remove_filter() fails to remove inserted filters in some cases.
For example:
1. Two filters A and B have specifications that result in an initial
hash collision.
2. A is inserted first, followed by B.
3. An attempt to remove B first succeeds, but if A is removed first
a subsequent attempt to remove B fails.
When searching for an existing filter (!for_insert),
efx_filter_search() must always continue to the maximum search depth
for the given type rather than stopping at the first unused entry.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Diffstat (limited to 'drivers/net/sfc')
-rw-r--r-- | drivers/net/sfc/filter.c | 41 |
1 files changed, 24 insertions, 17 deletions
diff --git a/drivers/net/sfc/filter.c b/drivers/net/sfc/filter.c index f2fc2587c09..054f0a3d45a 100644 --- a/drivers/net/sfc/filter.c +++ b/drivers/net/sfc/filter.c | |||
@@ -335,28 +335,35 @@ static int efx_filter_search(struct efx_filter_table *table, | |||
335 | bool for_insert, int *depth_required) | 335 | bool for_insert, int *depth_required) |
336 | { | 336 | { |
337 | unsigned hash, incr, filter_idx, depth, depth_max; | 337 | unsigned hash, incr, filter_idx, depth, depth_max; |
338 | struct efx_filter_spec *cmp; | ||
339 | 338 | ||
340 | hash = efx_filter_hash(key); | 339 | hash = efx_filter_hash(key); |
341 | incr = efx_filter_increment(key); | 340 | incr = efx_filter_increment(key); |
342 | depth_max = (spec->priority <= EFX_FILTER_PRI_HINT ? | 341 | |
343 | FILTER_CTL_SRCH_HINT_MAX : FILTER_CTL_SRCH_MAX); | 342 | filter_idx = hash & (table->size - 1); |
344 | 343 | depth = 1; | |
345 | for (depth = 1, filter_idx = hash & (table->size - 1); | 344 | depth_max = (for_insert ? |
346 | depth <= depth_max && test_bit(filter_idx, table->used_bitmap); | 345 | (spec->priority <= EFX_FILTER_PRI_HINT ? |
347 | ++depth) { | 346 | FILTER_CTL_SRCH_HINT_MAX : FILTER_CTL_SRCH_MAX) : |
348 | cmp = &table->spec[filter_idx]; | 347 | table->search_depth[spec->type]); |
349 | if (efx_filter_equal(spec, cmp)) | 348 | |
350 | goto found; | 349 | for (;;) { |
350 | /* Return success if entry is used and matches this spec | ||
351 | * or entry is unused and we are trying to insert. | ||
352 | */ | ||
353 | if (test_bit(filter_idx, table->used_bitmap) ? | ||
354 | efx_filter_equal(spec, &table->spec[filter_idx]) : | ||
355 | for_insert) { | ||
356 | *depth_required = depth; | ||
357 | return filter_idx; | ||
358 | } | ||
359 | |||
360 | /* Return failure if we reached the maximum search depth */ | ||
361 | if (depth == depth_max) | ||
362 | return for_insert ? -EBUSY : -ENOENT; | ||
363 | |||
351 | filter_idx = (filter_idx + incr) & (table->size - 1); | 364 | filter_idx = (filter_idx + incr) & (table->size - 1); |
365 | ++depth; | ||
352 | } | 366 | } |
353 | if (!for_insert) | ||
354 | return -ENOENT; | ||
355 | if (depth > depth_max) | ||
356 | return -EBUSY; | ||
357 | found: | ||
358 | *depth_required = depth; | ||
359 | return filter_idx; | ||
360 | } | 367 | } |
361 | 368 | ||
362 | /* Construct/deconstruct external filter IDs */ | 369 | /* Construct/deconstruct external filter IDs */ |