156b2bdd1SGireesh Nagabhushana /*
256b2bdd1SGireesh Nagabhushana  * This file and its contents are supplied under the terms of the
356b2bdd1SGireesh Nagabhushana  * Common Development and Distribution License ("CDDL"), version 1.0.
456b2bdd1SGireesh Nagabhushana  * You may only use this file in accordance with the terms of version
556b2bdd1SGireesh Nagabhushana  * 1.0 of the CDDL.
656b2bdd1SGireesh Nagabhushana  *
756b2bdd1SGireesh Nagabhushana  * A full copy of the text of the CDDL should have accompanied this
856b2bdd1SGireesh Nagabhushana  * source. A copy of the CDDL is also available via the Internet at
956b2bdd1SGireesh Nagabhushana  * http://www.illumos.org/license/CDDL.
1056b2bdd1SGireesh Nagabhushana  */
1156b2bdd1SGireesh Nagabhushana 
1256b2bdd1SGireesh Nagabhushana /*
1356b2bdd1SGireesh Nagabhushana  * This file is part of the Chelsio T4 support code.
1456b2bdd1SGireesh Nagabhushana  *
1556b2bdd1SGireesh Nagabhushana  * Copyright (C) 2010-2013 Chelsio Communications.  All rights reserved.
1656b2bdd1SGireesh Nagabhushana  *
1756b2bdd1SGireesh Nagabhushana  * This program is distributed in the hope that it will be useful, but WITHOUT
1856b2bdd1SGireesh Nagabhushana  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1956b2bdd1SGireesh Nagabhushana  * FITNESS FOR A PARTICULAR PURPOSE.  See the LICENSE file included in this
2056b2bdd1SGireesh Nagabhushana  * release for licensing terms and conditions.
2156b2bdd1SGireesh Nagabhushana  */
2256b2bdd1SGireesh Nagabhushana 
2356b2bdd1SGireesh Nagabhushana #include <sys/ddi.h>
2456b2bdd1SGireesh Nagabhushana #include <sys/sunddi.h>
2556b2bdd1SGireesh Nagabhushana #include <sys/sunndi.h>
2656b2bdd1SGireesh Nagabhushana #include <sys/atomic.h>
2756b2bdd1SGireesh Nagabhushana #include <sys/dlpi.h>
2856b2bdd1SGireesh Nagabhushana #include <sys/pattr.h>
2956b2bdd1SGireesh Nagabhushana #include <sys/strsubr.h>
3056b2bdd1SGireesh Nagabhushana #include <sys/stream.h>
3156b2bdd1SGireesh Nagabhushana #include <sys/strsun.h>
3256b2bdd1SGireesh Nagabhushana #include <sys/ethernet.h>
33*94c3dad2SToomas Soome #include <sys/containerof.h>
3456b2bdd1SGireesh Nagabhushana #include <inet/ip.h>
3556b2bdd1SGireesh Nagabhushana #include <inet/ipclassifier.h>
3656b2bdd1SGireesh Nagabhushana #include <inet/tcp.h>
3756b2bdd1SGireesh Nagabhushana 
3856b2bdd1SGireesh Nagabhushana #include "common/common.h"
3956b2bdd1SGireesh Nagabhushana #include "common/t4_msg.h"
4056b2bdd1SGireesh Nagabhushana #include "common/t4_regs.h"
4156b2bdd1SGireesh Nagabhushana #include "common/t4_regs_values.h"
4256b2bdd1SGireesh Nagabhushana #include "t4_l2t.h"
4356b2bdd1SGireesh Nagabhushana 
4456b2bdd1SGireesh Nagabhushana /* identifies sync vs async L2T_WRITE_REQs */
4556b2bdd1SGireesh Nagabhushana #define	S_SYNC_WR	12
4656b2bdd1SGireesh Nagabhushana #define	V_SYNC_WR(x)	((x) << S_SYNC_WR)
4756b2bdd1SGireesh Nagabhushana #define	F_SYNC_WR	V_SYNC_WR(1)
4856b2bdd1SGireesh Nagabhushana #define	VLAN_NONE	0xfff
4956b2bdd1SGireesh Nagabhushana 
5056b2bdd1SGireesh Nagabhushana /*
5156b2bdd1SGireesh Nagabhushana  * jhash.h: Jenkins hash support.
5256b2bdd1SGireesh Nagabhushana  *
5356b2bdd1SGireesh Nagabhushana  * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net)
5456b2bdd1SGireesh Nagabhushana  *
5556b2bdd1SGireesh Nagabhushana  * http://burtleburtle.net/bob/hash/
5656b2bdd1SGireesh Nagabhushana  *
5756b2bdd1SGireesh Nagabhushana  * These are the credits from Bob's sources:
5856b2bdd1SGireesh Nagabhushana  *
5956b2bdd1SGireesh Nagabhushana  * lookup2.c, by Bob Jenkins, December 1996, Public Domain.
6056b2bdd1SGireesh Nagabhushana  * hash(), hash2(), hash3, and mix() are externally useful functions.
6156b2bdd1SGireesh Nagabhushana  * Routines to test the hash are included if SELF_TEST is defined.
6256b2bdd1SGireesh Nagabhushana  * You can use this free for any purpose.  It has no warranty.
6356b2bdd1SGireesh Nagabhushana  */
6456b2bdd1SGireesh Nagabhushana 
6556b2bdd1SGireesh Nagabhushana /* NOTE: Arguments are modified. */
6656b2bdd1SGireesh Nagabhushana #define	__jhash_mix(a, b, c) \
6756b2bdd1SGireesh Nagabhushana { \
6856b2bdd1SGireesh Nagabhushana 	a -= b; a -= c; a ^= (c>>13); \
6956b2bdd1SGireesh Nagabhushana 	b -= c; b -= a; b ^= (a<<8); \
7056b2bdd1SGireesh Nagabhushana 	c -= a; c -= b; c ^= (b>>13); \
7156b2bdd1SGireesh Nagabhushana 	a -= b; a -= c; a ^= (c>>12);  \
7256b2bdd1SGireesh Nagabhushana 	b -= c; b -= a; b ^= (a<<16); \
7356b2bdd1SGireesh Nagabhushana 	c -= a; c -= b; c ^= (b>>5); \
7456b2bdd1SGireesh Nagabhushana 	a -= b; a -= c; a ^= (c>>3);  \
7556b2bdd1SGireesh Nagabhushana 	b -= c; b -= a; b ^= (a<<10); \
7656b2bdd1SGireesh Nagabhushana 	c -= a; c -= b; c ^= (b>>15); \
7756b2bdd1SGireesh Nagabhushana }
7856b2bdd1SGireesh Nagabhushana 
7956b2bdd1SGireesh Nagabhushana /* The golden ration: an arbitrary value */
8056b2bdd1SGireesh Nagabhushana #define	JHASH_GOLDEN_RATIO	0x9e3779b9
8156b2bdd1SGireesh Nagabhushana 
8256b2bdd1SGireesh Nagabhushana /*
8356b2bdd1SGireesh Nagabhushana  * A special ultra-optimized versions that knows they are hashing exactly
8456b2bdd1SGireesh Nagabhushana  * 3, 2 or 1 word(s).
8556b2bdd1SGireesh Nagabhushana  *
8656b2bdd1SGireesh Nagabhushana  * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally
8756b2bdd1SGireesh Nagabhushana  *	 done at the end is not done here.
8856b2bdd1SGireesh Nagabhushana  */
8956b2bdd1SGireesh Nagabhushana static inline u32
jhash_3words(u32 a,u32 b,u32 c,u32 initval)9056b2bdd1SGireesh Nagabhushana jhash_3words(u32 a, u32 b, u32 c, u32 initval)
9156b2bdd1SGireesh Nagabhushana {
9256b2bdd1SGireesh Nagabhushana 	a += JHASH_GOLDEN_RATIO;
9356b2bdd1SGireesh Nagabhushana 	b += JHASH_GOLDEN_RATIO;
9456b2bdd1SGireesh Nagabhushana 	c += initval;
9556b2bdd1SGireesh Nagabhushana 
9656b2bdd1SGireesh Nagabhushana 	__jhash_mix(a, b, c);
9756b2bdd1SGireesh Nagabhushana 
9856b2bdd1SGireesh Nagabhushana 	return (c);
9956b2bdd1SGireesh Nagabhushana }
10056b2bdd1SGireesh Nagabhushana 
10156b2bdd1SGireesh Nagabhushana static inline u32
jhash_2words(u32 a,u32 b,u32 initval)10256b2bdd1SGireesh Nagabhushana jhash_2words(u32 a, u32 b, u32 initval)
10356b2bdd1SGireesh Nagabhushana {
10456b2bdd1SGireesh Nagabhushana 	return (jhash_3words(a, b, 0, initval));
10556b2bdd1SGireesh Nagabhushana }
10656b2bdd1SGireesh Nagabhushana 
10756b2bdd1SGireesh Nagabhushana #if defined(__GNUC__)
10856b2bdd1SGireesh Nagabhushana #define	likely(x)	__builtin_expect((x), 1)
10956b2bdd1SGireesh Nagabhushana #define	unlikely(x)	__builtin_expect((x), 0)
11056b2bdd1SGireesh Nagabhushana #else
11156b2bdd1SGireesh Nagabhushana #define	likely(x)	(x)
11256b2bdd1SGireesh Nagabhushana #define	unlikely(x)	(x)
11356b2bdd1SGireesh Nagabhushana #endif /* defined(__GNUC__) */
11456b2bdd1SGireesh Nagabhushana 
11556b2bdd1SGireesh Nagabhushana enum {
11656b2bdd1SGireesh Nagabhushana 	L2T_STATE_VALID,	/* entry is up to date */
11756b2bdd1SGireesh Nagabhushana 	L2T_STATE_STALE,	/* entry may be used but needs revalidation */
11856b2bdd1SGireesh Nagabhushana 	L2T_STATE_RESOLVING,	/* entry needs address resolution */
11956b2bdd1SGireesh Nagabhushana 	L2T_STATE_SYNC_WRITE,	/* synchronous write of entry underway */
12056b2bdd1SGireesh Nagabhushana 
12156b2bdd1SGireesh Nagabhushana 	/* when state is one of the below the entry is not hashed */
12256b2bdd1SGireesh Nagabhushana 	L2T_STATE_SWITCHING,	/* entry is being used by a switching filter */
12356b2bdd1SGireesh Nagabhushana 	L2T_STATE_UNUSED	/* entry not in use */
12456b2bdd1SGireesh Nagabhushana };
12556b2bdd1SGireesh Nagabhushana 
12656b2bdd1SGireesh Nagabhushana struct l2t_data {
12756b2bdd1SGireesh Nagabhushana 	krwlock_t lock;
128de483253SVishal Kulkarni 	u_int l2t_size;
12956b2bdd1SGireesh Nagabhushana 	volatile uint_t nfree;	 /* number of free entries */
13056b2bdd1SGireesh Nagabhushana 	struct l2t_entry *rover; /* starting point for next allocation */
131de483253SVishal Kulkarni 	struct l2t_entry l2tab[];
13256b2bdd1SGireesh Nagabhushana };
13356b2bdd1SGireesh Nagabhushana 
13456b2bdd1SGireesh Nagabhushana #define	VLAN_NONE	0xfff
13556b2bdd1SGireesh Nagabhushana #define	SA(x)		((struct sockaddr *)(x))
13656b2bdd1SGireesh Nagabhushana #define	SIN(x)		((struct sockaddr_in *)(x))
13756b2bdd1SGireesh Nagabhushana #define	SINADDR(x)	(SIN(x)->sin_addr.s_addr)
13856b2bdd1SGireesh Nagabhushana #define	atomic_read(x) atomic_add_int_nv(x, 0)
1393dde7c95SVishal Kulkarni 
1403dde7c95SVishal Kulkarni #ifdef TCP_OFFLOAD_ENABLE
14156b2bdd1SGireesh Nagabhushana /*
14256b2bdd1SGireesh Nagabhushana  * Allocate a free L2T entry.
14356b2bdd1SGireesh Nagabhushana  * Must be called with l2t_data.lockatomic_load_acq_int held.
14456b2bdd1SGireesh Nagabhushana  */
14556b2bdd1SGireesh Nagabhushana static struct l2t_entry *
alloc_l2e(struct l2t_data * d)14656b2bdd1SGireesh Nagabhushana alloc_l2e(struct l2t_data *d)
14756b2bdd1SGireesh Nagabhushana {
14856b2bdd1SGireesh Nagabhushana 	struct l2t_entry *end, *e, **p;
14956b2bdd1SGireesh Nagabhushana 
15056b2bdd1SGireesh Nagabhushana 	ASSERT(rw_write_held(&d->lock));
15156b2bdd1SGireesh Nagabhushana 
15256b2bdd1SGireesh Nagabhushana 	if (!atomic_read(&d->nfree))
15356b2bdd1SGireesh Nagabhushana 		return (NULL);
15456b2bdd1SGireesh Nagabhushana 
15556b2bdd1SGireesh Nagabhushana 	/* there's definitely a free entry */
156de483253SVishal Kulkarni 	for (e = d->rover, end = &d->l2tab[d->l2t_size]; e != end; ++e)
15756b2bdd1SGireesh Nagabhushana 		if (atomic_read(&e->refcnt) == 0)
15856b2bdd1SGireesh Nagabhushana 			goto found;
15956b2bdd1SGireesh Nagabhushana 
16056b2bdd1SGireesh Nagabhushana 	for (e = d->l2tab; atomic_read(&e->refcnt); ++e)
16156b2bdd1SGireesh Nagabhushana 		/* */;
16256b2bdd1SGireesh Nagabhushana found:
16356b2bdd1SGireesh Nagabhushana 	d->rover = e + 1;
16456b2bdd1SGireesh Nagabhushana 	atomic_dec_uint(&d->nfree);
16556b2bdd1SGireesh Nagabhushana 
16656b2bdd1SGireesh Nagabhushana 	/*
16756b2bdd1SGireesh Nagabhushana 	 * The entry we found may be an inactive entry that is
16856b2bdd1SGireesh Nagabhushana 	 * presently in the hash table.  We need to remove it.
16956b2bdd1SGireesh Nagabhushana 	 */
17056b2bdd1SGireesh Nagabhushana 	if (e->state < L2T_STATE_SWITCHING) {
17156b2bdd1SGireesh Nagabhushana 		for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next) {
17256b2bdd1SGireesh Nagabhushana 			if (*p == e) {
17356b2bdd1SGireesh Nagabhushana 				*p = e->next;
17456b2bdd1SGireesh Nagabhushana 				e->next = NULL;
17556b2bdd1SGireesh Nagabhushana 				break;
17656b2bdd1SGireesh Nagabhushana 			}
17756b2bdd1SGireesh Nagabhushana 		}
17856b2bdd1SGireesh Nagabhushana 	}
17956b2bdd1SGireesh Nagabhushana 
18056b2bdd1SGireesh Nagabhushana 	e->state = L2T_STATE_UNUSED;
18156b2bdd1SGireesh Nagabhushana 	return (e);
18256b2bdd1SGireesh Nagabhushana }
18356b2bdd1SGireesh Nagabhushana 
18456b2bdd1SGireesh Nagabhushana /*
18556b2bdd1SGireesh Nagabhushana  * Write an L2T entry.  Must be called with the entry locked.
18656b2bdd1SGireesh Nagabhushana  * The write may be synchronous or asynchronous.
18756b2bdd1SGireesh Nagabhushana  */
18856b2bdd1SGireesh Nagabhushana static int
write_l2e(adapter_t * sc,struct l2t_entry * e,int sync)18956b2bdd1SGireesh Nagabhushana write_l2e(adapter_t *sc, struct l2t_entry *e, int sync)
19056b2bdd1SGireesh Nagabhushana {
19156b2bdd1SGireesh Nagabhushana 	mblk_t *m;
19256b2bdd1SGireesh Nagabhushana 	struct cpl_l2t_write_req *req;
193de483253SVishal Kulkarni 	int idx = e->idx + sc->vres.l2t.start;
19456b2bdd1SGireesh Nagabhushana 
19556b2bdd1SGireesh Nagabhushana 	ASSERT(MUTEX_HELD(&e->lock));
19656b2bdd1SGireesh Nagabhushana 
19756b2bdd1SGireesh Nagabhushana 	if ((m = allocb(sizeof (*req), BPRI_HI)) == NULL)
19856b2bdd1SGireesh Nagabhushana 		return (ENOMEM);
19956b2bdd1SGireesh Nagabhushana 
20056b2bdd1SGireesh Nagabhushana 	/* LINTED: E_BAD_PTR_CAST_ALIGN */
20156b2bdd1SGireesh Nagabhushana 	req = (struct cpl_l2t_write_req *)m->b_wptr;
20256b2bdd1SGireesh Nagabhushana 
20356b2bdd1SGireesh Nagabhushana 	/* LINTED: E_CONSTANT_CONDITION */
20456b2bdd1SGireesh Nagabhushana 	INIT_TP_WR(req, 0);
205de483253SVishal Kulkarni 	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, idx |
20656b2bdd1SGireesh Nagabhushana 	    V_SYNC_WR(sync) | V_TID_QID(sc->sge.fwq.abs_id)));
20756b2bdd1SGireesh Nagabhushana 	req->params = htons(V_L2T_W_PORT(e->lport) | V_L2T_W_NOREPLY(!sync));
208de483253SVishal Kulkarni 	req->l2t_idx = htons(idx);
20956b2bdd1SGireesh Nagabhushana 	req->vlan = htons(e->vlan);
21056b2bdd1SGireesh Nagabhushana 	(void) memcpy(req->dst_mac, e->dmac, sizeof (req->dst_mac));
21156b2bdd1SGireesh Nagabhushana 
21256b2bdd1SGireesh Nagabhushana 	m->b_wptr += sizeof (*req);
21356b2bdd1SGireesh Nagabhushana 
21456b2bdd1SGireesh Nagabhushana 	(void) t4_mgmt_tx(sc, m);
21556b2bdd1SGireesh Nagabhushana 
21656b2bdd1SGireesh Nagabhushana 	if (sync && e->state != L2T_STATE_SWITCHING)
21756b2bdd1SGireesh Nagabhushana 		e->state = L2T_STATE_SYNC_WRITE;
21856b2bdd1SGireesh Nagabhushana 
21956b2bdd1SGireesh Nagabhushana 	return (0);
22056b2bdd1SGireesh Nagabhushana }
2213dde7c95SVishal Kulkarni #endif
22256b2bdd1SGireesh Nagabhushana 
22356b2bdd1SGireesh Nagabhushana struct l2t_data *
t4_init_l2t(struct adapter * sc)22456b2bdd1SGireesh Nagabhushana t4_init_l2t(struct adapter *sc)
22556b2bdd1SGireesh Nagabhushana {
226de483253SVishal Kulkarni 	int i, l2t_size;
22756b2bdd1SGireesh Nagabhushana 	struct l2t_data *d;
22856b2bdd1SGireesh Nagabhushana 
229de483253SVishal Kulkarni 	l2t_size = sc->vres.l2t.size;
230de483253SVishal Kulkarni 	if(l2t_size < 1)
231de483253SVishal Kulkarni 		return (NULL);
23256b2bdd1SGireesh Nagabhushana 
233de483253SVishal Kulkarni 	d = kmem_zalloc(sizeof(*d) + l2t_size * sizeof (struct l2t_entry), KM_SLEEP);
234de483253SVishal Kulkarni 	if (!d)
235de483253SVishal Kulkarni 		return (NULL);
236de483253SVishal Kulkarni 
237de483253SVishal Kulkarni 	d->l2t_size = l2t_size;
238*94c3dad2SToomas Soome 
23956b2bdd1SGireesh Nagabhushana 	d->rover = d->l2tab;
240de483253SVishal Kulkarni 	(void) atomic_swap_uint(&d->nfree, l2t_size);
24156b2bdd1SGireesh Nagabhushana 	rw_init(&d->lock, NULL, RW_DRIVER, NULL);
24256b2bdd1SGireesh Nagabhushana 
243de483253SVishal Kulkarni 	for (i = 0; i < l2t_size; i++) {
24456b2bdd1SGireesh Nagabhushana 		/* LINTED: E_ASSIGN_NARROW_CONV */
24556b2bdd1SGireesh Nagabhushana 		d->l2tab[i].idx = i;
24656b2bdd1SGireesh Nagabhushana 		d->l2tab[i].state = L2T_STATE_UNUSED;
24756b2bdd1SGireesh Nagabhushana 		mutex_init(&d->l2tab[i].lock, NULL, MUTEX_DRIVER, NULL);
24856b2bdd1SGireesh Nagabhushana 		(void) atomic_swap_uint(&d->l2tab[i].refcnt, 0);
24956b2bdd1SGireesh Nagabhushana 	}
25056b2bdd1SGireesh Nagabhushana 
2513dde7c95SVishal Kulkarni #ifdef TCP_OFFLOAD_ENABLE
25256b2bdd1SGireesh Nagabhushana 	(void) t4_register_cpl_handler(sc, CPL_L2T_WRITE_RPL, do_l2t_write_rpl);
2533dde7c95SVishal Kulkarni #endif
25456b2bdd1SGireesh Nagabhushana 
25556b2bdd1SGireesh Nagabhushana 	return (d);
25656b2bdd1SGireesh Nagabhushana }
25756b2bdd1SGireesh Nagabhushana 
25856b2bdd1SGireesh Nagabhushana int
t4_free_l2t(struct l2t_data * d)25956b2bdd1SGireesh Nagabhushana t4_free_l2t(struct l2t_data *d)
26056b2bdd1SGireesh Nagabhushana {
26156b2bdd1SGireesh Nagabhushana 	int i;
26256b2bdd1SGireesh Nagabhushana 
26356b2bdd1SGireesh Nagabhushana 	for (i = 0; i < L2T_SIZE; i++)
26456b2bdd1SGireesh Nagabhushana 		mutex_destroy(&d->l2tab[i].lock);
26556b2bdd1SGireesh Nagabhushana 	rw_destroy(&d->lock);
26656b2bdd1SGireesh Nagabhushana 	kmem_free(d, sizeof (*d));
26756b2bdd1SGireesh Nagabhushana 
26856b2bdd1SGireesh Nagabhushana 	return (0);
26956b2bdd1SGireesh Nagabhushana }
27056b2bdd1SGireesh Nagabhushana 
2713dde7c95SVishal Kulkarni #ifdef TCP_OFFLOAD_ENABLE
27256b2bdd1SGireesh Nagabhushana static inline void
l2t_hold(struct l2t_data * d,struct l2t_entry * e)27356b2bdd1SGireesh Nagabhushana l2t_hold(struct l2t_data *d, struct l2t_entry *e)
27456b2bdd1SGireesh Nagabhushana {
27556b2bdd1SGireesh Nagabhushana 	if (atomic_inc_uint_nv(&e->refcnt) == 1)  /* 0 -> 1 transition */
27656b2bdd1SGireesh Nagabhushana 		atomic_dec_uint(&d->nfree);
27756b2bdd1SGireesh Nagabhushana }
27856b2bdd1SGireesh Nagabhushana 
27956b2bdd1SGireesh Nagabhushana /*
28056b2bdd1SGireesh Nagabhushana  * To avoid having to check address families we do not allow v4 and v6
28156b2bdd1SGireesh Nagabhushana  * neighbors to be on the same hash chain.  We keep v4 entries in the first
28256b2bdd1SGireesh Nagabhushana  * half of available hash buckets and v6 in the second.
28356b2bdd1SGireesh Nagabhushana  */
28456b2bdd1SGireesh Nagabhushana enum {
28556b2bdd1SGireesh Nagabhushana 	L2T_SZ_HALF = L2T_SIZE / 2,
28656b2bdd1SGireesh Nagabhushana 	L2T_HASH_MASK = L2T_SZ_HALF - 1
28756b2bdd1SGireesh Nagabhushana };
28856b2bdd1SGireesh Nagabhushana 
28956b2bdd1SGireesh Nagabhushana static inline unsigned int
arp_hash(const uint32_t * key,int ifindex)29056b2bdd1SGireesh Nagabhushana arp_hash(const uint32_t *key, int ifindex)
29156b2bdd1SGireesh Nagabhushana {
29256b2bdd1SGireesh Nagabhushana 	return (jhash_2words(*key, ifindex, 0) & L2T_HASH_MASK);
29356b2bdd1SGireesh Nagabhushana }
29456b2bdd1SGireesh Nagabhushana 
29556b2bdd1SGireesh Nagabhushana static inline unsigned int
ipv6_hash(const uint32_t * key,int ifindex)29656b2bdd1SGireesh Nagabhushana ipv6_hash(const uint32_t *key, int ifindex)
29756b2bdd1SGireesh Nagabhushana {
29856b2bdd1SGireesh Nagabhushana 	uint32_t xor = key[0] ^ key[1] ^ key[2] ^ key[3];
29956b2bdd1SGireesh Nagabhushana 
30056b2bdd1SGireesh Nagabhushana 	return (L2T_SZ_HALF + (jhash_2words(xor, ifindex, 0) & L2T_HASH_MASK));
30156b2bdd1SGireesh Nagabhushana }
30256b2bdd1SGireesh Nagabhushana 
30356b2bdd1SGireesh Nagabhushana static inline unsigned int
addr_hash(const uint32_t * addr,int addr_len,int ifindex)30456b2bdd1SGireesh Nagabhushana addr_hash(const uint32_t *addr, int addr_len, int ifindex)
30556b2bdd1SGireesh Nagabhushana {
30656b2bdd1SGireesh Nagabhushana 	return (addr_len == 4 ? arp_hash(addr, ifindex) :
30756b2bdd1SGireesh Nagabhushana 	    ipv6_hash(addr, ifindex));
30856b2bdd1SGireesh Nagabhushana }
30956b2bdd1SGireesh Nagabhushana 
31056b2bdd1SGireesh Nagabhushana /*
31156b2bdd1SGireesh Nagabhushana  * Checks if an L2T entry is for the given IP/IPv6 address.  It does not check
31256b2bdd1SGireesh Nagabhushana  * whether the L2T entry and the address are of the same address family.
31356b2bdd1SGireesh Nagabhushana  * Callers ensure an address is only checked against L2T entries of the same
31456b2bdd1SGireesh Nagabhushana  * family, something made trivial by the separation of IP and IPv6 hash chains
31556b2bdd1SGireesh Nagabhushana  * mentioned above.  Returns 0 if there's a match,
31656b2bdd1SGireesh Nagabhushana  */
31756b2bdd1SGireesh Nagabhushana static inline int
addreq(const struct l2t_entry * e,const uint32_t * addr)31856b2bdd1SGireesh Nagabhushana addreq(const struct l2t_entry *e, const uint32_t *addr)
31956b2bdd1SGireesh Nagabhushana {
32056b2bdd1SGireesh Nagabhushana 	if (e->v6 != 0)
32156b2bdd1SGireesh Nagabhushana 		return ((e->addr[0] ^ addr[0]) | (e->addr[1] ^ addr[1]) |
32256b2bdd1SGireesh Nagabhushana 		    (e->addr[2] ^ addr[2]) | (e->addr[3] ^ addr[3]));
32356b2bdd1SGireesh Nagabhushana 	return (e->addr[0] ^ addr[0]);
32456b2bdd1SGireesh Nagabhushana }
32556b2bdd1SGireesh Nagabhushana 
32656b2bdd1SGireesh Nagabhushana /*
32756b2bdd1SGireesh Nagabhushana  * Add a packet to an L2T entry's queue of packets awaiting resolution.
32856b2bdd1SGireesh Nagabhushana  * Must be called with the entry's lock held.
32956b2bdd1SGireesh Nagabhushana  */
33056b2bdd1SGireesh Nagabhushana static inline void
arpq_enqueue(struct l2t_entry * e,mblk_t * m)33156b2bdd1SGireesh Nagabhushana arpq_enqueue(struct l2t_entry *e, mblk_t *m)
33256b2bdd1SGireesh Nagabhushana {
33356b2bdd1SGireesh Nagabhushana 	ASSERT(MUTEX_HELD(&e->lock));
33456b2bdd1SGireesh Nagabhushana 
33556b2bdd1SGireesh Nagabhushana 	ASSERT(m->b_next == NULL);
33656b2bdd1SGireesh Nagabhushana 	if (e->arpq_head != NULL)
33756b2bdd1SGireesh Nagabhushana 		e->arpq_tail->b_next = m;
33856b2bdd1SGireesh Nagabhushana 	else
33956b2bdd1SGireesh Nagabhushana 		e->arpq_head = m;
34056b2bdd1SGireesh Nagabhushana 	e->arpq_tail = m;
34156b2bdd1SGireesh Nagabhushana }
34256b2bdd1SGireesh Nagabhushana 
34356b2bdd1SGireesh Nagabhushana int
t4_l2t_send(struct adapter * sc,mblk_t * m,struct l2t_entry * e)34456b2bdd1SGireesh Nagabhushana t4_l2t_send(struct adapter *sc, mblk_t *m, struct l2t_entry *e)
34556b2bdd1SGireesh Nagabhushana {
34656b2bdd1SGireesh Nagabhushana 	sin_t *sin;
34756b2bdd1SGireesh Nagabhushana 	ip2mac_t ip2m;
34856b2bdd1SGireesh Nagabhushana 
34956b2bdd1SGireesh Nagabhushana 	if (e->v6 != 0)
35056b2bdd1SGireesh Nagabhushana 		ASSERT(0);
35156b2bdd1SGireesh Nagabhushana again:
35256b2bdd1SGireesh Nagabhushana 	switch (e->state) {
35356b2bdd1SGireesh Nagabhushana 	case L2T_STATE_STALE:	/* entry is stale, kick off revalidation */
35456b2bdd1SGireesh Nagabhushana 
35556b2bdd1SGireesh Nagabhushana 	/* Fall through */
35656b2bdd1SGireesh Nagabhushana 	case L2T_STATE_VALID:	/* fast-path, send the packet on */
35756b2bdd1SGireesh Nagabhushana 		(void) t4_wrq_tx(sc, MBUF_EQ(m), m);
35856b2bdd1SGireesh Nagabhushana 		return (0);
35956b2bdd1SGireesh Nagabhushana 
36056b2bdd1SGireesh Nagabhushana 	case L2T_STATE_RESOLVING:
36156b2bdd1SGireesh Nagabhushana 	case L2T_STATE_SYNC_WRITE:
36256b2bdd1SGireesh Nagabhushana 		mutex_enter(&e->lock);
36356b2bdd1SGireesh Nagabhushana 		if (e->state != L2T_STATE_SYNC_WRITE &&
36456b2bdd1SGireesh Nagabhushana 		    e->state != L2T_STATE_RESOLVING) {
36556b2bdd1SGireesh Nagabhushana 			/* state changed by the time we got here */
36656b2bdd1SGireesh Nagabhushana 			mutex_exit(&e->lock);
36756b2bdd1SGireesh Nagabhushana 			goto again;
36856b2bdd1SGireesh Nagabhushana 		}
36956b2bdd1SGireesh Nagabhushana 		arpq_enqueue(e, m);
37056b2bdd1SGireesh Nagabhushana 		mutex_exit(&e->lock);
37156b2bdd1SGireesh Nagabhushana 
37256b2bdd1SGireesh Nagabhushana 		bzero(&ip2m, sizeof (ip2m));
37356b2bdd1SGireesh Nagabhushana 		sin = (sin_t *)&ip2m.ip2mac_pa;
37456b2bdd1SGireesh Nagabhushana 		sin->sin_family = AF_INET;
37556b2bdd1SGireesh Nagabhushana 		sin->sin_addr.s_addr = e->in_addr;
37656b2bdd1SGireesh Nagabhushana 		ip2m.ip2mac_ifindex = e->ifindex;
37756b2bdd1SGireesh Nagabhushana 
37856b2bdd1SGireesh Nagabhushana 		if (e->state == L2T_STATE_RESOLVING) {
37956b2bdd1SGireesh Nagabhushana 			(void) ip2mac(IP2MAC_RESOLVE, &ip2m, t4_l2t_update, e,
38056b2bdd1SGireesh Nagabhushana 			    0);
38156b2bdd1SGireesh Nagabhushana 			if (ip2m.ip2mac_err == EINPROGRESS)
38256b2bdd1SGireesh Nagabhushana 				ASSERT(0);
38356b2bdd1SGireesh Nagabhushana 			else if (ip2m.ip2mac_err == 0)
38456b2bdd1SGireesh Nagabhushana 				t4_l2t_update(&ip2m, e);
38556b2bdd1SGireesh Nagabhushana 			else
38656b2bdd1SGireesh Nagabhushana 				ASSERT(0);
38756b2bdd1SGireesh Nagabhushana 		}
38856b2bdd1SGireesh Nagabhushana 	}
38956b2bdd1SGireesh Nagabhushana 
39056b2bdd1SGireesh Nagabhushana 	return (0);
39156b2bdd1SGireesh Nagabhushana }
39256b2bdd1SGireesh Nagabhushana 
39356b2bdd1SGireesh Nagabhushana /*
39456b2bdd1SGireesh Nagabhushana  * Called when an L2T entry has no more users.  The entry is left in the hash
39556b2bdd1SGireesh Nagabhushana  * table since it is likely to be reused but we also bump nfree to indicate
39656b2bdd1SGireesh Nagabhushana  * that the entry can be reallocated for a different neighbor.  We also drop
39756b2bdd1SGireesh Nagabhushana  * the existing neighbor reference in case the neighbor is going away and is
39856b2bdd1SGireesh Nagabhushana  * waiting on our reference.
39956b2bdd1SGireesh Nagabhushana  *
40056b2bdd1SGireesh Nagabhushana  * Because entries can be reallocated to other neighbors once their ref count
40156b2bdd1SGireesh Nagabhushana  * drops to 0 we need to take the entry's lock to avoid races with a new
40256b2bdd1SGireesh Nagabhushana  * incarnation.
40356b2bdd1SGireesh Nagabhushana  */
40456b2bdd1SGireesh Nagabhushana static void
t4_l2e_free(struct l2t_entry * e)40556b2bdd1SGireesh Nagabhushana t4_l2e_free(struct l2t_entry *e)
40656b2bdd1SGireesh Nagabhushana {
40756b2bdd1SGireesh Nagabhushana 	struct l2t_data *d;
40856b2bdd1SGireesh Nagabhushana 
40956b2bdd1SGireesh Nagabhushana 	mutex_enter(&e->lock);
41056b2bdd1SGireesh Nagabhushana 	/* LINTED: E_NOP_IF_STMT */
41156b2bdd1SGireesh Nagabhushana 	if (atomic_read(&e->refcnt) == 0) {  /* hasn't been recycled */
41256b2bdd1SGireesh Nagabhushana 		/*
41356b2bdd1SGireesh Nagabhushana 		 * Don't need to worry about the arpq, an L2T entry can't be
41456b2bdd1SGireesh Nagabhushana 		 * released if any packets are waiting for resolution as we
41556b2bdd1SGireesh Nagabhushana 		 * need to be able to communicate with the device to close a
41656b2bdd1SGireesh Nagabhushana 		 * connection.
41756b2bdd1SGireesh Nagabhushana 		 */
41856b2bdd1SGireesh Nagabhushana 	}
41956b2bdd1SGireesh Nagabhushana 	mutex_exit(&e->lock);
42056b2bdd1SGireesh Nagabhushana 
421*94c3dad2SToomas Soome 	d = __containerof(e, struct l2t_data, l2tab[e->idx]);
4221a5e258fSJosef 'Jeff' Sipek 	atomic_inc_uint(&d->nfree);
42356b2bdd1SGireesh Nagabhushana 
42456b2bdd1SGireesh Nagabhushana }
42556b2bdd1SGireesh Nagabhushana 
42656b2bdd1SGireesh Nagabhushana void
t4_l2t_release(struct l2t_entry * e)42756b2bdd1SGireesh Nagabhushana t4_l2t_release(struct l2t_entry *e)
42856b2bdd1SGireesh Nagabhushana {
42956b2bdd1SGireesh Nagabhushana 	if (atomic_dec_uint_nv(&e->refcnt) == 0)
43056b2bdd1SGireesh Nagabhushana 		t4_l2e_free(e);
43156b2bdd1SGireesh Nagabhushana }
43256b2bdd1SGireesh Nagabhushana 
43356b2bdd1SGireesh Nagabhushana /* ARGSUSED */
43456b2bdd1SGireesh Nagabhushana int
do_l2t_write_rpl(struct sge_iq * iq,const struct rss_header * rss,mblk_t * m)43556b2bdd1SGireesh Nagabhushana do_l2t_write_rpl(struct sge_iq *iq, const struct rss_header *rss, mblk_t *m)
43656b2bdd1SGireesh Nagabhushana {
43756b2bdd1SGireesh Nagabhushana 	struct adapter *sc = iq->adapter;
43856b2bdd1SGireesh Nagabhushana 	const struct cpl_l2t_write_rpl *rpl = (const void *)(rss + 1);
43956b2bdd1SGireesh Nagabhushana 	unsigned int tid = GET_TID(rpl);
440de483253SVishal Kulkarni 	unsigned int idx = tid % L2T_SIZE;
44156b2bdd1SGireesh Nagabhushana 
44256b2bdd1SGireesh Nagabhushana 	if (likely(rpl->status != CPL_ERR_NONE)) {
44356b2bdd1SGireesh Nagabhushana 		cxgb_printf(sc->dip, CE_WARN,
44456b2bdd1SGireesh Nagabhushana 		    "Unexpected L2T_WRITE_RPL status %u for entry %u",
44556b2bdd1SGireesh Nagabhushana 		    rpl->status, idx);
44656b2bdd1SGireesh Nagabhushana 		return (-EINVAL);
44756b2bdd1SGireesh Nagabhushana 	}
44856b2bdd1SGireesh Nagabhushana 
44956b2bdd1SGireesh Nagabhushana 	return (0);
45056b2bdd1SGireesh Nagabhushana }
45156b2bdd1SGireesh Nagabhushana 
45256b2bdd1SGireesh Nagabhushana /*
45356b2bdd1SGireesh Nagabhushana  * The TOE wants an L2 table entry that it can use to reach the next hop over
45456b2bdd1SGireesh Nagabhushana  * the specified port.  Produce such an entry - create one if needed.
45556b2bdd1SGireesh Nagabhushana  *
45656b2bdd1SGireesh Nagabhushana  * Note that the ifnet could be a pseudo-device like if_vlan, if_lagg, etc. on
45756b2bdd1SGireesh Nagabhushana  * top of the real cxgbe interface.
45856b2bdd1SGireesh Nagabhushana  */
45956b2bdd1SGireesh Nagabhushana struct l2t_entry *
t4_l2t_get(struct port_info * pi,conn_t * connp)46056b2bdd1SGireesh Nagabhushana t4_l2t_get(struct port_info *pi, conn_t *connp)
46156b2bdd1SGireesh Nagabhushana {
46256b2bdd1SGireesh Nagabhushana 	struct l2t_entry *e;
46356b2bdd1SGireesh Nagabhushana 	struct l2t_data *d = pi->adapter->l2t;
46456b2bdd1SGireesh Nagabhushana 	int addr_len;
46556b2bdd1SGireesh Nagabhushana 	uint32_t *addr;
46656b2bdd1SGireesh Nagabhushana 	int hash;
46756b2bdd1SGireesh Nagabhushana 	int index = \
46856b2bdd1SGireesh Nagabhushana 	    connp->conn_ixa->ixa_ire->ire_ill->ill_phyint->phyint_ifindex;
46956b2bdd1SGireesh Nagabhushana 	unsigned int smt_idx = pi->port_id;
47056b2bdd1SGireesh Nagabhushana 	addr = (uint32_t *)&connp->conn_faddr_v4;
47156b2bdd1SGireesh Nagabhushana 	addr_len  = sizeof (connp->conn_faddr_v4);
47256b2bdd1SGireesh Nagabhushana 
47356b2bdd1SGireesh Nagabhushana 	hash = addr_hash(addr, addr_len, index);
47456b2bdd1SGireesh Nagabhushana 
47556b2bdd1SGireesh Nagabhushana 	rw_enter(&d->lock, RW_WRITER);
47656b2bdd1SGireesh Nagabhushana 	for (e = d->l2tab[hash].first; e; e = e->next) {
47756b2bdd1SGireesh Nagabhushana 		if (!addreq(e, addr) && e->smt_idx == smt_idx) {
47856b2bdd1SGireesh Nagabhushana 			l2t_hold(d, e);
47956b2bdd1SGireesh Nagabhushana 			goto done;
48056b2bdd1SGireesh Nagabhushana 		}
48156b2bdd1SGireesh Nagabhushana 	}
48256b2bdd1SGireesh Nagabhushana 
48356b2bdd1SGireesh Nagabhushana 	/* Need to allocate a new entry */
48456b2bdd1SGireesh Nagabhushana 	e = alloc_l2e(d);
48556b2bdd1SGireesh Nagabhushana 	if (e != NULL) {
48656b2bdd1SGireesh Nagabhushana 		mutex_enter(&e->lock);	/* avoid race with t4_l2t_free */
48756b2bdd1SGireesh Nagabhushana 		e->state = L2T_STATE_RESOLVING;
48856b2bdd1SGireesh Nagabhushana 		(void) memcpy(e->addr, addr, addr_len);
48956b2bdd1SGireesh Nagabhushana 		e->in_addr = connp->conn_faddr_v4;
49056b2bdd1SGireesh Nagabhushana 		e->ifindex = index;
49156b2bdd1SGireesh Nagabhushana 		/* LINTED: E_ASSIGN_NARROW_CONV */
49256b2bdd1SGireesh Nagabhushana 		e->smt_idx = smt_idx;
49356b2bdd1SGireesh Nagabhushana 		/* LINTED: E_ASSIGN_NARROW_CONV */
49456b2bdd1SGireesh Nagabhushana 		e->hash = hash;
49556b2bdd1SGireesh Nagabhushana 		e->lport = pi->lport;
49656b2bdd1SGireesh Nagabhushana 		e->arpq_head = e->arpq_tail = NULL;
49756b2bdd1SGireesh Nagabhushana 		e->v6 = (addr_len == 16);
49856b2bdd1SGireesh Nagabhushana 		e->sc = pi->adapter;
49956b2bdd1SGireesh Nagabhushana 		(void) atomic_swap_uint(&e->refcnt, 1);
50056b2bdd1SGireesh Nagabhushana 		e->vlan = VLAN_NONE;
50156b2bdd1SGireesh Nagabhushana 		e->next = d->l2tab[hash].first;
50256b2bdd1SGireesh Nagabhushana 		d->l2tab[hash].first = e;
50356b2bdd1SGireesh Nagabhushana 		mutex_exit(&e->lock);
50456b2bdd1SGireesh Nagabhushana 	} else {
50556b2bdd1SGireesh Nagabhushana 		ASSERT(0);
50656b2bdd1SGireesh Nagabhushana 	}
50756b2bdd1SGireesh Nagabhushana 
50856b2bdd1SGireesh Nagabhushana done:
50956b2bdd1SGireesh Nagabhushana 	rw_exit(&d->lock);
51056b2bdd1SGireesh Nagabhushana 	return (e);
51156b2bdd1SGireesh Nagabhushana }
51256b2bdd1SGireesh Nagabhushana 
51356b2bdd1SGireesh Nagabhushana /*
51456b2bdd1SGireesh Nagabhushana  * Called when the host's neighbor layer makes a change to some entry that is
51556b2bdd1SGireesh Nagabhushana  * loaded into the HW L2 table.
51656b2bdd1SGireesh Nagabhushana  */
51756b2bdd1SGireesh Nagabhushana void
t4_l2t_update(ip2mac_t * ip2macp,void * arg)51856b2bdd1SGireesh Nagabhushana t4_l2t_update(ip2mac_t *ip2macp, void *arg)
51956b2bdd1SGireesh Nagabhushana {
52056b2bdd1SGireesh Nagabhushana 	struct l2t_entry *e = (struct l2t_entry *)arg;
52156b2bdd1SGireesh Nagabhushana 	struct adapter *sc = e->sc;
52256b2bdd1SGireesh Nagabhushana 	uchar_t *cp;
52356b2bdd1SGireesh Nagabhushana 
52456b2bdd1SGireesh Nagabhushana 	if (ip2macp->ip2mac_err != 0) {
52556b2bdd1SGireesh Nagabhushana 		ASSERT(0); /* Don't know what to do. Needs to be investigated */
52656b2bdd1SGireesh Nagabhushana 	}
52756b2bdd1SGireesh Nagabhushana 
52856b2bdd1SGireesh Nagabhushana 	mutex_enter(&e->lock);
52956b2bdd1SGireesh Nagabhushana 	if (atomic_read(&e->refcnt) != 0)
53056b2bdd1SGireesh Nagabhushana 		goto found;
53156b2bdd1SGireesh Nagabhushana 	e->state = L2T_STATE_STALE;
53256b2bdd1SGireesh Nagabhushana 	mutex_exit(&e->lock);
53356b2bdd1SGireesh Nagabhushana 
53456b2bdd1SGireesh Nagabhushana 	/* The TOE has no interest in this LLE */
53556b2bdd1SGireesh Nagabhushana 	return;
53656b2bdd1SGireesh Nagabhushana 
53756b2bdd1SGireesh Nagabhushana found:
53856b2bdd1SGireesh Nagabhushana 	if (atomic_read(&e->refcnt) != 0) {
53956b2bdd1SGireesh Nagabhushana 
54056b2bdd1SGireesh Nagabhushana 		/* Entry is referenced by at least 1 offloaded connection. */
54156b2bdd1SGireesh Nagabhushana 
54256b2bdd1SGireesh Nagabhushana 		cp = (uchar_t *)LLADDR(&ip2macp->ip2mac_ha);
54356b2bdd1SGireesh Nagabhushana 		bcopy(cp, e->dmac, 6);
54456b2bdd1SGireesh Nagabhushana 		(void) write_l2e(sc, e, 1);
54556b2bdd1SGireesh Nagabhushana 		e->state = L2T_STATE_VALID;
54656b2bdd1SGireesh Nagabhushana 
54756b2bdd1SGireesh Nagabhushana 	}
54856b2bdd1SGireesh Nagabhushana 	mutex_exit(&e->lock);
54956b2bdd1SGireesh Nagabhushana }
55056b2bdd1SGireesh Nagabhushana #endif