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

Merge branch 'cxgb4-add-TC-MATCHALL-classifier-offload'



Rahul Lakkireddy says:

====================
cxgb4: add TC-MATCHALL classifier offload

This series of patches add support to offload TC-MATCHALL classifier
to hardware to classify all outgoing and incoming traffic on the
underlying port. Only 1 egress and 1 ingress rule each can be
offloaded on the underlying port.

Patch 1 adds support for TC-MATCHALL classifier offload on the egress
side. TC-POLICE is the only action that can be offloaded on the egress
side and is used to rate limit all outgoing traffic to specified max
rate.

Patch 2 adds logic to reject the current rule offload if its priority
conflicts with existing rules in the TCAM.

Patch 3 adds support for TC-MATCHALL classifier offload on the ingress
side. The same set of actions supported by existing TC-FLOWER
classifier offload can be applied on all the incoming traffic.

v5:
- Fixed commit message and comment to include comparison for equal
  priority in patch 2.

v4:
- Removed check in patch 1 to reject police offload if prio is not 1.
- Moved TC_SETUP_BLOCK code to separate function in patch 1.
- Added logic to ensure the prio passed by TC doesn't conflict with
  other rules in TCAM in patch 2.
- Higher index has lower priority than lower index in TCAM. So, rework
  cxgb4_get_free_ftid() to search free index from end of TCAM in
  descending order in patch 2.
- Added check to ensure the matchall rule's prio doesn't conflict with
  other rules in TCAM in patch 3.
- Added logic to fill default mask for VIID, if none has been
  provided, to prevent conflict with duplicate VIID rules in patch 3.
- Used existing variables in private structure to fill VIID info,
  instead of extracting the info manually in patch 3.

v3:
- Added check in patch 1 to reject police offload if prio is not 1.
- Assign block_shared variable only for TC_SETUP_BLOCK in patch 1.

v2:
- Added check to reject flow block sharing for policers in patch 1.
- Removed logic to fetch free index from end of TCAM in patch 2.
  Must maintain the same ordering as in kernel.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 77c05d2f 21c4c60b
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -8,7 +8,8 @@ obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
cxgb4-objs := cxgb4_main.o l2t.o smt.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o \
	      cxgb4_uld.o srq.o sched.o cxgb4_filter.o cxgb4_tc_u32.o \
	      cxgb4_ptp.o cxgb4_tc_flower.o cxgb4_cudbg.o cxgb4_mps.o \
	      cudbg_common.o cudbg_lib.o cudbg_zlib.o cxgb4_tc_mqprio.o
	      cudbg_common.o cudbg_lib.o cudbg_zlib.o cxgb4_tc_mqprio.o \
	      cxgb4_tc_matchall.o
cxgb4-$(CONFIG_CHELSIO_T4_DCB) +=  cxgb4_dcb.o
cxgb4-$(CONFIG_CHELSIO_T4_FCOE) +=  cxgb4_fcoe.o
cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
+10 −1
Original line number Diff line number Diff line
@@ -603,6 +603,8 @@ struct port_info {
	u8 vivld;
	u8 smt_idx;
	u8 rx_cchan;

	bool tc_block_shared;
};

struct dentry;
@@ -1101,6 +1103,9 @@ struct adapter {

	/* TC MQPRIO offload */
	struct cxgb4_tc_mqprio *tc_mqprio;

	/* TC MATCHALL classifier offload */
	struct cxgb4_tc_matchall *tc_matchall;
};

/* Support for "sched-class" command to allow a TX Scheduling Class to be
@@ -1130,6 +1135,7 @@ enum {

enum {
	SCHED_CLASS_LEVEL_CL_RL = 0,    /* class rate limiter */
	SCHED_CLASS_LEVEL_CH_RL = 2,    /* channel rate limiter */
};

