mirror of https://gitee.com/openkylin/linux.git
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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
d6606bcc6d
|
@ -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,
|
||||||
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 {
|
||||||
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 {
|
||||||
* @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 {
|
||||||
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,
|
||||||
|
|
||||||
#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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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,
|
||||||
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,
|
||||||
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)
|
||||||
|
|
Loading…
Reference in New Issue