Author: np
Date: Mon Jan 14 20:36:22 2013
New Revision: 245434
URL: http://svnweb.freebsd.org/changeset/base/245434

Log:
  cxgbe(4): Updates to the hardware L2 table management code.
  
  - Add full support for IPv6 addresses.
  
  - Read the size of the L2 table during attach.  Do not assume that PCIe
    physical function 4 of the card has all of the table to itself.
  
  - Use FNV instead of Jenkins to hash L3 addresses and drop the private
    copy of jhash.h from the driver.
  
  MFC after:    1 week

Deleted:
  head/sys/dev/cxgbe/common/jhash.h
Modified:
  head/sys/dev/cxgbe/offload.h
  head/sys/dev/cxgbe/t4_l2t.c
  head/sys/dev/cxgbe/t4_l2t.h
  head/sys/dev/cxgbe/t4_main.c
  head/sys/dev/cxgbe/tom/t4_tom_l2t.c
  head/sys/modules/cxgbe/tom/Makefile

Modified: head/sys/dev/cxgbe/offload.h
==============================================================================
--- head/sys/dev/cxgbe/offload.h        Mon Jan 14 20:31:45 2013        
(r245433)
+++ head/sys/dev/cxgbe/offload.h        Mon Jan 14 20:36:22 2013        
(r245434)
@@ -104,8 +104,8 @@ struct tid_info {
 };
 
 struct t4_range {
-       unsigned int start;
-       unsigned int size;
+       u_int start;
+       u_int size;
 };
 
 struct t4_virt_res {                      /* virtualized HW resources */
@@ -117,6 +117,7 @@ struct t4_virt_res {                    
        struct t4_range qp;
        struct t4_range cq;
        struct t4_range ocq;
+       struct t4_range l2t;
 };
 
 #ifdef TCP_OFFLOAD

Modified: head/sys/dev/cxgbe/t4_l2t.c
==============================================================================
--- head/sys/dev/cxgbe/t4_l2t.c Mon Jan 14 20:31:45 2013        (r245433)
+++ head/sys/dev/cxgbe/t4_l2t.c Mon Jan 14 20:36:22 2013        (r245434)
@@ -42,7 +42,6 @@ __FBSDID("$FreeBSD$");
 #include <netinet/in.h>
 
 #include "common/common.h"
-#include "common/jhash.h"
 #include "common/t4_msg.h"
 #include "t4_l2t.h"
 
@@ -78,7 +77,7 @@ t4_alloc_l2e(struct l2t_data *d)
                return (NULL);
 
        /* there's definitely a free entry */
-       for (e = d->rover, end = &d->l2tab[L2T_SIZE]; e != end; ++e)
+       for (e = d->rover, end = &d->l2tab[d->l2t_size]; e != end; ++e)
                if (atomic_load_acq_int(&e->refcnt) == 0)
                        goto found;
 
@@ -115,6 +114,7 @@ t4_write_l2e(struct adapter *sc, struct 
 {
        struct wrqe *wr;
        struct cpl_l2t_write_req *req;
+       int idx = e->idx + sc->vres.l2t.start;
 
        mtx_assert(&e->lock, MA_OWNED);
 
@@ -124,10 +124,10 @@ t4_write_l2e(struct adapter *sc, struct 
        req = wrtod(wr);
 
        INIT_TP_WR(req, 0);
-       OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, e->idx |
+       OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, idx |
            V_SYNC_WR(sync) | V_TID_QID(sc->sge.fwq.abs_id)));
        req->params = htons(V_L2T_W_PORT(e->lport) | V_L2T_W_NOREPLY(!sync));
-       req->l2t_idx = htons(e->idx);
+       req->l2t_idx = htons(idx);
        req->vlan = htons(e->vlan);
        memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac));
 
