Commit f993740e authored by Edward Cree's avatar Edward Cree Committed by David S. Miller

sfc: limit ARFS workitems in flight per channel

A misconfigured system (e.g. with all interrupts affinitised to all CPUs)
 may produce a storm of ARFS steering events.  With the existing sfc ARFS
 implementation, that could create a backlog of workitems that grinds the
 system to a halt.  To prevent this, limit the number of workitems that
 may be in flight for a given SFC device to 8 (EFX_RPS_MAX_IN_FLIGHT), and
 return EBUSY from our ndo_rx_flow_steer method if the limit is reached.
Given this limit, also store the workitems in an array of slots within the
 struct efx_nic, rather than dynamically allocating for each request.
The limit should not negatively impact performance, because it is only
 likely to be hit in cases where ARFS will be ineffective anyway.
Signed-off-by: default avatarEdward Cree <ecree@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a7f80189
...@@ -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,28 +827,13 @@ MODULE_PARM_DESC(rx_refill_threshold, ...@@ -827,28 +827,13 @@ 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, true); rc = efx->type->filter_insert(efx, &req->spec, true);
...@@ -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