1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2018 Chelsio Communications. 3 * All rights reserved. 4 */ 5 #include "common.h" 6 #include "l2t.h" 7 8 /** 9 * cxgbe_l2t_release - Release associated L2T entry 10 * @e: L2T entry to release 11 * 12 * Releases ref count and frees up an L2T entry from L2T table 13 */ 14 void cxgbe_l2t_release(struct l2t_entry *e) 15 { 16 if (rte_atomic32_read(&e->refcnt) != 0) 17 rte_atomic32_dec(&e->refcnt); 18 } 19 20 /** 21 * Process a CPL_L2T_WRITE_RPL. Note that the TID in the reply is really 22 * the L2T index it refers to. 23 */ 24 void do_l2t_write_rpl(struct adapter *adap, const struct cpl_l2t_write_rpl *rpl) 25 { 26 struct l2t_data *d = adap->l2t; 27 unsigned int tid = GET_TID(rpl); 28 unsigned int l2t_idx = tid % L2T_SIZE; 29 30 if (unlikely(rpl->status != CPL_ERR_NONE)) { 31 dev_err(adap, 32 "Unexpected L2T_WRITE_RPL status %u for entry %u\n", 33 rpl->status, l2t_idx); 34 return; 35 } 36 37 if (tid & F_SYNC_WR) { 38 struct l2t_entry *e = &d->l2tab[l2t_idx - d->l2t_start]; 39 40 t4_os_lock(&e->lock); 41 if (e->state != L2T_STATE_SWITCHING) 42 e->state = L2T_STATE_VALID; 43 t4_os_unlock(&e->lock); 44 } 45 } 46 47 /** 48 * Write an L2T entry. Must be called with the entry locked. 49 * The write may be synchronous or asynchronous. 50 */ 51 static int write_l2e(struct rte_eth_dev *dev, struct l2t_entry *e, int sync, 52 bool loopback, bool arpmiss) 53 { 54 struct adapter *adap = ethdev2adap(dev); 55 struct l2t_data *d = adap->l2t; 56 struct rte_mbuf *mbuf; 57 struct cpl_l2t_write_req *req; 58 struct sge_ctrl_txq *ctrlq; 59 unsigned int l2t_idx = e->idx + d->l2t_start; 60 unsigned int port_id = ethdev2pinfo(dev)->port_id; 61 62 ctrlq = &adap->sge.ctrlq[port_id]; 63 mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool); 64 if (!mbuf) 65 return -ENOMEM; 66 67 mbuf->data_len = sizeof(*req); 68 mbuf->pkt_len = mbuf->data_len; 69 70 req = rte_pktmbuf_mtod(mbuf, struct cpl_l2t_write_req *); 71 INIT_TP_WR(req, 0); 72 73 OPCODE_TID(req) = 74 cpu_to_be32(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, 75 l2t_idx | V_SYNC_WR(sync) | 76 V_TID_QID(adap->sge.fw_evtq.abs_id))); 77 req->params = cpu_to_be16(V_L2T_W_PORT(e->lport) | 78 V_L2T_W_LPBK(loopback) | 79 V_L2T_W_ARPMISS(arpmiss) | 80 V_L2T_W_NOREPLY(!sync)); 81 req->l2t_idx = cpu_to_be16(l2t_idx); 82 req->vlan = cpu_to_be16(e->vlan); 83 rte_memcpy(req->dst_mac, e->dmac, ETHER_ADDR_LEN); 84 85 if (loopback) 86 memset(req->dst_mac, 0, ETHER_ADDR_LEN); 87 88 t4_mgmt_tx(ctrlq, mbuf); 89 90 if (sync && e->state != L2T_STATE_SWITCHING) 91 e->state = L2T_STATE_SYNC_WRITE; 92 93 return 0; 94 } 95 96 /** 97 * find_or_alloc_l2e - Find/Allocate a free L2T entry 98 * @d: L2T table 99 * @vlan: VLAN id to compare/add 100 * @port: port id to compare/add 101 * @dmac: Destination MAC address to compare/add 102 * Returns pointer to the L2T entry found/created 103 * 104 * Finds/Allocates an L2T entry to be used by switching rule of a filter. 105 */ 106 static struct l2t_entry *find_or_alloc_l2e(struct l2t_data *d, u16 vlan, 107 u8 port, u8 *dmac) 108 { 109 struct l2t_entry *end, *e; 110 struct l2t_entry *first_free = NULL; 111 112 for (e = &d->l2tab[0], end = &d->l2tab[d->l2t_size]; e != end; ++e) { 113 if (rte_atomic32_read(&e->refcnt) == 0) { 114 if (!first_free) 115 first_free = e; 116 } else { 117 if (e->state == L2T_STATE_SWITCHING) { 118 if ((!memcmp(e->dmac, dmac, ETHER_ADDR_LEN)) && 119 e->vlan == vlan && e->lport == port) 120 goto exists; 121 } 122 } 123 } 124 125 if (first_free) { 126 e = first_free; 127 goto found; 128 } 129 130 return NULL; 131 132 found: 133 e->state = L2T_STATE_UNUSED; 134 135 exists: 136 return e; 137 } 138 139 static struct l2t_entry *t4_l2t_alloc_switching(struct rte_eth_dev *dev, 140 u16 vlan, u8 port, 141 u8 *eth_addr) 142 { 143 struct adapter *adap = ethdev2adap(dev); 144 struct l2t_data *d = adap->l2t; 145 struct l2t_entry *e; 146 int ret = 0; 147 148 t4_os_write_lock(&d->lock); 149 e = find_or_alloc_l2e(d, vlan, port, eth_addr); 150 if (e) { 151 t4_os_lock(&e->lock); 152 if (!rte_atomic32_read(&e->refcnt)) { 153 e->state = L2T_STATE_SWITCHING; 154 e->vlan = vlan; 155 e->lport = port; 156 rte_memcpy(e->dmac, eth_addr, ETHER_ADDR_LEN); 157 rte_atomic32_set(&e->refcnt, 1); 158 ret = write_l2e(dev, e, 0, !L2T_LPBK, !L2T_ARPMISS); 159 if (ret < 0) 160 dev_debug(adap, "Failed to write L2T entry: %d", 161 ret); 162 } else { 163 rte_atomic32_inc(&e->refcnt); 164 } 165 t4_os_unlock(&e->lock); 166 } 167 t4_os_write_unlock(&d->lock); 168 169 return ret ? NULL : e; 170 } 171 172 /** 173 * cxgbe_l2t_alloc_switching - Allocate a L2T entry for switching rule 174 * @dev: rte_eth_dev pointer 175 * @vlan: VLAN Id 176 * @port: Associated port 177 * @dmac: Destination MAC address to add to L2T 178 * Returns pointer to the allocated l2t entry 179 * 180 * Allocates a L2T entry for use by switching rule of a filter 181 */ 182 struct l2t_entry *cxgbe_l2t_alloc_switching(struct rte_eth_dev *dev, u16 vlan, 183 u8 port, u8 *dmac) 184 { 185 return t4_l2t_alloc_switching(dev, vlan, port, dmac); 186 } 187 188 /** 189 * Initialize L2 Table 190 */ 191 struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end) 192 { 193 unsigned int l2t_size; 194 unsigned int i; 195 struct l2t_data *d; 196 197 if (l2t_start >= l2t_end || l2t_end >= L2T_SIZE) 198 return NULL; 199 l2t_size = l2t_end - l2t_start + 1; 200 201 d = t4_os_alloc(sizeof(*d) + l2t_size * sizeof(struct l2t_entry)); 202 if (!d) 203 return NULL; 204 205 d->l2t_start = l2t_start; 206 d->l2t_size = l2t_size; 207 208 t4_os_rwlock_init(&d->lock); 209 210 for (i = 0; i < d->l2t_size; ++i) { 211 d->l2tab[i].idx = i; 212 d->l2tab[i].state = L2T_STATE_UNUSED; 213 t4_os_lock_init(&d->l2tab[i].lock); 214 rte_atomic32_set(&d->l2tab[i].refcnt, 0); 215 } 216 217 return d; 218 } 219 220 /** 221 * Cleanup L2 Table 222 */ 223 void t4_cleanup_l2t(struct adapter *adap) 224 { 225 if (adap->l2t) 226 t4_os_free(adap->l2t); 227 } 228