@@ -183,18 +183,24 @@ t4_l2t_set_switching(struct adapter *sc,
 int
 t4_init_l2t(struct adapter *sc, int flags)
 {
-       int i;
+       int i, l2t_size;
        struct l2t_data *d;
 
-       d = malloc(sizeof(*d), M_CXGBE, M_ZERO | flags);
+       l2t_size = sc->vres.l2t.size;
+       if (l2t_size < 2)       /* At least 1 bucket for IP and 1 for IPv6 */
+               return (EINVAL);
+
+       d = malloc(sizeof(*d) + l2t_size * sizeof (struct l2t_entry), M_CXGBE,
+           M_ZERO | flags);
        if (!d)
                return (ENOMEM);
 
+       d->l2t_size = l2t_size;
        d->rover = d->l2tab;
-       atomic_store_rel_int(&d->nfree, L2T_SIZE);
+       atomic_store_rel_int(&d->nfree, l2t_size);
        rw_init(&d->lock, "L2T");
 
-       for (i = 0; i < L2T_SIZE; i++) {
+       for (i = 0; i < l2t_size; i++) {
                struct l2t_entry *e = &d->l2tab[i];
 
                e->idx = i;
@@ -215,7 +221,7 @@ t4_free_l2t(struct l2t_data *d)
 {
        int i;
 
-       for (i = 0; i < L2T_SIZE; i++)
+       for (i = 0; i < d->l2t_size; i++)
                mtx_destroy(&d->l2tab[i].lock);
        rw_destroy(&d->lock);
        free(d, M_CXGBE);
@@ -229,11 +235,11 @@ do_l2t_write_rpl(struct sge_iq *iq, cons
 {
        const struct cpl_l2t_write_rpl *rpl = (const void *)(rss + 1);
        unsigned int tid = GET_TID(rpl);
-       unsigned int idx = tid & (L2T_SIZE - 1);
+       unsigned int idx = tid % L2T_SIZE;
 
        if (__predict_false(rpl->status != CPL_ERR_NONE)) {
                log(LOG_ERR,
-                   "Unexpected L2T_WRITE_RPL status %u for entry %u\n",
+                   "Unexpected L2T_WRITE_RPL (%u) for entry at hw_idx %u\n",
                    rpl->status, idx);
                return (EINVAL);
        }
@@ -269,7 +275,7 @@ sysctl_l2t(SYSCTL_HANDLER_ARGS)
        struct l2t_entry *e;
        struct sbuf *sb;
        int rc, i, header = 0;
-       char ip[60];
+       char ip[INET6_ADDRSTRLEN];
 
        if (l2t == NULL)
                return (ENXIO);
@@ -283,7 +289,7 @@ sysctl_l2t(SYSCTL_HANDLER_ARGS)
                return (ENOMEM);
 
        e = &l2t->l2tab[0];
-       for (i = 0; i < L2T_SIZE; i++, e++) {
+       for (i = 0; i < l2t->l2t_size; i++, e++) {
                mtx_lock(&e->lock);
                if (e->state == L2T_STATE_UNUSED)
                        goto skip;
@@ -295,11 +301,15 @@ sysctl_l2t(SYSCTL_HANDLER_ARGS)
                }
                if (e->state == L2T_STATE_SWITCHING)
                        ip[0] = 0;
-               else
-                       snprintf(ip, sizeof(ip), "%s",
-                           inet_ntoa(*(struct in_addr *)&e->addr));
+               else {
+                       inet_ntop(e->ipv6 ? AF_INET6 : AF_INET, &e->addr[0],
+                           &ip[0], sizeof(ip));
+               }
 
-               /* XXX: e->ifp may not be around */
+               /*
+                * XXX: e->ifp may not be around.
+                * XXX: IPv6 addresses may not align properly in the output.
+                */
                sbuf_printf(sb, "\n%4u %-15s %02x:%02x:%02x:%02x:%02x:%02x %4d"
                           " %u %2u   %c   %5u %s",
                           e->idx, ip, e->dmac[0], e->dmac[1], e->dmac[2],

Modified: head/sys/dev/cxgbe/t4_l2t.h
==============================================================================
--- head/sys/dev/cxgbe/t4_l2t.h Mon Jan 14 20:31:45 2013        (r245433)
+++ head/sys/dev/cxgbe/t4_l2t.h Mon Jan 14 20:36:22 2013        (r245434)
@@ -60,7 +60,7 @@ enum {
 struct l2t_entry {
        uint16_t state;                 /* entry state */
        uint16_t idx;                   /* entry index */
-       uint32_t addr;                  /* next hop IP address */
+       uint32_t addr[4];               /* next hop IP or IPv6 address */
        struct ifnet *ifp;              /* outgoing interface */
        uint16_t smt_idx;               /* SMT index */
        uint16_t vlan;                  /* VLAN TCI (id: 0-11, prio: 13-15) */
@@ -70,15 +70,17 @@ struct l2t_entry {
        struct mtx lock;
        volatile int refcnt;            /* entry reference count */
        uint16_t hash;                  /* hash bucket the entry is on */
+       uint8_t ipv6;                   /* entry is for an IPv6 address */
        uint8_t lport;                  /* associated offload logical port */
        uint8_t dmac[ETHER_ADDR_LEN];   /* next hop's MAC address */
 };
 
 struct l2t_data {
        struct rwlock lock;
+       u_int l2t_size;
        volatile int nfree;     /* number of free entries */
        struct l2t_entry *rover;/* starting point for next allocation */
-       struct l2t_entry l2tab[L2T_SIZE];
+       struct l2t_entry l2tab[];
 };
 
 

Modified: head/sys/dev/cxgbe/t4_main.c
==============================================================================
--- head/sys/dev/cxgbe/t4_main.c        Mon Jan 14 20:31:45 2013        
(r245433)
+++ head/sys/dev/cxgbe/t4_main.c        Mon Jan 14 20:36:22 2013        
(r245434)
@@ -1866,7 +1866,9 @@ get_params__post_init(struct adapter *sc
        param[1] = FW_PARAM_PFVF(EQ_START);
        param[2] = FW_PARAM_PFVF(FILTER_START);
        param[3] = FW_PARAM_PFVF(FILTER_END);
-       rc = -t4_query_params(sc, sc->mbox, sc->pf, 0, 4, param, val);
+       param[4] = FW_PARAM_PFVF(L2T_START);
+       param[5] = FW_PARAM_PFVF(L2T_END);
+       rc = -t4_query_params(sc, sc->mbox, sc->pf, 0, 6, param, val);
        if (rc != 0) {
                device_printf(sc->dev,
                    "failed to query parameters (post_init): %d.\n", rc);
@@ -1877,6 +1879,11 @@ get_params__post_init(struct adapter *sc
        sc->sge.eq_start = val[1];
        sc->tids.ftid_base = val[2];
        sc->tids.nftids = val[3] - val[2] + 1;
+       sc->vres.l2t.start = val[4];
+       sc->vres.l2t.size = val[5] - val[4] + 1;
+       KASSERT(sc->vres.l2t.size <= L2T_SIZE,
+           ("%s: L2 table size (%u) larger than expected (%u)",
+           __func__, sc->vres.l2t.size, L2T_SIZE));
 
        /* get capabilites */
        bzero(&caps, sizeof(caps));

Modified: head/sys/dev/cxgbe/tom/t4_tom_l2t.c
==============================================================================
--- head/sys/dev/cxgbe/tom/t4_tom_l2t.c Mon Jan 14 20:31:45 2013        
(r245433)
+++ head/sys/dev/cxgbe/tom/t4_tom_l2t.c Mon Jan 14 20:36:22 2013        
(r245434)
@@ -27,6 +27,7 @@
 __FBSDID("$FreeBSD$");
 
 #include "opt_inet.h"
+#include "opt_inet6.h"
 
 #ifdef TCP_OFFLOAD
 #include <sys/param.h>
@@ -34,6 +35,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/kernel.h>
 #include <sys/module.h>
 #include <sys/bus.h>
+#include <sys/fnv_hash.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
 #include <sys/rwlock.h>
@@ -48,28 +50,89 @@ __FBSDID("$FreeBSD$");
 #include <netinet/toecore.h>
 
 #include "common/common.h"
-#include "common/jhash.h"
 #include "common/t4_msg.h"
 #include "tom/t4_tom_l2t.h"
 #include "tom/t4_tom.h"
 
 #define VLAN_NONE      0xfff
 
-#define SA(x)           ((struct sockaddr *)(x))
-#define SIN(x)          ((struct sockaddr_in *)(x))
-#define SINADDR(x)      (SIN(x)->sin_addr.s_addr)
-
 static inline void
 l2t_hold(struct l2t_data *d, struct l2t_entry *e)
 {
+
        if (atomic_fetchadd_int(&e->refcnt, 1) == 0)  /* 0 -> 1 transition */
                atomic_subtract_int(&d->nfree, 1);
 }
 
-static inline unsigned int
-arp_hash(const uint32_t key, int ifindex)
+static inline u_int
+l2_hash(struct l2t_data *d, const struct sockaddr *sa, int ifindex)
+{
+       u_int hash, half = d->l2t_size / 2, start = 0;
+       const void *key;
+       size_t len;
+
+       KASSERT(sa->sa_family == AF_INET || sa->sa_family == AF_INET6,
+           ("%s: sa %p has unexpected sa_family %d", __func__, sa,
+           sa->sa_family));
+
+       if (sa->sa_family == AF_INET) {
+               const struct sockaddr_in *sin = (const void *)sa;
+
+               key = &sin->sin_addr;
+               len = sizeof(sin->sin_addr);
+       } else {
+               const struct sockaddr_in6 *sin6 = (const void *)sa;
+
+               key = &sin6->sin6_addr;
+               len = sizeof(sin6->sin6_addr);
+               start = half;
+       }
+
+       hash = fnv_32_buf(key, len, FNV1_32_INIT);
+       hash = fnv_32_buf(&ifindex, sizeof(ifindex), hash);
+       hash %= half;
+
+       return (hash + start);
+}
+
+static inline int
+l2_cmp(const struct sockaddr *sa, struct l2t_entry *e)
+{
+
+       KASSERT(sa->sa_family == AF_INET || sa->sa_family == AF_INET6,
+           ("%s: sa %p has unexpected sa_family %d", __func__, sa,
+           sa->sa_family));
+
+       if (sa->sa_family == AF_INET) {
+               const struct sockaddr_in *sin = (const void *)sa;
+
+               return (e->addr[0] != sin->sin_addr.s_addr);
+       } else {
+               const struct sockaddr_in6 *sin6 = (const void *)sa;
+
+               return (memcmp(&e->addr[0], &sin6->sin6_addr, sizeof(e->addr)));
+       }
+}
+
+static inline void
+l2_store(const struct sockaddr *sa, struct l2t_entry *e)
 {
-       return jhash_2words(key, ifindex, 0) & (L2T_SIZE - 1);
+
+       KASSERT(sa->sa_family == AF_INET || sa->sa_family == AF_INET6,
+           ("%s: sa %p has unexpected sa_family %d", __func__, sa,
+           sa->sa_family));
+
+       if (sa->sa_family == AF_INET) {
+               const struct sockaddr_in *sin = (const void *)sa;
+
+               e->addr[0] = sin->sin_addr.s_addr;
+               e->ipv6 = 0;
+       } else {
+               const struct sockaddr_in6 *sin6 = (const void *)sa;
+
+               memcpy(&e->addr[0], &sin6->sin6_addr, sizeof(e->addr));
+               e->ipv6 = 1;
+       }
 }
 
 /*
@@ -100,7 +163,7 @@ send_pending(struct adapter *sc, struct 
 static void
 resolution_failed_for_wr(struct wrqe *wr)
 {
-       log(LOG_ERR, "%s: leaked work request %p, wr_len %d", __func__, wr,
+       log(LOG_ERR, "%s: leaked work request %p, wr_len %d\n", __func__, wr,
            wr->wr_len);
 
        /* free(wr, M_CXGBE); */
@@ -175,15 +238,25 @@ resolve_entry(struct adapter *sc, struct
        struct tom_data *td = sc->tom_softc;
        struct toedev *tod = &td->tod;
        struct sockaddr_in sin = {0};
+       struct sockaddr_in6 sin6 = {0};
+       struct sockaddr *sa;
        uint8_t dmac[ETHER_ADDR_LEN];
        uint16_t vtag = VLAN_NONE;
        int rc;
 
-       sin.sin_family = AF_INET;
-       sin.sin_len = sizeof(struct sockaddr_in);
-       SINADDR(&sin) = e->addr;
+       if (e->ipv6 == 0) {
+               sin.sin_family = AF_INET;
+               sin.sin_len = sizeof(struct sockaddr_in);
+               sin.sin_addr.s_addr = e->addr[0];
+               sa = (void *)&sin;
+       } else {
+               sin6.sin6_family = AF_INET6;
+               sin6.sin6_len = sizeof(struct sockaddr_in6);
+               memcpy(&sin6.sin6_addr, &e->addr[0], sizeof(e->addr));
+               sa = (void *)&sin6;
+       }
 
-       rc = toe_l2_resolve(tod, e->ifp, SA(&sin), dmac, &vtag);
+       rc = toe_l2_resolve(tod, e->ifp, sa, dmac, &vtag);
        if (rc == EWOULDBLOCK)
                return (rc);
 
@@ -263,7 +336,7 @@ do_l2t_write_rpl2(struct sge_iq *iq, con
        struct adapter *sc = iq->adapter;
        const struct cpl_l2t_write_rpl *rpl = (const void *)(rss + 1);
        unsigned int tid = GET_TID(rpl);
-       unsigned int idx = tid & (L2T_SIZE - 1);
+       unsigned int idx = tid % L2T_SIZE;
        int rc;
 
        rc = do_l2t_write_rpl(iq, rss, m);
@@ -271,7 +344,7 @@ do_l2t_write_rpl2(struct sge_iq *iq, con
                return (rc);
 
        if (tid & F_SYNC_WR) {
-               struct l2t_entry *e = &sc->l2t->l2tab[idx];
+               struct l2t_entry *e = &sc->l2t->l2tab[idx - sc->vres.l2t.start];
 
                mtx_lock(&e->lock);
                if (e->state != L2T_STATE_SWITCHING) {
@@ -310,21 +383,22 @@ t4_l2t_get(struct port_info *pi, struct 
 {
        struct l2t_entry *e;
        struct l2t_data *d = pi->adapter->l2t;
-       uint32_t addr = SINADDR(sa);
-       int hash = arp_hash(addr, ifp->if_index);
-       unsigned int smt_idx = pi->port_id;
+       u_int hash, smt_idx = pi->port_id;
 
-       if (sa->sa_family != AF_INET)
-               return (NULL);  /* XXX: no IPv6 support right now */
+       KASSERT(sa->sa_family == AF_INET || sa->sa_family == AF_INET6,
+           ("%s: sa %p has unexpected sa_family %d", __func__, sa,
+           sa->sa_family));
 
 #ifndef VLAN_TAG
        if (ifp->if_type == IFT_L2VLAN)
                return (NULL);
 #endif
 
+       hash = l2_hash(d, sa, ifp->if_index);
        rw_wlock(&d->lock);
        for (e = d->l2tab[hash].first; e; e = e->next) {
-               if (e->addr == addr && e->ifp == ifp && e->smt_idx == smt_idx) {
+               if (l2_cmp(sa, e) == 0 && e->ifp == ifp &&
+                   e->smt_idx == smt_idx) {
                        l2t_hold(d, e);
                        goto done;
                }
@@ -338,7 +412,7 @@ t4_l2t_get(struct port_info *pi, struct 
                d->l2tab[hash].first = e;
 
                e->state = L2T_STATE_RESOLVING;
-               e->addr = addr;
+               l2_store(sa, e);
                e->ifp = ifp;
                e->smt_idx = smt_idx;
                e->hash = hash;
@@ -368,14 +442,14 @@ t4_l2_update(struct toedev *tod, struct 
        struct adapter *sc = tod->tod_softc;
        struct l2t_entry *e;
        struct l2t_data *d = sc->l2t;
-       uint32_t addr = SINADDR(sa);
-       int hash = arp_hash(addr, ifp->if_index);
+       u_int hash;
 
        KASSERT(d != NULL, ("%s: no L2 table", __func__));
 
+       hash = l2_hash(d, sa, ifp->if_index);
        rw_rlock(&d->lock);
        for (e = d->l2tab[hash].first; e; e = e->next) {
-               if (e->addr == addr && e->ifp == ifp) {
+               if (l2_cmp(sa, e) == 0 && e->ifp == ifp) {
                        mtx_lock(&e->lock);
                        if (atomic_load_acq_int(&e->refcnt))
                                goto found;

Modified: head/sys/modules/cxgbe/tom/Makefile
==============================================================================
--- head/sys/modules/cxgbe/tom/Makefile Mon Jan 14 20:31:45 2013        
(r245433)
+++ head/sys/modules/cxgbe/tom/Makefile Mon Jan 14 20:36:22 2013        
(r245434)
@@ -10,15 +10,20 @@ CXGBE = ${.CURDIR}/../../../dev/cxgbe
 KMOD = t4_tom
 SRCS = t4_tom.c t4_connect.c t4_listen.c t4_cpl_io.c t4_tom_l2t.c t4_ddp.c
 SRCS+= device_if.h bus_if.h pci_if.h
-SRCS+= opt_inet.h
+SRCS+= opt_inet.h opt_inet6.h
 
 CFLAGS+= -I${CXGBE}
 
 .if !defined(KERNBUILDDIR)
 .if ${MK_INET_SUPPORT} != "no"
 opt_inet.h:
-       echo "#define INET 1" > ${.TARGET}
-       echo "#define TCP_OFFLOAD 1" >> ${.TARGET}
+       @echo "#define INET 1" > ${.TARGET}
+       @echo "#define TCP_OFFLOAD 1" >> ${.TARGET}
+.endif
+
+.if ${MK_INET6_SUPPORT} != "no"
+opt_inet6.h:
+       @echo "#define INET6 1" > ${.TARGET}
 .endif
 .endif
 
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to