Author: np
Date: Sat Sep 22 01:24:30 2018
New Revision: 338874
URL: https://svnweb.freebsd.org/changeset/base/338874

Log:
  cxgbe(4): Reuse existing "switching" L2T entries when possible.
  
  Approved by:  re@ (rgrimes@)
  Sponsored by: Chelsio Communications

Modified:
  head/sys/dev/cxgbe/t4_filter.c
  head/sys/dev/cxgbe/t4_l2t.c
  head/sys/dev/cxgbe/t4_l2t.h

Modified: head/sys/dev/cxgbe/t4_filter.c
==============================================================================
--- head/sys/dev/cxgbe/t4_filter.c      Fri Sep 21 23:54:01 2018        
(r338873)
+++ head/sys/dev/cxgbe/t4_filter.c      Sat Sep 22 01:24:30 2018        
(r338874)
@@ -593,13 +593,8 @@ set_tcamfilter(struct adapter *sc, struct t4_filter *t
                }
        }
        mtx_unlock(&sc->tids.ftid_lock);
-       if (rc != 0) {
-               if (l2te)
-                       t4_l2t_release(l2te);
-               if (smt)
-                       t4_smt_release(smt);
+       if (rc != 0)
                return (rc);
-       }
 
        /*
         * Can't fail now.  A set-filter WR will definitely be sent.
@@ -817,8 +812,8 @@ int
 set_filter(struct adapter *sc, struct t4_filter *t)
 {
        struct tid_info *ti = &sc->tids;
-       struct l2t_entry *l2te;
-       struct smt_entry *smt;
+       struct l2t_entry *l2te = NULL;
+       struct smt_entry *smt = NULL;
        uint64_t ftuple;
        int rc;
 
@@ -942,43 +937,41 @@ done:
         * Allocate L2T entry, SMT entry, etc.
         */
 
-       l2te = NULL;
        if (t->fs.newdmac || t->fs.newvlan) {
                /* This filter needs an L2T entry; allocate one. */
-               l2te = t4_l2t_alloc_switching(sc->l2t);
-               if (__predict_false(l2te == NULL))
-                       return (EAGAIN);
-               rc = t4_l2t_set_switching(sc, l2te, t->fs.vlan, t->fs.eport,
+               l2te = t4_l2t_alloc_switching(sc, t->fs.vlan, t->fs.eport,
                    t->fs.dmac);
-               if (rc) {
-                       t4_l2t_release(l2te);
-                       return (ENOMEM);
+               if (__predict_false(l2te == NULL)) {
+                       rc = EAGAIN;
+                       goto error;
                }
        }
 
-       smt = NULL;
        if (t->fs.newsmac) {
                /* This filter needs an SMT entry; allocate one. */
                smt = t4_smt_alloc_switching(sc->smt, t->fs.smac);
                if (__predict_false(smt == NULL)) {
-                       if (l2te != NULL)
-                               t4_l2t_release(l2te);
-                       return (EAGAIN);
+                       rc = EAGAIN;
+                       goto error;
                }
                rc = t4_smt_set_switching(sc, smt, 0x0, t->fs.smac);
-               if (rc) {
-                       t4_smt_release(smt);
-                       if (l2te != NULL)
-                               t4_l2t_release(l2te);
-                       return (rc);
-               }
+               if (rc)
+                       goto error;
        }
 
        if (t->fs.hash)
-               return (set_hashfilter(sc, t, ftuple, l2te, smt));
+               rc = set_hashfilter(sc, t, ftuple, l2te, smt);
        else
-               return (set_tcamfilter(sc, t, l2te, smt));
+               rc = set_tcamfilter(sc, t, l2te, smt);
 
+       if (rc != 0 && rc != EINPROGRESS) {
+error:
+               if (l2te)
+                       t4_l2t_release(l2te);
+               if (smt)
+                       t4_smt_release(smt);
+       }
+       return (rc);
 }
 
 static int
@@ -1552,10 +1545,6 @@ set_hashfilter(struct adapter *sc, struct t4_filter *t
 
        f = malloc(sizeof(*f), M_CXGBE, M_ZERO | M_NOWAIT);
        if (__predict_false(f == NULL)) {
-               if (l2te)
-                       t4_l2t_release(l2te);
-               if (smt)
-                       t4_smt_release(smt);
                rc = ENOMEM;
                goto done;
        }
@@ -1565,10 +1554,6 @@ set_hashfilter(struct adapter *sc, struct t4_filter *t
 
        atid = alloc_atid(sc, f);
        if (__predict_false(atid) == -1) {
-               if (l2te)
-                       t4_l2t_release(l2te);
-               if (smt)
-                       t4_smt_release(smt);
                free(f, M_CXGBE);
                rc = EAGAIN;
                goto done;
@@ -1579,10 +1564,6 @@ set_hashfilter(struct adapter *sc, struct t4_filter *t
            &cookie);
        if (wr == NULL) {
                free_atid(sc, atid);
-               if (l2te)
-                       t4_l2t_release(l2te);
-               if (smt)
-                       t4_smt_release(smt);
                free(f, M_CXGBE);
                rc = ENOMEM;
                goto done;

Modified: head/sys/dev/cxgbe/t4_l2t.c
==============================================================================
--- head/sys/dev/cxgbe/t4_l2t.c Fri Sep 21 23:54:01 2018        (r338873)
+++ head/sys/dev/cxgbe/t4_l2t.c Sat Sep 22 01:24:30 2018        (r338874)
@@ -108,6 +108,44 @@ found:
        return (e);
 }
 
+static struct l2t_entry *
+find_or_alloc_l2e(struct l2t_data *d, uint16_t vlan, uint8_t port, uint8_t 
*dmac)
+{
+       struct l2t_entry *end, *e, **p;
+       struct l2t_entry *first_free = NULL;
+
+       for (e = &d->l2tab[0], end = &d->l2tab[d->l2t_size]; e != end; ++e) {
+               if (atomic_load_acq_int(&e->refcnt) == 0) {
+                       if (!first_free)
+                               first_free = e;
+               } else if (e->state == L2T_STATE_SWITCHING &&
+                   memcmp(e->dmac, dmac, ETHER_ADDR_LEN) == 0 &&
+                   e->vlan == vlan && e->lport == port)
+                       return (e);     /* Found existing entry that matches. */
+       }
+
+       if (first_free == NULL)
+               return (NULL);  /* No match and no room for a new entry. */
+
+       /*
+        * The entry we found may be an inactive entry that is
+        * presently in the hash table.  We need to remove it.
+        */
+       e = first_free;
+       if (e->state < L2T_STATE_SWITCHING) {
+               for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next) {
+                       if (*p == e) {
+                               *p = e->next;
+                               e->next = NULL;
+                               break;
+                       }
+               }
+       }
+       e->state = L2T_STATE_UNUSED;
+       return (e);
+}
+
+
 /*
  * Write an L2T entry.  Must be called with the entry locked.
  * The write may be synchronous or asynchronous.
@@ -154,41 +192,38 @@ t4_write_l2e(struct l2t_entry *e, int sync)
  * address resolution updates do not see them.
  */
 struct l2t_entry *
-t4_l2t_alloc_switching(struct l2t_data *d)
+t4_l2t_alloc_switching(struct adapter *sc, uint16_t vlan, uint8_t port,
+    uint8_t *eth_addr)
 {
+       struct l2t_data *d = sc->l2t;
        struct l2t_entry *e;
+       int rc;
 
        rw_wlock(&d->lock);
-       e = t4_alloc_l2e(d);
+       e = find_or_alloc_l2e(d, vlan, port, eth_addr);
        if (e) {
-               mtx_lock(&e->lock);          /* avoid race with t4_l2t_free */
-               e->state = L2T_STATE_SWITCHING;
-               atomic_store_rel_int(&e->refcnt, 1);
-               mtx_unlock(&e->lock);
+               if (atomic_load_acq_int(&e->refcnt) == 0) {
+                       mtx_lock(&e->lock);    /* avoid race with t4_l2t_free */
+                       e->wrq = &sc->sge.ctrlq[0];
+                       e->iqid = sc->sge.fwq.abs_id;
+                       e->state = L2T_STATE_SWITCHING;
+                       e->vlan = vlan;
+                       e->lport = port;
+                       memcpy(e->dmac, eth_addr, ETHER_ADDR_LEN);
+                       atomic_store_rel_int(&e->refcnt, 1);
+                       atomic_subtract_int(&d->nfree, 1);
+                       rc = t4_write_l2e(e, 0);
+                       mtx_unlock(&e->lock);
+                       if (rc != 0)
+                               e = NULL;
+               } else {
+                       MPASS(e->vlan == vlan);
+                       MPASS(e->lport == port);
+                       atomic_add_int(&e->refcnt, 1);
+               }
        }
        rw_wunlock(&d->lock);
-       return e;
-}
-
-/*
- * Sets/updates the contents of a switching L2T entry that has been allocated
- * with an earlier call to @t4_l2t_alloc_switching.
- */
-int
-t4_l2t_set_switching(struct adapter *sc, struct l2t_entry *e, uint16_t vlan,
-    uint8_t port, uint8_t *eth_addr)
-{
-       int rc;
-
-       e->vlan = vlan;
-       e->lport = port;
-       e->wrq = &sc->sge.ctrlq[0];
-       e->iqid = sc->sge.fwq.abs_id;
-       memcpy(e->dmac, eth_addr, ETHER_ADDR_LEN);
-       mtx_lock(&e->lock);
-       rc = t4_write_l2e(e, 0);
-       mtx_unlock(&e->lock);
-       return (rc);
+       return (e);
 }
 
 int

Modified: head/sys/dev/cxgbe/t4_l2t.h
==============================================================================
--- head/sys/dev/cxgbe/t4_l2t.h Fri Sep 21 23:54:01 2018        (r338873)
+++ head/sys/dev/cxgbe/t4_l2t.h Sat Sep 22 01:24:30 2018        (r338874)
@@ -91,7 +91,8 @@ struct l2t_data {
 int t4_init_l2t(struct adapter *, int);
 int t4_free_l2t(struct l2t_data *);
 struct l2t_entry *t4_alloc_l2e(struct l2t_data *);
-struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *);
+struct l2t_entry *t4_l2t_alloc_switching(struct adapter *, uint16_t, uint8_t,
+    uint8_t *);
 int t4_l2t_set_switching(struct adapter *, struct l2t_entry *, uint16_t,
     uint8_t, uint8_t *);
 int t4_write_l2e(struct l2t_entry *, int);
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to