Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 30 May 2011 21:07:26 +0000 (UTC)
From:      Navdeep Parhar <np@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r222509 - in head/sys: conf dev/cxgbe dev/cxgbe/common modules/cxgbe/if_cxgbe
Message-ID:  <201105302107.p4UL7QPB093269@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: np
Date: Mon May 30 21:07:26 2011
New Revision: 222509
URL: http://svn.freebsd.org/changeset/base/222509

Log:
  L2 table code.  This is enough to get the T4's switch + L2 rewrite
  filters working.  (All other filters - switch without L2 info rewrite,
  steer, and drop - were already fully-functional).
  
  Some contrived examples of "switch" filters with L2 rewriting:
  
  # cxgbetool t4nex0  iport 0  dport 80  action switch  vlan +9  eport 3
  Intercept all packets received on physical port 0 with TCP port 80 as
  destination, insert a vlan tag with VID 9, and send them out of port 3.
  
  # cxgbetool t4nex0  sip 192.168.1.1/32  ivlan 5  action switch \
  	vlan =9  smac aa:bb:cc:dd:ee:ff  eport 0
  Intercept all packets (received on any port) with source IP address
  192.168.1.1 and VLAN id 5, rewrite the VLAN id to 9, rewrite source mac
  to aa:bb:cc:dd:ee:ff, and send it out of port 0.
  
  MFC after:	1 week

Added:
  head/sys/dev/cxgbe/common/jhash.h   (contents, props changed)
  head/sys/dev/cxgbe/t4_l2t.c   (contents, props changed)
  head/sys/dev/cxgbe/t4_l2t.h   (contents, props changed)
Modified:
  head/sys/conf/files
  head/sys/dev/cxgbe/adapter.h
  head/sys/dev/cxgbe/offload.h
  head/sys/dev/cxgbe/osdep.h
  head/sys/dev/cxgbe/t4_ioctl.h
  head/sys/dev/cxgbe/t4_main.c
  head/sys/modules/cxgbe/if_cxgbe/Makefile

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Mon May 30 19:41:28 2011	(r222508)
+++ head/sys/conf/files	Mon May 30 21:07:26 2011	(r222509)
@@ -917,6 +917,8 @@ dev/cxgbe/t4_main.c		optional cxgbe pci 
 	compile-with "${NORMAL_C} -I$S/dev/cxgbe"
 dev/cxgbe/t4_sge.c		optional cxgbe pci \
 	compile-with "${NORMAL_C} -I$S/dev/cxgbe"
+dev/cxgbe/t4_l2t.c		optional cxgbe pci \
+	compile-with "${NORMAL_C} -I$S/dev/cxgbe"
 dev/cxgbe/common/t4_hw.c	optional cxgbe pci \
 	compile-with "${NORMAL_C} -I$S/dev/cxgbe"
 dev/cy/cy.c			optional cy