enum {
@@ -1280,8 +1286,11 @@ struct ch_filter_specification {
	u16 nat_lport;		/* local port to use after NAT'ing */
	u16 nat_fport;		/* foreign port to use after NAT'ing */

	u32 tc_prio;		/* TC's filter priority index */
	u64 tc_cookie;		/* Unique cookie identifying TC rules */

	/* reservation for future additions */
	u8 rsvd[24];
	u8 rsvd[12];

	/* Filter rule value/mask pairs.
	 */
+96 −24
Original line number Diff line number Diff line
@@ -440,36 +440,48 @@ int cxgb4_get_free_ftid(struct net_device *dev, int family)
{
	struct adapter *adap = netdev2adap(dev);
	struct tid_info *t = &adap->tids;
	bool found = false;
	u8 i, n, cnt;
	int ftid;

	spin_lock_bh(&t->ftid_lock);
	if (family == PF_INET) {
		ftid = find_first_zero_bit(t->ftid_bmap, t->nftids);
		if (ftid >= t->nftids)
			ftid = -1;
	} else {
		if (is_t6(adap->params.chip)) {
			ftid = bitmap_find_free_region(t->ftid_bmap,
						       t->nftids, 1);
			if (ftid < 0)
				goto out_unlock;

			/* this is only a lookup, keep the found region
			 * unallocated
	/* IPv4 occupy 1 slot. IPv6 occupy 2 slots on T6 and 4 slots
	 * on T5.
	 */
			bitmap_release_region(t->ftid_bmap, ftid, 1);
		} else {
			ftid = bitmap_find_free_region(t->ftid_bmap,
						       t->nftids, 2);
			if (ftid < 0)
				goto out_unlock;
	n = 1;
	if (family == PF_INET6) {
		n++;
		if (CHELSIO_CHIP_VERSION(adap->params.chip) < CHELSIO_T6)
			n += 2;
	}

	if (n > t->nftids)
		return -ENOMEM;

			bitmap_release_region(t->ftid_bmap, ftid, 2);
	/* Find free filter slots from the end of TCAM. Appropriate
	 * checks must be done by caller later to ensure the prio
	 * passed by TC doesn't conflict with prio saved by existing
	 * rules in the TCAM.
	 */
	spin_lock_bh(&t->ftid_lock);
	ftid = t->nftids - 1;
	while (ftid >= n - 1) {
		cnt = 0;
		for (i = 0; i < n; i++) {
			if (test_bit(ftid - i, t->ftid_bmap))
				break;
			cnt++;
		}
		if (cnt == n) {
			ftid &= ~(n - 1);
			found = true;
			break;
		}

		ftid -= n;
	}
out_unlock:
	spin_unlock_bh(&t->ftid_lock);
	return ftid;

	return found ? ftid : -ENOMEM;
}

static int cxgb4_set_ftid(struct tid_info *t, int fidx, int family,
@@ -510,6 +522,60 @@ static void cxgb4_clear_ftid(struct tid_info *t, int fidx, int family,
	spin_unlock_bh(&t->ftid_lock);
}

bool cxgb4_filter_prio_in_range(struct net_device *dev, u32 idx, u32 prio)
{
	struct adapter *adap = netdev2adap(dev);
	struct filter_entry *prev_fe, *next_fe;
	struct tid_info *t = &adap->tids;
	u32 prev_ftid, next_ftid;
	bool valid = true;

	/* Only insert the rule if both of the following conditions
	 * are met:
	 * 1. The immediate previous rule has priority <= @prio.
	 * 2. The immediate next rule has priority >= @prio.
	 */
	spin_lock_bh(&t->ftid_lock);
	/* Don't insert if there's a rule already present at @idx. */
	if (test_bit(idx, t->ftid_bmap)) {
		valid = false;
		goto out_unlock;
	}

	next_ftid = find_next_bit(t->ftid_bmap, t->nftids, idx);
	if (next_ftid >= t->nftids)
		next_ftid = idx;

	next_fe = &adap->tids.ftid_tab[next_ftid];

	prev_ftid = find_last_bit(t->ftid_bmap, idx);
	if (prev_ftid >= idx)
		prev_ftid = idx;

	/* See if the filter entry belongs to an IPv6 rule, which
	 * occupy 4 slots on T5 and 2 slots on T6. Adjust the
	 * reference to the previously inserted filter entry
	 * accordingly.
	 */
	if (CHELSIO_CHIP_VERSION(adap->params.chip) < CHELSIO_T6) {
		prev_fe = &adap->tids.ftid_tab[prev_ftid & ~0x3];
		if (!prev_fe->fs.type)
			prev_fe = &adap->tids.ftid_tab[prev_ftid];
	} else {
		prev_fe = &adap->tids.ftid_tab[prev_ftid & ~0x1];
		if (!prev_fe->fs.type)
			prev_fe = &adap->tids.ftid_tab[prev_ftid];
	}

	if ((prev_fe->valid && prio < prev_fe->fs.tc_prio) ||
	    (next_fe->valid && prio > next_fe->fs.tc_prio))
		valid = false;

out_unlock:
	spin_unlock_bh(&t->ftid_lock);
	return valid;
}

/* Delete the filter at a specified index. */
static int del_filter_wr(struct adapter *adapter, int fidx)
{
@@ -806,6 +872,12 @@ static void fill_default_mask(struct ch_filter_specification *fs)
		fs->mask.tos |= ~0;
	if (fs->val.proto && !fs->mask.proto)
		fs->mask.proto |= ~0;
	if (fs->val.pfvf_vld && !fs->mask.pfvf_vld)
		fs->mask.pfvf_vld |= ~0;
	if (fs->val.pf && !fs->mask.pf)
		fs->mask.pf |= ~0;
	if (fs->val.vf && !fs->mask.vf)
		fs->mask.vf |= ~0;

	for (i = 0; i < ARRAY_SIZE(fs->val.lip); i++) {
		lip |= fs->val.lip[i];
+1 −0
Original line number Diff line number Diff line
@@ -53,4 +53,5 @@ void clear_all_filters(struct adapter *adapter);
void init_hash_filter(struct adapter *adap);
bool is_filter_exact_match(struct adapter *adap,
			   struct ch_filter_specification *fs);
bool cxgb4_filter_prio_in_range(struct net_device *dev, u32 idx, u32 prio);
#endif /* __CXGB4_FILTER_H */
+83 −8
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@
#include "cxgb4_tc_u32.h"
#include "cxgb4_tc_flower.h"
#include "cxgb4_tc_mqprio.h"
#include "cxgb4_tc_matchall.h"
#include "cxgb4_ptp.h"
#include "cxgb4_cudbg.h"

@@ -3234,8 +3235,33 @@ static int cxgb_setup_tc_cls_u32(struct net_device *dev,
	}
}

static int cxgb_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
				  void *cb_priv)
static int cxgb_setup_tc_matchall(struct net_device *dev,
				  struct tc_cls_matchall_offload *cls_matchall,
				  bool ingress)
{
	struct adapter *adap = netdev2adap(dev);

	if (!adap->tc_matchall)
		return -ENOMEM;

	switch (cls_matchall->command) {
	case TC_CLSMATCHALL_REPLACE:
		return cxgb4_tc_matchall_replace(dev, cls_matchall, ingress);
	case TC_CLSMATCHALL_DESTROY:
		return cxgb4_tc_matchall_destroy(dev, cls_matchall, ingress);
	case TC_CLSMATCHALL_STATS:
		if (ingress)
			return cxgb4_tc_matchall_stats(dev, cls_matchall);
		break;
	default:
		break;
	}

	return -EOPNOTSUPP;
}

static int cxgb_setup_tc_block_ingress_cb(enum tc_setup_type type,
					  void *type_data, void *cb_priv)
{
	struct net_device *dev = cb_priv;
	struct port_info *pi = netdev2pinfo(dev);
@@ -3256,11 +3282,40 @@ static int cxgb_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
		return cxgb_setup_tc_cls_u32(dev, type_data);
	case TC_SETUP_CLSFLOWER:
		return cxgb_setup_tc_flower(dev, type_data);
	case TC_SETUP_CLSMATCHALL:
		return cxgb_setup_tc_matchall(dev, type_data, true);
	default:
		return -EOPNOTSUPP;
	}
}

static int cxgb_setup_tc_block_egress_cb(enum tc_setup_type type,
					 void *type_data, void *cb_priv)
{
	struct net_device *dev = cb_priv;
	struct port_info *pi = netdev2pinfo(dev);
	struct adapter *adap = netdev2adap(dev);

	if (!(adap->flags & CXGB4_FULL_INIT_DONE)) {
		dev_err(adap->pdev_dev,
			"Failed to setup tc on port %d. Link Down?\n",
			pi->port_id);
		return -EINVAL;
	}

	if (!tc_cls_can_offload_and_chain0(dev, type_data))
		return -EOPNOTSUPP;

	switch (type) {
	case TC_SETUP_CLSMATCHALL:
		return cxgb_setup_tc_matchall(dev, type_data, false);
	default:
		break;
	}

	return -EOPNOTSUPP;
}

static int cxgb_setup_tc_mqprio(struct net_device *dev,
				struct tc_mqprio_qopt_offload *mqprio)
{
@@ -3274,19 +3329,34 @@ static int cxgb_setup_tc_mqprio(struct net_device *dev,

static LIST_HEAD(cxgb_block_cb_list);

static int cxgb_setup_tc_block(struct net_device *dev,
			       struct flow_block_offload *f)
{
	struct port_info *pi = netdev_priv(dev);
	flow_setup_cb_t *cb;
	bool ingress_only;

	pi->tc_block_shared = f->block_shared;
	if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
		cb = cxgb_setup_tc_block_egress_cb;
		ingress_only = false;
	} else {
		cb = cxgb_setup_tc_block_ingress_cb;
		ingress_only = true;
	}

	return flow_block_cb_setup_simple(f, &cxgb_block_cb_list,
					  cb, pi, dev, ingress_only);
}

static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type,
			 void *type_data)
{
	struct port_info *pi = netdev2pinfo(dev);

	switch (type) {
	case TC_SETUP_QDISC_MQPRIO:
		return cxgb_setup_tc_mqprio(dev, type_data);
	case TC_SETUP_BLOCK:
		return flow_block_cb_setup_simple(type_data,
						  &cxgb_block_cb_list,
						  cxgb_setup_tc_block_cb,
						  pi, dev, true);
		return cxgb_setup_tc_block(dev, type_data);
	default:
		return -EOPNOTSUPP;
	}
@@ -5741,6 +5811,7 @@ static void free_some_resources(struct adapter *adapter)
	kvfree(adapter->srq);
	t4_cleanup_sched(adapter);
	kvfree(adapter->tids.tid_tab);
	cxgb4_cleanup_tc_matchall(adapter);
	cxgb4_cleanup_tc_mqprio(adapter);
	cxgb4_cleanup_tc_flower(adapter);
	cxgb4_cleanup_tc_u32(adapter);
@@ -6315,6 +6386,10 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
		if (cxgb4_init_tc_mqprio(adapter))
			dev_warn(&pdev->dev,
				 "could not offload tc mqprio, continuing\n");

		if (cxgb4_init_tc_matchall(adapter))
			dev_warn(&pdev->dev,
				 "could not offload tc matchall, continuing\n");
	}

	if (is_offload(adapter) || is_hashfilter(adapter)) {
Loading