rx: Get rid of some uneccessary temporary vars
[openafs.git] / src / rx / rx_rdwr.c
index a8a5d80..60f0852 100644 (file)
  /*
   * Copyright 2000, International Business Machines Corporation and others.
   * All Rights Reserved.
-  * 
+  *
   * This software has been released under the terms of the IBM Public
   * License.  For details, see the LICENSE file in the top-level source
   * directory or online at http://www.openafs.org/dl/license10.html
   */
 
 #include <afsconfig.h>
-#ifdef KERNEL
-#include "afs/param.h"
-#else
 #include <afs/param.h>
-#endif
-
-RCSID
-    ("$Header$");
 
 #ifdef KERNEL
-#ifndef UKERNEL
-#ifdef RX_KERNEL_TRACE
-#include "rx_kcommon.h"
-#endif
-#if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
-#include "afs/sysincludes.h"
-#else
-#include "h/types.h"
-#include "h/time.h"
-#include "h/stat.h"
-#if defined(AFS_AIX_ENV) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV) 
-#include "h/systm.h"
-#endif
-#ifdef AFS_OSF_ENV
-#include <net/net_globals.h>
-#endif /* AFS_OSF_ENV */
-#ifdef AFS_LINUX20_ENV
-#include "h/socket.h"
-#endif
-#include "netinet/in.h"
-#if defined(AFS_SGI_ENV)
-#include "afs/sysincludes.h"
-#endif
-#endif
-#include "afs/afs_args.h"
-#include "afs/afs_osi.h"
-#if    (defined(AFS_AUX_ENV) || defined(AFS_AIX_ENV))
-#include "h/systm.h"
-#endif
-#else /* !UKERNEL */
-#include "afs/sysincludes.h"
-#endif /* !UKERNEL */
-#ifdef RXDEBUG
-#undef RXDEBUG                 /* turn off debugging */
-#endif /* RXDEBUG */
-
-#include "rx_kmutex.h"
-#include "rx/rx_kernel.h"
-#include "rx/rx_clock.h"
-#include "rx/rx_queue.h"
-#include "rx/rx.h"
-#include "rx/rx_globals.h"
-#include "afs/lock.h"
-#include "afsint.h"
-#ifdef  AFS_OSF_ENV
-#undef kmem_alloc
-#undef kmem_free
-#undef mem_alloc
-#undef mem_free
-#undef register
-#endif /* AFS_OSF_ENV */
+# ifndef UKERNEL
+#  ifdef RX_KERNEL_TRACE
+#   include "rx_kcommon.h"
+#  endif
+#  if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
+#   include "afs/sysincludes.h"
+#  else
+#   include "h/types.h"
+#   include "h/time.h"
+#   include "h/stat.h"
+#   if defined(AFS_AIX_ENV) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV)
+#    include "h/systm.h"
+#   endif
+#   ifdef      AFS_OSF_ENV
+#    include <net/net_globals.h>
+#   endif /* AFS_OSF_ENV */
+#   ifdef AFS_LINUX20_ENV
+#    include "h/socket.h"
+#   endif
+#   include "netinet/in.h"
+#   if defined(AFS_SGI_ENV)
+#    include "afs/sysincludes.h"
+#   endif
+#  endif
+#  include "afs/afs_args.h"
+#  if  (defined(AFS_AUX_ENV) || defined(AFS_AIX_ENV))
+#   include "h/systm.h"
+#  endif
+# else /* !UKERNEL */
+#  include "afs/sysincludes.h"
+# endif /* !UKERNEL */
+
+# ifdef RXDEBUG
+#  undef RXDEBUG                       /* turn off debugging */
+# endif /* RXDEBUG */
+
+# include "afs/afs_osi.h"
+# include "rx_kmutex.h"
+# include "rx/rx_kernel.h"
+# include "afs/lock.h"
 #else /* KERNEL */
-# include <sys/types.h>
-#ifdef AFS_NT40_ENV
-# include <winsock2.h>
-#else /* !AFS_NT40_ENV */
-# include <sys/socket.h>
-# include <sys/file.h>
-# include <netdb.h>
-# include <netinet/in.h>
-# include <sys/stat.h>
-# include <sys/time.h>
-#endif /* !AFS_NT40_ENV */
-#include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-# include "rx_user.h"
-# include "rx_clock.h"
-# include "rx_queue.h"
-# include "rx.h"
-# include "rx_globals.h"
+# include <roken.h>
 #endif /* KERNEL */
 