Modified: head/sys/dev/cxgbe/adapter.h
==============================================================================
--- head/sys/dev/cxgbe/adapter.h	Mon May 30 19:41:28 2011	(r222508)
+++ head/sys/dev/cxgbe/adapter.h	Mon May 30 21:07:26 2011	(r222509)
@@ -445,6 +445,7 @@ struct adapter {
 	struct port_info *port[MAX_NPORTS];
 	uint8_t chan_map[NCHAN];
 
+	struct l2t_data *l2t;	/* L2 table */
 	struct tid_info tids;
 
 	int registered_device_map;

Added: head/sys/dev/cxgbe/common/jhash.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/cxgbe/common/jhash.h	Mon May 30 21:07:26 2011	(r222509)
@@ -0,0 +1,140 @@
+#ifndef _JHASH_H
+#define _JHASH_H
+
+/* jhash.h: Jenkins hash support.
+ *
+ * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net)
+ *
+ * http://burtleburtle.net/bob/hash/
+ *
+ * These are the credits from Bob's sources:
+ *
+ * lookup2.c, by Bob Jenkins, December 1996, Public Domain.
+ * hash(), hash2(), hash3, and mix() are externally useful functions.
+ * Routines to test the hash are included if SELF_TEST is defined.
+ * You can use this free for any purpose.  It has no warranty.
+ *
+ * $FreeBSD$
+ */
+
+/* NOTE: Arguments are modified. */
+#define __jhash_mix(a, b, c) \
+{ \
+  a -= b; a -= c; a ^= (c>>13); \
+  b -= c; b -= a; b ^= (a<<8); \
+  c -= a; c -= b; c ^= (b>>13); \
+  a -= b; a -= c; a ^= (c>>12);  \
+  b -= c; b -= a; b ^= (a<<16); \
+  c -= a; c -= b; c ^= (b>>5); \
+  a -= b; a -= c; a ^= (c>>3);  \
+  b -= c; b -= a; b ^= (a<<10); \
+  c -= a; c -= b; c ^= (b>>15); \
+}
+
+/* The golden ration: an arbitrary value */
+#define JHASH_GOLDEN_RATIO	0x9e3779b9
+
+/* The most generic version, hashes an arbitrary sequence
+ * of bytes.  No alignment or length assumptions are made about
+ * the input key.
+ */
+static inline u32 jhash(const void *key, u32 length, u32 initval)
+{
+	u32 a, b, c, len;
+	const u8 *k = key;
+
+	len = length;
+	a = b = JHASH_GOLDEN_RATIO;
+	c = initval;
+
+	while (len >= 12) {
+		a += (k[0] +((u32)k[1]<<8) +((u32)k[2]<<16) +((u32)k[3]<<24));
+		b += (k[4] +((u32)k[5]<<8) +((u32)k[6]<<16) +((u32)k[7]<<24));
+		c += (k[8] +((u32)k[9]<<8) +((u32)k[10]<<16)+((u32)k[11]<<24));
+
+		__jhash_mix(a,b,c);
+
+		k += 12;
+		len -= 12;
+	}
+
+	c += length;
+	switch (len) {
+	case 11: c += ((u32)k[10]<<24);
+	case 10: c += ((u32)k[9]<<16);
+	case 9 : c += ((u32)k[8]<<8);
+	case 8 : b += ((u32)k[7]<<24);
+	case 7 : b += ((u32)k[6]<<16);
+	case 6 : b += ((u32)k[5]<<8);
+	case 5 : b += k[4];
+	case 4 : a += ((u32)k[3]<<24);
+	case 3 : a += ((u32)k[2]<<16);
+	case 2 : a += ((u32)k[1]<<8);
+	case 1 : a += k[0];
+	};
+
+	__jhash_mix(a,b,c);
+
+	return c;
+}
+
+/* A special optimized version that handles 1 or more of u32s.
+ * The length parameter here is the number of u32s in the key.
+ */
+static inline u32 jhash2(u32 *k, u32 length, u32 initval)
+{
+	u32 a, b, c, len;
+
+	a = b = JHASH_GOLDEN_RATIO;
+	c = initval;
+	len = length;
+
+	while (len >= 3) {
+		a += k[0];
+		b += k[1];
+		c += k[2];
+		__jhash_mix(a, b, c);
+		k += 3; len -= 3;
+	}
+
+	c += length * 4;
+
+	switch (len) {
+	case 2 : b += k[1];
+	case 1 : a += k[0];
+	};
+
+	__jhash_mix(a,b,c);
+
+	return c;
+}
+
+
+/* A special ultra-optimized versions that knows they are hashing exactly
+ * 3, 2 or 1 word(s).
+ *
+ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally
+ *       done at the end is not done here.
+ */
+static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
+{
+	a += JHASH_GOLDEN_RATIO;
+	b += JHASH_GOLDEN_RATIO;
+	c += initval;
+
+	__jhash_mix(a, b, c);
+
+	return c;
+}
+
+static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
+{
+	return jhash_3words(a, b, 0, initval);
+}
+
+static inline u32 jhash_1word(u32 a, u32 initval)
+{
+	return jhash_3words(a, 0, 0, initval);
+}
+
+#endif /* _JHASH_H */

Modified: head/sys/dev/cxgbe/offload.h
==============================================================================
--- head/sys/dev/cxgbe/offload.h	Mon May 30 19:41:28 2011	(r222508)
+++ head/sys/dev/cxgbe/offload.h	Mon May 30 21:07:26 2011	(r222509)
@@ -31,6 +31,24 @@
 #ifndef __T4_OFFLOAD_H__
 #define __T4_OFFLOAD_H__
 
+/* CPL message priority levels */
+enum {
+	CPL_PRIORITY_DATA     = 0,  /* data messages */
+	CPL_PRIORITY_SETUP    = 1,  /* connection setup messages */
+	CPL_PRIORITY_TEARDOWN = 0,  /* connection teardown messages */
+	CPL_PRIORITY_LISTEN   = 1,  /* listen start/stop messages */
+	CPL_PRIORITY_ACK      = 1,  /* RX ACK messages */
+	CPL_PRIORITY_CONTROL  = 1   /* control messages */
+};
+
+#define INIT_TP_WR(w, tid) do { \
+	(w)->wr.wr_hi = htonl(V_FW_WR_OP(FW_TP_WR) | \
+                              V_FW_WR_IMMDLEN(sizeof(*w) - sizeof(w->wr))); \
+	(w)->wr.wr_mid = htonl(V_FW_WR_LEN16(DIV_ROUND_UP(sizeof(*w), 16)) | \
+                               V_FW_WR_FLOWID(tid)); \
+	(w)->wr.wr_lo = cpu_to_be64(0); \
+} while (0)
+
 /*
  * Max # of ATIDs.  The absolute HW max is 16K but we keep it lower.
  */

Modified: head/sys/dev/cxgbe/osdep.h
==============================================================================
--- head/sys/dev/cxgbe/osdep.h	Mon May 30 19:41:28 2011	(r222508)
+++ head/sys/dev/cxgbe/osdep.h	Mon May 30 21:07:26 2011	(r222509)
@@ -82,6 +82,7 @@ typedef boolean_t bool;
 #define DIV_ROUND_UP(x, y) howmany(x, y)
 
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#define container_of(p, s, f) ((s *)(((uint8_t *)(p)) - offsetof(s, f)))
 
 #define swab16(x) bswap16(x) 
 #define swab32(x) bswap32(x) 

Modified: head/sys/dev/cxgbe/t4_ioctl.h
==============================================================================
--- head/sys/dev/cxgbe/t4_ioctl.h	Mon May 30 19:41:28 2011	(r222508)
+++ head/sys/dev/cxgbe/t4_ioctl.h	Mon May 30 21:07:26 2011	(r222509)
@@ -178,6 +178,8 @@ struct t4_filter_specification {
 
 struct t4_filter {
 	uint32_t idx;
+	uint16_t l2tidx;
+	uint16_t smtidx;
 	uint64_t hits;
 	struct t4_filter_specification fs;
 };

Added: head/sys/dev/cxgbe/t4_l2t.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/cxgbe/t4_l2t.c	Mon May 30 21:07:26 2011	(r222509)
@@ -0,0 +1,361 @@
+/*-
+ * Copyright (c) 2011 Chelsio Communications, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rwlock.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_vlan_var.h>
+#include <net/if_dl.h>
+#include <net/if_llatbl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+
+#include "common/common.h"
+#include "common/jhash.h"
+#include "common/t4_msg.h"
+#include "offload.h"
+#include "t4_l2t.h"
+
+/* identifies sync vs async L2T_WRITE_REQs */
+#define S_SYNC_WR    12
+#define V_SYNC_WR(x) ((x) << S_SYNC_WR)
+#define F_SYNC_WR    V_SYNC_WR(1)
+
+enum {
+	L2T_STATE_VALID,	/* entry is up to date */
+	L2T_STATE_STALE,	/* entry may be used but needs revalidation */
+	L2T_STATE_RESOLVING,	/* entry needs address resolution */
+	L2T_STATE_SYNC_WRITE,	/* synchronous write of entry underway */
+
+	/* when state is one of the below the entry is not hashed */
+	L2T_STATE_SWITCHING,	/* entry is being used by a switching filter */
+	L2T_STATE_UNUSED	/* entry not in use */
+};
+
+struct l2t_data {
+	struct rwlock lock;
+	volatile int nfree;	/* number of free entries */
+	struct l2t_entry *rover;/* starting point for next allocation */
+	struct l2t_entry l2tab[L2T_SIZE];
+};
+
+/*
+ * Module locking notes:  There is a RW lock protecting the L2 table as a
+ * whole plus a spinlock per L2T entry.  Entry lookups and allocations happen
+ * under the protection of the table lock, individual entry changes happen
+ * while holding that entry's spinlock.  The table lock nests outside the
+ * entry locks.  Allocations of new entries take the table lock as writers so
+ * no other lookups can happen while allocating new entries.  Entry updates
+ * take the table lock as readers so multiple entries can be updated in
+ * parallel.  An L2T entry can be dropped by decrementing its reference count
+ * and therefore can happen in parallel with entry allocation but no entry
+ * can change state or increment its ref count during allocation as both of
+ * these perform lookups.
+ *
+ * Note: We do not take refereces to ifnets in this module because both
+ * the TOE and the sockets already hold references to the interfaces and the
+ * lifetime of an L2T entry is fully contained in the lifetime of the TOE.
+ */
+static inline unsigned int
+vlan_prio(const struct l2t_entry *e)
+{
+	return e->vlan >> 13;
+}
+
+static inline void
+l2t_hold(struct l2t_data *d, struct l2t_entry *e)
+{
+	if (atomic_fetchadd_int(&e->refcnt, 1) == 0)  /* 0 -> 1 transition */
+		atomic_add_int(&d->nfree, -1);
+}
+
+/*
+ * To avoid having to check address families we do not allow v4 and v6
+ * neighbors to be on the same hash chain.  We keep v4 entries in the first
+ * half of available hash buckets and v6 in the second.
+ */
+enum {
+	L2T_SZ_HALF = L2T_SIZE / 2,
+	L2T_HASH_MASK = L2T_SZ_HALF - 1
+};
+
+static inline unsigned int
+arp_hash(const uint32_t *key, int ifindex)
+{
+	return jhash_2words(*key, ifindex, 0) & L2T_HASH_MASK;
+}
+
+static inline unsigned int
+ipv6_hash(const uint32_t *key, int ifindex)
+{
+	uint32_t xor = key[0] ^ key[1] ^ key[2] ^ key[3];
+
+	return L2T_SZ_HALF + (jhash_2words(xor, ifindex, 0) & L2T_HASH_MASK);
+}
+
+static inline unsigned int
+addr_hash(const uint32_t *addr, int addr_len, int ifindex)
+{
+	return addr_len == 4 ? arp_hash(addr, ifindex) :
+			       ipv6_hash(addr, ifindex);
+}
+
+/*
+ * Checks if an L2T entry is for the given IP/IPv6 address.  It does not check
+ * whether the L2T entry and the address are of the same address family.
+ * Callers ensure an address is only checked against L2T entries of the same
+ * family, something made trivial by the separation of IP and IPv6 hash chains
+ * mentioned above.  Returns 0 if there's a match,
+ */
+static inline int
+addreq(const struct l2t_entry *e, const uint32_t *addr)
+{
+	if (e->v6)
+		return (e->addr[0] ^ addr[0]) | (e->addr[1] ^ addr[1]) |
+		       (e->addr[2] ^ addr[2]) | (e->addr[3] ^ addr[3]);
+	return e->addr[0] ^ addr[0];
+}
+
+/*
+ * Write an L2T entry.  Must be called with the entry locked (XXX: really?).
+ * The write may be synchronous or asynchronous.
+ */
+static int
+write_l2e(struct adapter *sc, struct l2t_entry *e, int sync)
+{
+	struct mbuf *m;
+	struct cpl_l2t_write_req *req;
+
+	if ((m = m_gethdr(M_NOWAIT, MT_DATA)) == NULL)
+		return (ENOMEM);
+
+	req = mtod(m, struct cpl_l2t_write_req *);
+	m->m_pkthdr.len = m->m_len = sizeof(*req);
+
+	INIT_TP_WR(req, 0);
+	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, e->idx |
+	    V_SYNC_WR(sync) | V_TID_QID(sc->sge.fwq.abs_id)));
+	req->params = htons(V_L2T_W_PORT(e->lport) | V_L2T_W_NOREPLY(!sync));
+	req->l2t_idx = htons(e->idx);
+	req->vlan = htons(e->vlan);
+	memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac));
+
+	t4_mgmt_tx(sc, m);
+
+	if (sync && e->state != L2T_STATE_SWITCHING)
+		e->state = L2T_STATE_SYNC_WRITE;
+
+	return (0);
+}
+
+/*
+ * Add a packet to an L2T entry's queue of packets awaiting resolution.
+ * Must be called with the entry's lock held.
+ */
+static inline void
+arpq_enqueue(struct l2t_entry *e, struct mbuf *m)
+{
+	mtx_assert(&e->lock, MA_OWNED);
+
+	m->m_next = NULL;
+	if (e->arpq_head)
+		e->arpq_tail->m_next = m;
+	else
+		e->arpq_head = m;
+	e->arpq_tail = m;
+}
+
+/*
+ * Allocate a free L2T entry.  Must be called with l2t_data.lock held.
+ */
+static struct l2t_entry *
+alloc_l2e(struct l2t_data *d)
+{
+	struct l2t_entry *end, *e, **p;
+
+	rw_assert(&d->lock, RA_WLOCKED);
+
+	if (!atomic_load_acq_int(&d->nfree))
+		return (NULL);
+
+	/* there's definitely a free entry */
+	for (e = d->rover, end = &d->l2tab[L2T_SIZE]; e != end; ++e)
+		if (atomic_load_acq_int(&e->refcnt) == 0)
+			goto found;
+
+	for (e = d->l2tab; atomic_load_acq_int(&e->refcnt); ++e) ;
+found:
+	d->rover = e + 1;
+	atomic_add_int(&d->nfree, -1);
+
+	/*
+	 * The entry we found may be an inactive entry that is
+	 * presently in the hash table.  We need to remove it.
+	 */
+	if (e->state < L2T_STATE_SWITCHING) {
+		for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next) {
+			if (*p == e) {
+				*p = e->next;
+				e->next = NULL;
+				break;
+			}
+		}
+	}
+
+	e->state = L2T_STATE_UNUSED;
+	return e;
+}
+
+/*
+ * Called when an L2T entry has no more users.  The entry is left in the hash
+ * table since it is likely to be reused but we also bump nfree to indicate
+ * that the entry can be reallocated for a different neighbor.  We also drop
+ * the existing neighbor reference in case the neighbor is going away and is
+ * waiting on our reference.
+ *
+ * Because entries can be reallocated to other neighbors once their ref count
+ * drops to 0 we need to take the entry's lock to avoid races with a new
+ * incarnation.
+ */
+static void
+t4_l2e_free(struct l2t_entry *e)
+{
+	struct llentry *lle = NULL;
+	struct l2t_data *d;
+
+	mtx_lock(&e->lock);
+	if (atomic_load_acq_int(&e->refcnt) == 0) {  /* hasn't been recycled */
+		lle = e->lle;
+		e->lle = NULL;
+		/*
+		 * Don't need to worry about the arpq, an L2T entry can't be
+		 * released if any packets are waiting for resolution as we
+		 * need to be able to communicate with the device to close a
+		 * connection.
+		 */
+	}
+	mtx_unlock(&e->lock);
+
+	d = container_of(e, struct l2t_data, l2tab[e->idx]);
+	atomic_add_int(&d->nfree, 1);
+
+	if (lle)
+		LLE_FREE(lle);
+}
+
+void
+t4_l2t_release(struct l2t_entry *e)
+{
+	if (atomic_fetchadd_int(&e->refcnt, -1) == 1)
+		t4_l2e_free(e);
+}
+
+/*
+ * Allocate an L2T entry for use by a switching rule.  Such need to be
+ * explicitly freed and while busy they are not on any hash chain, so normal
+ * address resolution updates do not see them.
+ */
+struct l2t_entry *
+t4_l2t_alloc_switching(struct l2t_data *d)
+{
+	struct l2t_entry *e;
+
+	rw_rlock(&d->lock);
+	e = alloc_l2e(d);
+	if (e) {
+		mtx_lock(&e->lock);          /* avoid race with t4_l2t_free */
+		e->state = L2T_STATE_SWITCHING;
+		atomic_store_rel_int(&e->refcnt, 1);
+		mtx_unlock(&e->lock);
+	}
+	rw_runlock(&d->lock);
+	return e;
+}
+
+/*
+ * Sets/updates the contents of a switching L2T entry that has been allocated
+ * with an earlier call to @t4_l2t_alloc_switching.
+ */
+int
+t4_l2t_set_switching(struct adapter *sc, struct l2t_entry *e, uint16_t vlan,
+    uint8_t port, uint8_t *eth_addr)
+{
+	e->vlan = vlan;
+	e->lport = port;
+	memcpy(e->dmac, eth_addr, ETHER_ADDR_LEN);
+	return write_l2e(sc, e, 0);
+}
+
+struct l2t_data *
+t4_init_l2t(int flags)
+{
+	int i;
+	struct l2t_data *d;
+
+	d = malloc(sizeof(*d), M_CXGBE, M_ZERO | flags);
+	if (!d)
+		return (NULL);
+
+	d->rover = d->l2tab;
+	atomic_store_rel_int(&d->nfree, L2T_SIZE);
+	rw_init(&d->lock, "L2T");
+
+	for (i = 0; i < L2T_SIZE; i++) {
+		d->l2tab[i].idx = i;
+		d->l2tab[i].state = L2T_STATE_UNUSED;
+		mtx_init(&d->l2tab[i].lock, "L2T_E", NULL, MTX_DEF);
+		atomic_store_rel_int(&d->l2tab[i].refcnt, 0);
+	}
+
+	return (d);
+}
+
+int
+t4_free_l2t(struct l2t_data *d)
+{
+	int i;
+
+	for (i = 0; i < L2T_SIZE; i++)
+		mtx_destroy(&d->l2tab[i].lock);
+	rw_destroy(&d->lock);
+	free(d, M_CXGBE);
+
+	return (0);
+}

Added: head/sys/dev/cxgbe/t4_l2t.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/cxgbe/t4_l2t.h	Mon May 30 21:07:26 2011	(r222509)
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 2011 Chelsio Communications, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef __T4_L2T_H
+#define __T4_L2T_H
+
+enum { L2T_SIZE = 4096 };     /* # of L2T entries */
+
+/*
+ * Each L2T entry plays multiple roles.  First of all, it keeps state for the
+ * corresponding entry of the HW L2 table and maintains a queue of offload
+ * packets awaiting address resolution.  Second, it is a node of a hash table
+ * chain, where the nodes of the chain are linked together through their next
+ * pointer.  Finally, each node is a bucket of a hash table, pointing to the
+ * first element in its chain through its first pointer.
+ */
+struct l2t_entry {
+	uint16_t state;			/* entry state */
+	uint16_t idx;			/* entry index */
+	uint32_t addr[4];		/* next hop IP or IPv6 address */
+	struct ifnet *ifp;		/* outgoing interface */
+	uint16_t smt_idx;		/* SMT index */
+	uint16_t vlan;			/* VLAN TCI (id: 0-11, prio: 13-15) */
+	int ifindex;			/* interface index */
+	struct llentry *lle;		/* llentry for next hop */
+	struct l2t_entry *first;	/* start of hash chain */
+	struct l2t_entry *next;		/* next l2t_entry on chain */
+	struct mbuf *arpq_head;		/* list of mbufs awaiting resolution */
+	struct mbuf *arpq_tail;
+	struct mtx lock;
+	volatile uint32_t refcnt;	/* entry reference count */
+	uint16_t hash;			/* hash bucket the entry is on */
+	uint8_t v6;			/* whether entry is for IPv6 */
+	uint8_t lport;			/* associated offload logical port */
+	uint8_t dmac[ETHER_ADDR_LEN];	/* next hop's MAC address */
+};
+
+struct l2t_data *t4_init_l2t(int);
+int t4_free_l2t(struct l2t_data *);
+struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *);
+int t4_l2t_set_switching(struct adapter *, struct l2t_entry *, uint16_t,
+    uint8_t, uint8_t *);
+void t4_l2t_release(struct l2t_entry *);
+
+#endif  /* __T4_L2T_H */

