Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -4776,8 +4776,7 @@ static bool efx_ef10_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id,
		goto out_unlock;
	}

	if (!rps_may_expire_flow(efx->net_dev, spec->dmaq_id,
				 flow_id, filter_idx)) {
	if (!rps_may_expire_flow(efx->net_dev, spec->dmaq_id, flow_id, 0)) {
		ret = false;
		goto out_unlock;
	}
+1 −1
Original line number Diff line number Diff line
@@ -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) &&
	    table->spec[index].priority == EFX_FILTER_PRI_HINT &&
	    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);
		ret = true;
	}
+25 −0
Original line number Diff line number Diff line
@@ -733,6 +733,27 @@ struct efx_rss_context {
	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
 * @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_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: 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.
 * @rxq_flush_pending: Count of number of receive queues that need to be flushed.
 *	Decremented when the efx_flush_rx_queue() is called.
@@ -1004,6 +1027,8 @@ struct efx_nic {
	struct mutex rps_mutex;
	unsigned int rps_expire_channel;
	unsigned int rps_expire_index;
	unsigned long rps_slot_map;
	struct efx_async_filter_insertion rps_slot[EFX_RPS_MAX_IN_FLIGHT];
#endif

	atomic_t active_queues;
+31 −29
Original line number Diff line number Diff line
@@ -827,31 +827,16 @@ MODULE_PARM_DESC(rx_refill_threshold,

#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)
{
	struct efx_async_filter_insertion *req = container_of(data, struct efx_async_filter_insertion,
							      work);
	struct efx_nic *efx = netdev_priv(req->net_dev);
	struct efx_channel *channel = efx_get_channel(efx, req->rxq_index);
	int slot_idx = req - efx->rps_slot;
	int rc;

	rc = efx->type->filter_insert(efx, &req->spec, false);
	rc = efx->type->filter_insert(efx, &req->spec, true);
	if (rc >= 0) {
		/* Remember this so we can check whether to expire the filter
		 * later.
@@ -878,8 +863,8 @@ static void efx_filter_rfs_work(struct work_struct *data)
	}

	/* Release references */
	clear_bit(slot_idx, &efx->rps_slot_map);
	dev_put(req->net_dev);
	kfree(req);
}

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_async_filter_insertion *req;
	struct flow_keys fk;
	int slot_idx;
	int rc;

	if (flow_id == RPS_FLOW_ID_INVALID)
		return -EINVAL;
	/* find a free slot */
	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))
		return -EPROTONOSUPPORT;
	if (flow_id == RPS_FLOW_ID_INVALID) {
		rc = -EINVAL;
		goto out_clear;
	}

	if (fk.basic.n_proto != htons(ETH_P_IP) && fk.basic.n_proto != htons(ETH_P_IPV6))
		return -EPROTONOSUPPORT;
	if (fk.control.flags & FLOW_DIS_IS_FRAGMENT)
		return -EPROTONOSUPPORT;
	if (!skb_flow_dissect_flow_keys(skb, &fk, 0)) {
		rc = -EPROTONOSUPPORT;
		goto out_clear;
	}

	req = kmalloc(sizeof(*req), GFP_ATOMIC);
	if (!req)
		return -ENOMEM;
	if (fk.basic.n_proto != htons(ETH_P_IP) && fk.basic.n_proto != htons(ETH_P_IPV6)) {
		rc = -EPROTONOSUPPORT;
		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->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0,
			   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;
	schedule_work(&req->work);
	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)