All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters
@ 2016-09-12  8:12 Rahul Lakkireddy
  2016-09-12  8:12 ` [PATCH net-next 1/7] cxgb4: move common filter code to separate file Rahul Lakkireddy
                   ` (8 more replies)
  0 siblings, 9 replies; 16+ messages in thread
From: Rahul Lakkireddy @ 2016-09-12  8:12 UTC (permalink / raw)
  To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy

This series of patches add support to offload TC u32 filters onto
Chelsio NICs.

Patch 1 moves current common filter code to separate files
in order to provide a common api for performing packet classification
and filtering in Chelsio NICs.

Patch 2 enables filters for normal NIC configuration and implements
common api for setting and deleting filters.

Patch 3 provides a debugfs for dumping filter information.

Patches 4-7 add support for TC u32 offload via ndo_setup_tc.

Rahul Lakkireddy (7):
  cxgb4: move common filter code to separate file
  cxgb4: add common api support for configuring filters
  cxgb4: add debugfs support to dump filter debug logs
  cxgb4: add parser to translate u32 filters to internal spec
  cxgb4: add support for setting u32 filters
  cxgb4: add support for deleting u32 filters
  cxgb4: add support for drop and redirect actions

 drivers/net/ethernet/chelsio/cxgb4/Makefile        |    2 +-
 drivers/net/ethernet/chelsio/cxgb4/cxgb4.h         |   30 +
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c |    4 +-
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c  | 1086 ++++++++++++++++++++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h  |   50 +
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c    |  338 ++----
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c  |  498 +++++++++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h  |   57 +
 .../ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h    |  294 ++++++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h     |   23 +-
 drivers/net/ethernet/chelsio/cxgb4/t4_values.h     |    5 +-
 11 files changed, 2104 insertions(+), 283 deletions(-)
 create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
 create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
 create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
 create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
 create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h

