NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/44473: (FAST-)IPSEC processing comsumes too much CPU in interrupt processing



>Number:         44473
>Category:       kern
>Synopsis:       (FAST-)IPSEC processing comsumes too much CPU in interrupt 
>processing
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Thu Jan 27 13:00:00 +0000 2011
>Originator:     Dr. Wolfgang Stukenbrock
>Release:        NetBSD 5.1
>Organization:
Dr. Nagler & Company GmbH
>Environment:
        
        
System: NetBSD test-s0 4.0 NetBSD 4.0 (NSW-WS) #0: Tue Aug 17 17:28:09 CEST 
2010 wgstuken@test-s0:/usr/src/sys/arch/amd64/compile/NSW-WS amd64
Architecture: x86_64
Machine: amd64
>Description:
        The IPSEC and FAST_IPSEC processing is done while processing the packet 
itself.
        After processing, the packet is placed in a queue in order to avoid 
stack problems and reinjected
        from there by a kernel-thread (cryptoret).
        This consumes a large amount of cpu-power during interrupt processing 
and that leads to bad response times
        to other IO-devices (or next packets on the network).
        I've meashured "only" a throughput of something about 6 MB/sec on a 
Xeon L3406.
        (In NetBSD 4.0 a Xeon E3110 has processed up to 12 MB/sec ...)
        The reason for this seems to be the KERNEL_LOCK and/or softnet_lock 
mutex that are hold during
        processing of the device interrupts and the network code.

        We run some systems as Firewalls that connects different locations via 
ESP/IPCOMP-tunnels to provide
        a transparent network for the locations. For this application 6 MB/sec 
is too slow ...

        While forwarding packets "top" shows something about 90% interrupt 
activity on CPU 0.

        In order to separate the interrupt processing from the crypto-stuff, 
I've create a new queue in cryptosoft.c
        and bypass the previous queue at end of processing. I also allow a 
dynamic number of kernel threads to 
        process requests in parallel tunable via sysctl at runtime.
        With this change, "top" now says something about 10% interrupt on CPU 0 
and a large number in % for system
        from the new threads. I've reached 12 MB/sec throughput again - but I 
think it may be larger, the other side (the E3110 system)
        was not able to process more ...

        The patch below does not turn on this new kind of processing by 
default. If 0 threads are selected, the processing is
        done in the old style during interrupt processing.
        Netherless always one new thread is present all the time in order to 
simplyfy the implementation.
        The number of threads is adjusted dynamicaly to the requested value 
that may be changed via sysctl at any time.
        For now there is no uppler limit check for the number of threads - 
perhaps it would make sence to add one.
        I've selected the name "kern.swcr_num_thr" for the parameter - feel 
free to choose another one ...

        I've tested the troughput with different number of threads (my L3406 
offers 4 CPU's) and found out that at least
        for my test-setup 2 threads lead to the best result.
        But for other setups other values may be best. Therefore I've left the 
number of threads tunable by the admin.
        When starting more threads, it looks like that the scheduling overhead 
gets to expensive. But that should depend on the number
        of concurrent available crypto request and that again depends on the 
amount of trafic ...

        What is still not perfect in this implementation:
        - no upper limit for the number of threads - an easy aproach may be the 
number of CPU's available
        - curently the threads are not bound to a CPU, they switch between 
them. But I simple do not know how to fix them to a 
          CPU and I'm not shure how to decide to which CPU it should be bound.
        - I use printf() for kernel messages as done in the other parts of 
opencrypto too.
        - I'm not shure if I would overrun the console with error messages if 
the thread-creation does not work, because
          the code will retry to create missing threads after processing each 
request again and again and again ....
          But I don't think it would is a good idea to reduche the 
sysctl-configured value. That might surprise the admin ...
>How-To-Repeat:
        No relevant - new functionality
>Fix:
        The following patch to /usr/src/sys/opencrypto/cryptosoft.[ch] requires 
PR44470 to be applied.
        Otherwise the change to "const" for the swcr_data pointers in the 
worker-routines does not work for swcr_compdec().
        (sorry - the const should have been in PR 44470, but I've overlooked 
them there - sorry again ...)


--- cryptosoft.c        2011/01/27 11:42:23     1.2
+++ cryptosoft.c        2011/01/27 12:08:43
@@ -33,6 +33,8 @@
 #include <sys/sysctl.h>
 #include <sys/errno.h>
 
