162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is part of the Chelsio T4 Ethernet driver for Linux. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This software is available to you under a choice of one of two 762306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 862306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 962306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 1062306a36Sopenharmony_ci * OpenIB.org BSD license below: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1362306a36Sopenharmony_ci * without modification, are permitted provided that the following 1462306a36Sopenharmony_ci * conditions are met: 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1762306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1862306a36Sopenharmony_ci * disclaimer. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2162306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2262306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2362306a36Sopenharmony_ci * provided with the distribution. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2662306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2762306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2862306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2962306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 3062306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3162306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3262306a36Sopenharmony_ci * SOFTWARE. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/skbuff.h> 3662306a36Sopenharmony_ci#include <linux/netdevice.h> 3762306a36Sopenharmony_ci#include <linux/if.h> 3862306a36Sopenharmony_ci#include <linux/if_vlan.h> 3962306a36Sopenharmony_ci#include <linux/jhash.h> 4062306a36Sopenharmony_ci#include <linux/module.h> 4162306a36Sopenharmony_ci#include <linux/debugfs.h> 4262306a36Sopenharmony_ci#include <linux/seq_file.h> 4362306a36Sopenharmony_ci#include <net/neighbour.h> 4462306a36Sopenharmony_ci#include "cxgb4.h" 4562306a36Sopenharmony_ci#include "l2t.h" 4662306a36Sopenharmony_ci#include "t4_msg.h" 4762306a36Sopenharmony_ci#include "t4fw_api.h" 4862306a36Sopenharmony_ci#include "t4_regs.h" 4962306a36Sopenharmony_ci#include "t4_values.h" 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* identifies sync vs async L2T_WRITE_REQs */ 5262306a36Sopenharmony_ci#define SYNC_WR_S 12 5362306a36Sopenharmony_ci#define SYNC_WR_V(x) ((x) << SYNC_WR_S) 5462306a36Sopenharmony_ci#define SYNC_WR_F SYNC_WR_V(1) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct l2t_data { 5762306a36Sopenharmony_ci unsigned int l2t_start; /* start index of our piece of the L2T */ 5862306a36Sopenharmony_ci unsigned int l2t_size; /* number of entries in l2tab */ 5962306a36Sopenharmony_ci rwlock_t lock; 6062306a36Sopenharmony_ci atomic_t nfree; /* number of free entries */ 6162306a36Sopenharmony_ci struct l2t_entry *rover; /* starting point for next allocation */ 6262306a36Sopenharmony_ci struct l2t_entry l2tab[]; /* MUST BE LAST */ 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic inline unsigned int vlan_prio(const struct l2t_entry *e) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci return e->vlan >> VLAN_PRIO_SHIFT; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci if (atomic_add_return(1, &e->refcnt) == 1) /* 0 -> 1 transition */ 7362306a36Sopenharmony_ci atomic_dec(&d->nfree); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * To avoid having to check address families we do not allow v4 and v6 7862306a36Sopenharmony_ci * neighbors to be on the same hash chain. We keep v4 entries in the first 7962306a36Sopenharmony_ci * half of available hash buckets and v6 in the second. We need at least two 8062306a36Sopenharmony_ci * entries in our L2T for this scheme to work. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cienum { 8362306a36Sopenharmony_ci L2T_MIN_HASH_BUCKETS = 2, 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic inline unsigned int arp_hash(struct l2t_data *d, const u32 *key, 8762306a36Sopenharmony_ci int ifindex) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci unsigned int l2t_size_half = d->l2t_size / 2; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return jhash_2words(*key, ifindex, 0) % l2t_size_half; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic inline unsigned int ipv6_hash(struct l2t_data *d, const u32 *key, 9562306a36Sopenharmony_ci int ifindex) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci unsigned int l2t_size_half = d->l2t_size / 2; 9862306a36Sopenharmony_ci u32 xor = key[0] ^ key[1] ^ key[2] ^ key[3]; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return (l2t_size_half + 10162306a36Sopenharmony_ci (jhash_2words(xor, ifindex, 0) % l2t_size_half)); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic unsigned int addr_hash(struct l2t_data *d, const u32 *addr, 10562306a36Sopenharmony_ci int addr_len, int ifindex) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci return addr_len == 4 ? arp_hash(d, addr, ifindex) : 10862306a36Sopenharmony_ci ipv6_hash(d, addr, ifindex); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* 11262306a36Sopenharmony_ci * Checks if an L2T entry is for the given IP/IPv6 address. It does not check 11362306a36Sopenharmony_ci * whether the L2T entry and the address are of the same address family. 11462306a36Sopenharmony_ci * Callers ensure an address is only checked against L2T entries of the same 11562306a36Sopenharmony_ci * family, something made trivial by the separation of IP and IPv6 hash chains 11662306a36Sopenharmony_ci * mentioned above. Returns 0 if there's a match, 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic int addreq(const struct l2t_entry *e, const u32 *addr) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci if (e->v6) 12162306a36Sopenharmony_ci return (e->addr[0] ^ addr[0]) | (e->addr[1] ^ addr[1]) | 12262306a36Sopenharmony_ci (e->addr[2] ^ addr[2]) | (e->addr[3] ^ addr[3]); 12362306a36Sopenharmony_ci return e->addr[0] ^ addr[0]; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void neigh_replace(struct l2t_entry *e, struct neighbour *n) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci neigh_hold(n); 12962306a36Sopenharmony_ci if (e->neigh) 13062306a36Sopenharmony_ci neigh_release(e->neigh); 13162306a36Sopenharmony_ci e->neigh = n; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* 13562306a36Sopenharmony_ci * Write an L2T entry. Must be called with the entry locked. 13662306a36Sopenharmony_ci * The write may be synchronous or asynchronous. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct l2t_data *d = adap->l2t; 14162306a36Sopenharmony_ci unsigned int l2t_idx = e->idx + d->l2t_start; 14262306a36Sopenharmony_ci struct sk_buff *skb; 14362306a36Sopenharmony_ci struct cpl_l2t_write_req *req; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci skb = alloc_skb(sizeof(*req), GFP_ATOMIC); 14662306a36Sopenharmony_ci if (!skb) 14762306a36Sopenharmony_ci return -ENOMEM; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci req = __skb_put(skb, sizeof(*req)); 15062306a36Sopenharmony_ci INIT_TP_WR(req, 0); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, 15362306a36Sopenharmony_ci l2t_idx | (sync ? SYNC_WR_F : 0) | 15462306a36Sopenharmony_ci TID_QID_V(adap->sge.fw_evtq.abs_id))); 15562306a36Sopenharmony_ci req->params = htons(L2T_W_PORT_V(e->lport) | L2T_W_NOREPLY_V(!sync)); 15662306a36Sopenharmony_ci req->l2t_idx = htons(l2t_idx); 15762306a36Sopenharmony_ci req->vlan = htons(e->vlan); 15862306a36Sopenharmony_ci if (e->neigh && !(e->neigh->dev->flags & IFF_LOOPBACK)) 15962306a36Sopenharmony_ci memcpy(e->dmac, e->neigh->ha, sizeof(e->dmac)); 16062306a36Sopenharmony_ci memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac)); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci t4_mgmt_tx(adap, skb); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (sync && e->state != L2T_STATE_SWITCHING) 16562306a36Sopenharmony_ci e->state = L2T_STATE_SYNC_WRITE; 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * Send packets waiting in an L2T entry's ARP queue. Must be called with the 17162306a36Sopenharmony_ci * entry locked. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic void send_pending(struct adapter *adap, struct l2t_entry *e) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct sk_buff *skb; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci while ((skb = __skb_dequeue(&e->arpq)) != NULL) 17862306a36Sopenharmony_ci t4_ofld_send(adap, skb); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* 18262306a36Sopenharmony_ci * Process a CPL_L2T_WRITE_RPL. Wake up the ARP queue if it completes a 18362306a36Sopenharmony_ci * synchronous L2T_WRITE. Note that the TID in the reply is really the L2T 18462306a36Sopenharmony_ci * index it refers to. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_civoid do_l2t_write_rpl(struct adapter *adap, const struct cpl_l2t_write_rpl *rpl) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct l2t_data *d = adap->l2t; 18962306a36Sopenharmony_ci unsigned int tid = GET_TID(rpl); 19062306a36Sopenharmony_ci unsigned int l2t_idx = tid % L2T_SIZE; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (unlikely(rpl->status != CPL_ERR_NONE)) { 19362306a36Sopenharmony_ci dev_err(adap->pdev_dev, 19462306a36Sopenharmony_ci "Unexpected L2T_WRITE_RPL status %u for entry %u\n", 19562306a36Sopenharmony_ci rpl->status, l2t_idx); 19662306a36Sopenharmony_ci return; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (tid & SYNC_WR_F) { 20062306a36Sopenharmony_ci struct l2t_entry *e = &d->l2tab[l2t_idx - d->l2t_start]; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci spin_lock(&e->lock); 20362306a36Sopenharmony_ci if (e->state != L2T_STATE_SWITCHING) { 20462306a36Sopenharmony_ci send_pending(adap, e); 20562306a36Sopenharmony_ci e->state = (e->neigh->nud_state & NUD_STALE) ? 20662306a36Sopenharmony_ci L2T_STATE_STALE : L2T_STATE_VALID; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci spin_unlock(&e->lock); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/* 21362306a36Sopenharmony_ci * Add a packet to an L2T entry's queue of packets awaiting resolution. 21462306a36Sopenharmony_ci * Must be called with the entry's lock held. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic inline void arpq_enqueue(struct l2t_entry *e, struct sk_buff *skb) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci __skb_queue_tail(&e->arpq, skb); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ciint cxgb4_l2t_send(struct net_device *dev, struct sk_buff *skb, 22262306a36Sopenharmony_ci struct l2t_entry *e) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct adapter *adap = netdev2adap(dev); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ciagain: 22762306a36Sopenharmony_ci switch (e->state) { 22862306a36Sopenharmony_ci case L2T_STATE_STALE: /* entry is stale, kick off revalidation */ 22962306a36Sopenharmony_ci neigh_event_send(e->neigh, NULL); 23062306a36Sopenharmony_ci spin_lock_bh(&e->lock); 23162306a36Sopenharmony_ci if (e->state == L2T_STATE_STALE) 23262306a36Sopenharmony_ci e->state = L2T_STATE_VALID; 23362306a36Sopenharmony_ci spin_unlock_bh(&e->lock); 23462306a36Sopenharmony_ci fallthrough; 23562306a36Sopenharmony_ci case L2T_STATE_VALID: /* fast-path, send the packet on */ 23662306a36Sopenharmony_ci return t4_ofld_send(adap, skb); 23762306a36Sopenharmony_ci case L2T_STATE_RESOLVING: 23862306a36Sopenharmony_ci case L2T_STATE_SYNC_WRITE: 23962306a36Sopenharmony_ci spin_lock_bh(&e->lock); 24062306a36Sopenharmony_ci if (e->state != L2T_STATE_SYNC_WRITE && 24162306a36Sopenharmony_ci e->state != L2T_STATE_RESOLVING) { 24262306a36Sopenharmony_ci spin_unlock_bh(&e->lock); 24362306a36Sopenharmony_ci goto again; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci arpq_enqueue(e, skb); 24662306a36Sopenharmony_ci spin_unlock_bh(&e->lock); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (e->state == L2T_STATE_RESOLVING && 24962306a36Sopenharmony_ci !neigh_event_send(e->neigh, NULL)) { 25062306a36Sopenharmony_ci spin_lock_bh(&e->lock); 25162306a36Sopenharmony_ci if (e->state == L2T_STATE_RESOLVING && 25262306a36Sopenharmony_ci !skb_queue_empty(&e->arpq)) 25362306a36Sopenharmony_ci write_l2e(adap, e, 1); 25462306a36Sopenharmony_ci spin_unlock_bh(&e->lock); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ciEXPORT_SYMBOL(cxgb4_l2t_send); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/* 26262306a36Sopenharmony_ci * Allocate a free L2T entry. Must be called with l2t_data.lock held. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_cistatic struct l2t_entry *alloc_l2e(struct l2t_data *d) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct l2t_entry *end, *e, **p; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (!atomic_read(&d->nfree)) 26962306a36Sopenharmony_ci return NULL; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* there's definitely a free entry */ 27262306a36Sopenharmony_ci for (e = d->rover, end = &d->l2tab[d->l2t_size]; e != end; ++e) 27362306a36Sopenharmony_ci if (atomic_read(&e->refcnt) == 0) 27462306a36Sopenharmony_ci goto found; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci for (e = d->l2tab; atomic_read(&e->refcnt); ++e) 27762306a36Sopenharmony_ci ; 27862306a36Sopenharmony_cifound: 27962306a36Sopenharmony_ci d->rover = e + 1; 28062306a36Sopenharmony_ci atomic_dec(&d->nfree); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * The entry we found may be an inactive entry that is 28462306a36Sopenharmony_ci * presently in the hash table. We need to remove it. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci if (e->state < L2T_STATE_SWITCHING) 28762306a36Sopenharmony_ci for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next) 28862306a36Sopenharmony_ci if (*p == e) { 28962306a36Sopenharmony_ci *p = e->next; 29062306a36Sopenharmony_ci e->next = NULL; 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci e->state = L2T_STATE_UNUSED; 29562306a36Sopenharmony_ci return e; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic struct l2t_entry *find_or_alloc_l2e(struct l2t_data *d, u16 vlan, 29962306a36Sopenharmony_ci u8 port, u8 *dmac) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct l2t_entry *end, *e, **p; 30262306a36Sopenharmony_ci struct l2t_entry *first_free = NULL; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci for (e = &d->l2tab[0], end = &d->l2tab[d->l2t_size]; e != end; ++e) { 30562306a36Sopenharmony_ci if (atomic_read(&e->refcnt) == 0) { 30662306a36Sopenharmony_ci if (!first_free) 30762306a36Sopenharmony_ci first_free = e; 30862306a36Sopenharmony_ci } else { 30962306a36Sopenharmony_ci if (e->state == L2T_STATE_SWITCHING) { 31062306a36Sopenharmony_ci if (ether_addr_equal(e->dmac, dmac) && 31162306a36Sopenharmony_ci (e->vlan == vlan) && (e->lport == port)) 31262306a36Sopenharmony_ci goto exists; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (first_free) { 31862306a36Sopenharmony_ci e = first_free; 31962306a36Sopenharmony_ci goto found; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return NULL; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cifound: 32562306a36Sopenharmony_ci /* The entry we found may be an inactive entry that is 32662306a36Sopenharmony_ci * presently in the hash table. We need to remove it. 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_ci if (e->state < L2T_STATE_SWITCHING) 32962306a36Sopenharmony_ci for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next) 33062306a36Sopenharmony_ci if (*p == e) { 33162306a36Sopenharmony_ci *p = e->next; 33262306a36Sopenharmony_ci e->next = NULL; 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci e->state = L2T_STATE_UNUSED; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ciexists: 33862306a36Sopenharmony_ci return e; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/* Called when an L2T entry has no more users. The entry is left in the hash 34262306a36Sopenharmony_ci * table since it is likely to be reused but we also bump nfree to indicate 34362306a36Sopenharmony_ci * that the entry can be reallocated for a different neighbor. We also drop 34462306a36Sopenharmony_ci * the existing neighbor reference in case the neighbor is going away and is 34562306a36Sopenharmony_ci * waiting on our reference. 34662306a36Sopenharmony_ci * 34762306a36Sopenharmony_ci * Because entries can be reallocated to other neighbors once their ref count 34862306a36Sopenharmony_ci * drops to 0 we need to take the entry's lock to avoid races with a new 34962306a36Sopenharmony_ci * incarnation. 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_cistatic void _t4_l2e_free(struct l2t_entry *e) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct l2t_data *d; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */ 35662306a36Sopenharmony_ci if (e->neigh) { 35762306a36Sopenharmony_ci neigh_release(e->neigh); 35862306a36Sopenharmony_ci e->neigh = NULL; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci __skb_queue_purge(&e->arpq); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci d = container_of(e, struct l2t_data, l2tab[e->idx]); 36462306a36Sopenharmony_ci atomic_inc(&d->nfree); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* Locked version of _t4_l2e_free */ 36862306a36Sopenharmony_cistatic void t4_l2e_free(struct l2t_entry *e) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct l2t_data *d; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci spin_lock_bh(&e->lock); 37362306a36Sopenharmony_ci if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */ 37462306a36Sopenharmony_ci if (e->neigh) { 37562306a36Sopenharmony_ci neigh_release(e->neigh); 37662306a36Sopenharmony_ci e->neigh = NULL; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci __skb_queue_purge(&e->arpq); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci spin_unlock_bh(&e->lock); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci d = container_of(e, struct l2t_data, l2tab[e->idx]); 38362306a36Sopenharmony_ci atomic_inc(&d->nfree); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_civoid cxgb4_l2t_release(struct l2t_entry *e) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci if (atomic_dec_and_test(&e->refcnt)) 38962306a36Sopenharmony_ci t4_l2e_free(e); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ciEXPORT_SYMBOL(cxgb4_l2t_release); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci/* 39462306a36Sopenharmony_ci * Update an L2T entry that was previously used for the same next hop as neigh. 39562306a36Sopenharmony_ci * Must be called with softirqs disabled. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_cistatic void reuse_entry(struct l2t_entry *e, struct neighbour *neigh) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci unsigned int nud_state; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci spin_lock(&e->lock); /* avoid race with t4_l2t_free */ 40262306a36Sopenharmony_ci if (neigh != e->neigh) 40362306a36Sopenharmony_ci neigh_replace(e, neigh); 40462306a36Sopenharmony_ci nud_state = neigh->nud_state; 40562306a36Sopenharmony_ci if (memcmp(e->dmac, neigh->ha, sizeof(e->dmac)) || 40662306a36Sopenharmony_ci !(nud_state & NUD_VALID)) 40762306a36Sopenharmony_ci e->state = L2T_STATE_RESOLVING; 40862306a36Sopenharmony_ci else if (nud_state & NUD_CONNECTED) 40962306a36Sopenharmony_ci e->state = L2T_STATE_VALID; 41062306a36Sopenharmony_ci else 41162306a36Sopenharmony_ci e->state = L2T_STATE_STALE; 41262306a36Sopenharmony_ci spin_unlock(&e->lock); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistruct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh, 41662306a36Sopenharmony_ci const struct net_device *physdev, 41762306a36Sopenharmony_ci unsigned int priority) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci u8 lport; 42062306a36Sopenharmony_ci u16 vlan; 42162306a36Sopenharmony_ci struct l2t_entry *e; 42262306a36Sopenharmony_ci unsigned int addr_len = neigh->tbl->key_len; 42362306a36Sopenharmony_ci u32 *addr = (u32 *)neigh->primary_key; 42462306a36Sopenharmony_ci int ifidx = neigh->dev->ifindex; 42562306a36Sopenharmony_ci int hash = addr_hash(d, addr, addr_len, ifidx); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (neigh->dev->flags & IFF_LOOPBACK) 42862306a36Sopenharmony_ci lport = netdev2pinfo(physdev)->tx_chan + 4; 42962306a36Sopenharmony_ci else 43062306a36Sopenharmony_ci lport = netdev2pinfo(physdev)->lport; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (is_vlan_dev(neigh->dev)) { 43362306a36Sopenharmony_ci vlan = vlan_dev_vlan_id(neigh->dev); 43462306a36Sopenharmony_ci vlan |= vlan_dev_get_egress_qos_mask(neigh->dev, priority); 43562306a36Sopenharmony_ci } else { 43662306a36Sopenharmony_ci vlan = VLAN_NONE; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci write_lock_bh(&d->lock); 44062306a36Sopenharmony_ci for (e = d->l2tab[hash].first; e; e = e->next) 44162306a36Sopenharmony_ci if (!addreq(e, addr) && e->ifindex == ifidx && 44262306a36Sopenharmony_ci e->vlan == vlan && e->lport == lport) { 44362306a36Sopenharmony_ci l2t_hold(d, e); 44462306a36Sopenharmony_ci if (atomic_read(&e->refcnt) == 1) 44562306a36Sopenharmony_ci reuse_entry(e, neigh); 44662306a36Sopenharmony_ci goto done; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* Need to allocate a new entry */ 45062306a36Sopenharmony_ci e = alloc_l2e(d); 45162306a36Sopenharmony_ci if (e) { 45262306a36Sopenharmony_ci spin_lock(&e->lock); /* avoid race with t4_l2t_free */ 45362306a36Sopenharmony_ci e->state = L2T_STATE_RESOLVING; 45462306a36Sopenharmony_ci if (neigh->dev->flags & IFF_LOOPBACK) 45562306a36Sopenharmony_ci memcpy(e->dmac, physdev->dev_addr, sizeof(e->dmac)); 45662306a36Sopenharmony_ci memcpy(e->addr, addr, addr_len); 45762306a36Sopenharmony_ci e->ifindex = ifidx; 45862306a36Sopenharmony_ci e->hash = hash; 45962306a36Sopenharmony_ci e->lport = lport; 46062306a36Sopenharmony_ci e->v6 = addr_len == 16; 46162306a36Sopenharmony_ci atomic_set(&e->refcnt, 1); 46262306a36Sopenharmony_ci neigh_replace(e, neigh); 46362306a36Sopenharmony_ci e->vlan = vlan; 46462306a36Sopenharmony_ci e->next = d->l2tab[hash].first; 46562306a36Sopenharmony_ci d->l2tab[hash].first = e; 46662306a36Sopenharmony_ci spin_unlock(&e->lock); 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_cidone: 46962306a36Sopenharmony_ci write_unlock_bh(&d->lock); 47062306a36Sopenharmony_ci return e; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ciEXPORT_SYMBOL(cxgb4_l2t_get); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ciu64 cxgb4_select_ntuple(struct net_device *dev, 47562306a36Sopenharmony_ci const struct l2t_entry *l2t) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct adapter *adap = netdev2adap(dev); 47862306a36Sopenharmony_ci struct tp_params *tp = &adap->params.tp; 47962306a36Sopenharmony_ci u64 ntuple = 0; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* Initialize each of the fields which we care about which are present 48262306a36Sopenharmony_ci * in the Compressed Filter Tuple. 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci if (tp->vlan_shift >= 0 && l2t->vlan != VLAN_NONE) 48562306a36Sopenharmony_ci ntuple |= (u64)(FT_VLAN_VLD_F | l2t->vlan) << tp->vlan_shift; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (tp->port_shift >= 0) 48862306a36Sopenharmony_ci ntuple |= (u64)l2t->lport << tp->port_shift; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (tp->protocol_shift >= 0) 49162306a36Sopenharmony_ci ntuple |= (u64)IPPROTO_TCP << tp->protocol_shift; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (tp->vnic_shift >= 0 && (tp->ingress_config & VNIC_F)) { 49462306a36Sopenharmony_ci struct port_info *pi = (struct port_info *)netdev_priv(dev); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ntuple |= (u64)(FT_VNID_ID_VF_V(pi->vin) | 49762306a36Sopenharmony_ci FT_VNID_ID_PF_V(adap->pf) | 49862306a36Sopenharmony_ci FT_VNID_ID_VLD_V(pi->vivld)) << tp->vnic_shift; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return ntuple; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ciEXPORT_SYMBOL(cxgb4_select_ntuple); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci/* 50662306a36Sopenharmony_ci * Called when the host's neighbor layer makes a change to some entry that is 50762306a36Sopenharmony_ci * loaded into the HW L2 table. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_civoid t4_l2t_update(struct adapter *adap, struct neighbour *neigh) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci unsigned int addr_len = neigh->tbl->key_len; 51262306a36Sopenharmony_ci u32 *addr = (u32 *) neigh->primary_key; 51362306a36Sopenharmony_ci int hash, ifidx = neigh->dev->ifindex; 51462306a36Sopenharmony_ci struct sk_buff_head *arpq = NULL; 51562306a36Sopenharmony_ci struct l2t_data *d = adap->l2t; 51662306a36Sopenharmony_ci struct l2t_entry *e; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci hash = addr_hash(d, addr, addr_len, ifidx); 51962306a36Sopenharmony_ci read_lock_bh(&d->lock); 52062306a36Sopenharmony_ci for (e = d->l2tab[hash].first; e; e = e->next) 52162306a36Sopenharmony_ci if (!addreq(e, addr) && e->ifindex == ifidx) { 52262306a36Sopenharmony_ci spin_lock(&e->lock); 52362306a36Sopenharmony_ci if (atomic_read(&e->refcnt)) 52462306a36Sopenharmony_ci goto found; 52562306a36Sopenharmony_ci spin_unlock(&e->lock); 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci read_unlock_bh(&d->lock); 52962306a36Sopenharmony_ci return; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci found: 53262306a36Sopenharmony_ci read_unlock(&d->lock); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (neigh != e->neigh) 53562306a36Sopenharmony_ci neigh_replace(e, neigh); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (e->state == L2T_STATE_RESOLVING) { 53862306a36Sopenharmony_ci if (neigh->nud_state & NUD_FAILED) { 53962306a36Sopenharmony_ci arpq = &e->arpq; 54062306a36Sopenharmony_ci } else if ((neigh->nud_state & (NUD_CONNECTED | NUD_STALE)) && 54162306a36Sopenharmony_ci !skb_queue_empty(&e->arpq)) { 54262306a36Sopenharmony_ci write_l2e(adap, e, 1); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci } else { 54562306a36Sopenharmony_ci e->state = neigh->nud_state & NUD_CONNECTED ? 54662306a36Sopenharmony_ci L2T_STATE_VALID : L2T_STATE_STALE; 54762306a36Sopenharmony_ci if (memcmp(e->dmac, neigh->ha, sizeof(e->dmac))) 54862306a36Sopenharmony_ci write_l2e(adap, e, 0); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (arpq) { 55262306a36Sopenharmony_ci struct sk_buff *skb; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Called when address resolution fails for an L2T 55562306a36Sopenharmony_ci * entry to handle packets on the arpq head. If a 55662306a36Sopenharmony_ci * packet specifies a failure handler it is invoked, 55762306a36Sopenharmony_ci * otherwise the packet is sent to the device. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci while ((skb = __skb_dequeue(&e->arpq)) != NULL) { 56062306a36Sopenharmony_ci const struct l2t_skb_cb *cb = L2T_SKB_CB(skb); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci spin_unlock(&e->lock); 56362306a36Sopenharmony_ci if (cb->arp_err_handler) 56462306a36Sopenharmony_ci cb->arp_err_handler(cb->handle, skb); 56562306a36Sopenharmony_ci else 56662306a36Sopenharmony_ci t4_ofld_send(adap, skb); 56762306a36Sopenharmony_ci spin_lock(&e->lock); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci spin_unlock_bh(&e->lock); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/* Allocate an L2T entry for use by a switching rule. Such need to be 57462306a36Sopenharmony_ci * explicitly freed and while busy they are not on any hash chain, so normal 57562306a36Sopenharmony_ci * address resolution updates do not see them. 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_cistruct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan, 57862306a36Sopenharmony_ci u8 port, u8 *eth_addr) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct l2t_data *d = adap->l2t; 58162306a36Sopenharmony_ci struct l2t_entry *e; 58262306a36Sopenharmony_ci int ret; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci write_lock_bh(&d->lock); 58562306a36Sopenharmony_ci e = find_or_alloc_l2e(d, vlan, port, eth_addr); 58662306a36Sopenharmony_ci if (e) { 58762306a36Sopenharmony_ci spin_lock(&e->lock); /* avoid race with t4_l2t_free */ 58862306a36Sopenharmony_ci if (!atomic_read(&e->refcnt)) { 58962306a36Sopenharmony_ci e->state = L2T_STATE_SWITCHING; 59062306a36Sopenharmony_ci e->vlan = vlan; 59162306a36Sopenharmony_ci e->lport = port; 59262306a36Sopenharmony_ci ether_addr_copy(e->dmac, eth_addr); 59362306a36Sopenharmony_ci atomic_set(&e->refcnt, 1); 59462306a36Sopenharmony_ci ret = write_l2e(adap, e, 0); 59562306a36Sopenharmony_ci if (ret < 0) { 59662306a36Sopenharmony_ci _t4_l2e_free(e); 59762306a36Sopenharmony_ci spin_unlock(&e->lock); 59862306a36Sopenharmony_ci write_unlock_bh(&d->lock); 59962306a36Sopenharmony_ci return NULL; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci } else { 60262306a36Sopenharmony_ci atomic_inc(&e->refcnt); 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci spin_unlock(&e->lock); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci write_unlock_bh(&d->lock); 60862306a36Sopenharmony_ci return e; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci/** 61262306a36Sopenharmony_ci * cxgb4_l2t_alloc_switching - Allocates an L2T entry for switch filters 61362306a36Sopenharmony_ci * @dev: net_device pointer 61462306a36Sopenharmony_ci * @vlan: VLAN Id 61562306a36Sopenharmony_ci * @port: Associated port 61662306a36Sopenharmony_ci * @dmac: Destination MAC address to add to L2T 61762306a36Sopenharmony_ci * Returns pointer to the allocated l2t entry 61862306a36Sopenharmony_ci * 61962306a36Sopenharmony_ci * Allocates an L2T entry for use by switching rule of a filter 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_cistruct l2t_entry *cxgb4_l2t_alloc_switching(struct net_device *dev, u16 vlan, 62262306a36Sopenharmony_ci u8 port, u8 *dmac) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci struct adapter *adap = netdev2adap(dev); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return t4_l2t_alloc_switching(adap, vlan, port, dmac); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ciEXPORT_SYMBOL(cxgb4_l2t_alloc_switching); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistruct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci unsigned int l2t_size; 63362306a36Sopenharmony_ci int i; 63462306a36Sopenharmony_ci struct l2t_data *d; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (l2t_start >= l2t_end || l2t_end >= L2T_SIZE) 63762306a36Sopenharmony_ci return NULL; 63862306a36Sopenharmony_ci l2t_size = l2t_end - l2t_start + 1; 63962306a36Sopenharmony_ci if (l2t_size < L2T_MIN_HASH_BUCKETS) 64062306a36Sopenharmony_ci return NULL; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci d = kvzalloc(struct_size(d, l2tab, l2t_size), GFP_KERNEL); 64362306a36Sopenharmony_ci if (!d) 64462306a36Sopenharmony_ci return NULL; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci d->l2t_start = l2t_start; 64762306a36Sopenharmony_ci d->l2t_size = l2t_size; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci d->rover = d->l2tab; 65062306a36Sopenharmony_ci atomic_set(&d->nfree, l2t_size); 65162306a36Sopenharmony_ci rwlock_init(&d->lock); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci for (i = 0; i < d->l2t_size; ++i) { 65462306a36Sopenharmony_ci d->l2tab[i].idx = i; 65562306a36Sopenharmony_ci d->l2tab[i].state = L2T_STATE_UNUSED; 65662306a36Sopenharmony_ci spin_lock_init(&d->l2tab[i].lock); 65762306a36Sopenharmony_ci atomic_set(&d->l2tab[i].refcnt, 0); 65862306a36Sopenharmony_ci skb_queue_head_init(&d->l2tab[i].arpq); 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci return d; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic inline void *l2t_get_idx(struct seq_file *seq, loff_t pos) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct l2t_data *d = seq->private; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci return pos >= d->l2t_size ? NULL : &d->l2tab[pos]; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic void *l2t_seq_start(struct seq_file *seq, loff_t *pos) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci return *pos ? l2t_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic void *l2t_seq_next(struct seq_file *seq, void *v, loff_t *pos) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci v = l2t_get_idx(seq, *pos); 67862306a36Sopenharmony_ci ++(*pos); 67962306a36Sopenharmony_ci return v; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic void l2t_seq_stop(struct seq_file *seq, void *v) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic char l2e_state(const struct l2t_entry *e) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci switch (e->state) { 68962306a36Sopenharmony_ci case L2T_STATE_VALID: return 'V'; 69062306a36Sopenharmony_ci case L2T_STATE_STALE: return 'S'; 69162306a36Sopenharmony_ci case L2T_STATE_SYNC_WRITE: return 'W'; 69262306a36Sopenharmony_ci case L2T_STATE_RESOLVING: 69362306a36Sopenharmony_ci return skb_queue_empty(&e->arpq) ? 'R' : 'A'; 69462306a36Sopenharmony_ci case L2T_STATE_SWITCHING: return 'X'; 69562306a36Sopenharmony_ci default: 69662306a36Sopenharmony_ci return 'U'; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cibool cxgb4_check_l2t_valid(struct l2t_entry *e) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci bool valid; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci spin_lock(&e->lock); 70562306a36Sopenharmony_ci valid = (e->state == L2T_STATE_VALID); 70662306a36Sopenharmony_ci spin_unlock(&e->lock); 70762306a36Sopenharmony_ci return valid; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ciEXPORT_SYMBOL(cxgb4_check_l2t_valid); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic int l2t_seq_show(struct seq_file *seq, void *v) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 71462306a36Sopenharmony_ci seq_puts(seq, " Idx IP address " 71562306a36Sopenharmony_ci "Ethernet address VLAN/P LP State Users Port\n"); 71662306a36Sopenharmony_ci else { 71762306a36Sopenharmony_ci char ip[60]; 71862306a36Sopenharmony_ci struct l2t_data *d = seq->private; 71962306a36Sopenharmony_ci struct l2t_entry *e = v; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci spin_lock_bh(&e->lock); 72262306a36Sopenharmony_ci if (e->state == L2T_STATE_SWITCHING) 72362306a36Sopenharmony_ci ip[0] = '\0'; 72462306a36Sopenharmony_ci else 72562306a36Sopenharmony_ci sprintf(ip, e->v6 ? "%pI6c" : "%pI4", e->addr); 72662306a36Sopenharmony_ci seq_printf(seq, "%4u %-25s %17pM %4d %u %2u %c %5u %s\n", 72762306a36Sopenharmony_ci e->idx + d->l2t_start, ip, e->dmac, 72862306a36Sopenharmony_ci e->vlan & VLAN_VID_MASK, vlan_prio(e), e->lport, 72962306a36Sopenharmony_ci l2e_state(e), atomic_read(&e->refcnt), 73062306a36Sopenharmony_ci e->neigh ? e->neigh->dev->name : ""); 73162306a36Sopenharmony_ci spin_unlock_bh(&e->lock); 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic const struct seq_operations l2t_seq_ops = { 73762306a36Sopenharmony_ci .start = l2t_seq_start, 73862306a36Sopenharmony_ci .next = l2t_seq_next, 73962306a36Sopenharmony_ci .stop = l2t_seq_stop, 74062306a36Sopenharmony_ci .show = l2t_seq_show 74162306a36Sopenharmony_ci}; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic int l2t_seq_open(struct inode *inode, struct file *file) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci int rc = seq_open(file, &l2t_seq_ops); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (!rc) { 74862306a36Sopenharmony_ci struct adapter *adap = inode->i_private; 74962306a36Sopenharmony_ci struct seq_file *seq = file->private_data; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci seq->private = adap->l2t; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci return rc; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ciconst struct file_operations t4_l2t_fops = { 75762306a36Sopenharmony_ci .owner = THIS_MODULE, 75862306a36Sopenharmony_ci .open = l2t_seq_open, 75962306a36Sopenharmony_ci .read = seq_read, 76062306a36Sopenharmony_ci .llseek = seq_lseek, 76162306a36Sopenharmony_ci .release = seq_release, 76262306a36Sopenharmony_ci}; 763