1341172Sjhb/*- 2341172Sjhb * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3341172Sjhb * 4341172Sjhb * Copyright (c) 2012 Chelsio Communications, Inc. 5341172Sjhb * All rights reserved. 6341172Sjhb * Written by: Navdeep Parhar <np@FreeBSD.org> 7341172Sjhb * 8341172Sjhb * Redistribution and use in source and binary forms, with or without 9341172Sjhb * modification, are permitted provided that the following conditions 10341172Sjhb * are met: 11341172Sjhb * 1. Redistributions of source code must retain the above copyright 12341172Sjhb * notice, this list of conditions and the following disclaimer. 13341172Sjhb * 2. Redistributions in binary form must reproduce the above copyright 14341172Sjhb * notice, this list of conditions and the following disclaimer in the 15341172Sjhb * documentation and/or other materials provided with the distribution. 16341172Sjhb * 17341172Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18341172Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19341172Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20341172Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21341172Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22341172Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23341172Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24341172Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25341172Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26341172Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27341172Sjhb * SUCH DAMAGE. 28341172Sjhb */ 29341172Sjhb 30341172Sjhb#include <sys/cdefs.h> 31341172Sjhb__FBSDID("$FreeBSD: stable/11/sys/dev/cxgbe/t4_clip.c 346946 2019-04-30 07:34:34Z np $"); 32341172Sjhb 33346934Snp#include "opt_inet.h" 34346934Snp#include "opt_inet6.h" 35346934Snp 36341172Sjhb#include <sys/types.h> 37341172Sjhb#include <sys/eventhandler.h> 38341172Sjhb#include <sys/malloc.h> 39341172Sjhb#include <sys/rmlock.h> 40341172Sjhb#include <sys/sbuf.h> 41341172Sjhb#include <sys/socket.h> 42341172Sjhb#include <sys/taskqueue.h> 43341172Sjhb#include <net/if.h> 44341172Sjhb#include <net/if_var.h> 45341172Sjhb#include <netinet/in.h> 46341172Sjhb#include <netinet6/in6_var.h> 47341172Sjhb#include <netinet6/scope6_var.h> 48341172Sjhb 49341172Sjhb#include "common/common.h" 50341172Sjhb#include "t4_clip.h" 51341172Sjhb 52346934Snp#if defined(INET6) 53341172Sjhbstatic int add_lip(struct adapter *, struct in6_addr *); 54341172Sjhbstatic int delete_lip(struct adapter *, struct in6_addr *); 55341172Sjhbstatic struct clip_entry *search_lip(struct adapter *, struct in6_addr *); 56341172Sjhbstatic void update_clip(struct adapter *, void *); 57341172Sjhbstatic void t4_clip_task(void *, int); 58341172Sjhbstatic void update_clip_table(struct adapter *); 59341172Sjhb 60341172Sjhbstatic int in6_ifaddr_gen; 61341172Sjhbstatic eventhandler_tag ifaddr_evhandler; 62341172Sjhbstatic struct timeout_task clip_task; 63341172Sjhb 64341172Sjhbstatic int 65341172Sjhbadd_lip(struct adapter *sc, struct in6_addr *lip) 66341172Sjhb{ 67341172Sjhb struct fw_clip_cmd c; 68341172Sjhb 69341172Sjhb ASSERT_SYNCHRONIZED_OP(sc); 70341172Sjhb mtx_assert(&sc->clip_table_lock, MA_OWNED); 71341172Sjhb 72341172Sjhb memset(&c, 0, sizeof(c)); 73341172Sjhb c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST | 74341172Sjhb F_FW_CMD_WRITE); 75341172Sjhb c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c)); 76341172Sjhb c.ip_hi = *(uint64_t *)&lip->s6_addr[0]; 77341172Sjhb c.ip_lo = *(uint64_t *)&lip->s6_addr[8]; 78341172Sjhb 79341172Sjhb return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c)); 80341172Sjhb} 81341172Sjhb 82341172Sjhbstatic int 83341172Sjhbdelete_lip(struct adapter *sc, struct in6_addr *lip) 84341172Sjhb{ 85341172Sjhb struct fw_clip_cmd c; 86341172Sjhb 87341172Sjhb ASSERT_SYNCHRONIZED_OP(sc); 88341172Sjhb mtx_assert(&sc->clip_table_lock, MA_OWNED); 89341172Sjhb 90341172Sjhb memset(&c, 0, sizeof(c)); 91341172Sjhb c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST | 92341172Sjhb F_FW_CMD_READ); 93341172Sjhb c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c)); 94341172Sjhb c.ip_hi = *(uint64_t *)&lip->s6_addr[0]; 95341172Sjhb c.ip_lo = *(uint64_t *)&lip->s6_addr[8]; 96341172Sjhb 97341172Sjhb return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c)); 98341172Sjhb} 99341172Sjhb 100341172Sjhbstatic struct clip_entry * 101341172Sjhbsearch_lip(struct adapter *sc, struct in6_addr *lip) 102341172Sjhb{ 103341172Sjhb struct clip_entry *ce; 104341172Sjhb 105341172Sjhb mtx_assert(&sc->clip_table_lock, MA_OWNED); 106341172Sjhb 107341172Sjhb TAILQ_FOREACH(ce, &sc->clip_table, link) { 108341172Sjhb if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip)) 109341172Sjhb return (ce); 110341172Sjhb } 111341172Sjhb 112341172Sjhb return (NULL); 113341172Sjhb} 114346934Snp#endif 115341172Sjhb 116341172Sjhbstruct clip_entry * 117341172Sjhbt4_hold_lip(struct adapter *sc, struct in6_addr *lip, struct clip_entry *ce) 118341172Sjhb{ 119341172Sjhb 120346934Snp#ifdef INET6 121341172Sjhb mtx_lock(&sc->clip_table_lock); 122341172Sjhb if (ce == NULL) 123341172Sjhb ce = search_lip(sc, lip); 124341172Sjhb if (ce != NULL) 125341172Sjhb ce->refcount++; 126341172Sjhb mtx_unlock(&sc->clip_table_lock); 127341172Sjhb 128341172Sjhb return (ce); 129346934Snp#else 130346934Snp return (NULL); 131346934Snp#endif 132341172Sjhb} 133341172Sjhb 134341172Sjhbvoid 135341172Sjhbt4_release_lip(struct adapter *sc, struct clip_entry *ce) 136341172Sjhb{ 137341172Sjhb 138346934Snp#ifdef INET6 139341172Sjhb mtx_lock(&sc->clip_table_lock); 140341172Sjhb KASSERT(search_lip(sc, &ce->lip) == ce, 141341172Sjhb ("%s: CLIP entry %p p not in CLIP table.", __func__, ce)); 142341172Sjhb KASSERT(ce->refcount > 0, 143341172Sjhb ("%s: CLIP entry %p has refcount 0", __func__, ce)); 144341172Sjhb --ce->refcount; 145341172Sjhb mtx_unlock(&sc->clip_table_lock); 146346934Snp#endif 147341172Sjhb} 148341172Sjhb 149346934Snp#ifdef INET6 150341172Sjhbvoid 151341172Sjhbt4_init_clip_table(struct adapter *sc) 152341172Sjhb{ 153341172Sjhb 154341172Sjhb mtx_init(&sc->clip_table_lock, "CLIP table lock", NULL, MTX_DEF); 155341172Sjhb TAILQ_INIT(&sc->clip_table); 156341172Sjhb sc->clip_gen = -1; 157341172Sjhb 158341172Sjhb /* 159341172Sjhb * Don't bother forcing an update of the clip table when the 160341172Sjhb * adapter is initialized. Before an interface can be used it 161341172Sjhb * must be assigned an address which will trigger the event 162341172Sjhb * handler to update the table. 163341172Sjhb */ 164341172Sjhb} 165341172Sjhb 166341172Sjhbstatic void 167341172Sjhbupdate_clip(struct adapter *sc, void *arg __unused) 168341172Sjhb{ 169341172Sjhb 170341172Sjhb if (begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4clip")) 171341172Sjhb return; 172341172Sjhb 173341172Sjhb if (mtx_initialized(&sc->clip_table_lock)) 174341172Sjhb update_clip_table(sc); 175341172Sjhb 176341172Sjhb end_synchronized_op(sc, LOCK_HELD); 177341172Sjhb} 178341172Sjhb 179341172Sjhbstatic void 180341172Sjhbt4_clip_task(void *arg, int count) 181341172Sjhb{ 182341172Sjhb 183341172Sjhb t4_iterate(update_clip, NULL); 184341172Sjhb} 185341172Sjhb 186341172Sjhbstatic void 187341172Sjhbupdate_clip_table(struct adapter *sc) 188341172Sjhb{ 189341172Sjhb struct rm_priotracker in6_ifa_tracker; 190341172Sjhb struct in6_ifaddr *ia; 191341172Sjhb struct in6_addr *lip, tlip; 192341172Sjhb TAILQ_HEAD(, clip_entry) stale; 193341172Sjhb struct clip_entry *ce, *ce_temp; 194341172Sjhb struct vi_info *vi; 195341172Sjhb int rc, gen, i, j; 196341172Sjhb uintptr_t last_vnet; 197341172Sjhb 198341172Sjhb ASSERT_SYNCHRONIZED_OP(sc); 199341172Sjhb 200341172Sjhb IN6_IFADDR_RLOCK(&in6_ifa_tracker); 201341172Sjhb mtx_lock(&sc->clip_table_lock); 202341172Sjhb 203341172Sjhb gen = atomic_load_acq_int(&in6_ifaddr_gen); 204341172Sjhb if (gen == sc->clip_gen) 205341172Sjhb goto done; 206341172Sjhb 207341172Sjhb TAILQ_INIT(&stale); 208341172Sjhb TAILQ_CONCAT(&stale, &sc->clip_table, link); 209341172Sjhb 210341172Sjhb /* 211341172Sjhb * last_vnet optimizes the common cases where all if_vnet = NULL (no 212341172Sjhb * VIMAGE) or all if_vnet = vnet0. 213341172Sjhb */ 214341172Sjhb last_vnet = (uintptr_t)(-1); 215341172Sjhb for_each_port(sc, i) 216341172Sjhb for_each_vi(sc->port[i], j, vi) { 217341172Sjhb if (last_vnet == (uintptr_t)vi->ifp->if_vnet) 218341172Sjhb continue; 219341172Sjhb 220341172Sjhb /* XXX: races with if_vmove */ 221341172Sjhb CURVNET_SET(vi->ifp->if_vnet); 222346934Snp TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) { 223341172Sjhb lip = &ia->ia_addr.sin6_addr; 224341172Sjhb 225341172Sjhb KASSERT(!IN6_IS_ADDR_MULTICAST(lip), 226341172Sjhb ("%s: mcast address in in6_ifaddr list", __func__)); 227341172Sjhb 228341172Sjhb if (IN6_IS_ADDR_LOOPBACK(lip)) 229341172Sjhb continue; 230341172Sjhb if (IN6_IS_SCOPE_EMBED(lip)) { 231341172Sjhb /* Remove the embedded scope */ 232341172Sjhb tlip = *lip; 233341172Sjhb lip = &tlip; 234341172Sjhb in6_clearscope(lip); 235341172Sjhb } 236341172Sjhb /* 237341172Sjhb * XXX: how to weed out the link local address for the 238341172Sjhb * loopback interface? It's fe80::1 usually (always?). 239341172Sjhb */ 240341172Sjhb 241341172Sjhb /* 242341172Sjhb * If it's in the main list then we already know it's 243341172Sjhb * not stale. 244341172Sjhb */ 245341172Sjhb TAILQ_FOREACH(ce, &sc->clip_table, link) { 246341172Sjhb if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip)) 247341172Sjhb goto next; 248341172Sjhb } 249341172Sjhb 250341172Sjhb /* 251341172Sjhb * If it's in the stale list we should move it to the 252341172Sjhb * main list. 253341172Sjhb */ 254341172Sjhb TAILQ_FOREACH(ce, &stale, link) { 255341172Sjhb if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip)) { 256341172Sjhb TAILQ_REMOVE(&stale, ce, link); 257341172Sjhb TAILQ_INSERT_TAIL(&sc->clip_table, ce, 258341172Sjhb link); 259341172Sjhb goto next; 260341172Sjhb } 261341172Sjhb } 262341172Sjhb 263341172Sjhb /* A new IP6 address; add it to the CLIP table */ 264341172Sjhb ce = malloc(sizeof(*ce), M_CXGBE, M_NOWAIT); 265341172Sjhb memcpy(&ce->lip, lip, sizeof(ce->lip)); 266341172Sjhb ce->refcount = 0; 267341172Sjhb rc = add_lip(sc, lip); 268341172Sjhb if (rc == 0) 269341172Sjhb TAILQ_INSERT_TAIL(&sc->clip_table, ce, link); 270341172Sjhb else { 271341172Sjhb char ip[INET6_ADDRSTRLEN]; 272341172Sjhb 273341172Sjhb inet_ntop(AF_INET6, &ce->lip, &ip[0], 274341172Sjhb sizeof(ip)); 275341172Sjhb log(LOG_ERR, "%s: could not add %s (%d)\n", 276341172Sjhb __func__, ip, rc); 277341172Sjhb free(ce, M_CXGBE); 278341172Sjhb } 279341172Sjhbnext: 280341172Sjhb continue; 281341172Sjhb } 282341172Sjhb CURVNET_RESTORE(); 283341172Sjhb last_vnet = (uintptr_t)vi->ifp->if_vnet; 284341172Sjhb } 285341172Sjhb 286341172Sjhb /* 287341172Sjhb * Remove stale addresses (those no longer in V_in6_ifaddrhead) that are 288341172Sjhb * no longer referenced by the driver. 289341172Sjhb */ 290341172Sjhb TAILQ_FOREACH_SAFE(ce, &stale, link, ce_temp) { 291341172Sjhb if (ce->refcount == 0) { 292341172Sjhb rc = delete_lip(sc, &ce->lip); 293341172Sjhb if (rc == 0) { 294341172Sjhb TAILQ_REMOVE(&stale, ce, link); 295341172Sjhb free(ce, M_CXGBE); 296341172Sjhb } else { 297341172Sjhb char ip[INET6_ADDRSTRLEN]; 298341172Sjhb 299341172Sjhb inet_ntop(AF_INET6, &ce->lip, &ip[0], 300341172Sjhb sizeof(ip)); 301341172Sjhb log(LOG_ERR, "%s: could not delete %s (%d)\n", 302341172Sjhb __func__, ip, rc); 303341172Sjhb } 304341172Sjhb } 305341172Sjhb } 306341172Sjhb /* The ones that are still referenced need to stay in the CLIP table */ 307341172Sjhb TAILQ_CONCAT(&sc->clip_table, &stale, link); 308341172Sjhb 309341172Sjhb sc->clip_gen = gen; 310341172Sjhbdone: 311341172Sjhb mtx_unlock(&sc->clip_table_lock); 312341172Sjhb IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); 313341172Sjhb} 314341172Sjhb 315341172Sjhbvoid 316341172Sjhbt4_destroy_clip_table(struct adapter *sc) 317341172Sjhb{ 318341172Sjhb struct clip_entry *ce, *ce_temp; 319341172Sjhb 320341172Sjhb if (mtx_initialized(&sc->clip_table_lock)) { 321341172Sjhb mtx_lock(&sc->clip_table_lock); 322341172Sjhb TAILQ_FOREACH_SAFE(ce, &sc->clip_table, link, ce_temp) { 323341172Sjhb KASSERT(ce->refcount == 0, 324341172Sjhb ("%s: CLIP entry %p still in use (%d)", __func__, 325341172Sjhb ce, ce->refcount)); 326341172Sjhb TAILQ_REMOVE(&sc->clip_table, ce, link); 327346946Snp#if 0 328341172Sjhb delete_lip(sc, &ce->lip); 329346946Snp#endif 330341172Sjhb free(ce, M_CXGBE); 331341172Sjhb } 332341172Sjhb mtx_unlock(&sc->clip_table_lock); 333341172Sjhb mtx_destroy(&sc->clip_table_lock); 334341172Sjhb } 335341172Sjhb} 336341172Sjhb 337341172Sjhbstatic void 338341172Sjhbt4_tom_ifaddr_event(void *arg __unused, struct ifnet *ifp) 339341172Sjhb{ 340341172Sjhb 341341172Sjhb atomic_add_rel_int(&in6_ifaddr_gen, 1); 342341172Sjhb taskqueue_enqueue_timeout(taskqueue_thread, &clip_task, -hz / 4); 343341172Sjhb} 344341172Sjhb 345341172Sjhbint 346341172Sjhbsysctl_clip(SYSCTL_HANDLER_ARGS) 347341172Sjhb{ 348341172Sjhb struct adapter *sc = arg1; 349341172Sjhb struct clip_entry *ce; 350341172Sjhb struct sbuf *sb; 351341172Sjhb int rc, header = 0; 352341172Sjhb char ip[INET6_ADDRSTRLEN]; 353341172Sjhb 354341172Sjhb rc = sysctl_wire_old_buffer(req, 0); 355341172Sjhb if (rc != 0) 356341172Sjhb return (rc); 357341172Sjhb 358341172Sjhb sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req); 359341172Sjhb if (sb == NULL) 360341172Sjhb return (ENOMEM); 361341172Sjhb 362341172Sjhb mtx_lock(&sc->clip_table_lock); 363341172Sjhb TAILQ_FOREACH(ce, &sc->clip_table, link) { 364341172Sjhb if (header == 0) { 365341172Sjhb sbuf_printf(sb, "%-40s %-5s", "IP address", "Users"); 366341172Sjhb header = 1; 367341172Sjhb } 368341172Sjhb inet_ntop(AF_INET6, &ce->lip, &ip[0], sizeof(ip)); 369341172Sjhb 370341172Sjhb sbuf_printf(sb, "\n%-40s %5u", ip, ce->refcount); 371341172Sjhb } 372341172Sjhb mtx_unlock(&sc->clip_table_lock); 373341172Sjhb 374341172Sjhb rc = sbuf_finish(sb); 375341172Sjhb sbuf_delete(sb); 376341172Sjhb 377341172Sjhb return (rc); 378341172Sjhb} 379341172Sjhb 380341172Sjhbvoid 381341172Sjhbt4_clip_modload(void) 382341172Sjhb{ 383341172Sjhb 384341172Sjhb TIMEOUT_TASK_INIT(taskqueue_thread, &clip_task, 0, t4_clip_task, NULL); 385341172Sjhb ifaddr_evhandler = EVENTHANDLER_REGISTER(ifaddr_event, 386341172Sjhb t4_tom_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY); 387341172Sjhb} 388341172Sjhb 389341172Sjhbvoid 390341172Sjhbt4_clip_modunload(void) 391341172Sjhb{ 392341172Sjhb 393341172Sjhb EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_evhandler); 394341172Sjhb taskqueue_cancel_timeout(taskqueue_thread, &clip_task, NULL); 395341172Sjhb} 396346934Snp#endif 397