sfc: change ARFS expiry mechanism
The old rfs_filters_added method for determining the quota could potentially allow the NIC to become filled with old filters, which never get tested for expiry. Instead, explicitly make expiry check work depend on the number of filters installed, and don't count checking slots without filters in as doing work. This guarantees that each filter will be checked for expiry at least once every thirty seconds (assuming the channel to which it belongs is NAPI polling actively) regardless of fill level. Signed-off-by: Edward Cree <ecree@solarflare.com> Tested-by: David Ahern <dahern@digitalocean.com> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
This commit is contained in:
parent
c4f2cbd376
commit
8490e75cdb
|
@ -1969,6 +1969,8 @@ static int efx_probe_filters(struct efx_nic *efx)
|
||||||
++i)
|
++i)
|
||||||
channel->rps_flow_id[i] =
|
channel->rps_flow_id[i] =
|
||||||
RPS_FLOW_ID_INVALID;
|
RPS_FLOW_ID_INVALID;
|
||||||
|
channel->rfs_expire_index = 0;
|
||||||
|
channel->rfs_filter_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
@ -1978,8 +1980,6 @@ static int efx_probe_filters(struct efx_nic *efx)
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
efx->rps_expire_index = efx->rps_expire_channel = 0;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
out_unlock:
|
out_unlock:
|
||||||
|
@ -1993,8 +1993,10 @@ static void efx_remove_filters(struct efx_nic *efx)
|
||||||
#ifdef CONFIG_RFS_ACCEL
|
#ifdef CONFIG_RFS_ACCEL
|
||||||
struct efx_channel *channel;
|
struct efx_channel *channel;
|
||||||
|
|
||||||
efx_for_each_channel(channel, efx)
|
efx_for_each_channel(channel, efx) {
|
||||||
|
flush_work(&channel->filter_work);
|
||||||
kfree(channel->rps_flow_id);
|
kfree(channel->rps_flow_id);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
down_write(&efx->filter_sem);
|
down_write(&efx->filter_sem);
|
||||||
efx->type->filter_table_remove(efx);
|
efx->type->filter_table_remove(efx);
|
||||||
|
|
|
@ -166,15 +166,16 @@ static inline s32 efx_filter_get_rx_ids(struct efx_nic *efx,
|
||||||
#ifdef CONFIG_RFS_ACCEL
|
#ifdef CONFIG_RFS_ACCEL
|
||||||
int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
|
int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
|
||||||
u16 rxq_index, u32 flow_id);
|
u16 rxq_index, u32 flow_id);
|
||||||
bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned quota);
|
bool __efx_filter_rfs_expire(struct efx_channel *channel, unsigned int quota);
|
||||||
static inline void efx_filter_rfs_expire(struct work_struct *data)
|
static inline void efx_filter_rfs_expire(struct work_struct *data)
|
||||||
{
|
{
|
||||||
struct efx_channel *channel = container_of(data, struct efx_channel,
|
struct efx_channel *channel = container_of(data, struct efx_channel,
|
||||||
filter_work);
|
filter_work);
|
||||||
|
unsigned int time = jiffies - channel->rfs_last_expiry, quota;
|
||||||
|
|
||||||
if (channel->rfs_filters_added >= 60 &&
|
quota = channel->rfs_filter_count * time / (30 * HZ);
|
||||||
__efx_filter_rfs_expire(channel->efx, 100))
|
if (quota > 20 && __efx_filter_rfs_expire(channel, min(channel->rfs_filter_count, quota)))
|
||||||
channel->rfs_filters_added -= 60;
|
channel->rfs_last_expiry += time;
|
||||||
}
|
}
|
||||||
#define efx_filter_rfs_enabled() 1
|
#define efx_filter_rfs_enabled() 1
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -439,6 +439,11 @@ enum efx_sync_events_state {
|
||||||
* @event_test_cpu: Last CPU to handle interrupt or test event for this channel
|
* @event_test_cpu: Last CPU to handle interrupt or test event for this channel
|
||||||
* @irq_count: Number of IRQs since last adaptive moderation decision
|
* @irq_count: Number of IRQs since last adaptive moderation decision
|
||||||
* @irq_mod_score: IRQ moderation score
|
* @irq_mod_score: IRQ moderation score
|
||||||
|
* @rfs_filter_count: number of accelerated RFS filters currently in place;
|
||||||
|
* equals the count of @rps_flow_id slots filled
|
||||||
|
* @rfs_last_expiry: value of jiffies last time some accelerated RFS filters
|
||||||
|
* were checked for expiry
|
||||||
|
* @rfs_expire_index: next accelerated RFS filter ID to check for expiry
|
||||||
* @filter_work: Work item for efx_filter_rfs_expire()
|
* @filter_work: Work item for efx_filter_rfs_expire()
|
||||||
* @rps_flow_id: Flow IDs of filters allocated for accelerated RFS,
|
* @rps_flow_id: Flow IDs of filters allocated for accelerated RFS,
|
||||||
* indexed by filter ID
|
* indexed by filter ID
|
||||||
|
@ -489,7 +494,9 @@ struct efx_channel {
|
||||||
unsigned int irq_count;
|
unsigned int irq_count;
|
||||||
unsigned int irq_mod_score;
|
unsigned int irq_mod_score;
|
||||||
#ifdef CONFIG_RFS_ACCEL
|
#ifdef CONFIG_RFS_ACCEL
|
||||||
unsigned int rfs_filters_added;
|
unsigned int rfs_filter_count;
|
||||||
|
unsigned int rfs_last_expiry;
|
||||||
|
unsigned int rfs_expire_index;
|
||||||
struct work_struct filter_work;
|
struct work_struct filter_work;
|
||||||
#define RPS_FLOW_ID_INVALID 0xFFFFFFFF
|
#define RPS_FLOW_ID_INVALID 0xFFFFFFFF
|
||||||
u32 *rps_flow_id;
|
u32 *rps_flow_id;
|
||||||
|
@ -923,9 +930,6 @@ struct efx_async_filter_insertion {
|
||||||
* @filter_sem: Filter table rw_semaphore, protects existence of @filter_state
|
* @filter_sem: Filter table rw_semaphore, protects existence of @filter_state
|
||||||
* @filter_state: Architecture-dependent filter table state
|
* @filter_state: Architecture-dependent filter table state
|
||||||
* @rps_mutex: Protects RPS state of all channels
|
* @rps_mutex: Protects RPS state of all channels
|
||||||
* @rps_expire_channel: Next channel to check for expiry
|
|
||||||
* @rps_expire_index: Next index to check for expiry in
|
|
||||||
* @rps_expire_channel's @rps_flow_id
|
|
||||||
* @rps_slot_map: bitmap of in-flight entries in @rps_slot
|
* @rps_slot_map: bitmap of in-flight entries in @rps_slot
|
||||||
* @rps_slot: array of ARFS insertion requests for efx_filter_rfs_work()
|
* @rps_slot: array of ARFS insertion requests for efx_filter_rfs_work()
|
||||||
* @rps_hash_lock: Protects ARFS filter mapping state (@rps_hash_table and
|
* @rps_hash_lock: Protects ARFS filter mapping state (@rps_hash_table and
|
||||||
|
@ -1096,8 +1100,6 @@ struct efx_nic {
|
||||||
void *filter_state;
|
void *filter_state;
|
||||||
#ifdef CONFIG_RFS_ACCEL
|
#ifdef CONFIG_RFS_ACCEL
|
||||||
struct mutex rps_mutex;
|
struct mutex rps_mutex;
|
||||||
unsigned int rps_expire_channel;
|
|
||||||
unsigned int rps_expire_index;
|
|
||||||
unsigned long rps_slot_map;
|
unsigned long rps_slot_map;
|
||||||
struct efx_async_filter_insertion rps_slot[EFX_RPS_MAX_IN_FLIGHT];
|
struct efx_async_filter_insertion rps_slot[EFX_RPS_MAX_IN_FLIGHT];
|
||||||
spinlock_t rps_hash_lock;
|
spinlock_t rps_hash_lock;
|
||||||
|
|
|
@ -988,6 +988,7 @@ static void efx_filter_rfs_work(struct work_struct *data)
|
||||||
|
|
||||||
rc = efx->type->filter_insert(efx, &req->spec, true);
|
rc = efx->type->filter_insert(efx, &req->spec, true);
|
||||||
if (rc >= 0)
|
if (rc >= 0)
|
||||||
|
/* Discard 'priority' part of EF10+ filter ID (mcdi_filters) */
|
||||||
rc %= efx->type->max_rx_ip_filters;
|
rc %= efx->type->max_rx_ip_filters;
|
||||||
if (efx->rps_hash_table) {
|
if (efx->rps_hash_table) {
|
||||||
spin_lock_bh(&efx->rps_hash_lock);
|
spin_lock_bh(&efx->rps_hash_lock);
|
||||||
|
@ -1012,8 +1013,9 @@ static void efx_filter_rfs_work(struct work_struct *data)
|
||||||
* later.
|
* later.
|
||||||
*/
|
*/
|
||||||
mutex_lock(&efx->rps_mutex);
|
mutex_lock(&efx->rps_mutex);
|
||||||
|
if (channel->rps_flow_id[rc] == RPS_FLOW_ID_INVALID)
|
||||||
|
channel->rfs_filter_count++;
|
||||||
channel->rps_flow_id[rc] = req->flow_id;
|
channel->rps_flow_id[rc] = req->flow_id;
|
||||||
++channel->rfs_filters_added;
|
|
||||||
mutex_unlock(&efx->rps_mutex);
|
mutex_unlock(&efx->rps_mutex);
|
||||||
|
|
||||||
if (req->spec.ether_type == htons(ETH_P_IP))
|
if (req->spec.ether_type == htons(ETH_P_IP))
|
||||||
|
@ -1139,38 +1141,44 @@ out_clear:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned int quota)
|
bool __efx_filter_rfs_expire(struct efx_channel *channel, unsigned int quota)
|
||||||
{
|
{
|
||||||
bool (*expire_one)(struct efx_nic *efx, u32 flow_id, unsigned int index);
|
bool (*expire_one)(struct efx_nic *efx, u32 flow_id, unsigned int index);
|
||||||
unsigned int channel_idx, index, size;
|
struct efx_nic *efx = channel->efx;
|
||||||
|
unsigned int index, size, start;
|
||||||
u32 flow_id;
|
u32 flow_id;
|
||||||
|
|
||||||
if (!mutex_trylock(&efx->rps_mutex))
|
if (!mutex_trylock(&efx->rps_mutex))
|
||||||
return false;
|
return false;
|
||||||
expire_one = efx->type->filter_rfs_expire_one;
|
expire_one = efx->type->filter_rfs_expire_one;
|
||||||
channel_idx = efx->rps_expire_channel;
|
index = channel->rfs_expire_index;
|
||||||
index = efx->rps_expire_index;
|
start = index;
|
||||||
size = efx->type->max_rx_ip_filters;
|
size = efx->type->max_rx_ip_filters;
|
||||||
while (quota--) {
|
while (quota) {
|
||||||
struct efx_channel *channel = efx_get_channel(efx, channel_idx);
|
|
||||||
flow_id = channel->rps_flow_id[index];
|
flow_id = channel->rps_flow_id[index];
|
||||||
|
|
||||||
if (flow_id != RPS_FLOW_ID_INVALID &&
|
if (flow_id != RPS_FLOW_ID_INVALID) {
|
||||||
expire_one(efx, flow_id, index)) {
|
quota--;
|
||||||
netif_info(efx, rx_status, efx->net_dev,
|
if (expire_one(efx, flow_id, index)) {
|
||||||
"expired filter %d [queue %u flow %u]\n",
|
netif_info(efx, rx_status, efx->net_dev,
|
||||||
index, channel_idx, flow_id);
|
"expired filter %d [channel %u flow %u]\n",
|
||||||
channel->rps_flow_id[index] = RPS_FLOW_ID_INVALID;
|
index, channel->channel, flow_id);
|
||||||
|
channel->rps_flow_id[index] = RPS_FLOW_ID_INVALID;
|
||||||
|
channel->rfs_filter_count--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (++index == size) {
|
if (++index == size)
|
||||||
if (++channel_idx == efx->n_channels)
|
|
||||||
channel_idx = 0;
|
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
/* If we were called with a quota that exceeds the total number
|
||||||
|
* of filters in the table (which should never happen), ensure
|
||||||
|
* that we don't loop forever - stop when we've examined every
|
||||||
|
* row of the table.
|
||||||
|
*/
|
||||||
|
if (WARN_ON(index == start && quota))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
efx->rps_expire_channel = channel_idx;
|
|
||||||
efx->rps_expire_index = index;
|
|
||||||
|
|
||||||
|
channel->rfs_expire_index = index;
|
||||||
mutex_unlock(&efx->rps_mutex);
|
mutex_unlock(&efx->rps_mutex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue