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
This commit is contained in:
parent
9aa461b570
commit
4dba21f17e
@ -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
|
||||
|
@ -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;
|
||||
|
140
sys/dev/cxgbe/common/jhash.h
Normal file
140
sys/dev/cxgbe/common/jhash.h
Normal file
@ -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 */
|
@ -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.
|
||||
*/
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
|
361
sys/dev/cxgbe/t4_l2t.c
Normal file
361
sys/dev/cxgbe/t4_l2t.c
Normal file
@ -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);
|
||||
}
|
71
sys/dev/cxgbe/t4_l2t.h
Normal file
71
sys/dev/cxgbe/t4_l2t.h
Normal file
@ -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 */
|
@ -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 adapter *, uint32_t);
|
||||
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_filter *t)
|
||||
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_filter *t)
|
||||
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 fidx)
|
||||
|
||||
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 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(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 fidx)
|
||||
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 struct cpl_set_tcb_rpl *rpl)
|
||||
* 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 struct cpl_set_tcb_rpl *rpl)
|
||||
*/
|
||||
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--;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user