Modified: head/sys/dev/cxgbe/t4_main.c
==============================================================================
--- head/sys/dev/cxgbe/t4_main.c	Mon May 30 19:41:28 2011	(r222508)
+++ head/sys/dev/cxgbe/t4_main.c	Mon May 30 21:07:26 2011	(r222509)
@@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$");
 #include "common/t4_regs_values.h"
 #include "common/t4fw_interface.h"
 #include "t4_ioctl.h"
+#include "t4_l2t.h"
 
 /* T4 bus driver interface */
 static int t4_probe(device_t);
@@ -240,6 +241,7 @@ struct filter_entry {
         uint32_t locked:1;	/* filter is administratively locked */
         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 */
 
         struct t4_filter_specification fs;
 };
@@ -304,7 +306,7 @@ static int set_filter_mode(struct adapte
 static int get_filter(struct adapter *, struct t4_filter *);
 static int set_filter(struct adapter *, struct t4_filter *);
 static int del_filter(struct adapter *, struct t4_filter *);
-static void clear_filter(struct adapter *, struct filter_entry *);
+static void clear_filter(struct filter_entry *);
 static int set_filter_wr(struct adapter *, int);
 static int del_filter_wr(struct adapter *, int);
 void filter_rpl(struct adapter *, const struct cpl_set_tcb_rpl *);
@@ -604,6 +606,8 @@ t4_attach(device_t dev)
 	sc->irq = malloc(sc->intr_count * sizeof(struct irq), M_CXGBE,
 	    M_ZERO | M_WAITOK);
 
+	sc->l2t = t4_init_l2t(M_WAITOK);
+
 	t4_sysctls(sc);
 
 	/*
@@ -691,6 +695,9 @@ t4_detach(device_t dev)
 		bus_release_resource(dev, SYS_RES_MEMORY, sc->msix_rid,
 		    sc->msix_res);
 
+	if (sc->l2t)
+		t4_free_l2t(sc->l2t);
+
 	free(sc->irq, M_CXGBE);
 	free(sc->sge.rxq, M_CXGBE);
 	free(sc->sge.txq, M_CXGBE);
@@ -2913,8 +2920,10 @@ get_filter(struct adapter *sc, struct t4
 	for (i = t->idx; i < nfilters; i++, f++) {
 		if (f->valid) {
 			t->idx = i;
-			t->fs = f->fs;
+			t->l2tidx = f->l2t ? f->l2t->idx : 0;
+			t->smtidx = f->smtidx;
 			t->hits = 0;	/* XXX implement */
+			t->fs = f->fs;
 
 			return (0);
 		}
