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