Rx: protect rx_conn and rx_call refCount field with rx_refcnt_mutex
[openafs.git] / src / rx / rx.c
index 0761018..a0363b0 100644 (file)
@@ -180,6 +180,7 @@ afs_kmutex_t rx_atomic_mutex;
 extern afs_kmutex_t rx_quota_mutex;
 extern afs_kmutex_t rx_pthread_mutex;
 extern afs_kmutex_t rx_packets_mutex;
+extern afs_kmutex_t rx_refcnt_mutex;
 extern afs_kmutex_t des_init_mutex;
 extern afs_kmutex_t des_random_mutex;
 extern afs_kmutex_t rx_clock_mutex;
@@ -210,6 +211,7 @@ rxi_InitPthread(void)
     MUTEX_INIT(&rx_quota_mutex, "quota", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_pthread_mutex, "pthread", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_packets_mutex, "packets", MUTEX_DEFAULT, 0);
+    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);
@@ -356,7 +358,9 @@ struct rx_connection *rxLastConn = 0;
  * lowest level:
  *     multi_handle->lock
  *     rxevent_lock
+ *      rx_packets_mutex
  *     rx_stats_mutex
+ *      rx_refcnt_mutex
  *     rx_atomic_mutex
  *
  * Do we need a lock to protect the peer field in the conn structure?
@@ -485,6 +489,7 @@ rx_InitHost(u_int host, u_int port)
     MUTEX_INIT(&rx_quota_mutex, "rx_quota_mutex", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_pthread_mutex, "rx_pthread_mutex", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_packets_mutex, "rx_packets_mutex", MUTEX_DEFAULT, 0);
+    MUTEX_INIT(&rx_refcnt_mutex, "rx_refcnt_mutex", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_rpc_stats, "rx_rpc_stats", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_freePktQ_lock, "rx_freePktQ_lock", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&freeSQEList_lock, "freeSQEList lock", MUTEX_DEFAULT, 0);
@@ -980,6 +985,7 @@ rxi_DestroyConnectionNoLock(struct rx_connection *conn)
 
     NETPRI;
     MUTEX_ENTER(&conn->conn_data_lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
     if (conn->refCount > 0)
        conn->refCount--;
     else {
@@ -992,6 +998,7 @@ rxi_DestroyConnectionNoLock(struct rx_connection *conn)
 
     if ((conn->refCount > 0) || (conn->flags & RX_CONN_BUSY)) {
        /* Busy; wait till the last guy before proceeding */
+        MUTEX_EXIT(&rx_refcnt_mutex);
        MUTEX_EXIT(&conn->conn_data_lock);
        USERPRI;
        return;
@@ -1007,6 +1014,7 @@ rxi_DestroyConnectionNoLock(struct rx_connection *conn)
        USERPRI;
        return;
     }
+    MUTEX_EXIT(&rx_refcnt_mutex);
     MUTEX_EXIT(&conn->conn_data_lock);
 
     /* Check for extant references to this connection */
@@ -1119,9 +1127,9 @@ rx_GetConnection(struct rx_connection *conn)
     SPLVAR;
 
     NETPRI;
-    MUTEX_ENTER(&conn->conn_data_lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
     conn->refCount++;
-    MUTEX_EXIT(&conn->conn_data_lock);
+    MUTEX_EXIT(&rx_refcnt_mutex);
     USERPRI;
 }
 
@@ -1227,8 +1235,10 @@ rx_NewCall(struct rx_connection *conn)
                          * effect on overall system performance.
                          */
                         call->state = RX_STATE_RESET;
-                        CALL_HOLD(call, RX_CALL_REFCOUNT_BEGIN);
                         MUTEX_EXIT(&conn->conn_call_lock);
+                        MUTEX_ENTER(&rx_refcnt_mutex);
+                        CALL_HOLD(call, RX_CALL_REFCOUNT_BEGIN);
+                        MUTEX_EXIT(&rx_refcnt_mutex);
                         rxi_ResetCall(call, 0);
                         (*call->callNumber)++;
                         if (MUTEX_TRYENTER(&conn->conn_call_lock))
@@ -1260,7 +1270,9 @@ rx_NewCall(struct rx_connection *conn)
                          * Instead, cycle through one more time to see if
                          * we can find a call that can call our own.
                          */
+                        MUTEX_ENTER(&rx_refcnt_mutex);
                         CALL_RELE(call, RX_CALL_REFCOUNT_BEGIN);
+                        MUTEX_EXIT(&rx_refcnt_mutex);
                         wait = 0;
                     }
                     MUTEX_EXIT(&call->lock);
@@ -1268,7 +1280,9 @@ rx_NewCall(struct rx_connection *conn)
            } else {
                 /* rxi_NewCall returns with mutex locked */
                call = rxi_NewCall(conn, i);
+                MUTEX_ENTER(&rx_refcnt_mutex);
                 CALL_HOLD(call, RX_CALL_REFCOUNT_BEGIN);
+                MUTEX_EXIT(&rx_refcnt_mutex);
                break;
            }
        }
@@ -1835,8 +1849,10 @@ rx_GetCall(int tno, struct rx_service *cur_service, osi_socket * socketp)
             call->conn->service->servicePort, call->conn->service->serviceId,
             call));
 
-       CALL_HOLD(call, RX_CALL_REFCOUNT_BEGIN);
        MUTEX_EXIT(&call->lock);
+        MUTEX_ENTER(&rx_refcnt_mutex);
+       CALL_HOLD(call, RX_CALL_REFCOUNT_BEGIN);
+        MUTEX_EXIT(&rx_refcnt_mutex);
     } else {
        dpf(("rx_GetCall(socketp=%p, *socketp=0x%x)\n", socketp, *socketp));
     }
@@ -2144,9 +2160,11 @@ rx_EndCall(struct rx_call *call, afs_int32 rc)
     call->iovqc -=
 #endif /* RXDEBUG_PACKET */
         rxi_FreePackets(0, &call->iovq);
+    MUTEX_EXIT(&call->lock);
 
+    MUTEX_ENTER(&rx_refcnt_mutex);
     CALL_RELE(call, RX_CALL_REFCOUNT_BEGIN);
-    MUTEX_EXIT(&call->lock);
+    MUTEX_EXIT(&rx_refcnt_mutex);
     if (conn->type == RX_CLIENT_CONNECTION) {
        MUTEX_ENTER(&conn->conn_data_lock);
        conn->flags &= ~RX_CONN_BUSY;
@@ -2190,9 +2208,9 @@ rx_Finalize(void)
            for (conn = *conn_ptr; conn; conn = next) {
                next = conn->next;
                if (conn->type == RX_CLIENT_CONNECTION) {
-                   /* MUTEX_ENTER(&conn->conn_data_lock); when used in kernel */
+                    MUTEX_ENTER(&rx_refcnt_mutex);
                    conn->refCount++;
-                   /* MUTEX_EXIT(&conn->conn_data_lock); when used in kernel */
+                    MUTEX_EXIT(&rx_refcnt_mutex);
 #ifdef RX_ENABLE_LOCKS
                    rxi_DestroyConnectionNoLock(conn);
 #else /* RX_ENABLE_LOCKS */
@@ -2371,16 +2389,12 @@ rxi_NewCall(struct rx_connection *conn, int channel)
 /* A call has been inactive long enough that so we can throw away
  * state, including the call structure, which is placed on the call
  * free list.
- * Call is locked upon entry.
- * haveCTLock set if called from rxi_ReapConnections
+ *
+ * call->lock amd rx_refcnt_mutex are held upon entry.
+ * haveCTLock is set when called from rxi_ReapConnections.
  */
-#ifdef RX_ENABLE_LOCKS
 void
 rxi_FreeCall(struct rx_call *call, int haveCTLock)
-#else /* RX_ENABLE_LOCKS */
-void
-rxi_FreeCall(struct rx_call *call)
-#endif                         /* RX_ENABLE_LOCKS */
 {
     int channel = call->channel;
     struct rx_connection *conn = call->conn;
@@ -2390,6 +2404,7 @@ rxi_FreeCall(struct rx_call *call)
        (*call->callNumber)++;
     rxi_ResetCall(call, 0);
     call->conn->call[channel] = (struct rx_call *)0;
+    MUTEX_EXIT(&rx_refcnt_mutex);
 
     MUTEX_ENTER(&rx_freeCallQueue_lock);
     SET_CALL_QUEUE_LOCK(call, &rx_freeCallQueue_lock);
@@ -2423,7 +2438,9 @@ rxi_FreeCall(struct rx_call *call)
      */
     MUTEX_ENTER(&conn->conn_data_lock);
     if (conn->flags & RX_CONN_DESTROY_ME && !(conn->flags & RX_CONN_MAKECALL_WAITING)) {
+        MUTEX_ENTER(&rx_refcnt_mutex);
        conn->refCount++;
+        MUTEX_EXIT(&rx_refcnt_mutex);
        MUTEX_EXIT(&conn->conn_data_lock);
 #ifdef RX_ENABLE_LOCKS
        if (haveCTLock)
@@ -2436,6 +2453,7 @@ rxi_FreeCall(struct rx_call *call)
     } else {
        MUTEX_EXIT(&conn->conn_data_lock);
     }
+    MUTEX_ENTER(&rx_refcnt_mutex);
 }
 
 rx_atomic_t rxi_Allocsize = RX_ATOMIC_INIT(0);
@@ -2681,9 +2699,9 @@ rxi_FindConnection(osi_socket socket, afs_uint32 host,
             rx_atomic_inc(&rx_stats.nServerConns);
     }
 
-    MUTEX_ENTER(&conn->conn_data_lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
     conn->refCount++;
-    MUTEX_EXIT(&conn->conn_data_lock);
+    MUTEX_EXIT(&rx_refcnt_mutex);
 
     rxLastConn = conn;         /* store this connection as the last conn used */
     MUTEX_EXIT(&rx_connHashTable_lock);
@@ -2794,7 +2812,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
        MUTEX_ENTER(&conn->conn_data_lock);
        if (np->header.type != RX_PACKET_TYPE_ABORT)
            np = rxi_SendConnectionAbort(conn, np, 1, 0);
+        MUTEX_ENTER(&rx_refcnt_mutex);
        conn->refCount--;
+        MUTEX_EXIT(&rx_refcnt_mutex);
        MUTEX_EXIT(&conn->conn_data_lock);
        return np;
     }
@@ -2807,30 +2827,30 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
            afs_int32 errcode = ntohl(rx_GetInt32(np, 0));
            dpf(("rxi_ReceivePacket ABORT rx_GetInt32 = %d", errcode));
            rxi_ConnectionError(conn, errcode);
-           MUTEX_ENTER(&conn->conn_data_lock);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
-           MUTEX_EXIT(&conn->conn_data_lock);
+            MUTEX_EXIT(&rx_refcnt_mutex);
            return np;
        }
        case RX_PACKET_TYPE_CHALLENGE:
            tnp = rxi_ReceiveChallengePacket(conn, np, 1);
-           MUTEX_ENTER(&conn->conn_data_lock);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
-           MUTEX_EXIT(&conn->conn_data_lock);
+            MUTEX_EXIT(&rx_refcnt_mutex);
            return tnp;
        case RX_PACKET_TYPE_RESPONSE:
            tnp = rxi_ReceiveResponsePacket(conn, np, 1);
-           MUTEX_ENTER(&conn->conn_data_lock);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
-           MUTEX_EXIT(&conn->conn_data_lock);
+            MUTEX_EXIT(&rx_refcnt_mutex);
            return tnp;
        case RX_PACKET_TYPE_PARAMS:
        case RX_PACKET_TYPE_PARAMS + 1:
        case RX_PACKET_TYPE_PARAMS + 2:
            /* ignore these packet types for now */
-           MUTEX_ENTER(&conn->conn_data_lock);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
-           MUTEX_EXIT(&conn->conn_data_lock);
+            MUTEX_EXIT(&rx_refcnt_mutex);
            return np;
 
 
@@ -2840,7 +2860,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
            rxi_ConnectionError(conn, RX_PROTOCOL_ERROR);
            MUTEX_ENTER(&conn->conn_data_lock);
            tnp = rxi_SendConnectionAbort(conn, np, 1, 0);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
+            MUTEX_EXIT(&rx_refcnt_mutex);
            MUTEX_EXIT(&conn->conn_data_lock);
            return tnp;
        }
@@ -2878,9 +2900,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
             */
             if (rx_stats_active)
                rx_atomic_inc(&rx_stats.spuriousPacketsRead);
-           MUTEX_ENTER(&conn->conn_data_lock);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
-           MUTEX_EXIT(&conn->conn_data_lock);
+            MUTEX_EXIT(&rx_refcnt_mutex);
            return np;
        }
     }
@@ -2895,9 +2917,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
            if (call)
                MUTEX_EXIT(&call->lock);
 #endif
-           MUTEX_ENTER(&conn->conn_data_lock);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
-           MUTEX_EXIT(&conn->conn_data_lock);
+            MUTEX_EXIT(&rx_refcnt_mutex);
            return np;
        }
        if (!call) {
@@ -2927,9 +2949,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
                rxi_CallError(call, rx_BusyError);
                tp = rxi_SendCallAbort(call, np, 1, 0);
                MUTEX_EXIT(&call->lock);
-               MUTEX_ENTER(&conn->conn_data_lock);
+                MUTEX_ENTER(&rx_refcnt_mutex);
                conn->refCount--;
-               MUTEX_EXIT(&conn->conn_data_lock);
+                MUTEX_EXIT(&rx_refcnt_mutex);
                 if (rx_stats_active)
                     rx_atomic_inc(&rx_stats.nBusies);
                return tp;
@@ -2967,9 +2989,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
                tp = rxi_SendSpecial(call, conn, np, RX_PACKET_TYPE_BUSY,
                                     NULL, 0, 1);
                MUTEX_EXIT(&call->lock);
-               MUTEX_ENTER(&conn->conn_data_lock);
+                MUTEX_ENTER(&rx_refcnt_mutex);
                conn->refCount--;
-               MUTEX_EXIT(&conn->conn_data_lock);
+                MUTEX_EXIT(&rx_refcnt_mutex);
                return tp;
            }
            rxi_ResetCall(call, 0);
