1334447Snp/*- 2334447Snp * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3334447Snp * 4334447Snp * Copyright (c) 2018 Chelsio Communications, Inc. 5334447Snp * All rights reserved. 6334447Snp * 7334447Snp * Redistribution and use in source and binary forms, with or without 8334447Snp * modification, are permitted provided that the following conditions 9334447Snp * are met: 10334447Snp * 1. Redistributions of source code must retain the above copyright 11334447Snp * notice, this list of conditions and the following disclaimer. 12334447Snp * 2. Redistributions in binary form must reproduce the above copyright 13334447Snp * notice, this list of conditions and the following disclaimer in the 14334447Snp * documentation and/or other materials provided with the distribution. 15334447Snp * 16334447Snp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17334447Snp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18334447Snp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19334447Snp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20334447Snp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21334447Snp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22334447Snp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23334447Snp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24334447Snp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25334447Snp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26334447Snp * SUCH DAMAGE. 27334447Snp */ 28334447Snp#include <sys/cdefs.h> 29334447Snp__FBSDID("$FreeBSD: stable/11/sys/dev/cxgbe/t4_smt.c 346876 2019-04-29 04:42:18Z np $"); 30334447Snp 31334447Snp#include "opt_inet.h" 32334447Snp#include "opt_inet6.h" 33334447Snp 34334447Snp#include <sys/param.h> 35334447Snp#include <sys/eventhandler.h> 36334447Snp#include <sys/systm.h> 37334447Snp#include <sys/kernel.h> 38334447Snp#include <sys/module.h> 39334447Snp#include <sys/bus.h> 40334447Snp#include <sys/lock.h> 41334447Snp#include <sys/mutex.h> 42334447Snp#include <sys/rwlock.h> 43334447Snp#include <sys/socket.h> 44334447Snp#include <sys/sbuf.h> 45334447Snp#include <netinet/in.h> 46334447Snp 47334447Snp#include "common/common.h" 48334447Snp#include "common/t4_msg.h" 49334447Snp#include "t4_smt.h" 50334447Snp 51334447Snp/* 52334447Snp * Module locking notes: There is a RW lock protecting the SMAC table as a 53334447Snp * whole plus a spinlock per SMT entry. Entry lookups and allocations happen 54334447Snp * under the protection of the table lock, individual entry changes happen 55334447Snp * while holding that entry's spinlock. The table lock nests outside the 56334447Snp * entry locks. Allocations of new entries take the table lock as writers so 57334447Snp * no other lookups can happen while allocating new entries. Entry updates 58334447Snp * take the table lock as readers so multiple entries can be updated in 59334447Snp * parallel. An SMT entry can be dropped by decrementing its reference count 60334447Snp * and therefore can happen in parallel with entry allocation but no entry 61334447Snp * can change state or increment its ref count during allocation as both of 62334447Snp * these perform lookups. 63334447Snp * 64334447Snp * Note: We do not take references to ifnets in this module because both 65334447Snp * the TOE and the sockets already hold references to the interfaces and the 66334447Snp * lifetime of an SMT entry is fully contained in the lifetime of the TOE. 67334447Snp */ 68334447Snp 69334447Snp/* 70334447Snp * Allocate a free SMT entry. Must be called with smt_data.lock held. 71334447Snp */ 72334447Snpstruct smt_entry * 73334447Snpt4_find_or_alloc_sme(struct smt_data *s, uint8_t *smac) 74334447Snp{ 75334447Snp struct smt_entry *end, *e; 76334447Snp struct smt_entry *first_free = NULL; 77334447Snp 78334447Snp rw_assert(&s->lock, RA_WLOCKED); 79334447Snp for (e = &s->smtab[0], end = &s->smtab[s->smt_size]; e != end; ++e) { 80334447Snp if (atomic_load_acq_int(&e->refcnt) == 0) { 81334447Snp if (!first_free) 82334447Snp first_free = e; 83334447Snp } else { 84334447Snp if (e->state == SMT_STATE_SWITCHING) { 85334447Snp /* 86334447Snp * This entry is actually in use. See if we can 87334447Snp * re-use it? 88334447Snp */ 89334447Snp if (memcmp(e->smac, smac, ETHER_ADDR_LEN) == 0) 90334447Snp goto found_reuse; 91334447Snp } 92334447Snp } 93334447Snp } 94334447Snp if (first_free) { 95334447Snp e = first_free; 96334447Snp goto found; 97334447Snp } 98334447Snp return NULL; 99334447Snp 100334447Snpfound: 101334447Snp e->state = SMT_STATE_UNUSED; 102334447Snpfound_reuse: 103334447Snp atomic_add_int(&e->refcnt, 1); 104334447Snp return e; 105334447Snp} 106334447Snp 107334447Snp/* 108334447Snp * Write an SMT entry. Must be called with the entry locked. 109334447Snp */ 110334447Snpint 111334447Snpt4_write_sme(struct smt_entry *e) 112334447Snp{ 113334447Snp struct smt_data *s; 114334447Snp struct sge_wrq *wrq; 115334447Snp struct adapter *sc; 116334447Snp struct wrq_cookie cookie; 117334447Snp struct cpl_smt_write_req *req; 118334447Snp struct cpl_t6_smt_write_req *t6req; 119334447Snp u8 row; 120334447Snp 121334447Snp mtx_assert(&e->lock, MA_OWNED); 122334447Snp 123334447Snp MPASS(e->wrq != NULL); 124334447Snp wrq = e->wrq; 125334447Snp sc = wrq->adapter; 126334447Snp MPASS(wrq->adapter != NULL); 127334447Snp s = sc->smt; 128334447Snp 129334447Snp 130334447Snp if (chip_id(sc) <= CHELSIO_T5) { 131334447Snp /* Source MAC Table (SMT) contains 256 SMAC entries 132334447Snp * organized in 128 rows of 2 entries each. 133334447Snp */ 134334447Snp req = start_wrq_wr(wrq, howmany(sizeof(*req), 16), &cookie); 135334447Snp if (req == NULL) 136334447Snp return (ENOMEM); 137334447Snp INIT_TP_WR(req, 0); 138334447Snp /* Each row contains an SMAC pair. 139334447Snp * LSB selects the SMAC entry within a row 140334447Snp */ 141334447Snp row = (e->idx >> 1); 142334447Snp if (e->idx & 1) { 143334447Snp req->pfvf1 = 0x0; 144334447Snp memcpy(req->src_mac1, e->smac, ETHER_ADDR_LEN); 145334447Snp /* fill pfvf0/src_mac0 with entry 146334447Snp * at prev index from smt-tab. 147334447Snp */ 148334447Snp req->pfvf0 = 0x0; 149334447Snp memcpy(req->src_mac0, s->smtab[e->idx - 1].smac, 150334447Snp ETHER_ADDR_LEN); 151334447Snp } else { 152334447Snp req->pfvf0 = 0x0; 153334447Snp memcpy(req->src_mac0, e->smac, ETHER_ADDR_LEN); 154334447Snp /* fill pfvf1/src_mac1 with entry 155334447Snp * at next index from smt-tab 156334447Snp */ 157334447Snp req->pfvf1 = 0x0; 158334447Snp memcpy(req->src_mac1, s->smtab[e->idx + 1].smac, 159334447Snp ETHER_ADDR_LEN); 160334447Snp } 161334447Snp } else { 162334447Snp /* Source MAC Table (SMT) contains 256 SMAC entries */ 163334447Snp t6req = start_wrq_wr(wrq, howmany(sizeof(*t6req), 16), &cookie); 164334447Snp if (t6req == NULL) 165334447Snp return (ENOMEM); 166334447Snp INIT_TP_WR(t6req, 0); 167334447Snp req = (struct cpl_smt_write_req *)t6req; 168334447Snp 169334447Snp /* fill pfvf0/src_mac0 from smt-tab */ 170334447Snp req->pfvf0 = 0x0; 171334447Snp memcpy(req->src_mac0, s->smtab[e->idx].smac, ETHER_ADDR_LEN); 172334447Snp row = e->idx; 173334447Snp } 174334447Snp OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, e->idx | 175334447Snp V_TID_QID(e->iqid))); 176334447Snp req->params = htonl(V_SMTW_NORPL(0) | 177334447Snp V_SMTW_IDX(row) | 178334447Snp V_SMTW_OVLAN_IDX(0)); 179334447Snp 180334447Snp commit_wrq_wr(wrq, req, &cookie); 181334447Snp 182334447Snp return (0); 183334447Snp} 184334447Snp 185334447Snp/* 186334447Snp * Allocate an SMT entry for use by a switching rule. 187334447Snp */ 188334447Snpstruct smt_entry * 189334447Snpt4_smt_alloc_switching(struct smt_data *s, uint8_t *smac) 190334447Snp{ 191334447Snp struct smt_entry *e; 192334447Snp 193334447Snp MPASS(s != NULL); 194334447Snp rw_wlock(&s->lock); 195334447Snp e = t4_find_or_alloc_sme(s, smac); 196334447Snp rw_wunlock(&s->lock); 197334447Snp return e; 198334447Snp} 199334447Snp 200334447Snp/* 201334447Snp * Sets/updates the contents of a switching SMT entry that has been allocated 202334447Snp * with an earlier call to @t4_smt_alloc_switching. 203334447Snp */ 204334447Snpint 205334447Snpt4_smt_set_switching(struct adapter *sc, struct smt_entry *e, uint16_t pfvf, 206334447Snp uint8_t *smac) 207334447Snp{ 208334447Snp int rc = 0; 209334447Snp 210334447Snp if (atomic_load_acq_int(&e->refcnt) == 1) { 211334447Snp /* Setup the entry for the first time */ 212334447Snp mtx_lock(&e->lock); 213346876Snp e->wrq = &sc->sge.ctrlq[0]; 214334447Snp e->iqid = sc->sge.fwq.abs_id; 215334447Snp e->pfvf = pfvf; 216334447Snp e->state = SMT_STATE_SWITCHING; 217334447Snp memcpy(e->smac, smac, ETHER_ADDR_LEN); 218334447Snp rc = t4_write_sme(e); 219334447Snp mtx_unlock(&e->lock); 220334447Snp } 221334447Snp 222334447Snp return (rc); 223334447Snp} 224334447Snp 225334447Snpint 226334447Snpt4_init_smt(struct adapter *sc, int flags) 227334447Snp{ 228334447Snp int i, smt_size; 229334447Snp struct smt_data *s; 230334447Snp 231334447Snp smt_size = SMT_SIZE; 232334447Snp s = malloc(sizeof(*s) + smt_size * sizeof (struct smt_entry), M_CXGBE, 233334447Snp M_ZERO | flags); 234334447Snp if (!s) 235334447Snp return (ENOMEM); 236334447Snp 237334447Snp s->smt_size = smt_size; 238334447Snp rw_init(&s->lock, "SMT"); 239334447Snp 240334447Snp for (i = 0; i < smt_size; i++) { 241334447Snp struct smt_entry *e = &s->smtab[i]; 242334447Snp 243334447Snp e->idx = i; 244334447Snp e->state = SMT_STATE_UNUSED; 245334447Snp mtx_init(&e->lock, "SMT_E", NULL, MTX_DEF); 246334447Snp atomic_store_rel_int(&e->refcnt, 0); 247334447Snp } 248334447Snp 249334447Snp sc->smt = s; 250334447Snp 251334447Snp return (0); 252334447Snp} 253334447Snp 254334447Snpint 255334447Snpt4_free_smt(struct smt_data *s) 256334447Snp{ 257334447Snp int i; 258334447Snp 259334447Snp for (i = 0; i < s->smt_size; i++) 260334447Snp mtx_destroy(&s->smtab[i].lock); 261334447Snp rw_destroy(&s->lock); 262334447Snp free(s, M_CXGBE); 263334447Snp 264334447Snp return (0); 265334447Snp} 266334447Snp 267334447Snpint 268334447Snpdo_smt_write_rpl(struct sge_iq *iq, const struct rss_header *rss, 269334447Snp struct mbuf *m) 270334447Snp{ 271334447Snp struct adapter *sc = iq->adapter; 272334447Snp const struct cpl_smt_write_rpl *rpl = (const void *)(rss + 1); 273334447Snp unsigned int tid = GET_TID(rpl); 274334447Snp unsigned int smtidx = G_TID_TID(tid); 275334447Snp 276334447Snp if (__predict_false(rpl->status != CPL_ERR_NONE)) { 277334447Snp struct smt_entry *e = &sc->smt->smtab[smtidx]; 278334447Snp log(LOG_ERR, 279334447Snp "Unexpected SMT_WRITE_RPL (%u) for entry at hw_idx %u\n", 280334447Snp rpl->status, smtidx); 281334447Snp mtx_lock(&e->lock); 282334447Snp e->state = SMT_STATE_ERROR; 283334447Snp mtx_unlock(&e->lock); 284334447Snp return (EINVAL); 285334447Snp } 286334447Snp 287334447Snp return (0); 288334447Snp} 289334447Snp 290334447Snpstatic char 291334447Snpsmt_state(const struct smt_entry *e) 292334447Snp{ 293334447Snp switch (e->state) { 294334447Snp case SMT_STATE_SWITCHING: return 'X'; 295334447Snp case SMT_STATE_ERROR: return 'E'; 296334447Snp default: return 'U'; 297334447Snp } 298334447Snp} 299334447Snp 300334447Snpint 301334447Snpsysctl_smt(SYSCTL_HANDLER_ARGS) 302334447Snp{ 303334447Snp struct adapter *sc = arg1; 304334447Snp struct smt_data *smt = sc->smt; 305334447Snp struct smt_entry *e; 306334447Snp struct sbuf *sb; 307334447Snp int rc, i, header = 0; 308334447Snp 309334447Snp if (smt == NULL) 310334447Snp return (ENXIO); 311334447Snp 312334447Snp rc = sysctl_wire_old_buffer(req, 0); 313334447Snp if (rc != 0) 314334447Snp return (rc); 315334447Snp 316334447Snp sb = sbuf_new_for_sysctl(NULL, NULL, SMT_SIZE, req); 317334447Snp if (sb == NULL) 318334447Snp return (ENOMEM); 319334447Snp 320334447Snp e = &smt->smtab[0]; 321334447Snp for (i = 0; i < smt->smt_size; i++, e++) { 322334447Snp mtx_lock(&e->lock); 323334447Snp if (e->state == SMT_STATE_UNUSED) 324334447Snp goto skip; 325334447Snp 326334447Snp if (header == 0) { 327334447Snp sbuf_printf(sb, " Idx " 328334447Snp "Ethernet address State Users"); 329334447Snp header = 1; 330334447Snp } 331334447Snp sbuf_printf(sb, "\n%4u %02x:%02x:%02x:%02x:%02x:%02x " 332334447Snp "%c %5u", 333334447Snp e->idx, e->smac[0], e->smac[1], e->smac[2], 334334447Snp e->smac[3], e->smac[4], e->smac[5], 335334447Snp smt_state(e), atomic_load_acq_int(&e->refcnt)); 336334447Snpskip: 337334447Snp mtx_unlock(&e->lock); 338334447Snp } 339334447Snp 340334447Snp rc = sbuf_finish(sb); 341334447Snp sbuf_delete(sb); 342334447Snp 343334447Snp return (rc); 344334447Snp} 345