rx: Use a red black tree for the event stack
[openafs.git] / src / rx / rx.c
index 917abcc..1c5622e 100644 (file)
 #   include "h/socket.h"
 #  endif
 #  include "netinet/in.h"
-#  ifdef AFS_SUN58_ENV
+#  ifdef AFS_SUN5_ENV
 #   include "netinet/ip6.h"
-#  endif
-#  ifdef AFS_SUN57_ENV
 #   include "inet/common.h"
 #   include "inet/ip.h"
 #   include "inet/ip_ire.h"
@@ -80,6 +78,7 @@ extern afs_int32 afs_termState;
 #include "rx_trace.h"
 #include "rx_internal.h"
 #include "rx_stats.h"
+#include "rx_event.h"
 
 #include <afs/rxgen_consts.h>
 
@@ -102,6 +101,14 @@ static void rxi_ComputeRoundTripTime(struct rx_packet *, struct rx_ackPacket *,
                                     struct clock *);
 static void rxi_Resend(struct rxevent *event, void *arg0, void *arg1,
                       int istack);
+static void rxi_SendDelayedAck(struct rxevent *event, void *call,
+                               void *dummy, int dummy2);
+static void rxi_SendDelayedCallAbort(struct rxevent *event, void *arg1,
+                                    void *dummy, int dummy2);
+static void rxi_SendDelayedConnAbort(struct rxevent *event, void *arg1,
+                                    void *unused, int unused2);
+static void rxi_ReapConnections(struct rxevent *unused, void *unused1,
+                               void *unused2, int unused3);
 
 #ifdef RX_ENABLE_LOCKS
 static void rxi_SetAcksInTransmitQueue(struct rx_call *call);
@@ -190,13 +197,10 @@ extern afs_kmutex_t des_init_mutex;
 extern afs_kmutex_t des_random_mutex;
 extern afs_kmutex_t rx_clock_mutex;
 extern afs_kmutex_t rxi_connCacheMutex;
-extern afs_kmutex_t rx_event_mutex;
 extern afs_kmutex_t event_handler_mutex;
 extern afs_kmutex_t listener_mutex;
 extern afs_kmutex_t rx_if_init_mutex;
 extern afs_kmutex_t rx_if_mutex;
-extern afs_kmutex_t rxkad_client_uid_mutex;
-extern afs_kmutex_t rxkad_random_mutex;
 
 extern afs_kcondvar_t rx_event_handler_cond;
 extern afs_kcondvar_t rx_listener_cond;
@@ -218,14 +222,11 @@ rxi_InitPthread(void)
     MUTEX_INIT(&rx_refcnt_mutex, "refcnts", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&epoch_mutex, "epoch", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_init_mutex, "init", MUTEX_DEFAULT, 0);
-    MUTEX_INIT(&rx_event_mutex, "event", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&event_handler_mutex, "event handler", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rxi_connCacheMutex, "conn cache", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&listener_mutex, "listener", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_if_init_mutex, "if init", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_if_mutex, "if", MUTEX_DEFAULT, 0);
-    MUTEX_INIT(&rxkad_client_uid_mutex, "uid", MUTEX_DEFAULT, 0);
-    MUTEX_INIT(&rxkad_random_mutex, "rxkad random", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_debug_mutex, "debug", MUTEX_DEFAULT, 0);
 
     CV_INIT(&rx_event_handler_cond, "evhand", CV_DEFAULT, 0);
@@ -234,8 +235,6 @@ rxi_InitPthread(void)
     osi_Assert(pthread_key_create(&rx_thread_id_key, NULL) == 0);
     osi_Assert(pthread_key_create(&rx_ts_info_key, NULL) == 0);
 
-    rxkad_global_stats_init();
-
     MUTEX_INIT(&rx_rpc_stats, "rx_rpc_stats", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_freePktQ_lock, "rx_freePktQ_lock", MUTEX_DEFAULT, 0);
 #ifdef RX_ENABLE_LOCKS
@@ -656,8 +655,8 @@ rxi_rto_startTimer(struct rx_call *call, int lastPacket, int istack)
     MUTEX_ENTER(&rx_refcnt_mutex);
     CALL_HOLD(call, RX_CALL_REFCOUNT_RESEND);
     MUTEX_EXIT(&rx_refcnt_mutex);
-    call->resendEvent = rxevent_PostNow2(&retryTime, &now, rxi_Resend,
-                                        call, 0, istack);
+    call->resendEvent = rxevent_Post(&retryTime, &now, rxi_Resend,
+                                    call, NULL, istack);
 }
 
 /*!
@@ -674,10 +673,7 @@ rxi_rto_startTimer(struct rx_call *call, int lastPacket, int istack)
 static_inline void
 rxi_rto_cancel(struct rx_call *call)
 {
-    if (!call->resendEvent)
-       return;
-
-    rxevent_Cancel(call->resendEvent, call, RX_CALL_REFCOUNT_RESEND);
+    rxevent_Cancel(&call->resendEvent, call, RX_CALL_REFCOUNT_RESEND);
 }
 
 /*!
@@ -763,6 +759,37 @@ rx_SetBusyChannelError(afs_int32 error)
     rxi_busyChannelError = error;
 }
 
+/**
+ * Set a delayed ack event on the specified call for the given time
+ *
+ * @param[in] call - the call on which to set the event
+ * @param[in] offset - the delay from now after which the event fires
+ */
+void
+rxi_PostDelayedAckEvent(struct rx_call *call, struct clock *offset)
+{
+    struct clock now, when;
+
+    clock_GetTime(&now);
+    when = now;
+    clock_Add(&when, offset);
+
+    if (!call->delayedAckEvent
+       || clock_Gt(&call->delayedAckTime, &when)) {
+
+        rxevent_Cancel(&call->delayedAckEvent, call,
+                      RX_CALL_REFCOUNT_DELAY);
+       MUTEX_ENTER(&rx_refcnt_mutex);
+       CALL_HOLD(call, RX_CALL_REFCOUNT_DELAY);
+       MUTEX_EXIT(&rx_refcnt_mutex);
+
+       call->delayedAckEvent = rxevent_Post(&when, &now,
+                                            rxi_SendDelayedAck,
+                                            call, NULL, 0);
+       call->delayedAckTime = when;
+    }
+}
+
 /* called with unincremented nRequestsRunning to see if it is OK to start
  * a new thread in this service.  Could be "no" for two reasons: over the
  * max quota, or would prevent others from reaching their min quota.
@@ -922,7 +949,7 @@ rx_StartServer(int donateMe)
     }
 
     /* Turn on reaping of idle server connections */
-    rxi_ReapConnections(NULL, NULL, NULL);
+    rxi_ReapConnections(NULL, NULL, NULL, 0);
 
     USERPRI;
 
@@ -1210,6 +1237,7 @@ rxi_DestroyConnectionNoLock(struct rx_connection *conn)
     MUTEX_EXIT(&conn->conn_data_lock);
 
     /* Check for extant references to this connection */