@@ -2996,9 +3018,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
                rxi_CallError(call, rx_BusyError);
                tp = rxi_SendCallAbort(call, np, 1, 0);
                MUTEX_EXIT(&call->lock);
-               MUTEX_ENTER(&conn->conn_data_lock);
+                MUTEX_ENTER(&rx_refcnt_mutex);
                conn->refCount--;
-               MUTEX_EXIT(&conn->conn_data_lock);
+                MUTEX_EXIT(&rx_refcnt_mutex);
                 if (rx_stats_active)
                     rx_atomic_inc(&rx_stats.nBusies);
                return tp;
@@ -3018,9 +3040,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
                MUTEX_EXIT(&call->lock);
            }
 #endif
-           MUTEX_ENTER(&conn->conn_data_lock);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
-           MUTEX_EXIT(&conn->conn_data_lock);
+            MUTEX_EXIT(&rx_refcnt_mutex);
            return np;
        }
 
@@ -3034,9 +3056,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
                MUTEX_EXIT(&call->lock);
            }
 #endif
-           MUTEX_ENTER(&conn->conn_data_lock);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
-           MUTEX_EXIT(&conn->conn_data_lock);
+            MUTEX_EXIT(&rx_refcnt_mutex);
            return np;
        }
        /* If the service security object index stamped in the packet does not
@@ -3045,9 +3067,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
 #ifdef RX_ENABLE_LOCKS
            MUTEX_EXIT(&call->lock);
 #endif
-           MUTEX_ENTER(&conn->conn_data_lock);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
-           MUTEX_EXIT(&conn->conn_data_lock);
+            MUTEX_EXIT(&rx_refcnt_mutex);
            return np;
        }
 
@@ -3068,7 +3090,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
 #ifdef RX_ENABLE_LOCKS
                rxi_SetAcksInTransmitQueue(call);
 #else
+                MUTEX_ENTER(&rx_refcnt_mutex);
                conn->refCount--;
+                MUTEX_EXIT(&rx_refcnt_mutex);
                return np;      /* xmitting; drop packet */
 #endif
            } else {
@@ -3096,9 +3120,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
                     if (rx_stats_active)
                         rx_atomic_inc(&rx_stats.spuriousPacketsRead);
                    MUTEX_EXIT(&call->lock);
-                   MUTEX_ENTER(&conn->conn_data_lock);
+                    MUTEX_ENTER(&rx_refcnt_mutex);
                    conn->refCount--;
-                   MUTEX_EXIT(&conn->conn_data_lock);
+                    MUTEX_EXIT(&rx_refcnt_mutex);
                    return np;
                }
            }