+#include <sys/kthread.h>
+
 #include "opt_ocf.h"
 #include <opencrypto/cryptodev.h>
 #include <opencrypto/cryptosoft.h>
@@ -53,6 +55,14 @@
 u_int32_t swcr_sesnum = 0;
 int32_t swcr_id = -1;
 
+static  void swcrthr(void);           /* kernel thread(s) for processing 
request in parallel */
+
+static kcondvar_t swcr_kcv;
+static kmutex_t   swcr_kmtx;
+static int        swcr_numthr = 0; /* default number of thread to use */
+static int        swcr_curthr = 0;
+static TAILQ_HEAD(,cryptop) swcr_q = TAILQ_HEAD_INITIALIZER(swcr_q);
+
 #define COPYBACK(x, a, b, c, d) \
        (x) == CRYPTO_BUF_MBUF ? m_copyback((struct mbuf *)a,b,c,d) \
        : cuio_copyback((struct uio *)a,b,c,d)
@@ -60,9 +70,10 @@
        (x) == CRYPTO_BUF_MBUF ? m_copydata((struct mbuf *)a,b,c,d) \
        : cuio_copydata((struct uio *)a,b,c,d)
 
-static int swcr_encdec(struct cryptodesc *, struct swcr_data *, void *, int);
-static int swcr_compdec(struct cryptodesc *, struct swcr_data *, void *, int, 
int *);
+static int swcr_encdec(struct cryptodesc *, const struct swcr_data *, void *, 
int);
+static int swcr_compdec(struct cryptodesc *, const struct swcr_data *, void *, 
int, int *);
 static int swcr_process(void *, struct cryptop *, int);
+static void swcr_process2(struct cryptop *);
 static int swcr_newsession(void *, u_int32_t *, struct cryptoini *);
 static int swcr_freesession(void *, u_int64_t);
 
@@ -70,7 +81,7 @@
  * Apply a symmetric encryption/decryption algorithm.
  */
 static int