+    MUTEX_ENTER(&conn->conn_call_lock);
     for (i = 0; i < RX_MAXCALLS; i++) {
        struct rx_call *call = conn->call[i];
        if (call) {
@@ -1220,7 +1248,7 @@ rxi_DestroyConnectionNoLock(struct rx_connection *conn)
                    /* Push the final acknowledgment out now--there
                     * won't be a subsequent call to acknowledge the
                     * last reply packets */
-                   rxevent_Cancel(call->delayedAckEvent, call,
+                   rxevent_Cancel(&call->delayedAckEvent, call,
                                   RX_CALL_REFCOUNT_DELAY);
                    if (call->state == RX_STATE_PRECALL
                        || call->state == RX_STATE_ACTIVE) {
@@ -1233,6 +1261,8 @@ rxi_DestroyConnectionNoLock(struct rx_connection *conn)
            }
        }
     }
+    MUTEX_EXIT(&conn->conn_call_lock);
+
 #ifdef RX_ENABLE_LOCKS
     if (!havecalls) {
        if (MUTEX_TRYENTER(&conn->conn_data_lock)) {
@@ -1259,7 +1289,7 @@ rxi_DestroyConnectionNoLock(struct rx_connection *conn)
     }
 
     if (conn->delayedAbortEvent) {
-       rxevent_Cancel(conn->delayedAbortEvent, (struct rx_call *)0, 0);
+       rxevent_Cancel(&conn->delayedAbortEvent, NULL, 0);
        packet = rxi_AllocPacket(RX_PACKET_CLASS_SPECIAL);
        if (packet) {
            MUTEX_ENTER(&conn->conn_data_lock);
@@ -1287,12 +1317,9 @@ rxi_DestroyConnectionNoLock(struct rx_connection *conn)
 
     /* Make sure the connection is completely reset before deleting it. */
     /* get rid of pending events that could zap us later */
-    if (conn->challengeEvent)
-       rxevent_Cancel(conn->challengeEvent, (struct rx_call *)0, 0);
-    if (conn->checkReachEvent)
-       rxevent_Cancel(conn->checkReachEvent, (struct rx_call *)0, 0);
-    if (conn->natKeepAliveEvent)
-       rxevent_Cancel(conn->natKeepAliveEvent, (struct rx_call *)0, 0);
+    rxevent_Cancel(&conn->challengeEvent, NULL, 0);
+    rxevent_Cancel(&conn->checkReachEvent, NULL, 0);
+    rxevent_Cancel(&conn->natKeepAliveEvent, NULL, 0);
 
     /* Add the connection to the list of destroyed connections that
      * need to be cleaned up. This is necessary to avoid deadlocks
@@ -1813,6 +1840,20 @@ rxi_ServerProc(int threadID, struct rx_call *newcall, osi_socket * socketp)
            }
        }
 
+#ifdef KERNEL
+       if (afs_termState == AFSOP_STOP_RXCALLBACK) {
+#ifdef RX_ENABLE_LOCKS
+           AFS_GLOCK();
+#endif /* RX_ENABLE_LOCKS */
+           afs_termState = AFSOP_STOP_AFS;
+           afs_osi_Wakeup(&afs_termState);
+#ifdef RX_ENABLE_LOCKS
+           AFS_GUNLOCK();
+#endif /* RX_ENABLE_LOCKS */
+           return;
+       }
+#endif
+
        /* if server is restarting( typically smooth shutdown) then do not
         * allow any new calls.
         */
@@ -1828,20 +1869,8 @@ rxi_ServerProc(int threadID, struct rx_call *newcall, osi_socket * socketp)
 
            MUTEX_EXIT(&call->lock);
            USERPRI;
+           continue;
        }
-#ifdef KERNEL
-       if (afs_termState == AFSOP_STOP_RXCALLBACK) {
-#ifdef RX_ENABLE_LOCKS
-           AFS_GLOCK();
-#endif /* RX_ENABLE_LOCKS */
-           afs_termState = AFSOP_STOP_AFS;
-           afs_osi_Wakeup(&afs_termState);
-#ifdef RX_ENABLE_LOCKS
-           AFS_GUNLOCK();
-#endif /* RX_ENABLE_LOCKS */
-           return;
-       }
-#endif
 
        tservice = call->conn->service;
 
@@ -1854,6 +1883,10 @@ rxi_ServerProc(int threadID, struct rx_call *newcall, osi_socket * socketp)
            (*tservice->afterProc) (call, code);
 
        rx_EndCall(call, code);
+
+       if (tservice->postProc)
+           (*tservice->postProc) (code);
+
         if (rx_stats_active) {
             MUTEX_ENTER(&rx_stats_mutex);
             rxi_nCalls++;
@@ -2334,7 +2367,7 @@ rx_EndCall(struct rx_call *call, afs_int32 rc)
            call->state = RX_STATE_DALLY;
            rxi_ClearTransmitQueue(call, 0);
            rxi_rto_cancel(call);
-           rxevent_Cancel(call->keepAliveEvent, call,
+           rxevent_Cancel(&call->keepAliveEvent, call,
                           RX_CALL_REFCOUNT_ALIVE);
        }
     } else {                   /* Client connection */
@@ -2352,10 +2385,9 @@ rx_EndCall(struct rx_call *call, afs_int32 rc)
         * and force-send it now.
         */
        if (call->delayedAckEvent) {
-           rxevent_Cancel(call->delayedAckEvent, call,
+           rxevent_Cancel(&call->delayedAckEvent, call,
                           RX_CALL_REFCOUNT_DELAY);
-           call->delayedAckEvent = NULL;
-           rxi_SendDelayedAck(NULL, call, NULL);
+           rxi_SendDelayedAck(NULL, call, NULL, 0);
        }
 
        /* We need to release the call lock since it's lower than the
@@ -2667,7 +2699,11 @@ rxi_FreeCall(struct rx_call *call, int haveCTLock)
     call->state = RX_STATE_RESET;
     MUTEX_EXIT(&rx_refcnt_mutex);
     rxi_ResetCall(call, 0);
-    call->conn->call[channel] = (struct rx_call *)0;
+
+    MUTEX_ENTER(&conn->conn_call_lock);
+    if (call->conn->call[channel] == call)
+        call->conn->call[channel] = 0;
+    MUTEX_EXIT(&conn->conn_call_lock);
 
     MUTEX_ENTER(&rx_freeCallQueue_lock);
     SET_CALL_QUEUE_LOCK(call, &rx_freeCallQueue_lock);
@@ -3209,94 +3245,77 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
 
     channel = np->header.cid & RX_CHANNELMASK;
     call = conn->call[channel];
-#ifdef RX_ENABLE_LOCKS
-    if (call)
-       MUTEX_ENTER(&call->lock);
-    /* Test to see if call struct is still attached to conn. */
-    if (call != conn->call[channel]) {
-       if (call)
-           MUTEX_EXIT(&call->lock);
-       if (type == RX_SERVER_CONNECTION) {
-           call = conn->call[channel];
-           /* If we started with no call attached and there is one now,
-            * another thread is also running this routine and has gotten
-            * the connection channel. We should drop this packet in the tests
-            * below. If there was a call on this connection and it's now
-            * gone, then we'll be making a new call below.
-            * If there was previously a call and it's now different then
-            * the old call was freed and another thread running this routine
-            * has created a call on this channel. One of these two threads
-            * has a packet for the old call and the code below handles those
-            * cases.
-            */
-           if (call)
-               MUTEX_ENTER(&call->lock);
-       } else {
-           /* This packet can't be for this call. If the new call address is
-            * 0 then no call is running on this channel. If there is a call
-            * then, since this is a client connection we're getting data for
-            * it must be for the previous call.
-            */
-            if (rx_stats_active)
-               rx_atomic_inc(&rx_stats.spuriousPacketsRead);
-            MUTEX_ENTER(&rx_refcnt_mutex);
-           conn->refCount--;
-            MUTEX_EXIT(&rx_refcnt_mutex);
-           return np;
-       }
-    }
-#endif
-    currentCallNumber = conn->callNumber[channel];
 
-    if (type == RX_SERVER_CONNECTION) {        /* We're the server */
-       if (np->header.callNumber < currentCallNumber) {
-            if (rx_stats_active)
-               rx_atomic_inc(&rx_stats.spuriousPacketsRead);
-#ifdef RX_ENABLE_LOCKS
-           if (call)
-               MUTEX_EXIT(&call->lock);
-#endif
-            MUTEX_ENTER(&rx_refcnt_mutex);
-           conn->refCount--;
-            MUTEX_EXIT(&rx_refcnt_mutex);
-           return np;
-       }
-       if (!call) {
-           MUTEX_ENTER(&conn->conn_call_lock);
-           call = rxi_NewCall(conn, channel);
-           MUTEX_EXIT(&conn->conn_call_lock);
-           *call->callNumber = np->header.callNumber;
+    if (call) {
+       MUTEX_ENTER(&call->lock);
+        currentCallNumber = conn->callNumber[channel];
+    } else if (type == RX_SERVER_CONNECTION) {  /* No call allocated */
+        MUTEX_ENTER(&conn->conn_call_lock);
+        call = conn->call[channel];
+        if (call) {
+            MUTEX_ENTER(&call->lock);
+            MUTEX_EXIT(&conn->conn_call_lock);
+            currentCallNumber = conn->callNumber[channel];
+        } else {
+            call = rxi_NewCall(conn, channel);  /* returns locked call */
+            MUTEX_EXIT(&conn->conn_call_lock);
+            *call->callNumber = currentCallNumber = np->header.callNumber;
 #ifdef RXDEBUG
-           if (np->header.callNumber == 0)
-               dpf(("RecPacket call 0 %d %s: %x.%u.%u.%u.%u.%u.%u flags %d, packet %"AFS_PTR_FMT" len %d\n",
-                      np->header.serial, rx_packetTypes[np->header.type - 1], ntohl(conn->peer->host), ntohs(conn->peer->port),
-                      np->header.serial, np->header.epoch, np->header.cid, np->header.callNumber, np->header.seq,
-                      np->header.flags, np, np->length));
+            if (np->header.callNumber == 0)
+                dpf(("RecPacket call 0 %d %s: %x.%u.%u.%u.%u.%u.%u flags %d, packet %"AFS_PTR_FMT" len %d\n",
+                     np->header.serial, rx_packetTypes[np->header.type - 1], ntohl(conn->peer->host), ntohs(conn->peer->port),
+                     np->header.serial, np->header.epoch, np->header.cid, np->header.callNumber, np->header.seq,
+                     np->header.flags, np, np->length));
 #endif
-           call->state = RX_STATE_PRECALL;
-           clock_GetTime(&call->queueTime);
-           hzero(call->bytesSent);
-           hzero(call->bytesRcvd);
-           /*
-            * If the number of queued calls exceeds the overload
-            * threshold then abort this call.
-            */
-           if ((rx_BusyThreshold > 0) &&
-               (rx_atomic_read(&rx_nWaiting) > rx_BusyThreshold)) {
-               struct rx_packet *tp;
-
-               rxi_CallError(call, rx_BusyError);
-               tp = rxi_SendCallAbort(call, np, 1, 0);
-               MUTEX_EXIT(&call->lock);
+            call->state = RX_STATE_PRECALL;
+            clock_GetTime(&call->queueTime);
+            hzero(call->bytesSent);
+            hzero(call->bytesRcvd);
+            /*
+             * If the number of queued calls exceeds the overload
+             * threshold then abort this call.
+             */
+            if ((rx_BusyThreshold > 0) &&
+                (rx_atomic_read(&rx_nWaiting) > rx_BusyThreshold)) {
+                struct rx_packet *tp;
+
+                rxi_CallError(call, rx_BusyError);
+                tp = rxi_SendCallAbort(call, np, 1, 0);
+                MUTEX_EXIT(&call->lock);
                 MUTEX_ENTER(&rx_refcnt_mutex);
-               conn->refCount--;
+                conn->refCount--;
                 MUTEX_EXIT(&rx_refcnt_mutex);
                 if (rx_stats_active)
                     rx_atomic_inc(&rx_stats.nBusies);
-               return tp;
-           }
-           rxi_KeepAliveOn(call);
-       } else if (np->header.callNumber != currentCallNumber) {
+                return tp;
+            }
+            rxi_KeepAliveOn(call);
+        }
+    } else {    /* RX_CLIENT_CONNECTION and No call allocated */
+        /* This packet can't be for this call. If the new call address is
+         * 0 then no call is running on this channel. If there is a call
+         * then, since this is a client connection we're getting data for
+         * it must be for the previous call.
+         */
+        if (rx_stats_active)
+            rx_atomic_inc(&rx_stats.spuriousPacketsRead);
+        MUTEX_ENTER(&rx_refcnt_mutex);
+        conn->refCount--;
+        MUTEX_EXIT(&rx_refcnt_mutex);
+        return np;
+    }
+
+    /* There is a non-NULL locked call at this point */
+    if (type == RX_SERVER_CONNECTION) {        /* We're the server */
+        if (np->header.callNumber < currentCallNumber) {
+            MUTEX_EXIT(&call->lock);
+            if (rx_stats_active)
+                rx_atomic_inc(&rx_stats.spuriousPacketsRead);
+            MUTEX_ENTER(&rx_refcnt_mutex);
+            conn->refCount--;
+            MUTEX_EXIT(&rx_refcnt_mutex);
+            return np;
+        } else if (np->header.callNumber != currentCallNumber) {
            /* Wait until the transmit queue is idle before deciding
             * whether to reset the current call. Chances are that the
             * call will be in ether DALLY or HOLD state once the TQ_BUSY
@@ -3372,15 +3391,11 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
        }
     } else {                   /* we're the client */
        /* Ignore all incoming acknowledgements for calls in DALLY state */
-       if (call && (call->state == RX_STATE_DALLY)
+       if ((call->state == RX_STATE_DALLY)
            && (np->header.type == RX_PACKET_TYPE_ACK)) {
             if (rx_stats_active)
                 rx_atomic_inc(&rx_stats.ignorePacketDally);
-#ifdef  RX_ENABLE_LOCKS
-           if (call) {
-               MUTEX_EXIT(&call->lock);
-           }
-#endif
+            MUTEX_EXIT(&call->lock);
             MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
             MUTEX_EXIT(&rx_refcnt_mutex);
@@ -3389,14 +3404,10 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
 
        /* Ignore anything that's not relevant to the current call.  If there
         * isn't a current call, then no packet is relevant. */
-       if (!call || (np->header.callNumber != currentCallNumber)) {
+       if (np->header.callNumber != currentCallNumber) {
             if (rx_stats_active)
                 rx_atomic_inc(&rx_stats.spuriousPacketsRead);
-#ifdef RX_ENABLE_LOCKS
-           if (call) {
-               MUTEX_EXIT(&call->lock);
-           }
-#endif
+            MUTEX_EXIT(&call->lock);
             MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
             MUTEX_EXIT(&rx_refcnt_mutex);
@@ -3405,9 +3416,7 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
        /* If the service security object index stamped in the packet does not
         * match the connection's security index, ignore the packet */
        if (np->header.securityIndex != conn->securityIndex) {
-#ifdef RX_ENABLE_LOCKS
            MUTEX_EXIT(&call->lock);
-#endif
             MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
             MUTEX_EXIT(&rx_refcnt_mutex);
@@ -3650,8 +3659,35 @@ TooLow(struct rx_packet *ap, struct rx_call *acall)
 }
 #endif /* KERNEL */
 
+/*!
+ * Clear the attach wait flag on a connection and proceed.
+ *
+ * Any processing waiting for a connection to be attached should be
+ * unblocked. We clear the flag and do any other needed tasks.
+ *
+ * @param[in] conn
+ *      the conn to unmark waiting for attach
+ *
+ * @pre conn's conn_data_lock must be locked before calling this function
+ *
+ */
+static void
+rxi_ConnClearAttachWait(struct rx_connection *conn)
+{
+    /* Indicate that rxi_CheckReachEvent is no longer running by
+     * clearing the flag.  Must be atomic under conn_data_lock to
+     * avoid a new call slipping by: rxi_CheckConnReach holds
+     * conn_data_lock while checking RX_CONN_ATTACHWAIT.
+     */
+    conn->flags &= ~RX_CONN_ATTACHWAIT;
+    if (conn->flags & RX_CONN_NAT_PING) {
+       conn->flags &= ~RX_CONN_NAT_PING;
+       rxi_ScheduleNatKeepAliveEvent(conn);
+    }
+}
+
 static void
-rxi_CheckReachEvent(struct rxevent *event, void *arg1, void *arg2)
+rxi_CheckReachEvent(struct rxevent *event, void *arg1, void *arg2, int dummy)
 {
     struct rx_connection *conn = arg1;
     struct rx_call *acall = arg2;
@@ -3660,7 +3696,12 @@ rxi_CheckReachEvent(struct rxevent *event, void *arg1, void *arg2)
     int i, waiting;
 
     MUTEX_ENTER(&conn->conn_data_lock);
-    conn->checkReachEvent = NULL;
+
+    if (event) {
+       rxevent_Put(conn->checkReachEvent);
+       conn->checkReachEvent = NULL;
+    }
+
     waiting = conn->flags & RX_CONN_ATTACHWAIT;
     if (event) {
         MUTEX_ENTER(&rx_refcnt_mutex);
@@ -3681,12 +3722,7 @@ rxi_CheckReachEvent(struct rxevent *event, void *arg1, void *arg2)
                }
            }
            if (!call)
-               /* Indicate that rxi_CheckReachEvent is no longer running by
-                * clearing the flag.  Must be atomic under conn_data_lock to
-                * avoid a new call slipping by: rxi_CheckConnReach holds
-                * conn_data_lock while checking RX_CONN_ATTACHWAIT.
-                */
-               conn->flags &= ~RX_CONN_ATTACHWAIT;
+               rxi_ConnClearAttachWait(conn);
            MUTEX_EXIT(&conn->conn_data_lock);
            MUTEX_EXIT(&conn->conn_call_lock);
        }
@@ -3706,9 +3742,9 @@ rxi_CheckReachEvent(struct rxevent *event, void *arg1, void *arg2)
                 MUTEX_ENTER(&rx_refcnt_mutex);
                conn->refCount++;
                 MUTEX_EXIT(&rx_refcnt_mutex);
-               conn->checkReachEvent =
-                   rxevent_PostNow(&when, &now, rxi_CheckReachEvent, conn,
-                                   NULL);
+               conn->checkReachEvent = rxevent_Post(&when, &now,
+                                                    rxi_CheckReachEvent, conn,
+                                                    NULL, 0);
            }
            MUTEX_EXIT(&conn->conn_data_lock);
        }
@@ -3740,7 +3776,7 @@ rxi_CheckConnReach(struct rx_connection *conn, struct rx_call *call)
     conn->flags |= RX_CONN_ATTACHWAIT;
     MUTEX_EXIT(&conn->conn_data_lock);
     if (!conn->checkReachEvent)
-       rxi_CheckReachEvent(NULL, conn, call);
+       rxi_CheckReachEvent(NULL, conn, call, 0);
 
     return 1;
 }
@@ -3786,7 +3822,6 @@ rxi_ReceiveDataPacket(struct rx_call *call,
     afs_uint32 serial=0, flags=0;
     int isFirst;
     struct rx_packet *tnp;
-    struct clock when, now;
     if (rx_stats_active)
         rx_atomic_inc(&rx_stats.dataPacketsRead);
 
@@ -3803,22 +3838,11 @@ rxi_ReceiveDataPacket(struct rx_call *call,
        call->rprev = np->header.serial;
        rxi_calltrace(RX_TRACE_DROP, call);
        dpf(("packet %"AFS_PTR_FMT" dropped on receipt - quota problems\n", np));
-       if (rxi_doreclaim)
-           rxi_ClearReceiveQueue(call);
-       clock_GetTime(&now);
-       when = now;
-       clock_Add(&when, &rx_softAckDelay);
-       if (!call->delayedAckEvent
-           || clock_Gt(&call->delayedAckEvent->eventTime, &when)) {
-           rxevent_Cancel(call->delayedAckEvent, call,
-                          RX_CALL_REFCOUNT_DELAY);
-            MUTEX_ENTER(&rx_refcnt_mutex);
-           CALL_HOLD(call, RX_CALL_REFCOUNT_DELAY);
-            MUTEX_EXIT(&rx_refcnt_mutex);
+        /* We used to clear the receive queue here, in an attempt to free
+         * packets. However this is unsafe if the queue has received a
+         * soft ACK for the final packet */
+       rxi_PostDelayedAckEvent(call, &rx_softAckDelay);
 
-           call->delayedAckEvent =
-               rxevent_PostNow(&when, &now, rxi_SendDelayedAck, call, 0);
-       }
        /* we've damaged this call already, might as well do it in. */
        return np;
     }
@@ -3870,7 +3894,7 @@ rxi_ReceiveDataPacket(struct rx_call *call,
                 if (rx_stats_active)
                     rx_atomic_inc(&rx_stats.dupPacketsRead);
                dpf(("packet %"AFS_PTR_FMT" dropped on receipt - duplicate\n", np));
-               rxevent_Cancel(call->delayedAckEvent, call,
+               rxevent_Cancel(&call->delayedAckEvent, call,
                               RX_CALL_REFCOUNT_DELAY);
                np = rxi_SendAck(call, np, serial, RX_ACK_DUPLICATE, istack);
                ackNeeded = 0;
@@ -3960,7 +3984,7 @@ rxi_ReceiveDataPacket(struct rx_call *call,
            if (seq < call->rnext) {
                 if (rx_stats_active)
                     rx_atomic_inc(&rx_stats.dupPacketsRead);
-               rxevent_Cancel(call->delayedAckEvent, call,
+               rxevent_Cancel(&call->delayedAckEvent, call,
                               RX_CALL_REFCOUNT_DELAY);
                np = rxi_SendAck(call, np, serial, RX_ACK_DUPLICATE, istack);
                ackNeeded = 0;
@@ -3972,7 +3996,7 @@ rxi_ReceiveDataPacket(struct rx_call *call,
             * accomodated by the current window, then send a negative
             * acknowledge and drop the packet */
            if ((call->rnext + call->rwind) <= seq) {
-               rxevent_Cancel(call->delayedAckEvent, call,
+               rxevent_Cancel(&call->delayedAckEvent, call,
                               RX_CALL_REFCOUNT_DELAY);
                np = rxi_SendAck(call, np, serial, RX_ACK_EXCEEDS_WINDOW,
                                 istack);
@@ -3988,7 +4012,7 @@ rxi_ReceiveDataPacket(struct rx_call *call,
                if (seq == tp->header.seq) {
                     if (rx_stats_active)
                         rx_atomic_inc(&rx_stats.dupPacketsRead);
-                   rxevent_Cancel(call->delayedAckEvent, call,
+                   rxevent_Cancel(&call->delayedAckEvent, call,
                                   RX_CALL_REFCOUNT_DELAY);
                    np = rxi_SendAck(call, np, serial, RX_ACK_DUPLICATE,
                                     istack);
@@ -4096,55 +4120,25 @@ rxi_ReceiveDataPacket(struct rx_call *call,
      * Send an ack when requested by the peer, or once every
      * rxi_SoftAckRate packets until the last packet has been
      * received. Always send a soft ack for the last packet in
-     * the server's reply.
-     *
-     * If there was more than one packet received for the call
-     * and we have received all of them, immediately send an
-     * RX_PACKET_TYPE_ACKALL packet so that the peer can empty
-     * its packet transmit queue and cancel all resend events.
-     *
-     * When there is only one packet in the call there is a
-     * chance that we can race with Ping ACKs sent as part of
-     * connection establishment if the udp packets are delivered
-     * out of order.  When the race occurs, a two second delay
-     * will occur while waiting for a new Ping ACK to be sent.
-     */
-    if (!isFirst && (call->flags & RX_CALL_RECEIVE_DONE)) {
-        rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
-        rxi_AckAll(NULL, call, 0);
-    } else if (ackNeeded) {
-       rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
+     * the server's reply. */
+    if (ackNeeded) {
+       rxevent_Cancel(&call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
        np = rxi_SendAck(call, np, serial, ackNeeded, istack);
     } else if (call->nSoftAcks > (u_short) rxi_SoftAckRate) {
-       rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
+       rxevent_Cancel(&call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
        np = rxi_SendAck(call, np, serial, RX_ACK_IDLE, istack);
     } else if (call->nSoftAcks) {
-       clock_GetTime(&now);
-       when = now;
-       if (haveLast && !(flags & RX_CLIENT_INITIATED)) {
-           clock_Add(&when, &rx_lastAckDelay);
-       } else {
-           clock_Add(&when, &rx_softAckDelay);
-       }
-       if (!call->delayedAckEvent
-           || clock_Gt(&call->delayedAckEvent->eventTime, &when)) {
-           rxevent_Cancel(call->delayedAckEvent, call,
-                          RX_CALL_REFCOUNT_DELAY);
-            MUTEX_ENTER(&rx_refcnt_mutex);
-           CALL_HOLD(call, RX_CALL_REFCOUNT_DELAY);
-            MUTEX_EXIT(&rx_refcnt_mutex);
-           call->delayedAckEvent =
-               rxevent_PostNow(&when, &now, rxi_SendDelayedAck, call, 0);
-       }
+       if (haveLast && !(flags & RX_CLIENT_INITIATED))
+           rxi_PostDelayedAckEvent(call, &rx_lastAckDelay);
+       else
+           rxi_PostDelayedAckEvent(call, &rx_softAckDelay);
+    } else if (call->flags & RX_CALL_RECEIVE_DONE) {
+       rxevent_Cancel(&call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
     }
 
     return np;
 }
 
-#ifdef ADAPT_WINDOW
-static void rxi_ComputeRate();
-#endif
-
 static void
 rxi_UpdatePeerReach(struct rx_connection *conn, struct rx_call *acall)
 {
@@ -4158,7 +4152,7 @@ rxi_UpdatePeerReach(struct rx_connection *conn, struct rx_call *acall)
     if (conn->flags & RX_CONN_ATTACHWAIT) {
        int i;
 
-       conn->flags &= ~RX_CONN_ATTACHWAIT;
+       rxi_ConnClearAttachWait(conn);
        MUTEX_EXIT(&conn->conn_data_lock);
 
        for (i = 0; i < RX_MAXCALLS; i++) {
@@ -4390,10 +4384,6 @@ rxi_ReceiveAckPacket(struct rx_call *call, struct rx_packet *np,
            rxi_ComputeRoundTripTime(tp, ap, call, peer, &now);
        }
 
-#ifdef ADAPT_WINDOW
-       rxi_ComputeRate(call->conn->peer, call, p, np, ap->reason);
-#endif
-
 #ifdef AFS_GLOBAL_RXLOCK_KERNEL
        /* XXX Hack. Because we have to release the global rx lock when sending
         * packets (osi_NetSend) we drop all acks while we're traversing the tq
@@ -4426,13 +4416,6 @@ rxi_ReceiveAckPacket(struct rx_call *call, struct rx_packet *np,
        tp = next;
     }
 
-#ifdef ADAPT_WINDOW
-    /* Give rate detector a chance to respond to ping requests */
-    if (ap->reason == RX_ACK_PING_RESPONSE) {
-       rxi_ComputeRate(peer, call, 0, np, ap->reason);
-    }
-#endif
-
     /* N.B. we don't turn off any timers here.  They'll go away by themselves, anyway */
 
     /* Second section of the queue - packets for which we are receiving
@@ -4460,9 +4443,6 @@ rxi_ReceiveAckPacket(struct rx_call *call, struct rx_packet *np,
                newAckCount++;
                tp->flags |= RX_PKTFLAG_ACKED;
                rxi_ComputeRoundTripTime(tp, ap, call, peer, &now);
-#ifdef ADAPT_WINDOW
-               rxi_ComputeRate(call->conn->peer, call, tp, np, ap->reason);
-#endif
            }
            if (missing) {
                nNacked++;
@@ -4630,7 +4610,7 @@ rxi_ReceiveAckPacket(struct rx_call *call, struct rx_packet *np,
        rxi_rto_packet_acked(call, istack);
 
     if (call->flags & RX_CALL_FAST_RECOVER) {
-       if (nNacked) {
+       if (newAckCount == 0) {
            call->cwind = MIN((int)(call->cwind + 1), rx_maxSendWindow);
        } else {
            call->flags &= ~RX_CALL_FAST_RECOVER;
@@ -4718,7 +4698,7 @@ rxi_ReceiveAckPacket(struct rx_call *call, struct rx_packet *np,
        && call->tfirst + call->nSoftAcked >= call->tnext) {
        call->state = RX_STATE_DALLY;
        rxi_ClearTransmitQueue(call, 0);
-        rxevent_Cancel(call->keepAliveEvent, call, RX_CALL_REFCOUNT_ALIVE);
+        rxevent_Cancel(&call->keepAliveEvent, call, RX_CALL_REFCOUNT_ALIVE);
     } else if (!queue_IsEmpty(&call->tq)) {
        rxi_Start(call, istack);
     }
@@ -4927,6 +4907,7 @@ rxi_AckAll(struct rxevent *event, struct rx_call *call, char *dummy)
 #ifdef RX_ENABLE_LOCKS
     if (event) {
        MUTEX_ENTER(&call->lock);
+       rxevent_Put(call->delayedAckEvent);
        call->delayedAckEvent = NULL;
         MUTEX_ENTER(&rx_refcnt_mutex);
        CALL_RELE(call, RX_CALL_REFCOUNT_ACKALL);
@@ -4938,8 +4919,10 @@ rxi_AckAll(struct rxevent *event, struct rx_call *call, char *dummy)
     if (event)
        MUTEX_EXIT(&call->lock);
 #else /* RX_ENABLE_LOCKS */
-    if (event)
+    if (event) {
+       rxevent_Put(call->delayedAckEvent);
        call->delayedAckEvent = NULL;
+    }
     rxi_SendSpecial(call, call->conn, (struct rx_packet *)0,
                    RX_PACKET_TYPE_ACKALL, NULL, 0, 0);
     call->flags |= RX_CALL_ACKALL_SENT;
@@ -4947,14 +4930,17 @@ rxi_AckAll(struct rxevent *event, struct rx_call *call, char *dummy)
 }
 
 void
-rxi_SendDelayedAck(struct rxevent *event, void *arg1, void *unused)
+rxi_SendDelayedAck(struct rxevent *event, void *arg1, void *unused1,
+                  int unused2)
 {
     struct rx_call *call = arg1;
 #ifdef RX_ENABLE_LOCKS
     if (event) {
        MUTEX_ENTER(&call->lock);
-       if (event == call->delayedAckEvent)
+       if (event == call->delayedAckEvent) {
+           rxevent_Put(call->delayedAckEvent);
            call->delayedAckEvent = NULL;
+       }
         MUTEX_ENTER(&rx_refcnt_mutex);
        CALL_RELE(call, RX_CALL_REFCOUNT_DELAY);
         MUTEX_EXIT(&rx_refcnt_mutex);
@@ -4963,8 +4949,10 @@ rxi_SendDelayedAck(struct rxevent *event, void *arg1, void *unused)
     if (event)
        MUTEX_EXIT(&call->lock);
 #else /* RX_ENABLE_LOCKS */
-    if (event)
+    if (event) {
+       rxevent_Put(call->delayedAckEvent);
        call->delayedAckEvent = NULL;
+    }
     (void)rxi_SendAck(call, 0, 0, RX_ACK_DELAY, 0);
 #endif /* RX_ENABLE_LOCKS */
 }
@@ -5092,7 +5080,7 @@ rxi_SendCallAbort(struct rx_call *call, struct rx_packet *packet,
     if (force || rxi_callAbortThreshhold == 0
        || call->abortCount < rxi_callAbortThreshhold) {
        if (call->delayedAbortEvent) {
-           rxevent_Cancel(call->delayedAbortEvent, call,
+           rxevent_Cancel(&call->delayedAbortEvent, call,
                           RX_CALL_REFCOUNT_ABORT);
        }
        error = htonl(call->error);
@@ -5108,7 +5096,7 @@ rxi_SendCallAbort(struct rx_call *call, struct rx_packet *packet,
        CALL_HOLD(call, RX_CALL_REFCOUNT_ABORT);
         MUTEX_EXIT(&rx_refcnt_mutex);
        call->delayedAbortEvent =
-           rxevent_PostNow(&when, &now, rxi_SendDelayedCallAbort, call, 0);
+           rxevent_Post(&when, &now, rxi_SendDelayedCallAbort, call, 0, 0);
     }
     return packet;
 }
@@ -5138,9 +5126,8 @@ rxi_SendConnectionAbort(struct rx_connection *conn,
 
     if (force || rxi_connAbortThreshhold == 0
        || conn->abortCount < rxi_connAbortThreshhold) {
-       if (conn->delayedAbortEvent) {
-           rxevent_Cancel(conn->delayedAbortEvent, (struct rx_call *)0, 0);
-       }
+
+       rxevent_Cancel(&conn->delayedAbortEvent, NULL, 0);
        error = htonl(conn->error);
        conn->abortCount++;
        MUTEX_EXIT(&conn->conn_data_lock);
@@ -5154,7 +5141,7 @@ rxi_SendConnectionAbort(struct rx_connection *conn,
        when = now;
        clock_Addmsec(&when, rxi_connAbortDelay);
        conn->delayedAbortEvent =
-           rxevent_PostNow(&when, &now, rxi_SendDelayedConnAbort, conn, 0);
+           rxevent_Post(&when, &now, rxi_SendDelayedConnAbort, conn, NULL, 0);
     }
     return packet;
 }
@@ -5174,14 +5161,11 @@ rxi_ConnectionError(struct rx_connection *conn,
        dpf(("rxi_ConnectionError conn %"AFS_PTR_FMT" error %d\n", conn, error));
 
        MUTEX_ENTER(&conn->conn_data_lock);
-       if (conn->challengeEvent)
-           rxevent_Cancel(conn->challengeEvent, (struct rx_call *)0, 0);
-       if (conn->natKeepAliveEvent)
-           rxevent_Cancel(conn->natKeepAliveEvent, (struct rx_call *)0, 0);
+       rxevent_Cancel(&conn->challengeEvent, NULL, 0);
+       rxevent_Cancel(&conn->natKeepAliveEvent, NULL, 0);
        if (conn->checkReachEvent) {
-           rxevent_Cancel(conn->checkReachEvent, (struct rx_call *)0, 0);
-           conn->checkReachEvent = 0;
-           conn->flags &= ~RX_CONN_ATTACHWAIT;
+           rxevent_Cancel(&conn->checkReachEvent, NULL, 0);
+           conn->flags &= ~(RX_CONN_ATTACHWAIT|RX_CONN_NAT_PING);
             MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
             MUTEX_EXIT(&rx_refcnt_mutex);
@@ -5242,9 +5226,6 @@ rxi_CallError(struct rx_call *call, afs_int32 error)
  * nFree are not reset, since these fields are manipulated by
  * unprotected macros, and may only be reset by non-interrupting code.
  */
-#ifdef ADAPT_WINDOW
-/* this code requires that call->conn be set properly as a pre-condition. */
-#endif /* ADAPT_WINDOW */
 
 void
 rxi_ResetCall(struct rx_call *call, int newcall)
@@ -5264,12 +5245,11 @@ rxi_ResetCall(struct rx_call *call, int newcall)
        call->arrivalProc = (void (*)())0;
     }
 
-    if (call->growMTUEvent)
-       rxevent_Cancel(call->growMTUEvent, call,
-                      RX_CALL_REFCOUNT_ALIVE);
+
+    rxevent_Cancel(&call->growMTUEvent, call, RX_CALL_REFCOUNT_ALIVE);
 
     if (call->delayedAbortEvent) {
-       rxevent_Cancel(call->delayedAbortEvent, call, RX_CALL_REFCOUNT_ABORT);
+       rxevent_Cancel(&call->delayedAbortEvent, call, RX_CALL_REFCOUNT_ABORT);
        packet = rxi_AllocPacket(RX_PACKET_CLASS_SPECIAL);
        if (packet) {
            rxi_SendCallAbort(call, packet, 0, 1);
@@ -5399,7 +5379,7 @@ rxi_ResetCall(struct rx_call *call, int newcall)
 #endif /* RX_ENABLE_LOCKS */
 
     rxi_KeepAliveOff(call);
-    rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
+    rxevent_Cancel(&call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
 }
 
 /* Send an acknowledge for the indicated packet (seq,serial) of the
@@ -5435,7 +5415,7 @@ rxi_SendAck(struct rx_call *call,
     struct rx_packet *rqp;
     struct rx_packet *nxp;     /* For queue_Scan */
     struct rx_packet *p;
-    u_char offset;
+    u_char offset = 0;
     afs_int32 templ;
     afs_uint32 padbytes = 0;
 #ifdef RX_ENABLE_TSFPQ
@@ -5543,37 +5523,38 @@ rxi_SendAck(struct rx_call *call,
     if ((call->flags & RX_CALL_ACKALL_SENT) &&
         !queue_IsEmpty(&call->rq)) {
         ap->firstPacket = htonl(queue_Last(&call->rq, rx_packet)->header.seq + 1);
-    } else
+    } else {
         ap->firstPacket = htonl(call->rnext);
 
-    ap->previousPacket = htonl(call->rprev);   /* Previous packet received */
+       ap->previousPacket = htonl(call->rprev);        /* Previous packet received */
 
-    /* No fear of running out of ack packet here because there can only be at most
-     * one window full of unacknowledged packets.  The window size must be constrained
-     * to be less than the maximum ack size, of course.  Also, an ack should always
-     * fit into a single packet -- it should not ever be fragmented.  */
-    for (offset = 0, queue_Scan(&call->rq, rqp, nxp, rx_packet)) {
-       if (!rqp || !call->rq.next
-           || (rqp->header.seq > (call->rnext + call->rwind))) {
+       /* No fear of running out of ack packet here because there can only be at most
+        * one window full of unacknowledged packets.  The window size must be constrained
+        * to be less than the maximum ack size, of course.  Also, an ack should always
+        * fit into a single packet -- it should not ever be fragmented.  */
+       for (offset = 0, queue_Scan(&call->rq, rqp, nxp, rx_packet)) {
+           if (!rqp || !call->rq.next
+               || (rqp->header.seq > (call->rnext + call->rwind))) {
 #ifndef RX_ENABLE_TSFPQ
-           if (!optionalPacket)
-               rxi_FreePacket(p);
+               if (!optionalPacket)
+                   rxi_FreePacket(p);
 #endif
-           rxi_CallError(call, RX_CALL_DEAD);
-           return optionalPacket;
-       }
+               rxi_CallError(call, RX_CALL_DEAD);
+               return optionalPacket;
+           }
 
-       while (rqp->header.seq > call->rnext + offset)
-           ap->acks[offset++] = RX_ACK_TYPE_NACK;
-       ap->acks[offset++] = RX_ACK_TYPE_ACK;
+           while (rqp->header.seq > call->rnext + offset)
+               ap->acks[offset++] = RX_ACK_TYPE_NACK;
+           ap->acks[offset++] = RX_ACK_TYPE_ACK;
 
-       if ((offset > (u_char) rx_maxReceiveWindow) || (offset > call->rwind)) {
+           if ((offset > (u_char) rx_maxReceiveWindow) || (offset > call->rwind)) {
 #ifndef RX_ENABLE_TSFPQ
-           if (!optionalPacket)
-               rxi_FreePacket(p);
+               if (!optionalPacket)
+                   rxi_FreePacket(p);
 #endif
-           rxi_CallError(call, RX_CALL_DEAD);
-           return optionalPacket;
+               rxi_CallError(call, RX_CALL_DEAD);
+               return optionalPacket;
+           }
        }
     }
 
@@ -5608,9 +5589,6 @@ rxi_SendAck(struct rx_call *call,
     p->header.flags = RX_SLOW_START_OK;
     if (reason == RX_ACK_PING) {
        p->header.flags |= RX_REQUEST_ACK;
-#ifdef ADAPT_WINDOW
-       clock_GetTime(&call->pingRequestTime);
-#endif
        if (padbytes) {
            p->length = padbytes +
                rx_AckDataSize(call->rwind) + 4 * sizeof(afs_int32);
@@ -5764,7 +5742,7 @@ rxi_SendList(struct rx_call *call, struct xmitlist *xmit,
 
     /* Since we're about to send a data packet to the peer, it's
      * safe to nuke any scheduled end-of-packets ack */
-    rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
+    rxevent_Cancel(&call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
 
     MUTEX_EXIT(&call->lock);
     MUTEX_ENTER(&rx_refcnt_mutex);
@@ -5927,6 +5905,7 @@ rxi_Resend(struct rxevent *event, void *arg0, void *arg1, int istack)
         MUTEX_ENTER(&rx_refcnt_mutex);
        CALL_RELE(call, RX_CALL_REFCOUNT_RESEND);
         MUTEX_EXIT(&rx_refcnt_mutex);
+       rxevent_Put(call->resendEvent);
        call->resendEvent = NULL;
     }
 
@@ -6165,7 +6144,7 @@ rxi_Send(struct rx_call *call, struct rx_packet *p,
 
     /* Since we're about to send SOME sort of packet to the peer, it's
      * safe to nuke any scheduled end-of-packets ack */
-    rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
+    rxevent_Cancel(&call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
 
     /* Actually send the packet, filling in more connection-specific fields */
     MUTEX_EXIT(&call->lock);
@@ -6237,7 +6216,7 @@ rxi_CheckCall(struct rx_call *call)
     if (now > (call->lastReceiveTime + deadTime)) {
        if (call->state == RX_STATE_ACTIVE) {
 #ifdef ADAPT_PMTU
-#if defined(KERNEL) && defined(AFS_SUN57_ENV)
+#if defined(KERNEL) && defined(AFS_SUN5_ENV)
            ire_t *ire;
 #if defined(AFS_SUN510_ENV) && defined(GLOBAL_NETSTACKID)
            netstack_t *ns =  netstack_find_by_stackid(GLOBAL_NETSTACKID);
@@ -6268,14 +6247,13 @@ rxi_CheckCall(struct rx_call *call)
        } else {
 #ifdef RX_ENABLE_LOCKS
            /* Cancel pending events */
-           rxevent_Cancel(call->delayedAckEvent, call,
+           rxevent_Cancel(&call->delayedAckEvent, call,
                           RX_CALL_REFCOUNT_DELAY);
            rxi_rto_cancel(call);
-           rxevent_Cancel(call->keepAliveEvent, call,
+           rxevent_Cancel(&call->keepAliveEvent, call,
+                          RX_CALL_REFCOUNT_ALIVE);
+           rxevent_Cancel(&call->growMTUEvent, call,
                           RX_CALL_REFCOUNT_ALIVE);
-           if (call->growMTUEvent)
-               rxevent_Cancel(call->growMTUEvent, call,
-                              RX_CALL_REFCOUNT_ALIVE);
             MUTEX_ENTER(&rx_refcnt_mutex);
            if (call->refCount == 0) {
                rxi_FreeCall(call, haveCTLock);
@@ -6359,7 +6337,8 @@ mtuout:
 }
 
 void
-rxi_NatKeepAliveEvent(struct rxevent *event, void *arg1, void *dummy)
+rxi_NatKeepAliveEvent(struct rxevent *event, void *arg1,
+                     void *dummy, int dummy2)
 {
     struct rx_connection *conn = arg1;
     struct rx_header theader;
@@ -6401,6 +6380,7 @@ rxi_NatKeepAliveEvent(struct rxevent *event, void *arg1, void *dummy)
     MUTEX_ENTER(&rx_refcnt_mutex);
     /* Only reschedule ourselves if the connection would not be destroyed */
     if (conn->refCount <= 1) {
+       rxevent_Put(conn->natKeepAliveEvent);
        conn->natKeepAliveEvent = NULL;
         MUTEX_EXIT(&rx_refcnt_mutex);
        MUTEX_EXIT(&conn->conn_data_lock);
@@ -6408,6 +6388,7 @@ rxi_NatKeepAliveEvent(struct rxevent *event, void *arg1, void *dummy)
     } else {
        conn->refCount--; /* drop the reference for this */
         MUTEX_EXIT(&rx_refcnt_mutex);
+       rxevent_Put(conn->natKeepAliveEvent);
        conn->natKeepAliveEvent = NULL;
        rxi_ScheduleNatKeepAliveEvent(conn);
        MUTEX_EXIT(&conn->conn_data_lock);
@@ -6426,7 +6407,7 @@ rxi_ScheduleNatKeepAliveEvent(struct rx_connection *conn)
        conn->refCount++; /* hold a reference for this */
         MUTEX_EXIT(&rx_refcnt_mutex);
        conn->natKeepAliveEvent =
-           rxevent_PostNow(&when, &now, rxi_NatKeepAliveEvent, conn, 0);
+           rxevent_Post(&when, &now, rxi_NatKeepAliveEvent, conn, NULL, 0);
     }
 }
 
@@ -6435,8 +6416,12 @@ rx_SetConnSecondsUntilNatPing(struct rx_connection *conn, afs_int32 seconds)
 {
     MUTEX_ENTER(&conn->conn_data_lock);
     conn->secondsUntilNatPing = seconds;
-    if (seconds != 0)
-       rxi_ScheduleNatKeepAliveEvent(conn);
+    if (seconds != 0) {
+       if (!(conn->flags & RX_CONN_ATTACHWAIT))
+           rxi_ScheduleNatKeepAliveEvent(conn);
+       else
+           conn->flags |= RX_CONN_NAT_PING;
+    }
     MUTEX_EXIT(&conn->conn_data_lock);
 }
 
@@ -6444,7 +6429,11 @@ void
 rxi_NatKeepAliveOn(struct rx_connection *conn)
 {
     MUTEX_ENTER(&conn->conn_data_lock);
-    rxi_ScheduleNatKeepAliveEvent(conn);
+    /* if it's already attached */
+    if (!(conn->flags & RX_CONN_ATTACHWAIT))
+       rxi_ScheduleNatKeepAliveEvent(conn);
+    else
+       conn->flags |= RX_CONN_NAT_PING;
     MUTEX_EXIT(&conn->conn_data_lock);
 }
 
@@ -6455,7 +6444,8 @@ rxi_NatKeepAliveOn(struct rx_connection *conn)
  * keep-alive packet (if we're actually trying to keep the call alive)
  */
 void
-rxi_KeepAliveEvent(struct rxevent *event, void *arg1, void *dummy)
+rxi_KeepAliveEvent(struct rxevent *event, void *arg1, void *dummy,
+                  int dummy2)
 {
     struct rx_call *call = arg1;
     struct rx_connection *conn;
@@ -6465,8 +6455,12 @@ rxi_KeepAliveEvent(struct rxevent *event, void *arg1, void *dummy)
     CALL_RELE(call, RX_CALL_REFCOUNT_ALIVE);
     MUTEX_EXIT(&rx_refcnt_mutex);
     MUTEX_ENTER(&call->lock);
-    if (event == call->keepAliveEvent)
+
+    if (event == call->keepAliveEvent) {
+       rxevent_Put(call->keepAliveEvent);
        call->keepAliveEvent = NULL;
+    }
+
     now = clock_Sec();
 
 #ifdef RX_ENABLE_LOCKS
@@ -6498,7 +6492,7 @@ rxi_KeepAliveEvent(struct rxevent *event, void *arg1, void *dummy)
 
 /* Does what's on the nameplate. */
 void
-rxi_GrowMTUEvent(struct rxevent *event, void *arg1, void *dummy)
+rxi_GrowMTUEvent(struct rxevent *event, void *arg1, void *dummy, int dummy2)
 {
     struct rx_call *call = arg1;
     struct rx_connection *conn;
@@ -6508,8 +6502,10 @@ rxi_GrowMTUEvent(struct rxevent *event, void *arg1, void *dummy)
     MUTEX_EXIT(&rx_refcnt_mutex);
     MUTEX_ENTER(&call->lock);
 
-    if (event == call->growMTUEvent)
+    if (event == call->growMTUEvent) {
+       rxevent_Put(call->growMTUEvent);
        call->growMTUEvent = NULL;
+    }
 
 #ifdef RX_ENABLE_LOCKS
     if (rxi_CheckCall(call, 0)) {
@@ -6553,7 +6549,7 @@ rxi_ScheduleKeepAliveEvent(struct rx_call *call)
        CALL_HOLD(call, RX_CALL_REFCOUNT_ALIVE);
         MUTEX_EXIT(&rx_refcnt_mutex);
        call->keepAliveEvent =
-           rxevent_PostNow(&when, &now, rxi_KeepAliveEvent, call, 0);
+           rxevent_Post(&when, &now, rxi_KeepAliveEvent, call, NULL, 0);
     }
 }
 
@@ -6578,7 +6574,7 @@ rxi_ScheduleGrowMTUEvent(struct rx_call *call, int secs)
        CALL_HOLD(call, RX_CALL_REFCOUNT_ALIVE);
         MUTEX_EXIT(&rx_refcnt_mutex);
        call->growMTUEvent =
-           rxevent_PostNow(&when, &now, rxi_GrowMTUEvent, call, 0);
+           rxevent_Post(&when, &now, rxi_GrowMTUEvent, call, NULL, 0);
     }
 }
 
@@ -6608,8 +6604,8 @@ rxi_GrowMTUOn(struct rx_call *call)
 /* This routine is called to send connection abort messages
  * that have been delayed to throttle looping clients. */
 void
-rxi_SendDelayedConnAbort(struct rxevent *event,
-                        void *arg1, void *unused)
+rxi_SendDelayedConnAbort(struct rxevent *event, void *arg1, void *unused,
+                        int unused2)
 {
     struct rx_connection *conn = arg1;
 
@@ -6617,6 +6613,7 @@ rxi_SendDelayedConnAbort(struct rxevent *event,
     struct rx_packet *packet;
 
     MUTEX_ENTER(&conn->conn_data_lock);
+    rxevent_Put(conn->delayedAbortEvent);
     conn->delayedAbortEvent = NULL;
     error = htonl(conn->error);
     conn->abortCount++;
@@ -6633,9 +6630,9 @@ rxi_SendDelayedConnAbort(struct rxevent *event,
 
 /* This routine is called to send call abort messages
  * that have been delayed to throttle looping clients. */
-void
-rxi_SendDelayedCallAbort(struct rxevent *event,
-                        void *arg1, void *dummy)
+static void
+rxi_SendDelayedCallAbort(struct rxevent *event, void *arg1, void *dummy,
+                        int dummy2)
 {
     struct rx_call *call = arg1;
 
@@ -6643,6 +6640,7 @@ rxi_SendDelayedCallAbort(struct rxevent *event,
     struct rx_packet *packet;
 
     MUTEX_ENTER(&call->lock);
+    rxevent_Put(call->delayedAbortEvent);
     call->delayedAbortEvent = NULL;
     error = htonl(call->error);
     call->abortCount++;
@@ -6669,7 +6667,11 @@ rxi_ChallengeEvent(struct rxevent *event,
 {
     struct rx_connection *conn = arg0;
 
-    conn->challengeEvent = NULL;
+    if (event) {
+       rxevent_Put(conn->challengeEvent);
+       conn->challengeEvent = NULL;
+    }
+
     if (RXS_CheckAuthentication(conn->securityObject, conn) != 0) {
        struct rx_packet *packet;
        struct clock when, now;
@@ -6709,7 +6711,7 @@ rxi_ChallengeEvent(struct rxevent *event,
        when = now;
        when.sec += RX_CHALLENGE_TIMEOUT;
        conn->challengeEvent =
-           rxevent_PostNow2(&when, &now, rxi_ChallengeEvent, conn, 0,
+           rxevent_Post(&when, &now, rxi_ChallengeEvent, conn, 0,
                         (tries - 1));
     }
 }
@@ -6889,7 +6891,8 @@ rxi_ComputeRoundTripTime(struct rx_packet *p,
 /* Find all server connections that have not been active for a long time, and
  * toss them */
 void
-rxi_ReapConnections(struct rxevent *unused, void *unused1, void *unused2)
+rxi_ReapConnections(struct rxevent *unused, void *unused1, void *unused2,
+                   int unused3)
 {
     struct clock now, when;
     clock_GetTime(&now);
@@ -7096,7 +7099,7 @@ rxi_ReapConnections(struct rxevent *unused, void *unused1, void *unused2)
 
     when = now;
     when.sec += RX_REAP_TIME;  /* Check every RX_REAP_TIME seconds */
-    rxevent_Post(&when, rxi_ReapConnections, 0, 0);
+    rxevent_Put(rxevent_Post(&when, &now, rxi_ReapConnections, 0, NULL, 0));
 }
 
 
@@ -7115,160 +7118,6 @@ rxs_Release(struct rx_securityClass *aobj)
     return RXS_Close(aobj);
 }
 
-#ifdef ADAPT_WINDOW
-#define        RXRATE_PKT_OH   (RX_HEADER_SIZE + RX_IPUDP_SIZE)
-#define        RXRATE_SMALL_PKT    (RXRATE_PKT_OH + sizeof(struct rx_ackPacket))
-#define        RXRATE_AVG_SMALL_PKT    (RXRATE_PKT_OH + (sizeof(struct rx_ackPacket)/2))
-#define        RXRATE_LARGE_PKT    (RXRATE_SMALL_PKT + 256)
-
-/* Adjust our estimate of the transmission rate to this peer, given
- * that the packet p was just acked. We can adjust peer->timeout and
- * call->twind. Pragmatically, this is called
- * only with packets of maximal length.
- * Called with peer and call locked.
- */
-
-static void
-rxi_ComputeRate(struct rx_peer *peer, struct rx_call *call,
-               struct rx_packet *p, struct rx_packet *ackp, u_char ackReason)
-{
-    afs_int32 xferSize, xferMs;
-    afs_int32 minTime;
-    struct clock newTO;
-
-    /* Count down packets */
-    if (peer->rateFlag > 0)
-       peer->rateFlag--;
-    /* Do nothing until we're enabled */
-    if (peer->rateFlag != 0)
-       return;
-    if (!call->conn)
-       return;
-
-    /* Count only when the ack seems legitimate */
-    switch (ackReason) {
-    case RX_ACK_REQUESTED:
-       xferSize =
-           p->length + RX_HEADER_SIZE + call->conn->securityMaxTrailerSize;
-       xferMs = call->rtt;
-       break;
-
-    case RX_ACK_PING_RESPONSE:
-       if (p)                  /* want the response to ping-request, not data send */
-           return;
-       clock_GetTime(&newTO);
-       if (clock_Gt(&newTO, &call->pingRequestTime)) {
-           clock_Sub(&newTO, &call->pingRequestTime);
-           xferMs = (newTO.sec * 1000) + (newTO.usec / 1000);
-       } else {
-           return;
-       }
-       xferSize = rx_AckDataSize(rx_maxSendWindow) + RX_HEADER_SIZE;
-       break;
-
-    default:
-       return;
-    }
-
-    dpf(("CONG peer %lx/%u: sample (%s) size %ld, %ld ms (to %d.%06d, rtt %u, ps %u)\n",
-          ntohl(peer->host), ntohs(peer->port), (ackReason == RX_ACK_REQUESTED ? "dataack" : "pingack"),
-          xferSize, xferMs, peer->timeout.sec, peer->timeout.usec, peer->smRtt, peer->ifMTU));
-
-    /* Track only packets that are big enough. */
-    if ((p->length + RX_HEADER_SIZE + call->conn->securityMaxTrailerSize) <
-       peer->ifMTU)
-       return;
-
-    /* absorb RTT data (in milliseconds) for these big packets */
-    if (peer->smRtt == 0) {
-       peer->smRtt = xferMs;
-    } else {
-       peer->smRtt = ((peer->smRtt * 15) + xferMs + 4) >> 4;
-       if (!peer->smRtt)
-           peer->smRtt = 1;
-    }
-
-    if (peer->countDown) {
-       peer->countDown--;
-       return;
-    }
-    peer->countDown = 10;      /* recalculate only every so often */
-
-    /* In practice, we can measure only the RTT for full packets,
-     * because of the way Rx acks the data that it receives.  (If it's
-     * smaller than a full packet, it often gets implicitly acked
-     * either by the call response (from a server) or by the next call
-     * (from a client), and either case confuses transmission times
-     * with processing times.)  Therefore, replace the above
-     * more-sophisticated processing with a simpler version, where the
-     * smoothed RTT is kept for full-size packets, and the time to
-     * transmit a windowful of full-size packets is simply RTT *
-     * windowSize. Again, we take two steps:
-     - ensure the timeout is large enough for a single packet's RTT;
-     - ensure that the window is small enough to fit in the desired timeout.*/
-
-    /* First, the timeout check. */
-    minTime = peer->smRtt;
-    /* Get a reasonable estimate for a timeout period */
-    minTime += minTime;
-    newTO.sec = minTime / 1000;
-    newTO.usec = (minTime - (newTO.sec * 1000)) * 1000;
-
-    /* Increase the timeout period so that we can always do at least
-     * one packet exchange */
-    if (clock_Gt(&newTO, &peer->timeout)) {
-
-       dpf(("CONG peer %lx/%u: timeout %d.%06d ==> %ld.%06d (rtt %u)\n",
-              ntohl(peer->host), ntohs(peer->port), peer->timeout.sec, peer->timeout.usec,
-              newTO.sec, newTO.usec, peer->smRtt));
-
-       peer->timeout = newTO;
-    }
-
-    /* Now, get an estimate for the transmit window size. */
-    minTime = peer->timeout.sec * 1000 + (peer->timeout.usec / 1000);
-    /* Now, convert to the number of full packets that could fit in a
-     * reasonable fraction of that interval */
-    minTime /= (peer->smRtt << 1);
-    minTime = MAX(minTime, rx_minPeerTimeout);
-    xferSize = minTime;                /* (make a copy) */
-
-    /* Now clamp the size to reasonable bounds. */
-    if (minTime <= 1)
-       minTime = 1;
-    else if (minTime > rx_maxSendWindow)
-       minTime = rx_maxSendWindow;
-/*    if (minTime != peer->maxWindow) {
-      dpf(("CONG peer %lx/%u: windowsize %lu ==> %lu (to %lu.%06lu, rtt %u)\n",
-            ntohl(peer->host), ntohs(peer->port), peer->maxWindow, minTime,
-            peer->timeout.sec, peer->timeout.usec, peer->smRtt));
-      peer->maxWindow = minTime;
-       elide... call->twind = minTime;
-    }
-*/
-
-    /* Cut back on the peer timeout if it had earlier grown unreasonably.
-     * Discern this by calculating the timeout necessary for rx_Window
-     * packets. */
-    if ((xferSize > rx_maxSendWindow) && (peer->timeout.sec >= 3)) {
-       /* calculate estimate for transmission interval in milliseconds */
-       minTime = rx_maxSendWindow * peer->smRtt;
-       if (minTime < 1000) {
-           dpf(("CONG peer %lx/%u: cut TO %d.%06d by 0.5 (rtt %u)\n",
-                ntohl(peer->host), ntohs(peer->port), peer->timeout.sec,
-                peer->timeout.usec, peer->smRtt));
-
-           newTO.sec = 0;      /* cut back on timeout by half a second */
-           newTO.usec = 500000;
-           clock_Sub(&peer->timeout, &newTO);
-       }
-    }
-
-    return;
-}                              /* end of rxi_ComputeRate */
-#endif /* ADAPT_WINDOW */
-
-
 void
 rxi_DebugInit(void)
 {
@@ -7841,7 +7690,6 @@ rx_GetServerPeers(osi_socket socket, afs_uint32 remoteAddr,
        peer->reSends = ntohl(peer->reSends);
        peer->inPacketSkew = ntohl(peer->inPacketSkew);
        peer->outPacketSkew = ntohl(peer->outPacketSkew);
-       peer->rateFlag = ntohl(peer->rateFlag);
        peer->natMTU = ntohs(peer->natMTU);
        peer->maxMTU = ntohs(peer->maxMTU);
        peer->maxDgramPackets = ntohs(peer->maxDgramPackets);
@@ -7900,7 +7748,6 @@ rx_GetLocalPeers(afs_uint32 peerHost, afs_uint16 peerPort,
                peerStats->reSends = tp->reSends;
                peerStats->inPacketSkew = tp->inPacketSkew;
                peerStats->outPacketSkew = tp->outPacketSkew;
-               peerStats->rateFlag = tp->rateFlag;
                peerStats->natMTU = tp->natMTU;
                peerStats->maxMTU = tp->maxMTU;
                peerStats->maxDgramPackets = tp->maxDgramPackets;