@@ -3160,9 +3184,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
        dpf(("rxi_ReceivePacket ABORT rx_DataOf = %d", errdata));
        rxi_CallError(call, errdata);
        MUTEX_EXIT(&call->lock);
-       MUTEX_ENTER(&conn->conn_data_lock);
+        MUTEX_ENTER(&rx_refcnt_mutex);
        conn->refCount--;
-       MUTEX_EXIT(&conn->conn_data_lock);
+        MUTEX_EXIT(&rx_refcnt_mutex);
        return np;              /* xmitting; drop packet */
     }
     case RX_PACKET_TYPE_BUSY:
@@ -3187,9 +3211,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
            break;
 #else /* RX_ENABLE_LOCKS */
            MUTEX_EXIT(&call->lock);
-           MUTEX_ENTER(&conn->conn_data_lock);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
-           MUTEX_EXIT(&conn->conn_data_lock);
+            MUTEX_EXIT(&rx_refcnt_mutex);
            return np;          /* xmitting; drop packet */
 #endif /* RX_ENABLE_LOCKS */
        }
@@ -3210,9 +3234,9 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
      * (if not, then the time won't actually be re-evaluated here). */
     call->lastReceiveTime = clock_Sec();
     MUTEX_EXIT(&call->lock);
-    MUTEX_ENTER(&conn->conn_data_lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
     conn->refCount--;
-    MUTEX_EXIT(&conn->conn_data_lock);
+    MUTEX_EXIT(&rx_refcnt_mutex);
     return np;
 }
 
@@ -3279,8 +3303,11 @@ rxi_CheckReachEvent(struct rxevent *event, void *arg1, void *arg2)
     MUTEX_ENTER(&conn->conn_data_lock);
     conn->checkReachEvent = NULL;
     waiting = conn->flags & RX_CONN_ATTACHWAIT;
-    if (event)
+    if (event) {
+        MUTEX_ENTER(&rx_refcnt_mutex);
        conn->refCount--;
+        MUTEX_EXIT(&rx_refcnt_mutex);
+    }
     MUTEX_EXIT(&conn->conn_data_lock);
 
     if (waiting) {
@@ -3317,7 +3344,9 @@ rxi_CheckReachEvent(struct rxevent *event, void *arg1, void *arg2)
            when.sec += RX_CHECKREACH_TIMEOUT;
            MUTEX_ENTER(&conn->conn_data_lock);
            if (!conn->checkReachEvent) {
+                MUTEX_ENTER(&rx_refcnt_mutex);
                conn->refCount++;
+                MUTEX_EXIT(&rx_refcnt_mutex);
                conn->checkReachEvent =
                    rxevent_PostNow(&when, &now, rxi_CheckReachEvent, conn,
                                    NULL);
@@ -3424,7 +3453,10 @@ rxi_ReceiveDataPacket(struct rx_call *call,
            || 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);
        }
@@ -3724,7 +3756,9 @@ rxi_ReceiveDataPacket(struct rx_call *call,
            || 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);
        }
@@ -4485,7 +4519,9 @@ rxi_AttachServerProc(struct rx_call *call,
            *tnop = sq->tno;
            *sq->socketp = socket;
            clock_GetTime(&call->startTime);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            CALL_HOLD(call, RX_CALL_REFCOUNT_BEGIN);
+            MUTEX_EXIT(&rx_refcnt_mutex);
        } else {
            sq->newcall = call;
        }
@@ -4543,7 +4579,9 @@ rxi_AckAll(struct rxevent *event, struct rx_call *call, char *dummy)
     if (event) {
        MUTEX_ENTER(&call->lock);
        call->delayedAckEvent = NULL;
+        MUTEX_ENTER(&rx_refcnt_mutex);
        CALL_RELE(call, RX_CALL_REFCOUNT_ACKALL);
+        MUTEX_EXIT(&rx_refcnt_mutex);
     }
     rxi_SendSpecial(call, call->conn, (struct rx_packet *)0,
                    RX_PACKET_TYPE_ACKALL, NULL, 0, 0);
@@ -4566,7 +4604,9 @@ rxi_SendDelayedAck(struct rxevent *event, void *arg1, void *unused)
        MUTEX_ENTER(&call->lock);
        if (event == call->delayedAckEvent)
            call->delayedAckEvent = NULL;
+        MUTEX_ENTER(&rx_refcnt_mutex);
        CALL_RELE(call, RX_CALL_REFCOUNT_DELAY);
+        MUTEX_EXIT(&rx_refcnt_mutex);
     }
     (void)rxi_SendAck(call, 0, 0, RX_ACK_DELAY, 0);
     if (event)
