The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/cxgbe/t4_clip.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2012-2021 Chelsio Communications, Inc.
    5  * All rights reserved.
    6  * Written by: Navdeep Parhar <np@FreeBSD.org>
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include "opt_inet.h"
   34 #include "opt_inet6.h"
   35 
   36 #include <sys/types.h>
   37 #include <sys/ck.h>
   38 #include <sys/eventhandler.h>
   39 #include <sys/malloc.h>
   40 #include <sys/rmlock.h>
   41 #include <sys/sbuf.h>
   42 #include <sys/socket.h>
   43 #include <sys/taskqueue.h>
   44 #include <net/if.h>
   45 #include <net/if_var.h>
   46 #include <netinet/in.h>
   47 #include <netinet6/in6_var.h>
   48 #include <netinet6/scope6_var.h>
   49 
   50 #include "common/common.h"
   51 #include "t4_clip.h"
   52 
   53 /*
   54  * Code to deal with the Compressed Local IPv6 (CLIP) table in the ASIC.
   55  *
   56  * The driver maintains a global CLIP database (clip_db) of IPv6 addresses and a
   57  * per-adapter CLIP table (sc->clip_table) with entries that point to an IPv6 in
   58  * the clip_db.  All access is protected by a single global lock (clip_db_lock).
   59  * The correct lock order is clip lock before synchronized op.
   60  *
   61  * By default (hw.cxgbe.clip_db_auto=1) all local IPv6 addresses are added to
   62  * the db.  Addresses are also added on-demand when the driver allocates an
   63  * entry for a filter, TOE tid, etc.  krn_ref counts the number of times an
   64  * address appears in the system.  adp_ref counts the number of adapters that
   65  * have that address in their CLIP table.  If both are 0 then the entry is
   66  * evicted from the db.  Consumers of the CLIP table entry (filters, TOE tids)
   67  * are tracked in ce->refcount.  Driver ioctls let external consumers add/remove
   68  * addresses from the CLIP table.
   69  */
   70 
   71 #if defined(INET6)
   72 struct clip_db_entry {
   73         LIST_ENTRY(clip_db_entry) link; /* clip_db hash linkage */
   74         struct in6_addr lip;
   75         u_int krn_ref;  /* # of times this IP6 appears in list of all IP6 */
   76         u_int adp_ref;  /* # of adapters with this IP6 in their CLIP */
   77         u_int tmp_ref;  /* Used only during refresh */
   78 };
   79 
   80 struct clip_entry {
   81         LIST_ENTRY(clip_entry) link;    /* clip_table hash linkage */
   82         TAILQ_ENTRY(clip_entry) plink;  /* clip_pending linkage */
   83         struct clip_db_entry *cde;
   84         int16_t clip_idx;               /* index in the hw table */
   85         bool pending;                   /* in clip_pending list */
   86         int refcount;
   87 };
   88 
   89 static eventhandler_tag ifaddr_evhandler;
   90 static struct mtx clip_db_lock;
   91 static LIST_HEAD(, clip_db_entry) *clip_db;
   92 static u_long clip_db_mask;
   93 static int clip_db_gen;
   94 static struct task clip_db_task;
   95 
   96 static int add_lip(struct adapter *, struct in6_addr *, int16_t *);
   97 static int del_lip(struct adapter *, struct in6_addr *);
   98 static void t4_clip_db_task(void *, int);
   99 static void t4_clip_task(void *, int);
  100 static void update_clip_db(void);
  101 static int update_sw_clip_table(struct adapter *);
  102 static int update_hw_clip_table(struct adapter *);
  103 static void update_clip_table(struct adapter *, void *);
  104 static int sysctl_clip_db(SYSCTL_HANDLER_ARGS);
  105 static int sysctl_clip_db_auto(SYSCTL_HANDLER_ARGS);
  106 static struct clip_db_entry *lookup_clip_db_entry(struct in6_addr *, bool);
  107 static struct clip_entry *lookup_clip_entry(struct adapter *, struct in6_addr *,
  108     bool);
  109 
  110 SYSCTL_PROC(_hw_cxgbe, OID_AUTO, clip_db, CTLTYPE_STRING | CTLFLAG_RD |
  111     CTLFLAG_SKIP | CTLFLAG_MPSAFE, NULL, 0, sysctl_clip_db, "A",
  112     "CLIP database");
  113 
  114 int t4_clip_db_auto = 1;
  115 SYSCTL_PROC(_hw_cxgbe, OID_AUTO, clip_db_auto, CTLTYPE_INT | CTLFLAG_RWTUN |
  116     CTLFLAG_MPSAFE, NULL, 0, sysctl_clip_db_auto, "I",
  117     "Add local IPs to CLIP db automatically (0 = no, 1 = yes)");
  118 
  119 static inline uint32_t
  120 clip_hashfn(struct in6_addr *addr)
  121 {
  122         return (fnv_32_buf(addr, sizeof(*addr), FNV1_32_INIT) & clip_db_mask);
  123 }
  124 
  125 static inline struct clip_db_entry *
  126 alloc_clip_db_entry(struct in6_addr *in6)
  127 {
  128         struct clip_db_entry *cde;
  129 
  130         cde = malloc(sizeof(*cde), M_CXGBE, M_NOWAIT | M_ZERO);
  131         if (__predict_true(cde != NULL))
  132                 memcpy(&cde->lip, in6, sizeof(cde->lip));
  133 
  134         return (cde);
  135 }
  136 
  137 static inline struct clip_entry *
  138 alloc_clip_entry(struct clip_db_entry *cde)
  139 {
  140         struct clip_entry *ce;
  141 
  142         mtx_assert(&clip_db_lock, MA_OWNED);
  143 
  144         ce = malloc(sizeof(*ce), M_CXGBE, M_NOWAIT | M_ZERO);
  145         if (__predict_true(ce != NULL)) {
  146                 ce->cde = cde;
  147                 cde->adp_ref++;
  148                 ce->clip_idx = -1;
  149         }
  150 
  151         return (ce);
  152 }
  153 
  154 /*
  155  * Look up the IP6 address in the CLIP db.  If add is set then an entry for the
  156  * IP6 will be added to the db.
  157  */
  158 static struct clip_db_entry *
  159 lookup_clip_db_entry(struct in6_addr *in6, bool add)
  160 {
  161         struct clip_db_entry *cde;
  162         const int bucket = clip_hashfn(in6);
  163 
  164         mtx_assert(&clip_db_lock, MA_OWNED);
  165 
  166         LIST_FOREACH(cde, &clip_db[bucket], link) {
  167                 if (IN6_ARE_ADDR_EQUAL(&cde->lip, in6))
  168                         return (cde);
  169         }
  170 
  171         /* Not found.  Create a new entry if requested. */
  172         if (add) {
  173                 cde = alloc_clip_db_entry(in6);
  174                 if (cde != NULL)
  175                         LIST_INSERT_HEAD(&clip_db[bucket], cde, link);
  176         }
  177 
  178         return (cde);
  179 }
  180 
  181 /*
  182  * Look up the IP6 address in the CLIP db.  If add is set then an entry for the
  183  * IP6 will be added to the db.
  184  */
  185 static struct clip_entry *
  186 lookup_clip_entry(struct adapter *sc, struct in6_addr *in6, bool add)
  187 {
  188         struct clip_db_entry *cde;
  189         struct clip_entry *ce;
  190         const int bucket = clip_hashfn(in6);
  191 
  192         mtx_assert(&clip_db_lock, MA_OWNED);
  193 
  194         cde = lookup_clip_db_entry(in6, add);
  195         if (cde == NULL)
  196                 return (NULL);
  197 
  198         LIST_FOREACH(ce, &sc->clip_table[bucket], link) {
  199                 if (ce->cde == cde)
  200                         return (ce);
  201         }
  202 
  203         /* Not found.  Create a new entry if requested. */
  204         if (add) {
  205                 ce = alloc_clip_entry(cde);
  206                 if (ce != NULL) {
  207                         LIST_INSERT_HEAD(&sc->clip_table[bucket], ce, link);
  208                         TAILQ_INSERT_TAIL(&sc->clip_pending, ce, plink);
  209                         ce->pending = true;
  210                 }
  211         }
  212 
  213         return (ce);
  214 }
  215 
  216 static int
  217 add_lip(struct adapter *sc, struct in6_addr *lip, int16_t *idx)
  218 {
  219         struct fw_clip_cmd c;
  220         int rc;
  221 
  222         ASSERT_SYNCHRONIZED_OP(sc);
  223 
  224         memset(&c, 0, sizeof(c));
  225         c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
  226             F_FW_CMD_WRITE);
  227         c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
  228         c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
  229         c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
  230 
  231         rc = -t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c);
  232         if (rc == 0 && idx != NULL)
  233                 *idx = G_FW_CLIP_CMD_INDEX(ntohl(c.alloc_to_len16));
  234         return (rc);
  235 }
  236 
  237 static int
  238 del_lip(struct adapter *sc, struct in6_addr *lip)
  239 {
  240         struct fw_clip_cmd c;
  241 
  242         ASSERT_SYNCHRONIZED_OP(sc);
  243 
  244         memset(&c, 0, sizeof(c));
  245         c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
  246             F_FW_CMD_READ);
  247         c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
  248         c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
  249         c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
  250 
  251         return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
  252 }
  253 #endif
  254 
  255 struct clip_entry *
  256 t4_get_clip_entry(struct adapter *sc, struct in6_addr *in6, bool add)
  257 {
  258 #ifdef INET6
  259         struct clip_entry *ce;
  260         bool schedule = false;
  261 
  262         mtx_lock(&clip_db_lock);
  263         ce = lookup_clip_entry(sc, in6, add);
  264         if (ce != NULL) {
  265                 MPASS(ce->cde->adp_ref > 0);
  266                 if (++ce->refcount == 1 && ce->pending && ce->clip_idx != -1) {
  267                         /*
  268                          * Valid entry that was waiting to be deleted.  It is in
  269                          * use now so take it off the pending list.
  270                          */
  271                         TAILQ_REMOVE(&sc->clip_pending, ce, plink);
  272                         ce->pending = false;
  273                 }
  274                 if (ce->clip_idx == -1 && update_hw_clip_table(sc) != 0)
  275                         schedule = true;
  276         }
  277         mtx_unlock(&clip_db_lock);
  278         if (schedule)
  279                 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task, 0);
  280 
  281         return (ce);
  282 #else
  283         return (NULL);
  284 #endif
  285 }
  286 
  287 void
  288 t4_hold_clip_entry(struct adapter *sc, struct clip_entry *ce)
  289 {
  290 #ifdef INET6
  291         MPASS(ce != NULL);
  292         MPASS(ce->cde->adp_ref > 0);
  293 
  294         mtx_lock(&clip_db_lock);
  295         MPASS(ce->refcount > 0); /* Caller should already have a reference */
  296         ce->refcount++;
  297         mtx_unlock(&clip_db_lock);
  298 #endif
  299 }
  300 
  301 #ifdef INET6
  302 static void
  303 release_clip_entry_locked(struct adapter *sc, struct clip_entry *ce)
  304 {
  305         struct clip_db_entry *cde;
  306 
  307         mtx_assert(&clip_db_lock, MA_OWNED);
  308         MPASS(ce->refcount > 0);
  309         cde = ce->cde;
  310         MPASS(cde->adp_ref > 0);
  311         if (--ce->refcount == 0 && cde->krn_ref == 0) {
  312                 if (ce->clip_idx == -1) {
  313                         /* Was never written to the hardware. */
  314                         MPASS(ce->pending);
  315                         TAILQ_REMOVE(&sc->clip_pending, ce, plink);
  316                         LIST_REMOVE(ce, link);
  317                         free(ce, M_CXGBE);
  318                         if (--cde->adp_ref == 0) {
  319                                 LIST_REMOVE(cde, link);
  320                                 free(cde, M_CXGBE);
  321                         }
  322                 } else {
  323                         /*
  324                          * Valid entry is now unused, add to the pending list
  325                          * for deletion.  Its refcount was 1 on entry so it
  326                          * can't already be pending.
  327                          */
  328                         MPASS(!ce->pending);
  329                         TAILQ_INSERT_HEAD(&sc->clip_pending, ce, plink);
  330                         ce->pending = true;
  331                 }
  332         }
  333 }
  334 #endif
  335 
  336 void
  337 t4_release_clip_entry(struct adapter *sc, struct clip_entry *ce)
  338 {
  339 #ifdef INET6
  340         MPASS(ce != NULL);
  341 
  342         mtx_lock(&clip_db_lock);
  343         release_clip_entry_locked(sc, ce);
  344         /*
  345          * This isn't a manual release via the ioctl.  No need to update the
  346          * hw right now even if the release resulted in the entry being queued
  347          * for deletion.
  348          */
  349         mtx_unlock(&clip_db_lock);
  350 #endif
  351 }
  352 
  353 int
  354 t4_release_clip_addr(struct adapter *sc, struct in6_addr *in6)
  355 {
  356         int rc = ENOTSUP;
  357 #ifdef INET6
  358         struct clip_entry *ce;
  359         bool schedule = false;
  360 
  361         mtx_lock(&clip_db_lock);
  362         ce = lookup_clip_entry(sc, in6, false);
  363         if (ce == NULL)
  364                 rc = ENOENT;
  365         else if (ce->refcount == 0)
  366                 rc = EIO;
  367         else {
  368                 release_clip_entry_locked(sc, ce);
  369                 if (update_hw_clip_table(sc) != 0)
  370                         schedule = true;
  371                 rc = 0;
  372         }
  373         mtx_unlock(&clip_db_lock);
  374         if (schedule)
  375                 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task, 0);
  376 #endif
  377         return (rc);
  378 }
  379 
  380 #ifdef INET6
  381 void
  382 t4_init_clip_table(struct adapter *sc)
  383 {
  384         TAILQ_INIT(&sc->clip_pending);
  385         TIMEOUT_TASK_INIT(taskqueue_thread, &sc->clip_task, 0, t4_clip_task, sc);
  386         sc->clip_gen = -1;
  387         sc->clip_table = hashinit(CLIP_HASH_SIZE, M_CXGBE, &sc->clip_mask);
  388 
  389         /* Both the hashes must use the same bucket for the same key. */
  390         if (sc->clip_table != NULL)
  391                 MPASS(sc->clip_mask == clip_db_mask);
  392         /*
  393          * Don't bother forcing an update of the clip table when the
  394          * adapter is initialized.  Before an interface can be used it
  395          * must be assigned an address which will trigger the event
  396          * handler to update the table.
  397          */
  398 }
  399 
  400 /*
  401  * Returns true if any additions or deletions were made to the CLIP DB.
  402  */
  403 static void
  404 update_clip_db(void)
  405 {
  406         VNET_ITERATOR_DECL(vnet_iter);
  407         struct rm_priotracker in6_ifa_tracker;
  408         struct in6_addr *in6, tin6;
  409         struct in6_ifaddr *ia;
  410         struct clip_db_entry *cde, *cde_tmp;
  411         int i, addel;
  412 
  413         VNET_LIST_RLOCK();
  414         IN6_IFADDR_RLOCK(&in6_ifa_tracker);
  415         mtx_lock(&clip_db_lock);
  416         VNET_FOREACH(vnet_iter) {
  417                 CURVNET_SET_QUIET(vnet_iter);
  418                 CK_STAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
  419                         if (ia->ia_ifp->if_flags & IFF_LOOPBACK)
  420                                 continue;
  421                         in6 = &ia->ia_addr.sin6_addr;
  422                         KASSERT(!IN6_IS_ADDR_MULTICAST(in6),
  423                             ("%s: mcast address in in6_ifaddr list", __func__));
  424                         if (IN6_IS_ADDR_LOOPBACK(in6))
  425                                 continue;
  426 
  427                         if (IN6_IS_SCOPE_EMBED(in6)) {
  428                                 tin6 = *in6;
  429                                 in6 = &tin6;
  430                                 in6_clearscope(in6);
  431                         }
  432                         cde = lookup_clip_db_entry(in6, true);
  433                         if (cde == NULL)
  434                                 continue;
  435                         cde->tmp_ref++;
  436                 }
  437                 CURVNET_RESTORE();
  438         }
  439 
  440         addel = 0;
  441         for (i = 0; i <= clip_db_mask; i++) {
  442                 LIST_FOREACH_SAFE(cde, &clip_db[i], link, cde_tmp) {
  443                         if (cde->krn_ref == 0 && cde->tmp_ref > 0) {
  444                                 addel++;        /* IP6 addr added. */
  445                         } else if (cde->krn_ref > 0 && cde->tmp_ref == 0) {
  446                                 if (cde->adp_ref == 0) {
  447                                         LIST_REMOVE(cde, link);
  448                                         free(cde, M_CXGBE);
  449                                         continue;
  450                                 }
  451                                 addel++;        /* IP6 addr deleted. */
  452                         }
  453                         cde->krn_ref = cde->tmp_ref;
  454                         cde->tmp_ref = 0;
  455                 }
  456         }
  457         if (addel > 0)
  458                 clip_db_gen++;
  459         mtx_unlock(&clip_db_lock);
  460         IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
  461         VNET_LIST_RUNLOCK();
  462 
  463 }
  464 
  465 /*
  466  * Update the CLIP db and then update the CLIP tables on all the adapters.
  467  */
  468 static void
  469 t4_clip_db_task(void *arg, int count)
  470 {
  471         update_clip_db();
  472         t4_iterate(update_clip_table, NULL);
  473 }
  474 
  475 /*
  476  * Refresh the sw CLIP table for this adapter from the global CLIP db.  Entries
  477  * that need to be added or deleted from the hardware CLIP table are placed on a
  478  * pending list but the hardware is not touched.  The pending list is something
  479  * reasonable even if this fails so it's ok to apply that to the hardware.
  480  */
  481 static int
  482 update_sw_clip_table(struct adapter *sc)
  483 {
  484         struct clip_db_entry *cde;
  485         struct clip_entry *ce, *ce_temp;
  486         int i;
  487         bool found;
  488 
  489         mtx_assert(&clip_db_lock, MA_OWNED);
  490 
  491         /*
  492          * We are about to rebuild the pending list from scratch.  Deletions are
  493          * placed before additions because that's how we want to submit them to
  494          * the hardware.
  495          */
  496         TAILQ_INIT(&sc->clip_pending);
  497 
  498         /*
  499          * Walk the sw CLIP table first.  We want to reset every entry's pending
  500          * status as we're rebuilding the pending list.
  501          */
  502         for (i = 0; i <= clip_db_mask; i++) {
  503                 LIST_FOREACH_SAFE(ce, &sc->clip_table[i], link, ce_temp) {
  504                         cde = ce->cde;
  505                         MPASS(cde->adp_ref > 0);
  506                         if (ce->refcount != 0 || cde->krn_ref != 0) {
  507                                 /*
  508                                  * Entry should stay in the CLIP.
  509                                  */
  510 
  511                                 if (ce->clip_idx != -1) {
  512                                         ce->pending = false;
  513                                 } else {
  514                                         /* Was never added, carry forward. */
  515                                         MPASS(ce->pending);
  516                                         TAILQ_INSERT_TAIL(&sc->clip_pending, ce,
  517                                             plink);
  518                                 }
  519                                 continue;
  520                         }
  521 
  522                         /*
  523                          * Entry should be removed from the CLIP.
  524                          */
  525 
  526                         if (ce->clip_idx != -1) {
  527                                 ce->pending = true;
  528                                 TAILQ_INSERT_HEAD(&sc->clip_pending, ce, plink);
  529                         } else {
  530                                 /* Was never added, free right now. */
  531                                 MPASS(ce->pending);
  532                                 LIST_REMOVE(ce, link);
  533                                 free(ce, M_CXGBE);
  534                                 if (--cde->adp_ref == 0) {
  535                                         LIST_REMOVE(cde, link);
  536                                         free(cde, M_CXGBE);
  537                                 }
  538                         }
  539                 }
  540         }
  541 
  542         for (i = 0; i <= clip_db_mask; i++) {
  543                 LIST_FOREACH(cde, &clip_db[i], link) {
  544                         if (cde->krn_ref == 0)
  545                                 continue;
  546 
  547                         found = false;
  548                         LIST_FOREACH(ce, &sc->clip_table[i], link) {
  549                                 if (ce->cde == cde) {
  550                                         found = true;
  551                                         break;
  552                                 }
  553                         }
  554                         if (found)
  555                                 continue;
  556                         ce = alloc_clip_entry(cde);
  557                         if (ce == NULL)
  558                                 return (ENOMEM);
  559                         LIST_INSERT_HEAD(&sc->clip_table[i], ce, link);
  560                         TAILQ_INSERT_TAIL(&sc->clip_pending, ce, plink);
  561                         ce->pending = true;
  562                 }
  563         }
  564 
  565         sc->clip_gen = clip_db_gen;
  566         return (0);
  567 }
  568 
  569 static int
  570 update_hw_clip_table(struct adapter *sc)
  571 {
  572         struct clip_db_entry *cde;
  573         struct clip_entry *ce;
  574         int rc;
  575         char ip[INET6_ADDRSTRLEN];
  576 
  577         mtx_assert(&clip_db_lock, MA_OWNED);
  578         rc = begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4clip");
  579         if (rc != 0)
  580                 return (rc);
  581         if (hw_off_limits(sc))
  582                 goto done;      /* with rc = 0, we don't want to reschedule. */
  583         while (!TAILQ_EMPTY(&sc->clip_pending)) {
  584                 ce = TAILQ_FIRST(&sc->clip_pending);
  585                 MPASS(ce->pending);
  586                 cde = ce->cde;
  587                 MPASS(cde->adp_ref > 0);
  588 
  589                 if (ce->clip_idx == -1) {
  590                         /*
  591                          * Entry was queued for addition to the HW CLIP.
  592                          */
  593 
  594                         if (ce->refcount == 0 && cde->krn_ref == 0) {
  595                                 /* No need to add to HW CLIP. */
  596                                 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
  597                                 LIST_REMOVE(ce, link);
  598                                 free(ce, M_CXGBE);
  599                                 if (--cde->adp_ref == 0) {
  600                                         LIST_REMOVE(cde, link);
  601                                         free(cde, M_CXGBE);
  602                                 }
  603                         } else {
  604                                 /* Add to the HW CLIP. */
  605                                 rc = add_lip(sc, &cde->lip, &ce->clip_idx);
  606                                 if (rc == FW_ENOMEM) {
  607                                         /* CLIP full, no point in retrying. */
  608                                         rc = 0;
  609                                         goto done;
  610                                 }
  611                                 if (rc != 0) {
  612                                         inet_ntop(AF_INET6, &cde->lip, &ip[0],
  613                                             sizeof(ip));
  614                                         CH_ERR(sc, "add_lip(%s) failed: %d\n",
  615                                             ip, rc);
  616                                         goto done;
  617                                 }
  618                                 MPASS(ce->clip_idx != -1);
  619                                 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
  620                                 ce->pending = false;
  621                         }
  622                 } else {
  623                         /*
  624                          * Entry was queued for deletion from the HW CLIP.
  625                          */
  626 
  627                         if (ce->refcount == 0 && cde->krn_ref == 0) {
  628                                 /*
  629                                  * Delete from the HW CLIP.  Delete should never
  630                                  * fail so we always log an error.  But if the
  631                                  * failure is that the entry wasn't found in the
  632                                  * CLIP then we carry on as if it was deleted.
  633                                  */
  634                                 rc = del_lip(sc, &cde->lip);
  635                                 if (rc != 0)
  636                                         CH_ERR(sc, "del_lip(%s) failed: %d\n",
  637                                             ip, rc);
  638                                 if (rc == FW_EPROTO)
  639                                         rc = 0;
  640                                 if (rc != 0)
  641                                         goto done;
  642 
  643                                 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
  644                                 LIST_REMOVE(ce, link);
  645                                 free(ce, M_CXGBE);
  646                                 if (--cde->adp_ref == 0) {
  647                                         LIST_REMOVE(cde, link);
  648                                         free(cde, M_CXGBE);
  649                                 }
  650                         } else {
  651                                 /* No need to delete from HW CLIP. */
  652                                 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
  653                                 ce->pending = false;
  654                         }
  655                 }
  656         }
  657 done:
  658         end_synchronized_op(sc, LOCK_HELD);
  659         return (rc);
  660 }
  661 
  662 static void
  663 update_clip_table(struct adapter *sc, void *arg __unused)
  664 {
  665         bool reschedule;
  666 
  667         if (sc->clip_table == NULL)
  668                 return;
  669 
  670         reschedule = false;
  671         mtx_lock(&clip_db_lock);
  672         if (sc->clip_gen != clip_db_gen && update_sw_clip_table(sc) != 0)
  673                 reschedule = true;
  674         if (!TAILQ_EMPTY(&sc->clip_pending) && update_hw_clip_table(sc) != 0)
  675                 reschedule = true;
  676         mtx_unlock(&clip_db_lock);
  677         if (reschedule)
  678                 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task,
  679                     -hz / 4);
  680 }
  681 
  682 /*
  683  * Update the CLIP table of the specified adapter.
  684  */
  685 static void
  686 t4_clip_task(void *sc, int count)
  687 {
  688         update_clip_table(sc, NULL);
  689 }
  690 
  691 void
  692 t4_destroy_clip_table(struct adapter *sc)
  693 {
  694         struct clip_entry *ce, *ce_temp;
  695         int i;
  696 
  697         mtx_lock(&clip_db_lock);
  698         if (sc->clip_table == NULL)
  699                 goto done;              /* CLIP was never initialized. */
  700         for (i = 0; i <= sc->clip_mask; i++) {
  701                 LIST_FOREACH_SAFE(ce, &sc->clip_table[i], link, ce_temp) {
  702                         MPASS(ce->refcount == 0);
  703                         MPASS(ce->cde->adp_ref > 0);
  704 #if 0
  705                         del_lip(sc, &ce->lip);
  706 #endif
  707                         LIST_REMOVE(ce, link);
  708                         if (--ce->cde->adp_ref == 0 && ce->cde->krn_ref == 0) {
  709                                 LIST_REMOVE(ce->cde, link);
  710                                 free(ce->cde, M_CXGBE);
  711                         }
  712                         free(ce, M_CXGBE);
  713                 }
  714         }
  715         hashdestroy(sc->clip_table, M_CXGBE, sc->clip_mask);
  716         sc->clip_table = NULL;
  717 done:
  718         mtx_unlock(&clip_db_lock);
  719 }
  720 
  721 static void
  722 t4_ifaddr_event(void *arg __unused, struct ifnet *ifp, struct ifaddr *ifa,
  723     int event)
  724 {
  725         struct in6_addr *in6;
  726 
  727         if (t4_clip_db_auto == 0)
  728                 return;         /* Automatic updates not allowed. */
  729         if (ifa->ifa_addr->sa_family != AF_INET6)
  730                 return;
  731         if (ifp->if_flags & IFF_LOOPBACK)
  732                 return;
  733         in6 = &((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr;
  734         if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_MULTICAST(in6))
  735                 return;
  736 
  737         taskqueue_enqueue(taskqueue_thread, &clip_db_task);
  738 }
  739 
  740 int
  741 sysctl_clip(SYSCTL_HANDLER_ARGS)
  742 {
  743         struct adapter *sc = arg1;
  744         struct clip_entry *ce;
  745         struct sbuf *sb;
  746         int i, rc, header = 0;
  747         char ip[INET6_ADDRSTRLEN];
  748 
  749         rc = sysctl_wire_old_buffer(req, 0);
  750         if (rc != 0)
  751                 return (rc);
  752 
  753         sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
  754         if (sb == NULL)
  755                 return (ENOMEM);
  756 
  757         mtx_lock(&clip_db_lock);
  758         for (i = 0; i <= sc->clip_mask; i++) {
  759                 LIST_FOREACH(ce, &sc->clip_table[i], link) {
  760                         if (header == 0) {
  761                                 sbuf_printf(sb, "%-4s %-4s %s", "Indx", "Refs",
  762                                     "IP address");
  763                                 header = 1;
  764                         }
  765                         inet_ntop(AF_INET6, &ce->cde->lip, &ip[0], sizeof(ip));
  766                         if (ce->clip_idx == -1) {
  767                                 sbuf_printf(sb, "\n%-4s %-4d %s", "-",
  768                                     ce->refcount, ip);
  769                         } else {
  770                                 sbuf_printf(sb, "\n%-4d %-4d %s", ce->clip_idx,
  771                                     ce->refcount, ip);
  772                         }
  773                 }
  774         }
  775         mtx_unlock(&clip_db_lock);
  776 
  777         rc = sbuf_finish(sb);
  778         sbuf_delete(sb);
  779 
  780         return (rc);
  781 }
  782 
  783 static int
  784 sysctl_clip_db(SYSCTL_HANDLER_ARGS)
  785 {
  786         struct clip_db_entry *cde;
  787         struct sbuf *sb;
  788         int i, rc, header = 0;
  789         char ip[INET6_ADDRSTRLEN];
  790 
  791         rc = sysctl_wire_old_buffer(req, 0);
  792         if (rc != 0)
  793                 return (rc);
  794 
  795         sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
  796         if (sb == NULL)
  797                 return (ENOMEM);
  798 
  799         mtx_lock(&clip_db_lock);
  800         for (i = 0; i <= clip_db_mask; i++) {
  801                 LIST_FOREACH(cde, &clip_db[i], link) {
  802                         MPASS(cde->tmp_ref == 0);
  803                         if (header == 0) {
  804                                 sbuf_printf(sb, "%-4s %-4s %s", "Kref", "Aref",
  805                                     "IP address");
  806                                 header = 1;
  807                         }
  808                         inet_ntop(AF_INET6, &cde->lip, &ip[0], sizeof(ip));
  809                         sbuf_printf(sb, "\n%-4d %-4d %s", cde->krn_ref,
  810                             cde->adp_ref, ip);
  811                 }
  812         }
  813         mtx_unlock(&clip_db_lock);
  814 
  815         rc = sbuf_finish(sb);
  816         sbuf_delete(sb);
  817 
  818         return (rc);
  819 }
  820 
  821 static int
  822 sysctl_clip_db_auto(SYSCTL_HANDLER_ARGS)
  823 {
  824         int rc, val;
  825 
  826         val = t4_clip_db_auto;
  827         rc = sysctl_handle_int(oidp, &val, 0, req);
  828         if (rc != 0 || req->newptr == NULL)
  829                 return (rc);
  830 
  831         if (val == 0 || val == 1)
  832                 t4_clip_db_auto = val;
  833         else {
  834                 /*
  835                  * Writing a value other than 0 or 1 forces a one-time update of
  836                  * the clip_db directly in the sysctl and not in some taskqueue.
  837                  */
  838                 t4_clip_db_task(NULL, 0);
  839         }
  840 
  841         return (0);
  842 }
  843 
  844 void
  845 t4_clip_modload(void)
  846 {
  847         mtx_init(&clip_db_lock, "clip_db", NULL, MTX_DEF);
  848         clip_db = hashinit(CLIP_HASH_SIZE, M_CXGBE, &clip_db_mask);
  849         TASK_INIT(&clip_db_task, 0, t4_clip_db_task, NULL);
  850         ifaddr_evhandler = EVENTHANDLER_REGISTER(ifaddr_event_ext,
  851             t4_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY);
  852 }
  853 
  854 void
  855 t4_clip_modunload(void)
  856 {
  857         struct clip_db_entry *cde;
  858         int i;
  859 
  860         EVENTHANDLER_DEREGISTER(ifaddr_event_ext, ifaddr_evhandler);
  861         taskqueue_drain(taskqueue_thread, &clip_db_task);
  862         mtx_lock(&clip_db_lock);
  863         for (i = 0; i <= clip_db_mask; i++) {
  864                 while ((cde = LIST_FIRST(&clip_db[i])) != NULL) {
  865                         MPASS(cde->tmp_ref == 0);
  866                         MPASS(cde->adp_ref == 0);
  867                         LIST_REMOVE(cde, link);
  868                         free(cde, M_CXGBE);
  869                 }
  870         }
  871         mtx_unlock(&clip_db_lock);
  872         hashdestroy(clip_db, M_CXGBE, clip_db_mask);
  873         mtx_destroy(&clip_db_lock);
  874 }
  875 #endif

Cache object: 81b12dd74d91acf1167de05fcc15221c


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.