Commit d6606bcc authored by David S. Miller's avatar David S. Miller

Merge branch 'sfc-ARFS-fixes'

Edward Cree says:

====================
sfc: ARFS fixes

Three issues introduced by my recent asynchronous filter handling changes:
1. The old filter_rfs_insert would replace a matching filter of equal
   priority; we need to pass the appropriate argument to filter_insert to
   make it do the same.
2. We're lying to the kernel with our return value from ndo_rx_flow_steer,
   so we need to lie consistently when calling rps_may_expire_flow.  This
   is only a partial fix, as the lie still prevents us from steering
   multiple flows with the same ID to different queues; a proper fix that
   stops us lying at all will hopefully follow later.
3. It's possible to cause the kernel to hammer ndo_rx_flow_steer very
   hard, so make sure we don't build up too huge a backlog of workitems.

Possibly it would be better to fix #3 on the kernel side; I have a patch
 which I think does that but it's not a regression in 4.17 so isn't 'net'
 material.
There's also the issue that we come up in the bad configuration that
 triggers #3 by default, but that too is a problem for another time.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c5042dac f993740e
...@@ -4776,8 +4776,7 @@ static bool efx_ef10_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id, ...@@ -4776,8 +4776,7 @@ static bool efx_ef10_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id,
goto out_unlock; goto out_unlock;
} }
if (!rps_may_expire_flow(efx->net_dev, spec->dmaq_id, if (!rps_may_expire_flow(efx->net_dev, spec->dmaq_id, flow_id, 0)) {
flow_id, filter_idx)) {
ret = false; ret = false;
goto out_unlock; goto out_unlock;
} }
......
...@@ -2912,7 +2912,7 @@ bool efx_farch_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id, ...@@ -2912,7 +2912,7 @@ bool efx_farch_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id,
if (test_bit(index, table->used_bitmap) && if (test_bit(index, table->used_bitmap) &&
table->spec[index].priority == EFX_FILTER_PRI_HINT && table->spec[index].priority == EFX_FILTER_PRI_HINT &&
rps_may_expire_flow(efx->net_dev, table->spec[index].dmaq_id, rps_may_expire_flow(efx->net_dev, table->spec[index].dmaq_id,
flow_id, index)) { flow_id, 0)) {
efx_farch_filter_table_clear_entry(efx, table, index); efx_farch_filter_table_clear_entry(efx, table, index);
ret = true; ret = true;
} }
......
...@@ -733,6 +733,27 @@ struct efx_rss_context { ...@@ -733,6 +733,27 @@ struct efx_rss_context {
u32 rx_indir_table[128]; u32 rx_indir_table[128];
}; };
#ifdef CONFIG_RFS_ACCEL
/**
* struct efx_async_filter_insertion - Request to asynchronously insert a filter
* @net_dev: Reference to the netdevice
* @spec: The filter to insert
* @work: Workitem for this request
* @rxq_index: Identifies the channel for which this request was made
* @flow_id: Identifies the kernel-side flow for which this request was made
*/
struct efx_async_filter_insertion {
struct net_device *net_dev;
struct efx_filter_spec spec;
struct work_struct work;
u16 rxq_index;
u32 flow_id;
};
/* Maximum number of ARFS workitems that may be in flight on an efx_nic */
#define EFX_RPS_MAX_IN_FLIGHT 8
#endif /* CONFIG_RFS_ACCEL */
/** /**
* struct efx_nic - an Efx NIC * struct efx_nic - an Efx NIC
* @name: Device name (net device name or bus id before net device registered) * @name: Device name (net device name or bus id before net device registered)
...@@ -850,6 +871,8 @@ struct efx_rss_context { ...@@ -850,6 +871,8 @@ struct efx_rss_context {
* @rps_expire_channel: Next channel to check for expiry * @rps_expire_channel: Next channel to check for expiry
* @rps_expire_index: Next index to check for expiry in * @rps_expire_index: Next index to check for expiry in
* @rps_expire_channel's @rps_flow_id * @rps_expire_channel's @rps_flow_id
* @rps_slot_map: bitmap of in-flight entries in @rps_slot
* @rps_slot: array of ARFS insertion requests for efx_filter_rfs_work()
* @active_queues: Count of RX and TX queues that haven't been flushed and drained. * @active_queues: Count of RX and TX queues that haven't been flushed and drained.
* @rxq_flush_pending: Count of number of receive queues that need to be flushed. * @rxq_flush_pending: Count of number of receive queues that need to be flushed.
* Decremented when the efx_flush_rx_queue() is called. * Decremented when the efx_flush_rx_queue() is called.
...@@ -1004,6 +1027,8 @@ struct efx_nic { ...@@ -1004,6 +1027,8 @@ struct efx_nic {
struct mutex rps_mutex; struct mutex rps_mutex;
unsigned int rps_expire_channel; unsigned int rps_expire_channel;
unsigned int rps_expire_index; unsigned int rps_expire_index;
unsigned long rps_slot_map;
struct efx_async_filter_insertion rps_slot[EFX_RPS_MAX_IN_FLIGHT];
#endif #endif
atomic_t active_queues; atomic_t active_queues;
......
...@@ -827,31 +827,16 @@ MODULE_PARM_DESC(rx_refill_threshold, ...@@ -827,31 +827,16 @@ MODULE_PARM_DESC(rx_refill_threshold,
#ifdef CONFIG_RFS_ACCEL #ifdef CONFIG_RFS_ACCEL
/**
* struct efx_async_filter_insertion - Request to asynchronously insert a filter
* @net_dev: Reference to the netdevice
* @spec: The filter to insert
* @work: Workitem for this request
* @rxq_index: Identifies the channel for which this request was made
* @flow_id: Identifies the kernel-side flow for which this request was made
*/
struct efx_async_filter_insertion {
struct net_device *net_dev;
struct efx_filter_spec spec;
struct work_struct work;
u16 rxq_index;
u32 flow_id;
};
static void efx_filter_rfs_work(struct work_struct *data) static void efx_filter_rfs_work(struct work_struct *data)
{ {
struct efx_async_filter_insertion *req = container_of(data, struct efx_async_filter_insertion, struct efx_async_filter_insertion *req = container_of(data, struct efx_async_filter_insertion,
work); work);
struct efx_nic *efx = netdev_priv(req->net_dev); struct efx_nic *efx = netdev_priv(req->net_dev);
struct efx_channel *channel = efx_get_channel(efx, req->rxq_index); struct efx_channel *channel = efx_get_channel(efx, req->rxq_index);
int slot_idx = req - efx->rps_slot;
int rc; int rc;
rc = efx->type->filter_insert(efx, &req->spec, false); rc = efx->type->filter_insert(efx, &req->spec, true);
if (rc >= 0) { if (rc >= 0) {
/* Remember this so we can check whether to expire the filter /* Remember this so we can check whether to expire the filter
* later. * later.
...@@ -878,8 +863,8 @@ static void efx_filter_rfs_work(struct work_struct *data) ...@@ -878,8 +863,8 @@ static void efx_filter_rfs_work(struct work_struct *data)
} }
/* Release references */ /* Release references */
clear_bit(slot_idx, &efx->rps_slot_map);
dev_put(req->net_dev); dev_put(req->net_dev);
kfree(req);
} }
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,
...@@ -888,22 +873,36 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, ...@@ -888,22 +873,36 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
struct efx_nic *efx = netdev_priv(net_dev); struct efx_nic *efx = netdev_priv(net_dev);
struct efx_async_filter_insertion *req; struct efx_async_filter_insertion *req;
struct flow_keys fk; struct flow_keys fk;
int slot_idx;
int rc;
if (flow_id == RPS_FLOW_ID_INVALID) /* find a free slot */
return -EINVAL; for (slot_idx = 0; slot_idx < EFX_RPS_MAX_IN_FLIGHT; slot_idx++)
if (!test_and_set_bit(slot_idx, &efx->rps_slot_map))
break;
if (slot_idx >= EFX_RPS_MAX_IN_FLIGHT)
return -EBUSY;
if (!skb_flow_dissect_flow_keys(skb, &fk, 0)) if (flow_id == RPS_FLOW_ID_INVALID) {
return -EPROTONOSUPPORT; rc = -EINVAL;
goto out_clear;
}
if (fk.basic.n_proto != htons(ETH_P_IP) && fk.basic.n_proto != htons(ETH_P_IPV6)) if (!skb_flow_dissect_flow_keys(skb, &fk, 0)) {
return -EPROTONOSUPPORT; rc = -EPROTONOSUPPORT;
if (fk.control.flags & FLOW_DIS_IS_FRAGMENT) goto out_clear;
return -EPROTONOSUPPORT; }
req = kmalloc(sizeof(*req), GFP_ATOMIC); if (fk.basic.n_proto != htons(ETH_P_IP) && fk.basic.n_proto != htons(ETH_P_IPV6)) {
if (!req) rc = -EPROTONOSUPPORT;
return -ENOMEM; goto out_clear;
}
if (fk.control.flags & FLOW_DIS_IS_FRAGMENT) {
rc = -EPROTONOSUPPORT;
goto out_clear;
}
req = efx->rps_slot + slot_idx;
efx_filter_init_rx(&req->spec, EFX_FILTER_PRI_HINT, efx_filter_init_rx(&req->spec, EFX_FILTER_PRI_HINT,
efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0, efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0,
rxq_index); rxq_index);
...@@ -933,6 +932,9 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, ...@@ -933,6 +932,9 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
req->flow_id = flow_id; req->flow_id = flow_id;
schedule_work(&req->work); schedule_work(&req->work);
return 0; return 0;
out_clear:
clear_bit(slot_idx, &efx->rps_slot_map);
return rc;
} }
bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned int quota) bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned int quota)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment