rx-buffer-allocation-corrected-20080813
[openafs.git] / src / rx / rx_packet.c
index 298cf20..398ca06 100644 (file)
@@ -61,7 +61,7 @@ RCSID
 #include "sys/types.h"
 #include <sys/stat.h>
 #include <errno.h>
-#if defined(AFS_NT40_ENV) || defined(AFS_DJGPP_ENV)
+#if defined(AFS_NT40_ENV) 
 #ifdef AFS_NT40_ENV
 #include <winsock2.h>
 #ifndef EWOULDBLOCK
@@ -71,6 +71,7 @@ RCSID
 #include <sys/socket.h>
 #include <netinet/in.h>
 #endif /* AFS_NT40_ENV */
+#include "rx_user.h"
 #include "rx_xmit_nt.h"
 #include <stdlib.h>
 #else
@@ -87,13 +88,7 @@ RCSID
 #include "rx_globals.h"
 #include <lwp.h>
 #include <assert.h>
-#ifdef HAVE_STRING_H
 #include <string.h>
-#else
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
-#endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -108,10 +103,19 @@ struct rx_packet *rx_mallocedP = 0;
 extern char cml_version_number[];
 extern int (*rx_almostSent) ();
 
+static int AllocPacketBufs(int class, int num_pkts, struct rx_queue *q);
+
 static void rxi_SendDebugPacket(struct rx_packet *apacket, osi_socket asocket,
                                afs_int32 ahost, short aport,
                                afs_int32 istack);
 
+static int rxi_FreeDataBufsToQueue(struct rx_packet *p, 
+                                  afs_uint32 first, 
+                                  struct rx_queue * q);
+static int
+rxi_FreeDataBufsTSFPQ(struct rx_packet *p, afs_uint32 first, int flush_global);
+
+
 /* some rules about packets:
  * 1.  When a packet is allocated, the final iov_buf contains room for
  * a security trailer, but iov_len masks that fact.  If the security
@@ -185,13 +189,16 @@ rx_SlowReadPacket(struct rx_packet * packet, unsigned int offset, int resid,
     /* i is the iovec which contains the first little bit of data in which we
      * are interested.  l is the total length of everything prior to this iovec.
      * j is the number of bytes we can safely copy out of this iovec.
+     * offset only applies to the first iovec.
      */
     r = resid;
     while ((resid > 0) && (i < packet->niovecs)) {
        j = MIN(resid, packet->wirevec[i].iov_len - (offset - l));
        memcpy(out, (char *)(packet->wirevec[i].iov_base) + (offset - l), j);
        resid -= j;
-       l += packet->wirevec[i].iov_len;
+        out += j;
+       l += packet->wirevec[i].iov_len;
+       offset = l;
        i++;
     }
 
@@ -220,6 +227,7 @@ rx_SlowWritePacket(struct rx_packet * packet, int offset, int resid, char *in)
     /* i is the iovec which contains the first little bit of data in which we
      * are interested.  l is the total length of everything prior to this iovec.
      * j is the number of bytes we can safely copy out of this iovec.
+     * offset only applies to the first iovec.
      */
     r = resid;
     while ((resid > 0) && (i < RX_MAXWVECS)) {
@@ -231,66 +239,124 @@ rx_SlowWritePacket(struct rx_packet * packet, int offset, int resid, char *in)
        j = MIN(resid, packet->wirevec[i].iov_len - (offset - l));
        memcpy(b, in, j);
        resid -= j;
+        in += j;
        l += packet->wirevec[i].iov_len;
+       offset = l;
        i++;
     }
 
     return (resid ? (r - resid) : r);
 }
 
