18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is part of the Chelsio T4 Ethernet driver for Linux. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 78c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 88c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 98c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 108c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 138c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 148c2ecf20Sopenharmony_ci * conditions are met: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 178c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 188c2ecf20Sopenharmony_ci * disclaimer. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 218c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 228c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 238c2ecf20Sopenharmony_ci * provided with the distribution. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 268c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 278c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 288c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 298c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 308c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 318c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 328c2ecf20Sopenharmony_ci * SOFTWARE. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 368c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 378c2ecf20Sopenharmony_ci#include <linux/if.h> 388c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 398c2ecf20Sopenharmony_ci#include <linux/jhash.h> 408c2ecf20Sopenharmony_ci#include <linux/module.h> 418c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 428c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 438c2ecf20Sopenharmony_ci#include <net/neighbour.h> 448c2ecf20Sopenharmony_ci#include "cxgb4.h" 458c2ecf20Sopenharmony_ci#include "l2t.h" 468c2ecf20Sopenharmony_ci#include "t4_msg.h" 478c2ecf20Sopenharmony_ci#include "t4fw_api.h" 488c2ecf20Sopenharmony_ci#include "t4_regs.h" 498c2ecf20Sopenharmony_ci#include "t4_values.h" 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* identifies sync vs async L2T_WRITE_REQs */ 528c2ecf20Sopenharmony_ci#define SYNC_WR_S 12 538c2ecf20Sopenharmony_ci#define SYNC_WR_V(x) ((x) << SYNC_WR_S) 548c2ecf20Sopenharmony_ci#define SYNC_WR_F SYNC_WR_V(1) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct l2t_data { 578c2ecf20Sopenharmony_ci unsigned int l2t_start; /* start index of our piece of the L2T */ 588c2ecf20Sopenharmony_ci unsigned int l2t_size; /* number of entries in l2tab */ 598c2ecf20Sopenharmony_ci rwlock_t lock; 608c2ecf20Sopenharmony_ci atomic_t nfree; /* number of free entries */ 618c2ecf20Sopenharmony_ci struct l2t_entry *rover; /* starting point for next allocation */ 628c2ecf20Sopenharmony_ci struct l2t_entry l2tab[]; /* MUST BE LAST */ 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic inline unsigned int vlan_prio(const struct l2t_entry *e) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci return e->vlan >> VLAN_PRIO_SHIFT; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci if (atomic_add_return(1, &e->refcnt) == 1) /* 0 -> 1 transition */ 738c2ecf20Sopenharmony_ci atomic_dec(&d->nfree); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * To avoid having to check address families we do not allow v4 and v6 788c2ecf20Sopenharmony_ci * neighbors to be on the same hash chain. We keep v4 entries in the first 798c2ecf20Sopenharmony_ci * half of available hash buckets and v6 in the second. We need at least two 808c2ecf20Sopenharmony_ci * entries in our L2T for this scheme to work. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_cienum { 838c2ecf20Sopenharmony_ci L2T_MIN_HASH_BUCKETS = 2, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic inline unsigned int arp_hash(struct l2t_data *d, const u32 *key, 878c2ecf20Sopenharmony_ci int ifindex) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci unsigned int l2t_size_half = d->l2t_size / 2; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return jhash_2words(*key, ifindex, 0) % l2t_size_half; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline unsigned int ipv6_hash(struct l2t_data *d, const u32 *key, 958c2ecf20Sopenharmony_ci int ifindex) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci unsigned int l2t_size_half = d->l2t_size / 2; 988c2ecf20Sopenharmony_ci u32 xor = key[0] ^ key[1] ^ key[2] ^ key[3]; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return (l2t_size_half + 1018c2ecf20Sopenharmony_ci (jhash_2words(xor, ifindex, 0) % l2t_size_half)); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic unsigned int addr_hash(struct l2t_data *d, const u32 *addr, 1058c2ecf20Sopenharmony_ci int addr_len, int ifindex) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci return addr_len == 4 ? arp_hash(d, addr, ifindex) : 1088c2ecf20Sopenharmony_ci ipv6_hash(d, addr, ifindex); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * Checks if an L2T entry is for the given IP/IPv6 address. It does not check 1138c2ecf20Sopenharmony_ci * whether the L2T entry and the address are of the same address family. 1148c2ecf20Sopenharmony_ci * Callers ensure an address is only checked against L2T entries of the same 1158c2ecf20Sopenharmony_ci * family, something made trivial by the separation of IP and IPv6 hash chains 1168c2ecf20Sopenharmony_ci * mentioned above. Returns 0 if there's a match, 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_cistatic int addreq(const struct l2t_entry *e, const u32 *addr) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci if (e->v6) 1218c2ecf20Sopenharmony_ci return (e->addr[0] ^ addr[0]) | (e->addr[1] ^ addr[1]) | 1228c2ecf20Sopenharmony_ci (e->addr[2] ^ addr[2]) | (e->addr[3] ^ addr[3]); 1238c2ecf20Sopenharmony_ci return e->addr[0] ^ addr[0]; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void neigh_replace(struct l2t_entry *e, struct neighbour *n) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci neigh_hold(n); 1298c2ecf20Sopenharmony_ci if (e->neigh) 1308c2ecf20Sopenharmony_ci neigh_release(e->neigh); 1318c2ecf20Sopenharmony_ci e->neigh = n; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* 1358c2ecf20Sopenharmony_ci * Write an L2T entry. Must be called with the entry locked. 1368c2ecf20Sopenharmony_ci * The write may be synchronous or asynchronous. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct l2t_data *d = adap->l2t; 1418c2ecf20Sopenharmony_ci unsigned int l2t_idx = e->idx + d->l2t_start; 1428c2ecf20Sopenharmony_ci struct sk_buff *skb; 1438c2ecf20Sopenharmony_ci struct cpl_l2t_write_req *req; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci skb = alloc_skb(sizeof(*req), GFP_ATOMIC); 1468c2ecf20Sopenharmony_ci if (!skb) 1478c2ecf20Sopenharmony_ci return -ENOMEM; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci req = __skb_put(skb, sizeof(*req)); 1508c2ecf20Sopenharmony_ci INIT_TP_WR(req, 0); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, 1538c2ecf20Sopenharmony_ci l2t_idx | (sync ? SYNC_WR_F : 0) | 1548c2ecf20Sopenharmony_ci TID_QID_V(adap->sge.fw_evtq.abs_id))); 1558c2ecf20Sopenharmony_ci req->params = htons(L2T_W_PORT_V(e->lport) | L2T_W_NOREPLY_V(!sync)); 1568c2ecf20Sopenharmony_ci req->l2t_idx = htons(l2t_idx); 1578c2ecf20Sopenharmony_ci req->vlan = htons(e->vlan); 1588c2ecf20Sopenharmony_ci if (e->neigh && !(e->neigh->dev->flags & IFF_LOOPBACK)) 1598c2ecf20Sopenharmony_ci memcpy(e->dmac, e->neigh->ha, sizeof(e->dmac)); 1608c2ecf20Sopenharmony_ci memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac)); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci t4_mgmt_tx(adap, skb); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (sync && e->state != L2T_STATE_SWITCHING) 1658c2ecf20Sopenharmony_ci e->state = L2T_STATE_SYNC_WRITE; 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* 1708c2ecf20Sopenharmony_ci * Send packets waiting in an L2T entry's ARP queue. Must be called with the 1718c2ecf20Sopenharmony_ci * entry locked. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_cistatic void send_pending(struct adapter *adap, struct l2t_entry *e) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct sk_buff *skb; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci while ((skb = __skb_dequeue(&e->arpq)) != NULL) 1788c2ecf20Sopenharmony_ci t4_ofld_send(adap, skb); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * Process a CPL_L2T_WRITE_RPL. Wake up the ARP queue if it completes a 1838c2ecf20Sopenharmony_ci * synchronous L2T_WRITE. Note that the TID in the reply is really the L2T 1848c2ecf20Sopenharmony_ci * index it refers to. 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_civoid do_l2t_write_rpl(struct adapter *adap, const struct cpl_l2t_write_rpl *rpl) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct l2t_data *d = adap->l2t; 1898c2ecf20Sopenharmony_ci unsigned int tid = GET_TID(rpl); 1908c2ecf20Sopenharmony_ci unsigned int l2t_idx = tid % L2T_SIZE; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (unlikely(rpl->status != CPL_ERR_NONE)) { 1938c2ecf20Sopenharmony_ci dev_err(adap->pdev_dev, 1948c2ecf20Sopenharmony_ci "Unexpected L2T_WRITE_RPL status %u for entry %u\n", 1958c2ecf20Sopenharmony_ci rpl->status, l2t_idx); 1968c2ecf20Sopenharmony_ci return; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (tid & SYNC_WR_F) { 2008c2ecf20Sopenharmony_ci struct l2t_entry *e = &d->l2tab[l2t_idx - d->l2t_start]; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci spin_lock(&e->lock); 2038c2ecf20Sopenharmony_ci if (e->state != L2T_STATE_SWITCHING) { 2048c2ecf20Sopenharmony_ci send_pending(adap, e); 2058c2ecf20Sopenharmony_ci e->state = (e->neigh->nud_state & NUD_STALE) ? 2068c2ecf20Sopenharmony_ci L2T_STATE_STALE : L2T_STATE_VALID; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci spin_unlock(&e->lock); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* 2138c2ecf20Sopenharmony_ci * Add a packet to an L2T entry's queue of packets awaiting resolution. 2148c2ecf20Sopenharmony_ci * Must be called with the entry's lock held. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cistatic inline void arpq_enqueue(struct l2t_entry *e, struct sk_buff *skb) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci __skb_queue_tail(&e->arpq, skb); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ciint cxgb4_l2t_send(struct net_device *dev, struct sk_buff *skb, 2228c2ecf20Sopenharmony_ci struct l2t_entry *e) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct adapter *adap = netdev2adap(dev); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ciagain: 2278c2ecf20Sopenharmony_ci switch (e->state) { 2288c2ecf20Sopenharmony_ci case L2T_STATE_STALE: /* entry is stale, kick off revalidation */ 2298c2ecf20Sopenharmony_ci neigh_event_send(e->neigh, NULL); 2308c2ecf20Sopenharmony_ci spin_lock_bh(&e->lock); 2318c2ecf20Sopenharmony_ci if (e->state == L2T_STATE_STALE) 2328c2ecf20Sopenharmony_ci e->state = L2T_STATE_VALID; 2338c2ecf20Sopenharmony_ci spin_unlock_bh(&e->lock); 2348c2ecf20Sopenharmony_ci fallthrough; 2358c2ecf20Sopenharmony_ci case L2T_STATE_VALID: /* fast-path, send the packet on */ 2368c2ecf20Sopenharmony_ci return t4_ofld_send(adap, skb); 2378c2ecf20Sopenharmony_ci case L2T_STATE_RESOLVING: 2388c2ecf20Sopenharmony_ci case L2T_STATE_SYNC_WRITE: 2398c2ecf20Sopenharmony_ci spin_lock_bh(&e->lock); 2408c2ecf20Sopenharmony_ci if (e->state != L2T_STATE_SYNC_WRITE && 2418c2ecf20Sopenharmony_ci e->state != L2T_STATE_RESOLVING) { 2428c2ecf20Sopenharmony_ci spin_unlock_bh(&e->lock); 2438c2ecf20Sopenharmony_ci goto again; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci arpq_enqueue(e, skb); 2468c2ecf20Sopenharmony_ci spin_unlock_bh(&e->lock); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (e->state == L2T_STATE_RESOLVING && 2498c2ecf20Sopenharmony_ci !neigh_event_send(e->neigh, NULL)) { 2508c2ecf20Sopenharmony_ci spin_lock_bh(&e->lock); 2518c2ecf20Sopenharmony_ci if (e->state == L2T_STATE_RESOLVING && 2528c2ecf20Sopenharmony_ci !skb_queue_empty(&e->arpq)) 2538c2ecf20Sopenharmony_ci write_l2e(adap, e, 1); 2548c2ecf20Sopenharmony_ci spin_unlock_bh(&e->lock); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgb4_l2t_send); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci/* 2628c2ecf20Sopenharmony_ci * Allocate a free L2T entry. Must be called with l2t_data.lock held. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_cistatic struct l2t_entry *alloc_l2e(struct l2t_data *d) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct l2t_entry *end, *e, **p; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (!atomic_read(&d->nfree)) 2698c2ecf20Sopenharmony_ci return NULL; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* there's definitely a free entry */ 2728c2ecf20Sopenharmony_ci for (e = d->rover, end = &d->l2tab[d->l2t_size]; e != end; ++e) 2738c2ecf20Sopenharmony_ci if (atomic_read(&e->refcnt) == 0) 2748c2ecf20Sopenharmony_ci goto found; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci for (e = d->l2tab; atomic_read(&e->refcnt); ++e) 2778c2ecf20Sopenharmony_ci ; 2788c2ecf20Sopenharmony_cifound: 2798c2ecf20Sopenharmony_ci d->rover = e + 1; 2808c2ecf20Sopenharmony_ci atomic_dec(&d->nfree); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* 2838c2ecf20Sopenharmony_ci * The entry we found may be an inactive entry that is 2848c2ecf20Sopenharmony_ci * presently in the hash table. We need to remove it. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci if (e->state < L2T_STATE_SWITCHING) 2878c2ecf20Sopenharmony_ci for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next) 2888c2ecf20Sopenharmony_ci if (*p == e) { 2898c2ecf20Sopenharmony_ci *p = e->next; 2908c2ecf20Sopenharmony_ci e->next = NULL; 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci e->state = L2T_STATE_UNUSED; 2958c2ecf20Sopenharmony_ci return e; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic struct l2t_entry *find_or_alloc_l2e(struct l2t_data *d, u16 vlan, 2998c2ecf20Sopenharmony_ci u8 port, u8 *dmac) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct l2t_entry *end, *e, **p; 3028c2ecf20Sopenharmony_ci struct l2t_entry *first_free = NULL; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci for (e = &d->l2tab[0], end = &d->l2tab[d->l2t_size]; e != end; ++e) { 3058c2ecf20Sopenharmony_ci if (atomic_read(&e->refcnt) == 0) { 3068c2ecf20Sopenharmony_ci if (!first_free) 3078c2ecf20Sopenharmony_ci first_free = e; 3088c2ecf20Sopenharmony_ci } else { 3098c2ecf20Sopenharmony_ci if (e->state == L2T_STATE_SWITCHING) { 3108c2ecf20Sopenharmony_ci if (ether_addr_equal(e->dmac, dmac) && 3118c2ecf20Sopenharmony_ci (e->vlan == vlan) && (e->lport == port)) 3128c2ecf20Sopenharmony_ci goto exists; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (first_free) { 3188c2ecf20Sopenharmony_ci e = first_free; 3198c2ecf20Sopenharmony_ci goto found; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return NULL; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cifound: 3258c2ecf20Sopenharmony_ci /* The entry we found may be an inactive entry that is 3268c2ecf20Sopenharmony_ci * presently in the hash table. We need to remove it. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci if (e->state < L2T_STATE_SWITCHING) 3298c2ecf20Sopenharmony_ci for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next) 3308c2ecf20Sopenharmony_ci if (*p == e) { 3318c2ecf20Sopenharmony_ci *p = e->next; 3328c2ecf20Sopenharmony_ci e->next = NULL; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci e->state = L2T_STATE_UNUSED; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ciexists: 3388c2ecf20Sopenharmony_ci return e; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/* Called when an L2T entry has no more users. The entry is left in the hash 3428c2ecf20Sopenharmony_ci * table since it is likely to be reused but we also bump nfree to indicate 3438c2ecf20Sopenharmony_ci * that the entry can be reallocated for a different neighbor. We also drop 3448c2ecf20Sopenharmony_ci * the existing neighbor reference in case the neighbor is going away and is 3458c2ecf20Sopenharmony_ci * waiting on our reference. 3468c2ecf20Sopenharmony_ci * 3478c2ecf20Sopenharmony_ci * Because entries can be reallocated to other neighbors once their ref count 3488c2ecf20Sopenharmony_ci * drops to 0 we need to take the entry's lock to avoid races with a new 3498c2ecf20Sopenharmony_ci * incarnation. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_cistatic void _t4_l2e_free(struct l2t_entry *e) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct l2t_data *d; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */ 3568c2ecf20Sopenharmony_ci if (e->neigh) { 3578c2ecf20Sopenharmony_ci neigh_release(e->neigh); 3588c2ecf20Sopenharmony_ci e->neigh = NULL; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci __skb_queue_purge(&e->arpq); 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci d = container_of(e, struct l2t_data, l2tab[e->idx]); 3648c2ecf20Sopenharmony_ci atomic_inc(&d->nfree); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/* Locked version of _t4_l2e_free */ 3688c2ecf20Sopenharmony_cistatic void t4_l2e_free(struct l2t_entry *e) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct l2t_data *d; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci spin_lock_bh(&e->lock); 3738c2ecf20Sopenharmony_ci if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */ 3748c2ecf20Sopenharmony_ci if (e->neigh) { 3758c2ecf20Sopenharmony_ci neigh_release(e->neigh); 3768c2ecf20Sopenharmony_ci e->neigh = NULL; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci __skb_queue_purge(&e->arpq); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci spin_unlock_bh(&e->lock); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci d = container_of(e, struct l2t_data, l2tab[e->idx]); 3838c2ecf20Sopenharmony_ci atomic_inc(&d->nfree); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_civoid cxgb4_l2t_release(struct l2t_entry *e) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&e->refcnt)) 3898c2ecf20Sopenharmony_ci t4_l2e_free(e); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgb4_l2t_release); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* 3948c2ecf20Sopenharmony_ci * Update an L2T entry that was previously used for the same next hop as neigh. 3958c2ecf20Sopenharmony_ci * Must be called with softirqs disabled. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_cistatic void reuse_entry(struct l2t_entry *e, struct neighbour *neigh) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci unsigned int nud_state; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci spin_lock(&e->lock); /* avoid race with t4_l2t_free */ 4028c2ecf20Sopenharmony_ci if (neigh != e->neigh) 4038c2ecf20Sopenharmony_ci neigh_replace(e, neigh); 4048c2ecf20Sopenharmony_ci nud_state = neigh->nud_state; 4058c2ecf20Sopenharmony_ci if (memcmp(e->dmac, neigh->ha, sizeof(e->dmac)) || 4068c2ecf20Sopenharmony_ci !(nud_state & NUD_VALID)) 4078c2ecf20Sopenharmony_ci e->state = L2T_STATE_RESOLVING; 4088c2ecf20Sopenharmony_ci else if (nud_state & NUD_CONNECTED) 4098c2ecf20Sopenharmony_ci e->state = L2T_STATE_VALID; 4108c2ecf20Sopenharmony_ci else 4118c2ecf20Sopenharmony_ci e->state = L2T_STATE_STALE; 4128c2ecf20Sopenharmony_ci spin_unlock(&e->lock); 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistruct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh, 4168c2ecf20Sopenharmony_ci const struct net_device *physdev, 4178c2ecf20Sopenharmony_ci unsigned int priority) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci u8 lport; 4208c2ecf20Sopenharmony_ci u16 vlan; 4218c2ecf20Sopenharmony_ci struct l2t_entry *e; 4228c2ecf20Sopenharmony_ci unsigned int addr_len = neigh->tbl->key_len; 4238c2ecf20Sopenharmony_ci u32 *addr = (u32 *)neigh->primary_key; 4248c2ecf20Sopenharmony_ci int ifidx = neigh->dev->ifindex; 4258c2ecf20Sopenharmony_ci int hash = addr_hash(d, addr, addr_len, ifidx); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (neigh->dev->flags & IFF_LOOPBACK) 4288c2ecf20Sopenharmony_ci lport = netdev2pinfo(physdev)->tx_chan + 4; 4298c2ecf20Sopenharmony_ci else 4308c2ecf20Sopenharmony_ci lport = netdev2pinfo(physdev)->lport; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (is_vlan_dev(neigh->dev)) { 4338c2ecf20Sopenharmony_ci vlan = vlan_dev_vlan_id(neigh->dev); 4348c2ecf20Sopenharmony_ci vlan |= vlan_dev_get_egress_qos_mask(neigh->dev, priority); 4358c2ecf20Sopenharmony_ci } else { 4368c2ecf20Sopenharmony_ci vlan = VLAN_NONE; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci write_lock_bh(&d->lock); 4408c2ecf20Sopenharmony_ci for (e = d->l2tab[hash].first; e; e = e->next) 4418c2ecf20Sopenharmony_ci if (!addreq(e, addr) && e->ifindex == ifidx && 4428c2ecf20Sopenharmony_ci e->vlan == vlan && e->lport == lport) { 4438c2ecf20Sopenharmony_ci l2t_hold(d, e); 4448c2ecf20Sopenharmony_ci if (atomic_read(&e->refcnt) == 1) 4458c2ecf20Sopenharmony_ci reuse_entry(e, neigh); 4468c2ecf20Sopenharmony_ci goto done; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* Need to allocate a new entry */ 4508c2ecf20Sopenharmony_ci e = alloc_l2e(d); 4518c2ecf20Sopenharmony_ci if (e) { 4528c2ecf20Sopenharmony_ci spin_lock(&e->lock); /* avoid race with t4_l2t_free */ 4538c2ecf20Sopenharmony_ci e->state = L2T_STATE_RESOLVING; 4548c2ecf20Sopenharmony_ci if (neigh->dev->flags & IFF_LOOPBACK) 4558c2ecf20Sopenharmony_ci memcpy(e->dmac, physdev->dev_addr, sizeof(e->dmac)); 4568c2ecf20Sopenharmony_ci memcpy(e->addr, addr, addr_len); 4578c2ecf20Sopenharmony_ci e->ifindex = ifidx; 4588c2ecf20Sopenharmony_ci e->hash = hash; 4598c2ecf20Sopenharmony_ci e->lport = lport; 4608c2ecf20Sopenharmony_ci e->v6 = addr_len == 16; 4618c2ecf20Sopenharmony_ci atomic_set(&e->refcnt, 1); 4628c2ecf20Sopenharmony_ci neigh_replace(e, neigh); 4638c2ecf20Sopenharmony_ci e->vlan = vlan; 4648c2ecf20Sopenharmony_ci e->next = d->l2tab[hash].first; 4658c2ecf20Sopenharmony_ci d->l2tab[hash].first = e; 4668c2ecf20Sopenharmony_ci spin_unlock(&e->lock); 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_cidone: 4698c2ecf20Sopenharmony_ci write_unlock_bh(&d->lock); 4708c2ecf20Sopenharmony_ci return e; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgb4_l2t_get); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ciu64 cxgb4_select_ntuple(struct net_device *dev, 4758c2ecf20Sopenharmony_ci const struct l2t_entry *l2t) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct adapter *adap = netdev2adap(dev); 4788c2ecf20Sopenharmony_ci struct tp_params *tp = &adap->params.tp; 4798c2ecf20Sopenharmony_ci u64 ntuple = 0; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Initialize each of the fields which we care about which are present 4828c2ecf20Sopenharmony_ci * in the Compressed Filter Tuple. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci if (tp->vlan_shift >= 0 && l2t->vlan != VLAN_NONE) 4858c2ecf20Sopenharmony_ci ntuple |= (u64)(FT_VLAN_VLD_F | l2t->vlan) << tp->vlan_shift; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (tp->port_shift >= 0) 4888c2ecf20Sopenharmony_ci ntuple |= (u64)l2t->lport << tp->port_shift; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (tp->protocol_shift >= 0) 4918c2ecf20Sopenharmony_ci ntuple |= (u64)IPPROTO_TCP << tp->protocol_shift; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (tp->vnic_shift >= 0 && (tp->ingress_config & VNIC_F)) { 4948c2ecf20Sopenharmony_ci struct port_info *pi = (struct port_info *)netdev_priv(dev); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci ntuple |= (u64)(FT_VNID_ID_VF_V(pi->vin) | 4978c2ecf20Sopenharmony_ci FT_VNID_ID_PF_V(adap->pf) | 4988c2ecf20Sopenharmony_ci FT_VNID_ID_VLD_V(pi->vivld)) << tp->vnic_shift; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci return ntuple; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgb4_select_ntuple); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/* 5068c2ecf20Sopenharmony_ci * Called when the host's neighbor layer makes a change to some entry that is 5078c2ecf20Sopenharmony_ci * loaded into the HW L2 table. 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_civoid t4_l2t_update(struct adapter *adap, struct neighbour *neigh) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci unsigned int addr_len = neigh->tbl->key_len; 5128c2ecf20Sopenharmony_ci u32 *addr = (u32 *) neigh->primary_key; 5138c2ecf20Sopenharmony_ci int hash, ifidx = neigh->dev->ifindex; 5148c2ecf20Sopenharmony_ci struct sk_buff_head *arpq = NULL; 5158c2ecf20Sopenharmony_ci struct l2t_data *d = adap->l2t; 5168c2ecf20Sopenharmony_ci struct l2t_entry *e; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci hash = addr_hash(d, addr, addr_len, ifidx); 5198c2ecf20Sopenharmony_ci read_lock_bh(&d->lock); 5208c2ecf20Sopenharmony_ci for (e = d->l2tab[hash].first; e; e = e->next) 5218c2ecf20Sopenharmony_ci if (!addreq(e, addr) && e->ifindex == ifidx) { 5228c2ecf20Sopenharmony_ci spin_lock(&e->lock); 5238c2ecf20Sopenharmony_ci if (atomic_read(&e->refcnt)) 5248c2ecf20Sopenharmony_ci goto found; 5258c2ecf20Sopenharmony_ci spin_unlock(&e->lock); 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci read_unlock_bh(&d->lock); 5298c2ecf20Sopenharmony_ci return; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci found: 5328c2ecf20Sopenharmony_ci read_unlock(&d->lock); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (neigh != e->neigh) 5358c2ecf20Sopenharmony_ci neigh_replace(e, neigh); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (e->state == L2T_STATE_RESOLVING) { 5388c2ecf20Sopenharmony_ci if (neigh->nud_state & NUD_FAILED) { 5398c2ecf20Sopenharmony_ci arpq = &e->arpq; 5408c2ecf20Sopenharmony_ci } else if ((neigh->nud_state & (NUD_CONNECTED | NUD_STALE)) && 5418c2ecf20Sopenharmony_ci !skb_queue_empty(&e->arpq)) { 5428c2ecf20Sopenharmony_ci write_l2e(adap, e, 1); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci } else { 5458c2ecf20Sopenharmony_ci e->state = neigh->nud_state & NUD_CONNECTED ? 5468c2ecf20Sopenharmony_ci L2T_STATE_VALID : L2T_STATE_STALE; 5478c2ecf20Sopenharmony_ci if (memcmp(e->dmac, neigh->ha, sizeof(e->dmac))) 5488c2ecf20Sopenharmony_ci write_l2e(adap, e, 0); 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (arpq) { 5528c2ecf20Sopenharmony_ci struct sk_buff *skb; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Called when address resolution fails for an L2T 5558c2ecf20Sopenharmony_ci * entry to handle packets on the arpq head. If a 5568c2ecf20Sopenharmony_ci * packet specifies a failure handler it is invoked, 5578c2ecf20Sopenharmony_ci * otherwise the packet is sent to the device. 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci while ((skb = __skb_dequeue(&e->arpq)) != NULL) { 5608c2ecf20Sopenharmony_ci const struct l2t_skb_cb *cb = L2T_SKB_CB(skb); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci spin_unlock(&e->lock); 5638c2ecf20Sopenharmony_ci if (cb->arp_err_handler) 5648c2ecf20Sopenharmony_ci cb->arp_err_handler(cb->handle, skb); 5658c2ecf20Sopenharmony_ci else 5668c2ecf20Sopenharmony_ci t4_ofld_send(adap, skb); 5678c2ecf20Sopenharmony_ci spin_lock(&e->lock); 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci spin_unlock_bh(&e->lock); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci/* Allocate an L2T entry for use by a switching rule. Such need to be 5748c2ecf20Sopenharmony_ci * explicitly freed and while busy they are not on any hash chain, so normal 5758c2ecf20Sopenharmony_ci * address resolution updates do not see them. 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_cistruct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan, 5788c2ecf20Sopenharmony_ci u8 port, u8 *eth_addr) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci struct l2t_data *d = adap->l2t; 5818c2ecf20Sopenharmony_ci struct l2t_entry *e; 5828c2ecf20Sopenharmony_ci int ret; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci write_lock_bh(&d->lock); 5858c2ecf20Sopenharmony_ci e = find_or_alloc_l2e(d, vlan, port, eth_addr); 5868c2ecf20Sopenharmony_ci if (e) { 5878c2ecf20Sopenharmony_ci spin_lock(&e->lock); /* avoid race with t4_l2t_free */ 5888c2ecf20Sopenharmony_ci if (!atomic_read(&e->refcnt)) { 5898c2ecf20Sopenharmony_ci e->state = L2T_STATE_SWITCHING; 5908c2ecf20Sopenharmony_ci e->vlan = vlan; 5918c2ecf20Sopenharmony_ci e->lport = port; 5928c2ecf20Sopenharmony_ci ether_addr_copy(e->dmac, eth_addr); 5938c2ecf20Sopenharmony_ci atomic_set(&e->refcnt, 1); 5948c2ecf20Sopenharmony_ci ret = write_l2e(adap, e, 0); 5958c2ecf20Sopenharmony_ci if (ret < 0) { 5968c2ecf20Sopenharmony_ci _t4_l2e_free(e); 5978c2ecf20Sopenharmony_ci spin_unlock(&e->lock); 5988c2ecf20Sopenharmony_ci write_unlock_bh(&d->lock); 5998c2ecf20Sopenharmony_ci return NULL; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci } else { 6028c2ecf20Sopenharmony_ci atomic_inc(&e->refcnt); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci spin_unlock(&e->lock); 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci write_unlock_bh(&d->lock); 6088c2ecf20Sopenharmony_ci return e; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci/** 6128c2ecf20Sopenharmony_ci * cxgb4_l2t_alloc_switching - Allocates an L2T entry for switch filters 6138c2ecf20Sopenharmony_ci * @dev: net_device pointer 6148c2ecf20Sopenharmony_ci * @vlan: VLAN Id 6158c2ecf20Sopenharmony_ci * @port: Associated port 6168c2ecf20Sopenharmony_ci * @dmac: Destination MAC address to add to L2T 6178c2ecf20Sopenharmony_ci * Returns pointer to the allocated l2t entry 6188c2ecf20Sopenharmony_ci * 6198c2ecf20Sopenharmony_ci * Allocates an L2T entry for use by switching rule of a filter 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_cistruct l2t_entry *cxgb4_l2t_alloc_switching(struct net_device *dev, u16 vlan, 6228c2ecf20Sopenharmony_ci u8 port, u8 *dmac) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct adapter *adap = netdev2adap(dev); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci return t4_l2t_alloc_switching(adap, vlan, port, dmac); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgb4_l2t_alloc_switching); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistruct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci unsigned int l2t_size; 6338c2ecf20Sopenharmony_ci int i; 6348c2ecf20Sopenharmony_ci struct l2t_data *d; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (l2t_start >= l2t_end || l2t_end >= L2T_SIZE) 6378c2ecf20Sopenharmony_ci return NULL; 6388c2ecf20Sopenharmony_ci l2t_size = l2t_end - l2t_start + 1; 6398c2ecf20Sopenharmony_ci if (l2t_size < L2T_MIN_HASH_BUCKETS) 6408c2ecf20Sopenharmony_ci return NULL; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci d = kvzalloc(struct_size(d, l2tab, l2t_size), GFP_KERNEL); 6438c2ecf20Sopenharmony_ci if (!d) 6448c2ecf20Sopenharmony_ci return NULL; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci d->l2t_start = l2t_start; 6478c2ecf20Sopenharmony_ci d->l2t_size = l2t_size; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci d->rover = d->l2tab; 6508c2ecf20Sopenharmony_ci atomic_set(&d->nfree, l2t_size); 6518c2ecf20Sopenharmony_ci rwlock_init(&d->lock); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci for (i = 0; i < d->l2t_size; ++i) { 6548c2ecf20Sopenharmony_ci d->l2tab[i].idx = i; 6558c2ecf20Sopenharmony_ci d->l2tab[i].state = L2T_STATE_UNUSED; 6568c2ecf20Sopenharmony_ci spin_lock_init(&d->l2tab[i].lock); 6578c2ecf20Sopenharmony_ci atomic_set(&d->l2tab[i].refcnt, 0); 6588c2ecf20Sopenharmony_ci skb_queue_head_init(&d->l2tab[i].arpq); 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci return d; 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic inline void *l2t_get_idx(struct seq_file *seq, loff_t pos) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci struct l2t_data *d = seq->private; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci return pos >= d->l2t_size ? NULL : &d->l2tab[pos]; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic void *l2t_seq_start(struct seq_file *seq, loff_t *pos) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci return *pos ? l2t_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic void *l2t_seq_next(struct seq_file *seq, void *v, loff_t *pos) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci v = l2t_get_idx(seq, *pos); 6788c2ecf20Sopenharmony_ci ++(*pos); 6798c2ecf20Sopenharmony_ci return v; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic void l2t_seq_stop(struct seq_file *seq, void *v) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic char l2e_state(const struct l2t_entry *e) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci switch (e->state) { 6898c2ecf20Sopenharmony_ci case L2T_STATE_VALID: return 'V'; 6908c2ecf20Sopenharmony_ci case L2T_STATE_STALE: return 'S'; 6918c2ecf20Sopenharmony_ci case L2T_STATE_SYNC_WRITE: return 'W'; 6928c2ecf20Sopenharmony_ci case L2T_STATE_RESOLVING: 6938c2ecf20Sopenharmony_ci return skb_queue_empty(&e->arpq) ? 'R' : 'A'; 6948c2ecf20Sopenharmony_ci case L2T_STATE_SWITCHING: return 'X'; 6958c2ecf20Sopenharmony_ci default: 6968c2ecf20Sopenharmony_ci return 'U'; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cibool cxgb4_check_l2t_valid(struct l2t_entry *e) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci bool valid; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci spin_lock(&e->lock); 7058c2ecf20Sopenharmony_ci valid = (e->state == L2T_STATE_VALID); 7068c2ecf20Sopenharmony_ci spin_unlock(&e->lock); 7078c2ecf20Sopenharmony_ci return valid; 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgb4_check_l2t_valid); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic int l2t_seq_show(struct seq_file *seq, void *v) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) 7148c2ecf20Sopenharmony_ci seq_puts(seq, " Idx IP address " 7158c2ecf20Sopenharmony_ci "Ethernet address VLAN/P LP State Users Port\n"); 7168c2ecf20Sopenharmony_ci else { 7178c2ecf20Sopenharmony_ci char ip[60]; 7188c2ecf20Sopenharmony_ci struct l2t_data *d = seq->private; 7198c2ecf20Sopenharmony_ci struct l2t_entry *e = v; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci spin_lock_bh(&e->lock); 7228c2ecf20Sopenharmony_ci if (e->state == L2T_STATE_SWITCHING) 7238c2ecf20Sopenharmony_ci ip[0] = '\0'; 7248c2ecf20Sopenharmony_ci else 7258c2ecf20Sopenharmony_ci sprintf(ip, e->v6 ? "%pI6c" : "%pI4", e->addr); 7268c2ecf20Sopenharmony_ci seq_printf(seq, "%4u %-25s %17pM %4d %u %2u %c %5u %s\n", 7278c2ecf20Sopenharmony_ci e->idx + d->l2t_start, ip, e->dmac, 7288c2ecf20Sopenharmony_ci e->vlan & VLAN_VID_MASK, vlan_prio(e), e->lport, 7298c2ecf20Sopenharmony_ci l2e_state(e), atomic_read(&e->refcnt), 7308c2ecf20Sopenharmony_ci e->neigh ? e->neigh->dev->name : ""); 7318c2ecf20Sopenharmony_ci spin_unlock_bh(&e->lock); 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci return 0; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic const struct seq_operations l2t_seq_ops = { 7378c2ecf20Sopenharmony_ci .start = l2t_seq_start, 7388c2ecf20Sopenharmony_ci .next = l2t_seq_next, 7398c2ecf20Sopenharmony_ci .stop = l2t_seq_stop, 7408c2ecf20Sopenharmony_ci .show = l2t_seq_show 7418c2ecf20Sopenharmony_ci}; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic int l2t_seq_open(struct inode *inode, struct file *file) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci int rc = seq_open(file, &l2t_seq_ops); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (!rc) { 7488c2ecf20Sopenharmony_ci struct adapter *adap = inode->i_private; 7498c2ecf20Sopenharmony_ci struct seq_file *seq = file->private_data; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci seq->private = adap->l2t; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci return rc; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ciconst struct file_operations t4_l2t_fops = { 7578c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7588c2ecf20Sopenharmony_ci .open = l2t_seq_open, 7598c2ecf20Sopenharmony_ci .read = seq_read, 7608c2ecf20Sopenharmony_ci .llseek = seq_lseek, 7618c2ecf20Sopenharmony_ci .release = seq_release, 7628c2ecf20Sopenharmony_ci}; 763