increase DEFLOOPBACKFAIL
[ppp.git] / modules / ppp_comp.c
index 215830eeaa7166dbf048ece6d0c7d048e2d8428a..4c261e7931146e6561447bb3efce867ff9ee502f 100644 (file)
  * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
  * OR MODIFICATIONS.
  *
- * $Id: ppp_comp.c,v 1.2 1996/01/18 03:18:07 paulus Exp $
+ * $Id: ppp_comp.c,v 1.5 1996/08/28 06:36:30 paulus Exp $
  */
 
 /*
- * This file is used under SVR4, Solaris 2, SunOS 4, and OSF/1.
+ * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX.
  */
 
 #include <sys/types.h>
 #include <net/pppio.h>
 #include "ppp_mod.h"
 
+#ifdef __osf__
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#endif
+
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
@@ -72,8 +77,9 @@ static int msg_byte __P((mblk_t *, unsigned int));
 /* Is this LCP packet one we have to transmit using LCP defaults? */
 #define LCP_USE_DFLT(mp)       (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
 
+#define PPP_COMP_ID 0xbadf
 static struct module_info minfo = {
-    0xbadf, "ppp_comp", 0, INFPSZ, 16384, 4096,
+    PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16384, 4096,
 };
 
 static struct qinit r_init = {
@@ -91,6 +97,19 @@ struct streamtab ppp_compinfo = {
 
 int ppp_comp_count;            /* number of module instances in use */
 
+#ifdef __osf__
+
+static void ppp_comp_alloc __P((comp_state_t *));
+typedef struct memreq {
+    unsigned char *comp;
+    void *mem;
+    int len;
+    int cmd;
+    int ret;
+} memreq_t;
+
+#endif
+
 typedef struct comp_state {
     int                flags;
     int                mru;
@@ -103,11 +122,21 @@ typedef struct comp_state {
     struct vjcompress vj_comp;
     int                vj_last_ierrors;
     struct pppstat stats;
+#ifdef __osf__
+    memreq_t   memreq;
+    thread_t   thread;
+#endif
 } comp_state_t;
 
+
+#ifdef __osf__
+extern task_t first_task;
+#endif
+
 /* Bits in flags are as defined in pppio.h. */
 #define CCP_ERR                (CCP_ERROR | CCP_FATALERROR)
 #define LAST_MOD       0x1000000       /* no ppp modules below us */
+#define DBGLOG         0x2000000       /* log debugging stuff */
 
 #define MAX_IPHDR      128     /* max TCP/IP header size */
 #define MAX_VJHDR      20      /* max VJ compressed header size (?) */
@@ -138,6 +167,9 @@ struct compressor *ppp_compressors[] = {
 MOD_OPEN(ppp_comp_open)
 {
     comp_state_t *cp;
+#ifdef __osf__
+    thread_t thread;
+#endif
 
     if (q->q_ptr == NULL) {
        cp = (comp_state_t *) ALLOC_SLEEP(sizeof(comp_state_t));
@@ -152,6 +184,11 @@ MOD_OPEN(ppp_comp_open)
        vj_compress_init(&cp->vj_comp, -1);
        ++ppp_comp_count;
        qprocson(q);
+#ifdef __osf__
+       if (!(thread = kernel_thread_w_arg(first_task, ppp_comp_alloc, (void *)cp)))
+               OPEN_ERROR(ENOSR);
+       cp->thread = thread;
+#endif
     }
     return 0;
 }
@@ -167,6 +204,12 @@ MOD_CLOSE(ppp_comp_close)
            (*cp->xcomp->comp_free)(cp->xstate);
        if (cp->rstate != NULL)
            (*cp->rcomp->decomp_free)(cp->rstate);
+#ifdef __osf__
+       if (!cp->thread)
+           printf("ppp_comp_close: NULL thread!\n");
+       else
+           thread_deallocate(cp->thread);
+#endif
        FREE(cp, sizeof(comp_state_t));
        q->q_ptr = NULL;
        OTHERQ(q)->q_ptr = NULL;
@@ -175,6 +218,82 @@ MOD_CLOSE(ppp_comp_close)
     return 0;
 }
 
+#ifdef __osf__
+
+/* thread for calling back to a compressor's memory allocator
+ * Needed for Digital UNIX since it's VM can't handle requests
+ * for large amounts of memory without blocking.  The thread
+ * provides a context in which we can call a memory allocator
+ * that may block.
+ */
+static void
+ppp_comp_alloc(comp_state_t *cp)
+{
+    unsigned char *opt_data;
+    int len, cmd;
+    struct compressor *comp;
+    thread_t thread;
+
+#if (MAJOR_VERSION <= 2)
+
+    /* In 2.x and earlier the argument gets passed
+     * in the thread structure itself.  Yuck.
+     */
+    thread = current_thread();
+    cp = thread->reply_port;
+    thread->reply_port = PORT_NULL;
+
+#endif
+
+    for (;;) {
+       assert_wait((vm_offset_t)&cp->memreq.comp, TRUE);
+       thread_block();
+       opt_data = cp->memreq.comp;
+       len = cp->memreq.len;
+       cmd = cp->memreq.cmd;
+
+        if (cmd == PPPIO_XCOMP) {
+           comp = cp->xcomp;
+           cp->memreq.mem = (*comp->comp_alloc)(opt_data, len);
+       } else {
+           comp = cp->rcomp;
+           cp->memreq.mem = (*comp->decomp_alloc)(opt_data, len);
+       }
+       if (!cp->memreq.mem)
+           cp->memreq.ret = ENOSR;
+       else
+           bcopy(opt_data, cp->memreq.mem, len);
+
+       /* have to free thunk here, since there's
+        * no guarantee that the user will call the ioctl
+        * again if we've taken a long time to complete
+        */
+       FREE(opt_data, len);
+       cp->memreq.ret = 0;
+    }
+}
+
+#endif /* __osf__ */
+
+/* here's the deal with memory allocation under Digital UNIX.
+ * Some other may also benefit from this...
+ * We can't ask for huge chunks of memory in a context where
+ * the caller can't be put to sleep (like, here.)  The alloc
+ * is likely to fail.  Instead we do this: the first time we
+ * get called, kick off a thread to do the allocation.  Return
+ * immediately to the caller with EAGAIN, as an indication that
+ * they should send down the ioctl again.  By the time the
+ * second call comes in it's likely that the memory allocation
+ * thread will have returned with the requested memory.  We will
+ * continue to return EAGAIN however until the thread has completed.
+ * When it has, we return zero (and the memory) if the allocator
+ * was successful and ENOSR otherwise.
+ *
+ * Callers of the RCOMP and XCOMP ioctls are encouraged (but not
+ * required) to loop for some number of iterations with a small
+ * delay in the loop body (for instance a 1/10-th second "sleep"
+ * via select.)
+ */
 static int
 ppp_comp_wput(q, mp)
     queue_t *q;
@@ -182,7 +301,7 @@ ppp_comp_wput(q, mp)
 {
     struct iocblk *iop;
     comp_state_t *cp;
-    int error, len;
+    int error, len, n;
     int flags, mask;
     mblk_t *np;
     struct compressor **comp;
@@ -258,19 +377,114 @@ ppp_comp_wput(q, mp)
                    /* here's the handler! */
                    error = 0;
                    if (iop->ioc_cmd == PPPIO_XCOMP) {
-                       if (cp->xstate != NULL)
+
+                       /* A previous call may have fetched memory for a compressor
+                        * that's now being retired or reset.  Free it using it's
+                        * mechanism for freeing stuff.
+                        */
+                       if (cp->xstate != NULL) {
                            (*cp->xcomp->comp_free)(cp->xstate);
+                           cp->xstate = NULL;
+                       }
+
+#ifdef __osf__
+                       /* Account for an orpahned call to get memory.
+                        * Free that memory up and go on.
+                        *
+                        * The trick is that we need to be able to tell
+                        * the difference between an old call that kicked
+                        * off a thread where the memory was subsequently
+                        * orphaned, and the memory we're really interested
+                        * in.  The thread helps by stamping the memory it
+                        * allocated with the parameters for the compressor
+                        * it belongs to.  If the parameters match then this
+                        * is the memory we want (whether it was an actual
+                        * orphan or not we don't care.)  If the parameters
+                        * don't match then this is an orphan.
+                        *
+                        * Note that cp->memreq.ret is the synchronization
+                        * point: we set it to EAGAIN in this function, then
+                        * wait for the thread to set it to something other
+                        * than EAGAIN before we fool with the data structure
+                        * again.  Basically, if ret != EAGAIN then the thread
+                        * is working and it owns the memreq struture.
+                        */
+                       if (cp->memreq.ret == 0 && cp->memreq.mem != NULL &&
+                           bcmp(cp->memreq.mem, opt_data, len)) {
+                           (*cp->xcomp->comp_free)(cp->memreq.mem);
+                           cp->memreq.mem = 0;
+                       }
+
+                       /* First time through, prime the pump and kick
+                        * off the thread.  Subsequent times though, the thread
+                        * is either busy (ret == EAGAIN) or finsihed (mem != NULL)
+                        * || (ret != 0)
+                        */
+                       if (cp->memreq.ret == 0 && cp->memreq.mem == NULL) {
+                           cp->memreq.comp = ALLOC_NOSLEEP(len);
+                           if (!cp->memreq.comp) {
+                               printf("gack! can't get memory for thunk\n");
+                               return ENOSR;
+                           }
+                           bcopy(opt_data, cp->memreq.comp, len);
+                           cp->memreq.len = len;
+                           cp->memreq.cmd = PPPIO_XCOMP;
+                           cp->xcomp = *comp;
+                           cp->memreq.ret = EAGAIN;
+                           thread_wakeup((vm_offset_t)&cp->memreq.comp);
+                       }
+
+                       /* Collect results from the thread, and reset the
+                        * mechanism for the next attempt to allocate memory
+                        * If the thread isn't finished (ret == EAGAIN) then
+                        * don't reset and just return.
+                        */
+                       if ((error = cp->memreq.ret) != EAGAIN) {
+                           cp->xstate = cp->memreq.mem;
+                           cp->memreq.mem = 0;
+                           cp->memreq.ret = 0;
+                       }
+#else
                        cp->xcomp = *comp;
                        cp->xstate = (*comp)->comp_alloc(opt_data, len);
                        if (cp->xstate == NULL)
                            error = ENOSR;
+#endif
                    } else {
-                       if (cp->rstate != NULL)
+                       if (cp->rstate != NULL) {
                            (*cp->rcomp->decomp_free)(cp->rstate);
+                           cp->rstate = NULL;
+                       }
+#ifdef __osf__
+                       if (cp->memreq.ret == 0 && cp->memreq.mem != NULL &&
+                           bcmp(cp->memreq.mem, opt_data, len)) {
+                           (*cp->rcomp->comp_free)(cp->memreq.mem);
+                           cp->memreq.mem = 0;
+                       }
+                       if (cp->memreq.ret == 0 && cp->memreq.mem == NULL) {
+                           cp->memreq.comp = ALLOC_NOSLEEP(len);
+                           if (!cp->memreq.comp) {
+                               printf("gack! can't get memory for thunk\n");
+                               return ENOSR;
+                           }
+                           bcopy(opt_data, cp->memreq.comp, len);
+                           cp->memreq.len = len;
+                           cp->memreq.cmd = PPPIO_RCOMP;
+                           cp->rcomp = *comp;
+                           cp->memreq.ret = EAGAIN;
+                           thread_wakeup((vm_offset_t)&cp->memreq.comp);
+                       }
+                       if ((error = cp->memreq.ret) != EAGAIN) {
+                           cp->rstate = cp->memreq.mem;
+                           cp->memreq.mem = 0;
+                           cp->memreq.ret = 0;
+                       }
+#else
                        cp->rcomp = *comp;
                        cp->rstate = (*comp)->decomp_alloc(opt_data, len);
                        if (cp->rstate == NULL)
                            error = ENOSR;
+#endif
                    }
                    break;
                }
@@ -318,6 +532,20 @@ ppp_comp_wput(q, mp)
            error = 0;
            break;
 
+       case PPPIO_DEBUG:
+           if (iop->ioc_count != sizeof(int))
+               break;
+           n = *(int *)mp->b_cont->b_rptr;
+           if (n == PPPDBG_LOG + PPPDBG_COMP) {
+               DPRINT1("ppp_comp%d: debug log enabled\n", cp->unit);
+               cp->flags |= DBGLOG;
+               error = 0;
+               iop->ioc_count = 0;
+           } else {
+               error = -1;
+           }
+           break;
+
        case PPPIO_LASTMOD:
            cp->flags |= LAST_MOD;
            error = 0;
@@ -396,9 +624,9 @@ ppp_comp_wsrv(q)
         * Make sure we've got enough data in the first mblk
         * and that we are its only user.
         */
-       if (proto = PPP_CCP)
+       if (proto == PPP_CCP)
            hlen = len;
-       else if (proto = PPP_IP)
+       else if (proto == PPP_IP)
            hlen = PPP_HDRLEN + MAX_IPHDR;
        else
            hlen = PPP_HDRLEN;
@@ -413,7 +641,6 @@ ppp_comp_wsrv(q)
                continue;
            }
        }
-       proto = PPP_PROTOCOL(mp->b_rptr);
 
        /*
         * Do VJ compression if requested.
@@ -688,14 +915,21 @@ ppp_comp_rsrv(q)
                 * First reset compressor if an input error has occurred.
                 */
                if (cp->stats.ppp_ierrors != cp->vj_last_ierrors) {
+                   if (cp->flags & DBGLOG)
+                       DPRINT1("ppp%d: resetting VJ\n", cp->unit);
                    vj_uncompress_err(&cp->vj_comp);
                    cp->vj_last_ierrors = cp->stats.ppp_ierrors;
                }
 
                vjlen = vj_uncompress_tcp(dp, np->b_wptr - dp, len,
                                          &cp->vj_comp, &iphdr, &iphlen);
-               if (vjlen < 0)
+               if (vjlen < 0) {
+                   if (cp->flags & DBGLOG)
+                       DPRINT2("ppp%d: vj_uncomp_tcp failed, pkt len %d\n",
+                               cp->unit, len);
+                   ++cp->vj_last_ierrors;  /* so we don't reset next time */
                    goto bad;
+               }
 
                /* drop ppp and vj headers off */
                if (mp != np) {
@@ -736,8 +970,14 @@ ppp_comp_rsrv(q)
                /*
                 * "Decompress" a VJ-uncompressed packet.
                 */
-               if (!vj_uncompress_uncomp(dp, &cp->vj_comp))
+               cp->vj_last_ierrors = cp->stats.ppp_ierrors;
+               if (!vj_uncompress_uncomp(dp, hlen, &cp->vj_comp)) {
+                   if (cp->flags & DBGLOG)
+                       DPRINT2("ppp%d: vj_uncomp_uncomp failed, pkt len %d\n",
+                               cp->unit, len);
+                   ++cp->vj_last_ierrors;  /* don't need to reset next time */
                    goto bad;
+               }
                mp->b_rptr[3] = PPP_IP; /* fix up the PPP protocol field */
            }
        }
@@ -793,13 +1033,13 @@ ppp_comp_ccp(q, mp, rcvd)
                if (cp->xstate != NULL
                    && (*cp->xcomp->comp_init)
                        (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
-                        cp->unit, 0, 0))
+                        cp->unit, 0, ((cp->flags & DBGLOG) != 0)))
                    cp->flags |= CCP_COMP_RUN;
            } else {
                if (cp->rstate != NULL
                    && (*cp->rcomp->decomp_init)
                        (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
-                        cp->unit, 0, cp->mru, 0))
+                        cp->unit, 0, cp->mru, ((cp->flags & DBGLOG) != 0)))
                    cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN;
            }
        }
@@ -821,7 +1061,7 @@ ppp_comp_ccp(q, mp, rcvd)
     }
 }
 
-#if DEBUG
+#if 0
 dump_msg(mp)
     mblk_t *mp;
 {