diff options
author | Edward Cree <ecree@solarflare.com> | 2018-04-24 12:09:30 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-04-24 13:48:22 -0400 |
commit | f8d6203780b73c07dc49ee421fedae8edb76b6e4 (patch) | |
tree | ed28e63c7b6e5b9a6a8a0f791ffa1cfc3c86ee08 /drivers | |
parent | d805c5209350ae725e3a1ee0204ba27d9e75ce3e (diff) |
sfc: ARFS filter IDs
Associate an arbitrary ID with each ARFS filter, allowing to properly query
for expiry. The association is maintained in a hash table, which is
protected by a spinlock.
v3: fix build warnings when CONFIG_RFS_ACCEL is disabled (thanks lkp-robot).
v2: fixed uninitialised variable (thanks davem and lkp-robot).
Fixes: 3af0f34290f6 ("sfc: replace asynchronous filter operations")
Signed-off-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/ethernet/sfc/ef10.c | 80 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/efx.c | 143 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/efx.h | 21 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/farch.c | 41 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/net_driver.h | 36 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/rx.c | 62 |
6 files changed, 337 insertions, 46 deletions
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 83ce229f4eb7..63036d9bf3e6 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c | |||
@@ -3999,29 +3999,6 @@ static void efx_ef10_prepare_flr(struct efx_nic *efx) | |||
3999 | atomic_set(&efx->active_queues, 0); | 3999 | atomic_set(&efx->active_queues, 0); |
4000 | } | 4000 | } |
4001 | 4001 | ||
4002 | static bool efx_ef10_filter_equal(const struct efx_filter_spec *left, | ||
4003 | const struct efx_filter_spec *right) | ||
4004 | { | ||
4005 | if ((left->match_flags ^ right->match_flags) | | ||
4006 | ((left->flags ^ right->flags) & | ||
4007 | (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX))) | ||
4008 | return false; | ||
4009 | |||
4010 | return memcmp(&left->outer_vid, &right->outer_vid, | ||
4011 | sizeof(struct efx_filter_spec) - | ||
4012 | offsetof(struct efx_filter_spec, outer_vid)) == 0; | ||
4013 | } | ||
4014 | |||
4015 | static unsigned int efx_ef10_filter_hash(const struct efx_filter_spec *spec) | ||
4016 | { | ||
4017 | BUILD_BUG_ON(offsetof(struct efx_filter_spec, outer_vid) & 3); | ||
4018 | return jhash2((const u32 *)&spec->outer_vid, | ||
4019 | (sizeof(struct efx_filter_spec) - | ||
4020 | offsetof(struct efx_filter_spec, outer_vid)) / 4, | ||
4021 | 0); | ||
4022 | /* XXX should we randomise the initval? */ | ||
4023 | } | ||
4024 | |||
4025 | /* Decide whether a filter should be exclusive or else should allow | 4002 | /* Decide whether a filter should be exclusive or else should allow |
4026 | * delivery to additional recipients. Currently we decide that | 4003 | * delivery to additional recipients. Currently we decide that |
4027 | * filters for specific local unicast MAC and IP addresses are | 4004 | * filters for specific local unicast MAC and IP addresses are |
@@ -4346,7 +4323,7 @@ static s32 efx_ef10_filter_insert(struct efx_nic *efx, | |||
4346 | goto out_unlock; | 4323 | goto out_unlock; |
4347 | match_pri = rc; | 4324 | match_pri = rc; |
4348 | 4325 | ||
4349 | hash = efx_ef10_filter_hash(spec); | 4326 | hash = efx_filter_spec_hash(spec); |
4350 | is_mc_recip = efx_filter_is_mc_recipient(spec); | 4327 | is_mc_recip = efx_filter_is_mc_recipient(spec); |
4351 | if (is_mc_recip) | 4328 | if (is_mc_recip) |
4352 | bitmap_zero(mc_rem_map, EFX_EF10_FILTER_SEARCH_LIMIT); | 4329 | bitmap_zero(mc_rem_map, EFX_EF10_FILTER_SEARCH_LIMIT); |
@@ -4378,7 +4355,7 @@ static s32 efx_ef10_filter_insert(struct efx_nic *efx, | |||
4378 | if (!saved_spec) { | 4355 | if (!saved_spec) { |
4379 | if (ins_index < 0) | 4356 | if (ins_index < 0) |
4380 | ins_index = i; | 4357 | ins_index = i; |
4381 | } else if (efx_ef10_filter_equal(spec, saved_spec)) { | 4358 | } else if (efx_filter_spec_equal(spec, saved_spec)) { |
4382 | if (spec->priority < saved_spec->priority && | 4359 | if (spec->priority < saved_spec->priority && |
4383 | spec->priority != EFX_FILTER_PRI_AUTO) { | 4360 | spec->priority != EFX_FILTER_PRI_AUTO) { |
4384 | rc = -EPERM; | 4361 | rc = -EPERM; |
@@ -4762,27 +4739,62 @@ static s32 efx_ef10_filter_get_rx_ids(struct efx_nic *efx, | |||
4762 | static bool efx_ef10_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id, | 4739 | static bool efx_ef10_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id, |
4763 | unsigned int filter_idx) | 4740 | unsigned int filter_idx) |
4764 | { | 4741 | { |
4742 | struct efx_filter_spec *spec, saved_spec; | ||
4765 | struct efx_ef10_filter_table *table; | 4743 | struct efx_ef10_filter_table *table; |
4766 | struct efx_filter_spec *spec; | 4744 | struct efx_arfs_rule *rule = NULL; |
4767 | bool ret; | 4745 | bool ret = true, force = false; |
4746 | u16 arfs_id; | ||
4768 | 4747 | ||
4769 | down_read(&efx->filter_sem); | 4748 | down_read(&efx->filter_sem); |
4770 | table = efx->filter_state; | 4749 | table = efx->filter_state; |
4771 | down_write(&table->lock); | 4750 | down_write(&table->lock); |
4772 | spec = efx_ef10_filter_entry_spec(table, filter_idx); | 4751 | spec = efx_ef10_filter_entry_spec(table, filter_idx); |
4773 | 4752 | ||
4774 | if (!spec || spec->priority != EFX_FILTER_PRI_HINT) { | 4753 | if (!spec || spec->priority != EFX_FILTER_PRI_HINT) |
4775 | ret = true; | ||
4776 | goto out_unlock; | 4754 | goto out_unlock; |
4777 | } | ||
4778 | 4755 | ||
4779 | if (!rps_may_expire_flow(efx->net_dev, spec->dmaq_id, flow_id, 0)) { | 4756 | spin_lock_bh(&efx->rps_hash_lock); |
4780 | ret = false; | 4757 | if (!efx->rps_hash_table) { |
4781 | goto out_unlock; | 4758 | /* In the absence of the table, we always return 0 to ARFS. */ |
4759 | arfs_id = 0; | ||
4760 | } else { | ||
4761 | rule = efx_rps_hash_find(efx, spec); | ||
4762 | if (!rule) | ||
4763 | /* ARFS table doesn't know of this filter, so remove it */ | ||
4764 | goto expire; | ||
4765 | arfs_id = rule->arfs_id; | ||
4766 | ret = efx_rps_check_rule(rule, filter_idx, &force); | ||
4767 | if (force) | ||
4768 | goto expire; | ||
4769 | if (!ret) { | ||
4770 | spin_unlock_bh(&efx->rps_hash_lock); | ||
4771 | goto out_unlock; | ||
4772 | } | ||
4782 | } | 4773 | } |
4783 | 4774 | if (!rps_may_expire_flow(efx->net_dev, spec->dmaq_id, flow_id, arfs_id)) | |
4775 | ret = false; | ||
4776 | else if (rule) | ||
4777 | rule->filter_id = EFX_ARFS_FILTER_ID_REMOVING; | ||
4778 | expire: | ||
4779 | saved_spec = *spec; /* remove operation will kfree spec */ | ||
4780 | spin_unlock_bh(&efx->rps_hash_lock); | ||
4781 | /* At this point (since we dropped the lock), another thread might queue | ||
4782 | * up a fresh insertion request (but the actual insertion will be held | ||
4783 | * up by our possession of the filter table lock). In that case, it | ||
4784 | * will set rule->filter_id to EFX_ARFS_FILTER_ID_PENDING, meaning that | ||
4785 | * the rule is not removed by efx_rps_hash_del() below. | ||
4786 | */ | ||
4784 | ret = efx_ef10_filter_remove_internal(efx, 1U << spec->priority, | 4787 | ret = efx_ef10_filter_remove_internal(efx, 1U << spec->priority, |
4785 | filter_idx, true) == 0; | 4788 | filter_idx, true) == 0; |
4789 | /* While we can't safely dereference rule (we dropped the lock), we can | ||
4790 | * still test it for NULL. | ||
4791 | */ | ||
4792 | if (ret && rule) { | ||
4793 | /* Expiring, so remove entry from ARFS table */ | ||
4794 | spin_lock_bh(&efx->rps_hash_lock); | ||
4795 | efx_rps_hash_del(efx, &saved_spec); | ||
4796 | spin_unlock_bh(&efx->rps_hash_lock); | ||
4797 | } | ||
4786 | out_unlock: | 4798 | out_unlock: |
4787 | up_write(&table->lock); | 4799 | up_write(&table->lock); |
4788 | up_read(&efx->filter_sem); | 4800 | up_read(&efx->filter_sem); |
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 692dd729ee2a..a4ebd8715494 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c | |||
@@ -3027,6 +3027,10 @@ static int efx_init_struct(struct efx_nic *efx, | |||
3027 | mutex_init(&efx->mac_lock); | 3027 | mutex_init(&efx->mac_lock); |
3028 | #ifdef CONFIG_RFS_ACCEL | 3028 | #ifdef CONFIG_RFS_ACCEL |
3029 | mutex_init(&efx->rps_mutex); | 3029 | mutex_init(&efx->rps_mutex); |
3030 | spin_lock_init(&efx->rps_hash_lock); | ||
3031 | /* Failure to allocate is not fatal, but may degrade ARFS performance */ | ||
3032 | efx->rps_hash_table = kcalloc(EFX_ARFS_HASH_TABLE_SIZE, | ||
3033 | sizeof(*efx->rps_hash_table), GFP_KERNEL); | ||
3030 | #endif | 3034 | #endif |
3031 | efx->phy_op = &efx_dummy_phy_operations; | 3035 | efx->phy_op = &efx_dummy_phy_operations; |
3032 | efx->mdio.dev = net_dev; | 3036 | efx->mdio.dev = net_dev; |
@@ -3070,6 +3074,10 @@ static void efx_fini_struct(struct efx_nic *efx) | |||
3070 | { | 3074 | { |
3071 | int i; | 3075 | int i; |
3072 | 3076 | ||
3077 | #ifdef CONFIG_RFS_ACCEL | ||
3078 | kfree(efx->rps_hash_table); | ||
3079 | #endif | ||
3080 | |||
3073 | for (i = 0; i < EFX_MAX_CHANNELS; i++) | 3081 | for (i = 0; i < EFX_MAX_CHANNELS; i++) |
3074 | kfree(efx->channel[i]); | 3082 | kfree(efx->channel[i]); |
3075 | 3083 | ||
@@ -3092,6 +3100,141 @@ void efx_update_sw_stats(struct efx_nic *efx, u64 *stats) | |||
3092 | stats[GENERIC_STAT_rx_noskb_drops] = atomic_read(&efx->n_rx_noskb_drops); | 3100 | stats[GENERIC_STAT_rx_noskb_drops] = atomic_read(&efx->n_rx_noskb_drops); |
3093 | } | 3101 | } |
3094 | 3102 | ||
3103 | bool efx_filter_spec_equal(const struct efx_filter_spec *left, | ||
3104 | const struct efx_filter_spec *right) | ||
3105 | { | ||
3106 | if ((left->match_flags ^ right->match_flags) | | ||
3107 | ((left->flags ^ right->flags) & | ||
3108 | (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX))) | ||
3109 | return false; | ||
3110 | |||
3111 | return memcmp(&left->outer_vid, &right->outer_vid, | ||
3112 | sizeof(struct efx_filter_spec) - | ||
3113 | offsetof(struct efx_filter_spec, outer_vid)) == 0; | ||
3114 | } | ||
3115 | |||
3116 | u32 efx_filter_spec_hash(const struct efx_filter_spec *spec) | ||
3117 | { | ||
3118 | BUILD_BUG_ON(offsetof(struct efx_filter_spec, outer_vid) & 3); | ||
3119 | return jhash2((const u32 *)&spec->outer_vid, | ||
3120 | (sizeof(struct efx_filter_spec) - | ||
3121 | offsetof(struct efx_filter_spec, outer_vid)) / 4, | ||
3122 | 0); | ||
3123 | } | ||
3124 | |||
3125 | #ifdef CONFIG_RFS_ACCEL | ||
3126 | bool efx_rps_check_rule(struct efx_arfs_rule *rule, unsigned int filter_idx, | ||
3127 | bool *force) | ||
3128 | { | ||
3129 | if (rule->filter_id == EFX_ARFS_FILTER_ID_PENDING) { | ||
3130 | /* ARFS is currently updating this entry, leave it */ | ||
3131 | return false; | ||
3132 | } | ||
3133 | if (rule->filter_id == EFX_ARFS_FILTER_ID_ERROR) { | ||
3134 | /* ARFS tried and failed to update this, so it's probably out | ||
3135 | * of date. Remove the filter and the ARFS rule entry. | ||
3136 | */ | ||
3137 | rule->filter_id = EFX_ARFS_FILTER_ID_REMOVING; | ||
3138 | *force = true; | ||
3139 | return true; | ||
3140 | } else if (WARN_ON(rule->filter_id != filter_idx)) { /* can't happen */ | ||
3141 | /* ARFS has moved on, so old filter is not needed. Since we did | ||
3142 | * not mark the rule with EFX_ARFS_FILTER_ID_REMOVING, it will | ||
3143 | * not be removed by efx_rps_hash_del() subsequently. | ||
3144 | */ | ||
3145 | *force = true; | ||
3146 | return true; | ||
3147 | } | ||
3148 | /* Remove it iff ARFS wants to. */ | ||
3149 | return true; | ||
3150 | } | ||
3151 | |||
3152 | struct hlist_head *efx_rps_hash_bucket(struct efx_nic *efx, | ||
3153 | const struct efx_filter_spec *spec) | ||
3154 | { | ||
3155 | u32 hash = efx_filter_spec_hash(spec); | ||
3156 | |||
3157 | WARN_ON(!spin_is_locked(&efx->rps_hash_lock)); | ||
3158 | if (!efx->rps_hash_table) | ||
3159 | return NULL; | ||
3160 | return &efx->rps_hash_table[hash % EFX_ARFS_HASH_TABLE_SIZE]; | ||
3161 | } | ||
3162 | |||
3163 | struct efx_arfs_rule *efx_rps_hash_find(struct efx_nic *efx, | ||
3164 | const struct efx_filter_spec *spec) | ||
3165 | { | ||
3166 | struct efx_arfs_rule *rule; | ||
3167 | struct hlist_head *head; | ||
3168 | struct hlist_node *node; | ||
3169 | |||
3170 | head = efx_rps_hash_bucket(efx, spec); | ||
3171 | if (!head) | ||
3172 | return NULL; | ||
3173 | hlist_for_each(node, head) { | ||
3174 | rule = container_of(node, struct efx_arfs_rule, node); | ||
3175 | if (efx_filter_spec_equal(spec, &rule->spec)) | ||
3176 | return rule; | ||
3177 | } | ||
3178 | return NULL; | ||
3179 | } | ||
3180 | |||
3181 | struct efx_arfs_rule *efx_rps_hash_add(struct efx_nic *efx, | ||
3182 | const struct efx_filter_spec *spec, | ||
3183 | bool *new) | ||
3184 | { | ||
3185 | struct efx_arfs_rule *rule; | ||
3186 | struct hlist_head *head; | ||
3187 | struct hlist_node *node; | ||
3188 | |||
3189 | head = efx_rps_hash_bucket(efx, spec); | ||
3190 | if (!head) | ||
3191 | return NULL; | ||
3192 | hlist_for_each(node, head) { | ||
3193 | rule = container_of(node, struct efx_arfs_rule, node); | ||
3194 | if (efx_filter_spec_equal(spec, &rule->spec)) { | ||
3195 | *new = false; | ||
3196 | return rule; | ||
3197 | } | ||
3198 | } | ||
3199 | rule = kmalloc(sizeof(*rule), GFP_ATOMIC); | ||
3200 | *new = true; | ||
3201 | if (rule) { | ||
3202 | memcpy(&rule->spec, spec, sizeof(rule->spec)); | ||
3203 | hlist_add_head(&rule->node, head); | ||
3204 | } | ||
3205 | return rule; | ||
3206 | } | ||
3207 | |||
3208 | void efx_rps_hash_del(struct efx_nic *efx, const struct efx_filter_spec *spec) | ||
3209 | { | ||
3210 | struct efx_arfs_rule *rule; | ||
3211 | struct hlist_head *head; | ||
3212 | struct hlist_node *node; | ||
3213 | |||
3214 | head = efx_rps_hash_bucket(efx, spec); | ||
3215 | if (WARN_ON(!head)) | ||
3216 | return; | ||
3217 | hlist_for_each(node, head) { | ||
3218 | rule = container_of(node, struct efx_arfs_rule, node); | ||
3219 | if (efx_filter_spec_equal(spec, &rule->spec)) { | ||
3220 | /* Someone already reused the entry. We know that if | ||
3221 | * this check doesn't fire (i.e. filter_id == REMOVING) | ||
3222 | * then the REMOVING mark was put there by our caller, | ||
3223 | * because caller is holding a lock on filter table and | ||
3224 | * only holders of that lock set REMOVING. | ||
3225 | */ | ||
3226 | if (rule->filter_id != EFX_ARFS_FILTER_ID_REMOVING) | ||
3227 | return; | ||
3228 | hlist_del(node); | ||
3229 | kfree(rule); | ||
3230 | return; | ||
3231 | } | ||
3232 | } | ||
3233 | /* We didn't find it. */ | ||
3234 | WARN_ON(1); | ||
3235 | } | ||
3236 | #endif | ||
3237 | |||
3095 | /* RSS contexts. We're using linked lists and crappy O(n) algorithms, because | 3238 | /* RSS contexts. We're using linked lists and crappy O(n) algorithms, because |
3096 | * (a) this is an infrequent control-plane operation and (b) n is small (max 64) | 3239 | * (a) this is an infrequent control-plane operation and (b) n is small (max 64) |
3097 | */ | 3240 | */ |
diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index a3140e16fcef..3f759ebdcf10 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h | |||
@@ -186,6 +186,27 @@ static inline void efx_filter_rfs_expire(struct work_struct *data) {} | |||
186 | #endif | 186 | #endif |
187 | bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec); | 187 | bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec); |
188 | 188 | ||
189 | bool efx_filter_spec_equal(const struct efx_filter_spec *left, | ||
190 | const struct efx_filter_spec *right); | ||
191 | u32 efx_filter_spec_hash(const struct efx_filter_spec *spec); | ||
192 | |||
193 | #ifdef CONFIG_RFS_ACCEL | ||
194 | bool efx_rps_check_rule(struct efx_arfs_rule *rule, unsigned int filter_idx, | ||
195 | bool *force); | ||
196 | |||
197 | struct efx_arfs_rule *efx_rps_hash_find(struct efx_nic *efx, | ||
198 | const struct efx_filter_spec *spec); | ||
199 | |||
200 | /* @new is written to indicate if entry was newly added (true) or if an old | ||
201 | * entry was found and returned (false). | ||
202 | */ | ||
203 | struct efx_arfs_rule *efx_rps_hash_add(struct efx_nic *efx, | ||
204 | const struct efx_filter_spec *spec, | ||
205 | bool *new); | ||
206 | |||
207 | void efx_rps_hash_del(struct efx_nic *efx, const struct efx_filter_spec *spec); | ||
208 | #endif | ||
209 | |||
189 | /* RSS contexts */ | 210 | /* RSS contexts */ |
190 | struct efx_rss_context *efx_alloc_rss_context_entry(struct efx_nic *efx); | 211 | struct efx_rss_context *efx_alloc_rss_context_entry(struct efx_nic *efx); |
191 | struct efx_rss_context *efx_find_rss_context_entry(struct efx_nic *efx, u32 id); | 212 | struct efx_rss_context *efx_find_rss_context_entry(struct efx_nic *efx, u32 id); |
diff --git a/drivers/net/ethernet/sfc/farch.c b/drivers/net/ethernet/sfc/farch.c index 7174ef5e5c5e..c72adf8b52ea 100644 --- a/drivers/net/ethernet/sfc/farch.c +++ b/drivers/net/ethernet/sfc/farch.c | |||
@@ -2905,18 +2905,45 @@ bool efx_farch_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id, | |||
2905 | { | 2905 | { |
2906 | struct efx_farch_filter_state *state = efx->filter_state; | 2906 | struct efx_farch_filter_state *state = efx->filter_state; |
2907 | struct efx_farch_filter_table *table; | 2907 | struct efx_farch_filter_table *table; |
2908 | bool ret = false; | 2908 | bool ret = false, force = false; |
2909 | u16 arfs_id; | ||
2909 | 2910 | ||
2910 | down_write(&state->lock); | 2911 | down_write(&state->lock); |
2912 | spin_lock_bh(&efx->rps_hash_lock); | ||
2911 | table = &state->table[EFX_FARCH_FILTER_TABLE_RX_IP]; | 2913 | table = &state->table[EFX_FARCH_FILTER_TABLE_RX_IP]; |
2912 | if (test_bit(index, table->used_bitmap) && | 2914 | if (test_bit(index, table->used_bitmap) && |
2913 | table->spec[index].priority == EFX_FILTER_PRI_HINT && | 2915 | table->spec[index].priority == EFX_FILTER_PRI_HINT) { |
2914 | rps_may_expire_flow(efx->net_dev, table->spec[index].dmaq_id, | 2916 | struct efx_arfs_rule *rule = NULL; |
2915 | flow_id, 0)) { | 2917 | struct efx_filter_spec spec; |
2916 | efx_farch_filter_table_clear_entry(efx, table, index); | 2918 | |
2917 | ret = true; | 2919 | efx_farch_filter_to_gen_spec(&spec, &table->spec[index]); |
2920 | if (!efx->rps_hash_table) { | ||
2921 | /* In the absence of the table, we always returned 0 to | ||
2922 | * ARFS, so use the same to query it. | ||
2923 | */ | ||
2924 | arfs_id = 0; | ||
2925 | } else { | ||
2926 | rule = efx_rps_hash_find(efx, &spec); | ||
2927 | if (!rule) { | ||
2928 | /* ARFS table doesn't know of this filter, remove it */ | ||
2929 | force = true; | ||
2930 | } else { | ||
2931 | arfs_id = rule->arfs_id; | ||
2932 | if (!efx_rps_check_rule(rule, index, &force)) | ||
2933 | goto out_unlock; | ||
2934 | } | ||
2935 | } | ||
2936 | if (force || rps_may_expire_flow(efx->net_dev, spec.dmaq_id, | ||
2937 | flow_id, arfs_id)) { | ||
2938 | if (rule) | ||
2939 | rule->filter_id = EFX_ARFS_FILTER_ID_REMOVING; | ||
2940 | efx_rps_hash_del(efx, &spec); | ||
2941 | efx_farch_filter_table_clear_entry(efx, table, index); | ||
2942 | ret = true; | ||
2943 | } | ||
2918 | } | 2944 | } |
2919 | 2945 | out_unlock: | |
2946 | spin_unlock_bh(&efx->rps_hash_lock); | ||
2920 | up_write(&state->lock); | 2947 | up_write(&state->lock); |
2921 | return ret; | 2948 | return ret; |
2922 | } | 2949 | } |
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index eea3808b3f25..65568925c3ef 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h | |||
@@ -734,6 +734,35 @@ struct efx_rss_context { | |||
734 | }; | 734 | }; |
735 | 735 | ||
736 | #ifdef CONFIG_RFS_ACCEL | 736 | #ifdef CONFIG_RFS_ACCEL |
737 | /* Order of these is important, since filter_id >= %EFX_ARFS_FILTER_ID_PENDING | ||
738 | * is used to test if filter does or will exist. | ||
739 | */ | ||
740 | #define EFX_ARFS_FILTER_ID_PENDING -1 | ||
741 | #define EFX_ARFS_FILTER_ID_ERROR -2 | ||
742 | #define EFX_ARFS_FILTER_ID_REMOVING -3 | ||
743 | /** | ||
744 | * struct efx_arfs_rule - record of an ARFS filter and its IDs | ||
745 | * @node: linkage into hash table | ||
746 | * @spec: details of the filter (used as key for hash table). Use efx->type to | ||
747 | * determine which member to use. | ||
748 | * @rxq_index: channel to which the filter will steer traffic. | ||
749 | * @arfs_id: filter ID which was returned to ARFS | ||
750 | * @filter_id: index in software filter table. May be | ||
751 | * %EFX_ARFS_FILTER_ID_PENDING if filter was not inserted yet, | ||
752 | * %EFX_ARFS_FILTER_ID_ERROR if filter insertion failed, or | ||
753 | * %EFX_ARFS_FILTER_ID_REMOVING if expiry is currently removing the filter. | ||
754 | */ | ||
755 | struct efx_arfs_rule { | ||
756 | struct hlist_node node; | ||
757 | struct efx_filter_spec spec; | ||
758 | u16 rxq_index; | ||
759 | u16 arfs_id; | ||
760 | s32 filter_id; | ||
761 | }; | ||
762 | |||
763 | /* Size chosen so that the table is one page (4kB) */ | ||
764 | #define EFX_ARFS_HASH_TABLE_SIZE 512 | ||
765 | |||
737 | /** | 766 | /** |
738 | * struct efx_async_filter_insertion - Request to asynchronously insert a filter | 767 | * struct efx_async_filter_insertion - Request to asynchronously insert a filter |
739 | * @net_dev: Reference to the netdevice | 768 | * @net_dev: Reference to the netdevice |
@@ -873,6 +902,10 @@ struct efx_async_filter_insertion { | |||
873 | * @rps_expire_channel's @rps_flow_id | 902 | * @rps_expire_channel's @rps_flow_id |
874 | * @rps_slot_map: bitmap of in-flight entries in @rps_slot | 903 | * @rps_slot_map: bitmap of in-flight entries in @rps_slot |
875 | * @rps_slot: array of ARFS insertion requests for efx_filter_rfs_work() | 904 | * @rps_slot: array of ARFS insertion requests for efx_filter_rfs_work() |
905 | * @rps_hash_lock: Protects ARFS filter mapping state (@rps_hash_table and | ||
906 | * @rps_next_id). | ||
907 | * @rps_hash_table: Mapping between ARFS filters and their various IDs | ||
908 | * @rps_next_id: next arfs_id for an ARFS filter | ||
876 | * @active_queues: Count of RX and TX queues that haven't been flushed and drained. | 909 | * @active_queues: Count of RX and TX queues that haven't been flushed and drained. |
877 | * @rxq_flush_pending: Count of number of receive queues that need to be flushed. | 910 | * @rxq_flush_pending: Count of number of receive queues that need to be flushed. |
878 | * Decremented when the efx_flush_rx_queue() is called. | 911 | * Decremented when the efx_flush_rx_queue() is called. |
@@ -1029,6 +1062,9 @@ struct efx_nic { | |||
1029 | unsigned int rps_expire_index; | 1062 | unsigned int rps_expire_index; |
1030 | unsigned long rps_slot_map; | 1063 | unsigned long rps_slot_map; |
1031 | struct efx_async_filter_insertion rps_slot[EFX_RPS_MAX_IN_FLIGHT]; | 1064 | struct efx_async_filter_insertion rps_slot[EFX_RPS_MAX_IN_FLIGHT]; |
1065 | spinlock_t rps_hash_lock; | ||
1066 | struct hlist_head *rps_hash_table; | ||
1067 | u32 rps_next_id; | ||
1032 | #endif | 1068 | #endif |
1033 | 1069 | ||
1034 | atomic_t active_queues; | 1070 | atomic_t active_queues; |
diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 9c593c661cbf..64a94f242027 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c | |||
@@ -834,9 +834,29 @@ static void efx_filter_rfs_work(struct work_struct *data) | |||
834 | struct efx_nic *efx = netdev_priv(req->net_dev); | 834 | struct efx_nic *efx = netdev_priv(req->net_dev); |
835 | struct efx_channel *channel = efx_get_channel(efx, req->rxq_index); | 835 | struct efx_channel *channel = efx_get_channel(efx, req->rxq_index); |
836 | int slot_idx = req - efx->rps_slot; | 836 | int slot_idx = req - efx->rps_slot; |
837 | struct efx_arfs_rule *rule; | ||
838 | u16 arfs_id = 0; | ||
837 | int rc; | 839 | int rc; |
838 | 840 | ||
839 | rc = efx->type->filter_insert(efx, &req->spec, true); | 841 | rc = efx->type->filter_insert(efx, &req->spec, true); |
842 | if (efx->rps_hash_table) { | ||
843 | spin_lock_bh(&efx->rps_hash_lock); | ||
844 | rule = efx_rps_hash_find(efx, &req->spec); | ||
845 | /* The rule might have already gone, if someone else's request | ||
846 | * for the same spec was already worked and then expired before | ||
847 | * we got around to our work. In that case we have nothing | ||
848 | * tying us to an arfs_id, meaning that as soon as the filter | ||
849 | * is considered for expiry it will be removed. | ||
850 | */ | ||
851 | if (rule) { | ||
852 | if (rc < 0) | ||
853 | rule->filter_id = EFX_ARFS_FILTER_ID_ERROR; | ||
854 | else | ||
855 | rule->filter_id = rc; | ||
856 | arfs_id = rule->arfs_id; | ||
857 | } | ||
858 | spin_unlock_bh(&efx->rps_hash_lock); | ||
859 | } | ||
840 | if (rc >= 0) { | 860 | if (rc >= 0) { |
841 | /* Remember this so we can check whether to expire the filter | 861 | /* Remember this so we can check whether to expire the filter |
842 | * later. | 862 | * later. |
@@ -848,18 +868,18 @@ static void efx_filter_rfs_work(struct work_struct *data) | |||
848 | 868 | ||
849 | if (req->spec.ether_type == htons(ETH_P_IP)) | 869 | if (req->spec.ether_type == htons(ETH_P_IP)) |
850 | netif_info(efx, rx_status, efx->net_dev, | 870 | netif_info(efx, rx_status, efx->net_dev, |
851 | "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n", | 871 | "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d id %u]\n", |
852 | (req->spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP", | 872 | (req->spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP", |
853 | req->spec.rem_host, ntohs(req->spec.rem_port), | 873 | req->spec.rem_host, ntohs(req->spec.rem_port), |
854 | req->spec.loc_host, ntohs(req->spec.loc_port), | 874 | req->spec.loc_host, ntohs(req->spec.loc_port), |
855 | req->rxq_index, req->flow_id, rc); | 875 | req->rxq_index, req->flow_id, rc, arfs_id); |
856 | else | 876 | else |
857 | netif_info(efx, rx_status, efx->net_dev, | 877 | netif_info(efx, rx_status, efx->net_dev, |
858 | "steering %s [%pI6]:%u:[%pI6]:%u to queue %u [flow %u filter %d]\n", | 878 | "steering %s [%pI6]:%u:[%pI6]:%u to queue %u [flow %u filter %d id %u]\n", |
859 | (req->spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP", | 879 | (req->spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP", |
860 | req->spec.rem_host, ntohs(req->spec.rem_port), | 880 | req->spec.rem_host, ntohs(req->spec.rem_port), |
861 | req->spec.loc_host, ntohs(req->spec.loc_port), | 881 | req->spec.loc_host, ntohs(req->spec.loc_port), |
862 | req->rxq_index, req->flow_id, rc); | 882 | req->rxq_index, req->flow_id, rc, arfs_id); |
863 | } | 883 | } |
864 | 884 | ||
865 | /* Release references */ | 885 | /* Release references */ |
@@ -872,8 +892,10 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, | |||
872 | { | 892 | { |
873 | struct efx_nic *efx = netdev_priv(net_dev); | 893 | struct efx_nic *efx = netdev_priv(net_dev); |
874 | struct efx_async_filter_insertion *req; | 894 | struct efx_async_filter_insertion *req; |
895 | struct efx_arfs_rule *rule; | ||
875 | struct flow_keys fk; | 896 | struct flow_keys fk; |
876 | int slot_idx; | 897 | int slot_idx; |
898 | bool new; | ||
877 | int rc; | 899 | int rc; |
878 | 900 | ||
879 | /* find a free slot */ | 901 | /* find a free slot */ |
@@ -926,12 +948,42 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, | |||
926 | req->spec.rem_port = fk.ports.src; | 948 | req->spec.rem_port = fk.ports.src; |
927 | req->spec.loc_port = fk.ports.dst; | 949 | req->spec.loc_port = fk.ports.dst; |
928 | 950 | ||
951 | if (efx->rps_hash_table) { | ||
952 | /* Add it to ARFS hash table */ | ||
953 | spin_lock(&efx->rps_hash_lock); | ||
954 | rule = efx_rps_hash_add(efx, &req->spec, &new); | ||
955 | if (!rule) { | ||
956 | rc = -ENOMEM; | ||
957 | goto out_unlock; | ||
958 | } | ||
959 | if (new) | ||
960 | rule->arfs_id = efx->rps_next_id++ % RPS_NO_FILTER; | ||
961 | rc = rule->arfs_id; | ||
962 | /* Skip if existing or pending filter already does the right thing */ | ||
963 | if (!new && rule->rxq_index == rxq_index && | ||
964 | rule->filter_id >= EFX_ARFS_FILTER_ID_PENDING) | ||
965 | goto out_unlock; | ||
966 | rule->rxq_index = rxq_index; | ||
967 | rule->filter_id = EFX_ARFS_FILTER_ID_PENDING; | ||
968 | spin_unlock(&efx->rps_hash_lock); | ||
969 | } else { | ||
970 | /* Without an ARFS hash table, we just use arfs_id 0 for all | ||
971 | * filters. This means if multiple flows hash to the same | ||
972 | * flow_id, all but the most recently touched will be eligible | ||
973 | * for expiry. | ||
974 | */ | ||
975 | rc = 0; | ||
976 | } | ||
977 | |||
978 | /* Queue the request */ | ||
929 | dev_hold(req->net_dev = net_dev); | 979 | dev_hold(req->net_dev = net_dev); |
930 | INIT_WORK(&req->work, efx_filter_rfs_work); | 980 | INIT_WORK(&req->work, efx_filter_rfs_work); |
931 | req->rxq_index = rxq_index; | 981 | req->rxq_index = rxq_index; |
932 | req->flow_id = flow_id; | 982 | req->flow_id = flow_id; |
933 | schedule_work(&req->work); | 983 | schedule_work(&req->work); |
934 | return 0; | 984 | return rc; |
985 | out_unlock: | ||
986 | spin_unlock(&efx->rps_hash_lock); | ||
935 | out_clear: | 987 | out_clear: |
936 | clear_bit(slot_idx, &efx->rps_slot_map); | 988 | clear_bit(slot_idx, &efx->rps_slot_map); |
937 | return rc; | 989 | return rc; |