-swcr_encdec(struct cryptodesc *crd, struct swcr_data *sw, void *bufv,
+swcr_encdec(struct cryptodesc *crd, const struct swcr_data *sw, void *bufv,
     int outtype)
 {
        char *buf = bufv;
@@ -418,7 +429,7 @@
  */
 int
 swcr_authcompute(struct cryptop *crp, struct cryptodesc *crd,
-    struct swcr_data *sw, void *buf, int outtype)
+    const struct swcr_data *sw, void *buf, int outtype)
 {
        unsigned char aalg[AALG_MAX_RESULT_LEN];
        const struct swcr_auth_hash *axf;
@@ -512,7 +523,7 @@
  * Apply a compression/decompression algorithm
  */
 static int
-swcr_compdec(struct cryptodesc *crd, struct swcr_data *sw,
+swcr_compdec(struct cryptodesc *crd, const struct swcr_data *sw,
     void *buf, int outtype, int *res_size)
 {
        u_int8_t *data, *out;
@@ -900,15 +911,39 @@
 static int
 swcr_process(void *arg, struct cryptop *crp, int hint)
 {
-       struct cryptodesc *crd;
-       struct swcr_data *sw;
-       u_int32_t lid;
-       int type;
+       int wasempty;
 
        /* Sanity check */
        if (crp == NULL)
                return EINVAL;
 
+       if (swcr_numthr != 0) { 
+               mutex_spin_enter(&swcr_kmtx);  
+               if (swcr_curthr == 0 || swcr_numthr == 0) {
+                       mutex_spin_exit(&swcr_kmtx);
+                       goto direct_call;
+               }       
+               wasempty = TAILQ_EMPTY(&swcr_q);
+               TAILQ_INSERT_TAIL(&swcr_q, crp, crp_next);
+               if (wasempty) {
+                       cv_signal(&swcr_kcv);
+               }       
+               mutex_spin_exit(&swcr_kmtx);
+       } else {        
+direct_call:    
+               swcr_process2(crp);
+       }               
+       return 0;       
+}
+
+static void
+swcr_process2(struct cryptop *crp)
+{
+       struct cryptodesc *crd;
+       struct swcr_data *sw;
+       u_int32_t lid;
+       int type;
+
        if (crp->crp_desc == NULL || crp->crp_buf == NULL) {
                crp->crp_etype = EINVAL;
                goto done;
@@ -1000,12 +1035,13 @@
 done:
        DPRINTF(("request %08x done\n", (uint32_t)crp));
        crypto_done(crp);
-       return 0;
 }
 
 static void
 swcr_init(void)
 {
+       int error;
+
        swcr_id = crypto_get_driverid(CRYPTOCAP_F_SOFTWARE);
        if (swcr_id < 0) {
                /* This should never happen */
@@ -1038,8 +1074,88 @@
        REGISTER(CRYPTO_DEFLATE_COMP);
        REGISTER(CRYPTO_GZIP_COMP);
 #undef REGISTER
+
+       mutex_init(&swcr_kmtx, MUTEX_DEFAULT, IPL_NET);
+       cv_init(&swcr_kcv, "swcr_wait");
+       error = kthread_create(PRI_COUNT - 1, KTHREAD_MPSAFE, NULL,
+                               (void (*)(void*))swcrthr, NULL, NULL, 
"swcrthr");
+       if (error) {
+               printf("softcrypto_init: cannot start softcrypto thread; error 
%d", error);
+       } else swcr_curthr = 1;
 }
 
+/*
+ * Kernel thread to do callbacks.
+ */
+static void
+swcrthr(void)        
+{
+       struct cryptop *crp;
+       int f_loop;
+
+       mutex_spin_enter(&swcr_kmtx);
+       for (f_loop = 1;;) {
+               crp = TAILQ_FIRST(&swcr_q);
+               if (crp == NULL) {
+                       cv_wait(&swcr_kcv, &swcr_kmtx);
+                       f_loop = 1;
+                       continue;
+               }
+               TAILQ_REMOVE(&swcr_q, crp, crp_next);
+               if (f_loop != 0 && !TAILQ_EMPTY(&swcr_q)) {
+                       cv_signal(&swcr_kcv); /* wake up another thread on 
first loop it not empty */
+               }
+               f_loop = 0;
+               mutex_spin_exit(&swcr_kmtx);
+
+               crp->crp_flags |= CRYPTO_F_CBIMM; /* force direct return - 
avoid additional scheduling ...*/
+               swcr_process2(crp);
+
+               mutex_spin_enter(&swcr_kmtx);
+               if (swcr_numthr != swcr_curthr) { /* need to correct number of 
threads ... */
+                       if (swcr_numthr < swcr_curthr && swcr_curthr > 1) {
+                               swcr_curthr--;
+                               mutex_spin_exit(&swcr_kmtx);
+                               lwp_exit(curlwp);
+                       } else if (swcr_numthr > swcr_curthr) {
+                               int need_new = swcr_numthr - swcr_curthr;
+                               int error;
+
+                               swcr_curthr = swcr_numthr;
+                               mutex_spin_exit(&swcr_kmtx);
+                               while (need_new > 0) {
+                                       error = kthread_create(PRI_COUNT - 1, 
KTHREAD_MPSAFE, NULL,
+                                               (void (*)(void*))swcrthr, NULL, 
NULL, "swcrthr");
+                                       if (error) {
+                                               printf("softcrypto_init: cannot 
start softcrypto thread; error %d", error);
+                                               break;
+                                       }
+                                       need_new--;
+                               }
+                               mutex_spin_enter(&swcr_kmtx);
+                               swcr_curthr -= need_new;
+                       }
+               }
+       }
+}
+
+
+SYSCTL_SETUP(sysctl_softcrypto_setup, "sysctl softcrypto subtree setup")
+{
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT,
+                       CTLTYPE_NODE, "kern", NULL,
+                       NULL, 0, NULL, 0,
+                       CTL_KERN, CTL_EOL);
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "swcr_num_thr",
+                       SYSCTL_DESCR("number of kernel thread to use for 
softcrypto"),
+                       NULL, 0, &swcr_numthr, 0,
+                       CTL_KERN, CTL_CREATE, CTL_EOL);
+}
+
+
 
 /*
  * Pseudo-device init routine for software crypto.
--- cryptosoft.h        2011/01/27 11:42:23     1.2
+++ cryptosoft.h        2011/01/27 11:53:10
@@ -57,7 +57,7 @@
 
 #ifdef _KERNEL
 int swcr_authcompute(struct cryptop *crp, struct cryptodesc *crd,
-    struct swcr_data *sw, void *buf, int outtype);
+    const struct swcr_data *sw, void *buf, int outtype);
 #endif /* _KERNEL */
 
 #endif /* _CRYPTO_CRYPTO_H_ */

>Unformatted:
        
        


Home | Main Index | Thread Index | Old Index