hi,

recently me and jsg have figured out that a traffic burst can kill
your interface (deplete the rx ring) if you're using ipsec.  the
problem is that there's no limit on number of outstanding crypto
operations.  every job contains a pointer to a cluster that comes
from the nic driver.

under high load a number of jobs on a crypto queue reaches the
limit for the mclpool and network card fails to allocate clusters
for the rx ring and stalls (both bnx and ix don't recover).

we experimented with large watermarks (>20000 clusters) but that
just delays the problem a bit.  real solution is to prevent an
excessive amount of operations to be enqueued to the crypto queue.
the problem is as usual: how to define a limit?  in this diff i've
used a 1/3 of the cluster pool hardlimit which behaves pretty good
with different kern.maxclusters values.

opinions?  OKs?

(i'm not sure that using `min' in the m_clhardlimit is anyhow better
than `max', so please speak up if you know how to improve this.)

Index: crypto/crypto.c
===================================================================
RCS file: /home/cvs/src/sys/crypto/crypto.c,v
retrieving revision 1.59
diff -u -p -r1.59 crypto.c
--- crypto/crypto.c     11 Jan 2011 15:42:05 -0000      1.59
+++ crypto/crypto.c     3 May 2011 13:56:41 -0000
@@ -677,3 +677,14 @@ out:
        *featp = feat;
        return (0);
 }
+
+int
+crypto_onqueue(u_int64_t sid)
+{
+       u_int32_t hid;
+
+       hid = (sid >> 32) & 0xffffffff;
+       if (hid < crypto_drivers_num)
+               return (crypto_drivers[hid].cc_queued);
+       return (0);
+}
Index: crypto/cryptodev.h
===================================================================
RCS file: /home/cvs/src/sys/crypto/cryptodev.h,v
retrieving revision 1.55
diff -u -p -r1.55 cryptodev.h
--- crypto/cryptodev.h  16 Dec 2010 16:56:08 -0000      1.55
+++ crypto/cryptodev.h  3 May 2011 13:21:36 -0000
@@ -323,6 +323,7 @@ int crypto_kinvoke(struct cryptkop *);
 void   crypto_done(struct cryptop *);
 void   crypto_kdone(struct cryptkop *);
 int    crypto_getfeat(int *);
+int    crypto_onqueue(u_int64_t);
 
 void   cuio_copydata(struct uio *, int, int, caddr_t);
 void   cuio_copyback(struct uio *, int, int, const void *);
Index: kern/uipc_mbuf.c
===================================================================
RCS file: /home/cvs/src/sys/kern/uipc_mbuf.c,v
retrieving revision 1.156
diff -u -p -r1.156 uipc_mbuf.c
--- kern/uipc_mbuf.c    18 Apr 2011 19:23:46 -0000      1.156
+++ kern/uipc_mbuf.c    3 May 2011 17:01:14 -0000
@@ -454,6 +454,30 @@ m_clget(struct mbuf *m, int how, struct 
        return (m);
 }
 
+int
+m_clhardlimit(struct mbuf *m)
+{
+       struct pool *pp;
+       int limit = 0;
+
+       if (m == NULL)
+               return (-1);
+
+       do {
+               if ((m->m_flags & (M_EXT|M_CLUSTER)) != (M_EXT|M_CLUSTER))
+                       continue;
+               pp = &mclpools[m->m_ext.ext_backend];
+               if (limit)
+                       limit = min(limit, pp->pr_hardlimit);
+               else
+                       limit = pp->pr_hardlimit;
+       } while ((m = m->m_next) != NULL);
+
+       if (!limit)
+               limit = -1;
+       return (limit);
+}
+
 struct mbuf *
 m_free_unlocked(struct mbuf *m)
 {
Index: netinet/ip_ah.c
===================================================================
RCS file: /home/cvs/src/sys/netinet/ip_ah.c,v
retrieving revision 1.99
diff -u -p -r1.99 ip_ah.c
--- netinet/ip_ah.c     11 Jan 2011 15:42:05 -0000      1.99
+++ netinet/ip_ah.c     3 May 2011 17:35:48 -0000
@@ -548,7 +548,7 @@ ah_input(struct mbuf *m, struct tdb *tdb
        int rplen;
 
        struct cryptodesc *crda = NULL;
-       struct cryptop *crp;
+       struct cryptop *crp = NULL;
 
        if (!(tdb->tdb_flags & TDBF_NOREPLAY))
                rplen = AH_FLENGTH + sizeof(u_int32_t);
@@ -632,7 +632,9 @@ ah_input(struct mbuf *m, struct tdb *tdb
        }
 
        /* Get crypto descriptors. */
-       crp = crypto_getreq(1);
+       if (m_clhardlimit(m) == -1 ||
+           crypto_onqueue(tdb->tdb_cryptoid) < m_clhardlimit(m) / 3)
+               crp = crypto_getreq(1);
        if (crp == NULL) {
                m_freem(m);
                DPRINTF(("ah_input(): failed to acquire crypto "
@@ -982,7 +984,7 @@ ah_output(struct mbuf *m, struct tdb *td
        struct cryptodesc *crda;
        struct tdb_crypto *tc;
        struct mbuf *mo, *mi;
-       struct cryptop *crp;
+       struct cryptop *crp = NULL;
        u_int16_t iplen;
        int len, rplen;
        u_int8_t prot;
@@ -1151,7 +1153,9 @@ ah_output(struct mbuf *m, struct tdb *td
        }
 
        /* Get crypto descriptors. */
-       crp = crypto_getreq(1);
+       if (m_clhardlimit(m) == -1 ||
+           crypto_onqueue(tdb->tdb_cryptoid) < m_clhardlimit(m) / 3)
+               crp = crypto_getreq(1);
        if (crp == NULL) {
                m_freem(m);
                DPRINTF(("ah_output(): failed to acquire crypto "
Index: netinet/ip_esp.c
===================================================================
RCS file: /home/cvs/src/sys/netinet/ip_esp.c,v
retrieving revision 1.116
diff -u -p -r1.116 ip_esp.c
--- netinet/ip_esp.c    11 Jan 2011 15:42:05 -0000      1.116
+++ netinet/ip_esp.c    3 May 2011 17:35:41 -0000
@@ -324,7 +324,7 @@ esp_input(struct mbuf *m, struct tdb *td
        struct auth_hash *esph = (struct auth_hash *) tdb->tdb_authalgxform;
        struct enc_xform *espx = (struct enc_xform *) tdb->tdb_encalgxform;
        struct cryptodesc *crde = NULL, *crda = NULL;
-       struct cryptop *crp;
+       struct cryptop *crp = NULL;
        struct tdb_crypto *tc;
        int plen, alen, hlen;
        struct m_tag *mtag;
@@ -427,7 +427,9 @@ esp_input(struct mbuf *m, struct tdb *td
 #endif
 
        /* Get crypto descriptors */
-       crp = crypto_getreq(esph && espx ? 2 : 1);
+       if (m_clhardlimit(m) == -1 ||
+           crypto_onqueue(tdb->tdb_cryptoid) < m_clhardlimit(m) / 3)
+               crp = crypto_getreq(esph && espx ? 2 : 1);
        if (crp == NULL) {
                m_freem(m);
                DPRINTF(("esp_input(): failed to acquire crypto 
descriptors\n"));
@@ -771,7 +773,7 @@ esp_output(struct mbuf *m, struct tdb *t
        u_int8_t prot;
 
        struct cryptodesc *crde = NULL, *crda = NULL;
-       struct cryptop *crp;
+       struct cryptop *crp = NULL;
 #if NBPFILTER > 0
        struct ifnet *encif;
 
@@ -953,7 +955,9 @@ esp_output(struct mbuf *m, struct tdb *t
        m_copyback(m, protoff, sizeof(u_int8_t), &prot, M_NOWAIT);
 
        /* Get crypto descriptors. */
-       crp = crypto_getreq(esph && espx ? 2 : 1);
+       if (m_clhardlimit(m) == -1 ||
+           crypto_onqueue(tdb->tdb_cryptoid) < m_clhardlimit(m) / 3)
+               crp = crypto_getreq(esph && espx ? 2 : 1);
        if (crp == NULL) {
                m_freem(m);
                DPRINTF(("esp_output(): failed to acquire crypto "
Index: sys/mbuf.h
===================================================================
RCS file: /home/cvs/src/sys/sys/mbuf.h,v
retrieving revision 1.151
diff -u -p -r1.151 mbuf.h
--- sys/mbuf.h  24 Apr 2011 19:36:54 -0000      1.151
+++ sys/mbuf.h  3 May 2011 13:52:39 -0000
@@ -415,6 +415,7 @@ int m_cldrop(struct ifnet *, int);
 void   m_clcount(struct ifnet *, int);
 void   m_cluncount(struct mbuf *, int);
 void   m_clinitifp(struct ifnet *);
+int    m_clhardlimit(struct mbuf *);
 void   m_adj(struct mbuf *, int);
 int    m_copyback(struct mbuf *, int, int, const void *, int);
 void   m_freem(struct mbuf *);

Reply via email to