@@ -3034,11 +3043,12 @@ del_filter(struct adapter *sc, struct t4
 	return (0);
 }
 
-/* XXX: L2T */
 static void
-clear_filter(struct adapter *sc, struct filter_entry *f)
+clear_filter(struct filter_entry *f)
 {
-	(void) sc;
+	if (f->l2t)
+		t4_l2t_release(f->l2t);
+
 	bzero(f, sizeof (*f));
 }
 
@@ -3053,8 +3063,18 @@ set_filter_wr(struct adapter *sc, int fi
 
 	ADAPTER_LOCK_ASSERT_OWNED(sc);
 
-	if (f->fs.newdmac || f->fs.newvlan)
-		return (ENOTSUP);	/* XXX: fix after L2T code */
+	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);
+		}
+	}
 
 	ftid = sc->tids.ftid_base + fidx;
 
@@ -3089,7 +3109,7 @@ set_filter_wr(struct adapter *sc, int fi
 		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(0));	/* XXX: L2T */
+		V_FW_FILTER_WR_L2TIX(f->l2t ? f->l2t->idx : 0));
 	fwr->ethtype = htobe16(f->fs.val.ethtype);
 	fwr->ethtypem = htobe16(f->fs.mask.ethtype);
 	fwr->frag_to_ovlan_vldm =
