diff options
author | Edward Cree <ecree@solarflare.com> | 2018-03-27 12:42:28 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-03-27 13:33:19 -0400 |
commit | c2bebe37c6b686817f795b6b63599ed4472775fa (patch) | |
tree | 6609695bdc12aa7d21fd708dd922ba8c41c23858 | |
parent | 3af0f34290f6192756ee1d9c2d5fe27222267035 (diff) |
sfc: give ef10 its own rwsem in the filter table instead of filter_lock
efx->filter_lock remains in place for use on farch, but EF10 now ignores it.
EFX_EF10_FILTER_FLAG_BUSY is no longer needed, hence it is removed.
Signed-off-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/sfc/ef10.c | 361 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/efx.c | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/net_driver.h | 2 |
3 files changed, 151 insertions, 213 deletions
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index bcbaba330fb5..9db1b9144e70 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c | |||
@@ -96,17 +96,15 @@ struct efx_ef10_filter_table { | |||
96 | MC_CMD_GET_PARSER_DISP_INFO_OUT_SUPPORTED_MATCHES_MAXNUM * 2]; | 96 | MC_CMD_GET_PARSER_DISP_INFO_OUT_SUPPORTED_MATCHES_MAXNUM * 2]; |
97 | unsigned int rx_match_count; | 97 | unsigned int rx_match_count; |
98 | 98 | ||
99 | struct rw_semaphore lock; /* Protects entries */ | ||
99 | struct { | 100 | struct { |
100 | unsigned long spec; /* pointer to spec plus flag bits */ | 101 | unsigned long spec; /* pointer to spec plus flag bits */ |
101 | /* BUSY flag indicates that an update is in progress. AUTO_OLD is | 102 | /* AUTO_OLD is used to mark and sweep MAC filters for the device address lists. */ |
102 | * used to mark and sweep MAC filters for the device address lists. | 103 | /* unused flag 1UL */ |
103 | */ | ||
104 | #define EFX_EF10_FILTER_FLAG_BUSY 1UL | ||
105 | #define EFX_EF10_FILTER_FLAG_AUTO_OLD 2UL | 104 | #define EFX_EF10_FILTER_FLAG_AUTO_OLD 2UL |
106 | #define EFX_EF10_FILTER_FLAGS 3UL | 105 | #define EFX_EF10_FILTER_FLAGS 3UL |
107 | u64 handle; /* firmware handle */ | 106 | u64 handle; /* firmware handle */ |
108 | } *entry; | 107 | } *entry; |
109 | wait_queue_head_t waitq; | ||
110 | /* Shadow of net_device address lists, guarded by mac_lock */ | 108 | /* Shadow of net_device address lists, guarded by mac_lock */ |
111 | struct efx_ef10_dev_addr dev_uc_list[EFX_EF10_FILTER_DEV_UC_MAX]; | 109 | struct efx_ef10_dev_addr dev_uc_list[EFX_EF10_FILTER_DEV_UC_MAX]; |
112 | struct efx_ef10_dev_addr dev_mc_list[EFX_EF10_FILTER_DEV_MC_MAX]; | 110 | struct efx_ef10_dev_addr dev_mc_list[EFX_EF10_FILTER_DEV_MC_MAX]; |
@@ -4302,26 +4300,33 @@ static s32 efx_ef10_filter_insert(struct efx_nic *efx, | |||
4302 | struct efx_filter_spec *spec, | 4300 | struct efx_filter_spec *spec, |
4303 | bool replace_equal) | 4301 | bool replace_equal) |
4304 | { | 4302 | { |
4305 | struct efx_ef10_filter_table *table = efx->filter_state; | ||
4306 | DECLARE_BITMAP(mc_rem_map, EFX_EF10_FILTER_SEARCH_LIMIT); | 4303 | DECLARE_BITMAP(mc_rem_map, EFX_EF10_FILTER_SEARCH_LIMIT); |
4304 | struct efx_ef10_filter_table *table; | ||
4307 | struct efx_filter_spec *saved_spec; | 4305 | struct efx_filter_spec *saved_spec; |
4308 | struct efx_rss_context *ctx = NULL; | 4306 | struct efx_rss_context *ctx = NULL; |
4309 | unsigned int match_pri, hash; | 4307 | unsigned int match_pri, hash; |
4310 | unsigned int priv_flags; | 4308 | unsigned int priv_flags; |
4311 | bool replacing = false; | 4309 | bool replacing = false; |
4310 | unsigned int depth, i; | ||
4312 | int ins_index = -1; | 4311 | int ins_index = -1; |
4313 | DEFINE_WAIT(wait); | 4312 | DEFINE_WAIT(wait); |
4314 | bool is_mc_recip; | 4313 | bool is_mc_recip; |
4315 | s32 rc; | 4314 | s32 rc; |
4316 | 4315 | ||
4316 | down_read(&efx->filter_sem); | ||
4317 | table = efx->filter_state; | ||
4318 | down_write(&table->lock); | ||
4319 | |||
4317 | /* For now, only support RX filters */ | 4320 | /* For now, only support RX filters */ |
4318 | if ((spec->flags & (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)) != | 4321 | if ((spec->flags & (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)) != |
4319 | EFX_FILTER_FLAG_RX) | 4322 | EFX_FILTER_FLAG_RX) { |
4320 | return -EINVAL; | 4323 | rc = -EINVAL; |
4324 | goto out_unlock; | ||
4325 | } | ||
4321 | 4326 | ||
4322 | rc = efx_ef10_filter_pri(table, spec); | 4327 | rc = efx_ef10_filter_pri(table, spec); |
4323 | if (rc < 0) | 4328 | if (rc < 0) |
4324 | return rc; | 4329 | goto out_unlock; |
4325 | match_pri = rc; | 4330 | match_pri = rc; |
4326 | 4331 | ||
4327 | hash = efx_ef10_filter_hash(spec); | 4332 | hash = efx_ef10_filter_hash(spec); |
@@ -4335,86 +4340,64 @@ static s32 efx_ef10_filter_insert(struct efx_nic *efx, | |||
4335 | &efx->rss_context.list); | 4340 | &efx->rss_context.list); |
4336 | else | 4341 | else |
4337 | ctx = &efx->rss_context; | 4342 | ctx = &efx->rss_context; |
4338 | if (!ctx) | 4343 | if (!ctx) { |
4339 | return -ENOENT; | 4344 | rc = -ENOENT; |
4340 | if (ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID) | 4345 | goto out_unlock; |
4341 | return -EOPNOTSUPP; | 4346 | } |
4347 | if (ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID) { | ||
4348 | rc = -EOPNOTSUPP; | ||
4349 | goto out_unlock; | ||
4350 | } | ||
4342 | } | 4351 | } |
4343 | 4352 | ||
4344 | /* Find any existing filters with the same match tuple or | 4353 | /* Find any existing filters with the same match tuple or |
4345 | * else a free slot to insert at. If any of them are busy, | 4354 | * else a free slot to insert at. |
4346 | * we have to wait and retry. | ||
4347 | */ | 4355 | */ |
4348 | for (;;) { | 4356 | for (depth = 1; depth < EFX_EF10_FILTER_SEARCH_LIMIT; depth++) { |
4349 | unsigned int depth = 1; | 4357 | i = (hash + depth) & (HUNT_FILTER_TBL_ROWS - 1); |
4350 | unsigned int i; | 4358 | saved_spec = efx_ef10_filter_entry_spec(table, i); |
4351 | |||
4352 | spin_lock_bh(&efx->filter_lock); | ||
4353 | 4359 | ||
4354 | for (;;) { | 4360 | if (!saved_spec) { |
4355 | i = (hash + depth) & (HUNT_FILTER_TBL_ROWS - 1); | 4361 | if (ins_index < 0) |
4356 | saved_spec = efx_ef10_filter_entry_spec(table, i); | 4362 | ins_index = i; |
4357 | 4363 | } else if (efx_ef10_filter_equal(spec, saved_spec)) { | |
4358 | if (!saved_spec) { | 4364 | if (spec->priority < saved_spec->priority && |
4359 | if (ins_index < 0) | 4365 | spec->priority != EFX_FILTER_PRI_AUTO) { |
4360 | ins_index = i; | 4366 | rc = -EPERM; |
4361 | } else if (efx_ef10_filter_equal(spec, saved_spec)) { | 4367 | goto out_unlock; |
4362 | if (table->entry[i].spec & | ||
4363 | EFX_EF10_FILTER_FLAG_BUSY) | ||
4364 | break; | ||
4365 | if (spec->priority < saved_spec->priority && | ||
4366 | spec->priority != EFX_FILTER_PRI_AUTO) { | ||
4367 | rc = -EPERM; | ||
4368 | goto out_unlock; | ||
4369 | } | ||
4370 | if (!is_mc_recip) { | ||
4371 | /* This is the only one */ | ||
4372 | if (spec->priority == | ||
4373 | saved_spec->priority && | ||
4374 | !replace_equal) { | ||
4375 | rc = -EEXIST; | ||
4376 | goto out_unlock; | ||
4377 | } | ||
4378 | ins_index = i; | ||
4379 | goto found; | ||
4380 | } else if (spec->priority > | ||
4381 | saved_spec->priority || | ||
4382 | (spec->priority == | ||
4383 | saved_spec->priority && | ||
4384 | replace_equal)) { | ||
4385 | if (ins_index < 0) | ||
4386 | ins_index = i; | ||
4387 | else | ||
4388 | __set_bit(depth, mc_rem_map); | ||
4389 | } | ||
4390 | } | 4368 | } |
4391 | 4369 | if (!is_mc_recip) { | |
4392 | /* Once we reach the maximum search depth, use | 4370 | /* This is the only one */ |
4393 | * the first suitable slot or return -EBUSY if | 4371 | if (spec->priority == |
4394 | * there was none | 4372 | saved_spec->priority && |
4395 | */ | 4373 | !replace_equal) { |
4396 | if (depth == EFX_EF10_FILTER_SEARCH_LIMIT) { | 4374 | rc = -EEXIST; |
4397 | if (ins_index < 0) { | ||
4398 | rc = -EBUSY; | ||
4399 | goto out_unlock; | 4375 | goto out_unlock; |
4400 | } | 4376 | } |
4401 | goto found; | 4377 | ins_index = i; |
4378 | break; | ||
4379 | } else if (spec->priority > | ||
4380 | saved_spec->priority || | ||
4381 | (spec->priority == | ||
4382 | saved_spec->priority && | ||
4383 | replace_equal)) { | ||
4384 | if (ins_index < 0) | ||
4385 | ins_index = i; | ||
4386 | else | ||
4387 | __set_bit(depth, mc_rem_map); | ||
4402 | } | 4388 | } |
4403 | |||
4404 | ++depth; | ||
4405 | } | 4389 | } |
4406 | |||
4407 | prepare_to_wait(&table->waitq, &wait, TASK_UNINTERRUPTIBLE); | ||
4408 | spin_unlock_bh(&efx->filter_lock); | ||
4409 | schedule(); | ||
4410 | } | 4390 | } |
4411 | 4391 | ||
4412 | found: | 4392 | /* Once we reach the maximum search depth, use the first suitable |
4413 | /* Create a software table entry if necessary, and mark it | 4393 | * slot, or return -EBUSY if there was none |
4414 | * busy. We might yet fail to insert, but any attempt to | ||
4415 | * insert a conflicting filter while we're waiting for the | ||
4416 | * firmware must find the busy entry. | ||
4417 | */ | 4394 | */ |
4395 | if (ins_index < 0) { | ||
4396 | rc = -EBUSY; | ||
4397 | goto out_unlock; | ||
4398 | } | ||
4399 | |||
4400 | /* Create a software table entry if necessary. */ | ||
4418 | saved_spec = efx_ef10_filter_entry_spec(table, ins_index); | 4401 | saved_spec = efx_ef10_filter_entry_spec(table, ins_index); |
4419 | if (saved_spec) { | 4402 | if (saved_spec) { |
4420 | if (spec->priority == EFX_FILTER_PRI_AUTO && | 4403 | if (spec->priority == EFX_FILTER_PRI_AUTO && |
@@ -4438,28 +4421,13 @@ found: | |||
4438 | *saved_spec = *spec; | 4421 | *saved_spec = *spec; |
4439 | priv_flags = 0; | 4422 | priv_flags = 0; |
4440 | } | 4423 | } |
4441 | efx_ef10_filter_set_entry(table, ins_index, saved_spec, | 4424 | efx_ef10_filter_set_entry(table, ins_index, saved_spec, priv_flags); |
4442 | priv_flags | EFX_EF10_FILTER_FLAG_BUSY); | ||
4443 | |||
4444 | /* Mark lower-priority multicast recipients busy prior to removal */ | ||
4445 | if (is_mc_recip) { | ||
4446 | unsigned int depth, i; | ||
4447 | |||
4448 | for (depth = 0; depth < EFX_EF10_FILTER_SEARCH_LIMIT; depth++) { | ||
4449 | i = (hash + depth) & (HUNT_FILTER_TBL_ROWS - 1); | ||
4450 | if (test_bit(depth, mc_rem_map)) | ||
4451 | table->entry[i].spec |= | ||
4452 | EFX_EF10_FILTER_FLAG_BUSY; | ||
4453 | } | ||
4454 | } | ||
4455 | |||
4456 | spin_unlock_bh(&efx->filter_lock); | ||
4457 | 4425 | ||
4426 | /* Actually insert the filter on the HW */ | ||
4458 | rc = efx_ef10_filter_push(efx, spec, &table->entry[ins_index].handle, | 4427 | rc = efx_ef10_filter_push(efx, spec, &table->entry[ins_index].handle, |
4459 | ctx, replacing); | 4428 | ctx, replacing); |
4460 | 4429 | ||
4461 | /* Finalise the software table entry */ | 4430 | /* Finalise the software table entry */ |
4462 | spin_lock_bh(&efx->filter_lock); | ||
4463 | if (rc == 0) { | 4431 | if (rc == 0) { |
4464 | if (replacing) { | 4432 | if (replacing) { |
4465 | /* Update the fields that may differ */ | 4433 | /* Update the fields that may differ */ |
@@ -4475,6 +4443,12 @@ found: | |||
4475 | } else if (!replacing) { | 4443 | } else if (!replacing) { |
4476 | kfree(saved_spec); | 4444 | kfree(saved_spec); |
4477 | saved_spec = NULL; | 4445 | saved_spec = NULL; |
4446 | } else { | ||
4447 | /* We failed to replace, so the old filter is still present. | ||
4448 | * Roll back the software table to reflect this. In fact the | ||
4449 | * efx_ef10_filter_set_entry() call below will do the right | ||
4450 | * thing, so nothing extra is needed here. | ||
4451 | */ | ||
4478 | } | 4452 | } |
4479 | efx_ef10_filter_set_entry(table, ins_index, saved_spec, priv_flags); | 4453 | efx_ef10_filter_set_entry(table, ins_index, saved_spec, priv_flags); |
4480 | 4454 | ||
@@ -4496,7 +4470,6 @@ found: | |||
4496 | priv_flags = efx_ef10_filter_entry_flags(table, i); | 4470 | priv_flags = efx_ef10_filter_entry_flags(table, i); |
4497 | 4471 | ||
4498 | if (rc == 0) { | 4472 | if (rc == 0) { |
4499 | spin_unlock_bh(&efx->filter_lock); | ||
4500 | MCDI_SET_DWORD(inbuf, FILTER_OP_IN_OP, | 4473 | MCDI_SET_DWORD(inbuf, FILTER_OP_IN_OP, |
4501 | MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE); | 4474 | MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE); |
4502 | MCDI_SET_QWORD(inbuf, FILTER_OP_IN_HANDLE, | 4475 | MCDI_SET_QWORD(inbuf, FILTER_OP_IN_HANDLE, |
@@ -4504,15 +4477,12 @@ found: | |||
4504 | rc = efx_mcdi_rpc(efx, MC_CMD_FILTER_OP, | 4477 | rc = efx_mcdi_rpc(efx, MC_CMD_FILTER_OP, |
4505 | inbuf, sizeof(inbuf), | 4478 | inbuf, sizeof(inbuf), |
4506 | NULL, 0, NULL); | 4479 | NULL, 0, NULL); |
4507 | spin_lock_bh(&efx->filter_lock); | ||
4508 | } | 4480 | } |
4509 | 4481 | ||
4510 | if (rc == 0) { | 4482 | if (rc == 0) { |
4511 | kfree(saved_spec); | 4483 | kfree(saved_spec); |
4512 | saved_spec = NULL; | 4484 | saved_spec = NULL; |
4513 | priv_flags = 0; | 4485 | priv_flags = 0; |
4514 | } else { | ||
4515 | priv_flags &= ~EFX_EF10_FILTER_FLAG_BUSY; | ||
4516 | } | 4486 | } |
4517 | efx_ef10_filter_set_entry(table, i, saved_spec, | 4487 | efx_ef10_filter_set_entry(table, i, saved_spec, |
4518 | priv_flags); | 4488 | priv_flags); |
@@ -4523,10 +4493,9 @@ found: | |||
4523 | if (rc == 0) | 4493 | if (rc == 0) |
4524 | rc = efx_ef10_make_filter_id(match_pri, ins_index); | 4494 | rc = efx_ef10_make_filter_id(match_pri, ins_index); |
4525 | 4495 | ||
4526 | wake_up_all(&table->waitq); | ||
4527 | out_unlock: | 4496 | out_unlock: |
4528 | spin_unlock_bh(&efx->filter_lock); | 4497 | up_write(&table->lock); |
4529 | finish_wait(&table->waitq, &wait); | 4498 | up_read(&efx->filter_sem); |
4530 | return rc; | 4499 | return rc; |
4531 | } | 4500 | } |
4532 | 4501 | ||
@@ -4539,6 +4508,8 @@ static void efx_ef10_filter_update_rx_scatter(struct efx_nic *efx) | |||
4539 | * If !by_index, remove by ID | 4508 | * If !by_index, remove by ID |
4540 | * If by_index, remove by index | 4509 | * If by_index, remove by index |
4541 | * Filter ID may come from userland and must be range-checked. | 4510 | * Filter ID may come from userland and must be range-checked. |
4511 | * Caller must hold efx->filter_sem for read, and efx->filter_state->lock | ||
4512 | * for write. | ||
4542 | */ | 4513 | */ |
4543 | static int efx_ef10_filter_remove_internal(struct efx_nic *efx, | 4514 | static int efx_ef10_filter_remove_internal(struct efx_nic *efx, |
4544 | unsigned int priority_mask, | 4515 | unsigned int priority_mask, |
@@ -4553,45 +4524,23 @@ static int efx_ef10_filter_remove_internal(struct efx_nic *efx, | |||
4553 | DEFINE_WAIT(wait); | 4524 | DEFINE_WAIT(wait); |
4554 | int rc; | 4525 | int rc; |
4555 | 4526 | ||
4556 | /* Find the software table entry and mark it busy. Don't | ||
4557 | * remove it yet; any attempt to update while we're waiting | ||
4558 | * for the firmware must find the busy entry. | ||
4559 | */ | ||
4560 | for (;;) { | ||
4561 | spin_lock_bh(&efx->filter_lock); | ||
4562 | if (!(table->entry[filter_idx].spec & | ||
4563 | EFX_EF10_FILTER_FLAG_BUSY)) | ||
4564 | break; | ||
4565 | prepare_to_wait(&table->waitq, &wait, TASK_UNINTERRUPTIBLE); | ||
4566 | spin_unlock_bh(&efx->filter_lock); | ||
4567 | schedule(); | ||
4568 | } | ||
4569 | |||
4570 | spec = efx_ef10_filter_entry_spec(table, filter_idx); | 4527 | spec = efx_ef10_filter_entry_spec(table, filter_idx); |
4571 | if (!spec || | 4528 | if (!spec || |
4572 | (!by_index && | 4529 | (!by_index && |
4573 | efx_ef10_filter_pri(table, spec) != | 4530 | efx_ef10_filter_pri(table, spec) != |
4574 | efx_ef10_filter_get_unsafe_pri(filter_id))) { | 4531 | efx_ef10_filter_get_unsafe_pri(filter_id))) |
4575 | rc = -ENOENT; | 4532 | return -ENOENT; |
4576 | goto out_unlock; | ||
4577 | } | ||
4578 | 4533 | ||
4579 | if (spec->flags & EFX_FILTER_FLAG_RX_OVER_AUTO && | 4534 | if (spec->flags & EFX_FILTER_FLAG_RX_OVER_AUTO && |
4580 | priority_mask == (1U << EFX_FILTER_PRI_AUTO)) { | 4535 | priority_mask == (1U << EFX_FILTER_PRI_AUTO)) { |
4581 | /* Just remove flags */ | 4536 | /* Just remove flags */ |
4582 | spec->flags &= ~EFX_FILTER_FLAG_RX_OVER_AUTO; | 4537 | spec->flags &= ~EFX_FILTER_FLAG_RX_OVER_AUTO; |
4583 | table->entry[filter_idx].spec &= ~EFX_EF10_FILTER_FLAG_AUTO_OLD; | 4538 | table->entry[filter_idx].spec &= ~EFX_EF10_FILTER_FLAG_AUTO_OLD; |
4584 | rc = 0; | 4539 | return 0; |
4585 | goto out_unlock; | ||
4586 | } | ||
4587 | |||
4588 | if (!(priority_mask & (1U << spec->priority))) { | ||
4589 | rc = -ENOENT; | ||
4590 | goto out_unlock; | ||
4591 | } | 4540 | } |
4592 | 4541 | ||
4593 | table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_BUSY; | 4542 | if (!(priority_mask & (1U << spec->priority))) |
4594 | spin_unlock_bh(&efx->filter_lock); | 4543 | return -ENOENT; |
4595 | 4544 | ||
4596 | if (spec->flags & EFX_FILTER_FLAG_RX_OVER_AUTO) { | 4545 | if (spec->flags & EFX_FILTER_FLAG_RX_OVER_AUTO) { |
4597 | /* Reset to an automatic filter */ | 4546 | /* Reset to an automatic filter */ |
@@ -4609,7 +4558,6 @@ static int efx_ef10_filter_remove_internal(struct efx_nic *efx, | |||
4609 | &efx->rss_context, | 4558 | &efx->rss_context, |
4610 | true); | 4559 | true); |
4611 | 4560 | ||
4612 | spin_lock_bh(&efx->filter_lock); | ||
4613 | if (rc == 0) | 4561 | if (rc == 0) |
4614 | *spec = new_spec; | 4562 | *spec = new_spec; |
4615 | } else { | 4563 | } else { |
@@ -4624,7 +4572,6 @@ static int efx_ef10_filter_remove_internal(struct efx_nic *efx, | |||
4624 | rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FILTER_OP, | 4572 | rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FILTER_OP, |
4625 | inbuf, sizeof(inbuf), NULL, 0, NULL); | 4573 | inbuf, sizeof(inbuf), NULL, 0, NULL); |
4626 | 4574 | ||
4627 | spin_lock_bh(&efx->filter_lock); | ||
4628 | if ((rc == 0) || (rc == -ENOENT)) { | 4575 | if ((rc == 0) || (rc == -ENOENT)) { |
4629 | /* Filter removed OK or didn't actually exist */ | 4576 | /* Filter removed OK or didn't actually exist */ |
4630 | kfree(spec); | 4577 | kfree(spec); |
@@ -4636,11 +4583,6 @@ static int efx_ef10_filter_remove_internal(struct efx_nic *efx, | |||
4636 | } | 4583 | } |
4637 | } | 4584 | } |
4638 | 4585 | ||
4639 | table->entry[filter_idx].spec &= ~EFX_EF10_FILTER_FLAG_BUSY; | ||
4640 | wake_up_all(&table->waitq); | ||
4641 | out_unlock: | ||
4642 | spin_unlock_bh(&efx->filter_lock); | ||
4643 | finish_wait(&table->waitq, &wait); | ||
4644 | return rc; | 4586 | return rc; |
4645 | } | 4587 | } |
4646 | 4588 | ||
@@ -4648,17 +4590,33 @@ static int efx_ef10_filter_remove_safe(struct efx_nic *efx, | |||
4648 | enum efx_filter_priority priority, | 4590 | enum efx_filter_priority priority, |
4649 | u32 filter_id) | 4591 | u32 filter_id) |
4650 | { | 4592 | { |
4651 | return efx_ef10_filter_remove_internal(efx, 1U << priority, | 4593 | struct efx_ef10_filter_table *table; |
4652 | filter_id, false); | 4594 | int rc; |
4595 | |||
4596 | down_read(&efx->filter_sem); | ||
4597 | table = efx->filter_state; | ||
4598 | down_write(&table->lock); | ||
4599 | rc = efx_ef10_filter_remove_internal(efx, 1U << priority, filter_id, | ||
4600 | false); | ||
4601 | up_write(&table->lock); | ||
4602 | up_read(&efx->filter_sem); | ||
4603 | return rc; | ||
4653 | } | 4604 | } |
4654 | 4605 | ||
4606 | /* Caller must hold efx->filter_sem for read */ | ||
4655 | static void efx_ef10_filter_remove_unsafe(struct efx_nic *efx, | 4607 | static void efx_ef10_filter_remove_unsafe(struct efx_nic *efx, |
4656 | enum efx_filter_priority priority, | 4608 | enum efx_filter_priority priority, |
4657 | u32 filter_id) | 4609 | u32 filter_id) |
4658 | { | 4610 | { |
4611 | struct efx_ef10_filter_table *table = efx->filter_state; | ||
4612 | |||
4659 | if (filter_id == EFX_EF10_FILTER_ID_INVALID) | 4613 | if (filter_id == EFX_EF10_FILTER_ID_INVALID) |
4660 | return; | 4614 | return; |
4661 | efx_ef10_filter_remove_internal(efx, 1U << priority, filter_id, true); | 4615 | |
4616 | down_write(&table->lock); | ||
4617 | efx_ef10_filter_remove_internal(efx, 1U << priority, filter_id, | ||
4618 | true); | ||
4619 | up_write(&table->lock); | ||
4662 | } | 4620 | } |
4663 | 4621 | ||
4664 | static int efx_ef10_filter_get_safe(struct efx_nic *efx, | 4622 | static int efx_ef10_filter_get_safe(struct efx_nic *efx, |
@@ -4666,11 +4624,13 @@ static int efx_ef10_filter_get_safe(struct efx_nic *efx, | |||
4666 | u32 filter_id, struct efx_filter_spec *spec) | 4624 | u32 filter_id, struct efx_filter_spec *spec) |
4667 | { | 4625 | { |
4668 | unsigned int filter_idx = efx_ef10_filter_get_unsafe_id(filter_id); | 4626 | unsigned int filter_idx = efx_ef10_filter_get_unsafe_id(filter_id); |
4669 | struct efx_ef10_filter_table *table = efx->filter_state; | ||
4670 | const struct efx_filter_spec *saved_spec; | 4627 | const struct efx_filter_spec *saved_spec; |
4628 | struct efx_ef10_filter_table *table; | ||
4671 | int rc; | 4629 | int rc; |
4672 | 4630 | ||
4673 | spin_lock_bh(&efx->filter_lock); | 4631 | down_read(&efx->filter_sem); |
4632 | table = efx->filter_state; | ||
4633 | down_read(&table->lock); | ||
4674 | saved_spec = efx_ef10_filter_entry_spec(table, filter_idx); | 4634 | saved_spec = efx_ef10_filter_entry_spec(table, filter_idx); |
4675 | if (saved_spec && saved_spec->priority == priority && | 4635 | if (saved_spec && saved_spec->priority == priority && |
4676 | efx_ef10_filter_pri(table, saved_spec) == | 4636 | efx_ef10_filter_pri(table, saved_spec) == |
@@ -4680,13 +4640,15 @@ static int efx_ef10_filter_get_safe(struct efx_nic *efx, | |||
4680 | } else { | 4640 | } else { |
4681 | rc = -ENOENT; | 4641 | rc = -ENOENT; |
4682 | } | 4642 | } |
4683 | spin_unlock_bh(&efx->filter_lock); | 4643 | up_read(&table->lock); |
4644 | up_read(&efx->filter_sem); | ||
4684 | return rc; | 4645 | return rc; |
4685 | } | 4646 | } |
4686 | 4647 | ||
4687 | static int efx_ef10_filter_clear_rx(struct efx_nic *efx, | 4648 | static int efx_ef10_filter_clear_rx(struct efx_nic *efx, |
4688 | enum efx_filter_priority priority) | 4649 | enum efx_filter_priority priority) |
4689 | { | 4650 | { |
4651 | struct efx_ef10_filter_table *table; | ||
4690 | unsigned int priority_mask; | 4652 | unsigned int priority_mask; |
4691 | unsigned int i; | 4653 | unsigned int i; |
4692 | int rc; | 4654 | int rc; |
@@ -4694,31 +4656,40 @@ static int efx_ef10_filter_clear_rx(struct efx_nic *efx, | |||
4694 | priority_mask = (((1U << (priority + 1)) - 1) & | 4656 | priority_mask = (((1U << (priority + 1)) - 1) & |
4695 | ~(1U << EFX_FILTER_PRI_AUTO)); | 4657 | ~(1U << EFX_FILTER_PRI_AUTO)); |
4696 | 4658 | ||
4659 | down_read(&efx->filter_sem); | ||
4660 | table = efx->filter_state; | ||
4661 | down_write(&table->lock); | ||
4697 | for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) { | 4662 | for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) { |
4698 | rc = efx_ef10_filter_remove_internal(efx, priority_mask, | 4663 | rc = efx_ef10_filter_remove_internal(efx, priority_mask, |
4699 | i, true); | 4664 | i, true); |
4700 | if (rc && rc != -ENOENT) | 4665 | if (rc && rc != -ENOENT) |
4701 | return rc; | 4666 | break; |
4667 | rc = 0; | ||
4702 | } | 4668 | } |
4703 | 4669 | ||
4704 | return 0; | 4670 | up_write(&table->lock); |
4671 | up_read(&efx->filter_sem); | ||
4672 | return rc; | ||
4705 | } | 4673 | } |
4706 | 4674 | ||
4707 | static u32 efx_ef10_filter_count_rx_used(struct efx_nic *efx, | 4675 | static u32 efx_ef10_filter_count_rx_used(struct efx_nic *efx, |
4708 | enum efx_filter_priority priority) | 4676 | enum efx_filter_priority priority) |
4709 | { | 4677 | { |
4710 | struct efx_ef10_filter_table *table = efx->filter_state; | 4678 | struct efx_ef10_filter_table *table; |
4711 | unsigned int filter_idx; | 4679 | unsigned int filter_idx; |
4712 | s32 count = 0; | 4680 | s32 count = 0; |
4713 | 4681 | ||
4714 | spin_lock_bh(&efx->filter_lock); | 4682 | down_read(&efx->filter_sem); |
4683 | table = efx->filter_state; | ||
4684 | down_read(&table->lock); | ||
4715 | for (filter_idx = 0; filter_idx < HUNT_FILTER_TBL_ROWS; filter_idx++) { | 4685 | for (filter_idx = 0; filter_idx < HUNT_FILTER_TBL_ROWS; filter_idx++) { |
4716 | if (table->entry[filter_idx].spec && | 4686 | if (table->entry[filter_idx].spec && |
4717 | efx_ef10_filter_entry_spec(table, filter_idx)->priority == | 4687 | efx_ef10_filter_entry_spec(table, filter_idx)->priority == |
4718 | priority) | 4688 | priority) |
4719 | ++count; | 4689 | ++count; |
4720 | } | 4690 | } |
4721 | spin_unlock_bh(&efx->filter_lock); | 4691 | up_read(&table->lock); |
4692 | up_read(&efx->filter_sem); | ||
4722 | return count; | 4693 | return count; |
4723 | } | 4694 | } |
4724 | 4695 | ||
@@ -4733,12 +4704,15 @@ static s32 efx_ef10_filter_get_rx_ids(struct efx_nic *efx, | |||
4733 | enum efx_filter_priority priority, | 4704 | enum efx_filter_priority priority, |
4734 | u32 *buf, u32 size) | 4705 | u32 *buf, u32 size) |
4735 | { | 4706 | { |
4736 | struct efx_ef10_filter_table *table = efx->filter_state; | 4707 | struct efx_ef10_filter_table *table; |
4737 | struct efx_filter_spec *spec; | 4708 | struct efx_filter_spec *spec; |
4738 | unsigned int filter_idx; | 4709 | unsigned int filter_idx; |
4739 | s32 count = 0; | 4710 | s32 count = 0; |
4740 | 4711 | ||
4741 | spin_lock_bh(&efx->filter_lock); | 4712 | down_read(&efx->filter_sem); |
4713 | table = efx->filter_state; | ||
4714 | down_read(&table->lock); | ||
4715 | |||
4742 | for (filter_idx = 0; filter_idx < HUNT_FILTER_TBL_ROWS; filter_idx++) { | 4716 | for (filter_idx = 0; filter_idx < HUNT_FILTER_TBL_ROWS; filter_idx++) { |
4743 | spec = efx_ef10_filter_entry_spec(table, filter_idx); | 4717 | spec = efx_ef10_filter_entry_spec(table, filter_idx); |
4744 | if (spec && spec->priority == priority) { | 4718 | if (spec && spec->priority == priority) { |
@@ -4752,73 +4726,44 @@ static s32 efx_ef10_filter_get_rx_ids(struct efx_nic *efx, | |||
4752 | filter_idx); | 4726 | filter_idx); |
4753 | } | 4727 | } |
4754 | } | 4728 | } |
4755 | spin_unlock_bh(&efx->filter_lock); | 4729 | up_read(&table->lock); |
4730 | up_read(&efx->filter_sem); | ||
4756 | return count; | 4731 | return count; |
4757 | } | 4732 | } |
4758 | 4733 | ||
4759 | #ifdef CONFIG_RFS_ACCEL | 4734 | #ifdef CONFIG_RFS_ACCEL |
4760 | 4735 | ||
4761 | static void | ||
4762 | efx_ef10_filter_rfs_expire_complete(struct efx_nic *efx, | ||
4763 | unsigned long filter_idx, | ||
4764 | int rc, efx_dword_t *outbuf, | ||
4765 | size_t outlen_actual); | ||
4766 | |||
4767 | static bool efx_ef10_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id, | 4736 | static bool efx_ef10_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id, |
4768 | unsigned int filter_idx) | 4737 | unsigned int filter_idx) |
4769 | { | 4738 | { |
4770 | struct efx_ef10_filter_table *table = efx->filter_state; | 4739 | struct efx_ef10_filter_table *table; |
4771 | struct efx_filter_spec *spec; | 4740 | struct efx_filter_spec *spec; |
4772 | MCDI_DECLARE_BUF(inbuf, | 4741 | bool ret; |
4773 | MC_CMD_FILTER_OP_IN_HANDLE_OFST + | ||
4774 | MC_CMD_FILTER_OP_IN_HANDLE_LEN); | ||
4775 | bool ret = true; | ||
4776 | 4742 | ||
4777 | spin_lock_bh(&efx->filter_lock); | 4743 | down_read(&efx->filter_sem); |
4744 | table = efx->filter_state; | ||
4745 | down_write(&table->lock); | ||
4778 | spec = efx_ef10_filter_entry_spec(table, filter_idx); | 4746 | spec = efx_ef10_filter_entry_spec(table, filter_idx); |
4779 | if (!spec || | 4747 | |
4780 | (table->entry[filter_idx].spec & EFX_EF10_FILTER_FLAG_BUSY) || | 4748 | if (!spec || spec->priority != EFX_FILTER_PRI_HINT) { |
4781 | spec->priority != EFX_FILTER_PRI_HINT || | 4749 | ret = true; |
4782 | !rps_may_expire_flow(efx->net_dev, spec->dmaq_id, | 4750 | goto out_unlock; |
4751 | } | ||
4752 | |||
4753 | if (!rps_may_expire_flow(efx->net_dev, spec->dmaq_id, | ||
4783 | flow_id, filter_idx)) { | 4754 | flow_id, filter_idx)) { |
4784 | ret = false; | 4755 | ret = false; |
4785 | goto out_unlock; | 4756 | goto out_unlock; |
4786 | } | 4757 | } |
4787 | 4758 | ||
4788 | MCDI_SET_DWORD(inbuf, FILTER_OP_IN_OP, | 4759 | ret = efx_ef10_filter_remove_internal(efx, 1U << spec->priority, |
4789 | MC_CMD_FILTER_OP_IN_OP_REMOVE); | 4760 | filter_idx, true) == 0; |
4790 | MCDI_SET_QWORD(inbuf, FILTER_OP_IN_HANDLE, | ||
4791 | table->entry[filter_idx].handle); | ||
4792 | if (efx_mcdi_rpc_async(efx, MC_CMD_FILTER_OP, inbuf, sizeof(inbuf), 0, | ||
4793 | efx_ef10_filter_rfs_expire_complete, filter_idx)) | ||
4794 | ret = false; | ||
4795 | else | ||
4796 | table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_BUSY; | ||
4797 | out_unlock: | 4761 | out_unlock: |
4798 | spin_unlock_bh(&efx->filter_lock); | 4762 | up_write(&table->lock); |
4763 | up_read(&efx->filter_sem); | ||
4799 | return ret; | 4764 | return ret; |
4800 | } | 4765 | } |
4801 | 4766 | ||
4802 | static void | ||
4803 | efx_ef10_filter_rfs_expire_complete(struct efx_nic *efx, | ||
4804 | unsigned long filter_idx, | ||
4805 | int rc, efx_dword_t *outbuf, | ||
4806 | size_t outlen_actual) | ||
4807 | { | ||
4808 | struct efx_ef10_filter_table *table = efx->filter_state; | ||
4809 | struct efx_filter_spec *spec = | ||
4810 | efx_ef10_filter_entry_spec(table, filter_idx); | ||
4811 | |||
4812 | spin_lock_bh(&efx->filter_lock); | ||
4813 | if (rc == 0) { | ||
4814 | kfree(spec); | ||
4815 | efx_ef10_filter_set_entry(table, filter_idx, NULL, 0); | ||
4816 | } | ||
4817 | table->entry[filter_idx].spec &= ~EFX_EF10_FILTER_FLAG_BUSY; | ||
4818 | wake_up_all(&table->waitq); | ||
4819 | spin_unlock_bh(&efx->filter_lock); | ||
4820 | } | ||
4821 | |||
4822 | #endif /* CONFIG_RFS_ACCEL */ | 4767 | #endif /* CONFIG_RFS_ACCEL */ |
4823 | 4768 | ||
4824 | static int efx_ef10_filter_match_flags_from_mcdi(bool encap, u32 mcdi_flags) | 4769 | static int efx_ef10_filter_match_flags_from_mcdi(bool encap, u32 mcdi_flags) |
@@ -5011,9 +4956,9 @@ static int efx_ef10_filter_table_probe(struct efx_nic *efx) | |||
5011 | table->vlan_filter = | 4956 | table->vlan_filter = |
5012 | !!(efx->net_dev->features & NETIF_F_HW_VLAN_CTAG_FILTER); | 4957 | !!(efx->net_dev->features & NETIF_F_HW_VLAN_CTAG_FILTER); |
5013 | INIT_LIST_HEAD(&table->vlan_list); | 4958 | INIT_LIST_HEAD(&table->vlan_list); |
4959 | init_rwsem(&table->lock); | ||
5014 | 4960 | ||
5015 | efx->filter_state = table; | 4961 | efx->filter_state = table; |
5016 | init_waitqueue_head(&table->waitq); | ||
5017 | 4962 | ||
5018 | list_for_each_entry(vlan, &nic_data->vlan_list, list) { | 4963 | list_for_each_entry(vlan, &nic_data->vlan_list, list) { |
5019 | rc = efx_ef10_filter_add_vlan(efx, vlan->vid); | 4964 | rc = efx_ef10_filter_add_vlan(efx, vlan->vid); |
@@ -5055,7 +5000,7 @@ static void efx_ef10_filter_table_restore(struct efx_nic *efx) | |||
5055 | if (!table) | 5000 | if (!table) |
5056 | return; | 5001 | return; |
5057 | 5002 | ||
5058 | spin_lock_bh(&efx->filter_lock); | 5003 | down_write(&table->lock); |
5059 | 5004 | ||
5060 | for (filter_idx = 0; filter_idx < HUNT_FILTER_TBL_ROWS; filter_idx++) { | 5005 | for (filter_idx = 0; filter_idx < HUNT_FILTER_TBL_ROWS; filter_idx++) { |
5061 | spec = efx_ef10_filter_entry_spec(table, filter_idx); | 5006 | spec = efx_ef10_filter_entry_spec(table, filter_idx); |
@@ -5093,15 +5038,11 @@ static void efx_ef10_filter_table_restore(struct efx_nic *efx) | |||
5093 | } | 5038 | } |
5094 | } | 5039 | } |
5095 | 5040 | ||
5096 | table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_BUSY; | ||
5097 | spin_unlock_bh(&efx->filter_lock); | ||
5098 | |||
5099 | rc = efx_ef10_filter_push(efx, spec, | 5041 | rc = efx_ef10_filter_push(efx, spec, |
5100 | &table->entry[filter_idx].handle, | 5042 | &table->entry[filter_idx].handle, |
5101 | ctx, false); | 5043 | ctx, false); |
5102 | if (rc) | 5044 | if (rc) |
5103 | failed++; | 5045 | failed++; |
5104 | spin_lock_bh(&efx->filter_lock); | ||
5105 | 5046 | ||
5106 | if (rc) { | 5047 | if (rc) { |
5107 | not_restored: | 5048 | not_restored: |
@@ -5113,13 +5054,10 @@ not_restored: | |||
5113 | 5054 | ||
5114 | kfree(spec); | 5055 | kfree(spec); |
5115 | efx_ef10_filter_set_entry(table, filter_idx, NULL, 0); | 5056 | efx_ef10_filter_set_entry(table, filter_idx, NULL, 0); |
5116 | } else { | ||
5117 | table->entry[filter_idx].spec &= | ||
5118 | ~EFX_EF10_FILTER_FLAG_BUSY; | ||
5119 | } | 5057 | } |
5120 | } | 5058 | } |
5121 | 5059 | ||
5122 | spin_unlock_bh(&efx->filter_lock); | 5060 | up_write(&table->lock); |
5123 | 5061 | ||
5124 | /* This can happen validly if the MC's capabilities have changed, so | 5062 | /* This can happen validly if the MC's capabilities have changed, so |
5125 | * is not an error. | 5063 | * is not an error. |
@@ -5187,6 +5125,8 @@ static void efx_ef10_filter_mark_one_old(struct efx_nic *efx, uint16_t *id) | |||
5187 | struct efx_ef10_filter_table *table = efx->filter_state; | 5125 | struct efx_ef10_filter_table *table = efx->filter_state; |
5188 | unsigned int filter_idx; | 5126 | unsigned int filter_idx; |
5189 | 5127 | ||
5128 | efx_rwsem_assert_write_locked(&table->lock); | ||
5129 | |||
5190 | if (*id != EFX_EF10_FILTER_ID_INVALID) { | 5130 | if (*id != EFX_EF10_FILTER_ID_INVALID) { |
5191 | filter_idx = efx_ef10_filter_get_unsafe_id(*id); | 5131 | filter_idx = efx_ef10_filter_get_unsafe_id(*id); |
5192 | if (!table->entry[filter_idx].spec) | 5132 | if (!table->entry[filter_idx].spec) |
@@ -5222,10 +5162,10 @@ static void efx_ef10_filter_mark_old(struct efx_nic *efx) | |||
5222 | struct efx_ef10_filter_table *table = efx->filter_state; | 5162 | struct efx_ef10_filter_table *table = efx->filter_state; |
5223 | struct efx_ef10_filter_vlan *vlan; | 5163 | struct efx_ef10_filter_vlan *vlan; |
5224 | 5164 | ||
5225 | spin_lock_bh(&efx->filter_lock); | 5165 | down_write(&table->lock); |
5226 | list_for_each_entry(vlan, &table->vlan_list, list) | 5166 | list_for_each_entry(vlan, &table->vlan_list, list) |
5227 | _efx_ef10_filter_vlan_mark_old(efx, vlan); | 5167 | _efx_ef10_filter_vlan_mark_old(efx, vlan); |
5228 | spin_unlock_bh(&efx->filter_lock); | 5168 | up_write(&table->lock); |
5229 | } | 5169 | } |
5230 | 5170 | ||
5231 | static void efx_ef10_filter_uc_addr_list(struct efx_nic *efx) | 5171 | static void efx_ef10_filter_uc_addr_list(struct efx_nic *efx) |
@@ -5502,10 +5442,7 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, | |||
5502 | return rc; | 5442 | return rc; |
5503 | } | 5443 | } |
5504 | 5444 | ||
5505 | /* Remove filters that weren't renewed. Since nothing else changes the AUTO_OLD | 5445 | /* Remove filters that weren't renewed. */ |
5506 | * flag or removes these filters, we don't need to hold the filter_lock while | ||
5507 | * scanning for these filters. | ||
5508 | */ | ||
5509 | static void efx_ef10_filter_remove_old(struct efx_nic *efx) | 5446 | static void efx_ef10_filter_remove_old(struct efx_nic *efx) |
5510 | { | 5447 | { |
5511 | struct efx_ef10_filter_table *table = efx->filter_state; | 5448 | struct efx_ef10_filter_table *table = efx->filter_state; |
@@ -5514,6 +5451,7 @@ static void efx_ef10_filter_remove_old(struct efx_nic *efx) | |||
5514 | int rc; | 5451 | int rc; |
5515 | int i; | 5452 | int i; |
5516 | 5453 | ||
5454 | down_write(&table->lock); | ||
5517 | for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) { | 5455 | for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) { |
5518 | if (READ_ONCE(table->entry[i].spec) & | 5456 | if (READ_ONCE(table->entry[i].spec) & |
5519 | EFX_EF10_FILTER_FLAG_AUTO_OLD) { | 5457 | EFX_EF10_FILTER_FLAG_AUTO_OLD) { |
@@ -5525,6 +5463,7 @@ static void efx_ef10_filter_remove_old(struct efx_nic *efx) | |||
5525 | remove_failed++; | 5463 | remove_failed++; |
5526 | } | 5464 | } |
5527 | } | 5465 | } |
5466 | up_write(&table->lock); | ||
5528 | 5467 | ||
5529 | if (remove_failed) | 5468 | if (remove_failed) |
5530 | netif_info(efx, drv, efx->net_dev, | 5469 | netif_info(efx, drv, efx->net_dev, |
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 0be93abdb2ad..cb9273016916 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c | |||
@@ -1783,7 +1783,6 @@ static int efx_probe_filters(struct efx_nic *efx) | |||
1783 | { | 1783 | { |
1784 | int rc; | 1784 | int rc; |
1785 | 1785 | ||
1786 | spin_lock_init(&efx->filter_lock); | ||
1787 | init_rwsem(&efx->filter_sem); | 1786 | init_rwsem(&efx->filter_sem); |
1788 | mutex_lock(&efx->mac_lock); | 1787 | mutex_lock(&efx->mac_lock); |
1789 | down_write(&efx->filter_sem); | 1788 | down_write(&efx->filter_sem); |
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index dad78e3dde70..e2901dda4e32 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h | |||
@@ -843,7 +843,7 @@ struct efx_rss_context { | |||
843 | * @loopback_mode: Loopback status | 843 | * @loopback_mode: Loopback status |
844 | * @loopback_modes: Supported loopback mode bitmask | 844 | * @loopback_modes: Supported loopback mode bitmask |
845 | * @loopback_selftest: Offline self-test private state | 845 | * @loopback_selftest: Offline self-test private state |
846 | * @filter_sem: Filter table rw_semaphore, for freeing the table | 846 | * @filter_sem: Filter table rw_semaphore, protects existence of @filter_state |
847 | * @filter_lock: Filter table lock, for mere content changes | 847 | * @filter_lock: Filter table lock, for mere content changes |
848 | * @filter_state: Architecture-dependent filter table state | 848 | * @filter_state: Architecture-dependent filter table state |
849 | * @rps_mutex: Protects RPS state of all channels | 849 | * @rps_mutex: Protects RPS state of all channels |