t4_l2t.c revision 56b2bdd1f04d465cfe4a95b88ae5cba5884154e4
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
*/
/*
* This file is part of the Chelsio T4 support code.
*
* Copyright (C) 2010-2013 Chelsio Communications. All rights reserved.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this
* release for licensing terms and conditions.
*/
#include <sys/ethernet.h>
#include <inet/ipclassifier.h>
#include "common/t4_regs_values.h"
#include "t4_l2t.h"
/* identifies sync vs async L2T_WRITE_REQs */
#define S_SYNC_WR 12
#define VLAN_NONE 0xfff
/*
* jhash.h: Jenkins hash support.
*
* Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net)
*
*
* 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.
*/
/* 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
/*
* 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
{
a += JHASH_GOLDEN_RATIO;
b += JHASH_GOLDEN_RATIO;
c += initval;
__jhash_mix(a, b, c);
return (c);
}
static inline u32
{
return (jhash_3words(a, b, 0, initval));
}
#ifndef container_of
#endif
#if defined(__GNUC__)
#define unlikely(x) __builtin_expect((x), 0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif /* defined(__GNUC__) */
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 {
};
#define VLAN_NONE 0xfff
#define SIN(x) ((struct sockaddr_in *)(x))
#define atomic_read(x) atomic_add_int_nv(x, 0)
/*
* Allocate a free L2T entry.
* Must be called with l2t_data.lockatomic_load_acq_int held.
*/
static struct l2t_entry *
{
if (!atomic_read(&d->nfree))
return (NULL);
/* there's definitely a free entry */
if (atomic_read(&e->refcnt) == 0)
goto found;
/* */;
d->rover = e + 1;
atomic_dec_uint(&d->nfree);
/*
* 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) {
if (*p == e) {
*p = e->next;
break;
}
}
}
e->state = L2T_STATE_UNUSED;
return (e);
}
/*
* Write an L2T entry. Must be called with the entry locked.
* The write may be synchronous or asynchronous.
*/
static int
{
mblk_t *m;
struct cpl_l2t_write_req *req;
return (ENOMEM);
/* LINTED: E_BAD_PTR_CAST_ALIGN */
/* LINTED: E_CONSTANT_CONDITION */
INIT_TP_WR(req, 0);
(void) t4_mgmt_tx(sc, m);
e->state = L2T_STATE_SYNC_WRITE;
return (0);
}
struct l2t_data *
{
int i;
struct l2t_data *d;
d = kmem_zalloc(sizeof (*d), KM_SLEEP);
for (i = 0; i < L2T_SIZE; i++) {
/* LINTED: E_ASSIGN_NARROW_CONV */
}
return (d);
}
int
t4_free_l2t(struct l2t_data *d)
{
int i;
for (i = 0; i < L2T_SIZE; i++)
rw_destroy(&d->lock);
kmem_free(d, sizeof (*d));
return (0);
}
#ifndef TCP_OFFLOAD_DISABLE
static inline void
{
atomic_dec_uint(&d->nfree);
}
/*
* 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 {
};
static inline unsigned int
{
}
static inline unsigned int
{
}
static inline unsigned int
{
}
/*
* 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
{
if (e->v6 != 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
{
else
e->arpq_head = m;
e->arpq_tail = m;
}
static inline void
{
}
}
int
{
if (e->v6 != 0)
ASSERT(0);
switch (e->state) {
case L2T_STATE_STALE: /* entry is stale, kick off revalidation */
/* Fall through */
case L2T_STATE_VALID: /* fast-path, send the packet on */
return (0);
case L2T_STATE_RESOLVING:
case L2T_STATE_SYNC_WRITE:
mutex_enter(&e->lock);
if (e->state != L2T_STATE_SYNC_WRITE &&
e->state != L2T_STATE_RESOLVING) {
/* state changed by the time we got here */
mutex_exit(&e->lock);
goto again;
}
arpq_enqueue(e, m);
mutex_exit(&e->lock);
if (e->state == L2T_STATE_RESOLVING) {
0);
ASSERT(0);
else if (ip2m.ip2mac_err == 0)
t4_l2t_update(&ip2m, e);
else
ASSERT(0);
}
}
return (0);
}
/*
* 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 l2t_data *d;
mutex_enter(&e->lock);
/* LINTED: E_NOP_IF_STMT */
/*
* 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.
*/
}
mutex_exit(&e->lock);
}
void
t4_l2t_release(struct l2t_entry *e)
{
if (atomic_dec_uint_nv(&e->refcnt) == 0)
t4_l2e_free(e);
}
/* ARGSUSED */
int
{
"Unexpected L2T_WRITE_RPL status %u for entry %u",
return (-EINVAL);
}
mutex_enter(&e->lock);
if (e->state != L2T_STATE_SWITCHING) {
send_pending(sc, e);
e->state = L2T_STATE_VALID;
}
mutex_exit(&e->lock);
}
return (0);
}
/*
* The TOE wants an L2 table entry that it can use to reach the next hop over
* the specified port. Produce such an entry - create one if needed.
*
* Note that the ifnet could be a pseudo-device like if_vlan, if_lagg, etc. on
* top of the real cxgbe interface.
*/
struct l2t_entry *
{
struct l2t_entry *e;
int addr_len;
int hash;
int index = \
l2t_hold(d, e);
goto done;
}
}
/* Need to allocate a new entry */
e = alloc_l2e(d);
if (e != NULL) {
e->state = L2T_STATE_RESOLVING;
/* LINTED: E_ASSIGN_NARROW_CONV */
/* LINTED: E_ASSIGN_NARROW_CONV */
mutex_exit(&e->lock);
} else {
ASSERT(0);
}
done:
return (e);
}
/*
* Called when the host's neighbor layer makes a change to some entry that is
* loaded into the HW L2 table.
*/
void
{
if (ip2macp->ip2mac_err != 0) {
ASSERT(0); /* Don't know what to do. Needs to be investigated */
}
mutex_enter(&e->lock);
if (atomic_read(&e->refcnt) != 0)
goto found;
e->state = L2T_STATE_STALE;
mutex_exit(&e->lock);
/* The TOE has no interest in this LLE */
return;
if (atomic_read(&e->refcnt) != 0) {
/* Entry is referenced by at least 1 offloaded connection. */
e->state = L2T_STATE_VALID;
}
mutex_exit(&e->lock);
}
#endif