@@ -3136,7 +3156,7 @@ set_filter_wr(struct adapter *sc, int fi
 	if (rc != 0) {
 		sc->tids.ftids_in_use--;
 		m_freem(m);
-		clear_filter(sc, f);
+		clear_filter(f);
 	}
 	return (rc);
 }
@@ -3188,12 +3208,12 @@ filter_rpl(struct adapter *sc, const str
 			 * Clear the filter when we get confirmation from the
 			 * hardware that the filter has been deleted.
 			 */
-			clear_filter(sc, f);
+			clear_filter(f);
 			sc->tids.ftids_in_use--;
 		} else if (rc == FW_FILTER_WR_SMT_TBL_FULL) {
 			device_printf(sc->dev,
 			    "filter %u setup failed due to full SMT\n", idx);
-			clear_filter(sc, f);
+			clear_filter(f);
 			sc->tids.ftids_in_use--;
 		} else if (rc == FW_FILTER_WR_FLT_ADDED) {
 			f->smtidx = (be64toh(rpl->oldval) >> 24) & 0xff;
@@ -3206,7 +3226,7 @@ filter_rpl(struct adapter *sc, const str
 			 */
 			device_printf(sc->dev,
 			    "filter %u setup failed with error %u\n", idx, rc);
-			clear_filter(sc, f);
+			clear_filter(f);
 			sc->tids.ftids_in_use--;
 		}
 	}

Modified: head/sys/modules/cxgbe/if_cxgbe/Makefile
==============================================================================
--- head/sys/modules/cxgbe/if_cxgbe/Makefile	Mon May 30 19:41:28 2011	(r222508)
+++ head/sys/modules/cxgbe/if_cxgbe/Makefile	Mon May 30 21:07:26 2011	(r222509)
@@ -6,7 +6,7 @@ CXGBE = ${.CURDIR}/../../../dev/cxgbe
 .PATH: ${CXGBE} ${CXGBE}/common
 
 KMOD = if_cxgbe
-SRCS = t4_main.c t4_sge.c
+SRCS = t4_main.c t4_sge.c t4_l2t.c
 SRCS+= t4_hw.c
 SRCS+= device_if.h bus_if.h pci_if.h
 SRCS+= opt_inet.h



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201105302107.p4UL7QPB093269>