+#include "rx.h"
+#include "rx_clock.h"
+#include "rx_queue.h"
+#include "rx_globals.h"
+
+#include "rx_conn.h"
+#include "rx_call.h"
+#include "rx_packet.h"
+
 #ifdef RX_LOCKS_DB
 /* rxdb_fileID is used to identify the lock location, along with line#. */
 static int rxdb_fileID = RXDB_FILE_RX_RDWR;
 #endif /* RX_LOCKS_DB */
 /* rxi_ReadProc -- internal version.
  *
- * LOCKS USED -- called at netpri with rx global lock and call->lock held.
+ * LOCKS USED -- called at netpri
  */
 int
 rxi_ReadProc(struct rx_call *call, char *buf,
@@ -123,13 +95,18 @@ rxi_ReadProc(struct rx_call *call, char *buf,
     do {
        if (call->nLeft == 0) {
            /* Get next packet */
+           MUTEX_ENTER(&call->lock);
            for (;;) {
                if (call->error || (call->mode != RX_MODE_RECEIVING)) {
                    if (call->error) {
+                        call->mode = RX_MODE_ERROR;
+                       MUTEX_EXIT(&call->lock);
                        return 0;
                    }
                    if (call->mode == RX_MODE_SENDING) {
+                        MUTEX_EXIT(&call->lock);
                        rxi_FlushWrite(call);
+                        MUTEX_ENTER(&call->lock);
                        continue;
                    }
                }
@@ -140,7 +117,9 @@ rxi_ReadProc(struct rx_call *call, char *buf,
                        afs_int32 error;
                        struct rx_connection *conn = call->conn;
                        queue_Remove(rp);
+#ifdef RX_TRACK_PACKETS
                        rp->flags &= ~RX_PKTFLAG_RQ;
+#endif
 #ifdef RXDEBUG_PACKET
                         call->rqc--;
 #endif /* RXDEBUG_PACKET */
@@ -152,8 +131,8 @@ rxi_ReadProc(struct rx_call *call, char *buf,
                        if ((error =
                             RXS_CheckPacket(conn->securityObject, call,
                                             rp))) {
-                           /* Used to merely shut down the call, but now we 
-                            * shut down the whole connection since this may 
+                           /* Used to merely shut down the call, but now we
+                            * shut down the whole connection since this may
                             * indicate an attempt to hijack it */
 
                            MUTEX_EXIT(&call->lock);
@@ -162,15 +141,16 @@ rxi_ReadProc(struct rx_call *call, char *buf,
                            rp = rxi_SendConnectionAbort(conn, rp, 0, 0);
                            MUTEX_EXIT(&conn->conn_data_lock);
                            rxi_FreePacket(rp);
-                           MUTEX_ENTER(&call->lock);
 
                            return 0;
                        }
                        call->rnext++;
                        cp = call->currentPacket = rp;
+#ifdef RX_TRACK_PACKETS
                        call->currentPacket->flags |= RX_PKTFLAG_CP;
+#endif
                        call->curvec = 1;       /* 0th vec is always header */
-                       /* begin at the beginning [ more or less ], continue 
+                       /* begin at the beginning [ more or less ], continue
                         * on until the end, then stop. */
                        call->curpos =
                            (char *)cp->wirevec[1].iov_base +
@@ -196,34 +176,20 @@ rxi_ReadProc(struct rx_call *call, char *buf,
                        call->nHardAcks++;
                        if (!(call->flags & RX_CALL_RECEIVE_DONE)) {
                            if (call->nHardAcks > (u_short) rxi_HardAckRate) {
-                               rxevent_Cancel(call->delayedAckEvent, call,
+                               rxevent_Cancel(&call->delayedAckEvent, call,
                                               RX_CALL_REFCOUNT_DELAY);
                                rxi_SendAck(call, 0, 0, RX_ACK_DELAY, 0);
                            } else {
-                               struct clock when, now;
-                               clock_GetTime(&now);
-                               when = now;
                                /* Delay to consolidate ack packets */
-                               clock_Add(&when, &rx_hardAckDelay);
-                               if (!call->delayedAckEvent
-                                   || clock_Gt(&call->delayedAckEvent->
-                                               eventTime, &when)) {
-                                   rxevent_Cancel(call->delayedAckEvent,
-                                                  call,
-                                                  RX_CALL_REFCOUNT_DELAY);
-                                   CALL_HOLD(call, RX_CALL_REFCOUNT_DELAY);
-                                   call->delayedAckEvent =
-                                     rxevent_PostNow(&when, &now,
-                                                    rxi_SendDelayedAck, call,
-                                                    0);
-                               }
+                               rxi_PostDelayedAckEvent(call,
+                                                       &rx_hardAckDelay);
                            }
                        }
                        break;
                    }
                }
 
-                /* 
+                /*
                  * If we reach this point either we have no packets in the
                  * receive queue or the next packet in the queue is not the
                  * one we are looking for.  There is nothing else for us to
@@ -232,6 +198,7 @@ rxi_ReadProc(struct rx_call *call, char *buf,
 
                /* Are there ever going to be any more packets? */
                if (call->flags & RX_CALL_RECEIVE_DONE) {
+                   MUTEX_EXIT(&call->lock);
                    return requestCount - nbytes;
                }
                /* Wait for in-sequence packet */
@@ -245,18 +212,19 @@ rxi_ReadProc(struct rx_call *call, char *buf,
                    osi_rxSleep(&call->rq);
 #endif
                }
-                /* cp is no longer valid since we may have given up the lock */
                 cp = call->currentPacket;
 
                call->startWait = 0;
 #ifdef RX_ENABLE_LOCKS
                if (call->error) {
+                   MUTEX_EXIT(&call->lock);
                    return 0;
                }
 #endif /* RX_ENABLE_LOCKS */
            }
+           MUTEX_EXIT(&call->lock);
        } else
-           /* assert(cp); */
+           /* osi_Assert(cp); */
            /* MTUXXX  this should be replaced by some error-recovery code before shipping */
            /* yes, the following block is allowed to be the ELSE clause (or not) */
            /* It's possible for call->nLeft to be smaller than any particular
@@ -277,7 +245,9 @@ rxi_ReadProc(struct rx_call *call, char *buf,
 
                if (!call->nLeft) {
                    /* out of packet.  Get another one. */
+#ifdef RX_TRACK_PACKETS
                    call->currentPacket->flags &= ~RX_PKTFLAG_CP;
+#endif
                    rxi_FreePacket(cp);
                    cp = call->currentPacket = (struct rx_packet *)0;
                } else if (!call->curlen) {
@@ -285,7 +255,9 @@ rxi_ReadProc(struct rx_call *call, char *buf,
                    if (++call->curvec >= cp->niovecs) {
                        /* current packet is exhausted, get ready for another */
                        /* don't worry about curvec and stuff, they get set somewhere else */
+#ifdef RX_TRACK_PACKETS
                        call->currentPacket->flags &= ~RX_PKTFLAG_CP;
+#endif
                        rxi_FreePacket(cp);
                        cp = call->currentPacket = (struct rx_packet *)0;
                        call->nLeft = 0;
@@ -310,18 +282,9 @@ int
 rx_ReadProc(struct rx_call *call, char *buf, int nbytes)
 {
     int bytes;
-    int tcurlen;
-    int tnLeft;
-    char *tcurpos;
     SPLVAR;
 
-    /*
-     * Free any packets from the last call to ReadvProc/WritevProc.
-     * We do not need the lock because the receiver threads only
-     * touch the iovq when the RX_CALL_IOVEC_WAIT flag is set, and the
-     * RX_CALL_IOVEC_WAIT is always cleared before returning from
-     * ReadvProc/WritevProc.
-     */
+    /* Free any packets from the last call to ReadvProc/WritevProc */
     if (!queue_IsEmpty(&call->iovq)) {
 #ifdef RXDEBUG_PACKET
         call->iovqc -=
@@ -331,36 +294,25 @@ rx_ReadProc(struct rx_call *call, char *buf, int nbytes)
 
     /*
      * Most common case, all of the data is in the current iovec.
-     * We do not need the lock because this is the only thread that
-     * updates the curlen, curpos, nLeft fields.
-     *
      * We are relying on nLeft being zero unless the call is in receive mode.
      */
-    tcurlen = call->curlen;
-    tnLeft = call->nLeft;
-    if (!call->error && tcurlen > nbytes && tnLeft > nbytes) {
-       tcurpos = call->curpos;
-       memcpy(buf, tcurpos, nbytes);
-       call->curpos = tcurpos + nbytes;
-       call->curlen = tcurlen - nbytes;
-       call->nLeft = tnLeft - nbytes;
+    if (!call->error && call->curlen > nbytes && call->nLeft > nbytes) {
+        memcpy(buf, call->curpos, nbytes);
+
+       call->curpos += nbytes;
+       call->curlen -= nbytes;
+       call->nLeft  -= nbytes;
 
-        if (!call->nLeft) {
+        if (!call->nLeft && call->currentPacket != NULL) {
             /* out of packet.  Get another one. */
-            NETPRI;
-            MUTEX_ENTER(&call->lock);
             rxi_FreePacket(call->currentPacket);
             call->currentPacket = (struct rx_packet *)0;
-            MUTEX_EXIT(&call->lock);
-            USERPRI;
         }
        return nbytes;
     }
 
     NETPRI;
-    MUTEX_ENTER(&call->lock);
     bytes = rxi_ReadProc(call, buf, nbytes);
-    MUTEX_EXIT(&call->lock);
     USERPRI;
     return bytes;
 }
@@ -370,18 +322,9 @@ int
 rx_ReadProc32(struct rx_call *call, afs_int32 * value)
 {
     int bytes;
-    int tcurlen;
-    int tnLeft;
-    char *tcurpos;
     SPLVAR;
 
-    /*
-     * Free any packets from the last call to ReadvProc/WritevProc.
-     * We do not need the lock because the receiver threads only
-     * touch the iovq when the RX_CALL_IOVEC_WAIT flag is set, and the
-     * RX_CALL_IOVEC_WAIT is always cleared before returning from
-     * ReadvProc/WritevProc.
-     */
+    /* Free any packets from the last call to ReadvProc/WritevProc */
     if (!queue_IsEmpty(&call->iovq)) {
 #ifdef RXDEBUG_PACKET
         call->iovqc -=
@@ -391,37 +334,29 @@ rx_ReadProc32(struct rx_call *call, afs_int32 * value)
 
     /*
      * Most common case, all of the data is in the current iovec.
-     * We do not need the lock because this is the only thread that
-     * updates the curlen, curpos, nLeft fields.
-     *
      * We are relying on nLeft being zero unless the call is in receive mode.
      */
-    tcurlen = call->curlen;
-    tnLeft = call->nLeft;
-    if (!call->error && tcurlen >= sizeof(afs_int32)
-       && tnLeft >= sizeof(afs_int32)) {
-       tcurpos = call->curpos;
-       memcpy((char *)value, tcurpos, sizeof(afs_int32));
-       call->curpos = tcurpos + sizeof(afs_int32);
-       call->curlen = (u_short)(tcurlen - sizeof(afs_int32));
-       call->nLeft = (u_short)(tnLeft - sizeof(afs_int32));
+    if (!call->error && call->curlen >= sizeof(afs_int32)
+       && call->nLeft >= sizeof(afs_int32)) {
+
+        memcpy((char *)value, call->curpos, sizeof(afs_int32));
+
+        call->curpos += sizeof(afs_int32);
+       call->curlen -= sizeof(afs_int32);
+       call->nLeft  -= sizeof(afs_int32);
+
         if (!call->nLeft && call->currentPacket != NULL) {
             /* out of packet.  Get another one. */
-            NETPRI;
-            MUTEX_ENTER(&call->lock);
             rxi_FreePacket(call->currentPacket);
             call->currentPacket = (struct rx_packet *)0;
-            MUTEX_EXIT(&call->lock);
-            USERPRI;
         }
        return sizeof(afs_int32);
     }
 
     NETPRI;
-    MUTEX_ENTER(&call->lock);
     bytes = rxi_ReadProc(call, (char *)value, sizeof(afs_int32));
-    MUTEX_EXIT(&call->lock);
     USERPRI;
+
     return bytes;
 }
 
@@ -458,7 +393,9 @@ rxi_FillReadVec(struct rx_call *call, afs_uint32 serial)
                    afs_int32 error;
                    struct rx_connection *conn = call->conn;
                    queue_Remove(rp);
+#ifdef RX_TRACK_PACKETS
                    rp->flags &= ~RX_PKTFLAG_RQ;
+#endif
 #ifdef RXDEBUG_PACKET
                     call->rqc--;
 #endif /* RXDEBUG_PACKET */
@@ -469,8 +406,8 @@ rxi_FillReadVec(struct rx_call *call, afs_uint32 serial)
                     * data + the header. */
                    if ((error =
                         RXS_CheckPacket(conn->securityObject, call, rp))) {
-                       /* Used to merely shut down the call, but now we 
-                        * shut down the whole connection since this may 
+                       /* Used to merely shut down the call, but now we
+                        * shut down the whole connection since this may
                         * indicate an attempt to hijack it */
 
                        MUTEX_EXIT(&call->lock);
@@ -485,10 +422,12 @@ rxi_FillReadVec(struct rx_call *call, afs_uint32 serial)
                    }
                    call->rnext++;
                    curp = call->currentPacket = rp;
+#ifdef RX_TRACK_PACKETS
                    call->currentPacket->flags |= RX_PKTFLAG_CP;
+#endif
                    call->curvec = 1;   /* 0th vec is always header */
                    cur_iov = &curp->wirevec[1];
-                   /* begin at the beginning [ more or less ], continue 
+                   /* begin at the beginning [ more or less ], continue
                     * on until the end, then stop. */
                    call->curpos =
                        (char *)curp->wirevec[1].iov_base +
@@ -540,8 +479,10 @@ rxi_FillReadVec(struct rx_call *call, afs_uint32 serial)
 
            if (!call->nLeft) {
                /* out of packet.  Get another one. */
+#ifdef RX_TRACK_PACKETS
                 curp->flags &= ~RX_PKTFLAG_CP;
                 curp->flags |= RX_PKTFLAG_IOVQ;
+#endif
                queue_Append(&call->iovq, curp);
 #ifdef RXDEBUG_PACKET
                 call->iovqc++;
@@ -552,8 +493,10 @@ rxi_FillReadVec(struct rx_call *call, afs_uint32 serial)
                if (++call->curvec >= curp->niovecs) {
                    /* current packet is exhausted, get ready for another */
                    /* don't worry about curvec and stuff, they get set somewhere else */
+#ifdef RX_TRACK_PACKETS
                    curp->flags &= ~RX_PKTFLAG_CP;
                    curp->flags |= RX_PKTFLAG_IOVQ;
+#endif
                    queue_Append(&call->iovq, curp);
 #ifdef RXDEBUG_PACKET
                     call->iovqc++;
@@ -573,24 +516,13 @@ rxi_FillReadVec(struct rx_call *call, afs_uint32 serial)
      * send a hard ack. */
     if (didConsume && (!(call->flags & RX_CALL_RECEIVE_DONE))) {
        if (call->nHardAcks > (u_short) rxi_HardAckRate) {
-           rxevent_Cancel(call->delayedAckEvent, call,
+           rxevent_Cancel(&call->delayedAckEvent, call,
                           RX_CALL_REFCOUNT_DELAY);
            rxi_SendAck(call, 0, serial, RX_ACK_DELAY, 0);
            didHardAck = 1;
        } else {
-           struct clock when, now;
-           clock_GetTime(&now);
-           when = now;
            /* Delay to consolidate ack packets */
-           clock_Add(&when, &rx_hardAckDelay);
-           if (!call->delayedAckEvent
-               || clock_Gt(&call->delayedAckEvent->eventTime, &when)) {
-               rxevent_Cancel(call->delayedAckEvent, call,
-                              RX_CALL_REFCOUNT_DELAY);
-               CALL_HOLD(call, RX_CALL_REFCOUNT_DELAY);
-               call->delayedAckEvent =
-                   rxevent_PostNow(&when, &now, rxi_SendDelayedAck, call, 0);
-           }
+           rxi_PostDelayedAckEvent(call, &rx_hardAckDelay);
        }
     }
     return didHardAck;
@@ -603,17 +535,13 @@ rxi_FillReadVec(struct rx_call *call, afs_uint32 serial)
  * except the last packet (new current packet) are moved to the iovq
  * while the application is processing the data.
  *
- * LOCKS USED -- called at netpri with rx global lock and call->lock held.
+ * LOCKS USED -- called at netpri.
  */
 int
 rxi_ReadvProc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
              int nbytes)
 {
-    int requestCount;
-    int nextio;
-
-    requestCount = nbytes;
-    nextio = 0;
+    int bytes;
 
     /* Free any packets from the last call to ReadvProc/WritevProc */
     if (queue_IsNotEmpty(&call->iovq)) {
@@ -627,9 +555,9 @@ rxi_ReadvProc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
        rxi_FlushWrite(call);
     }
 
-    if (call->error) {
-       return 0;
-    }
+    MUTEX_ENTER(&call->lock);
+    if (call->error)
+        goto error;
 
     /* Get whatever data is currently available in the receive queue.
      * If rxi_FillReadVec sends an ack packet then it is possible
@@ -661,15 +589,20 @@ rxi_ReadvProc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
        call->startWait = 0;
     }
     call->flags &= ~RX_CALL_IOVEC_WAIT;
-#ifdef RX_ENABLE_LOCKS
-    if (call->error) {
-       return 0;
-    }
-#endif /* RX_ENABLE_LOCKS */
+
+    if (call->error)
+        goto error;
 
     call->iov = NULL;
     *nio = call->iovNext;
-    return nbytes - call->iovNBytes;
+    bytes = nbytes - call->iovNBytes;
+    MUTEX_EXIT(&call->lock);
+    return bytes;
+
+  error:
+    MUTEX_EXIT(&call->lock);
+    call->mode = RX_MODE_ERROR;
+    return 0;
 }
 
 int
@@ -680,16 +613,15 @@ rx_ReadvProc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
     SPLVAR;
 
     NETPRI;
-    MUTEX_ENTER(&call->lock);
     bytes = rxi_ReadvProc(call, iov, nio, maxio, nbytes);
-    MUTEX_EXIT(&call->lock);
     USERPRI;
     return bytes;
 }
 
 /* rxi_WriteProc -- internal version.
  *
- * LOCKS USED -- called at netpri with rx global lock and call->lock held. */
+ * LOCKS USED -- called at netpri
+ */
 
 int
 rxi_WriteProc(struct rx_call *call, char *buf,
@@ -713,7 +645,9 @@ rxi_WriteProc(struct rx_call *call, char *buf,
            && (call->mode == RX_MODE_RECEIVING)) {
            call->mode = RX_MODE_SENDING;
            if (cp) {
+#ifdef RX_TRACK_PACKETS
                cp->flags &= ~RX_PKTFLAG_CP;
+#endif
                rxi_FreePacket(cp);
                cp = call->currentPacket = (struct rx_packet *)0;
                call->nLeft = 0;
@@ -730,48 +664,49 @@ rxi_WriteProc(struct rx_call *call, char *buf,
      * anyway. */
     do {
        if (call->nFree == 0) {
+           MUTEX_ENTER(&call->lock);
+            cp = call->currentPacket;
+            if (call->error)
+                call->mode = RX_MODE_ERROR;
            if (!call->error && cp) {
                 /* Clear the current packet now so that if
-                 * we are forced to wait and drop the lock 
-                 * the packet we are planning on using 
+                 * we are forced to wait and drop the lock
+                 * the packet we are planning on using
                  * cannot be freed.
                  */
+#ifdef RX_TRACK_PACKETS
                 cp->flags &= ~RX_PKTFLAG_CP;
+#endif
                call->currentPacket = (struct rx_packet *)0;
-#ifdef AFS_GLOBAL_RXLOCK_KERNEL
-               /* Wait until TQ_BUSY is reset before adding any
-                * packets to the transmit queue
-                */
-               while (call->flags & RX_CALL_TQ_BUSY) {
-                   call->flags |= RX_CALL_TQ_WAIT;
-#ifdef RX_ENABLE_LOCKS
-                   CV_WAIT(&call->cv_tq, &call->lock);
-#else /* RX_ENABLE_LOCKS */
-                   osi_rxSleep(&call->tq);
-#endif /* RX_ENABLE_LOCKS */
-               }
-#endif /* AFS_GLOBAL_RXLOCK_KERNEL */
                clock_NewTime();        /* Bogus:  need new time package */
-               /* The 0, below, specifies that it is not the last packet: 
+               /* The 0, below, specifies that it is not the last packet:
                 * there will be others. PrepareSendPacket may
                 * alter the packet length by up to
                 * conn->securityMaxTrailerSize */
                hadd32(call->bytesSent, cp->length);
                rxi_PrepareSendPacket(call, cp, 0);
+#ifdef AFS_GLOBAL_RXLOCK_KERNEL
+                /* PrepareSendPacket drops the call lock */
+                rxi_WaitforTQBusy(call);
+#endif /* AFS_GLOBAL_RXLOCK_KERNEL */
+#ifdef RX_TRACK_PACKETS
                cp->flags |= RX_PKTFLAG_TQ;
+#endif
                queue_Append(&call->tq, cp);
 #ifdef RXDEBUG_PACKET
                 call->tqc++;
 #endif /* RXDEBUG_PACKET */
                 cp = (struct rx_packet *)0;
-               if (!
-                   (call->
-                    flags & (RX_CALL_FAST_RECOVER |
-                             RX_CALL_FAST_RECOVER_WAIT))) {
-                   rxi_Start(0, call, 0, 0);
+               /* If the call is in recovery, let it exhaust its current
+                * retransmit queue before forcing it to send new packets
+                */
+               if (!(call->flags & (RX_CALL_FAST_RECOVER))) {
+                   rxi_Start(call, 0);
                }
            } else if (cp) {
+#ifdef RX_TRACK_PACKETS
                cp->flags &= ~RX_PKTFLAG_CP;
+#endif
                rxi_FreePacket(cp);
                cp = call->currentPacket = (struct rx_packet *)0;
            }
@@ -791,16 +726,20 @@ rxi_WriteProc(struct rx_call *call, char *buf,
                call->startWait = 0;
 #ifdef RX_ENABLE_LOCKS
                if (call->error) {
+                    call->mode = RX_MODE_ERROR;
+                   MUTEX_EXIT(&call->lock);
                    return 0;
                }
 #endif /* RX_ENABLE_LOCKS */
            }
            if ((cp = rxi_AllocSendPacket(call, nbytes))) {
+#ifdef RX_TRACK_PACKETS
                cp->flags |= RX_PKTFLAG_CP;
+#endif
                call->currentPacket = cp;
                call->nFree = cp->length;
                call->curvec = 1;       /* 0th vec is always header */
-               /* begin at the beginning [ more or less ], continue 
+               /* begin at the beginning [ more or less ], continue
                 * on until the end, then stop. */
                call->curpos =
                    (char *)cp->wirevec[1].iov_base +
@@ -809,13 +748,18 @@ rxi_WriteProc(struct rx_call *call, char *buf,
                    cp->wirevec[1].iov_len - call->conn->securityHeaderSize;
            }
            if (call->error) {
+                call->mode = RX_MODE_ERROR;
                if (cp) {
+#ifdef RX_TRACK_PACKETS
                    cp->flags &= ~RX_PKTFLAG_CP;
+#endif
                    rxi_FreePacket(cp);
                    call->currentPacket = NULL;
                }
+               MUTEX_EXIT(&call->lock);
                return 0;
            }
+           MUTEX_EXIT(&call->lock);
        }
 
        if (cp && (int)call->nFree < nbytes) {
@@ -882,13 +826,7 @@ rx_WriteProc(struct rx_call *call, char *buf, int nbytes)
     char *tcurpos;
     SPLVAR;
 
-    /*
-     * Free any packets from the last call to ReadvProc/WritevProc.
-     * We do not need the lock because the receiver threads only
-     * touch the iovq when the RX_CALL_IOVEC_WAIT flag is set, and the
-     * RX_CALL_IOVEC_WAIT is always cleared before returning from
-     * ReadvProc/WritevProc.
-     */
+    /* Free any packets from the last call to ReadvProc/WritevProc */
     if (queue_IsNotEmpty(&call->iovq)) {
 #ifdef RXDEBUG_PACKET
         call->iovqc -=
@@ -898,15 +836,13 @@ rx_WriteProc(struct rx_call *call, char *buf, int nbytes)
 
     /*
      * Most common case: all of the data fits in the current iovec.
-     * We do not need the lock because this is the only thread that
-     * updates the curlen, curpos, nFree fields.
-     *
      * We are relying on nFree being zero unless the call is in send mode.
      */
     tcurlen = (int)call->curlen;
     tnFree = (int)call->nFree;
     if (!call->error && tcurlen >= nbytes && tnFree >= nbytes) {
        tcurpos = call->curpos;
+
        memcpy(tcurpos, buf, nbytes);
        call->curpos = tcurpos + nbytes;
        call->curlen = (u_short)(tcurlen - nbytes);
@@ -915,9 +851,7 @@ rx_WriteProc(struct rx_call *call, char *buf, int nbytes)
     }
 
     NETPRI;
-    MUTEX_ENTER(&call->lock);
     bytes = rxi_WriteProc(call, buf, nbytes);
-    MUTEX_EXIT(&call->lock);
     USERPRI;
     return bytes;
 }
@@ -932,13 +866,6 @@ rx_WriteProc32(struct rx_call *call, afs_int32 * value)
     char *tcurpos;
     SPLVAR;
 
-    /*
-     * Free any packets from the last call to ReadvProc/WritevProc.
-     * We do not need the lock because the receiver threads only
-     * touch the iovq when the RX_CALL_IOVEC_WAIT flag is set, and the
-     * RX_CALL_IOVEC_WAIT is always cleared before returning from
-     * ReadvProc/WritevProc.
-     */
     if (queue_IsNotEmpty(&call->iovq)) {
 #ifdef RXDEBUG_PACKET
         call->iovqc -=
@@ -948,9 +875,6 @@ rx_WriteProc32(struct rx_call *call, afs_int32 * value)
 
     /*
      * Most common case: all of the data fits in the current iovec.
-     * We do not need the lock because this is the only thread that
-     * updates the curlen, curpos, nFree fields.
-     *
      * We are relying on nFree being zero unless the call is in send mode.
      */
     tcurlen = call->curlen;
@@ -958,6 +882,7 @@ rx_WriteProc32(struct rx_call *call, afs_int32 * value)
     if (!call->error && tcurlen >= sizeof(afs_int32)
        && tnFree >= sizeof(afs_int32)) {
        tcurpos = call->curpos;
+
        if (!((size_t)tcurpos & (sizeof(afs_int32) - 1))) {
            *((afs_int32 *) (tcurpos)) = *value;
        } else {
@@ -970,9 +895,7 @@ rx_WriteProc32(struct rx_call *call, afs_int32 * value)
     }
 
     NETPRI;
-    MUTEX_ENTER(&call->lock);
     bytes = rxi_WriteProc(call, (char *)value, sizeof(afs_int32));
-    MUTEX_EXIT(&call->lock);
     USERPRI;
     return bytes;
 }
@@ -982,9 +905,10 @@ rx_WriteProc32(struct rx_call *call, afs_int32 * value)
  * Fill in an iovec to point to data in packet buffers. The application
  * calls rxi_WritevProc when the buffers are full.
  *
- * LOCKS USED -- called at netpri with rx global lock and call->lock held. */
+ * LOCKS USED -- called at netpri.
+ */
 
-int
+static int
 rxi_WritevAlloc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
                int nbytes)
 {
@@ -994,7 +918,7 @@ rxi_WritevAlloc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
     int nextio;
     /* Temporary values, real work is done in rxi_WritevProc */
     int tnFree;
-    int tcurvec;
+    unsigned int tcurvec;
     char *tcurpos;
     int tcurlen;
 
@@ -1014,7 +938,9 @@ rxi_WritevAlloc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
            && (call->mode == RX_MODE_RECEIVING)) {
            call->mode = RX_MODE_SENDING;
            if (cp) {
+#ifdef RX_TRACK_PACKETS
                cp->flags &= ~RX_PKTFLAG_CP;
+#endif
                rxi_FreePacket(cp);
                cp = call->currentPacket = (struct rx_packet *)0;
                call->nLeft = 0;
@@ -1031,17 +957,21 @@ rxi_WritevAlloc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
     tcurpos = call->curpos;
     tcurlen = call->curlen;
     do {
-       unsigned int t;
+       int t;
 
        if (tnFree == 0) {
            /* current packet is full, allocate a new one */
+           MUTEX_ENTER(&call->lock);
            cp = rxi_AllocSendPacket(call, nbytes);
+           MUTEX_EXIT(&call->lock);
            if (cp == NULL) {
                /* out of space, return what we have */
                *nio = nextio;
                return requestCount - nbytes;
            }
+#ifdef RX_TRACK_PACKETS
            cp->flags |= RX_PKTFLAG_IOVQ;
+#endif
            queue_Append(&call->iovq, cp);
 #ifdef RXDEBUG_PACKET
             call->iovqc++;
@@ -1106,9 +1036,7 @@ rx_WritevAlloc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
     SPLVAR;
 
     NETPRI;
-    MUTEX_ENTER(&call->lock);
     bytes = rxi_WritevAlloc(call, iov, nio, maxio, nbytes);
-    MUTEX_EXIT(&call->lock);
     USERPRI;
     return bytes;
 }
@@ -1117,13 +1045,15 @@ rx_WritevAlloc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
  *
  * Send buffers allocated in rxi_WritevAlloc.
  *
- * LOCKS USED -- called at netpri with rx global lock and call->lock held. */
-
+ * LOCKS USED -- called at netpri.
+ */
 int
 rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
 {
     struct rx_packet *cp = NULL;
+#ifdef RX_TRACK_PACKETS
     struct rx_packet *p, *np;
+#endif
     int nextio;
     int requestCount;
     struct rx_queue tmpq;
@@ -1134,33 +1064,30 @@ rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
     requestCount = nbytes;
     nextio = 0;
 
-    if (call->mode != RX_MODE_SENDING) {
+    MUTEX_ENTER(&call->lock);
+    if (call->error) {
+        call->mode = RX_MODE_ERROR;
+    } else if (call->mode != RX_MODE_SENDING) {
        call->error = RX_PROTOCOL_ERROR;
     }
 #ifdef AFS_GLOBAL_RXLOCK_KERNEL
-    /* Wait until TQ_BUSY is reset before trying to move any
-     * packets to the transmit queue.  */
-    while (!call->error && call->flags & RX_CALL_TQ_BUSY) {
-       call->flags |= RX_CALL_TQ_WAIT;
-#ifdef RX_ENABLE_LOCKS
-       CV_WAIT(&call->cv_tq, &call->lock);
-#else /* RX_ENABLE_LOCKS */
-       osi_rxSleep(&call->tq);
-#endif /* RX_ENABLE_LOCKS */
-    }
+    rxi_WaitforTQBusy(call);
 #endif /* AFS_GLOBAL_RXLOCK_KERNEL */
-    /* cp is no longer valid since we may have given up the lock */
     cp = call->currentPacket;
 
     if (call->error) {
+        call->mode = RX_MODE_ERROR;
+       MUTEX_EXIT(&call->lock);
        if (cp) {
+#ifdef RX_TRACK_PACKETS
             cp->flags &= ~RX_PKTFLAG_CP;
             cp->flags |= RX_PKTFLAG_IOVQ;
+#endif
            queue_Prepend(&call->iovq, cp);
 #ifdef RXDEBUG_PACKET
             call->iovqc++;
 #endif /* RXDEBUG_PACKET */
-           cp = call->currentPacket = (struct rx_packet *)0;
+           call->currentPacket = (struct rx_packet *)0;
        }
 #ifdef RXDEBUG_PACKET
         call->iovqc -=
@@ -1182,12 +1109,16 @@ rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
     do {
        if (call->nFree == 0 && cp) {
            clock_NewTime();    /* Bogus:  need new time package */
-           /* The 0, below, specifies that it is not the last packet: 
+           /* The 0, below, specifies that it is not the last packet:
             * there will be others. PrepareSendPacket may
             * alter the packet length by up to
             * conn->securityMaxTrailerSize */
            hadd32(call->bytesSent, cp->length);
            rxi_PrepareSendPacket(call, cp, 0);
+#ifdef AFS_GLOBAL_RXLOCK_KERNEL
+            /* PrepareSendPacket drops the call lock */
+            rxi_WaitforTQBusy(call);
+#endif /* AFS_GLOBAL_RXLOCK_KERNEL */
            queue_Append(&tmpq, cp);
 #ifdef RXDEBUG_PACKET
             tmpqc++;
@@ -1197,6 +1128,7 @@ rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
            /* The head of the iovq is now the current packet */
            if (nbytes) {
                if (queue_IsEmpty(&call->iovq)) {
+                    MUTEX_EXIT(&call->lock);
                    call->error = RX_PROTOCOL_ERROR;
 #ifdef RXDEBUG_PACKET
                     tmpqc -=
@@ -1206,11 +1138,15 @@ rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
                }
                cp = queue_First(&call->iovq, rx_packet);
                queue_Remove(cp);
+#ifdef RX_TRACK_PACKETS
                 cp->flags &= ~RX_PKTFLAG_IOVQ;
+#endif
 #ifdef RXDEBUG_PACKET
                 call->iovqc--;
 #endif /* RXDEBUG_PACKET */
+#ifdef RX_TRACK_PACKETS
                 cp->flags |= RX_PKTFLAG_CP;
+#endif
                call->currentPacket = cp;
                call->nFree = cp->length;
                call->curvec = 1;
@@ -1227,8 +1163,11 @@ rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
            if (iov[nextio].iov_base != call->curpos
                || iov[nextio].iov_len > (int)call->curlen) {
                call->error = RX_PROTOCOL_ERROR;
+                MUTEX_EXIT(&call->lock);
                if (cp) {
+#ifdef RX_TRACK_PACKETS
                    cp->flags &= ~RX_PKTFLAG_CP;
+#endif
                     queue_Prepend(&tmpq, cp);
 #ifdef RXDEBUG_PACKET
                     tmpqc++;
@@ -1259,15 +1198,24 @@ rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
 
     /* Move the packets from the temporary queue onto the transmit queue.
      * We may end up with more than call->twind packets on the queue. */
-    
+
+#ifdef RX_TRACK_PACKETS
     for (queue_Scan(&tmpq, p, np, rx_packet))
     {
         p->flags |= RX_PKTFLAG_TQ;
     }
+#endif
+
+    if (call->error)
+        call->mode = RX_MODE_ERROR;
+
     queue_SpliceAppend(&call->tq, &tmpq);
 
-    if (!(call->flags & (RX_CALL_FAST_RECOVER | RX_CALL_FAST_RECOVER_WAIT))) {
-       rxi_Start(0, call, 0, 0);
+    /* If the call is in recovery, let it exhaust its current retransmit
+     * queue before forcing it to send new packets
+     */
+    if (!(call->flags & RX_CALL_FAST_RECOVER)) {
+       rxi_Start(call, 0);
     }
 
     /* Wait for the length of the transmit queue to fall below call->twind */
@@ -1282,17 +1230,23 @@ rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
 #endif
        call->startWait = 0;
     }
+
     /* cp is no longer valid since we may have given up the lock */
     cp = call->currentPacket;
 
     if (call->error) {
+        call->mode = RX_MODE_ERROR;
+        call->currentPacket = NULL;
+        MUTEX_EXIT(&call->lock);
        if (cp) {
+#ifdef RX_TRACK_PACKETS
            cp->flags &= ~RX_PKTFLAG_CP;
+#endif
            rxi_FreePacket(cp);
-            cp = call->currentPacket = (struct rx_packet *)0;
        }
        return 0;
     }
+    MUTEX_EXIT(&call->lock);
 
     return requestCount - nbytes;
 }
@@ -1304,15 +1258,16 @@ rx_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
     SPLVAR;
 
     NETPRI;
-    MUTEX_ENTER(&call->lock);
     bytes = rxi_WritevProc(call, iov, nio, nbytes);
-    MUTEX_EXIT(&call->lock);
     USERPRI;
     return bytes;
 }
 
 /* Flush any buffered data to the stream, switch to read mode
- * (clients) or to EOF mode (servers) */
+ * (clients) or to EOF mode (servers)
+ *
+ * LOCKS HELD: called at netpri.
+ */
 void
 rxi_FlushWrite(struct rx_call *call)
 {
@@ -1345,28 +1300,19 @@ rxi_FlushWrite(struct rx_call *call)
        }
 #endif
 
-#ifdef AFS_GLOBAL_RXLOCK_KERNEL
-       /* Wait until TQ_BUSY is reset before adding any
-        * packets to the transmit queue
-        */
-       while (call->flags & RX_CALL_TQ_BUSY) {
-           call->flags |= RX_CALL_TQ_WAIT;
-#ifdef RX_ENABLE_LOCKS
-           CV_WAIT(&call->cv_tq, &call->lock);
-#else /* RX_ENABLE_LOCKS */
-           osi_rxSleep(&call->tq);
-#endif /* RX_ENABLE_LOCKS */
-       }
-#endif /* AFS_GLOBAL_RXLOCK_KERNEL */
+        MUTEX_ENTER(&call->lock);
+        if (call->error)
+            call->mode = RX_MODE_ERROR;
 
-        /* cp is no longer valid since we may have given up the lock */
         cp = call->currentPacket;
 
        if (cp) {
            /* cp->length is only supposed to be the user's data */
-           /* cp->length was already set to (then-current) 
+           /* cp->length was already set to (then-current)
             * MaxUserDataSize or less. */
+#ifdef RX_TRACK_PACKETS
            cp->flags &= ~RX_PKTFLAG_CP;
+#endif
            cp->length -= call->nFree;
            call->currentPacket = (struct rx_packet *)0;
            call->nFree = 0;
@@ -1384,16 +1330,25 @@ rxi_FlushWrite(struct rx_call *call)
        /* The 1 specifies that this is the last packet */
        hadd32(call->bytesSent, cp->length);
        rxi_PrepareSendPacket(call, cp, 1);
+#ifdef AFS_GLOBAL_RXLOCK_KERNEL
+        /* PrepareSendPacket drops the call lock */
+        rxi_WaitforTQBusy(call);
+#endif /* AFS_GLOBAL_RXLOCK_KERNEL */
+#ifdef RX_TRACK_PACKETS
        cp->flags |= RX_PKTFLAG_TQ;
+#endif
        queue_Append(&call->tq, cp);
 #ifdef RXDEBUG_PACKET
         call->tqc++;
 #endif /* RXDEBUG_PACKET */
-       if (!
-           (call->
-            flags & (RX_CALL_FAST_RECOVER | RX_CALL_FAST_RECOVER_WAIT))) {
-           rxi_Start(0, call, 0, 0);
+
+       /* If the call is in recovery, let it exhaust its current retransmit
+        * queue before forcing it to send new packets
+        */
+       if (!(call->flags & RX_CALL_FAST_RECOVER)) {
+           rxi_Start(call, 0);
        }
+        MUTEX_EXIT(&call->lock);
     }
 }
 
@@ -1404,8 +1359,6 @@ rx_FlushWrite(struct rx_call *call)
 {
     SPLVAR;
     NETPRI;
-    MUTEX_ENTER(&call->lock);
     rxi_FlushWrite(call);
-    MUTEX_EXIT(&call->lock);
     USERPRI;
 }