@@ -4718,7 +4758,9 @@ rxi_SendCallAbort(struct rx_call *call, struct rx_packet *packet,
        clock_GetTime(&now);
        when = now;
        clock_Addmsec(&when, rxi_callAbortDelay);
+        MUTEX_ENTER(&rx_refcnt_mutex);
        CALL_HOLD(call, RX_CALL_REFCOUNT_ABORT);
+        MUTEX_EXIT(&rx_refcnt_mutex);
        call->delayedAbortEvent =
            rxevent_PostNow(&when, &now, rxi_SendDelayedCallAbort, call, 0);
     }
@@ -4794,7 +4836,9 @@ rxi_ConnectionError(struct rx_connection *conn,
            rxevent_Cancel(conn->checkReachEvent, (struct rx_call *)0, 0);
            conn->checkReachEvent = 0;
            conn->flags &= ~RX_CONN_ATTACHWAIT;
+            MUTEX_ENTER(&rx_refcnt_mutex);
            conn->refCount--;
+            MUTEX_EXIT(&rx_refcnt_mutex);
        }
        MUTEX_EXIT(&conn->conn_data_lock);
        for (i = 0; i < RX_MAXCALLS; i++) {
@@ -5343,15 +5387,19 @@ rxi_SendList(struct rx_call *call, struct rx_packet **list, int len,
      * safe to nuke any scheduled end-of-packets ack */
     rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
 
-    CALL_HOLD(call, RX_CALL_REFCOUNT_SEND);
     MUTEX_EXIT(&call->lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
+    CALL_HOLD(call, RX_CALL_REFCOUNT_SEND);
+    MUTEX_EXIT(&rx_refcnt_mutex);
     if (len > 1) {
        rxi_SendPacketList(call, conn, list, len, istack);
     } else {
        rxi_SendPacket(call, conn, list[0], istack);
     }
     MUTEX_ENTER(&call->lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
     CALL_RELE(call, RX_CALL_REFCOUNT_SEND);
+    MUTEX_EXIT(&rx_refcnt_mutex);
 
     /* Update last send time for this call (for keep-alive
      * processing), and for the connection (so that we can discover
@@ -5500,7 +5548,9 @@ rxi_Start(struct rxevent *event,
      * structure, since there is no longer a per-call retransmission
      * event pending. */
     if (event && event == call->resendEvent) {
+        MUTEX_ENTER(&rx_refcnt_mutex);
        CALL_RELE(call, RX_CALL_REFCOUNT_RESEND);
+        MUTEX_EXIT(&rx_refcnt_mutex);
        call->resendEvent = NULL;
        resending = 1;
        if (queue_IsEmpty(&call->tq)) {
@@ -5785,7 +5835,9 @@ rxi_Start(struct rxevent *event,
                    /* Post a new event to re-run rxi_Start when retries may be needed */
                    if (haveEvent && !(call->flags & RX_CALL_NEED_START)) {
 #ifdef RX_ENABLE_LOCKS
+                        MUTEX_ENTER(&rx_refcnt_mutex);
                        CALL_HOLD(call, RX_CALL_REFCOUNT_RESEND);
+                        MUTEX_EXIT(&rx_refcnt_mutex);
                        call->resendEvent =
                            rxevent_PostNow2(&retryTime, &usenow,
                                             rxi_StartUnlocked,
@@ -5846,11 +5898,15 @@ rxi_Send(struct rx_call *call, struct rx_packet *p,
     rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY);
 
     /* Actually send the packet, filling in more connection-specific fields */
-    CALL_HOLD(call, RX_CALL_REFCOUNT_SEND);
     MUTEX_EXIT(&call->lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
+    CALL_HOLD(call, RX_CALL_REFCOUNT_SEND);
+    MUTEX_EXIT(&rx_refcnt_mutex);
     rxi_SendPacket(call, conn, p, istack);
-    MUTEX_ENTER(&call->lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
     CALL_RELE(call, RX_CALL_REFCOUNT_SEND);
+    MUTEX_EXIT(&rx_refcnt_mutex);
+    MUTEX_ENTER(&call->lock);
 
     /* Update last send time for this call (for keep-alive
      * processing), and for the connection (so that we can discover
@@ -5946,13 +6002,16 @@ rxi_CheckCall(struct rx_call *call)
            rxevent_Cancel(call->resendEvent, call, RX_CALL_REFCOUNT_RESEND);
            rxevent_Cancel(call->keepAliveEvent, call,
                           RX_CALL_REFCOUNT_ALIVE);
+            MUTEX_ENTER(&rx_refcnt_mutex);
            if (call->refCount == 0) {
                rxi_FreeCall(call, haveCTLock);
+                MUTEX_EXIT(&rx_refcnt_mutex);
                return -2;
            }
+            MUTEX_EXIT(&rx_refcnt_mutex);
            return -1;
 #else /* RX_ENABLE_LOCKS */
-           rxi_FreeCall(call);
+           rxi_FreeCall(call, 0);
            return -2;
 #endif /* RX_ENABLE_LOCKS */
        }
@@ -5985,11 +6044,12 @@ rxi_CheckCall(struct rx_call *call)
     }
     return 0;
 mtuout:
-    if (conn->msgsizeRetryErr && cerror != RX_CALL_TIMEOUT) {
+    if (conn->msgsizeRetryErr && cerror != RX_CALL_TIMEOUT
+       && call->lastReceiveTime) {
        int oldMTU = conn->peer->ifMTU;
 
        /* if we thought we could send more, perhaps things got worse */
-       if (call->conn->peer->maxPacketSize > conn->lastPacketSize)
+       if (conn->peer->maxPacketSize > conn->lastPacketSize)
            /* maxpacketsize will be cleared in rxi_SetPeerMtu */
            newmtu = MAX(conn->peer->maxPacketSize-RX_IPUDP_SIZE,
                         conn->lastPacketSize-(128+RX_IPUDP_SIZE));
@@ -6054,14 +6114,17 @@ rxi_NatKeepAliveEvent(struct rxevent *event, void *arg1, void *dummy)
     osi_NetSend(socket, &taddr, tmpiov, 1, 1 + sizeof(struct rx_header), 1);
 
     MUTEX_ENTER(&conn->conn_data_lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
     /* Only reschedule ourselves if the connection would not be destroyed */
     if (conn->refCount <= 1) {
        conn->natKeepAliveEvent = NULL;
+        MUTEX_EXIT(&rx_refcnt_mutex);
        MUTEX_EXIT(&conn->conn_data_lock);
        rx_DestroyConnection(conn); /* drop the reference for this */
     } else {
-       conn->natKeepAliveEvent = NULL;
        conn->refCount--; /* drop the reference for this */
+        MUTEX_EXIT(&rx_refcnt_mutex);
+       conn->natKeepAliveEvent = NULL;
        rxi_ScheduleNatKeepAliveEvent(conn);
        MUTEX_EXIT(&conn->conn_data_lock);
     }
@@ -6075,7 +6138,9 @@ rxi_ScheduleNatKeepAliveEvent(struct rx_connection *conn)
        clock_GetTime(&now);
        when = now;
        when.sec += conn->secondsUntilNatPing;
+        MUTEX_ENTER(&rx_refcnt_mutex);
        conn->refCount++; /* hold a reference for this */
+        MUTEX_EXIT(&rx_refcnt_mutex);
        conn->natKeepAliveEvent =
            rxevent_PostNow(&when, &now, rxi_NatKeepAliveEvent, conn, 0);
     }
@@ -6112,8 +6177,10 @@ rxi_KeepAliveEvent(struct rxevent *event, void *arg1, void *dummy)
     struct rx_connection *conn;
     afs_uint32 now;
 
-    MUTEX_ENTER(&call->lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
     CALL_RELE(call, RX_CALL_REFCOUNT_ALIVE);
+    MUTEX_EXIT(&rx_refcnt_mutex);
+    MUTEX_ENTER(&call->lock);
     if (event == call->keepAliveEvent)
        call->keepAliveEvent = NULL;
     now = clock_Sec();
@@ -6152,8 +6219,11 @@ rxi_GrowMTUEvent(struct rxevent *event, void *arg1, void *dummy)
     struct rx_call *call = arg1;
     struct rx_connection *conn;
 
-    MUTEX_ENTER(&call->lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
     CALL_RELE(call, RX_CALL_REFCOUNT_ALIVE);
+    MUTEX_EXIT(&rx_refcnt_mutex);
+    MUTEX_ENTER(&call->lock);
+
     if (event == call->growMTUEvent)
        call->growMTUEvent = NULL;
 
@@ -6195,7 +6265,9 @@ rxi_ScheduleKeepAliveEvent(struct rx_call *call)
        clock_GetTime(&now);
        when = now;
        when.sec += call->conn->secondsUntilPing;
+        MUTEX_ENTER(&rx_refcnt_mutex);
        CALL_HOLD(call, RX_CALL_REFCOUNT_ALIVE);
+        MUTEX_EXIT(&rx_refcnt_mutex);
        call->keepAliveEvent =
            rxevent_PostNow(&when, &now, rxi_KeepAliveEvent, call, 0);
     }
@@ -6218,7 +6290,9 @@ rxi_ScheduleGrowMTUEvent(struct rx_call *call, int secs)
        }
 
        when.sec += secs;
+        MUTEX_ENTER(&rx_refcnt_mutex);
        CALL_HOLD(call, RX_CALL_REFCOUNT_ALIVE);
+        MUTEX_EXIT(&rx_refcnt_mutex);
        call->growMTUEvent =
            rxevent_PostNow(&when, &now, rxi_GrowMTUEvent, call, 0);
     }
@@ -6295,8 +6369,10 @@ rxi_SendDelayedCallAbort(struct rxevent *event,
                            (char *)&error, sizeof(error), 0);
        rxi_FreePacket(packet);
     }
-    CALL_RELE(call, RX_CALL_REFCOUNT_ABORT);
     MUTEX_EXIT(&call->lock);
+    MUTEX_ENTER(&rx_refcnt_mutex);
+    CALL_RELE(call, RX_CALL_REFCOUNT_ABORT);
+    MUTEX_EXIT(&rx_refcnt_mutex);
 }
 
 /* This routine is called periodically (every RX_AUTH_REQUEST_TIMEOUT
@@ -6542,10 +6618,12 @@ rxi_ReapConnections(struct rxevent *unused, void *unused1, void *unused2)
                    /* This only actually destroys the connection if
                     * there are no outstanding calls */
                    MUTEX_ENTER(&conn->conn_data_lock);
+                    MUTEX_ENTER(&rx_refcnt_mutex);
                    if (!havecalls && !conn->refCount
                        && ((conn->lastSendTime + rx_idleConnectionTime) <
                            now.sec)) {
                        conn->refCount++;       /* it will be decr in rx_DestroyConn */
+                        MUTEX_EXIT(&rx_refcnt_mutex);
                        MUTEX_EXIT(&conn->conn_data_lock);
 #ifdef RX_ENABLE_LOCKS
                        rxi_DestroyConnectionNoLock(conn);
@@ -6555,6 +6633,7 @@ rxi_ReapConnections(struct rxevent *unused, void *unused1, void *unused2)
                    }
 #ifdef RX_ENABLE_LOCKS
                    else {
+                        MUTEX_EXIT(&rx_refcnt_mutex);
                        MUTEX_EXIT(&conn->conn_data_lock);
                    }
 #endif /* RX_ENABLE_LOCKS */