svn commit: r333394 - in head/sys/dev/cxgbe: . common firmware tom

Navdeep Parhar np at FreeBSD.org
Wed May 9 04:09:52 UTC 2018


Author: np
Date: Wed May  9 04:09:49 2018
New Revision: 333394
URL: https://svnweb.freebsd.org/changeset/base/333394

Log:
  cxgbe(4): Add support for hash filters.
  
  These filters reside in the card's memory instead of its TCAM and can be
  configured via a new "hashfilter" subcommand in cxgbetool.  Hash and
  normal TCAM filters can be used together.  The hardware does an
  exact-match of packet fields for hash filters, unlike the masked match
  performed for TCAM filters.  Any T5/T6 card with memory can support at
  least half a million hash filters.  The sample config file with the
  driver configures 512K of these, it is possible to double this to 1
  million+ in some cases.
  
  The chip does an exact-match of fields of incoming datagrams with hash
  filters and performs the action configured for the filter if it matches.
  The fields to match are specified in a "filter mask" in the firmware
  config file.  The filter mask always includes the 5-tuple (sip, dip,
  sport, dport, ipproto).  It can, optionally, also include any subset of
  the filter mode (see filterMode and filterMask in the firmware config
  file).
  
  For example:
  filterMode = fragmentation, mpshittype, protocol, vlan, port, fcoe
  filterMask = protocol, port, vlan
  
  Exact values of the 5-tuple, the physical port, and VLAN tag would have
  to be provided while setting up a hash filter with the chip
  configuration above.
  
  Hash filters support all actions supported by TCAM filters.  A packet
  that hits a hash filter can be dropped, let through (with optional
  steering to a specific queue or RSS region), switched out of another
  port (with optional L2 rewrite of DMAC, SMAC, VLAN tag), or get NAT'ed.
  (Support for some of these will show up in the driver in a follow-up
  commit very shortly).
  
  Sponsored by:	Chelsio Communications

Added:
  head/sys/dev/cxgbe/firmware/t6fw_cfg_hashfilter.txt   (contents, props changed)
Modified:
  head/sys/dev/cxgbe/adapter.h
  head/sys/dev/cxgbe/common/common.h
  head/sys/dev/cxgbe/common/t4_msg.h
  head/sys/dev/cxgbe/offload.h
  head/sys/dev/cxgbe/t4_filter.c
  head/sys/dev/cxgbe/t4_ioctl.h
  head/sys/dev/cxgbe/t4_main.c
  head/sys/dev/cxgbe/t4_sge.c
  head/sys/dev/cxgbe/tom/t4_connect.c
  head/sys/dev/cxgbe/tom/t4_cpl_io.c