-- 
2.5.3

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH net-next 1/7] cxgb4: move common filter code to separate file
  2016-09-12  8:12 [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
@ 2016-09-12  8:12 ` Rahul Lakkireddy
  2016-09-12  8:12 ` [PATCH net-next 2/7] cxgb4: add common api support for configuring filters Rahul Lakkireddy
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Rahul Lakkireddy @ 2016-09-12  8:12 UTC (permalink / raw)
  To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy

Move common filter code to separate files.  Also fix the following
checkpatch checks.

CHECK: Comparison to NULL could be written "!f->l2t"
+               if (f->l2t == NULL) {

CHECK: spaces preferred around that '/' (ctx:VxV)
+       fwr->len16_pkd = htonl(FW_WR_LEN16_V(sizeof(*fwr)/16));

Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
 drivers/net/ethernet/chelsio/cxgb4/Makefile       |   2 +-
 drivers/net/ethernet/chelsio/cxgb4/cxgb4.h        |  24 ++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 277 ++++++++++++++++++++++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h |  47 ++++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c   | 264 +--------------------
 5 files changed, 350 insertions(+), 264 deletions(-)
 create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
 create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h

diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile
index 2461296..da88981 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/Makefile
+++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile
@@ -4,7 +4,7 @@
 
 obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
 
-cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o
+cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o
 cxgb4-$(CONFIG_CHELSIO_T4_DCB) +=  cxgb4_dcb.o
 cxgb4-$(CONFIG_CHELSIO_T4_FCOE) +=  cxgb4_fcoe.o
 cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 4595569..053976f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -1041,6 +1041,30 @@ enum {
 	VLAN_REWRITE
 };
 
+/* Host shadow copy of ingress filter entry.  This is in host native format
+ * and doesn't match the ordering or bit order, etc. of the hardware of the
+ * firmware command.  The use of bit-field structure elements is purely to
+ * remind ourselves of the field size limitations and save memory in the case
+ * where the filter table is large.
+ */
+struct filter_entry {
+	/* Administrative fields for filter.
+	 */
+	u32 valid:1;            /* filter allocated and valid */
+	u32 locked:1;           /* filter is administratively locked */
+
+	u32 pending:1;          /* filter action is pending firmware reply */
+	u32 smtidx:8;           /* Source MAC Table index for smac */
+	struct l2t_entry *l2t;  /* Layer Two Table entry for dmac */
+
+	/* The filter itself.  Most of this is a straight copy of information
+	 * provided by the extended ioctl().  Some fields are translated to
+	 * internal forms -- for instance the Ingress Queue ID passed in from
+	 * the ioctl() is translated into the Absolute Ingress Queue ID.
+	 */
+	struct ch_filter_specification fs;
+};
+
 static inline int is_offload(const struct adapter *adap)
 {
 	return adap->params.offload;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
new file mode 100644
index 0000000..2e86902
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
@@ -0,0 +1,277 @@
+/*
+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "cxgb4.h"
+#include "l2t.h"
+#include "t4fw_api.h"
+#include "cxgb4_filter.h"
+
+/* Delete the filter at a specified index.
+ */
+static int del_filter_wr(struct adapter *adapter, int fidx)
+{
+	struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
+	struct sk_buff *skb;
+	struct fw_filter_wr *fwr;
+	unsigned int len, ftid;
+
+	len = sizeof(*fwr);
+	ftid = adapter->tids.ftid_base + fidx;
+
+	skb = alloc_skb(len, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	fwr = (struct fw_filter_wr *)__skb_put(skb, len);
+	t4_mk_filtdelwr(ftid, fwr, adapter->sge.fw_evtq.abs_id);
+
+	/* Mark the filter as "pending" and ship off the Filter Work Request.
+	 * When we get the Work Request Reply we'll clear the pending status.
+	 */
+	f->pending = 1;
+	t4_mgmt_tx(adapter, skb);
+	return 0;
+}
+
+/* Send a Work Request to write the filter at a specified index.  We construct
+ * a Firmware Filter Work Request to have the work done and put the indicated
+ * filter into "pending" mode which will prevent any further actions against
+ * it till we get a reply from the firmware on the completion status of the
+ * request.
+ */
+int set_filter_wr(struct adapter *adapter, int fidx)
+{
+	struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
+	struct sk_buff *skb;
+	struct fw_filter_wr *fwr;
+	unsigned int ftid;
+
+	skb = alloc_skb(sizeof(*fwr), GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	/* If the new filter requires loopback Destination MAC and/or VLAN
+	 * rewriting then we need to allocate a Layer 2 Table (L2T) entry for
+	 * the filter.
+	 */
+	if (f->fs.newdmac || f->fs.newvlan) {
+		/* allocate L2T entry for new filter */
+		f->l2t = t4_l2t_alloc_switching(adapter, f->fs.vlan,
+						f->fs.eport, f->fs.dmac);
+		if (!f->l2t) {
+			kfree_skb(skb);
+			return -ENOMEM;
+		}
+	}
+
+	ftid = adapter->tids.ftid_base + fidx;
+
+	fwr = (struct fw_filter_wr *)__skb_put(skb, sizeof(*fwr));
+	memset(fwr, 0, sizeof(*fwr));
+
+	/* It would be nice to put most of the following in t4_hw.c but most
+	 * of the work is translating the cxgbtool ch_filter_specification
+	 * into the Work Request and the definition of that structure is
+	 * currently in cxgbtool.h which isn't appropriate to pull into the
+	 * common code.  We may eventually try to come up with a more neutral
+	 * filter specification structure but for now it's easiest to simply
+	 * put this fairly direct code in line ...
+	 */
+	fwr->op_pkd = htonl(FW_WR_OP_V(FW_FILTER_WR));
+	fwr->len16_pkd = htonl(FW_WR_LEN16_V(sizeof(*fwr) / 16));
+	fwr->tid_to_iq =
+		htonl(FW_FILTER_WR_TID_V(ftid) |
+		      FW_FILTER_WR_RQTYPE_V(f->fs.type) |
+		      FW_FILTER_WR_NOREPLY_V(0) |
+		      FW_FILTER_WR_IQ_V(f->fs.iq));
+	fwr->del_filter_to_l2tix =
+		htonl(FW_FILTER_WR_RPTTID_V(f->fs.rpttid) |
+		      FW_FILTER_WR_DROP_V(f->fs.action == FILTER_DROP) |
+		      FW_FILTER_WR_DIRSTEER_V(f->fs.dirsteer) |
+		      FW_FILTER_WR_MASKHASH_V(f->fs.maskhash) |
+		      FW_FILTER_WR_DIRSTEERHASH_V(f->fs.dirsteerhash) |
+		      FW_FILTER_WR_LPBK_V(f->fs.action == FILTER_SWITCH) |
+		      FW_FILTER_WR_DMAC_V(f->fs.newdmac) |
+		      FW_FILTER_WR_SMAC_V(f->fs.newsmac) |
+		      FW_FILTER_WR_INSVLAN_V(f->fs.newvlan == VLAN_INSERT ||
+					     f->fs.newvlan == VLAN_REWRITE) |
+		      FW_FILTER_WR_RMVLAN_V(f->fs.newvlan == VLAN_REMOVE ||
+					    f->fs.newvlan == VLAN_REWRITE) |
+		      FW_FILTER_WR_HITCNTS_V(f->fs.hitcnts) |
+		      FW_FILTER_WR_TXCHAN_V(f->fs.eport) |
+		      FW_FILTER_WR_PRIO_V(f->fs.prio) |
+		      FW_FILTER_WR_L2TIX_V(f->l2t ? f->l2t->idx : 0));
+	fwr->ethtype = htons(f->fs.val.ethtype);
+	fwr->ethtypem = htons(f->fs.mask.ethtype);
+	fwr->frag_to_ovlan_vldm =
+		(FW_FILTER_WR_FRAG_V(f->fs.val.frag) |
+		 FW_FILTER_WR_FRAGM_V(f->fs.mask.frag) |
+		 FW_FILTER_WR_IVLAN_VLD_V(f->fs.val.ivlan_vld) |
+		 FW_FILTER_WR_OVLAN_VLD_V(f->fs.val.ovlan_vld) |
+		 FW_FILTER_WR_IVLAN_VLDM_V(f->fs.mask.ivlan_vld) |
+		 FW_FILTER_WR_OVLAN_VLDM_V(f->fs.mask.ovlan_vld));
+	fwr->smac_sel = 0;
+	fwr->rx_chan_rx_rpl_iq =
+		htons(FW_FILTER_WR_RX_CHAN_V(0) |
+		      FW_FILTER_WR_RX_RPL_IQ_V(adapter->sge.fw_evtq.abs_id));
+	fwr->maci_to_matchtypem =
+		htonl(FW_FILTER_WR_MACI_V(f->fs.val.macidx) |
+		      FW_FILTER_WR_MACIM_V(f->fs.mask.macidx) |
+		      FW_FILTER_WR_FCOE_V(f->fs.val.fcoe) |
+		      FW_FILTER_WR_FCOEM_V(f->fs.mask.fcoe) |
+		      FW_FILTER_WR_PORT_V(f->fs.val.iport) |
+		      FW_FILTER_WR_PORTM_V(f->fs.mask.iport) |
+		      FW_FILTER_WR_MATCHTYPE_V(f->fs.val.matchtype) |
+		      FW_FILTER_WR_MATCHTYPEM_V(f->fs.mask.matchtype));
+	fwr->ptcl = f->fs.val.proto;
+	fwr->ptclm = f->fs.mask.proto;
+	fwr->ttyp = f->fs.val.tos;
+	fwr->ttypm = f->fs.mask.tos;
+	fwr->ivlan = htons(f->fs.val.ivlan);
+	fwr->ivlanm = htons(f->fs.mask.ivlan);
+	fwr->ovlan = htons(f->fs.val.ovlan);
+	fwr->ovlanm = htons(f->fs.mask.ovlan);
+	memcpy(fwr->lip, f->fs.val.lip, sizeof(fwr->lip));
+	memcpy(fwr->lipm, f->fs.mask.lip, sizeof(fwr->lipm));
+	memcpy(fwr->fip, f->fs.val.fip, sizeof(fwr->fip));
+	memcpy(fwr->fipm, f->fs.mask.fip, sizeof(fwr->fipm));
+	fwr->lp = htons(f->fs.val.lport);
+	fwr->lpm = htons(f->fs.mask.lport);
+	fwr->fp = htons(f->fs.val.fport);
+	fwr->fpm = htons(f->fs.mask.fport);
+	if (f->fs.newsmac)
+		memcpy(fwr->sma, f->fs.smac, sizeof(fwr->sma));
+
+	/* Mark the filter as "pending" and ship off the Filter Work Request.
+	 * When we get the Work Request Reply we'll clear the pending status.
+	 */
+	f->pending = 1;
+	set_wr_txq(skb, CPL_PRIORITY_CONTROL, f->fs.val.iport & 0x3);
+	t4_ofld_send(adapter, skb);
+	return 0;
+}
+
+/* Return an error number if the indicated filter isn't writable ...
+ */
+int writable_filter(struct filter_entry *f)
+{
+	if (f->locked)
+		return -EPERM;
+	if (f->pending)
+		return -EBUSY;
+
+	return 0;
+}
+
+/* Delete the filter at the specified index (if valid).  The checks for all
+ * the common problems with doing this like the filter being locked, currently
+ * pending in another operation, etc.
+ */
+int delete_filter(struct adapter *adapter, unsigned int fidx)
+{
+	struct filter_entry *f;
+	int ret;
+
+	if (fidx >= adapter->tids.nftids + adapter->tids.nsftids)
+		return -EINVAL;
+
+	f = &adapter->tids.ftid_tab[fidx];
+	ret = writable_filter(f);
+	if (ret)
+		return ret;
+	if (f->valid)
+		return del_filter_wr(adapter, fidx);
+
+	return 0;
+}
+
+/* Clear a filter and release any of its resources that we own.  This also
+ * clears the filter's "pending" status.
+ */
+void clear_filter(struct adapter *adap, struct filter_entry *f)
+{
+	/* If the new or old filter have loopback rewriteing rules then we'll
+	 * need to free any existing Layer Two Table (L2T) entries of the old
+	 * filter rule.  The firmware will handle freeing up any Source MAC
+	 * Table (SMT) entries used for rewriting Source MAC Addresses in
+	 * loopback rules.
+	 */
+	if (f->l2t)
+		cxgb4_l2t_release(f->l2t);
+
+	/* The zeroing of the filter rule below clears the filter valid,
+	 * pending, locked flags, l2t pointer, etc. so it's all we need for
+	 * this operation.
+	 */
+	memset(f, 0, sizeof(*f));
+}
+
+/* Handle a filter write/deletion reply.
+ */
+void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
+{
+	unsigned int idx = GET_TID(rpl);
+	unsigned int nidx = idx - adap->tids.ftid_base;
+	unsigned int ret;
+	struct filter_entry *f;
+
+	if (idx >= adap->tids.ftid_base && nidx <
+	   (adap->tids.nftids + adap->tids.nsftids)) {
+		idx = nidx;
+		ret = TCB_COOKIE_G(rpl->cookie);
+		f = &adap->tids.ftid_tab[idx];
+
+		if (ret == FW_FILTER_WR_FLT_DELETED) {
+			/* Clear the filter when we get confirmation from the
+			 * hardware that the filter has been deleted.
+			 */
+			clear_filter(adap, f);
+		} else if (ret == FW_FILTER_WR_SMT_TBL_FULL) {
+			dev_err(adap->pdev_dev, "filter %u setup failed due to full SMT\n",
+				idx);
+			clear_filter(adap, f);
+		} else if (ret == FW_FILTER_WR_FLT_ADDED) {
+			f->smtidx = (be64_to_cpu(rpl->oldval) >> 24) & 0xff;
+			f->pending = 0;  /* asynchronous setup completed */
+			f->valid = 1;
+		} else {
+			/* Something went wrong.  Issue a warning about the
+			 * problem and clear everything out.
+			 */
+			dev_err(adap->pdev_dev, "filter %u setup failed with error %u\n",
+				idx, ret);
+			clear_filter(adap, f);
+		}
+	}
+}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
new file mode 100644
index 0000000..f6bd0bf
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CXGB4_FILTER_H
+#define __CXGB4_FILTER_H
+
+#include "t4_msg.h"
+
+void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl);
+void clear_filter(struct adapter *adap, struct filter_entry *f);
+
+int set_filter_wr(struct adapter *adapter, int fidx);
+int delete_filter(struct adapter *adapter, unsigned int fidx);
+
+int writable_filter(struct filter_entry *f);
+#endif /* __CXGB4_FILTER_H */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 44cc976..00f4d7f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -67,6 +67,7 @@
 #include <linux/crash_dump.h>
 
 #include "cxgb4.h"
+#include "cxgb4_filter.h"
 #include "t4_regs.h"
 #include "t4_values.h"
 #include "t4_msg.h"
@@ -87,30 +88,6 @@ char cxgb4_driver_name[] = KBUILD_MODNAME;
 const char cxgb4_driver_version[] = DRV_VERSION;
 #define DRV_DESC "Chelsio T4/T5/T6 Network Driver"
 
-/* Host shadow copy of ingress filter entry.  This is in host native format
- * and doesn't match the ordering or bit order, etc. of the hardware of the
- * firmware command.  The use of bit-field structure elements is purely to
- * remind ourselves of the field size limitations and save memory in the case
- * where the filter table is large.
- */
-struct filter_entry {
-	/* Administrative fields for filter.
-	 */
-	u32 valid:1;            /* filter allocated and valid */
-	u32 locked:1;           /* filter is administratively locked */
-
-	u32 pending:1;          /* filter action is pending firmware reply */
-	u32 smtidx:8;           /* Source MAC Table index for smac */
-	struct l2t_entry *l2t;  /* Layer Two Table entry for dmac */
-
-	/* The filter itself.  Most of this is a straight copy of information
-	 * provided by the extended ioctl().  Some fields are translated to
-	 * internal forms -- for instance the Ingress Queue ID passed in from
-	 * the ioctl() is translated into the Absolute Ingress Queue ID.
-	 */
-	struct ch_filter_specification fs;
-};
-
 #define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \
 			 NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\
 			 NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)
@@ -532,66 +509,6 @@ static void dcb_rpl(struct adapter *adap, const struct fw_port_cmd *pcmd)
 }
 #endif /* CONFIG_CHELSIO_T4_DCB */
 
-/* Clear a filter and release any of its resources that we own.  This also
- * clears the filter's "pending" status.
- */
-static void clear_filter(struct adapter *adap, struct filter_entry *f)
-{
-	/* If the new or old filter have loopback rewriteing rules then we'll
-	 * need to free any existing Layer Two Table (L2T) entries of the old
-	 * filter rule.  The firmware will handle freeing up any Source MAC
-	 * Table (SMT) entries used for rewriting Source MAC Addresses in
-	 * loopback rules.
-	 */
-	if (f->l2t)
-		cxgb4_l2t_release(f->l2t);
-
-	/* The zeroing of the filter rule below clears the filter valid,
-	 * pending, locked flags, l2t pointer, etc. so it's all we need for
-	 * this operation.
-	 */
-	memset(f, 0, sizeof(*f));
-}
-
-/* Handle a filter write/deletion reply.
- */
-static void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
-{
-	unsigned int idx = GET_TID(rpl);
-	unsigned int nidx = idx - adap->tids.ftid_base;
-	unsigned int ret;
-	struct filter_entry *f;
-
-	if (idx >= adap->tids.ftid_base && nidx <
-	   (adap->tids.nftids + adap->tids.nsftids)) {
-		idx = nidx;
-		ret = TCB_COOKIE_G(rpl->cookie);
-		f = &adap->tids.ftid_tab[idx];
-
-		if (ret == FW_FILTER_WR_FLT_DELETED) {
-			/* Clear the filter when we get confirmation from the
-			 * hardware that the filter has been deleted.
-			 */
-			clear_filter(adap, f);
-		} else if (ret == FW_FILTER_WR_SMT_TBL_FULL) {
-			dev_err(adap->pdev_dev, "filter %u setup failed due to full SMT\n",
-				idx);
-			clear_filter(adap, f);
-		} else if (ret == FW_FILTER_WR_FLT_ADDED) {
-			f->smtidx = (be64_to_cpu(rpl->oldval) >> 24) & 0xff;
-			f->pending = 0;  /* asynchronous setup completed */
-			f->valid = 1;
-		} else {
-			/* Something went wrong.  Issue a warning about the
-			 * problem and clear everything out.
-			 */
-			dev_err(adap->pdev_dev, "filter %u setup failed with error %u\n",
-				idx, ret);
-			clear_filter(adap, f);
-		}
-	}
-}
-
 /* Response queue handler for the FW event queue.
  */
 static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp,
@@ -1198,151 +1115,6 @@ void t4_free_mem(void *addr)
 	kvfree(addr);
 }
 
-/* Send a Work Request to write the filter at a specified index.  We construct
- * a Firmware Filter Work Request to have the work done and put the indicated
- * filter into "pending" mode which will prevent any further actions against
- * it till we get a reply from the firmware on the completion status of the
- * request.
- */
-static int set_filter_wr(struct adapter *adapter, int fidx)
-{
-	struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
-	struct sk_buff *skb;
-	struct fw_filter_wr *fwr;
-	unsigned int ftid;
-
-	skb = alloc_skb(sizeof(*fwr), GFP_KERNEL);
-	if (!skb)
-		return -ENOMEM;
-
-	/* If the new filter requires loopback Destination MAC and/or VLAN
-	 * rewriting then we need to allocate a Layer 2 Table (L2T) entry for
-	 * the filter.
-	 */
-	if (f->fs.newdmac || f->fs.newvlan) {
-		/* allocate L2T entry for new filter */
-		f->l2t = t4_l2t_alloc_switching(adapter, f->fs.vlan,
-						f->fs.eport, f->fs.dmac);
-		if (f->l2t == NULL) {
-			kfree_skb(skb);
-			return -ENOMEM;
-		}
-	}
-
-	ftid = adapter->tids.ftid_base + fidx;
-
-	fwr = (struct fw_filter_wr *)__skb_put(skb, sizeof(*fwr));
-	memset(fwr, 0, sizeof(*fwr));
-
-	/* It would be nice to put most of the following in t4_hw.c but most
-	 * of the work is translating the cxgbtool ch_filter_specification
-	 * into the Work Request and the definition of that structure is
-	 * currently in cxgbtool.h which isn't appropriate to pull into the
-	 * common code.  We may eventually try to come up with a more neutral
-	 * filter specification structure but for now it's easiest to simply
-	 * put this fairly direct code in line ...
-	 */
-	fwr->op_pkd = htonl(FW_WR_OP_V(FW_FILTER_WR));
-	fwr->len16_pkd = htonl(FW_WR_LEN16_V(sizeof(*fwr)/16));
-	fwr->tid_to_iq =
-		htonl(FW_FILTER_WR_TID_V(ftid) |
-		      FW_FILTER_WR_RQTYPE_V(f->fs.type) |
-		      FW_FILTER_WR_NOREPLY_V(0) |
-		      FW_FILTER_WR_IQ_V(f->fs.iq));
-	fwr->del_filter_to_l2tix =
-		htonl(FW_FILTER_WR_RPTTID_V(f->fs.rpttid) |
-		      FW_FILTER_WR_DROP_V(f->fs.action == FILTER_DROP) |
-		      FW_FILTER_WR_DIRSTEER_V(f->fs.dirsteer) |
-		      FW_FILTER_WR_MASKHASH_V(f->fs.maskhash) |
-		      FW_FILTER_WR_DIRSTEERHASH_V(f->fs.dirsteerhash) |
-		      FW_FILTER_WR_LPBK_V(f->fs.action == FILTER_SWITCH) |
-		      FW_FILTER_WR_DMAC_V(f->fs.newdmac) |
-		      FW_FILTER_WR_SMAC_V(f->fs.newsmac) |
-		      FW_FILTER_WR_INSVLAN_V(f->fs.newvlan == VLAN_INSERT ||
-					     f->fs.newvlan == VLAN_REWRITE) |
-		      FW_FILTER_WR_RMVLAN_V(f->fs.newvlan == VLAN_REMOVE ||
-					    f->fs.newvlan == VLAN_REWRITE) |
-		      FW_FILTER_WR_HITCNTS_V(f->fs.hitcnts) |
-		      FW_FILTER_WR_TXCHAN_V(f->fs.eport) |
-		      FW_FILTER_WR_PRIO_V(f->fs.prio) |
-		      FW_FILTER_WR_L2TIX_V(f->l2t ? f->l2t->idx : 0));
-	fwr->ethtype = htons(f->fs.val.ethtype);
-	fwr->ethtypem = htons(f->fs.mask.ethtype);
-	fwr->frag_to_ovlan_vldm =
-		(FW_FILTER_WR_FRAG_V(f->fs.val.frag) |
-		 FW_FILTER_WR_FRAGM_V(f->fs.mask.frag) |
-		 FW_FILTER_WR_IVLAN_VLD_V(f->fs.val.ivlan_vld) |
-		 FW_FILTER_WR_OVLAN_VLD_V(f->fs.val.ovlan_vld) |
-		 FW_FILTER_WR_IVLAN_VLDM_V(f->fs.mask.ivlan_vld) |
-		 FW_FILTER_WR_OVLAN_VLDM_V(f->fs.mask.ovlan_vld));
-	fwr->smac_sel = 0;
-	fwr->rx_chan_rx_rpl_iq =
-		htons(FW_FILTER_WR_RX_CHAN_V(0) |
-		      FW_FILTER_WR_RX_RPL_IQ_V(adapter->sge.fw_evtq.abs_id));
-	fwr->maci_to_matchtypem =
-		htonl(FW_FILTER_WR_MACI_V(f->fs.val.macidx) |
-		      FW_FILTER_WR_MACIM_V(f->fs.mask.macidx) |
-		      FW_FILTER_WR_FCOE_V(f->fs.val.fcoe) |
-		      FW_FILTER_WR_FCOEM_V(f->fs.mask.fcoe) |
-		      FW_FILTER_WR_PORT_V(f->fs.val.iport) |
-		      FW_FILTER_WR_PORTM_V(f->fs.mask.iport) |
-		      FW_FILTER_WR_MATCHTYPE_V(f->fs.val.matchtype) |
-		      FW_FILTER_WR_MATCHTYPEM_V(f->fs.mask.matchtype));
-	fwr->ptcl = f->fs.val.proto;
-	fwr->ptclm = f->fs.mask.proto;
-	fwr->ttyp = f->fs.val.tos;
-	fwr->ttypm = f->fs.mask.tos;
-	fwr->ivlan = htons(f->fs.val.ivlan);
-	fwr->ivlanm = htons(f->fs.mask.ivlan);
-	fwr->ovlan = htons(f->fs.val.ovlan);
-	fwr->ovlanm = htons(f->fs.mask.ovlan);
-	memcpy(fwr->lip, f->fs.val.lip, sizeof(fwr->lip));
-	memcpy(fwr->lipm, f->fs.mask.lip, sizeof(fwr->lipm));
-	memcpy(fwr->fip, f->fs.val.fip, sizeof(fwr->fip));
-	memcpy(fwr->fipm, f->fs.mask.fip, sizeof(fwr->fipm));
-	fwr->lp = htons(f->fs.val.lport);
-	fwr->lpm = htons(f->fs.mask.lport);
-	fwr->fp = htons(f->fs.val.fport);
-	fwr->fpm = htons(f->fs.mask.fport);
-	if (f->fs.newsmac)
-		memcpy(fwr->sma, f->fs.smac, sizeof(fwr->sma));
-
-	/* Mark the filter as "pending" and ship off the Filter Work Request.
-	 * When we get the Work Request Reply we'll clear the pending status.
-	 */
-	f->pending = 1;
-	set_wr_txq(skb, CPL_PRIORITY_CONTROL, f->fs.val.iport & 0x3);
-	t4_ofld_send(adapter, skb);
-	return 0;
-}
-
-/* Delete the filter at a specified index.
- */
-static int del_filter_wr(struct adapter *adapter, int fidx)
-{
-	struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
-	struct sk_buff *skb;
-	struct fw_filter_wr *fwr;
-	unsigned int len, ftid;
-
-	len = sizeof(*fwr);
-	ftid = adapter->tids.ftid_base + fidx;
-
-	skb = alloc_skb(len, GFP_KERNEL);
-	if (!skb)
-		return -ENOMEM;
-
-	fwr = (struct fw_filter_wr *)__skb_put(skb, len);
-	t4_mk_filtdelwr(ftid, fwr, adapter->sge.fw_evtq.abs_id);
-
-	/* Mark the filter as "pending" and ship off the Filter Work Request.
-	 * When we get the Work Request Reply we'll clear the pending status.
-	 */
-	f->pending = 1;
-	t4_mgmt_tx(adapter, skb);
-	return 0;
-}
-
 static u16 cxgb_select_queue(struct net_device *dev, struct sk_buff *skb,
 			     void *accel_priv, select_queue_fallback_t fallback)
 {
@@ -2830,40 +2602,6 @@ static int cxgb_close(struct net_device *dev)
 	return t4_enable_vi(adapter, adapter->pf, pi->viid, false, false);
 }
 
-/* Return an error number if the indicated filter isn't writable ...
- */
-static int writable_filter(struct filter_entry *f)
-{
-	if (f->locked)
-		return -EPERM;
-	if (f->pending)
-		return -EBUSY;
-
-	return 0;
-}
-
-/* Delete the filter at the specified index (if valid).  The checks for all
- * the common problems with doing this like the filter being locked, currently
- * pending in another operation, etc.
- */
-static int delete_filter(struct adapter *adapter, unsigned int fidx)
-{
-	struct filter_entry *f;
-	int ret;
-
-	if (fidx >= adapter->tids.nftids + adapter->tids.nsftids)
-		return -EINVAL;
-
-	f = &adapter->tids.ftid_tab[fidx];
-	ret = writable_filter(f);
-	if (ret)
-		return ret;
-	if (f->valid)
-		return del_filter_wr(adapter, fidx);
-
-	return 0;
-}
-
 int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid,
 		__be32 sip, __be16 sport, __be16 vlan,
 		unsigned int queue, unsigned char port, unsigned char mask)
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH net-next 2/7] cxgb4: add common api support for configuring filters
  2016-09-12  8:12 [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
  2016-09-12  8:12 ` [PATCH net-next 1/7] cxgb4: move common filter code to separate file Rahul Lakkireddy
@ 2016-09-12  8:12 ` Rahul Lakkireddy
  2016-09-12  8:57   ` Jiri Pirko
  2016-09-12  8:12 ` [PATCH net-next 3/7] cxgb4: add debugfs support to dump filter debug logs Rahul Lakkireddy
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Rahul Lakkireddy @ 2016-09-12  8:12 UTC (permalink / raw)
  To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy

Enable filters for non-offload configuration and add common api support
for setting and deleting filters in LE-TCAM region of the hardware.

IPv4 filters occupy one slot.  IPv6 filters occupy 4 slots and must
be on a 4-slot boundary.  IPv4 filters can not occupy a slot belonging
to IPv6 and the vice-versa is also true.

Filters are set and deleted asynchronously.  Use completion to wait
for reply from firmware in order to allow for synchronization if needed.

Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
 drivers/net/ethernet/chelsio/cxgb4/cxgb4.h        |   3 +
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 424 +++++++++++++++++++++-
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h |   1 +
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c   |  33 +-
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h    |  23 +-
 5 files changed, 453 insertions(+), 31 deletions(-)

diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 053976f..fbd593a 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -1055,7 +1055,10 @@ struct filter_entry {
 
 	u32 pending:1;          /* filter action is pending firmware reply */
 	u32 smtidx:8;           /* Source MAC Table index for smac */
+	struct filter_ctx *ctx; /* Caller's completion hook */
 	struct l2t_entry *l2t;  /* Layer Two Table entry for dmac */
+	struct net_device *dev; /* Associated net device */
+	u32 tid;                /* This will store the actual tid */
 
 	/* The filter itself.  Most of this is a straight copy of information
 	 * provided by the extended ioctl().  Some fields are translated to
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
index 2e86902..490bd94 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
@@ -33,10 +33,138 @@
  */
 
 #include "cxgb4.h"
+#include "t4_regs.h"
 #include "l2t.h"
 #include "t4fw_api.h"
 #include "cxgb4_filter.h"
 
+/* Validate filter spec against configuration done on the card.
+ */
+static int validate_filter(struct net_device *dev,
+			   struct ch_filter_specification *fs)
+{
+	struct adapter *adapter = netdev2adap(dev);
+	u32 fconf, iconf;
+
+	/* Check for unconfigured fields being used. */
+	fconf = adapter->params.tp.vlan_pri_map;
+	iconf = adapter->params.tp.ingress_config;
+
+	#define S(_field) \
+		(fs->val._field || fs->mask._field)
+	#define U(_mask, _field) \
+		(!(fconf & (_mask)) && S(_field))
+
+	if (U(FCOE_F, fcoe) || U(PORT_F, iport) || U(TOS_F, tos) ||
+	    U(ETHERTYPE_F, ethtype) || U(MACMATCH_F, macidx) ||
+	    U(MPSHITTYPE_F, matchtype) || U(FRAGMENTATION_F, frag) ||
+	    U(PROTOCOL_F, proto) ||
+	    U(VNIC_ID_F, pfvf_vld) ||
+	    U(VNIC_ID_F, ovlan_vld) ||
+	    U(VLAN_F, ivlan_vld))
+		return -EOPNOTSUPP;
+
+	/* T4 inconveniently uses the same FT_VNIC_ID_W bits for both the Outer
+	 * VLAN Tag and PF/VF/VFvld fields based on VNIC_F being set
+	 * in TP_INGRESS_CONFIG.  Hense the somewhat crazy checks
+	 * below.  Additionally, since the T4 firmware interface also
+	 * carries that overlap, we need to translate any PF/VF
+	 * specification into that internal format below.
+	 */
+	if (S(pfvf_vld) && S(ovlan_vld))
+		return -EOPNOTSUPP;
+	if ((S(pfvf_vld) && !(iconf & VNIC_F)) ||
+	    (S(ovlan_vld) && (iconf & VNIC_F)))
+		return -EOPNOTSUPP;
+	if (fs->val.pf > 0x7 || fs->val.vf > 0x7f)
+		return -ERANGE;
+	fs->mask.pf &= 0x7;
+	fs->mask.vf &= 0x7f;
+
+	#undef S
+	#undef U
+
+	/* If the user is requesting that the filter action loop
+	 * matching packets back out one of our ports, make sure that
+	 * the egress port is in range.
+	 */
+	if (fs->action == FILTER_SWITCH &&
+	    fs->eport >= adapter->params.nports)
+		return -ERANGE;
+
+	/* Don't allow various trivially obvious bogus out-of-range values... */
+	if (fs->val.iport >= adapter->params.nports)
+		return -ERANGE;
+
+	/* T4 doesn't support removing VLAN Tags for loop back filters. */
+	if (is_t4(adapter->params.chip) &&
+	    fs->action == FILTER_SWITCH &&
+	    (fs->newvlan == VLAN_REMOVE ||
+	     fs->newvlan == VLAN_REWRITE))
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static unsigned int get_filter_steerq(struct net_device *dev,
+				      struct ch_filter_specification *fs)
+{
+	struct adapter *adapter = netdev2adap(dev);
+	unsigned int iq;
+
+	/* If the user has requested steering matching Ingress Packets
+	 * to a specific Queue Set, we need to make sure it's in range
+	 * for the port and map that into the Absolute Queue ID of the
+	 * Queue Set's Response Queue.
+	 */
+	if (!fs->dirsteer) {
+		if (fs->iq)
+			return -EINVAL;
+		iq = 0;
+	} else {
+		struct port_info *pi = netdev_priv(dev);
+
+		/* If the iq id is greater than the number of qsets,
+		 * then assume it is an absolute qid.
+		 */
+		if (fs->iq < pi->nqsets)
+			iq = adapter->sge.ethrxq[pi->first_qset +
+						 fs->iq].rspq.abs_id;
+		else
+			iq = fs->iq;
+	}
+
+	return iq;
+}
+
+static int cxgb4_set_ftid(struct tid_info *t, int fidx, int family)
+{
+	spin_lock_bh(&t->ftid_lock);
+
+	if (test_bit(fidx, t->ftid_bmap)) {
+		spin_unlock_bh(&t->ftid_lock);
+		return -EBUSY;
+	}
+
+	if (family == PF_INET)
+		__set_bit(fidx, t->ftid_bmap);
+	else
+		bitmap_allocate_region(t->ftid_bmap, fidx, 2);
+
+	spin_unlock_bh(&t->ftid_lock);
+	return 0;
+}
+
+static void cxgb4_clear_ftid(struct tid_info *t, int fidx, int family)
+{
+	spin_lock_bh(&t->ftid_lock);
+	if (family == PF_INET)
+		__clear_bit(fidx, t->ftid_bmap);
+	else
+		bitmap_release_region(t->ftid_bmap, fidx, 2);
+	spin_unlock_bh(&t->ftid_lock);
+}
+
 /* Delete the filter at a specified index.
  */
 static int del_filter_wr(struct adapter *adapter, int fidx)
@@ -44,17 +172,16 @@ static int del_filter_wr(struct adapter *adapter, int fidx)
 	struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
 	struct sk_buff *skb;
 	struct fw_filter_wr *fwr;
-	unsigned int len, ftid;
+	unsigned int len;
 
 	len = sizeof(*fwr);
-	ftid = adapter->tids.ftid_base + fidx;
 
 	skb = alloc_skb(len, GFP_KERNEL);
 	if (!skb)
 		return -ENOMEM;
 
 	fwr = (struct fw_filter_wr *)__skb_put(skb, len);
-	t4_mk_filtdelwr(ftid, fwr, adapter->sge.fw_evtq.abs_id);
+	t4_mk_filtdelwr(f->tid, fwr, adapter->sge.fw_evtq.abs_id);
 
 	/* Mark the filter as "pending" and ship off the Filter Work Request.
 	 * When we get the Work Request Reply we'll clear the pending status.
@@ -75,7 +202,6 @@ int set_filter_wr(struct adapter *adapter, int fidx)
 	struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
 	struct sk_buff *skb;
 	struct fw_filter_wr *fwr;
-	unsigned int ftid;
 
 	skb = alloc_skb(sizeof(*fwr), GFP_KERNEL);
 	if (!skb)
@@ -95,8 +221,6 @@ int set_filter_wr(struct adapter *adapter, int fidx)
 		}
 	}
 
-	ftid = adapter->tids.ftid_base + fidx;
-
 	fwr = (struct fw_filter_wr *)__skb_put(skb, sizeof(*fwr));
 	memset(fwr, 0, sizeof(*fwr));
 
@@ -111,7 +235,7 @@ int set_filter_wr(struct adapter *adapter, int fidx)
 	fwr->op_pkd = htonl(FW_WR_OP_V(FW_FILTER_WR));
 	fwr->len16_pkd = htonl(FW_WR_LEN16_V(sizeof(*fwr) / 16));
 	fwr->tid_to_iq =
-		htonl(FW_FILTER_WR_TID_V(ftid) |
+		htonl(FW_FILTER_WR_TID_V(f->tid) |
 		      FW_FILTER_WR_RQTYPE_V(f->fs.type) |
 		      FW_FILTER_WR_NOREPLY_V(0) |
 		      FW_FILTER_WR_IQ_V(f->fs.iq));
@@ -237,34 +361,300 @@ void clear_filter(struct adapter *adap, struct filter_entry *f)
 	memset(f, 0, sizeof(*f));
 }
 
+void clear_all_filters(struct adapter *adapter)
+{
+	unsigned int i;
+
+	if (adapter->tids.ftid_tab) {
+		struct filter_entry *f = &adapter->tids.ftid_tab[0];
+		unsigned int max_ftid = adapter->tids.nftids +
+					adapter->tids.nsftids;
+
+		for (i = 0; i < max_ftid; i++, f++)
+			if (f->valid || f->pending)
+				clear_filter(adapter, f);
+	}
+}
+
+/* Fill up default masks for set match fields. */
+static void fill_default_mask(struct ch_filter_specification *fs)
+{
+	unsigned int i;
+	unsigned int lip = 0, lip_mask = 0;
+	unsigned int fip = 0, fip_mask = 0;
+
+	if (fs->val.iport && !fs->mask.iport)
+		fs->mask.iport |= ~0;
+	if (fs->val.fcoe && !fs->mask.fcoe)
+		fs->mask.fcoe |= ~0;
+	if (fs->val.matchtype && !fs->mask.matchtype)
+		fs->mask.matchtype |= ~0;
+	if (fs->val.macidx && !fs->mask.macidx)
+		fs->mask.macidx |= ~0;
+	if (fs->val.ethtype && !fs->mask.ethtype)
+		fs->mask.ethtype |= ~0;
+	if (fs->val.ivlan && !fs->mask.ivlan)
+		fs->mask.ivlan |= ~0;
+	if (fs->val.ovlan && !fs->mask.ovlan)
+		fs->mask.ovlan |= ~0;
+	if (fs->val.frag && !fs->mask.frag)
+		fs->mask.frag |= ~0;
+	if (fs->val.tos && !fs->mask.tos)
+		fs->mask.tos |= ~0;
+	if (fs->val.proto && !fs->mask.proto)
+		fs->mask.proto |= ~0;
+
+	for (i = 0; i < ARRAY_SIZE(fs->val.lip); i++) {
+		lip |= fs->val.lip[i];
+		lip_mask |= fs->mask.lip[i];
+		fip |= fs->val.fip[i];
+		fip_mask |= fs->mask.fip[i];
+	}
+
+	if (lip && !lip_mask)
+		memset(fs->mask.lip, ~0, sizeof(fs->mask.lip));
+
+	if (fip && !fip_mask)
+		memset(fs->mask.fip, ~0, sizeof(fs->mask.lip));
+
+	if (fs->val.lport && !fs->mask.lport)
+		fs->mask.lport = ~0;
+	if (fs->val.fport && !fs->mask.fport)
+		fs->mask.fport = ~0;
+}
+
+/* Check a Chelsio Filter Request for validity, convert it into our internal
+ * format and send it to the hardware.  Return 0 on success, an error number
+ * otherwise.  We attach any provided filter operation context to the internal
+ * filter specification in order to facilitate signaling completion of the
+ * operation.
+ */
+int cxgb4_set_filter(struct net_device *dev, int filter_id,
+		     struct ch_filter_specification *fs,
+		     struct filter_ctx *ctx)
+{
+	struct adapter *adapter = netdev2adap(dev);
+	struct filter_entry *f;
+	u32 iconf;
+	unsigned int fidx, iq;
+	unsigned int max_fidx;
+	int ret;
+
+	max_fidx = adapter->tids.nftids;
+	if ((filter_id != (max_fidx + adapter->tids.nsftids - 1)) &&
+	    (filter_id >= max_fidx))
+		return -E2BIG;
+
+	fill_default_mask(fs);
+
+	ret = validate_filter(dev, fs);
+	if (ret)
+		return ret;
+
+	iq = get_filter_steerq(dev, fs);
+	if (iq < 0)
+		return iq;
+
+	/* IPv6 filters occupy four slots and must be aligned on
+	 * four-slot boundaries.  IPv4 filters only occupy a single
+	 * slot and have no alignment requirements but writing a new
+	 * IPv4 filter into the middle of an existing IPv6 filter
+	 * requires clearing the old IPv6 filter and hence we prevent
+	 * insertion.
+	 */
+	if (fs->type == 0) { /* IPv4 */
+		/* If our IPv4 filter isn't being written to a
+		 * multiple of four filter index and there's an IPv6
+		 * filter at the multiple of 4 base slot, then we
+		 * prevent insertion.
+		 */
+		fidx = filter_id & ~0x3;
+		if (fidx != filter_id &&
+		    adapter->tids.ftid_tab[fidx].fs.type) {
+			f = &adapter->tids.ftid_tab[fidx];
+			if (f->valid) {
+				dev_err(adapter->pdev_dev,
+					"Invalid location. IPv6 requires 4 slots and is occupying slots %u to %u\n",
+					fidx, fidx + 3);
+				return -EINVAL;
+			}
+		}
+	} else { /* IPv6 */
+		/* Ensure that the IPv6 filter is aligned on a
+		 * multiple of 4 boundary.
+		 */
+		if (filter_id & 0x3) {
+			dev_err(adapter->pdev_dev,
+				"Invalid location. IPv6 must be aligned on a 4-slot boundary\n");
+			return -EINVAL;
+		}
+
+		/* Check all except the base overlapping IPv4 filter slots. */
+		for (fidx = filter_id + 1; fidx < filter_id + 4; fidx++) {
+			f = &adapter->tids.ftid_tab[fidx];
+			if (f->valid) {
+				dev_err(adapter->pdev_dev,
+					"Invalid location.  IPv6 requires 4 slots and an IPv4 filter exists at %u\n",
+					fidx);
+				return -EINVAL;
+			}
+		}
+	}
+
+	/* Check to make sure that provided filter index is not
+	 * already in use by someone else
+	 */
+	f = &adapter->tids.ftid_tab[filter_id];
+	if (f->valid)
+		return -EBUSY;
+
+	fidx = filter_id + adapter->tids.ftid_base;
+	ret = cxgb4_set_ftid(&adapter->tids, filter_id,
+			     fs->type ? PF_INET6 : PF_INET);
+	if (ret)
+		return ret;
+
+	/* Check to make sure the filter requested is writable ... */
+	ret = writable_filter(f);
+	if (ret) {
+		/* Clear the bits we have set above */
+		cxgb4_clear_ftid(&adapter->tids, filter_id,
+				 fs->type ? PF_INET6 : PF_INET);
+		return ret;
+	}
+
+	/* Clear out any old resources being used by the filter before
+	 * we start constructing the new filter.
+	 */
+	if (f->valid)
+		clear_filter(adapter, f);
+
+	/* Convert the filter specification into our internal format.
+	 * We copy the PF/VF specification into the Outer VLAN field
+	 * here so the rest of the code -- including the interface to
+	 * the firmware -- doesn't have to constantly do these checks.
+	 */
+	f->fs = *fs;
+	f->fs.iq = iq;
+	f->dev = dev;
+
+	iconf = adapter->params.tp.ingress_config;
+	if (iconf & VNIC_F) {
+		f->fs.val.ovlan = (fs->val.pf << 13) | fs->val.vf;
+		f->fs.mask.ovlan = (fs->mask.pf << 13) | fs->mask.vf;
+		f->fs.val.ovlan_vld = fs->val.pfvf_vld;
+		f->fs.mask.ovlan_vld = fs->mask.pfvf_vld;
+	}
+
+	/* Attempt to set the filter.  If we don't succeed, we clear
+	 * it and return the failure.
+	 */
+	f->ctx = ctx;
+	f->tid = fidx; /* Save the actual tid */
+	ret = set_filter_wr(adapter, filter_id);
+	if (ret) {
+		cxgb4_clear_ftid(&adapter->tids, filter_id,
+				 fs->type ? PF_INET6 : PF_INET);
+		clear_filter(adapter, f);
+	}
+
+	return ret;
+}
+
+/* Check a delete filter request for validity and send it to the hardware.
+ * Return 0 on success, an error number otherwise.  We attach any provided
+ * filter operation context to the internal filter specification in order to
+ * facilitate signaling completion of the operation.
+ */
+int cxgb4_del_filter(struct net_device *dev, int filter_id,
+		     struct filter_ctx *ctx)
+{
+	struct adapter *adapter = netdev2adap(dev);
+	struct filter_entry *f;
+	unsigned int max_fidx;
+	int ret = 0;
+
+	max_fidx = adapter->tids.nftids;
+	if ((filter_id != (max_fidx + adapter->tids.nsftids - 1)) &&
+	    (filter_id >= max_fidx))
+		return -E2BIG;
+
+	f = &adapter->tids.ftid_tab[filter_id];
+	ret = writable_filter(f);
+	if (ret)
+		return ret;
+
+	if (f->valid) {
+		f->ctx = ctx;
+		cxgb4_clear_ftid(&adapter->tids, filter_id,
+				 f->fs.type ? PF_INET6 : PF_INET);
+		return del_filter_wr(adapter, filter_id);
+	}
+
+	/* If the caller has passed in a Completion Context then we need to
+	 * mark it as a successful completion so they don't stall waiting
+	 * for it.
+	 */
+	if (ctx) {
+		ctx->result = 0;
+		complete(&ctx->completion);
+	}
+	return ret;
+}
+
 /* Handle a filter write/deletion reply.
  */
 void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
 {
-	unsigned int idx = GET_TID(rpl);
-	unsigned int nidx = idx - adap->tids.ftid_base;
-	unsigned int ret;
-	struct filter_entry *f;
+	unsigned int tid = GET_TID(rpl);
+	struct filter_entry *f = NULL;
+	unsigned int max_fidx;
+	int idx;
 
-	if (idx >= adap->tids.ftid_base && nidx <
-	   (adap->tids.nftids + adap->tids.nsftids)) {
-		idx = nidx;
-		ret = TCB_COOKIE_G(rpl->cookie);
+	max_fidx = adap->tids.nftids + adap->tids.nsftids;
+	/* Get the corresponding filter entry for this tid */
+	if (adap->tids.ftid_tab) {
+		/* Check this in normal filter region */
+		idx = tid - adap->tids.ftid_base;
+		if (idx >= max_fidx)
+			return;
 		f = &adap->tids.ftid_tab[idx];
+		if (f->tid != tid)
+			return;
+	}
+
+	/* We found the filter entry for this tid */
+	if (f) {
+		unsigned int ret = TCB_COOKIE_G(rpl->cookie);
+		struct filter_ctx *ctx;
+
+		/* Pull off any filter operation context attached to the
+		 * filter.
+		 */
+		ctx = f->ctx;
+		f->ctx = NULL;
 
 		if (ret == FW_FILTER_WR_FLT_DELETED) {
 			/* Clear the filter when we get confirmation from the
 			 * hardware that the filter has been deleted.
 			 */
 			clear_filter(adap, f);
+			if (ctx)
+				ctx->result = 0;
 		} else if (ret == FW_FILTER_WR_SMT_TBL_FULL) {
 			dev_err(adap->pdev_dev, "filter %u setup failed due to full SMT\n",
 				idx);
 			clear_filter(adap, f);
+			if (ctx)
+				ctx->result = -ENOMEM;
 		} else if (ret == FW_FILTER_WR_FLT_ADDED) {
 			f->smtidx = (be64_to_cpu(rpl->oldval) >> 24) & 0xff;
 			f->pending = 0;  /* asynchronous setup completed */
 			f->valid = 1;
+			if (ctx) {
+				ctx->result = 0;
+				ctx->tid = idx;
+			}
 		} else {
 			/* Something went wrong.  Issue a warning about the
 			 * problem and clear everything out.
@@ -272,6 +662,10 @@ void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
 			dev_err(adap->pdev_dev, "filter %u setup failed with error %u\n",
 				idx, ret);
 			clear_filter(adap, f);
+			if (ctx)
+				ctx->result = -EINVAL;
 		}
+		if (ctx)
+			complete(&ctx->completion);
 	}
 }
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
index f6bd0bf..23742cb 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
@@ -44,4 +44,5 @@ int set_filter_wr(struct adapter *adapter, int fidx);
 int delete_filter(struct adapter *adapter, unsigned int fidx);
 
 int writable_filter(struct filter_entry *f);
+void clear_all_filters(struct adapter *adapter);
 #endif /* __CXGB4_FILTER_H */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 00f4d7f..af07f9d 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -1498,17 +1498,20 @@ static int tid_init(struct tid_info *t)
 {
 	size_t size;
 	unsigned int stid_bmap_size;
+	unsigned int ftid_bmap_size;
 	unsigned int natids = t->natids;
+	unsigned int max_ftids = t->nftids + t->nsftids;
 	struct adapter *adap = container_of(t, struct adapter, tids);
 
 	stid_bmap_size = BITS_TO_LONGS(t->nstids + t->nsftids);
+	ftid_bmap_size = BITS_TO_LONGS(t->nftids);
 	size = t->ntids * sizeof(*t->tid_tab) +
 	       natids * sizeof(*t->atid_tab) +
 	       t->nstids * sizeof(*t->stid_tab) +
 	       t->nsftids * sizeof(*t->stid_tab) +
 	       stid_bmap_size * sizeof(long) +
-	       t->nftids * sizeof(*t->ftid_tab) +
-	       t->nsftids * sizeof(*t->ftid_tab);
+	       max_ftids * sizeof(*t->ftid_tab) +
+	       ftid_bmap_size * sizeof(long);
 
 	t->tid_tab = t4_alloc_mem(size);
 	if (!t->tid_tab)
@@ -1518,8 +1521,10 @@ static int tid_init(struct tid_info *t)
 	t->stid_tab = (struct serv_entry *)&t->atid_tab[natids];
 	t->stid_bmap = (unsigned long *)&t->stid_tab[t->nstids + t->nsftids];
 	t->ftid_tab = (struct filter_entry *)&t->stid_bmap[stid_bmap_size];
+	t->ftid_bmap = (unsigned long *)&t->ftid_tab[max_ftids];
 	spin_lock_init(&t->stid_lock);
 	spin_lock_init(&t->atid_lock);
+	spin_lock_init(&t->ftid_lock);
 
 	t->stids_in_use = 0;
 	t->sftids_in_use = 0;
@@ -1534,12 +1539,16 @@ static int tid_init(struct tid_info *t)
 			t->atid_tab[natids - 1].next = &t->atid_tab[natids];
 		t->afree = t->atid_tab;
 	}
-	bitmap_zero(t->stid_bmap, t->nstids + t->nsftids);
-	/* Reserve stid 0 for T4/T5 adapters */
-	if (!t->stid_base &&
-	    (CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5))
-		__set_bit(0, t->stid_bmap);
 
+	if (is_offload(adap)) {
+		bitmap_zero(t->stid_bmap, t->nstids + t->nsftids);
+		/* Reserve stid 0 for T4/T5 adapters */
+		if (!t->stid_base &&
+		    (CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5))
+			__set_bit(0, t->stid_bmap);
+	}
+
+	bitmap_zero(t->ftid_bmap, t->nftids);
 	return 0;
 }
 
@@ -5200,7 +5209,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 				 i);
 	}
 
-	if (is_offload(adapter) && tid_init(&adapter->tids) < 0) {
+	if (tid_init(&adapter->tids) < 0) {
 		dev_warn(&pdev->dev, "could not allocate TID table, "
 			 "continuing\n");
 		adapter->params.offload = 0;
@@ -5383,13 +5392,7 @@ static void remove_one(struct pci_dev *pdev)
 		/* If we allocated filters, free up state associated with any
 		 * valid filters ...
 		 */
-		if (adapter->tids.ftid_tab) {
-			struct filter_entry *f = &adapter->tids.ftid_tab[0];
-			for (i = 0; i < (adapter->tids.nftids +
-					adapter->tids.nsftids); i++, f++)
-				if (f->valid)
-					clear_filter(adapter, f);
-		}
+		clear_all_filters(adapter);
 
 		if (adapter->flags & FULL_INIT_DONE)
 			cxgb_down(adapter);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
index ab40372..7efb2d0 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
@@ -1,7 +1,7 @@
 /*
  * This file is part of the Chelsio T4 Ethernet driver for Linux.
  *
- * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -104,6 +104,7 @@ struct tid_info {
 	unsigned int atid_base;
 
 	struct filter_entry *ftid_tab;
+	unsigned long *ftid_bmap;
 	unsigned int nftids;
 	unsigned int ftid_base;
 	unsigned int aftid_base;
@@ -124,6 +125,8 @@ struct tid_info {
 	atomic_t tids_in_use;
 	/* TIDs in the HASH */
 	atomic_t hash_tids_in_use;
+	/* lock for setting/clearing filter bitmap */
+	spinlock_t ftid_lock;
 };
 
 static inline void *lookup_tid(const struct tid_info *t, unsigned int tid)
@@ -183,6 +186,24 @@ int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid,
 int cxgb4_remove_server_filter(const struct net_device *dev, unsigned int stid,
 			       unsigned int queue, bool ipv6);
 
+/* Filter operation context to allow callers of cxgb4_set_filter() and
+ * cxgb4_del_filter() to wait for an asynchronous completion.
+ */
+struct filter_ctx {
+	struct completion completion;	/* completion rendezvous */
+	void *closure;			/* caller's opaque information */
+	int result;			/* result of operation */
+	u32 tid;			/* to store tid */
+};
+
+struct ch_filter_specification;
+
+int cxgb4_set_filter(struct net_device *dev, int filter_id,
+		     struct ch_filter_specification *fs,
+		     struct filter_ctx *ctx);
+int cxgb4_del_filter(struct net_device *dev, int filter_id,
+		     struct filter_ctx *ctx);
+
 static inline void set_wr_txq(struct sk_buff *skb, int prio, int queue)
 {
 	skb_set_queue_mapping(skb, (queue << 1) | prio);
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH net-next 3/7] cxgb4: add debugfs support to dump filter debug logs
  2016-09-12  8:12 [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
  2016-09-12  8:12 ` [PATCH net-next 1/7] cxgb4: move common filter code to separate file Rahul Lakkireddy
  2016-09-12  8:12 ` [PATCH net-next 2/7] cxgb4: add common api support for configuring filters Rahul Lakkireddy
@ 2016-09-12  8:12 ` Rahul Lakkireddy
  2016-09-12  8:36   ` Jiri Pirko
  2016-09-12  8:12 ` [PATCH net-next 4/7] cxgb4: add parser to translate u32 filters to internal spec Rahul Lakkireddy
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Rahul Lakkireddy @ 2016-09-12  8:12 UTC (permalink / raw)
  To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy

Add debugfs support to dump filter debug information.

Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c |   4 +-
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c  | 415 +++++++++++++++++++++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h  |   2 +
 drivers/net/ethernet/chelsio/cxgb4/t4_values.h     |   5 +-
 4 files changed, 424 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
index 91fb508..72cf3de2 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
@@ -1,7 +1,7 @@
 /*
  * This file is part of the Chelsio T4 Ethernet driver for Linux.
  *
- * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -45,6 +45,7 @@
 #include "cxgb4_debugfs.h"
 #include "clip_tbl.h"
 #include "l2t.h"
+#include "cxgb4_filter.h"
 
 /* generic seq_file support for showing a table of size rows x width. */
 static void *seq_tab_get_idx(struct seq_tab *tb, loff_t pos)
@@ -3272,6 +3273,7 @@ int t4_setup_debugfs(struct adapter *adap)
 		{ "tids", &tid_info_debugfs_fops, S_IRUSR, 0},
 		{ "blocked_fl", &blocked_fl_fops, S_IRUSR | S_IWUSR, 0 },
 		{ "meminfo", &meminfo_fops, S_IRUSR, 0 },
+		{ "filters", &filters_debugfs_fops, S_IRUSR, 0 },
 	};
 
 	/* Debug FS nodes common to all T5 and later adapters.
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
index 490bd94..51b6745 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
@@ -34,6 +34,7 @@
 
 #include "cxgb4.h"
 #include "t4_regs.h"
+#include "t4_values.h"
 #include "l2t.h"
 #include "t4fw_api.h"
 #include "cxgb4_filter.h"
@@ -669,3 +670,417 @@ void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
 			complete(&ctx->completion);
 	}
 }
+
+/* Retrieve the packet count for the specified filter. */
+int cxgb4_get_filter_count(struct adapter *adapter, unsigned int fidx,
+			   u64 *c, int hash, bool get_byte)
+{
+	struct filter_entry *f;
+	unsigned int tcb_base, tcbaddr;
+	unsigned int max_ftids;
+	int ret;
+
+	tcb_base = t4_read_reg(adapter, TP_CMM_TCB_BASE_A);
+	max_ftids = adapter->tids.nftids;
+	if ((fidx != (max_ftids + adapter->tids.nsftids - 1)) &&
+	    (fidx >= max_ftids))
+		return -E2BIG;
+
+	f = &adapter->tids.ftid_tab[fidx];
+	if (!f->valid)
+		return -EINVAL;
+
+	tcbaddr = tcb_base + f->tid * TCB_SIZE;
+
+	if (is_t4(adapter->params.chip)) {
+		/* For T4, the Filter Packet Hit Count is maintained as a
+		 * 64-bit Big Endian value in the TCB fields
+		 * {t_rtt_ts_recent_age, t_rtseq_recent} ... The format in
+		 * memory is swizzled/mapped in a manner such that instead
+		 * of having this 64-bit counter show up at offset 24
+		 * ((TCB_T_RTT_TS_RECENT_AGE_W == 6) * sizeof(u32)), it
+		 * actually shows up at offset 16. Hence the constant "4"
+		 * below instead of TCB_T_RTT_TS_RECENT_AGE_W.
+		 */
+		if (get_byte) {
+			unsigned int word_offset = 4;
+			__be64 be64_byte_count;
+
+			spin_lock(&adapter->win0_lock);
+			ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
+					   tcbaddr +
+					   (word_offset * sizeof(__be32)),
+					   sizeof(be64_byte_count),
+					   &be64_byte_count,
+					   T4_MEMORY_READ);
+			spin_unlock(&adapter->win0_lock);
+			if (ret < 0)
+				return ret;
+			*c = be64_to_cpu(be64_byte_count);
+		} else {
+			unsigned int word_offset = 4;
+			__be64 be64_count;
+
+			spin_lock(&adapter->win0_lock);
+			ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
+					   tcbaddr +
+					   (word_offset * sizeof(__be32)),
+					   sizeof(be64_count),
+					   (__be32 *)&be64_count,
+					   T4_MEMORY_READ);
+			spin_unlock(&adapter->win0_lock);
+			if (ret < 0)
+				return ret;
+			*c = be64_to_cpu(be64_count);
+		}
+	} else {
+		/* For T5, the Filter Packet Hit Count is maintained as a
+		 * 32-bit Big Endian value in the TCB field {timestamp}.
+		 * Instead of the filter hit count showing up at offset 20
+		 * ((TCB_TIMESTAMP_W == 5) * sizeof(u32)), it actually shows
+		 * up at offset 24.  Hence the constant "6" below.
+		 */
+		if (get_byte) {
+			unsigned int word_offset = 4;
+			__be64 be64_byte_count;
+
+			spin_lock(&adapter->win0_lock);
+			ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
+					   tcbaddr +
+					   (word_offset * sizeof(__be32)),
+					   sizeof(be64_byte_count),
+					   &be64_byte_count,
+					   T4_MEMORY_READ);
+			spin_unlock(&adapter->win0_lock);
+			if (ret < 0)
+				return ret;
+			*c = be64_to_cpu(be64_byte_count);
+		} else {
+			unsigned int word_offset = 6;
+			__be32 be32_count;
+
+			spin_lock(&adapter->win0_lock);
+			ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
+					   tcbaddr +
+					   (word_offset * sizeof(__be32)),
+					   sizeof(be32_count), &be32_count,
+					   T4_MEMORY_READ);
+			spin_unlock(&adapter->win0_lock);
+			if (ret < 0)
+				return ret;
+			*c = (u64)be32_to_cpu(be32_count);
+		}
+	}
+
+	return 0;
+}
+
+/* Filter Table. */
+static void filters_show_ipaddr(struct seq_file *seq,
+				int type, u8 *addr, u8 *addrm)
+{
+	int noctets, octet;
+
+	seq_puts(seq, " ");
+	if (type == 0) {
+		noctets = 4;
+		seq_printf(seq, "%48s", " ");
+	} else {
+		noctets = 16;
+	}
+
+	for (octet = 0; octet < noctets; octet++)
+		seq_printf(seq, "%02x", addr[octet]);
+	seq_puts(seq, "/");
+	for (octet = 0; octet < noctets; octet++)
+		seq_printf(seq, "%02x", addrm[octet]);
+}
+
+static void filters_display(struct seq_file *seq, unsigned int fidx,
+			    struct filter_entry *f, int hash)
+{
+	struct adapter *adapter = seq->private;
+	u32 fconf = adapter->params.tp.vlan_pri_map;
+	u32 tpiconf = adapter->params.tp.ingress_config;
+	int i;
+
+	/* Filter index */
+	seq_printf(seq, "%4d%c%c", fidx,
+		   (!f->locked  ? ' ' : '!'),
+		   (!f->pending ? ' ' : (!f->valid ? '+' : '-')));
+
+	if (f->fs.hitcnts) {
+		u64 hitcnt;
+		int ret;
+
+		ret = cxgb4_get_filter_count(adapter, fidx, &hitcnt,
+					     hash, false);
+		if (ret)
+			seq_printf(seq, " %20s", "hits={ERROR}");
+		else
+			seq_printf(seq, " %20llu", hitcnt);
+	} else {
+		seq_printf(seq, " %20s", "Disabled");
+	}
+
+	/* Compressed header portion of filter. */
+	for (i = FT_FIRST_S; i <= FT_LAST_S; i++) {
+		switch (fconf & (1 << i)) {
+		case 0:
+			/* compressed filter field not enabled */
+			break;
+
+		case FCOE_F:
+			seq_printf(seq, "  %1d/%1d",
+				   f->fs.val.fcoe, f->fs.mask.fcoe);
+			break;
+
+		case PORT_F:
+			seq_printf(seq, "  %1d/%1d",
+				   f->fs.val.iport, f->fs.mask.iport);
+			break;
+
+		case VNIC_ID_F:
+			if ((tpiconf & VNIC_F) == 0)
+				seq_printf(seq, " %1d:%04x/%1d:%04x",
+					   f->fs.val.ovlan_vld,
+					   f->fs.val.ovlan,
+					   f->fs.mask.ovlan_vld,
+					   f->fs.mask.ovlan);
+			else
+				seq_printf(seq, " %1d:%1x:%02x/%1d:%1x:%02x",
+					   f->fs.val.ovlan_vld,
+					   (f->fs.val.ovlan >> 13) & 0x7,
+					   f->fs.val.ovlan & 0x7f,
+					   f->fs.mask.ovlan_vld,
+					   (f->fs.mask.ovlan >> 13) & 0x7,
+					   f->fs.mask.ovlan & 0x7f);
+			break;
+
+		case VLAN_F:
+			seq_printf(seq, " %1d:%04x/%1d:%04x",
+				   f->fs.val.ivlan_vld,
+				   f->fs.val.ivlan,
+				   f->fs.mask.ivlan_vld,
+				   f->fs.mask.ivlan);
+			break;
+
+		case TOS_F:
+			seq_printf(seq, " %02x/%02x",
+				   f->fs.val.tos, f->fs.mask.tos);
+			break;
+
+		case PROTOCOL_F:
+			seq_printf(seq, " %02x/%02x",
+				   f->fs.val.proto, f->fs.mask.proto);
+			break;
+
+		case ETHERTYPE_F:
+			seq_printf(seq, " %04x/%04x",
+				   f->fs.val.ethtype, f->fs.mask.ethtype);
+			break;
+
+		case MACMATCH_F:
+			seq_printf(seq, " %03x/%03x",
+				   f->fs.val.macidx, f->fs.mask.macidx);
+			break;
+
+		case MPSHITTYPE_F:
+			seq_printf(seq, " %1x/%1x",
+				   f->fs.val.matchtype,
+				   f->fs.mask.matchtype);
+			break;
+
+		case FRAGMENTATION_F:
+			seq_printf(seq, "  %1d/%1d",
+				   f->fs.val.frag, f->fs.mask.frag);
+			break;
+		}
+	}
+
+	/* Fixed portion of filter. */
+	filters_show_ipaddr(seq, f->fs.type,
+			    f->fs.val.lip, f->fs.mask.lip);
+	filters_show_ipaddr(seq, f->fs.type,
+			    f->fs.val.fip, f->fs.mask.fip);
+	seq_printf(seq, " %04x/%04x %04x/%04x",
+		   f->fs.val.lport, f->fs.mask.lport,
+		   f->fs.val.fport, f->fs.mask.fport);
+
+	/* Variable length filter action. */
+	if (f->fs.action == FILTER_DROP) {
+		seq_puts(seq, " Drop");
+	} else if (f->fs.action == FILTER_SWITCH) {
+		seq_printf(seq, " Switch: port=%d", f->fs.eport);
+		if (f->fs.newdmac)
+			seq_printf(seq,
+				   ", dmac=%02x:%02x:%02x:%02x:%02x:%02x, l2tidx=%d",
+				   f->fs.dmac[0], f->fs.dmac[1],
+				   f->fs.dmac[2], f->fs.dmac[3],
+				   f->fs.dmac[4], f->fs.dmac[5],
+				   f->l2t->idx);
+		if (f->fs.newsmac)
+			seq_printf(seq,
+				   ", smac=%02x:%02x:%02x:%02x:%02x:%02x, smtidx=%d",
+				   f->fs.smac[0], f->fs.smac[1],
+				   f->fs.smac[2], f->fs.smac[3],
+				   f->fs.smac[4], f->fs.smac[5],
+				   f->smtidx);
+		if (f->fs.newvlan == VLAN_REMOVE)
+			seq_puts(seq, ", vlan=none");
+		else if (f->fs.newvlan == VLAN_INSERT)
+			seq_printf(seq, ", vlan=insert(%x)",
+				   f->fs.vlan);
+		else if (f->fs.newvlan == VLAN_REWRITE)
+			seq_printf(seq, ", vlan=rewrite(%x)",
+				   f->fs.vlan);
+	} else {
+		seq_puts(seq, " Pass: Q=");
+		if (f->fs.dirsteer == 0) {
+			seq_puts(seq, "RSS");
+			if (f->fs.maskhash)
+				seq_puts(seq, "(TCB=hash)");
+		} else {
+			seq_printf(seq, "%d", f->fs.iq);
+			if (f->fs.dirsteerhash == 0)
+				seq_puts(seq, "(QID)");
+			else
+				seq_puts(seq, "(hash)");
+		}
+	}
+	if (f->fs.prio)
+		seq_puts(seq, " Prio");
+	if (f->fs.rpttid)
+		seq_puts(seq, " RptTID");
+	seq_puts(seq, "\n");
+}
+
+static int filters_show(struct seq_file *seq, void *v)
+{
+	struct adapter *adapter = seq->private;
+	u32 fconf = adapter->params.tp.vlan_pri_map;
+	u32 tpiconf = adapter->params.tp.ingress_config;
+	int i;
+
+	if (v == SEQ_START_TOKEN) {
+		seq_puts(seq, "[[Legend: '!' => locked; '+' => pending set; '-' => pending clear]]\n");
+		seq_puts(seq, " Idx                   Hits");
+		for (i = FT_FIRST_S; i <= FT_LAST_S; i++) {
+			switch (fconf & (1 << i)) {
+			case 0:
+				/* compressed filter field not enabled */
+				break;
+
+			case FCOE_F:
+				seq_puts(seq, " FCoE");
+				break;
+
+			case PORT_F:
+				seq_puts(seq, " Port");
+				break;
+
+			case VNIC_ID_F:
+				if ((tpiconf & VNIC_F) == 0)
+					seq_puts(seq, "     vld:oVLAN");
+				else
+					seq_puts(seq, "   VFvld:PF:VF");
+				break;
+
+			case VLAN_F:
+				seq_puts(seq, "     vld:iVLAN");
+				break;
+
+			case TOS_F:
+				seq_puts(seq, "   TOS");
+				break;
+
+			case PROTOCOL_F:
+				seq_puts(seq, "  Prot");
+				break;
+
+			case ETHERTYPE_F:
+				seq_puts(seq, "   EthType");
+				break;
+
+			case MACMATCH_F:
+				seq_puts(seq, "  MACIdx");
+				break;
+
+			case MPSHITTYPE_F:
+				seq_puts(seq, " MPS");
+				break;
+
+			case FRAGMENTATION_F:
+				seq_puts(seq, " Frag");
+				break;
+			}
+		}
+		seq_printf(seq, " %65s %65s %9s %9s %s\n",
+			   "LIP", "FIP", "LPORT", "FPORT", "Action");
+	} else {
+		int fidx = (uintptr_t)v - 2;
+		struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
+
+		/* if this entry isn't filled in just return */
+		if (!f->valid && !f->pending)
+			return 0;
+
+		filters_display(seq, fidx, f, 0);
+	}
+	return 0;
+}
+
+static inline void *filters_get_idx(struct adapter *adapter, loff_t pos)
+{
+	if (pos > (adapter->tids.nftids + adapter->tids.nsftids))
+		return NULL;
+
+	return (void *)(uintptr_t)(pos + 1);
+}
+
+static void *filters_start(struct seq_file *seq, loff_t *pos)
+{
+	struct adapter *adapter = seq->private;
+
+	return *pos ? filters_get_idx(adapter, *pos) : SEQ_START_TOKEN;
+}
+
+static void *filters_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct adapter *adapter = seq->private;
+
+	(*pos)++;
+	return filters_get_idx(adapter, *pos);
+}
+
+static void filters_stop(struct seq_file *seq, void *v)
+{
+}
+
+static const struct seq_operations filters_seq_ops = {
+	.start = filters_start,
+	.next  = filters_next,
+	.stop  = filters_stop,
+	.show  = filters_show
+};
+
+int filters_open(struct inode *inode, struct file *file)
+{
+	struct adapter *adapter = inode->i_private;
+	int res;
+
+	res = seq_open(file, &filters_seq_ops);
+	if (!res) {
+		struct seq_file *seq = file->private_data;
+
+		seq->private = adapter;
+	}
+	return res;
+}
+
+const struct file_operations filters_debugfs_fops = {
+	.owner   = THIS_MODULE,
+	.open    = filters_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+};
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
index 23742cb..e801e0b 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
@@ -37,6 +37,8 @@
 
 #include "t4_msg.h"
 
+extern const struct file_operations filters_debugfs_fops;
+
 void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl);
 void clear_filter(struct adapter *adap, struct filter_entry *f);
 
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
index 36cf307..0115222 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
@@ -1,7 +1,7 @@
 /*
  * This file is part of the Chelsio T4 Ethernet driver for Linux.
  *
- * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -121,6 +121,9 @@
  * selects for a particular field being present.  These fields, when present
  * in the Compressed Filter Tuple, have the following widths in bits.
  */
+#define FT_FIRST_S                      FCOE_S
+#define FT_LAST_S                       FRAGMENTATION_S
+
 #define FT_FCOE_W                       1
 #define FT_PORT_W                       3
 #define FT_VNIC_ID_W                    17
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH net-next 4/7] cxgb4: add parser to translate u32 filters to internal spec
  2016-09-12  8:12 [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
                   ` (2 preceding siblings ...)
  2016-09-12  8:12 ` [PATCH net-next 3/7] cxgb4: add debugfs support to dump filter debug logs Rahul Lakkireddy
@ 2016-09-12  8:12 ` Rahul Lakkireddy
  2016-09-12  8:12 ` [PATCH net-next 5/7] cxgb4: add support for setting u32 filters Rahul Lakkireddy
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Rahul Lakkireddy @ 2016-09-12  8:12 UTC (permalink / raw)
  To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy

Parse information sent by u32 into internal filter specification.
Add support for parsing several fields in IPv4, IPv6, TCP, and UDP.

Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
 .../ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h    | 282 +++++++++++++++++++++
 1 file changed, 282 insertions(+)
 create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h

diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
new file mode 100644
index 0000000..261aa4a
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
@@ -0,0 +1,282 @@
+/*
+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CXGB4_TC_U32_PARSE_H
+#define __CXGB4_TC_U32_PARSE_H
+
+struct cxgb4_match_field {
+	int off; /* Offset from the beginning of the header to match */
+	/* Fill the value/mask pair in the spec if matched */
+	int (*val)(struct ch_filter_specification *f, u32 val, u32 mask);
+};
+
+/* IPv4 match fields */
+static inline int cxgb4_fill_ipv4_tos(struct ch_filter_specification *f,
+				      u32 val, u32 mask)
+{
+	f->val.tos  = (ntohl(val)  >> 16) & 0x000000FF;
+	f->mask.tos = (ntohl(mask) >> 16) & 0x000000FF;
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv4_frag(struct ch_filter_specification *f,
+				       u32 val, u32 mask)
+{
+	u8 frag_val;
+	u32 mask_val;
+
+	frag_val = (ntohl(val) >> 13) & 0x00000007;
+	mask_val = ntohl(mask) & 0x0000FFFF;
+
+	if (frag_val == 0x1 && mask_val != 0x3FFF) { /* MF set */
+		f->val.frag = 1;
+		f->mask.frag = 1;
+	} else if (frag_val == 0x2 && mask_val != 0x3FFF) { /* DF set */
+		f->val.frag = 0;
+		f->mask.frag = 1;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv4_proto(struct ch_filter_specification *f,
+					u32 val, u32 mask)
+{
+	f->val.proto  = (ntohl(val)  >> 16) & 0x000000FF;
+	f->mask.proto = (ntohl(mask) >> 16) & 0x000000FF;
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv4_src_ip(struct ch_filter_specification *f,
+					 u32 val, u32 mask)
+{
+	memcpy(&f->val.fip[0],  &val,  sizeof(u32));
+	memcpy(&f->mask.fip[0], &mask, sizeof(u32));
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv4_dst_ip(struct ch_filter_specification *f,
+					 u32 val, u32 mask)
+{
+	memcpy(&f->val.lip[0],  &val,  sizeof(u32));
+	memcpy(&f->mask.lip[0], &mask, sizeof(u32));
+
+	return 0;
+}
+
+static const struct cxgb4_match_field cxgb4_ipv4_fields[] = {
+	{ .off = 0,  .val = cxgb4_fill_ipv4_tos },
+	{ .off = 4,  .val = cxgb4_fill_ipv4_frag },
+	{ .off = 8,  .val = cxgb4_fill_ipv4_proto },
+	{ .off = 12, .val = cxgb4_fill_ipv4_src_ip },
+	{ .off = 16, .val = cxgb4_fill_ipv4_dst_ip },
+	{ .val = NULL }
+};
+
+/* IPv6 match fields */
+static inline int cxgb4_fill_ipv6_tos(struct ch_filter_specification *f,
+				      u32 val, u32 mask)
+{
+	f->val.tos  = (ntohl(val)  >> 20) & 0x000000FF;
+	f->mask.tos = (ntohl(mask) >> 20) & 0x000000FF;
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv6_proto(struct ch_filter_specification *f,
+					u32 val, u32 mask)
+{
+	f->val.proto  = (ntohl(val)  >> 8) & 0x000000FF;
+	f->mask.proto = (ntohl(mask) >> 8) & 0x000000FF;
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv6_src_ip0(struct ch_filter_specification *f,
+					  u32 val, u32 mask)
+{
+	memcpy(&f->val.fip[0],  &val,  sizeof(u32));
+	memcpy(&f->mask.fip[0], &mask, sizeof(u32));
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv6_src_ip1(struct ch_filter_specification *f,
+					  u32 val, u32 mask)
+{
+	memcpy(&f->val.fip[4],  &val,  sizeof(u32));
+	memcpy(&f->mask.fip[4], &mask, sizeof(u32));
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv6_src_ip2(struct ch_filter_specification *f,
+					  u32 val, u32 mask)
+{
+	memcpy(&f->val.fip[8],  &val,  sizeof(u32));
+	memcpy(&f->mask.fip[8], &mask, sizeof(u32));
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv6_src_ip3(struct ch_filter_specification *f,
+					  u32 val, u32 mask)
+{
+	memcpy(&f->val.fip[12],  &val,  sizeof(u32));
+	memcpy(&f->mask.fip[12], &mask, sizeof(u32));
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv6_dst_ip0(struct ch_filter_specification *f,
+					  u32 val, u32 mask)
+{
+	memcpy(&f->val.lip[0],  &val,  sizeof(u32));
+	memcpy(&f->mask.lip[0], &mask, sizeof(u32));
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv6_dst_ip1(struct ch_filter_specification *f,
+					  u32 val, u32 mask)
+{
+	memcpy(&f->val.lip[4],  &val,  sizeof(u32));
+	memcpy(&f->mask.lip[4], &mask, sizeof(u32));
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv6_dst_ip2(struct ch_filter_specification *f,
+					  u32 val, u32 mask)
+{
+	memcpy(&f->val.lip[8],  &val,  sizeof(u32));
+	memcpy(&f->mask.lip[8], &mask, sizeof(u32));
+
+	return 0;
+}
+
+static inline int cxgb4_fill_ipv6_dst_ip3(struct ch_filter_specification *f,
+					  u32 val, u32 mask)
+{
+	memcpy(&f->val.lip[12],  &val,  sizeof(u32));
+	memcpy(&f->mask.lip[12], &mask, sizeof(u32));
+
+	return 0;
+}
+
+static const struct cxgb4_match_field cxgb4_ipv6_fields[] = {
+	{ .off = 0,  .val = cxgb4_fill_ipv6_tos },
+	{ .off = 4,  .val = cxgb4_fill_ipv6_proto },
+	{ .off = 8,  .val = cxgb4_fill_ipv6_src_ip0 },
+	{ .off = 12, .val = cxgb4_fill_ipv6_src_ip1 },
+	{ .off = 16, .val = cxgb4_fill_ipv6_src_ip2 },
+	{ .off = 20, .val = cxgb4_fill_ipv6_src_ip3 },
+	{ .off = 24, .val = cxgb4_fill_ipv6_dst_ip0 },
+	{ .off = 28, .val = cxgb4_fill_ipv6_dst_ip1 },
+	{ .off = 32, .val = cxgb4_fill_ipv6_dst_ip2 },
+	{ .off = 36, .val = cxgb4_fill_ipv6_dst_ip3 },
+	{ .val = NULL }
+};
+
+/* TCP/UDP match */
+static inline int cxgb4_fill_l4_ports(struct ch_filter_specification *f,
+				      u32 val, u32 mask)
+{
+	f->val.fport  = ntohl(val)  >> 16;
+	f->mask.fport = ntohl(mask) >> 16;
+	f->val.lport  = ntohl(val)  & 0x0000FFFF;
+	f->mask.lport = ntohl(mask) & 0x0000FFFF;
+
+	return 0;
+};
+
+static const struct cxgb4_match_field cxgb4_tcp_fields[] = {
+	{ .off = 0, .val = cxgb4_fill_l4_ports },
+	{ .val = NULL }
+};
+
+static const struct cxgb4_match_field cxgb4_udp_fields[] = {
+	{ .off = 0, .val = cxgb4_fill_l4_ports },
+	{ .val = NULL }
+};
+
+struct cxgb4_next_header {
+	unsigned int offset; /* Offset to next header */
+	/* offset, shift, and mask added to offset above
+	 * to get to next header.  Useful when using a header
+	 * field's value to jump to next header such as IHL field
+	 * in IPv4 header.
+	 */
+	unsigned int offoff;
+	u32 shift;
+	u32 mask;
+	/* match criteria to make this jump */
+	unsigned int match_off;
+	u32 match_val;
+	u32 match_mask;
+	/* location of jump to make */
+	const struct cxgb4_match_field *jump;
+};
+
+/* Accept a rule with a jump to transport layer header based on IHL field in
+ * IPv4 header.
+ */
+static const struct cxgb4_next_header cxgb4_ipv4_jumps[] = {
+	{ .offset = 0, .offoff = 0, .shift = 6, .mask = 0xF,
+	  .match_off = 8, .match_val = 0x600, .match_mask = 0xFF00,
+	  .jump = cxgb4_tcp_fields },
+	{ .offset = 0, .offoff = 0, .shift = 6, .mask = 0xF,
+	  .match_off = 8, .match_val = 0x1100, .match_mask = 0xFF00,
+	  .jump = cxgb4_udp_fields },
+	{ .jump = NULL }
+};
+
+/* Accept a rule with a jump directly past the 40 Bytes of IPv6 fixed header
+ * to get to transport layer header.
+ */
+static const struct cxgb4_next_header cxgb4_ipv6_jumps[] = {
+	{ .offset = 0x28, .offoff = 0, .shift = 0, .mask = 0,
+	  .match_off = 4, .match_val = 0x60000, .match_mask = 0xFF0000,
+	  .jump = cxgb4_tcp_fields },
+	{ .offset = 0x28, .offoff = 0, .shift = 0, .mask = 0,
+	  .match_off = 4, .match_val = 0x110000, .match_mask = 0xFF0000,
+	  .jump = cxgb4_udp_fields },
+	{ .jump = NULL }
+};
+#endif /* __CXGB4_TC_U32_PARSE_H */
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH net-next 5/7] cxgb4: add support for setting u32 filters
  2016-09-12  8:12 [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
                   ` (3 preceding siblings ...)
  2016-09-12  8:12 ` [PATCH net-next 4/7] cxgb4: add parser to translate u32 filters to internal spec Rahul Lakkireddy
@ 2016-09-12  8:12 ` Rahul Lakkireddy
  2016-09-12  8:40   ` Jiri Pirko
  2016-09-12  8:12 ` [PATCH net-next 6/7] cxgb4: add support for deleting " Rahul Lakkireddy
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Rahul Lakkireddy @ 2016-09-12  8:12 UTC (permalink / raw)
  To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy

Add support for offloading u32 filter onto hardware.  Links are stored
in a jump table to perform necessary jumps to match TCP/UDP header.
When inserting rules in the linked bucket, the TCP/UDP match fields
in the corresponding entry of the jump table are appended to the filter
rule before insertion.

Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
 drivers/net/ethernet/chelsio/cxgb4/Makefile        |   2 +-
 drivers/net/ethernet/chelsio/cxgb4/cxgb4.h         |   3 +
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c    |  36 +++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c  | 343 +++++++++++++++++++++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h  |  55 ++++
 .../ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h    |  12 +
 6 files changed, 450 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
 create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h

diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile
index da88981..c6b71f6 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/Makefile
+++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile
@@ -4,7 +4,7 @@
 
 obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
 
-cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o
+cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o cxgb4_tc_u32.o
 cxgb4-$(CONFIG_CHELSIO_T4_DCB) +=  cxgb4_dcb.o
 cxgb4-$(CONFIG_CHELSIO_T4_FCOE) +=  cxgb4_fcoe.o
 cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index fbd593a..1adb28f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -867,6 +867,9 @@ struct adapter {
 
 	spinlock_t stats_lock;
 	spinlock_t win0_lock ____cacheline_aligned_in_smp;
+
+	/* TC u32 offload */
+	struct cxgb4_tc_u32_table *tc_u32;
 };
 
 /* Support for "sched-class" command to allow a TX Scheduling Class to be
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index af07f9d..28396f5 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -78,6 +78,7 @@
 #include "clip_tbl.h"
 #include "l2t.h"
 #include "sched.h"
+#include "cxgb4_tc_u32.h"
 
 char cxgb4_driver_name[] = KBUILD_MODNAME;
 
@@ -3027,6 +3028,33 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate)
 	return err;
 }
 
+int cxgb_setup_tc(struct net_device *dev, u32 handle, __be16 proto,
+		  struct tc_to_netdev *tc)
+{
+	struct adapter *adap = netdev2adap(dev);
+	struct port_info *pi = netdev2pinfo(dev);
+
+	if (!(adap->flags & 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_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) &&
+	    tc->type == TC_SETUP_CLSU32) {
+		switch (tc->cls_u32->command) {
+		case TC_CLSU32_NEW_KNODE:
+		case TC_CLSU32_REPLACE_KNODE:
+			return cxgb4_config_knode(dev, proto, tc->cls_u32);
+		default:
+			return -EOPNOTSUPP;
+		}
+	}
+
+	return -EOPNOTSUPP;
+}
+
 static const struct net_device_ops cxgb4_netdev_ops = {
 	.ndo_open             = cxgb_open,
 	.ndo_stop             = cxgb_close,
@@ -3050,6 +3078,7 @@ static const struct net_device_ops cxgb4_netdev_ops = {
 	.ndo_busy_poll        = cxgb_busy_poll,
 #endif
 	.ndo_set_tx_maxrate   = cxgb_set_tx_maxrate,
+	.ndo_setup_tc         = cxgb_setup_tc,
 };
 
 #ifdef CONFIG_PCI_IOV
@@ -4781,6 +4810,7 @@ static void free_some_resources(struct adapter *adapter)
 	t4_free_mem(adapter->l2t);
 	t4_cleanup_sched(adapter);
 	t4_free_mem(adapter->tids.tid_tab);
+	cxgb4_cleanup_tc_u32(adapter);
 	kfree(adapter->sge.egr_map);
 	kfree(adapter->sge.ingr_map);
 	kfree(adapter->sge.starving_fl);
@@ -5213,6 +5243,12 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 		dev_warn(&pdev->dev, "could not allocate TID table, "
 			 "continuing\n");
 		adapter->params.offload = 0;
+	} else {
+		adapter->tc_u32 = cxgb4_init_tc_u32(adapter,
+						    CXGB4_MAX_LINK_HANDLE);
+		if (!adapter->tc_u32)
+			dev_warn(&pdev->dev,
+				 "could not offload tc u32, continuing\n");
 	}
 
 	if (is_offload(adapter)) {
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
new file mode 100644
index 0000000..62c1695
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
@@ -0,0 +1,343 @@
+/*
+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "cxgb4.h"
+#include "cxgb4_tc_u32_parse.h"
+#include "cxgb4_tc_u32.h"
+
+/* Fill ch_filter_specification with parsed match value/mask pair. */
+static int fill_match_fields(struct adapter *adap,
+			     struct ch_filter_specification *fs,
+			     struct tc_cls_u32_offload *cls,
+			     const struct cxgb4_match_field *entry,
+			     bool next_header)
+{
+	unsigned int i, j;
+	int off;
+	u32 val, mask;
+	int err;
+	bool found = false;
+
+	for (i = 0; i < cls->knode.sel->nkeys; i++) {
+		off = cls->knode.sel->keys[i].off;
+		val = cls->knode.sel->keys[i].val;
+		mask = cls->knode.sel->keys[i].mask;
+
+		if (next_header) {
+			/* For next headers, parse only keys with offmask */
+			if (!cls->knode.sel->keys[i].offmask)
+				continue;
+		} else {
+			/* For the remaining, parse only keys without offmask */
+			if (cls->knode.sel->keys[i].offmask)
+				continue;
+		}
+
+		found = false;
+
+		for (j = 0; entry[j].val; j++) {
+			if (off == entry[j].off) {
+				found = true;
+				err = entry[j].val(fs, val, mask);
+				if (err)
+					return err;
+				break;
+			}
+		}
+
+		if (!found)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
+		       struct tc_cls_u32_offload *cls)
+{
+	struct adapter *adapter = netdev2adap(dev);
+	struct cxgb4_tc_u32_table *t;
+	const struct cxgb4_match_field *start, *link_start = NULL;
+	struct cxgb4_link *link;
+	struct ch_filter_specification fs;
+	struct filter_ctx ctx;
+	unsigned int filter_id;
+	u32 uhtid, link_uhtid;
+	int ret;
+	bool is_ipv6 = false;
+
+	if (!can_tc_u32_offload(dev))
+		return -EOPNOTSUPP;
+
+	if ((protocol != htons(ETH_P_IP)) && (protocol != htons(ETH_P_IPV6)))
+		return -EOPNOTSUPP;
+
+	/* Fetch the location to insert the filter. */
+	filter_id = (cls->knode.handle & 0xFFFFF);
+
+	if (filter_id > adapter->tids.nftids) {
+		dev_err(adapter->pdev_dev,
+			"Location %d out of range for insertion. Max: %d\n",
+			filter_id, adapter->tids.nftids);
+		return -ERANGE;
+	}
+
+	t = adapter->tc_u32;
+	uhtid = TC_U32_USERHTID(cls->knode.handle);
+	link_uhtid = TC_U32_USERHTID(cls->knode.link_handle);
+
+	/* Ensure that uhtid is either root u32 (i.e. 0x800)
+	 * or a a valid linked bucket.
+	 */
+	if (uhtid != 0x800 && uhtid >= t->size)
+		return -EINVAL;
+
+	/* Ensure link handle uhtid is sane, if specified. */
+	if (link_uhtid >= t->size)
+		return -EINVAL;
+
+	memset(&fs, 0, sizeof(fs));
+
+	if (protocol == htons(ETH_P_IPV6)) {
+		start = cxgb4_ipv6_fields;
+		is_ipv6 = true;
+	} else {
+		start = cxgb4_ipv4_fields;
+		is_ipv6 = false;
+	}
+
+	if (uhtid != 0x800) {
+		/* Link must exist from root node before insertion. */
+		if (!t->table[uhtid - 1].link_handle)
+			return -EINVAL;
+
+		/* Link must have a valid supported next header. */
+		link_start = (&t->table[uhtid - 1])->match_field;
+		if (!link_start)
+			return -EINVAL;
+	}
+
+	/* Parse links and record them for subsequent jumps to valid
+	 * next headers.
+	 */
+	if (link_uhtid) {
+		const struct cxgb4_next_header *next;
+		unsigned int i, j;
+		int off;
+		u32 val, mask;
+		bool found = false;
+
+		if (t->table[link_uhtid - 1].link_handle) {
+			dev_err(adapter->pdev_dev,
+				"Link handle exists for: 0x%x\n",
+				link_uhtid);
+			return -EINVAL;
+		}
+
+		next = is_ipv6 ? cxgb4_ipv6_jumps : cxgb4_ipv4_jumps;
+
+		/* Try to find matches that allow jumps to next header. */
+		for (i = 0; next[i].jump; i++) {
+			if (next[i].offoff != cls->knode.sel->offoff ||
+			    next[i].shift != cls->knode.sel->offshift ||
+			    next[i].mask != cls->knode.sel->offmask ||
+			    next[i].offset != cls->knode.sel->off)
+				continue;
+
+			/* Found a possible candidate.  Find a key that
+			 * matches the corresponding offset, value, and
+			 * mask to jump to next header.
+			 */
+			for (j = 0; j < cls->knode.sel->nkeys; j++) {
+				off = cls->knode.sel->keys[j].off;
+				val = cls->knode.sel->keys[j].val;
+				mask = cls->knode.sel->keys[j].mask;
+
+				if (next[i].match_off == off &&
+				    next[i].match_val == val &&
+				    next[i].match_mask == mask) {
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+				continue; /* Try next candidate. */
+
+			/* Candidate to jump to next header found.
+			 * Translate all keys to internal specification
+			 * and store them in jump table. This spec is copied
+			 * later to set the actual filters.
+			 */
+			ret = fill_match_fields(adapter, &fs, cls,
+						start, false);
+			if (ret)
+				goto out;
+
+			link = &t->table[link_uhtid - 1];
+			link->match_field = next[i].jump;
+			link->link_handle = cls->knode.handle;
+			memcpy(&link->fs, &fs, sizeof(fs));
+			break;
+		}
+
+		/* No candidate found to jump to next header. */
+		if (!found)
+			return -EINVAL;
+
+		return 0;
+	}
+
+	/* Fill ch_filter_specification match fields to be shipped to hardware.
+	 * Copy the linked spec (if any) first.  And then update the spec as
+	 * needed.
+	 */
+	if ((uhtid != 0x800) && t->table[uhtid - 1].link_handle) {
+		/* Copy linked ch_filter_specification */
+		memcpy(&fs, &t->table[uhtid - 1].fs, sizeof(fs));
+		ret = fill_match_fields(adapter, &fs, cls,
+					link_start, true);
+		if (ret)
+			goto out;
+	}
+
+	ret = fill_match_fields(adapter, &fs, cls, start, false);
+	if (ret)
+		goto out;
+
+	/* The filter spec has been completely built from the info
+	 * provided from u32.  We now set some default fields in the
+	 * spec for sanity.
+	 */
+
+	/* Match only packets coming from the ingress port where this
+	 * filter will be created.
+	 */
+	fs.val.iport = netdev2pinfo(dev)->port_id;
+	fs.mask.iport = ~0;
+
+	/* Enable filter hit counts. */
+	fs.hitcnts = 1;
+
+	/* Set type of filter - IPv6 or IPv4 */
+	fs.type = is_ipv6 ? 1 : 0;
+
+	init_completion(&ctx.completion);
+
+	/* Set the filter */
+	ret = cxgb4_set_filter(dev, filter_id, &fs, &ctx);
+	if (ret)
+		goto out;
+
+	/* Wait for reply */
+	ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ);
+	if (!ret)
+		return -ETIMEDOUT;
+
+	ret = ctx.result;
+	if (!ret) {
+		/* If this is a linked bucket, then set the corresponding
+		 * entry in the bitmap to mark it as belonging to this linked
+		 * bucket.
+		 */
+		if ((uhtid != 0x800) && t->table[uhtid - 1].link_handle)
+			set_bit(filter_id, (&t->table[uhtid - 1])->tid_map);
+	}
+
+out:
+	return ret;
+}
+
+void cxgb4_cleanup_tc_u32(struct adapter *adap)
+{
+	struct cxgb4_tc_u32_table *t;
+	unsigned int i;
+
+	if (!adap->tc_u32)
+		return;
+
+	/* Free up all allocated memory. */
+	t = adap->tc_u32;
+	for (i = 0; i < t->size; i++) {
+		struct cxgb4_link *link = &t->table[i];
+
+		t4_free_mem(link->tid_map);
+	}
+	t4_free_mem(adap->tc_u32);
+}
+
+struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
+					     unsigned int size)
+{
+	struct cxgb4_tc_u32_table *t;
+	unsigned int i;
+
+	if (!size)
+		return NULL;
+
+	t = t4_alloc_mem(sizeof(*t) +
+			 (size * sizeof(struct cxgb4_link)));
+	if (!t)
+		return NULL;
+
+	t->size = size;
+
+	for (i = 0; i < t->size; i++) {
+		struct cxgb4_link *link = &t->table[i];
+		unsigned int bmap_size;
+		unsigned int max_tids;
+
+		max_tids = adap->tids.nftids;
+		bmap_size = BITS_TO_LONGS(max_tids);
+		link->tid_map = t4_alloc_mem(sizeof(unsigned long) * bmap_size);
+		if (!link->tid_map)
+			goto out_no_mem;
+		bitmap_zero(link->tid_map, max_tids);
+	}
+
+	return t;
+
+out_no_mem:
+	for (i = 0; i < t->size; i++) {
+		struct cxgb4_link *link = &t->table[i];
+
+		if (link->tid_map)
+			t4_free_mem(link->tid_map);
+	}
+
+	if (t)
+		t4_free_mem(t);
+
+	return NULL;
+}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
new file mode 100644
index 0000000..46575843
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CXGB4_TC_U32_H
+#define __CXGB4_TC_U32_H
+
+#include <net/pkt_cls.h>
+
+#define CXGB4_MAX_LINK_HANDLE 32
+
+static inline bool can_tc_u32_offload(struct net_device *dev)
+{
+	struct adapter *adap = netdev2adap(dev);
+
+	return (dev->features & NETIF_F_HW_TC) && adap->tc_u32 ? true : false;
+}
+
+int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
+		       struct tc_cls_u32_offload *cls);
+
+void cxgb4_cleanup_tc_u32(struct adapter *adapter);
+struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
+					     unsigned int size);
+#endif /* __CXGB4_TC_U32_H */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
index 261aa4a..de321bf 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
@@ -279,4 +279,16 @@ static const struct cxgb4_next_header cxgb4_ipv6_jumps[] = {
 	  .jump = cxgb4_udp_fields },
 	{ .jump = NULL }
 };
+
+struct cxgb4_link {
+	const struct cxgb4_match_field *match_field;  /* Next header */
+	struct ch_filter_specification fs; /* Match spec associated with link */
+	u32 link_handle;         /* Knode handle associated with the link */
+	unsigned long *tid_map;  /* Bitmap for filter tids */
+};
+
+struct cxgb4_tc_u32_table {
+	unsigned int size;          /* number of entries in table */
+	struct cxgb4_link table[0]; /* Jump table */
+};
 #endif /* __CXGB4_TC_U32_PARSE_H */
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH net-next 6/7] cxgb4: add support for deleting u32 filters
  2016-09-12  8:12 [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
                   ` (4 preceding siblings ...)
  2016-09-12  8:12 ` [PATCH net-next 5/7] cxgb4: add support for setting u32 filters Rahul Lakkireddy
@ 2016-09-12  8:12 ` Rahul Lakkireddy
  2016-09-12  8:47   ` Jiri Pirko
  2016-09-12  8:12 ` [PATCH net-next 7/7] cxgb4: add support for drop and redirect actions Rahul Lakkireddy
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Rahul Lakkireddy @ 2016-09-12  8:12 UTC (permalink / raw)
  To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy

Add support for deleting an offloaded u32 filter from hardware.  If a
link is deleted, then all corresponding filters associated with the link
are also deleted.  Also enable hardware tc offload as a supported
feature.

Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c   |  5 +-
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 92 +++++++++++++++++++++++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h |  2 +
 3 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 28396f5..087066a 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -3047,6 +3047,8 @@ int cxgb_setup_tc(struct net_device *dev, u32 handle, __be16 proto,
 		case TC_CLSU32_NEW_KNODE:
 		case TC_CLSU32_REPLACE_KNODE:
 			return cxgb4_config_knode(dev, proto, tc->cls_u32);
+		case TC_CLSU32_DELETE_KNODE:
+			return cxgb4_delete_knode(dev, proto, tc->cls_u32);
 		default:
 			return -EOPNOTSUPP;
 		}
@@ -5155,7 +5157,8 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 		netdev->hw_features = NETIF_F_SG | TSO_FLAGS |
 			NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
 			NETIF_F_RXCSUM | NETIF_F_RXHASH |
-			NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+			NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+			NETIF_F_HW_TC;
 		if (highdma)
 			netdev->hw_features |= NETIF_F_HIGHDMA;
 		netdev->features |= netdev->hw_features;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
index 62c1695..31847e3 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
@@ -279,6 +279,98 @@ out:
 	return ret;
 }
 
+int cxgb4_delete_knode(struct net_device *dev, __be16 protocol,
+		       struct tc_cls_u32_offload *cls)
+{
+	struct adapter *adapter = netdev2adap(dev);
+	struct cxgb4_tc_u32_table *t;
+	struct cxgb4_link *link = NULL;
+	struct filter_ctx ctx;
+	u32 handle, uhtid;
+	unsigned int filter_id;
+	unsigned int max_tids;
+	unsigned int i, j;
+	int ret;
+
+	if (!can_tc_u32_offload(dev))
+		return -EOPNOTSUPP;
+
+	/* Fetch the location to delete the filter. */
+	filter_id = (cls->knode.handle & 0xFFFFF);
+
+	if (filter_id > adapter->tids.nftids) {
+		dev_err(adapter->pdev_dev,
+			"Location %d out of range for deletion. Max: %d\n",
+			filter_id, adapter->tids.nftids);
+		return -ERANGE;
+	}
+
+	t = adapter->tc_u32;
+	handle = cls->knode.handle;
+	uhtid = TC_U32_USERHTID(cls->knode.handle);
+
+	/* Ensure that uhtid is either root u32 (i.e. 0x800)
+	 * or a a valid linked bucket.
+	 */
+	if (uhtid != 0x800 && uhtid >= t->size)
+		return -EINVAL;
+
+	/* Delete the specified filter */
+	if (uhtid != 0x800) {
+		link = &t->table[uhtid - 1];
+		if (!link->link_handle)
+			return -EINVAL;
+
+		if (!test_bit(filter_id, link->tid_map))
+			return -EINVAL;
+	}
+
+	init_completion(&ctx.completion);
+
+	ret = cxgb4_del_filter(dev, filter_id, &ctx);
+	if (ret)
+		goto out;
+
+	/* Wait for reply */
+	ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ);
+	if (!ret)
+		return -ETIMEDOUT;
+
+	ret = ctx.result;
+	if (!ret && link)
+		clear_bit(filter_id, link->tid_map);
+
+	/* If a link is being deleted, then delete all filters
+	 * associated with the link.
+	 */
+	max_tids = adapter->tids.nftids;
+	for (i = 0; i < t->size; i++) {
+		link = &t->table[i];
+
+		if (link->link_handle == handle) {
+			for (j = 0; j < max_tids; j++) {
+				if (!test_bit(j, link->tid_map))
+					continue;
+
+				ret = cxgb4_del_filter(dev, j, NULL);
+				if (ret)
+					goto out;
+
+				clear_bit(j, link->tid_map);
+			}
+
+			/* Clear the link state */
+			link->match_field = NULL;
+			link->link_handle = 0;
+			memset(&link->fs, 0, sizeof(link->fs));
+			break;
+		}
+	}
+
+out:
+	return ret;
+}
+
 void cxgb4_cleanup_tc_u32(struct adapter *adap)
 {
 	struct cxgb4_tc_u32_table *t;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
index 46575843..6bdc885 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
@@ -48,6 +48,8 @@ static inline bool can_tc_u32_offload(struct net_device *dev)
 
 int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
 		       struct tc_cls_u32_offload *cls);
+int cxgb4_delete_knode(struct net_device *dev, __be16 protocol,
+		       struct tc_cls_u32_offload *cls);
 
 void cxgb4_cleanup_tc_u32(struct adapter *adapter);
 struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH net-next 7/7] cxgb4: add support for drop and redirect actions
  2016-09-12  8:12 [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
                   ` (5 preceding siblings ...)
  2016-09-12  8:12 ` [PATCH net-next 6/7] cxgb4: add support for deleting " Rahul Lakkireddy
@ 2016-09-12  8:12 ` Rahul Lakkireddy
  2016-09-12  8:52   ` Jiri Pirko
  2016-09-13  9:07 ` [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
  2016-09-13 16:12 ` David Miller
  8 siblings, 1 reply; 16+ messages in thread
From: Rahul Lakkireddy @ 2016-09-12  8:12 UTC (permalink / raw)
  To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy

Add support for dropping matched packets in hardware.  Also add support
for re-directing matched packets to a specified port in hardware.

Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 63 +++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
index 31847e3..584ccb3 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
@@ -32,6 +32,9 @@
  * SOFTWARE.
  */
 
+#include <net/tc_act/tc_gact.h>
+#include <net/tc_act/tc_mirred.h>
+
 #include "cxgb4.h"
 #include "cxgb4_tc_u32_parse.h"
 #include "cxgb4_tc_u32.h"
@@ -83,6 +86,59 @@ static int fill_match_fields(struct adapter *adap,
 	return 0;
 }
 
+/* Fill ch_filter_specification with parsed action. */
+static int fill_action_fields(struct adapter *adap,
+			      struct ch_filter_specification *fs,
+			      struct tc_cls_u32_offload *cls)
+{
+	const struct tc_action *a;
+	struct tcf_exts *exts;
+	LIST_HEAD(actions);
+	unsigned int num_actions = 0;
+	bool found = false;
+
+	exts = cls->knode.exts;
+	if (tc_no_actions(exts))
+		return -EINVAL;
+
+	tcf_exts_to_list(exts, &actions);
+	list_for_each_entry(a, &actions, list) {
+		/* Don't allow more than one action per rule. */
+		if (num_actions)
+			return -EINVAL;
+
+		/* Drop in hardware. */
+		if (is_tcf_gact_shot(a)) {
+			fs->action = FILTER_DROP;
+			found = true;
+		}
+
+		/* Re-direct to specified port in hardware. */
+		if (is_tcf_mirred_redirect(a)) {
+			struct net_device *n_dev;
+			unsigned int i, index;
+
+			index = tcf_mirred_ifindex(a);
+			for_each_port(adap, i) {
+				n_dev = adap->port[i];
+				if (index == n_dev->ifindex) {
+					fs->action = FILTER_SWITCH;
+					fs->eport = i;
+					break;
+				}
+			}
+			found = true;
+		}
+
+		num_actions++;
+	}
+
+	if (!found)
+		return -EINVAL;
+
+	return 0;
+}
+
 int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
 		       struct tc_cls_u32_offload *cls)
 {
@@ -236,6 +292,13 @@ int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
 	if (ret)
 		goto out;
 
+	/* Fill ch_filter_specification action fields to be shipped to
+	 * hardware.
+	 */
+	ret = fill_action_fields(adapter, &fs, cls);
+	if (ret)
+		goto out;
+
 	/* The filter spec has been completely built from the info
 	 * provided from u32.  We now set some default fields in the
 	 * spec for sanity.
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH net-next 3/7] cxgb4: add debugfs support to dump filter debug logs
  2016-09-12  8:12 ` [PATCH net-next 3/7] cxgb4: add debugfs support to dump filter debug logs Rahul Lakkireddy
@ 2016-09-12  8:36   ` Jiri Pirko
  0 siblings, 0 replies; 16+ messages in thread
From: Jiri Pirko @ 2016-09-12  8:36 UTC (permalink / raw)
  To: Rahul Lakkireddy; +Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil

Mon, Sep 12, 2016 at 10:12:36AM CEST, rahul.lakkireddy@chelsio.com wrote:
>Add debugfs support to dump filter debug information.

Please no debugfs. Why would you want to use it?
Use a well defined user api instead. If not available, please introduce it.



>
>Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
>Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
>---
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c |   4 +-
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c  | 415 +++++++++++++++++++++
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h  |   2 +
> drivers/net/ethernet/chelsio/cxgb4/t4_values.h     |   5 +-
> 4 files changed, 424 insertions(+), 2 deletions(-)
>
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
>index 91fb508..72cf3de2 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
>@@ -1,7 +1,7 @@
> /*
>  * This file is part of the Chelsio T4 Ethernet driver for Linux.
>  *
>- * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
>+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
>  *
>  * This software is available to you under a choice of one of two
>  * licenses.  You may choose to be licensed under the terms of the GNU
>@@ -45,6 +45,7 @@
> #include "cxgb4_debugfs.h"
> #include "clip_tbl.h"
> #include "l2t.h"
>+#include "cxgb4_filter.h"
> 
> /* generic seq_file support for showing a table of size rows x width. */
> static void *seq_tab_get_idx(struct seq_tab *tb, loff_t pos)
>@@ -3272,6 +3273,7 @@ int t4_setup_debugfs(struct adapter *adap)
> 		{ "tids", &tid_info_debugfs_fops, S_IRUSR, 0},
> 		{ "blocked_fl", &blocked_fl_fops, S_IRUSR | S_IWUSR, 0 },
> 		{ "meminfo", &meminfo_fops, S_IRUSR, 0 },
>+		{ "filters", &filters_debugfs_fops, S_IRUSR, 0 },
> 	};
> 
> 	/* Debug FS nodes common to all T5 and later adapters.
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>index 490bd94..51b6745 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>@@ -34,6 +34,7 @@
> 
> #include "cxgb4.h"
> #include "t4_regs.h"
>+#include "t4_values.h"
> #include "l2t.h"
> #include "t4fw_api.h"
> #include "cxgb4_filter.h"
>@@ -669,3 +670,417 @@ void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
> 			complete(&ctx->completion);
> 	}
> }
>+
>+/* Retrieve the packet count for the specified filter. */
>+int cxgb4_get_filter_count(struct adapter *adapter, unsigned int fidx,
>+			   u64 *c, int hash, bool get_byte)
>+{
>+	struct filter_entry *f;
>+	unsigned int tcb_base, tcbaddr;
>+	unsigned int max_ftids;
>+	int ret;
>+
>+	tcb_base = t4_read_reg(adapter, TP_CMM_TCB_BASE_A);
>+	max_ftids = adapter->tids.nftids;
>+	if ((fidx != (max_ftids + adapter->tids.nsftids - 1)) &&
>+	    (fidx >= max_ftids))
>+		return -E2BIG;
>+
>+	f = &adapter->tids.ftid_tab[fidx];
>+	if (!f->valid)
>+		return -EINVAL;
>+
>+	tcbaddr = tcb_base + f->tid * TCB_SIZE;
>+
>+	if (is_t4(adapter->params.chip)) {
>+		/* For T4, the Filter Packet Hit Count is maintained as a
>+		 * 64-bit Big Endian value in the TCB fields
>+		 * {t_rtt_ts_recent_age, t_rtseq_recent} ... The format in
>+		 * memory is swizzled/mapped in a manner such that instead
>+		 * of having this 64-bit counter show up at offset 24
>+		 * ((TCB_T_RTT_TS_RECENT_AGE_W == 6) * sizeof(u32)), it
>+		 * actually shows up at offset 16. Hence the constant "4"
>+		 * below instead of TCB_T_RTT_TS_RECENT_AGE_W.
>+		 */
>+		if (get_byte) {
>+			unsigned int word_offset = 4;
>+			__be64 be64_byte_count;
>+
>+			spin_lock(&adapter->win0_lock);
>+			ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
>+					   tcbaddr +
>+					   (word_offset * sizeof(__be32)),
>+					   sizeof(be64_byte_count),
>+					   &be64_byte_count,
>+					   T4_MEMORY_READ);
>+			spin_unlock(&adapter->win0_lock);
>+			if (ret < 0)
>+				return ret;
>+			*c = be64_to_cpu(be64_byte_count);
>+		} else {
>+			unsigned int word_offset = 4;
>+			__be64 be64_count;
>+
>+			spin_lock(&adapter->win0_lock);
>+			ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
>+					   tcbaddr +
>+					   (word_offset * sizeof(__be32)),
>+					   sizeof(be64_count),
>+					   (__be32 *)&be64_count,
>+					   T4_MEMORY_READ);
>+			spin_unlock(&adapter->win0_lock);
>+			if (ret < 0)
>+				return ret;
>+			*c = be64_to_cpu(be64_count);
>+		}
>+	} else {
>+		/* For T5, the Filter Packet Hit Count is maintained as a
>+		 * 32-bit Big Endian value in the TCB field {timestamp}.
>+		 * Instead of the filter hit count showing up at offset 20
>+		 * ((TCB_TIMESTAMP_W == 5) * sizeof(u32)), it actually shows
>+		 * up at offset 24.  Hence the constant "6" below.
>+		 */
>+		if (get_byte) {
>+			unsigned int word_offset = 4;
>+			__be64 be64_byte_count;
>+
>+			spin_lock(&adapter->win0_lock);
>+			ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
>+					   tcbaddr +
>+					   (word_offset * sizeof(__be32)),
>+					   sizeof(be64_byte_count),
>+					   &be64_byte_count,
>+					   T4_MEMORY_READ);
>+			spin_unlock(&adapter->win0_lock);
>+			if (ret < 0)
>+				return ret;
>+			*c = be64_to_cpu(be64_byte_count);
>+		} else {
>+			unsigned int word_offset = 6;
>+			__be32 be32_count;
>+
>+			spin_lock(&adapter->win0_lock);
>+			ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
>+					   tcbaddr +
>+					   (word_offset * sizeof(__be32)),
>+					   sizeof(be32_count), &be32_count,
>+					   T4_MEMORY_READ);
>+			spin_unlock(&adapter->win0_lock);
>+			if (ret < 0)
>+				return ret;
>+			*c = (u64)be32_to_cpu(be32_count);
>+		}
>+	}
>+
>+	return 0;
>+}
>+
>+/* Filter Table. */
>+static void filters_show_ipaddr(struct seq_file *seq,
>+				int type, u8 *addr, u8 *addrm)
>+{
>+	int noctets, octet;
>+
>+	seq_puts(seq, " ");
>+	if (type == 0) {
>+		noctets = 4;
>+		seq_printf(seq, "%48s", " ");
>+	} else {
>+		noctets = 16;
>+	}
>+
>+	for (octet = 0; octet < noctets; octet++)
>+		seq_printf(seq, "%02x", addr[octet]);
>+	seq_puts(seq, "/");
>+	for (octet = 0; octet < noctets; octet++)
>+		seq_printf(seq, "%02x", addrm[octet]);
>+}
>+
>+static void filters_display(struct seq_file *seq, unsigned int fidx,
>+			    struct filter_entry *f, int hash)
>+{
>+	struct adapter *adapter = seq->private;
>+	u32 fconf = adapter->params.tp.vlan_pri_map;
>+	u32 tpiconf = adapter->params.tp.ingress_config;
>+	int i;
>+
>+	/* Filter index */
>+	seq_printf(seq, "%4d%c%c", fidx,
>+		   (!f->locked  ? ' ' : '!'),
>+		   (!f->pending ? ' ' : (!f->valid ? '+' : '-')));
>+
>+	if (f->fs.hitcnts) {
>+		u64 hitcnt;
>+		int ret;
>+
>+		ret = cxgb4_get_filter_count(adapter, fidx, &hitcnt,
>+					     hash, false);
>+		if (ret)
>+			seq_printf(seq, " %20s", "hits={ERROR}");
>+		else
>+			seq_printf(seq, " %20llu", hitcnt);
>+	} else {
>+		seq_printf(seq, " %20s", "Disabled");
>+	}
>+
>+	/* Compressed header portion of filter. */
>+	for (i = FT_FIRST_S; i <= FT_LAST_S; i++) {
>+		switch (fconf & (1 << i)) {
>+		case 0:
>+			/* compressed filter field not enabled */
>+			break;
>+
>+		case FCOE_F:
>+			seq_printf(seq, "  %1d/%1d",
>+				   f->fs.val.fcoe, f->fs.mask.fcoe);
>+			break;
>+
>+		case PORT_F:
>+			seq_printf(seq, "  %1d/%1d",
>+				   f->fs.val.iport, f->fs.mask.iport);
>+			break;
>+
>+		case VNIC_ID_F:
>+			if ((tpiconf & VNIC_F) == 0)
>+				seq_printf(seq, " %1d:%04x/%1d:%04x",
>+					   f->fs.val.ovlan_vld,
>+					   f->fs.val.ovlan,
>+					   f->fs.mask.ovlan_vld,
>+					   f->fs.mask.ovlan);
>+			else
>+				seq_printf(seq, " %1d:%1x:%02x/%1d:%1x:%02x",
>+					   f->fs.val.ovlan_vld,
>+					   (f->fs.val.ovlan >> 13) & 0x7,
>+					   f->fs.val.ovlan & 0x7f,
>+					   f->fs.mask.ovlan_vld,
>+					   (f->fs.mask.ovlan >> 13) & 0x7,
>+					   f->fs.mask.ovlan & 0x7f);
>+			break;
>+
>+		case VLAN_F:
>+			seq_printf(seq, " %1d:%04x/%1d:%04x",
>+				   f->fs.val.ivlan_vld,
>+				   f->fs.val.ivlan,
>+				   f->fs.mask.ivlan_vld,
>+				   f->fs.mask.ivlan);
>+			break;
>+
>+		case TOS_F:
>+			seq_printf(seq, " %02x/%02x",
>+				   f->fs.val.tos, f->fs.mask.tos);
>+			break;
>+
>+		case PROTOCOL_F:
>+			seq_printf(seq, " %02x/%02x",
>+				   f->fs.val.proto, f->fs.mask.proto);
>+			break;
>+
>+		case ETHERTYPE_F:
>+			seq_printf(seq, " %04x/%04x",
>+				   f->fs.val.ethtype, f->fs.mask.ethtype);
>+			break;
>+
>+		case MACMATCH_F:
>+			seq_printf(seq, " %03x/%03x",
>+				   f->fs.val.macidx, f->fs.mask.macidx);
>+			break;
>+
>+		case MPSHITTYPE_F:
>+			seq_printf(seq, " %1x/%1x",
>+				   f->fs.val.matchtype,
>+				   f->fs.mask.matchtype);
>+			break;
>+
>+		case FRAGMENTATION_F:
>+			seq_printf(seq, "  %1d/%1d",
>+				   f->fs.val.frag, f->fs.mask.frag);
>+			break;
>+		}
>+	}
>+
>+	/* Fixed portion of filter. */
>+	filters_show_ipaddr(seq, f->fs.type,
>+			    f->fs.val.lip, f->fs.mask.lip);
>+	filters_show_ipaddr(seq, f->fs.type,
>+			    f->fs.val.fip, f->fs.mask.fip);
>+	seq_printf(seq, " %04x/%04x %04x/%04x",
>+		   f->fs.val.lport, f->fs.mask.lport,
>+		   f->fs.val.fport, f->fs.mask.fport);
>+
>+	/* Variable length filter action. */
>+	if (f->fs.action == FILTER_DROP) {
>+		seq_puts(seq, " Drop");
>+	} else if (f->fs.action == FILTER_SWITCH) {
>+		seq_printf(seq, " Switch: port=%d", f->fs.eport);
>+		if (f->fs.newdmac)
>+			seq_printf(seq,
>+				   ", dmac=%02x:%02x:%02x:%02x:%02x:%02x, l2tidx=%d",
>+				   f->fs.dmac[0], f->fs.dmac[1],
>+				   f->fs.dmac[2], f->fs.dmac[3],
>+				   f->fs.dmac[4], f->fs.dmac[5],
>+				   f->l2t->idx);
>+		if (f->fs.newsmac)
>+			seq_printf(seq,
>+				   ", smac=%02x:%02x:%02x:%02x:%02x:%02x, smtidx=%d",
>+				   f->fs.smac[0], f->fs.smac[1],
>+				   f->fs.smac[2], f->fs.smac[3],
>+				   f->fs.smac[4], f->fs.smac[5],
>+				   f->smtidx);
>+		if (f->fs.newvlan == VLAN_REMOVE)
>+			seq_puts(seq, ", vlan=none");
>+		else if (f->fs.newvlan == VLAN_INSERT)
>+			seq_printf(seq, ", vlan=insert(%x)",
>+				   f->fs.vlan);
>+		else if (f->fs.newvlan == VLAN_REWRITE)
>+			seq_printf(seq, ", vlan=rewrite(%x)",
>+				   f->fs.vlan);
>+	} else {
>+		seq_puts(seq, " Pass: Q=");
>+		if (f->fs.dirsteer == 0) {
>+			seq_puts(seq, "RSS");
>+			if (f->fs.maskhash)
>+				seq_puts(seq, "(TCB=hash)");
>+		} else {
>+			seq_printf(seq, "%d", f->fs.iq);
>+			if (f->fs.dirsteerhash == 0)
>+				seq_puts(seq, "(QID)");
>+			else
>+				seq_puts(seq, "(hash)");
>+		}
>+	}
>+	if (f->fs.prio)
>+		seq_puts(seq, " Prio");
>+	if (f->fs.rpttid)
>+		seq_puts(seq, " RptTID");
>+	seq_puts(seq, "\n");
>+}
>+
>+static int filters_show(struct seq_file *seq, void *v)
>+{
>+	struct adapter *adapter = seq->private;
>+	u32 fconf = adapter->params.tp.vlan_pri_map;
>+	u32 tpiconf = adapter->params.tp.ingress_config;
>+	int i;
>+
>+	if (v == SEQ_START_TOKEN) {
>+		seq_puts(seq, "[[Legend: '!' => locked; '+' => pending set; '-' => pending clear]]\n");
>+		seq_puts(seq, " Idx                   Hits");
>+		for (i = FT_FIRST_S; i <= FT_LAST_S; i++) {
>+			switch (fconf & (1 << i)) {
>+			case 0:
>+				/* compressed filter field not enabled */
>+				break;
>+
>+			case FCOE_F:
>+				seq_puts(seq, " FCoE");
>+				break;
>+
>+			case PORT_F:
>+				seq_puts(seq, " Port");
>+				break;
>+
>+			case VNIC_ID_F:
>+				if ((tpiconf & VNIC_F) == 0)
>+					seq_puts(seq, "     vld:oVLAN");
>+				else
>+					seq_puts(seq, "   VFvld:PF:VF");
>+				break;
>+
>+			case VLAN_F:
>+				seq_puts(seq, "     vld:iVLAN");
>+				break;
>+
>+			case TOS_F:
>+				seq_puts(seq, "   TOS");
>+				break;
>+
>+			case PROTOCOL_F:
>+				seq_puts(seq, "  Prot");
>+				break;
>+
>+			case ETHERTYPE_F:
>+				seq_puts(seq, "   EthType");
>+				break;
>+
>+			case MACMATCH_F:
>+				seq_puts(seq, "  MACIdx");
>+				break;
>+
>+			case MPSHITTYPE_F:
>+				seq_puts(seq, " MPS");
>+				break;
>+
>+			case FRAGMENTATION_F:
>+				seq_puts(seq, " Frag");
>+				break;
>+			}
>+		}
>+		seq_printf(seq, " %65s %65s %9s %9s %s\n",
>+			   "LIP", "FIP", "LPORT", "FPORT", "Action");
>+	} else {
>+		int fidx = (uintptr_t)v - 2;
>+		struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
>+
>+		/* if this entry isn't filled in just return */
>+		if (!f->valid && !f->pending)
>+			return 0;
>+
>+		filters_display(seq, fidx, f, 0);
>+	}
>+	return 0;
>+}
>+
>+static inline void *filters_get_idx(struct adapter *adapter, loff_t pos)
>+{
>+	if (pos > (adapter->tids.nftids + adapter->tids.nsftids))
>+		return NULL;
>+
>+	return (void *)(uintptr_t)(pos + 1);
>+}
>+
>+static void *filters_start(struct seq_file *seq, loff_t *pos)
>+{
>+	struct adapter *adapter = seq->private;
>+
>+	return *pos ? filters_get_idx(adapter, *pos) : SEQ_START_TOKEN;
>+}
>+
>+static void *filters_next(struct seq_file *seq, void *v, loff_t *pos)
>+{
>+	struct adapter *adapter = seq->private;
>+
>+	(*pos)++;
>+	return filters_get_idx(adapter, *pos);
>+}
>+
>+static void filters_stop(struct seq_file *seq, void *v)
>+{
>+}
>+
>+static const struct seq_operations filters_seq_ops = {
>+	.start = filters_start,
>+	.next  = filters_next,
>+	.stop  = filters_stop,
>+	.show  = filters_show
>+};
>+
>+int filters_open(struct inode *inode, struct file *file)
>+{
>+	struct adapter *adapter = inode->i_private;
>+	int res;
>+
>+	res = seq_open(file, &filters_seq_ops);
>+	if (!res) {
>+		struct seq_file *seq = file->private_data;
>+
>+		seq->private = adapter;
>+	}
>+	return res;
>+}
>+
>+const struct file_operations filters_debugfs_fops = {
>+	.owner   = THIS_MODULE,
>+	.open    = filters_open,
>+	.read    = seq_read,
>+	.llseek  = seq_lseek,
>+};
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
>index 23742cb..e801e0b 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
>@@ -37,6 +37,8 @@
> 
> #include "t4_msg.h"
> 
>+extern const struct file_operations filters_debugfs_fops;
>+
> void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl);
> void clear_filter(struct adapter *adap, struct filter_entry *f);
> 
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
>index 36cf307..0115222 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
>@@ -1,7 +1,7 @@
> /*
>  * This file is part of the Chelsio T4 Ethernet driver for Linux.
>  *
>- * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
>+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
>  *
>  * This software is available to you under a choice of one of two
>  * licenses.  You may choose to be licensed under the terms of the GNU
>@@ -121,6 +121,9 @@
>  * selects for a particular field being present.  These fields, when present
>  * in the Compressed Filter Tuple, have the following widths in bits.
>  */
>+#define FT_FIRST_S                      FCOE_S
>+#define FT_LAST_S                       FRAGMENTATION_S
>+
> #define FT_FCOE_W                       1
> #define FT_PORT_W                       3
> #define FT_VNIC_ID_W                    17
>-- 
>2.5.3
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net-next 5/7] cxgb4: add support for setting u32 filters
  2016-09-12  8:12 ` [PATCH net-next 5/7] cxgb4: add support for setting u32 filters Rahul Lakkireddy
@ 2016-09-12  8:40   ` Jiri Pirko
  0 siblings, 0 replies; 16+ messages in thread
From: Jiri Pirko @ 2016-09-12  8:40 UTC (permalink / raw)
  To: Rahul Lakkireddy; +Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil

Mon, Sep 12, 2016 at 10:12:38AM CEST, rahul.lakkireddy@chelsio.com wrote:
>Add support for offloading u32 filter onto hardware.  Links are stored
>in a jump table to perform necessary jumps to match TCP/UDP header.
>When inserting rules in the linked bucket, the TCP/UDP match fields
>in the corresponding entry of the jump table are appended to the filter
>rule before insertion.
>
>Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
>Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
>---
> drivers/net/ethernet/chelsio/cxgb4/Makefile        |   2 +-
> drivers/net/ethernet/chelsio/cxgb4/cxgb4.h         |   3 +
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c    |  36 +++
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c  | 343 +++++++++++++++++++++
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h  |  55 ++++
> .../ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h    |  12 +
> 6 files changed, 450 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
> create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile
>index da88981..c6b71f6 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/Makefile
>+++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile
>@@ -4,7 +4,7 @@
> 
> obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
> 
>-cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o
>+cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o cxgb4_tc_u32.o
> cxgb4-$(CONFIG_CHELSIO_T4_DCB) +=  cxgb4_dcb.o
> cxgb4-$(CONFIG_CHELSIO_T4_FCOE) +=  cxgb4_fcoe.o
> cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>index fbd593a..1adb28f 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>@@ -867,6 +867,9 @@ struct adapter {
> 
> 	spinlock_t stats_lock;
> 	spinlock_t win0_lock ____cacheline_aligned_in_smp;
>+
>+	/* TC u32 offload */
>+	struct cxgb4_tc_u32_table *tc_u32;
> };
> 
> /* Support for "sched-class" command to allow a TX Scheduling Class to be
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>index af07f9d..28396f5 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>@@ -78,6 +78,7 @@
> #include "clip_tbl.h"
> #include "l2t.h"
> #include "sched.h"
>+#include "cxgb4_tc_u32.h"
> 
> char cxgb4_driver_name[] = KBUILD_MODNAME;
> 
>@@ -3027,6 +3028,33 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate)
> 	return err;
> }
> 
>+int cxgb_setup_tc(struct net_device *dev, u32 handle, __be16 proto,
>+		  struct tc_to_netdev *tc)
>+{
>+	struct adapter *adap = netdev2adap(dev);
>+	struct port_info *pi = netdev2pinfo(dev);
>+
>+	if (!(adap->flags & 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_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) &&
>+	    tc->type == TC_SETUP_CLSU32) {
>+		switch (tc->cls_u32->command) {
>+		case TC_CLSU32_NEW_KNODE:
>+		case TC_CLSU32_REPLACE_KNODE:
>+			return cxgb4_config_knode(dev, proto, tc->cls_u32);
>+		default:
>+			return -EOPNOTSUPP;
>+		}
>+	}
>+
>+	return -EOPNOTSUPP;
>+}
>+
> static const struct net_device_ops cxgb4_netdev_ops = {
> 	.ndo_open             = cxgb_open,
> 	.ndo_stop             = cxgb_close,
>@@ -3050,6 +3078,7 @@ static const struct net_device_ops cxgb4_netdev_ops = {
> 	.ndo_busy_poll        = cxgb_busy_poll,
> #endif
> 	.ndo_set_tx_maxrate   = cxgb_set_tx_maxrate,
>+	.ndo_setup_tc         = cxgb_setup_tc,
> };
> 
> #ifdef CONFIG_PCI_IOV
>@@ -4781,6 +4810,7 @@ static void free_some_resources(struct adapter *adapter)
> 	t4_free_mem(adapter->l2t);
> 	t4_cleanup_sched(adapter);
> 	t4_free_mem(adapter->tids.tid_tab);
>+	cxgb4_cleanup_tc_u32(adapter);
> 	kfree(adapter->sge.egr_map);
> 	kfree(adapter->sge.ingr_map);
> 	kfree(adapter->sge.starving_fl);
>@@ -5213,6 +5243,12 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> 		dev_warn(&pdev->dev, "could not allocate TID table, "
> 			 "continuing\n");
> 		adapter->params.offload = 0;
>+	} else {
>+		adapter->tc_u32 = cxgb4_init_tc_u32(adapter,
>+						    CXGB4_MAX_LINK_HANDLE);
>+		if (!adapter->tc_u32)
>+			dev_warn(&pdev->dev,
>+				 "could not offload tc u32, continuing\n");
> 	}
> 
> 	if (is_offload(adapter)) {
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>new file mode 100644
>index 0000000..62c1695
>--- /dev/null
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>@@ -0,0 +1,343 @@
>+/*
>+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
>+ *
>+ * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
>+ *
>+ * This software is available to you under a choice of one of two
>+ * licenses.  You may choose to be licensed under the terms of the GNU
>+ * General Public License (GPL) Version 2, available from the file
>+ * COPYING in the main directory of this source tree, or the
>+ * OpenIB.org BSD license below:
>+ *
>+ *     Redistribution and use in source and binary forms, with or
>+ *     without modification, are permitted provided that the following
>+ *     conditions are met:
>+ *
>+ *      - Redistributions of source code must retain the above
>+ *        copyright notice, this list of conditions and the following
>+ *        disclaimer.
>+ *
>+ *      - Redistributions in binary form must reproduce the above
>+ *        copyright notice, this list of conditions and the following
>+ *        disclaimer in the documentation and/or other materials
>+ *        provided with the distribution.
>+ *
>+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
>+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
>+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
>+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
>+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
>+ * SOFTWARE.
>+ */
>+
>+#include "cxgb4.h"
>+#include "cxgb4_tc_u32_parse.h"
>+#include "cxgb4_tc_u32.h"
>+
>+/* Fill ch_filter_specification with parsed match value/mask pair. */
>+static int fill_match_fields(struct adapter *adap,
>+			     struct ch_filter_specification *fs,
>+			     struct tc_cls_u32_offload *cls,
>+			     const struct cxgb4_match_field *entry,
>+			     bool next_header)
>+{
>+	unsigned int i, j;
>+	int off;
>+	u32 val, mask;
>+	int err;
>+	bool found = false;
>+
>+	for (i = 0; i < cls->knode.sel->nkeys; i++) {
>+		off = cls->knode.sel->keys[i].off;
>+		val = cls->knode.sel->keys[i].val;
>+		mask = cls->knode.sel->keys[i].mask;
>+
>+		if (next_header) {
>+			/* For next headers, parse only keys with offmask */
>+			if (!cls->knode.sel->keys[i].offmask)
>+				continue;
>+		} else {
>+			/* For the remaining, parse only keys without offmask */
>+			if (cls->knode.sel->keys[i].offmask)
>+				continue;
>+		}
>+
>+		found = false;
>+
>+		for (j = 0; entry[j].val; j++) {
>+			if (off == entry[j].off) {
>+				found = true;
>+				err = entry[j].val(fs, val, mask);
>+				if (err)
>+					return err;
>+				break;
>+			}
>+		}
>+
>+		if (!found)
>+			return -EINVAL;
>+	}
>+
>+	return 0;
>+}
>+
>+int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
>+		       struct tc_cls_u32_offload *cls)
>+{
>+	struct adapter *adapter = netdev2adap(dev);
>+	struct cxgb4_tc_u32_table *t;
>+	const struct cxgb4_match_field *start, *link_start = NULL;
>+	struct cxgb4_link *link;
>+	struct ch_filter_specification fs;
>+	struct filter_ctx ctx;
>+	unsigned int filter_id;
>+	u32 uhtid, link_uhtid;
>+	int ret;
>+	bool is_ipv6 = false;
>+
>+	if (!can_tc_u32_offload(dev))
>+		return -EOPNOTSUPP;
>+
>+	if ((protocol != htons(ETH_P_IP)) && (protocol != htons(ETH_P_IPV6)))

You use a lot of unnecessary ()


>+		return -EOPNOTSUPP;
>+
>+	/* Fetch the location to insert the filter. */
>+	filter_id = (cls->knode.handle & 0xFFFFF);

here as well


>+
>+	if (filter_id > adapter->tids.nftids) {
>+		dev_err(adapter->pdev_dev,
>+			"Location %d out of range for insertion. Max: %d\n",
>+			filter_id, adapter->tids.nftids);
>+		return -ERANGE;
>+	}
>+
>+	t = adapter->tc_u32;
>+	uhtid = TC_U32_USERHTID(cls->knode.handle);
>+	link_uhtid = TC_U32_USERHTID(cls->knode.link_handle);
>+
>+	/* Ensure that uhtid is either root u32 (i.e. 0x800)
>+	 * or a a valid linked bucket.
>+	 */
>+	if (uhtid != 0x800 && uhtid >= t->size)
>+		return -EINVAL;
>+
>+	/* Ensure link handle uhtid is sane, if specified. */
>+	if (link_uhtid >= t->size)
>+		return -EINVAL;
>+
>+	memset(&fs, 0, sizeof(fs));
>+
>+	if (protocol == htons(ETH_P_IPV6)) {
>+		start = cxgb4_ipv6_fields;
>+		is_ipv6 = true;
>+	} else {
>+		start = cxgb4_ipv4_fields;
>+		is_ipv6 = false;
>+	}
>+
>+	if (uhtid != 0x800) {
>+		/* Link must exist from root node before insertion. */
>+		if (!t->table[uhtid - 1].link_handle)
>+			return -EINVAL;
>+
>+		/* Link must have a valid supported next header. */
>+		link_start = (&t->table[uhtid - 1])->match_field;

here as well

>+		if (!link_start)
>+			return -EINVAL;
>+	}
>+
>+	/* Parse links and record them for subsequent jumps to valid
>+	 * next headers.
>+	 */
>+	if (link_uhtid) {
>+		const struct cxgb4_next_header *next;
>+		unsigned int i, j;
>+		int off;
>+		u32 val, mask;
>+		bool found = false;
>+
>+		if (t->table[link_uhtid - 1].link_handle) {
>+			dev_err(adapter->pdev_dev,
>+				"Link handle exists for: 0x%x\n",
>+				link_uhtid);
>+			return -EINVAL;
>+		}
>+
>+		next = is_ipv6 ? cxgb4_ipv6_jumps : cxgb4_ipv4_jumps;
>+
>+		/* Try to find matches that allow jumps to next header. */
>+		for (i = 0; next[i].jump; i++) {
>+			if (next[i].offoff != cls->knode.sel->offoff ||
>+			    next[i].shift != cls->knode.sel->offshift ||
>+			    next[i].mask != cls->knode.sel->offmask ||
>+			    next[i].offset != cls->knode.sel->off)
>+				continue;
>+
>+			/* Found a possible candidate.  Find a key that
>+			 * matches the corresponding offset, value, and
>+			 * mask to jump to next header.
>+			 */
>+			for (j = 0; j < cls->knode.sel->nkeys; j++) {
>+				off = cls->knode.sel->keys[j].off;
>+				val = cls->knode.sel->keys[j].val;
>+				mask = cls->knode.sel->keys[j].mask;
>+
>+				if (next[i].match_off == off &&
>+				    next[i].match_val == val &&
>+				    next[i].match_mask == mask) {
>+					found = true;
>+					break;
>+				}
>+			}
>+
>+			if (!found)
>+				continue; /* Try next candidate. */
>+
>+			/* Candidate to jump to next header found.
>+			 * Translate all keys to internal specification
>+			 * and store them in jump table. This spec is copied
>+			 * later to set the actual filters.
>+			 */
>+			ret = fill_match_fields(adapter, &fs, cls,
>+						start, false);
>+			if (ret)
>+				goto out;
>+
>+			link = &t->table[link_uhtid - 1];
>+			link->match_field = next[i].jump;
>+			link->link_handle = cls->knode.handle;
>+			memcpy(&link->fs, &fs, sizeof(fs));
>+			break;
>+		}
>+
>+		/* No candidate found to jump to next header. */
>+		if (!found)
>+			return -EINVAL;
>+
>+		return 0;
>+	}
>+
>+	/* Fill ch_filter_specification match fields to be shipped to hardware.
>+	 * Copy the linked spec (if any) first.  And then update the spec as
>+	 * needed.
>+	 */
>+	if ((uhtid != 0x800) && t->table[uhtid - 1].link_handle) {
>+		/* Copy linked ch_filter_specification */
>+		memcpy(&fs, &t->table[uhtid - 1].fs, sizeof(fs));
>+		ret = fill_match_fields(adapter, &fs, cls,
>+					link_start, true);
>+		if (ret)
>+			goto out;
>+	}
>+
>+	ret = fill_match_fields(adapter, &fs, cls, start, false);
>+	if (ret)
>+		goto out;
>+
>+	/* The filter spec has been completely built from the info
>+	 * provided from u32.  We now set some default fields in the
>+	 * spec for sanity.
>+	 */
>+
>+	/* Match only packets coming from the ingress port where this
>+	 * filter will be created.
>+	 */
>+	fs.val.iport = netdev2pinfo(dev)->port_id;
>+	fs.mask.iport = ~0;
>+
>+	/* Enable filter hit counts. */
>+	fs.hitcnts = 1;
>+
>+	/* Set type of filter - IPv6 or IPv4 */
>+	fs.type = is_ipv6 ? 1 : 0;
>+
>+	init_completion(&ctx.completion);
>+
>+	/* Set the filter */
>+	ret = cxgb4_set_filter(dev, filter_id, &fs, &ctx);
>+	if (ret)
>+		goto out;
>+
>+	/* Wait for reply */
>+	ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ);
>+	if (!ret)
>+		return -ETIMEDOUT;
>+
>+	ret = ctx.result;
>+	if (!ret) {
>+		/* If this is a linked bucket, then set the corresponding
>+		 * entry in the bitmap to mark it as belonging to this linked
>+		 * bucket.
>+		 */
>+		if ((uhtid != 0x800) && t->table[uhtid - 1].link_handle)
>+			set_bit(filter_id, (&t->table[uhtid - 1])->tid_map);
>+	}
>+
>+out:
>+	return ret;
>+}
>+
>+void cxgb4_cleanup_tc_u32(struct adapter *adap)
>+{
>+	struct cxgb4_tc_u32_table *t;
>+	unsigned int i;
>+
>+	if (!adap->tc_u32)
>+		return;
>+
>+	/* Free up all allocated memory. */
>+	t = adap->tc_u32;
>+	for (i = 0; i < t->size; i++) {
>+		struct cxgb4_link *link = &t->table[i];
>+
>+		t4_free_mem(link->tid_map);
>+	}
>+	t4_free_mem(adap->tc_u32);
>+}
>+
>+struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
>+					     unsigned int size)
>+{
>+	struct cxgb4_tc_u32_table *t;
>+	unsigned int i;
>+
>+	if (!size)
>+		return NULL;
>+
>+	t = t4_alloc_mem(sizeof(*t) +
>+			 (size * sizeof(struct cxgb4_link)));
>+	if (!t)
>+		return NULL;
>+
>+	t->size = size;
>+
>+	for (i = 0; i < t->size; i++) {
>+		struct cxgb4_link *link = &t->table[i];
>+		unsigned int bmap_size;
>+		unsigned int max_tids;
>+
>+		max_tids = adap->tids.nftids;
>+		bmap_size = BITS_TO_LONGS(max_tids);
>+		link->tid_map = t4_alloc_mem(sizeof(unsigned long) * bmap_size);
>+		if (!link->tid_map)
>+			goto out_no_mem;
>+		bitmap_zero(link->tid_map, max_tids);
>+	}
>+
>+	return t;
>+
>+out_no_mem:
>+	for (i = 0; i < t->size; i++) {
>+		struct cxgb4_link *link = &t->table[i];
>+
>+		if (link->tid_map)
>+			t4_free_mem(link->tid_map);
>+	}
>+
>+	if (t)
>+		t4_free_mem(t);
>+
>+	return NULL;
>+}
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>new file mode 100644
>index 0000000..46575843
>--- /dev/null
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>@@ -0,0 +1,55 @@
>+/*
>+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
>+ *
>+ * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
>+ *
>+ * This software is available to you under a choice of one of two
>+ * licenses.  You may choose to be licensed under the terms of the GNU
>+ * General Public License (GPL) Version 2, available from the file
>+ * COPYING in the main directory of this source tree, or the
>+ * OpenIB.org BSD license below:
>+ *
>+ *     Redistribution and use in source and binary forms, with or
>+ *     without modification, are permitted provided that the following
>+ *     conditions are met:
>+ *
>+ *      - Redistributions of source code must retain the above
>+ *        copyright notice, this list of conditions and the following
>+ *        disclaimer.
>+ *
>+ *      - Redistributions in binary form must reproduce the above
>+ *        copyright notice, this list of conditions and the following
>+ *        disclaimer in the documentation and/or other materials
>+ *        provided with the distribution.
>+ *
>+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
>+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
>+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
>+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
>+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
>+ * SOFTWARE.
>+ */
>+
>+#ifndef __CXGB4_TC_U32_H
>+#define __CXGB4_TC_U32_H
>+
>+#include <net/pkt_cls.h>
>+
>+#define CXGB4_MAX_LINK_HANDLE 32
>+
>+static inline bool can_tc_u32_offload(struct net_device *dev)
>+{
>+	struct adapter *adap = netdev2adap(dev);
>+
>+	return (dev->features & NETIF_F_HW_TC) && adap->tc_u32 ? true : false;
>+}
>+
>+int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
>+		       struct tc_cls_u32_offload *cls);
>+
>+void cxgb4_cleanup_tc_u32(struct adapter *adapter);
>+struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
>+					     unsigned int size);
>+#endif /* __CXGB4_TC_U32_H */
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
>index 261aa4a..de321bf 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
>@@ -279,4 +279,16 @@ static const struct cxgb4_next_header cxgb4_ipv6_jumps[] = {
> 	  .jump = cxgb4_udp_fields },
> 	{ .jump = NULL }
> };
>+
>+struct cxgb4_link {
>+	const struct cxgb4_match_field *match_field;  /* Next header */
>+	struct ch_filter_specification fs; /* Match spec associated with link */
>+	u32 link_handle;         /* Knode handle associated with the link */
>+	unsigned long *tid_map;  /* Bitmap for filter tids */
>+};
>+
>+struct cxgb4_tc_u32_table {
>+	unsigned int size;          /* number of entries in table */
>+	struct cxgb4_link table[0]; /* Jump table */
>+};
> #endif /* __CXGB4_TC_U32_PARSE_H */
>-- 
>2.5.3
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net-next 6/7] cxgb4: add support for deleting u32 filters
  2016-09-12  8:12 ` [PATCH net-next 6/7] cxgb4: add support for deleting " Rahul Lakkireddy
@ 2016-09-12  8:47   ` Jiri Pirko
  0 siblings, 0 replies; 16+ messages in thread
From: Jiri Pirko @ 2016-09-12  8:47 UTC (permalink / raw)
  To: Rahul Lakkireddy; +Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil

Mon, Sep 12, 2016 at 10:12:39AM CEST, rahul.lakkireddy@chelsio.com wrote:
>Add support for deleting an offloaded u32 filter from hardware.  If a
>link is deleted, then all corresponding filters associated with the link
>are also deleted.  Also enable hardware tc offload as a supported
>feature.
>
>Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
>Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>

I don't understand why add and delete are 2 separate patches. I believe
it should go in one.



>---
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c   |  5 +-
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 92 +++++++++++++++++++++++
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h |  2 +
> 3 files changed, 98 insertions(+), 1 deletion(-)
>
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>index 28396f5..087066a 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>@@ -3047,6 +3047,8 @@ int cxgb_setup_tc(struct net_device *dev, u32 handle, __be16 proto,
> 		case TC_CLSU32_NEW_KNODE:
> 		case TC_CLSU32_REPLACE_KNODE:
> 			return cxgb4_config_knode(dev, proto, tc->cls_u32);
>+		case TC_CLSU32_DELETE_KNODE:
>+			return cxgb4_delete_knode(dev, proto, tc->cls_u32);
> 		default:
> 			return -EOPNOTSUPP;
> 		}
>@@ -5155,7 +5157,8 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> 		netdev->hw_features = NETIF_F_SG | TSO_FLAGS |
> 			NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
> 			NETIF_F_RXCSUM | NETIF_F_RXHASH |
>-			NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
>+			NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
>+			NETIF_F_HW_TC;
> 		if (highdma)
> 			netdev->hw_features |= NETIF_F_HIGHDMA;
> 		netdev->features |= netdev->hw_features;
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>index 62c1695..31847e3 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>@@ -279,6 +279,98 @@ out:
> 	return ret;
> }
> 
>+int cxgb4_delete_knode(struct net_device *dev, __be16 protocol,
>+		       struct tc_cls_u32_offload *cls)
>+{
>+	struct adapter *adapter = netdev2adap(dev);
>+	struct cxgb4_tc_u32_table *t;
>+	struct cxgb4_link *link = NULL;
>+	struct filter_ctx ctx;
>+	u32 handle, uhtid;
>+	unsigned int filter_id;
>+	unsigned int max_tids;
>+	unsigned int i, j;
>+	int ret;
>+
>+	if (!can_tc_u32_offload(dev))
>+		return -EOPNOTSUPP;
>+
>+	/* Fetch the location to delete the filter. */
>+	filter_id = (cls->knode.handle & 0xFFFFF);

Again with the (). Looks like you have it all over the place.


>+
>+	if (filter_id > adapter->tids.nftids) {
>+		dev_err(adapter->pdev_dev,
>+			"Location %d out of range for deletion. Max: %d\n",
>+			filter_id, adapter->tids.nftids);
>+		return -ERANGE;
>+	}
>+
>+	t = adapter->tc_u32;
>+	handle = cls->knode.handle;
>+	uhtid = TC_U32_USERHTID(cls->knode.handle);
>+
>+	/* Ensure that uhtid is either root u32 (i.e. 0x800)
>+	 * or a a valid linked bucket.
>+	 */
>+	if (uhtid != 0x800 && uhtid >= t->size)
>+		return -EINVAL;
>+
>+	/* Delete the specified filter */
>+	if (uhtid != 0x800) {
>+		link = &t->table[uhtid - 1];
>+		if (!link->link_handle)
>+			return -EINVAL;
>+
>+		if (!test_bit(filter_id, link->tid_map))
>+			return -EINVAL;
>+	}
>+
>+	init_completion(&ctx.completion);
>+
>+	ret = cxgb4_del_filter(dev, filter_id, &ctx);
>+	if (ret)
>+		goto out;
>+
>+	/* Wait for reply */
>+	ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ);
>+	if (!ret)
>+		return -ETIMEDOUT;


Hmm, I think it would be nicer to have the completion dance wrapped
inside cxgb4_del_filter and have __cxgb4_del_filter for case you don't
need it.


>+
>+	ret = ctx.result;
>+	if (!ret && link)
>+		clear_bit(filter_id, link->tid_map);
>+
>+	/* If a link is being deleted, then delete all filters
>+	 * associated with the link.
>+	 */
>+	max_tids = adapter->tids.nftids;
>+	for (i = 0; i < t->size; i++) {
>+		link = &t->table[i];
>+
>+		if (link->link_handle == handle) {
>+			for (j = 0; j < max_tids; j++) {
>+				if (!test_bit(j, link->tid_map))
>+					continue;
>+
>+				ret = cxgb4_del_filter(dev, j, NULL);
>+				if (ret)
>+					goto out;
>+
>+				clear_bit(j, link->tid_map);
>+			}
>+
>+			/* Clear the link state */
>+			link->match_field = NULL;
>+			link->link_handle = 0;
>+			memset(&link->fs, 0, sizeof(link->fs));
>+			break;
>+		}
>+	}
>+
>+out:
>+	return ret;
>+}
>+
> void cxgb4_cleanup_tc_u32(struct adapter *adap)
> {
> 	struct cxgb4_tc_u32_table *t;
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>index 46575843..6bdc885 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>@@ -48,6 +48,8 @@ static inline bool can_tc_u32_offload(struct net_device *dev)
> 
> int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
> 		       struct tc_cls_u32_offload *cls);
>+int cxgb4_delete_knode(struct net_device *dev, __be16 protocol,
>+		       struct tc_cls_u32_offload *cls);
> 
> void cxgb4_cleanup_tc_u32(struct adapter *adapter);
> struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
>-- 
>2.5.3
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net-next 7/7] cxgb4: add support for drop and redirect actions
  2016-09-12  8:12 ` [PATCH net-next 7/7] cxgb4: add support for drop and redirect actions Rahul Lakkireddy
@ 2016-09-12  8:52   ` Jiri Pirko
  2016-09-12 15:17     ` John Fastabend
  0 siblings, 1 reply; 16+ messages in thread
From: Jiri Pirko @ 2016-09-12  8:52 UTC (permalink / raw)
  To: Rahul Lakkireddy; +Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil

Mon, Sep 12, 2016 at 10:12:40AM CEST, rahul.lakkireddy@chelsio.com wrote:
>Add support for dropping matched packets in hardware.  Also add support
>for re-directing matched packets to a specified port in hardware.
>
>Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
>Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
>---
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 63 +++++++++++++++++++++++
> 1 file changed, 63 insertions(+)
>
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>index 31847e3..584ccb3 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>@@ -32,6 +32,9 @@
>  * SOFTWARE.
>  */
> 
>+#include <net/tc_act/tc_gact.h>
>+#include <net/tc_act/tc_mirred.h>
>+
> #include "cxgb4.h"
> #include "cxgb4_tc_u32_parse.h"
> #include "cxgb4_tc_u32.h"
>@@ -83,6 +86,59 @@ static int fill_match_fields(struct adapter *adap,
> 	return 0;
> }
> 
>+/* Fill ch_filter_specification with parsed action. */
>+static int fill_action_fields(struct adapter *adap,
>+			      struct ch_filter_specification *fs,
>+			      struct tc_cls_u32_offload *cls)
>+{
>+	const struct tc_action *a;
>+	struct tcf_exts *exts;
>+	LIST_HEAD(actions);
>+	unsigned int num_actions = 0;
>+	bool found = false;
>+
>+	exts = cls->knode.exts;
>+	if (tc_no_actions(exts))
>+		return -EINVAL;
>+
>+	tcf_exts_to_list(exts, &actions);
>+	list_for_each_entry(a, &actions, list) {
>+		/* Don't allow more than one action per rule. */
>+		if (num_actions)
>+			return -EINVAL;


Looking at this, unrelated to this patch, we really need some advanced
reporting to user about what went wrong. Otherwise he's playing a
guessing game.



>+
>+		/* Drop in hardware. */
>+		if (is_tcf_gact_shot(a)) {
>+			fs->action = FILTER_DROP;
>+			found = true;
>+		}
>+
>+		/* Re-direct to specified port in hardware. */
>+		if (is_tcf_mirred_redirect(a)) {

		else if ?

>+			struct net_device *n_dev;
>+			unsigned int i, index;
>+
>+			index = tcf_mirred_ifindex(a);
>+			for_each_port(adap, i) {
>+				n_dev = adap->port[i];
>+				if (index == n_dev->ifindex) {
>+					fs->action = FILTER_SWITCH;
>+					fs->eport = i;
>+					break;
>+				}
>+			}
>+			found = true;
>+		}

You need to report an error in case you don't support the action. 




>+
>+		num_actions++;
>+	}
>+
>+	if (!found)
>+		return -EINVAL;

Oh, I guess this is to fail on unsupported action. Odd. Rather just
return directly the list_for_each_entry loop


>+
>+	return 0;
>+}
>+
> int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
> 		       struct tc_cls_u32_offload *cls)
> {
>@@ -236,6 +292,13 @@ int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
> 	if (ret)
> 		goto out;
> 
>+	/* Fill ch_filter_specification action fields to be shipped to
>+	 * hardware.
>+	 */
>+	ret = fill_action_fields(adapter, &fs, cls);
>+	if (ret)
>+		goto out;
>+
> 	/* The filter spec has been completely built from the info
> 	 * provided from u32.  We now set some default fields in the
> 	 * spec for sanity.
>-- 
>2.5.3
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net-next 2/7] cxgb4: add common api support for configuring filters
  2016-09-12  8:12 ` [PATCH net-next 2/7] cxgb4: add common api support for configuring filters Rahul Lakkireddy
@ 2016-09-12  8:57   ` Jiri Pirko
  0 siblings, 0 replies; 16+ messages in thread
From: Jiri Pirko @ 2016-09-12  8:57 UTC (permalink / raw)
  To: Rahul Lakkireddy; +Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil

Mon, Sep 12, 2016 at 10:12:35AM CEST, rahul.lakkireddy@chelsio.com wrote:
>Enable filters for non-offload configuration and add common api support
>for setting and deleting filters in LE-TCAM region of the hardware.
>
>IPv4 filters occupy one slot.  IPv6 filters occupy 4 slots and must
>be on a 4-slot boundary.  IPv4 filters can not occupy a slot belonging
>to IPv6 and the vice-versa is also true.
>
>Filters are set and deleted asynchronously.  Use completion to wait
>for reply from firmware in order to allow for synchronization if needed.
>
>Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
>Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
>---
> drivers/net/ethernet/chelsio/cxgb4/cxgb4.h        |   3 +
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 424 +++++++++++++++++++++-
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h |   1 +
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c   |  33 +-
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h    |  23 +-
> 5 files changed, 453 insertions(+), 31 deletions(-)
>
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>index 053976f..fbd593a 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>@@ -1055,7 +1055,10 @@ struct filter_entry {
> 
> 	u32 pending:1;          /* filter action is pending firmware reply */
> 	u32 smtidx:8;           /* Source MAC Table index for smac */
>+	struct filter_ctx *ctx; /* Caller's completion hook */
> 	struct l2t_entry *l2t;  /* Layer Two Table entry for dmac */
>+	struct net_device *dev; /* Associated net device */
>+	u32 tid;                /* This will store the actual tid */
> 
> 	/* The filter itself.  Most of this is a straight copy of information
> 	 * provided by the extended ioctl().  Some fields are translated to
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>index 2e86902..490bd94 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>@@ -33,10 +33,138 @@
>  */
> 
> #include "cxgb4.h"
>+#include "t4_regs.h"
> #include "l2t.h"
> #include "t4fw_api.h"
> #include "cxgb4_filter.h"
> 
>+/* Validate filter spec against configuration done on the card.
>+ */
>+static int validate_filter(struct net_device *dev,
>+			   struct ch_filter_specification *fs)
>+{
>+	struct adapter *adapter = netdev2adap(dev);
>+	u32 fconf, iconf;
>+
>+	/* Check for unconfigured fields being used. */
>+	fconf = adapter->params.tp.vlan_pri_map;
>+	iconf = adapter->params.tp.ingress_config;
>+
>+	#define S(_field) \
>+		(fs->val._field || fs->mask._field)
>+	#define U(_mask, _field) \
>+		(!(fconf & (_mask)) && S(_field))


Wow, this is really odd. Please replace these mysterious "S" and "U"
with some properly named static helper function.


>+
>+	if (U(FCOE_F, fcoe) || U(PORT_F, iport) || U(TOS_F, tos) ||
>+	    U(ETHERTYPE_F, ethtype) || U(MACMATCH_F, macidx) ||
>+	    U(MPSHITTYPE_F, matchtype) || U(FRAGMENTATION_F, frag) ||
>+	    U(PROTOCOL_F, proto) ||
>+	    U(VNIC_ID_F, pfvf_vld) ||
>+	    U(VNIC_ID_F, ovlan_vld) ||
>+	    U(VLAN_F, ivlan_vld))
>+		return -EOPNOTSUPP;
>+
>+	/* T4 inconveniently uses the same FT_VNIC_ID_W bits for both the Outer
>+	 * VLAN Tag and PF/VF/VFvld fields based on VNIC_F being set
>+	 * in TP_INGRESS_CONFIG.  Hense the somewhat crazy checks
>+	 * below.  Additionally, since the T4 firmware interface also
>+	 * carries that overlap, we need to translate any PF/VF
>+	 * specification into that internal format below.
>+	 */
>+	if (S(pfvf_vld) && S(ovlan_vld))
>+		return -EOPNOTSUPP;
>+	if ((S(pfvf_vld) && !(iconf & VNIC_F)) ||
>+	    (S(ovlan_vld) && (iconf & VNIC_F)))
>+		return -EOPNOTSUPP;
>+	if (fs->val.pf > 0x7 || fs->val.vf > 0x7f)
>+		return -ERANGE;
>+	fs->mask.pf &= 0x7;
>+	fs->mask.vf &= 0x7f;
>+
>+	#undef S
>+	#undef U
>+

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net-next 7/7] cxgb4: add support for drop and redirect actions
  2016-09-12  8:52   ` Jiri Pirko
@ 2016-09-12 15:17     ` John Fastabend
  0 siblings, 0 replies; 16+ messages in thread
From: John Fastabend @ 2016-09-12 15:17 UTC (permalink / raw)
  To: Jiri Pirko, Rahul Lakkireddy, Jamal Hadi Salim
  Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil

On 16-09-12 01:52 AM, Jiri Pirko wrote:
> Mon, Sep 12, 2016 at 10:12:40AM CEST, rahul.lakkireddy@chelsio.com wrote:
>> Add support for dropping matched packets in hardware.  Also add support
>> for re-directing matched packets to a specified port in hardware.
>>
>> Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
>> Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
>> ---

[...]

>>
>> +/* Fill ch_filter_specification with parsed action. */
>> +static int fill_action_fields(struct adapter *adap,
>> +			      struct ch_filter_specification *fs,
>> +			      struct tc_cls_u32_offload *cls)
>> +{
>> +	const struct tc_action *a;
>> +	struct tcf_exts *exts;
>> +	LIST_HEAD(actions);
>> +	unsigned int num_actions = 0;
>> +	bool found = false;
>> +
>> +	exts = cls->knode.exts;
>> +	if (tc_no_actions(exts))
>> +		return -EINVAL;
>> +
>> +	tcf_exts_to_list(exts, &actions);
>> +	list_for_each_entry(a, &actions, list) {
>> +		/* Don't allow more than one action per rule. */
>> +		if (num_actions)
>> +			return -EINVAL;
> 
> 
> Looking at this, unrelated to this patch, we really need some advanced
> reporting to user about what went wrong. Otherwise he's playing a
> guessing game.
> 

+1 my recommendation to new users has been to annotate or read the
kernel source when they get errors which is obviously a failure on
our part to build usable error messages.

Note its not even really related to hardware offload its bad just
with software use case and the hardware offloads make it a bit more
mysterious when an error is returned.

Maybe Jamal can add it to his tc-workshop at netdev conference so
we can get some consensus about how to do this.

.John

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters
  2016-09-12  8:12 [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
                   ` (6 preceding siblings ...)
  2016-09-12  8:12 ` [PATCH net-next 7/7] cxgb4: add support for drop and redirect actions Rahul Lakkireddy
@ 2016-09-13  9:07 ` Rahul Lakkireddy
  2016-09-13 16:12 ` David Miller
  8 siblings, 0 replies; 16+ messages in thread
From: Rahul Lakkireddy @ 2016-09-13  9:07 UTC (permalink / raw)
  To: Jiri Pirko; +Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil

On Monday, September 09/12/16, 2016 at 13:42:33 +0530, Rahul Lakkireddy wrote:
> This series of patches add support to offload TC u32 filters onto
> Chelsio NICs.
> 
> Patch 1 moves current common filter code to separate files
> in order to provide a common api for performing packet classification
> and filtering in Chelsio NICs.
> 
> Patch 2 enables filters for normal NIC configuration and implements
> common api for setting and deleting filters.
> 
> Patch 3 provides a debugfs for dumping filter information.
> 
> Patches 4-7 add support for TC u32 offload via ndo_setup_tc.
> 
> Rahul Lakkireddy (7):
>   cxgb4: move common filter code to separate file
>   cxgb4: add common api support for configuring filters
>   cxgb4: add debugfs support to dump filter debug logs
>   cxgb4: add parser to translate u32 filters to internal spec
>   cxgb4: add support for setting u32 filters
>   cxgb4: add support for deleting u32 filters
>   cxgb4: add support for drop and redirect actions
> 
>  drivers/net/ethernet/chelsio/cxgb4/Makefile        |    2 +-
>  drivers/net/ethernet/chelsio/cxgb4/cxgb4.h         |   30 +
>  drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c |    4 +-
>  drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c  | 1086 ++++++++++++++++++++
>  drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h  |   50 +
>  drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c    |  338 ++----
>  drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c  |  498 +++++++++
>  drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h  |   57 +
>  .../ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h    |  294 ++++++
>  drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h     |   23 +-
>  drivers/net/ethernet/chelsio/cxgb4/t4_values.h     |    5 +-
>  11 files changed, 2104 insertions(+), 283 deletions(-)
>  create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>  create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
>  create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>  create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>  create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
> 
> -- 
> 2.5.3
> 

Thanks for the review Jiri.  Will send a v2 with necessary changes.

Thanks,
Rahul

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters
  2016-09-12  8:12 [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
                   ` (7 preceding siblings ...)
  2016-09-13  9:07 ` [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
@ 2016-09-13 16:12 ` David Miller
  8 siblings, 0 replies; 16+ messages in thread
From: David Miller @ 2016-09-13 16:12 UTC (permalink / raw)
  To: rahul.lakkireddy; +Cc: netdev, hariprasad, leedom, nirranjan, indranil

From: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Date: Mon, 12 Sep 2016 13:42:33 +0530

> This series of patches add support to offload TC u32 filters onto
> Chelsio NICs.
> 
> Patch 1 moves current common filter code to separate files
> in order to provide a common api for performing packet classification
> and filtering in Chelsio NICs.
> 
> Patch 2 enables filters for normal NIC configuration and implements
> common api for setting and deleting filters.
> 
> Patch 3 provides a debugfs for dumping filter information.
> 
> Patches 4-7 add support for TC u32 offload via ndo_setup_tc.

Like Jiri I'm getting increasingly disappointed how liberally people
toss tons of things into debugfs.

This seems to be the way driver developers throw their hands into the
air when they can't find a quick and easy way to export some piece of
information.

Please work on the long term usability of the kernel and all
networking drivers by finding a bonafide piece of existing
infrastructure by which to export things, or build a new one if
needed.

Thanks.

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2016-09-13 16:12 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-12  8:12 [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
2016-09-12  8:12 ` [PATCH net-next 1/7] cxgb4: move common filter code to separate file Rahul Lakkireddy
2016-09-12  8:12 ` [PATCH net-next 2/7] cxgb4: add common api support for configuring filters Rahul Lakkireddy
2016-09-12  8:57   ` Jiri Pirko
2016-09-12  8:12 ` [PATCH net-next 3/7] cxgb4: add debugfs support to dump filter debug logs Rahul Lakkireddy
2016-09-12  8:36   ` Jiri Pirko
2016-09-12  8:12 ` [PATCH net-next 4/7] cxgb4: add parser to translate u32 filters to internal spec Rahul Lakkireddy
2016-09-12  8:12 ` [PATCH net-next 5/7] cxgb4: add support for setting u32 filters Rahul Lakkireddy
2016-09-12  8:40   ` Jiri Pirko
2016-09-12  8:12 ` [PATCH net-next 6/7] cxgb4: add support for deleting " Rahul Lakkireddy
2016-09-12  8:47   ` Jiri Pirko
2016-09-12  8:12 ` [PATCH net-next 7/7] cxgb4: add support for drop and redirect actions Rahul Lakkireddy
2016-09-12  8:52   ` Jiri Pirko
2016-09-12 15:17     ` John Fastabend
2016-09-13  9:07 ` [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters Rahul Lakkireddy
2016-09-13 16:12 ` David Miller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.