-static struct rx_packet *
-allocCBuf(int class)
+int
+rxi_AllocPackets(int class, int num_pkts, struct rx_queue * q)
+{
+    register struct rx_packet *p, *np;
+
+    num_pkts = AllocPacketBufs(class, num_pkts, q);
+
+    for (queue_Scan(q, p, np, rx_packet)) {
+        RX_PACKET_IOV_FULLINIT(p);
+    }
+
+    return num_pkts;
+}
+
+#ifdef RX_ENABLE_TSFPQ
+static int
+AllocPacketBufs(int class, int num_pkts, struct rx_queue * q)
+{
+    register struct rx_ts_info_t * rx_ts_info;
+    int transfer, alloc;
+    SPLVAR;
+
+    RX_TS_INFO_GET(rx_ts_info);
+
+    transfer = num_pkts - rx_ts_info->_FPQ.len;
+    if (transfer > 0) {
+        NETPRI;
+        MUTEX_ENTER(&rx_freePktQ_lock);
+       
+       if ((transfer + rx_TSFPQGlobSize) <= rx_nFreePackets) {
+           transfer += rx_TSFPQGlobSize;
+       } else if (transfer <= rx_nFreePackets) {
+           transfer = rx_nFreePackets;
+       } else {
+           /* alloc enough for us, plus a few globs for other threads */
+           alloc = transfer + (3 * rx_TSFPQGlobSize) - rx_nFreePackets;
+           rxi_MorePacketsNoLock(MAX(alloc, rx_initSendWindow));
+           transfer = rx_TSFPQGlobSize;
+       }
+
+       RX_TS_FPQ_GTOL2(rx_ts_info, transfer);
+
+       MUTEX_EXIT(&rx_freePktQ_lock);
+       USERPRI;
+    }
+
+    RX_TS_FPQ_QCHECKOUT(rx_ts_info, num_pkts, q);
+
+    return num_pkts;
+}
+#else /* RX_ENABLE_TSFPQ */
+static int
+AllocPacketBufs(int class, int num_pkts, struct rx_queue * q)
 {
     struct rx_packet *c;
+    int i, overq = 0;
     SPLVAR;
 
     NETPRI;
+
     MUTEX_ENTER(&rx_freePktQ_lock);
 
 #ifdef KERNEL
-    if (rxi_OverQuota(class)) {
-       c = NULL;
+    for (; (num_pkts > 0) && (rxi_OverQuota2(class,num_pkts)); 
+        num_pkts--, overq++);
+
+    if (overq) {
        rxi_NeedMorePackets = TRUE;
-       MUTEX_ENTER(&rx_stats_mutex);
        switch (class) {
        case RX_PACKET_CLASS_RECEIVE:
-           rx_stats.receivePktAllocFailures++;
+           rx_MutexIncrement(rx_stats.receivePktAllocFailures, rx_stats_mutex);
            break;
        case RX_PACKET_CLASS_SEND:
-           rx_stats.sendPktAllocFailures++;
+           rx_MutexIncrement(rx_stats.sendPktAllocFailures, rx_stats_mutex);
            break;
        case RX_PACKET_CLASS_SPECIAL:
-           rx_stats.specialPktAllocFailures++;
+            rx_MutexIncrement(rx_stats.specialPktAllocFailures, rx_stats_mutex);
            break;
        case RX_PACKET_CLASS_RECV_CBUF:
-           rx_stats.receiveCbufPktAllocFailures++;
+           rx_MutexIncrement(rx_stats.receiveCbufPktAllocFailures, rx_stats_mutex);
            break;
        case RX_PACKET_CLASS_SEND_CBUF:
-           rx_stats.sendCbufPktAllocFailures++;
+           rx_MutexIncrement(rx_stats.sendCbufPktAllocFailures, rx_stats_mutex);
            break;
        }
-       MUTEX_EXIT(&rx_stats_mutex);
-       goto done;
     }
 
-    if (queue_IsEmpty(&rx_freePacketQueue)) {
-       c = NULL;
+    if (rx_nFreePackets < num_pkts)
+        num_pkts = rx_nFreePackets;
+
+    if (!num_pkts) {
        rxi_NeedMorePackets = TRUE;
        goto done;
     }
 #else /* KERNEL */
-    if (queue_IsEmpty(&rx_freePacketQueue)) {
-       rxi_MorePacketsNoLock(rx_initSendWindow);
+    if (rx_nFreePackets < num_pkts) {
+        rxi_MorePacketsNoLock(MAX((num_pkts-rx_nFreePackets), rx_initSendWindow));
     }
 #endif /* KERNEL */
 
-    rx_nFreePackets--;
-    c = queue_First(&rx_freePacketQueue, rx_packet);
-    queue_Remove(c);
-    if (!(c->flags & RX_PKTFLAG_FREE))
-       osi_Panic("rxi_AllocPacket: packet not free\n");
-    c->flags = 0;              /* clear RX_PKTFLAG_FREE, initialize the rest */
-    c->header.flags = 0;
+    for (i=0, c=queue_First(&rx_freePacketQueue, rx_packet);
+        i < num_pkts; 
+        i++, c=queue_Next(c, rx_packet)) {
+        RX_FPQ_MARK_USED(c);
+    }
+
+    queue_SplitBeforeAppend(&rx_freePacketQueue,q,c);
+
+    rx_nFreePackets -= num_pkts;
 
 #ifdef KERNEL
   done:
@@ -298,27 +364,106 @@ allocCBuf(int class)
     MUTEX_EXIT(&rx_freePktQ_lock);
 
     USERPRI;
-    return c;
+    return num_pkts;
 }
+#endif /* RX_ENABLE_TSFPQ */
 
 /*
  * Free a packet currently used as a continuation buffer
  */
-void
-rxi_freeCBuf(struct rx_packet *c)
+#ifdef RX_ENABLE_TSFPQ
+/* num_pkts=0 means queue length is unknown */
+int
+rxi_FreePackets(int num_pkts, struct rx_queue * q)
+{
+    register struct rx_ts_info_t * rx_ts_info;
+    register struct rx_packet *c, *nc;
+    SPLVAR;
+
+    osi_Assert(num_pkts >= 0);
+    RX_TS_INFO_GET(rx_ts_info);
+
+    if (!num_pkts) {
+       for (queue_Scan(q, c, nc, rx_packet), num_pkts++) {
+           rxi_FreeDataBufsTSFPQ(c, 2, 0);
+       }
+    } else {
+       for (queue_Scan(q, c, nc, rx_packet)) {
+           rxi_FreeDataBufsTSFPQ(c, 2, 0);
+       }
+    }
+
+    if (num_pkts) {
+       RX_TS_FPQ_QCHECKIN(rx_ts_info, num_pkts, q);
+    }
+
+    if (rx_ts_info->_FPQ.len > rx_TSFPQLocalMax) {
+        NETPRI;
+       MUTEX_ENTER(&rx_freePktQ_lock);
+
+       RX_TS_FPQ_LTOG(rx_ts_info);
+
+       /* Wakeup anyone waiting for packets */
+       rxi_PacketsUnWait();
+
+       MUTEX_EXIT(&rx_freePktQ_lock);
+       USERPRI;
+    }
+
+    return num_pkts;
+}
+#else /* RX_ENABLE_TSFPQ */
+/* num_pkts=0 means queue length is unknown */
+int
+rxi_FreePackets(int num_pkts, struct rx_queue *q)
 {
+    struct rx_queue cbs;
+    register struct rx_packet *p, *np;
+    int qlen = 0;
     SPLVAR;
 
+    osi_Assert(num_pkts >= 0);
+    queue_Init(&cbs);
+
+    if (!num_pkts) {
+        for (queue_Scan(q, p, np, rx_packet), num_pkts++) {
+           if (p->niovecs > 2) {
+               qlen += rxi_FreeDataBufsToQueue(p, 2, &cbs);
+           }
+            RX_FPQ_MARK_FREE(p);
+       }
+       if (!num_pkts)
+           return 0;
+    } else {
+        for (queue_Scan(q, p, np, rx_packet)) {
+           if (p->niovecs > 2) {
+               qlen += rxi_FreeDataBufsToQueue(p, 2, &cbs);
+           }
+            RX_FPQ_MARK_FREE(p);
+       }
+    }
+
+    if (qlen) {
+       queue_SpliceAppend(q, &cbs);
+       qlen += num_pkts;
+    } else
+       qlen = num_pkts;
+
     NETPRI;
     MUTEX_ENTER(&rx_freePktQ_lock);
 
-    rxi_FreePacketNoLock(c);
+    queue_SpliceAppend(&rx_freePacketQueue, q);
+    rx_nFreePackets += qlen;
+
     /* Wakeup anyone waiting for packets */
     rxi_PacketsUnWait();
 
     MUTEX_EXIT(&rx_freePktQ_lock);
     USERPRI;
+
+    return num_pkts;
 }
+#endif /* RX_ENABLE_TSFPQ */
 
 /* this one is kind of awful.
  * In rxkad, the packet has been all shortened, and everything, ready for 
@@ -351,29 +496,81 @@ rxi_RoundUpPacket(struct rx_packet *p, unsigned int nb)
  * returns the number of bytes >0 which it failed to come up with.
  * Don't need to worry about locking on packet, since only
  * one thread can manipulate one at a time. Locking on continution
- * packets is handled by allocCBuf */
+ * packets is handled by AllocPacketBufs */
 /* MTUXXX don't need to go throught the for loop if we can trust niovecs */
 int
 rxi_AllocDataBuf(struct rx_packet *p, int nb, int class)
 {
-    int i;
-
-    for (i = p->niovecs; nb > 0 && i < RX_MAXWVECS; i++) {
-       register struct rx_packet *cb;
-       if ((cb = allocCBuf(class))) {
-           p->wirevec[i].iov_base = (caddr_t) cb->localdata;
-           p->wirevec[i].iov_len = RX_CBUFFERSIZE;
-           nb -= RX_CBUFFERSIZE;
-           p->length += RX_CBUFFERSIZE;
-           p->niovecs++;
-       } else
-           break;
+    int i, nv;
+    struct rx_queue q;
+    register struct rx_packet *cb, *ncb;
+
+    /* compute the number of cbuf's we need */
+    nv = nb / RX_CBUFFERSIZE;
+    if ((nv * RX_CBUFFERSIZE) < nb)
+        nv++;
+    if ((nv + p->niovecs) > RX_MAXWVECS)
+        nv = RX_MAXWVECS - p->niovecs;
+    if (nv < 1)
+        return nb;
+
+    /* allocate buffers */
+    queue_Init(&q);
+    nv = AllocPacketBufs(class, nv, &q);
+
+    /* setup packet iovs */
+    for (i = p->niovecs, queue_Scan(&q, cb, ncb, rx_packet), i++) {
+        queue_Remove(cb);
+        p->wirevec[i].iov_base = (caddr_t) cb->localdata;
+        p->wirevec[i].iov_len = RX_CBUFFERSIZE;
     }
 
+    nb -= (nv * RX_CBUFFERSIZE);
+    p->length += (nv * RX_CBUFFERSIZE);
+    p->niovecs += nv;
+
     return nb;
 }
 
 /* Add more packet buffers */
+#ifdef RX_ENABLE_TSFPQ
+void
+rxi_MorePackets(int apackets)
+{
+    struct rx_packet *p, *e;
+    register struct rx_ts_info_t * rx_ts_info;
+    int getme;
+    SPLVAR;
+
+    getme = apackets * sizeof(struct rx_packet);
+    p = rx_mallocedP = (struct rx_packet *)osi_Alloc(getme);
+    osi_Assert(p);
+
+    PIN(p, getme);             /* XXXXX */
+    memset((char *)p, 0, getme);
+    RX_TS_INFO_GET(rx_ts_info);
+
+    for (e = p + apackets; p < e; p++) {
+        RX_PACKET_IOV_INIT(p);
+       p->niovecs = 2;
+
+       RX_TS_FPQ_CHECKIN(rx_ts_info,p);
+    }
+    rx_ts_info->_FPQ.delta += apackets;
+
+    if (rx_ts_info->_FPQ.len > rx_TSFPQLocalMax) {
+        NETPRI;
+       MUTEX_ENTER(&rx_freePktQ_lock);
+
+       RX_TS_FPQ_LTOG(rx_ts_info);
+       rxi_NeedMorePackets = FALSE;
+       rxi_PacketsUnWait();
+
+       MUTEX_EXIT(&rx_freePktQ_lock);
+       USERPRI;
+    }
+}
+#else /* RX_ENABLE_TSFPQ */
 void
 rxi_MorePackets(int apackets)
 {
@@ -383,18 +580,15 @@ rxi_MorePackets(int apackets)
 
     getme = apackets * sizeof(struct rx_packet);
     p = rx_mallocedP = (struct rx_packet *)osi_Alloc(getme);
+    osi_Assert(p);
 
     PIN(p, getme);             /* XXXXX */
     memset((char *)p, 0, getme);
     NETPRI;
-    AFS_RXGLOCK();
     MUTEX_ENTER(&rx_freePktQ_lock);
 
     for (e = p + apackets; p < e; p++) {
-       p->wirevec[0].iov_base = (char *)(p->wirehead);
-       p->wirevec[0].iov_len = RX_HEADER_SIZE;
-       p->wirevec[1].iov_base = (char *)(p->localdata);
-       p->wirevec[1].iov_len = RX_FIRSTBUFFERSIZE;
+        RX_PACKET_IOV_INIT(p);
        p->flags |= RX_PKTFLAG_FREE;
        p->niovecs = 2;
 
@@ -404,10 +598,49 @@ rxi_MorePackets(int apackets)
     rxi_NeedMorePackets = FALSE;
     rxi_PacketsUnWait();
 
-    AFS_RXGUNLOCK();
     MUTEX_EXIT(&rx_freePktQ_lock);
     USERPRI;
 }
+#endif /* RX_ENABLE_TSFPQ */
+
+#ifdef RX_ENABLE_TSFPQ
+void
+rxi_MorePacketsTSFPQ(int apackets, int flush_global, int num_keep_local)
+{
+    struct rx_packet *p, *e;
+    register struct rx_ts_info_t * rx_ts_info;
+    int getme;
+    SPLVAR;
+
+    getme = apackets * sizeof(struct rx_packet);
+    p = rx_mallocedP = (struct rx_packet *)osi_Alloc(getme);
+
+    PIN(p, getme);             /* XXXXX */
+    memset((char *)p, 0, getme);
+    RX_TS_INFO_GET(rx_ts_info);
+
+    for (e = p + apackets; p < e; p++) {
+        RX_PACKET_IOV_INIT(p);
+       p->niovecs = 2;
+
+       RX_TS_FPQ_CHECKIN(rx_ts_info,p);
+    }
+    rx_ts_info->_FPQ.delta += apackets;
+
+    if (flush_global && 
+        (num_keep_local < apackets)) {
+        NETPRI;
+       MUTEX_ENTER(&rx_freePktQ_lock);
+
+       RX_TS_FPQ_LTOG2(rx_ts_info, (apackets - num_keep_local));
+       rxi_NeedMorePackets = FALSE;
+       rxi_PacketsUnWait();
+
+       MUTEX_EXIT(&rx_freePktQ_lock);
+       USERPRI;
+    }
+}
+#endif /* RX_ENABLE_TSFPQ */
 
 #ifndef KERNEL
 /* Add more packet buffers */
@@ -421,22 +654,32 @@ rxi_MorePacketsNoLock(int apackets)
      * to hold maximal amounts of data */
     apackets += (apackets / 4)
        * ((rx_maxJumboRecvSize - RX_FIRSTBUFFERSIZE) / RX_CBUFFERSIZE);
-    getme = apackets * sizeof(struct rx_packet);
-    p = rx_mallocedP = (struct rx_packet *)osi_Alloc(getme);
-
+    do {
+        getme = apackets * sizeof(struct rx_packet);
+        p = rx_mallocedP = (struct rx_packet *)osi_Alloc(getme);
+       if (p == NULL) {
+            apackets -= apackets / 4;
+            osi_Assert(apackets > 0);
+        }
+    } while(p == NULL);
     memset((char *)p, 0, getme);
 
     for (e = p + apackets; p < e; p++) {
-       p->wirevec[0].iov_base = (char *)(p->wirehead);
-       p->wirevec[0].iov_len = RX_HEADER_SIZE;
-       p->wirevec[1].iov_base = (char *)(p->localdata);
-       p->wirevec[1].iov_len = RX_FIRSTBUFFERSIZE;
+        RX_PACKET_IOV_INIT(p);
        p->flags |= RX_PKTFLAG_FREE;
        p->niovecs = 2;
 
        queue_Append(&rx_freePacketQueue, p);
     }
+
     rx_nFreePackets += apackets;
+#ifdef RX_ENABLE_TSFPQ
+    /* TSFPQ patch also needs to keep track of total packets */
+    MUTEX_ENTER(&rx_stats_mutex);
+    rx_nPackets += apackets;
+    RX_TS_FPQ_COMPUTE_LIMITS;
+    MUTEX_EXIT(&rx_stats_mutex);
+#endif /* RX_ENABLE_TSFPQ */
     rxi_NeedMorePackets = FALSE;
     rxi_PacketsUnWait();
 }
@@ -452,6 +695,44 @@ rxi_FreeAllPackets(void)
     UNPIN(rx_mallocedP, (rx_maxReceiveWindow + 2) * sizeof(struct rx_packet));
 }
 
+#ifdef RX_ENABLE_TSFPQ
+void
+rxi_AdjustLocalPacketsTSFPQ(int num_keep_local, int allow_overcommit)
+{
+    register struct rx_ts_info_t * rx_ts_info;
+    register int xfer;
+    SPLVAR;
+
+    RX_TS_INFO_GET(rx_ts_info);
+
+    if (num_keep_local != rx_ts_info->_FPQ.len) {
+        NETPRI;
+       MUTEX_ENTER(&rx_freePktQ_lock);
+        if (num_keep_local < rx_ts_info->_FPQ.len) {
+            xfer = rx_ts_info->_FPQ.len - num_keep_local;
+           RX_TS_FPQ_LTOG2(rx_ts_info, xfer);
+           rxi_PacketsUnWait();
+        } else {
+            xfer = num_keep_local - rx_ts_info->_FPQ.len;
+            if ((num_keep_local > rx_TSFPQLocalMax) && !allow_overcommit)
+                xfer = rx_TSFPQLocalMax - rx_ts_info->_FPQ.len;
+            if (rx_nFreePackets < xfer) {
+                rxi_MorePacketsNoLock(xfer - rx_nFreePackets);
+            }
+            RX_TS_FPQ_GTOL2(rx_ts_info, xfer);
+        }
+       MUTEX_EXIT(&rx_freePktQ_lock);
+       USERPRI;
+    }
+}
+
+void
+rxi_FlushLocalPacketsTSFPQ(void)
+{
+    rxi_AdjustLocalPacketsTSFPQ(0, 0);
+}
+#endif /* RX_ENABLE_TSFPQ */
+
 /* Allocate more packets iff we need more continuation buffers */
 /* In kernel, can't page in memory with interrupts disabled, so we
  * don't use the event mechanism. */
@@ -477,32 +758,105 @@ rx_CheckPackets(void)
    */
 
 /* Actually free the packet p. */
+#ifdef RX_ENABLE_TSFPQ
+void
+rxi_FreePacketNoLock(struct rx_packet *p)
+{
+    register struct rx_ts_info_t * rx_ts_info;
+    dpf(("Free %lx\n", (unsigned long)p));
+
+    RX_TS_INFO_GET(rx_ts_info);
+    RX_TS_FPQ_CHECKIN(rx_ts_info,p);
+    if (rx_ts_info->_FPQ.len > rx_TSFPQLocalMax) {
+        RX_TS_FPQ_LTOG(rx_ts_info);
+    }
+}
+#else /* RX_ENABLE_TSFPQ */
 void
 rxi_FreePacketNoLock(struct rx_packet *p)
 {
-    dpf(("Free %x\n", (int)p));
+    dpf(("Free %lx\n", (unsigned long)p));
 
-    if (p->flags & RX_PKTFLAG_FREE)
-       osi_Panic("rxi_FreePacketNoLock: packet already free\n");
+    RX_FPQ_MARK_FREE(p);
     rx_nFreePackets++;
-    p->flags |= RX_PKTFLAG_FREE;
     queue_Append(&rx_freePacketQueue, p);
 }
+#endif /* RX_ENABLE_TSFPQ */
+
+#ifdef RX_ENABLE_TSFPQ
+void
+rxi_FreePacketTSFPQ(struct rx_packet *p, int flush_global)
+{
+    register struct rx_ts_info_t * rx_ts_info;
+    dpf(("Free %lx\n", (unsigned long)p));
+
+    RX_TS_INFO_GET(rx_ts_info);
+    RX_TS_FPQ_CHECKIN(rx_ts_info,p);
+
+    if (flush_global && (rx_ts_info->_FPQ.len > rx_TSFPQLocalMax)) {
+        NETPRI;
+       MUTEX_ENTER(&rx_freePktQ_lock);
+
+       RX_TS_FPQ_LTOG(rx_ts_info);
+
+       /* Wakeup anyone waiting for packets */
+       rxi_PacketsUnWait();
+
+       MUTEX_EXIT(&rx_freePktQ_lock);
+       USERPRI;
+    }
+}
+#endif /* RX_ENABLE_TSFPQ */
+
+/* 
+ * free continuation buffers off a packet into a queue
+ *
+ * [IN] p      -- packet from which continuation buffers will be freed
+ * [IN] first  -- iovec offset of first continuation buffer to free
+ * [IN] q      -- queue into which continuation buffers will be chained
+ *
+ * returns:
+ *   number of continuation buffers freed
+ */
+static int
+rxi_FreeDataBufsToQueue(struct rx_packet *p, afs_uint32 first, struct rx_queue * q)
+{
+    struct iovec *iov;
+    struct rx_packet * cb;
+    int count = 0;
+
+    for (first = MAX(2, first); first < p->niovecs; first++, count++) {
+       iov = &p->wirevec[first];
+       if (!iov->iov_base)
+           osi_Panic("rxi_FreeDataBufsToQueue: unexpected NULL iov");
+       cb = RX_CBUF_TO_PACKET(iov->iov_base, p);
+       RX_FPQ_MARK_FREE(cb);
+       queue_Append(q, cb);
+    }
+    p->length = 0;
+    p->niovecs = 0;
+
+    return count;
+}
 
+/*
+ * free packet continuation buffers into the global free packet pool
+ *
+ * [IN] p      -- packet from which to free continuation buffers
+ * [IN] first  -- iovec offset of first continuation buffer to free
+ *
+ * returns:
+ *   zero always
+ */
 int
-rxi_FreeDataBufsNoLock(struct rx_packet *p, int first)
+rxi_FreeDataBufsNoLock(struct rx_packet *p, afs_uint32 first)
 {
-    struct iovec *iov, *end;
+    struct iovec *iov;
 
-    if (first != 1)            /* MTUXXX */
-       osi_Panic("FreeDataBufs 1: first must be 1");
-    iov = &p->wirevec[1];
-    end = iov + (p->niovecs - 1);
-    if (iov->iov_base != (caddr_t) p->localdata)       /* MTUXXX */
-       osi_Panic("FreeDataBufs 2: vec 1 must be localdata");
-    for (iov++; iov < end; iov++) {
+    for (first = MAX(2, first); first < p->niovecs; first++) {
+       iov = &p->wirevec[first];
        if (!iov->iov_base)
-           osi_Panic("FreeDataBufs 3: vecs 2-niovecs must not be NULL");
+           osi_Panic("rxi_FreeDataBufsNoLock: unexpected NULL iov");
        rxi_FreePacketNoLock(RX_CBUF_TO_PACKET(iov->iov_base, p));
     }
     p->length = 0;
@@ -511,6 +865,51 @@ rxi_FreeDataBufsNoLock(struct rx_packet *p, int first)
     return 0;
 }
 
+#ifdef RX_ENABLE_TSFPQ
+/*
+ * free packet continuation buffers into the thread-local free pool
+ *
+ * [IN] p             -- packet from which continuation buffers will be freed
+ * [IN] first         -- iovec offset of first continuation buffer to free
+ * [IN] flush_global  -- if nonzero, we will flush overquota packets to the
+ *                       global free pool before returning
+ *
+ * returns:
+ *   zero always
+ */
+static int
+rxi_FreeDataBufsTSFPQ(struct rx_packet *p, afs_uint32 first, int flush_global)
+{
+    struct iovec *iov;
+    register struct rx_ts_info_t * rx_ts_info;
+
+    RX_TS_INFO_GET(rx_ts_info);
+
+    for (first = MAX(2, first); first < p->niovecs; first++) {
+       iov = &p->wirevec[first];
+       if (!iov->iov_base)
+           osi_Panic("rxi_FreeDataBufsTSFPQ: unexpected NULL iov");
+       RX_TS_FPQ_CHECKIN(rx_ts_info,RX_CBUF_TO_PACKET(iov->iov_base, p));
+    }
+    p->length = 0;
+    p->niovecs = 0;
+
+    if (flush_global && (rx_ts_info->_FPQ.len > rx_TSFPQLocalMax)) {
+        NETPRI;
+       MUTEX_ENTER(&rx_freePktQ_lock);
+
+       RX_TS_FPQ_LTOG(rx_ts_info);
+
+       /* Wakeup anyone waiting for packets */
+       rxi_PacketsUnWait();
+
+       MUTEX_EXIT(&rx_freePktQ_lock);
+       USERPRI;
+    }
+    return 0;
+}
+#endif /* RX_ENABLE_TSFPQ */
+
 int rxi_nBadIovecs = 0;
 
 /* rxi_RestoreDataBufs 
@@ -524,10 +923,7 @@ rxi_RestoreDataBufs(struct rx_packet *p)
     int i;
     struct iovec *iov = &p->wirevec[2];
 
-    p->wirevec[0].iov_base = (char *)(p->wirehead);
-    p->wirevec[0].iov_len = RX_HEADER_SIZE;
-    p->wirevec[1].iov_base = (char *)(p->localdata);
-    p->wirevec[1].iov_len = RX_FIRSTBUFFERSIZE;
+    RX_PACKET_IOV_INIT(p);
 
     for (i = 2, iov = &p->wirevec[2]; i < p->niovecs; i++, iov++) {
        if (!iov->iov_base) {
@@ -539,6 +935,53 @@ rxi_RestoreDataBufs(struct rx_packet *p)
     }
 }
 
+#ifdef RX_ENABLE_TSFPQ
+int
+rxi_TrimDataBufs(struct rx_packet *p, int first)
+{
+    int length;
+    struct iovec *iov, *end;
+    register struct rx_ts_info_t * rx_ts_info;
+    SPLVAR;
+
+    if (first != 1)
+       osi_Panic("TrimDataBufs 1: first must be 1");
+
+    /* Skip over continuation buffers containing message data */
+    iov = &p->wirevec[2];
+    end = iov + (p->niovecs - 2);
+    length = p->length - p->wirevec[1].iov_len;
+    for (; iov < end && length > 0; iov++) {
+       if (!iov->iov_base)
+           osi_Panic("TrimDataBufs 3: vecs 1-niovecs must not be NULL");
+       length -= iov->iov_len;
+    }
+
+    /* iov now points to the first empty data buffer. */
+    if (iov >= end)
+       return 0;
+
+    RX_TS_INFO_GET(rx_ts_info);
+    for (; iov < end; iov++) {
+       if (!iov->iov_base)
+           osi_Panic("TrimDataBufs 4: vecs 2-niovecs must not be NULL");
+       RX_TS_FPQ_CHECKIN(rx_ts_info,RX_CBUF_TO_PACKET(iov->iov_base, p));
+       p->niovecs--;
+    }
+    if (rx_ts_info->_FPQ.len > rx_TSFPQLocalMax) {
+        NETPRI;
+        MUTEX_ENTER(&rx_freePktQ_lock);
+
+       RX_TS_FPQ_LTOG(rx_ts_info);
+        rxi_PacketsUnWait();
+
+        MUTEX_EXIT(&rx_freePktQ_lock);
+       USERPRI;
+    }
+
+    return 0;
+}
+#else /* RX_ENABLE_TSFPQ */
 int
 rxi_TrimDataBufs(struct rx_packet *p, int first)
 {
@@ -579,9 +1022,18 @@ rxi_TrimDataBufs(struct rx_packet *p, int first)
 
     return 0;
 }
+#endif /* RX_ENABLE_TSFPQ */
 
 /* Free the packet p.  P is assumed not to be on any queue, i.e.
  * remove it yourself first if you call this routine. */
+#ifdef RX_ENABLE_TSFPQ
+void
+rxi_FreePacket(struct rx_packet *p)
+{
+    rxi_FreeDataBufsTSFPQ(p, 2, 0);
+    rxi_FreePacketTSFPQ(p, RX_TS_FPQ_FLUSH_GLOBAL);
+}
+#else /* RX_ENABLE_TSFPQ */
 void
 rxi_FreePacket(struct rx_packet *p)
 {
@@ -590,7 +1042,7 @@ rxi_FreePacket(struct rx_packet *p)
     NETPRI;
     MUTEX_ENTER(&rx_freePktQ_lock);
 
-    rxi_FreeDataBufsNoLock(p, 1);
+    rxi_FreeDataBufsNoLock(p, 2);
     rxi_FreePacketNoLock(p);
     /* Wakeup anyone waiting for packets */
     rxi_PacketsUnWait();
@@ -598,46 +1050,103 @@ rxi_FreePacket(struct rx_packet *p)
     MUTEX_EXIT(&rx_freePktQ_lock);
     USERPRI;
 }
-
+#endif /* RX_ENABLE_TSFPQ */
 
 /* rxi_AllocPacket sets up p->length so it reflects the number of 
  * bytes in the packet at this point, **not including** the header.
  * The header is absolutely necessary, besides, this is the way the
  * length field is usually used */
+#ifdef RX_ENABLE_TSFPQ
 struct rx_packet *
 rxi_AllocPacketNoLock(int class)
 {
     register struct rx_packet *p;
+    register struct rx_ts_info_t * rx_ts_info;
+
+    RX_TS_INFO_GET(rx_ts_info);
 
 #ifdef KERNEL
     if (rxi_OverQuota(class)) {
        rxi_NeedMorePackets = TRUE;
-       MUTEX_ENTER(&rx_stats_mutex);
        switch (class) {
        case RX_PACKET_CLASS_RECEIVE:
-           rx_stats.receivePktAllocFailures++;
+           rx_MutexIncrement(rx_stats.receivePktAllocFailures, rx_stats_mutex);
            break;
        case RX_PACKET_CLASS_SEND:
-           rx_stats.sendPktAllocFailures++;
+           rx_MutexIncrement(rx_stats.sendPktAllocFailures, rx_stats_mutex);
            break;
        case RX_PACKET_CLASS_SPECIAL:
-           rx_stats.specialPktAllocFailures++;
+            rx_MutexIncrement(rx_stats.specialPktAllocFailures, rx_stats_mutex);
            break;
        case RX_PACKET_CLASS_RECV_CBUF:
-           rx_stats.receiveCbufPktAllocFailures++;
+           rx_MutexIncrement(rx_stats.receiveCbufPktAllocFailures, rx_stats_mutex);
            break;
        case RX_PACKET_CLASS_SEND_CBUF:
-           rx_stats.sendCbufPktAllocFailures++;
+           rx_MutexIncrement(rx_stats.sendCbufPktAllocFailures, rx_stats_mutex);
+           break;
+       }
+        return (struct rx_packet *)0;
+    }
+#endif /* KERNEL */
+
+    rx_MutexIncrement(rx_stats.packetRequests, rx_stats_mutex);
+    if (queue_IsEmpty(&rx_ts_info->_FPQ)) {
+
+#ifdef KERNEL
+        if (queue_IsEmpty(&rx_freePacketQueue))
+           osi_Panic("rxi_AllocPacket error");
+#else /* KERNEL */
+        if (queue_IsEmpty(&rx_freePacketQueue))
+           rxi_MorePacketsNoLock(rx_initSendWindow);
+#endif /* KERNEL */
+
+
+       RX_TS_FPQ_GTOL(rx_ts_info);
+    }
+
+    RX_TS_FPQ_CHECKOUT(rx_ts_info,p);
+
+    dpf(("Alloc %lx, class %d\n", (unsigned long)p, class));
+
+
+    /* have to do this here because rx_FlushWrite fiddles with the iovs in
+     * order to truncate outbound packets.  In the near future, may need 
+     * to allocate bufs from a static pool here, and/or in AllocSendPacket
+     */
+    RX_PACKET_IOV_FULLINIT(p);
+    return p;
+}
+#else /* RX_ENABLE_TSFPQ */
+struct rx_packet *
+rxi_AllocPacketNoLock(int class)
+{
+    register struct rx_packet *p;
+
+#ifdef KERNEL
+    if (rxi_OverQuota(class)) {
+       rxi_NeedMorePackets = TRUE;
+       switch (class) {
+       case RX_PACKET_CLASS_RECEIVE:
+           rx_MutexIncrement(rx_stats.receivePktAllocFailures, rx_stats_mutex);
+           break;
+       case RX_PACKET_CLASS_SEND:
+           rx_MutexIncrement(rx_stats.sendPktAllocFailures, rx_stats_mutex);
+           break;
+       case RX_PACKET_CLASS_SPECIAL:
+            rx_MutexIncrement(rx_stats.specialPktAllocFailures, rx_stats_mutex);
+           break;
+       case RX_PACKET_CLASS_RECV_CBUF:
+           rx_MutexIncrement(rx_stats.receiveCbufPktAllocFailures, rx_stats_mutex);
+           break;
+       case RX_PACKET_CLASS_SEND_CBUF:
+           rx_MutexIncrement(rx_stats.sendCbufPktAllocFailures, rx_stats_mutex);
            break;
        }
-       MUTEX_EXIT(&rx_stats_mutex);
        return (struct rx_packet *)0;
     }
 #endif /* KERNEL */
 
-    MUTEX_ENTER(&rx_stats_mutex);
-    rx_stats.packetRequests++;
-    MUTEX_EXIT(&rx_stats_mutex);
+    rx_MutexIncrement(rx_stats.packetRequests, rx_stats_mutex);
 
 #ifdef KERNEL
     if (queue_IsEmpty(&rx_freePacketQueue))
@@ -649,28 +1158,67 @@ rxi_AllocPacketNoLock(int class)
 
     rx_nFreePackets--;
     p = queue_First(&rx_freePacketQueue, rx_packet);
-    if (!(p->flags & RX_PKTFLAG_FREE))
-       osi_Panic("rxi_AllocPacket: packet not free\n");
+    queue_Remove(p);
+    RX_FPQ_MARK_USED(p);
 
-    dpf(("Alloc %x, class %d\n", (int)p, class));
+    dpf(("Alloc %lx, class %d\n", (unsigned long)p, class));
 
-    queue_Remove(p);
-    p->flags = 0;              /* clear RX_PKTFLAG_FREE, initialize the rest */
-    p->header.flags = 0;
 
     /* have to do this here because rx_FlushWrite fiddles with the iovs in
      * order to truncate outbound packets.  In the near future, may need 
      * to allocate bufs from a static pool here, and/or in AllocSendPacket
      */
-    p->wirevec[0].iov_base = (char *)(p->wirehead);
-    p->wirevec[0].iov_len = RX_HEADER_SIZE;
-    p->wirevec[1].iov_base = (char *)(p->localdata);
-    p->wirevec[1].iov_len = RX_FIRSTBUFFERSIZE;
-    p->niovecs = 2;
-    p->length = RX_FIRSTBUFFERSIZE;
+    RX_PACKET_IOV_FULLINIT(p);
+    return p;
+}
+#endif /* RX_ENABLE_TSFPQ */
+
+#ifdef RX_ENABLE_TSFPQ
+struct rx_packet *
+rxi_AllocPacketTSFPQ(int class, int pull_global)
+{
+    register struct rx_packet *p;
+    register struct rx_ts_info_t * rx_ts_info;
+
+    RX_TS_INFO_GET(rx_ts_info);
+
+    rx_MutexIncrement(rx_stats.packetRequests, rx_stats_mutex);
+    if (pull_global && queue_IsEmpty(&rx_ts_info->_FPQ)) {
+        MUTEX_ENTER(&rx_freePktQ_lock);
+
+        if (queue_IsEmpty(&rx_freePacketQueue))
+            rxi_MorePacketsNoLock(rx_initSendWindow);
+
+       RX_TS_FPQ_GTOL(rx_ts_info);
+
+        MUTEX_EXIT(&rx_freePktQ_lock);
+    } else if (queue_IsEmpty(&rx_ts_info->_FPQ)) {
+        return NULL;
+    }
+
+    RX_TS_FPQ_CHECKOUT(rx_ts_info,p);
+
+    dpf(("Alloc %lx, class %d\n", (unsigned long)p, class));
+
+    /* have to do this here because rx_FlushWrite fiddles with the iovs in
+     * order to truncate outbound packets.  In the near future, may need 
+     * to allocate bufs from a static pool here, and/or in AllocSendPacket
+     */
+    RX_PACKET_IOV_FULLINIT(p);
     return p;
 }
+#endif /* RX_ENABLE_TSFPQ */
 
+#ifdef RX_ENABLE_TSFPQ
+struct rx_packet *
+rxi_AllocPacket(int class)
+{
+    register struct rx_packet *p;
+
+    p = rxi_AllocPacketTSFPQ(class, RX_TS_FPQ_PULL_GLOBAL);
+    return p;
+}
+#else /* RX_ENABLE_TSFPQ */
 struct rx_packet *
 rxi_AllocPacket(int class)
 {
@@ -681,6 +1229,7 @@ rxi_AllocPacket(int class)
     MUTEX_EXIT(&rx_freePktQ_lock);
     return p;
 }
+#endif /* RX_ENABLE_TSFPQ */
 
 /* This guy comes up with as many buffers as it {takes,can get} given
  * the MTU for this call. It also sets the packet length before
@@ -700,6 +1249,28 @@ rxi_AllocSendPacket(register struct rx_call *call, int want)
        rx_GetSecurityHeaderSize(rx_ConnectionOf(call)) +
        rx_GetSecurityMaxTrailerSize(rx_ConnectionOf(call));
 
+#ifdef RX_ENABLE_TSFPQ
+    if ((p = rxi_AllocPacketTSFPQ(RX_PACKET_CLASS_SEND, 0))) {
+        want += delta;
+       want = MIN(want, mud);
+
+       if ((unsigned)want > p->length)
+           (void)rxi_AllocDataBuf(p, (want - p->length),
+                                  RX_PACKET_CLASS_SEND_CBUF);
+
+       if ((unsigned)p->length > mud)
+            p->length = mud;
+
+       if (delta >= p->length) {
+           rxi_FreePacket(p);
+           p = NULL;
+       } else {
+           p->length -= delta;
+       }
+       return p;
+    }
+#endif /* RX_ENABLE_TSFPQ */
+
     while (!(call->error)) {
        MUTEX_ENTER(&rx_freePktQ_lock);
        /* if an error occurred, or we get the packet we want, we're done */
@@ -751,7 +1322,10 @@ rxi_AllocSendPacket(register struct rx_call *call, int want)
 }
 
 #ifndef KERNEL
-
+#ifdef AFS_NT40_ENV     
+/* Windows does not use file descriptors. */
+#define CountFDs(amax) 0
+#else
 /* count the number of used FDs */
 static int
 CountFDs(register int amax)
@@ -768,7 +1342,7 @@ CountFDs(register int amax)
     }
     return count;
 }
-
+#endif /* AFS_NT40_ENV */
 #else /* KERNEL */
 
 #define CountFDs(amax) amax
@@ -783,7 +1357,7 @@ CountFDs(register int amax)
  * the data length of the packet is stored in the packet structure.
  * The header is decoded. */
 int
-rxi_ReadPacket(int socket, register struct rx_packet *p, afs_uint32 * host,
+rxi_ReadPacket(osi_socket socket, register struct rx_packet *p, afs_uint32 * host,
               u_short * port)
 {
     struct sockaddr_in from;
@@ -827,22 +1401,35 @@ rxi_ReadPacket(int socket, register struct rx_packet *p, afs_uint32 * host,
 
     p->length = (nbytes - RX_HEADER_SIZE);
     if ((nbytes > tlen) || (p->length & 0x8000)) {     /* Bogus packet */
-       if (nbytes > 0)
-           rxi_MorePackets(rx_initSendWindow);
-       else if (nbytes < 0 && errno == EWOULDBLOCK) {
-           MUTEX_ENTER(&rx_stats_mutex);
-           rx_stats.noPacketOnRead++;
-           MUTEX_EXIT(&rx_stats_mutex);
-       } else {
+       if (nbytes < 0 && errno == EWOULDBLOCK) {
+            rx_MutexIncrement(rx_stats.noPacketOnRead, rx_stats_mutex);
+       } else if (nbytes <= 0) {
            MUTEX_ENTER(&rx_stats_mutex);
            rx_stats.bogusPacketOnRead++;
            rx_stats.bogusHost = from.sin_addr.s_addr;
            MUTEX_EXIT(&rx_stats_mutex);
-           dpf(("B: bogus packet from [%x,%d] nb=%d", from.sin_addr.s_addr,
-                from.sin_port, nbytes));
+           dpf(("B: bogus packet from [%x,%d] nb=%d", ntohl(from.sin_addr.s_addr),
+                ntohs(from.sin_port), nbytes));
        }
        return 0;
-    } else {
+    } 
+#ifdef RXDEBUG
+    else if ((rx_intentionallyDroppedOnReadPer100 > 0)
+               && (random() % 100 < rx_intentionallyDroppedOnReadPer100)) {
+       rxi_DecodePacketHeader(p);
+
+       *host = from.sin_addr.s_addr;
+       *port = from.sin_port;
+
+       dpf(("Dropped %d %s: %x.%u.%u.%u.%u.%u.%u flags %d len %d",
+             p->header.serial, rx_packetTypes[p->header.type - 1], ntohl(*host), ntohs(*port), p->header.serial, 
+             p->header.epoch, p->header.cid, p->header.callNumber, p->header.seq, p->header.flags, 
+             p->length));
+       rxi_TrimDataBufs(p, 1);
+       return 0;
+    } 
+#endif
+    else {
        /* Extract packet header. */
        rxi_DecodePacketHeader(p);
 
@@ -850,9 +1437,7 @@ rxi_ReadPacket(int socket, register struct rx_packet *p, afs_uint32 * host,
        *port = from.sin_port;
        if (p->header.type > 0 && p->header.type < RX_N_PACKET_TYPES) {
            struct rx_peer *peer;
-           MUTEX_ENTER(&rx_stats_mutex);
-           rx_stats.packetsRead[p->header.type - 1]++;
-           MUTEX_EXIT(&rx_stats_mutex);
+            rx_MutexIncrement(rx_stats.packetsRead[p->header.type - 1], rx_stats_mutex);
            /*
             * Try to look up this peer structure.  If it doesn't exist,
             * don't create a new one - 
@@ -865,7 +1450,11 @@ rxi_ReadPacket(int socket, register struct rx_packet *p, afs_uint32 * host,
             * never be cleaned up.
             */
            peer = rxi_FindPeer(*host, *port, 0, 0);
-           if (peer) {
+           /* Since this may not be associated with a connection,
+            * it may have no refCount, meaning we could race with
+            * ReapConnections
+            */
+           if (peer && (peer->refCount > 0)) {
                MUTEX_ENTER(&peer->peer_lock);
                hadd32(peer->bytesReceived, p->length);
                MUTEX_EXIT(&peer->peer_lock);
@@ -957,6 +1546,7 @@ osi_NetSend(osi_socket socket, void *addr, struct iovec *dvec, int nvecs,
            int length, int istack)
 {
     struct msghdr msg;
+       int ret;
 
     memset(&msg, 0, sizeof(msg));
     msg.msg_iov = dvec;
@@ -964,9 +1554,9 @@ osi_NetSend(osi_socket socket, void *addr, struct iovec *dvec, int nvecs,
     msg.msg_name = addr;
     msg.msg_namelen = sizeof(struct sockaddr_in);
 
-    rxi_Sendmsg(socket, &msg, 0);
+    ret = rxi_Sendmsg(socket, &msg, 0);
 
-    return 0;
+    return ret;
 }
 #elif !defined(UKERNEL)
 /*
@@ -1035,7 +1625,7 @@ cpytoiovec(mblk_t * mp, int off, int len, register struct iovec *iovs,
 #define m_cpytoc(a, b, c, d)  cpytoc(a, b, c, d)
 #define m_cpytoiovec(a, b, c, d, e) cpytoiovec(a, b, c, d, e)
 #else
-#if !defined(AFS_LINUX20_ENV)
+#if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN80_ENV)
 static int
 m_cpytoiovec(struct mbuf *m, int off, int len, struct iovec iovs[], int niovs)
 {
@@ -1091,7 +1681,7 @@ m_cpytoiovec(struct mbuf *m, int off, int len, struct iovec iovs[], int niovs)
 #endif /* LINUX */
 #endif /* AFS_SUN5_ENV */
 
-#if !defined(AFS_LINUX20_ENV)
+#if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN80_ENV)
 int
 rx_mb_to_packet(amb, free, hdr_len, data_len, phandle)
 #if defined(AFS_SUN5_ENV) || defined(AFS_HPUX110_ENV)
@@ -1477,7 +2067,6 @@ rxi_SendDebugPacket(struct rx_packet *apacket, osi_socket asocket,
        } else
            nbytes -= apacket->wirevec[i].iov_len;
     }
-    AFS_RXGUNLOCK();
 #ifdef KERNEL
 #ifdef RX_KERNEL_TRACE
     if (ICL_SETACTIVE(afs_iclSetp)) {
@@ -1509,7 +2098,6 @@ rxi_SendDebugPacket(struct rx_packet *apacket, osi_socket asocket,
        AFS_GLOCK();
 #endif
 #endif
-    AFS_RXGLOCK();
     if (saven) {               /* means we truncated the packet above. */
        apacket->wirevec[i - 1].iov_len = savelen;
        apacket->niovecs = saven;
@@ -1595,7 +2183,6 @@ rxi_SendPacket(struct rx_call *call, struct rx_connection *conn,
         * blocking socket, but unfortunately the interface doesn't
         * allow us to have the socket block in send mode, and not
         * block in receive mode */
-       AFS_RXGUNLOCK();
 #ifdef KERNEL
        waslocked = ISAFS_GLOCK();
 #ifdef RX_KERNEL_TRACE
@@ -1615,22 +2202,27 @@ rxi_SendPacket(struct rx_call *call, struct rx_connection *conn,
             osi_NetSend(socket, &addr, p->wirevec, p->niovecs,
                         p->length + RX_HEADER_SIZE, istack)) != 0) {
            /* send failed, so let's hurry up the resend, eh? */
-           MUTEX_ENTER(&rx_stats_mutex);
-           rx_stats.netSendFailures++;
-           MUTEX_EXIT(&rx_stats_mutex);
+            rx_MutexIncrement(rx_stats.netSendFailures, rx_stats_mutex);
            p->retryTime = p->timeSent; /* resend it very soon */
            clock_Addmsec(&(p->retryTime),
                          10 + (((afs_uint32) p->backoff) << 8));
-
-#if defined(KERNEL) && defined(AFS_LINUX20_ENV)
-           /* Linux is nice -- it can tell us right away that we cannot
-            * reach this recipient by returning an ENETUNREACH error
-            * code.  So, when this happens let's "down" the host NOW so
+           /* Some systems are nice and tell us right away that we cannot
+            * reach this recipient by returning an error code. 
+            * So, when this happens let's "down" the host NOW so
             * we don't sit around waiting for this host to timeout later.
             */
-           if (call && code == -ENETUNREACH)
-               call->lastReceiveTime = 0;
+           if (call && 
+#ifdef AFS_NT40_ENV
+               code == -1 && WSAGetLastError() == WSAEHOSTUNREACH
+#elif defined(AFS_LINUX20_ENV) && defined(KERNEL)
+               code == -ENETUNREACH
+#elif defined(AFS_DARWIN_ENV) && defined(KERNEL)
+               code == EHOSTUNREACH
+#else
+               0
 #endif
+               )
+               call->lastReceiveTime = 0;
        }
 #ifdef KERNEL
 #ifdef RX_KERNEL_TRACE
@@ -1646,14 +2238,11 @@ rxi_SendPacket(struct rx_call *call, struct rx_connection *conn,
            AFS_GLOCK();
 #endif
 #endif
-       AFS_RXGLOCK();
 #ifdef RXDEBUG
     }
-    dpf(("%c %d %s: %x.%u.%u.%u.%u.%u.%u flags %d, packet %x resend %d.%0.3d len %d", deliveryType, p->header.serial, rx_packetTypes[p->header.type - 1], peer->host, peer->port, p->header.serial, p->header.epoch, p->header.cid, p->header.callNumber, p->header.seq, p->header.flags, (int)p, p->retryTime.sec, p->retryTime.usec / 1000, p->length));
+    dpf(("%c %d %s: %x.%u.%u.%u.%u.%u.%u flags %d, packet %lx resend %d.%0.3d len %d", deliveryType, p->header.serial, rx_packetTypes[p->header.type - 1], ntohl(peer->host), ntohs(peer->port), p->header.serial, p->header.epoch, p->header.cid, p->header.callNumber, p->header.seq, p->header.flags, (unsigned long)p, p->retryTime.sec, p->retryTime.usec / 1000, p->length));
 #endif
-    MUTEX_ENTER(&rx_stats_mutex);
-    rx_stats.packetsSent[p->header.type - 1]++;
-    MUTEX_EXIT(&rx_stats_mutex);
+    rx_MutexIncrement(rx_stats.packetsSent[p->header.type - 1], rx_stats_mutex);
     MUTEX_ENTER(&peer->peer_lock);
     hadd32(peer->bytesSent, p->length);
     MUTEX_EXIT(&peer->peer_lock);
@@ -1789,7 +2378,6 @@ rxi_SendPacketList(struct rx_call *call, struct rx_connection *conn,
         * blocking socket, but unfortunately the interface doesn't
         * allow us to have the socket block in send mode, and not
         * block in receive mode */
-       AFS_RXGUNLOCK();
 #if    defined(AFS_SUN5_ENV) && defined(KERNEL)
        waslocked = ISAFS_GLOCK();
        if (!istack && waslocked)
@@ -1799,43 +2387,45 @@ rxi_SendPacketList(struct rx_call *call, struct rx_connection *conn,
             osi_NetSend(socket, &addr, &wirevec[0], len + 1, length,
                         istack)) != 0) {
            /* send failed, so let's hurry up the resend, eh? */
-           MUTEX_ENTER(&rx_stats_mutex);
-           rx_stats.netSendFailures++;
-           MUTEX_EXIT(&rx_stats_mutex);
+            rx_MutexIncrement(rx_stats.netSendFailures, rx_stats_mutex);
            for (i = 0; i < len; i++) {
                p = list[i];
                p->retryTime = p->timeSent;     /* resend it very soon */
                clock_Addmsec(&(p->retryTime),
                              10 + (((afs_uint32) p->backoff) << 8));
            }
-#if defined(KERNEL) && defined(AFS_LINUX20_ENV)
-           /* Linux is nice -- it can tell us right away that we cannot
-            * reach this recipient by returning an ENETUNREACH error
-            * code.  So, when this happens let's "down" the host NOW so
+           /* Some systems are nice and tell us right away that we cannot
+            * reach this recipient by returning an error code. 
+            * So, when this happens let's "down" the host NOW so
             * we don't sit around waiting for this host to timeout later.
             */
-           if (call && code == -ENETUNREACH)
-               call->lastReceiveTime = 0;
+           if (call && 
+#ifdef AFS_NT40_ENV
+               code == -1 && WSAGetLastError() == WSAEHOSTUNREACH
+#elif defined(AFS_LINUX20_ENV) && defined(KERNEL)
+               code == -ENETUNREACH
+#elif defined(AFS_DARWIN_ENV) && defined(KERNEL)
+               code == EHOSTUNREACH
+#else
+               0
 #endif
+               )
+               call->lastReceiveTime = 0;
        }
 #if    defined(AFS_SUN5_ENV) && defined(KERNEL)
        if (!istack && waslocked)
            AFS_GLOCK();
 #endif
-       AFS_RXGLOCK();
 #ifdef RXDEBUG
     }
 
     assert(p != NULL);
 
-    dpf(("%c %d %s: %x.%u.%u.%u.%u.%u.%u flags %d, packet %x resend %d.%0.3d len %d", deliveryType, p->header.serial, rx_packetTypes[p->header.type - 1], peer->host, peer->port, p->header.serial, p->header.epoch, p->header.cid, p->header.callNumber, p->header.seq, p->header.flags, (int)p, p->retryTime.sec, p->retryTime.usec / 1000, p->length));
+    dpf(("%c %d %s: %x.%u.%u.%u.%u.%u.%u flags %d, packet %lx resend %d.%0.3d len %d", deliveryType, p->header.serial, rx_packetTypes[p->header.type - 1], ntohl(peer->host), ntohs(peer->port), p->header.serial, p->header.epoch, p->header.cid, p->header.callNumber, p->header.seq, p->header.flags, (unsigned long)p, p->retryTime.sec, p->retryTime.usec / 1000, p->length));
 
 #endif
-    MUTEX_ENTER(&rx_stats_mutex);
-    rx_stats.packetsSent[p->header.type - 1]++;
-    MUTEX_EXIT(&rx_stats_mutex);
+    rx_MutexIncrement(rx_stats.packetsSent[p->header.type - 1], rx_stats_mutex);
     MUTEX_ENTER(&peer->peer_lock);
-
     hadd32(peer->bytesSent, p->length);
     MUTEX_EXIT(&peer->peer_lock);
 }
@@ -1984,13 +2574,18 @@ rxi_PrepareSendPacket(register struct rx_call *call,
                      register struct rx_packet *p, register int last)
 {
     register struct rx_connection *conn = call->conn;
-    int i, j;
+    int i;
     ssize_t len;               /* len must be a signed type; it can go negative */
 
     p->flags &= ~RX_PKTFLAG_ACKED;
     p->header.cid = (conn->cid | call->channel);
     p->header.serviceId = conn->serviceId;
     p->header.securityIndex = conn->securityIndex;
+
+    /* No data packets on call 0. Where do these come from? */
+    if (*call->callNumber == 0)
+       *call->callNumber = 1;
+
     p->header.callNumber = *call->callNumber;
     p->header.seq = call->tnext++;
     p->header.epoch = conn->epoch;
@@ -2017,14 +2612,19 @@ rxi_PrepareSendPacket(register struct rx_call *call,
     }
     if (len > 0) {
        osi_Panic("PrepareSendPacket 1\n");     /* MTUXXX */
-    } else {
+    } else if (i < p->niovecs) {
        /* Free any extra elements in the wirevec */
-       for (j = MAX(2, i); j < p->niovecs; j++) {
-           rxi_freeCBuf(RX_CBUF_TO_PACKET(p->wirevec[j].iov_base, p));
-       }
+#if defined(RX_ENABLE_TSFPQ)
+       rxi_FreeDataBufsTSFPQ(p, i, 1 /* allow global pool flush if overquota */);
+#else /* !RX_ENABLE_TSFPQ */
+        MUTEX_ENTER(&rx_freePktQ_lock);
+       rxi_FreeDataBufsNoLock(p, i);
+        MUTEX_EXIT(&rx_freePktQ_lock);
+#endif /* !RX_ENABLE_TSFPQ */
+
        p->niovecs = i;
-       p->wirevec[i - 1].iov_len += len;
     }
+    p->wirevec[i - 1].iov_len += len;
     RXS_PreparePacket(conn->securityObject, call, p);
 }
 
@@ -2037,6 +2637,8 @@ rxi_AdjustIfMTU(int mtu)
     int adjMTU;
     int frags;
 
+    if (rxi_nRecvFrags == 1 && rxi_nSendFrags == 1)
+        return mtu;
     adjMTU = RX_HEADER_SIZE + RX_JUMBOBUFFERSIZE + RX_JUMBOHEADERSIZE;
     if (mtu <= adjMTU) {
        return mtu;