Modified: head/sys/dev/cxgbe/adapter.h
==============================================================================
--- head/sys/dev/cxgbe/adapter.h	Wed May  9 02:07:09 2018	(r333393)
+++ head/sys/dev/cxgbe/adapter.h	Wed May  9 04:09:49 2018	(r333394)
@@ -371,7 +371,7 @@ enum {
 	CPL_COOKIE_DDP0,
 	CPL_COOKIE_DDP1,
 	CPL_COOKIE_TOM,
-	CPL_COOKIE_AVAILABLE1,
+	CPL_COOKIE_HASHFILTER,
 	CPL_COOKIE_AVAILABLE2,
 	CPL_COOKIE_AVAILABLE3,
 
@@ -1244,6 +1244,9 @@ int get_filter(struct adapter *, struct t4_filter *);
 int set_filter(struct adapter *, struct t4_filter *);
 int del_filter(struct adapter *, struct t4_filter *);
 int t4_filter_rpl(struct sge_iq *, const struct rss_header *, struct mbuf *);
+int t4_hashfilter_ao_rpl(struct sge_iq *, const struct rss_header *, struct mbuf *);
+int t4_hashfilter_tcb_rpl(struct sge_iq *, const struct rss_header *, struct mbuf *);
+int t4_del_hashfilter_rpl(struct sge_iq *, const struct rss_header *, struct mbuf *);
 
 static inline struct wrqe *
 alloc_wrqe(int wr_len, struct sge_wrq *wrq)

Modified: head/sys/dev/cxgbe/common/common.h
==============================================================================
--- head/sys/dev/cxgbe/common/common.h	Wed May  9 02:07:09 2018	(r333393)
+++ head/sys/dev/cxgbe/common/common.h	Wed May  9 04:09:49 2018	(r333394)
@@ -370,6 +370,7 @@ struct adapter_params {
 				   resources for TOE operation. */
 	unsigned int bypass:1;	/* this is a bypass card */
 	unsigned int ethoffload:1;
+	unsigned int hash_filter:1;
 
 	unsigned int ofldq_wr_cred;
 	unsigned int eo_wr_cred;
@@ -456,6 +457,11 @@ static inline int is_offload(const struct adapter *ada
 static inline int is_ethoffload(const struct adapter *adap)
 {
 	return adap->params.ethoffload;
+}
+
+static inline int is_hashfilter(const struct adapter *adap)
+{
+	return adap->params.hash_filter;
 }
 
 static inline int chip_id(struct adapter *adap)

Modified: head/sys/dev/cxgbe/common/t4_msg.h
==============================================================================
--- head/sys/dev/cxgbe/common/t4_msg.h	Wed May  9 02:07:09 2018	(r333393)
+++ head/sys/dev/cxgbe/common/t4_msg.h	Wed May  9 04:09:49 2018	(r333394)
@@ -197,6 +197,30 @@ static inline int act_open_has_tid(int status)
 		status != CPL_ERR_CONN_EXIST);
 }
 
+/*
+ * Convert an ACT_OPEN_RPL status to an errno.
+ */
+static inline int
+act_open_rpl_status_to_errno(int status)
+{
+
+	switch (status) {
+	case CPL_ERR_CONN_RESET:
+		return (ECONNREFUSED);
+	case CPL_ERR_ARP_MISS:
+		return (EHOSTUNREACH);
+	case CPL_ERR_CONN_TIMEDOUT:
+		return (ETIMEDOUT);
+	case CPL_ERR_TCAM_FULL:
+		return (EAGAIN);
+	case CPL_ERR_CONN_EXIST:
+		return (EAGAIN);
+	default:
+		return (EIO);
+	}
+}
+
+
 enum {
 	CPL_CONN_POLICY_AUTO = 0,
 	CPL_CONN_POLICY_ASK  = 1,
@@ -1040,6 +1064,14 @@ struct cpl_abort_req {
 	__u8  rsvd2[6];
 };
 
+struct cpl_abort_req_core {
+	union opcode_tid ot;
+	__be32 rsvd0;
+	__u8  rsvd1;
+	__u8  cmd;
+	__u8  rsvd2[6];
+};
+
 struct cpl_abort_rpl_rss {
 	RSS_HDR
 	union opcode_tid ot;
@@ -1055,6 +1087,14 @@ struct cpl_abort_rpl_rss6 {
 
 struct cpl_abort_rpl {
 	WR_HDR;
+	union opcode_tid ot;
+	__be32 rsvd0;
+	__u8  rsvd1;
+	__u8  cmd;
+	__u8  rsvd2[6];
+};
+
+struct cpl_abort_rpl_core {
 	union opcode_tid ot;
 	__be32 rsvd0;
 	__u8  rsvd1;

Added: head/sys/dev/cxgbe/firmware/t6fw_cfg_hashfilter.txt
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/cxgbe/firmware/t6fw_cfg_hashfilter.txt	Wed May  9 04:09:49 2018	(r333394)
@@ -0,0 +1,265 @@
+# Firmware configuration file.
+#
+# Global limits (some are hardware limits, others are due to the firmware).
+# nvi = 128		virtual interfaces
+# niqflint = 1023	ingress queues with freelists and/or interrupts
+# nethctrl = 64K	Ethernet or ctrl egress queues
+# neq = 64K		egress queues of all kinds, including freelists
+# nexactf = 512		MPS TCAM entries, can oversubscribe.
+
+[global]
+	rss_glb_config_mode = basicvirtual
+	rss_glb_config_options = tnlmapen,hashtoeplitz,tnlalllkp
+
+	# PL_TIMEOUT register
+	pl_timeout_value = 200		# the timeout value in units of us
+
+	sge_timer_value = 1, 5, 10, 50, 100, 200	# SGE_TIMER_VALUE* in usecs
+
+	reg[0x10c4] = 0x20000000/0x20000000 # GK_CONTROL, enable 5th thread
+
+	reg[0x7dc0] = 0x0e2f8849	# TP_SHIFT_CNT
+
+	#Tick granularities in kbps
+	tsch_ticks = 100000, 10000, 1000, 10
+
+	filterMode = fragmentation, mpshittype, protocol, vlan, port, fcoe
+	filterMask = port, protocol
+
+	tp_pmrx = 20, 512
+	tp_pmrx_pagesize = 16K
+
+	# TP number of RX channels (0 = auto)
+	tp_nrxch = 0
+
+	tp_pmtx = 40, 512
+	tp_pmtx_pagesize = 64K
+
+	# TP number of TX channels (0 = auto)
+	tp_ntxch = 0
+
+	# TP OFLD MTUs
+	tp_mtus = 88, 256, 512, 576, 808, 1024, 1280, 1488, 1500, 2002, 2048, 4096, 4352, 8192, 9000, 9600
+
+	# enable TP_OUT_CONFIG.IPIDSPLITMODE and CRXPKTENC
+	reg[0x7d04] = 0x00010008/0x00010008
+
+	# TP_GLOBAL_CONFIG
+	reg[0x7d08] = 0x00000800/0x00000800 # set IssFromCplEnable
+
+	# TP_PC_CONFIG
+	reg[0x7d48] = 0x00000000/0x00000400 # clear EnableFLMError
+
+	# TP_PC_CONFIG2
+	reg[0x7d4c] = 0x00010000/0x00010000 # set DisableNewPshFlag
+
+	# TP_PARA_REG0
+	reg[0x7d60] = 0x06000000/0x07000000 # set InitCWND to 6
+
+	# TP_PARA_REG3
+	reg[0x7d6c] = 0x28000000/0x28000000 # set EnableTnlCngHdr
+					    # set RxMacCheck (Note:
+					    # Only for hash filter,
+					    # no tcp offload)
+
+	# LE_DB_CONFIG
+	reg[0x19c04] = 0x00000000/0x02040000 # LE IPv4 compression disabled
+					     # EXTN_HASH_IPV4 Disable
+
+	# LE_DB_RSP_CODE_0
+	reg[0x19c74] = 0x00000004/0x0000000f # TCAM_ACTV_HIT = 4
+
+	# LE_DB_RSP_CODE_1
+	reg[0x19c78] = 0x08000000/0x0e000000 # HASH_ACTV_HIT = 4
+
+	# LE_DB_HASH_CONFIG
+	reg[0x19c28] = 0x00800000/0x01f00000 # LE Hash bucket size 8, 
+
+	# MC configuration
+	mc_mode_brc[0] = 0		# mc0 - 1: enable BRC, 0: enable RBC
+
+# PFs 0-3.  These get 8 MSI/8 MSI-X vectors each.  VFs are supported by
+# these 4 PFs only.
+[function "0"]
+	nvf = 4
+	wx_caps = all
+	r_caps = all
+	nvi = 2
+	rssnvi = 2
+	niqflint = 4
+	nethctrl = 4
+	neq = 8
+	nexactf = 4
+	cmask = all
+	pmask = 0x1
+
+[function "1"]
+	nvf = 4
+	wx_caps = all
+	r_caps = all
+	nvi = 2
+	rssnvi = 2
+	niqflint = 4
+	nethctrl = 4
+	neq = 8
+	nexactf = 4
+	cmask = all
+	pmask = 0x2
+
+[function "2"]
+	nvf = 4
+	wx_caps = all
+	r_caps = all
+	nvi = 2
+	rssnvi = 2
+	niqflint = 4
+	nethctrl = 4
+	neq = 8
+	nexactf = 4
+	cmask = all
+	pmask = 0x4
+
+[function "3"]
+	nvf = 4
+	wx_caps = all
+	r_caps = all
+	nvi = 2
+	rssnvi = 2
+	niqflint = 4
+	nethctrl = 4
+	neq = 8
+	nexactf = 4
+	cmask = all
+	pmask = 0x8
+
+# PF4 is the resource-rich PF that the bus/nexus driver attaches to.
+# It gets 32 MSI/128 MSI-X vectors.
+[function "4"]
+	wx_caps = all
+	r_caps = all
+	nvi = 32
+	rssnvi = 8
+	niqflint = 512
+	nethctrl = 1024
+	neq = 2048
+	nqpcq = 8192
+	nexactf = 456
+	cmask = all
+	pmask = all
+	nclip = 320
+
+	# TCAM has 6K cells; each region must start at a multiple of 128 cell.
+	# Each entry in these categories takes 2 cells each.  nhash will use the
+	# TCAM iff there is room left (that is, the rest don't add up to 3072).
+	nfilter = 2032
+	nserver = 512
+	nhpfilter = 0
+	nhash = 524288
+	protocol = nic_hashfilter
+	tp_l2t = 4096
+
+# PF5 is the SCSI Controller PF. It gets 32 MSI/40 MSI-X vectors.
+# Not used right now.
+[function "5"]
+	nvi = 1
+	rssnvi = 0
+
+# PF6 is the FCoE Controller PF. It gets 32 MSI/40 MSI-X vectors.
+# Not used right now.
+[function "6"]
+	nvi = 1
+	rssnvi = 0
+
+# The following function, 1023, is not an actual PCIE function but is used to
+# configure and reserve firmware internal resources that come from the global
+# resource pool.
+#
+[function "1023"]
+	wx_caps = all
+	r_caps = all
+	nvi = 4
+	rssnvi = 0
+	cmask = all
+	pmask = all
+	nexactf = 8
+	nfilter = 16
+
+
+# For Virtual functions, we only allow NIC functionality and we only allow
+# access to one port (1 << PF).  Note that because of limitations in the
+# Scatter Gather Engine (SGE) hardware which checks writes to VF KDOORBELL
+# and GTS registers, the number of Ingress and Egress Queues must be a power
+# of 2.
+#
+[function "0/*"]
+	wx_caps = 0x82
+	r_caps = 0x86
+	nvi = 1
+	rssnvi = 1
+	niqflint = 2
+	nethctrl = 2
+	neq = 4
+	nexactf = 2
+	cmask = all
+	pmask = 0x1
+
+[function "1/*"]
+	wx_caps = 0x82
+	r_caps = 0x86
+	nvi = 1
+	rssnvi = 1
+	niqflint = 2
+	nethctrl = 2
+	neq = 4
+	nexactf = 2
+	cmask = all
+	pmask = 0x2
+
+[function "2/*"]
+	wx_caps = 0x82
+	r_caps = 0x86
+	nvi = 1
+	rssnvi = 1
+	niqflint = 2
+	nethctrl = 2
+	neq = 4
+	nexactf = 2
+	cmask = all
+	pmask = 0x1
+
+[function "3/*"]
+	wx_caps = 0x82
+	r_caps = 0x86
+	nvi = 1
+	rssnvi = 1
+	niqflint = 2
+	nethctrl = 2
+	neq = 4
+	nexactf = 2
+	cmask = all
+	pmask = 0x2
+
+# MPS has 192K buffer space for ingress packets from the wire as well as
+# loopback path of the L2 switch.
+[port "0"]
+	dcb = none
+	#bg_mem = 25
+	#lpbk_mem = 25
+	hwm = 60
+	lwm = 15
+	dwm = 30
+
+[port "1"]
+	dcb = none
+	#bg_mem = 25
+	#lpbk_mem = 25
+	hwm = 60
+	lwm = 15
+	dwm = 30
+
+[fini]
+	version = 0x1
+	checksum = 0xb577311e
+#
+# $FreeBSD$
+#

Modified: head/sys/dev/cxgbe/offload.h
==============================================================================
--- head/sys/dev/cxgbe/offload.h	Wed May  9 02:07:09 2018	(r333393)
+++ head/sys/dev/cxgbe/offload.h	Wed May  9 04:09:49 2018	(r333394)
@@ -32,6 +32,9 @@
 
 #ifndef __T4_OFFLOAD_H__
 #define __T4_OFFLOAD_H__
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/condvar.h>
 
 #define INIT_ULPTX_WRH(w, wrlen, atomic, tid) do { \
 	(w)->wr_hi = htonl(V_FW_WR_OP(FW_ULPTX_WR) | V_FW_WR_ATOMIC(atomic)); \
@@ -100,10 +103,16 @@ struct tid_info {
 	u_int atids_in_use;
 
 	struct mtx ftid_lock __aligned(CACHE_LINE_SIZE);
+	struct cv ftid_cv;
 	struct filter_entry *ftid_tab;
 	u_int nftids;
 	u_int ftid_base;
 	u_int ftids_in_use;
+
+	struct mtx hftid_lock __aligned(CACHE_LINE_SIZE);
+	struct cv hftid_cv;
+	void **hftid_tab;
+	/* ntids, tids_in_use */
 
 	struct mtx etid_lock __aligned(CACHE_LINE_SIZE);
 	struct etid_entry *etid_tab;

Modified: head/sys/dev/cxgbe/t4_filter.c
==============================================================================
--- head/sys/dev/cxgbe/t4_filter.c	Wed May  9 02:07:09 2018	(r333393)
+++ head/sys/dev/cxgbe/t4_filter.c	Wed May  9 04:09:49 2018	(r333394)
@@ -47,18 +47,54 @@ __FBSDID("$FreeBSD$");
 #include "common/common.h"
 #include "common/t4_msg.h"
 #include "common/t4_regs.h"
+#include "common/t4_regs_values.h"
+#include "common/t4_tcb.h"
 #include "t4_l2t.h"
 
 struct filter_entry {
-        uint32_t valid:1;	/* filter allocated and valid */
-        uint32_t locked:1;	/* filter is administratively locked */
-        uint32_t pending:1;	/* filter action is pending firmware reply */
+	uint32_t valid:1;	/* filter allocated and valid */
+	uint32_t locked:1;	/* filter is administratively locked or busy */
+	uint32_t pending:1;	/* filter action is pending firmware reply */
 	uint32_t smtidx:8;	/* Source MAC Table index for smac */
-	struct l2t_entry *l2t;	/* Layer Two Table entry for dmac */
+	int tid;		/* tid of the filter TCB */
+	struct l2t_entry *l2te;	/* L2 table entry for DMAC rewrite */
 
-        struct t4_filter_specification fs;
+	struct t4_filter_specification fs;
 };
 
+static void free_filter_resources(struct filter_entry *);
+static int get_hashfilter(struct adapter *, struct t4_filter *);
+static int set_hashfilter(struct adapter *, struct t4_filter *,
+    struct l2t_entry *);
+static int del_hashfilter(struct adapter *, struct t4_filter *);
+static int configure_hashfilter_tcb(struct adapter *, struct filter_entry *);
+
+static void
+insert_hftid(struct adapter *sc, int tid, void *ctx, int ntids)
+{
+	struct tid_info *t = &sc->tids;
+
+	t->hftid_tab[tid] = ctx;
+	atomic_add_int(&t->tids_in_use, ntids);
+}
+
+static void *
+lookup_hftid(struct adapter *sc, int tid)
+{
+	struct tid_info *t = &sc->tids;
+
+	return (t->hftid_tab[tid]);
+}
+
+static void
+remove_hftid(struct adapter *sc, int tid, int ntids)
+{
+	struct tid_info *t = &sc->tids;
+
+	t->hftid_tab[tid] = NULL;
+	atomic_subtract_int(&t->tids_in_use, ntids);
+}
+
 static uint32_t
 fconf_iconf_to_mode(uint32_t fconf, uint32_t iconf)
 {
@@ -209,7 +245,7 @@ get_filter_mode(struct adapter *sc, uint32_t *mode)
 	/*
 	 * We trust the cached values of the relevant TP registers.  This means
 	 * things work reliably only if writes to those registers are always via
-	 * t4_set_filter_mode_.
+	 * t4_set_filter_mode.
 	 */
 	*mode = fconf_iconf_to_mode(tpp->vlan_pri_map, tpp->ingress_config);
 
@@ -230,6 +266,10 @@ set_filter_mode(struct adapter *sc, uint32_t mode)
 		 * already set to the correct value for the requested filter
 		 * mode.  It's not clear if it's safe to write to this register
 		 * on the fly.  (And we trust the cached value of the register).
+		 *
+		 * check_fspec_against_fconf_iconf and other code that looks at
+		 * tp->vlan_pri_map and tp->ingress_config needs to be reviewed
+		 * thorougly before allowing dynamic filter mode changes.
 		 */
 		return (EBUSY);
 	}
@@ -260,12 +300,11 @@ done:
 }
 
 static inline uint64_t
-get_filter_hits(struct adapter *sc, uint32_t fid)
+get_filter_hits(struct adapter *sc, uint32_t tid)
 {
 	uint32_t tcb_addr;
 
-	tcb_addr = t4_read_reg(sc, A_TP_CMM_TCB_BASE) +
-	    (fid + sc->tids.ftid_base) * TCB_SIZE;
+	tcb_addr = t4_read_reg(sc, A_TP_CMM_TCB_BASE) + tid * TCB_SIZE;
 
 	if (is_t4(sc)) {
 		uint64_t hits;
@@ -283,28 +322,28 @@ get_filter_hits(struct adapter *sc, uint32_t fid)
 int
 get_filter(struct adapter *sc, struct t4_filter *t)
 {
-	int i, rc, nfilters = sc->tids.nftids;
+	int i, nfilters = sc->tids.nftids;
 	struct filter_entry *f;
 
-	rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK,
-	    "t4getf");
-	if (rc)
-		return (rc);
+	if (t->fs.hash)
+		return (get_hashfilter(sc, t));
 
 	if (sc->tids.ftids_in_use == 0 || sc->tids.ftid_tab == NULL ||
 	    t->idx >= nfilters) {
 		t->idx = 0xffffffff;
-		goto done;
+		return (0);
 	}
 
+	mtx_lock(&sc->tids.ftid_lock);
 	f = &sc->tids.ftid_tab[t->idx];
+	MPASS(f->tid == sc->tids.ftid_base + t->idx);
 	for (i = t->idx; i < nfilters; i++, f++) {
 		if (f->valid) {
 			t->idx = i;
-			t->l2tidx = f->l2t ? f->l2t->idx : 0;
+			t->l2tidx = f->l2te ? f->l2te->idx : 0;
 			t->smtidx = f->smtidx;
 			if (f->fs.hitcnts)
-				t->hits = get_filter_hits(sc, t->idx);
+				t->hits = get_filter_hits(sc, f->tid);
 			else
 				t->hits = UINT64_MAX;
 			t->fs = f->fs;
@@ -312,59 +351,78 @@ get_filter(struct adapter *sc, struct t4_filter *t)
 			goto done;
 		}
 	}
-
 	t->idx = 0xffffffff;
 done:
-	end_synchronized_op(sc, LOCK_HELD);
+	mtx_unlock(&sc->tids.ftid_lock);
 	return (0);
 }
 
 static int
-set_filter_wr(struct adapter *sc, int fidx)
+set_tcamfilter(struct adapter *sc, struct t4_filter *t, struct l2t_entry *l2te)
 {
-	struct filter_entry *f = &sc->tids.ftid_tab[fidx];
+	struct filter_entry *f;
 	struct fw_filter_wr *fwr;
-	unsigned int ftid, vnic_vld, vnic_vld_mask;
+	u_int vnic_vld, vnic_vld_mask;
 	struct wrq_cookie cookie;
+	int i, rc, busy, locked;
+	const int ntids = t->fs.type ? 4 : 1;
 
-	ASSERT_SYNCHRONIZED_OP(sc);
+	MPASS(!t->fs.hash);
+	MPASS(t->idx < sc->tids.nftids);
+	/* Already validated against fconf, iconf */
+	MPASS((t->fs.val.pfvf_vld & t->fs.val.ovlan_vld) == 0);
+	MPASS((t->fs.mask.pfvf_vld & t->fs.mask.ovlan_vld) == 0);
 
-	if (f->fs.newdmac || f->fs.newvlan) {
-		/* This filter needs an L2T entry; allocate one. */
-		f->l2t = t4_l2t_alloc_switching(sc->l2t);
-		if (f->l2t == NULL)
-			return (EAGAIN);
-		if (t4_l2t_set_switching(sc, f->l2t, f->fs.vlan, f->fs.eport,
-		    f->fs.dmac)) {
-			t4_l2t_release(f->l2t);
-			f->l2t = NULL;
-			return (ENOMEM);
+	f = &sc->tids.ftid_tab[t->idx];
+	rc = busy = locked = 0;
+	mtx_lock(&sc->tids.ftid_lock);
+	for (i = 0; i < ntids; i++) {
+		busy += f[i].pending + f[i].valid;
+		locked += f[i].locked;
+	}
+	if (locked > 0)
+		rc = EPERM;
+	else if (busy > 0)
+		rc = EBUSY;
+	else {
+		fwr = start_wrq_wr(&sc->sge.mgmtq, howmany(sizeof(*fwr), 16),
+		    &cookie);
+		if (__predict_false(fwr == NULL))
+			rc = ENOMEM;
+		else {
+			f->pending = 1;
+			sc->tids.ftids_in_use++;
 		}
 	}
+	mtx_unlock(&sc->tids.ftid_lock);
+	if (rc != 0) {
+		if (l2te)
+			t4_l2t_release(l2te);
+		return (rc);
+	}
 
-	/* Already validated against fconf, iconf */
-	MPASS((f->fs.val.pfvf_vld & f->fs.val.ovlan_vld) == 0);
-	MPASS((f->fs.mask.pfvf_vld & f->fs.mask.ovlan_vld) == 0);
-	if (f->fs.val.pfvf_vld || f->fs.val.ovlan_vld)
+	/*
+	 * Can't fail now.  A set-filter WR will definitely be sent.
+	 */
+
+	f->tid = sc->tids.ftid_base + t->idx;
+	f->fs = t->fs;
+	f->l2te = l2te;
+
+	if (t->fs.val.pfvf_vld || t->fs.val.ovlan_vld)
 		vnic_vld = 1;
 	else
 		vnic_vld = 0;
-	if (f->fs.mask.pfvf_vld || f->fs.mask.ovlan_vld)
+	if (t->fs.mask.pfvf_vld || t->fs.mask.ovlan_vld)
 		vnic_vld_mask = 1;
 	else
 		vnic_vld_mask = 0;
 
-	ftid = sc->tids.ftid_base + fidx;
-
-	fwr = start_wrq_wr(&sc->sge.mgmtq, howmany(sizeof(*fwr), 16), &cookie);
-	if (fwr == NULL)
-		return (ENOMEM);
 	bzero(fwr, sizeof(*fwr));
-
 	fwr->op_pkd = htobe32(V_FW_WR_OP(FW_FILTER_WR));
 	fwr->len16_pkd = htobe32(FW_LEN16(*fwr));
 	fwr->tid_to_iq =
-	    htobe32(V_FW_FILTER_WR_TID(ftid) |
+	    htobe32(V_FW_FILTER_WR_TID(f->tid) |
 		V_FW_FILTER_WR_RQTYPE(f->fs.type) |
 		V_FW_FILTER_WR_NOREPLY(0) |
 		V_FW_FILTER_WR_IQ(f->fs.iq));
@@ -384,7 +442,7 @@ set_filter_wr(struct adapter *sc, int fidx)
 		V_FW_FILTER_WR_HITCNTS(f->fs.hitcnts) |
 		V_FW_FILTER_WR_TXCHAN(f->fs.eport) |
 		V_FW_FILTER_WR_PRIO(f->fs.prio) |
-		V_FW_FILTER_WR_L2TIX(f->l2t ? f->l2t->idx : 0));
+		V_FW_FILTER_WR_L2TIX(f->l2te ? f->l2te->idx : 0));
 	fwr->ethtype = htobe16(f->fs.val.ethtype);
 	fwr->ethtypem = htobe16(f->fs.mask.ethtype);
 	fwr->frag_to_ovlan_vldm =
@@ -422,265 +480,948 @@ set_filter_wr(struct adapter *sc, int fidx)
 	fwr->lpm = htobe16(f->fs.mask.dport);
 	fwr->fp = htobe16(f->fs.val.sport);
 	fwr->fpm = htobe16(f->fs.mask.sport);
-	if (f->fs.newsmac)
+	if (f->fs.newsmac) {
+		/* XXX: need to use SMT idx instead */
 		bcopy(f->fs.smac, fwr->sma, sizeof (fwr->sma));
-
-	f->pending = 1;
-	sc->tids.ftids_in_use++;
-
+	}
 	commit_wrq_wr(&sc->sge.mgmtq, fwr, &cookie);
-	return (0);
+
+	/* Wait for response. */
+	mtx_lock(&sc->tids.ftid_lock);
+	for (;;) {
+		if (f->pending == 0) {
+			rc = f->valid ? 0 : EIO;
+			break;
+		}
+		if (cv_wait_sig(&sc->tids.ftid_cv, &sc->tids.ftid_lock) != 0) {
+			rc = EINPROGRESS;
+			break;
+		}
+	}
+	mtx_unlock(&sc->tids.ftid_lock);
+	return (rc);
 }
 
 int
 set_filter(struct adapter *sc, struct t4_filter *t)
 {
-	unsigned int nfilters, nports;
-	struct filter_entry *f;
-	int i, rc;
+	struct tid_info *ti = &sc->tids;
+	struct l2t_entry *l2te;
+	int rc;
 
+	/*
+	 * Basic filter checks first.
+	 */
+
+	if (t->fs.hash) {
+		if (!is_hashfilter(sc) || ti->ntids == 0)
+			return (ENOTSUP);
+		if (t->idx != (uint32_t)-1)
+			return (EINVAL);	/* hw, not user picks the idx */
+	} else {
+		if (ti->nftids == 0)
+			return (ENOTSUP);
+		if (t->idx >= ti->nftids)
+			return (EINVAL);
+		/* IPv6 filter idx must be 4 aligned */
+		if (t->fs.type == 1 &&
+		    ((t->idx & 0x3) || t->idx + 4 >= ti->nftids))
+			return (EINVAL);
+	}
+
+	/* T4 doesn't support removing VLAN Tags for loop back filters. */
+	if (is_t4(sc) && t->fs.action == FILTER_SWITCH &&
+	    (t->fs.newvlan == VLAN_REMOVE || t->fs.newvlan == VLAN_REWRITE))
+		return (ENOTSUP);
+
+	if (t->fs.action == FILTER_SWITCH && t->fs.eport >= sc->params.nports)
+		return (EINVAL);
+	if (t->fs.val.iport >= sc->params.nports)
+		return (EINVAL);
+
+	/* Can't specify an iq if not steering to it */
+	if (!t->fs.dirsteer && t->fs.iq)
+		return (EINVAL);
+
+	/* Validate against the global filter mode and ingress config */
+	rc = check_fspec_against_fconf_iconf(sc, &t->fs);
+	if (rc != 0)
+		return (rc);
+
+	/*
+	 * Basic checks passed.  Make sure the queues and tid tables are setup.
+	 */
+
 	rc = begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t4setf");
 	if (rc)
 		return (rc);
+	if (!(sc->flags & FULL_INIT_DONE) &&
+	    ((rc = adapter_full_init(sc)) != 0)) {
+		end_synchronized_op(sc, 0);
+		return (rc);
+	}
+	if (t->fs.hash) {
+		if (__predict_false(ti->hftid_tab == NULL)) {
+			ti->hftid_tab = malloc(sizeof(*ti->hftid_tab) * ti->ntids,
+			    M_CXGBE, M_NOWAIT | M_ZERO);
+			if (ti->hftid_tab == NULL) {
+				rc = ENOMEM;
+				goto done;
+			}
+			mtx_init(&ti->hftid_lock, "T4 hashfilters", 0, MTX_DEF);
+			cv_init(&ti->hftid_cv, "t4hfcv");
+		}
+		if (__predict_false(sc->tids.atid_tab == NULL)) {
+			rc = alloc_atid_tab(&sc->tids, M_NOWAIT);
+			if (rc != 0)
+				goto done;
+		}
+	} else if (__predict_false(ti->ftid_tab == NULL)) {
+		KASSERT(ti->ftids_in_use == 0,
+		    ("%s: no memory allocated but ftids_in_use > 0", __func__));
+		ti->ftid_tab = malloc(sizeof(struct filter_entry) * ti->nftids,
+		    M_CXGBE, M_NOWAIT | M_ZERO);
+		if (ti->ftid_tab == NULL) {
+			rc = ENOMEM;
+			goto done;
+		}
+		mtx_init(&ti->ftid_lock, "T4 filters", 0, MTX_DEF);
+		cv_init(&ti->ftid_cv, "t4fcv");
+	}
+done:
+	end_synchronized_op(sc, 0);
+	if (rc != 0)
+		return (rc);
 
-	nfilters = sc->tids.nftids;
-	nports = sc->params.nports;
+	/*
+	 * Allocate L2T entry, SMT entry, etc.
+	 */
 
-	if (nfilters == 0) {
-		rc = ENOTSUP;
-		goto done;
+	l2te = NULL;
+	if (t->fs.newdmac || t->fs.newvlan) {
+		/* This filter needs an L2T entry; allocate one. */
+		l2te = t4_l2t_alloc_switching(sc->l2t);
+		if (__predict_false(l2te == NULL))
+			return (EAGAIN);
+		if (t4_l2t_set_switching(sc, l2te, t->fs.vlan, t->fs.eport,
+		    t->fs.dmac)) {
+			t4_l2t_release(l2te);
+			return (ENOMEM);
+		}
 	}
 
-	if (t->idx >= nfilters) {
-		rc = EINVAL;
-		goto done;
+	if (t->fs.newsmac) {
+		/* XXX: alloc SMT */
+		return (ENOTSUP);
 	}
 
-	/* Validate against the global filter mode and ingress config */
-	rc = check_fspec_against_fconf_iconf(sc, &t->fs);
-	if (rc != 0)
-		goto done;
+	if (t->fs.hash)
+		return (set_hashfilter(sc, t, l2te));
+	else
+		return (set_tcamfilter(sc, t, l2te));
 
-	if (t->fs.action == FILTER_SWITCH && t->fs.eport >= nports) {
-		rc = EINVAL;
+}
+
+static int
+del_tcamfilter(struct adapter *sc, struct t4_filter *t)
+{
+	struct filter_entry *f;
+	struct fw_filter_wr *fwr;
+	struct wrq_cookie cookie;
+	int rc;
+
+	MPASS(sc->tids.ftid_tab != NULL);
+	MPASS(sc->tids.nftids > 0);
+
+	if (t->idx >= sc->tids.nftids)
+		return (EINVAL);
+
+	mtx_lock(&sc->tids.ftid_lock);
+	f = &sc->tids.ftid_tab[t->idx];
+	if (f->locked) {
+		rc = EPERM;
 		goto done;
 	}
-
-	if (t->fs.val.iport >= nports) {
-		rc = EINVAL;
+	if (f->pending) {
+		rc = EBUSY;
 		goto done;
 	}
-
-	/* Can't specify an iq if not steering to it */
-	if (!t->fs.dirsteer && t->fs.iq) {
+	if (f->valid == 0) {
 		rc = EINVAL;
 		goto done;
 	}
-
-	/* IPv6 filter idx must be 4 aligned */
-	if (t->fs.type == 1 &&
-	    ((t->idx & 0x3) || t->idx + 4 >= nfilters)) {
-		rc = EINVAL;
+	MPASS(f->tid == sc->tids.ftid_base + t->idx);
+	fwr = start_wrq_wr(&sc->sge.mgmtq, howmany(sizeof(*fwr), 16), &cookie);
+	if (fwr == NULL) {
+		rc = ENOMEM;
 		goto done;
 	}
 
-	if (!(sc->flags & FULL_INIT_DONE) &&
-	    ((rc = adapter_full_init(sc)) != 0))
-		goto done;
+	bzero(fwr, sizeof (*fwr));
+	t4_mk_filtdelwr(f->tid, fwr, sc->sge.fwq.abs_id);
+	f->pending = 1;
+	commit_wrq_wr(&sc->sge.mgmtq, fwr, &cookie);
+	t->fs = f->fs;	/* extra info for the caller */
 
-	if (sc->tids.ftid_tab == NULL) {
-		KASSERT(sc->tids.ftids_in_use == 0,
-		    ("%s: no memory allocated but filters_in_use > 0",
-		    __func__));
-
-		sc->tids.ftid_tab = malloc(sizeof (struct filter_entry) *
-		    nfilters, M_CXGBE, M_NOWAIT | M_ZERO);
-		if (sc->tids.ftid_tab == NULL) {
-			rc = ENOMEM;
-			goto done;
+	for (;;) {
+		if (f->pending == 0) {
+			rc = f->valid ? EIO : 0;
+			break;
 		}
-		mtx_init(&sc->tids.ftid_lock, "T4 filters", 0, MTX_DEF);
+		if (cv_wait_sig(&sc->tids.ftid_cv, &sc->tids.ftid_lock) != 0) {
+			rc = EINPROGRESS;
+			break;
+		}
 	}
+done:
+	mtx_unlock(&sc->tids.ftid_lock);
+	return (rc);
+}
 
-	for (i = 0; i < 4; i++) {
-		f = &sc->tids.ftid_tab[t->idx + i];
+int
+del_filter(struct adapter *sc, struct t4_filter *t)
+{
 
-		if (f->pending || f->valid) {
-			rc = EBUSY;
-			goto done;
-		}
-		if (f->locked) {
-			rc = EPERM;
-			goto done;
-		}
+	/* No filters possible if not initialized yet. */
+	if (!(sc->flags & FULL_INIT_DONE))
+		return (EINVAL);
 
-		if (t->fs.type == 0)
-			break;
+	/*
+	 * The checks for tid tables ensure that the locks that del_* will reach
+	 * for are initialized.
+	 */
+	if (t->fs.hash) {
+		if (sc->tids.hftid_tab != NULL)
+			return (del_hashfilter(sc, t));
+	} else {
+		if (sc->tids.ftid_tab != NULL)
+			return (del_tcamfilter(sc, t));
 	}
 
-	f = &sc->tids.ftid_tab[t->idx];
-	f->fs = t->fs;
+	return (EINVAL);
+}
 
-	rc = set_filter_wr(sc, t->idx);
-done:
-	end_synchronized_op(sc, 0);
+/*
+ * Release secondary resources associated with the filter.
+ */
+static void
+free_filter_resources(struct filter_entry *f)
+{
 
-	if (rc == 0) {
-		mtx_lock(&sc->tids.ftid_lock);
-		for (;;) {
-			if (f->pending == 0) {
-				rc = f->valid ? 0 : EIO;
-				break;
-			}
+	if (f->l2te) {
+		t4_l2t_release(f->l2te);
+		f->l2te = NULL;
+	}
+}
 
-			if (mtx_sleep(&sc->tids.ftid_tab, &sc->tids.ftid_lock,
-			    PCATCH, "t4setfw", 0)) {
-				rc = EINPROGRESS;
-				break;
-			}
-		}
-		mtx_unlock(&sc->tids.ftid_lock);
+int
+t4_filter_rpl(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
+{
+	struct adapter *sc = iq->adapter;
+	const struct cpl_set_tcb_rpl *rpl = (const void *)(rss + 1);
+	u_int tid = GET_TID(rpl);
+	u_int rc, cleanup, idx;
+	struct filter_entry *f;
+
+	KASSERT(m == NULL, ("%s: payload with opcode %02x", __func__,
+	    rss->opcode));
+	MPASS(is_ftid(sc, tid));
+
+	cleanup = 0;
+	idx = tid - sc->tids.ftid_base;
+	f = &sc->tids.ftid_tab[idx];
+	rc = G_COOKIE(rpl->cookie);
+
+	mtx_lock(&sc->tids.ftid_lock);
+	KASSERT(f->pending, ("%s: reply %d for filter[%u] that isn't pending.",
+	    __func__, rc, idx));
+	switch(rc) {
+	case FW_FILTER_WR_FLT_ADDED:
+		/* set-filter succeeded */
+		f->valid = 1;
+		f->smtidx = (be64toh(rpl->oldval) >> 24) & 0xff;
+		break;
+	case FW_FILTER_WR_FLT_DELETED:
+		/* del-filter succeeded */
+		MPASS(f->valid == 1);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list