rx-packet-count-debugging-20081229
authorJeffrey Altman <jaltman@secure-endpoints.com>
Mon, 29 Dec 2008 22:36:32 +0000 (22:36 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Mon, 29 Dec 2008 22:36:32 +0000 (22:36 +0000)
LICENSE MIT

Validating the state of the rx library is hard.  This patch makes it
a bit easier to validate from within crash dumps (or on Windows in
"fs memdump" output) whether rx packets and calls are in a sane state.
When the library is compiled with -DDEBUG the rx_call and rx_packet
data structures are extended to include a linked list of all allocated
objects and counters are added to track the number of packets on each
of the transmit, receive, and iovec queues.

This compliments the tracking of packet states via RX_PKTFLAGs.

For Windows, rx_DumpCalls and rx_DumpPackets functions are added
that will dump the state of all allocated calls and packets (in Checked
build) in response to a "fs memdump" command.  These functions are
exported from afsrpc.dll.

src/WINNT/afsd/afsd_service.c
src/WINNT/afsd/cm_ioctl.c
src/libafsrpc/afsrpc.def
src/rx/rx.c
src/rx/rx.h
src/rx/rx_packet.c
src/rx/rx_packet.h
src/rx/rx_rdwr.c

index 290eee3..dc49549 100644 (file)
@@ -91,6 +91,8 @@ static void afsd_notifier(char *msgp, char *filep, long line)
     cm_DumpSCache(afsi_file, "a", 0);
     cm_DumpBufHashTable(afsi_file, "a", 0);
     smb_DumpVCP(afsi_file, "a", 0);                    
+    rx_DumpPackets(afsi_file, "a");
+    rx_DumpCalls(afsi_file, "a");
     afsi_log("--- end   dump ---");
     
 #ifdef DEBUG
index 613dba5..d3ecaf0 100644 (file)
@@ -3017,6 +3017,8 @@ cm_IoctlMemoryDump(struct cm_ioctl *ioctlp, struct cm_user *userp)
     cm_DumpSCache(hLogFile, cookie, 1);
     cm_DumpBufHashTable(hLogFile, cookie, 1);
     smb_DumpVCP(hLogFile, cookie, 1);
+    rx_DumpCalls(hLogFile, cookie);
+    rx_DumpPackets(hLogFile, cookie);
 
     CloseHandle(hLogFile);                          
   
index 6409618..01a3b17 100644 (file)
@@ -247,3 +247,6 @@ EXPORTS
         rx_WritevAlloc                          @2006
         rxi_MorePackets                         @2007
 
+; for debugging
+        rx_DumpCalls                            @9998
+        rx_DumpPackets                          @9999
index eebf1cb..6f42483 100644 (file)
@@ -1157,7 +1157,6 @@ rx_NewCall(register struct rx_connection *conn)
 {
     register int i;
     register struct rx_call *call;
-       register struct rx_connection *tconn;
     struct clock queueTime;
     SPLVAR;
 
@@ -2068,7 +2067,10 @@ rx_EndCall(register struct rx_call *call, afs_int32 rc)
     call->nLeft = call->nFree = call->curlen = 0;
 
     /* Free any packets from the last call to ReadvProc/WritevProc */
-    rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+    call->iovqc -=
+#endif /* DEBUG */
+        rxi_FreePackets(0, &call->iovq);
 
     CALL_RELE(call, RX_CALL_REFCOUNT_BEGIN);
     MUTEX_EXIT(&call->lock);
@@ -2185,6 +2187,14 @@ rxi_FindService(register osi_socket socket, register u_short serviceId)
     return 0;
 }
 
+#ifdef DEBUG
+#ifdef KDUMP_RX_LOCK
+static struct rx_call_rx_lock *rx_allCallsp = 0;
+#else
+static struct rx_call *rx_allCallsp = 0;
+#endif
+#endif /* DEBUG */
+
 /* Allocate a call structure, for the indicated channel of the
  * supplied connection.  The mode and state of the call must be set by
  * the caller. Returns the call with mutex locked. */
@@ -2239,8 +2249,14 @@ rxi_NewCall(register struct rx_connection *conn, register int channel)
     } else {
 
        call = (struct rx_call *)rxi_Alloc(sizeof(struct rx_call));
-        rx_MutexIncrement(rx_stats.nCallStructs, rx_stats_mutex);
-       MUTEX_EXIT(&rx_freeCallQueue_lock);
+#ifdef DEBUG
+        call->allNextp = rx_allCallsp;
+        rx_allCallsp = call;
+        call->call_id = 
+#endif /* DEBUG */
+            rx_MutexIncrement(rx_stats.nCallStructs, rx_stats_mutex);
+        
+        MUTEX_EXIT(&rx_freeCallQueue_lock);
        MUTEX_INIT(&call->lock, "call lock", MUTEX_DEFAULT, NULL);
        MUTEX_ENTER(&call->lock);
        CV_INIT(&call->cv_twind, "call twind", CV_DEFAULT, 0);
@@ -2251,6 +2267,9 @@ rxi_NewCall(register struct rx_connection *conn, register int channel)
        queue_Init(&call->tq);
        queue_Init(&call->rq);
        queue_Init(&call->iovq);
+#ifdef DEBUG
+        call->rqc = call->tqc = call->iovqc = 0;
+#endif /* DEBUG */
        /* Bind the call to its connection structure (prereq for reset) */
        call->conn = conn;
        rxi_ResetCall(call, 1);
@@ -3298,6 +3317,9 @@ rxi_ReceiveDataPacket(register struct rx_call *call,
             * the reader once all packets have been processed */
            np->flags |= RX_PKTFLAG_RQ;
            queue_Prepend(&call->rq, np);
+#ifdef DEBUG
+            call->rqc++;
+#endif /* DEBUG */
            call->nSoftAcks++;
            np = NULL;          /* We can't use this anymore */
            newPackets = 1;
@@ -3426,6 +3448,10 @@ rxi_ReceiveDataPacket(register struct rx_call *call,
             * packet before which to insert the new packet, or at the
             * queue head if the queue is empty or the packet should be
             * appended. */
+            np->flags |= RX_PKTFLAG_RQ;
+#ifdef DEBUG
+            call->rqc++;
+#endif /* DEBUG */
            queue_InsertBefore(tp, np);
            call->nSoftAcks++;
            np = NULL;
@@ -3744,6 +3770,9 @@ rxi_ReceiveAckPacket(register struct rx_call *call, struct rx_packet *np,
        {
            queue_Remove(tp);
            tp->flags &= ~RX_PKTFLAG_TQ;
+#ifdef DEBUG
+            call->tqc--;
+#endif /* DEBUG */
            rxi_FreePacket(tp); /* rxi_FreePacket mustn't wake up anyone, preemptively. */
        }
     }
@@ -4355,7 +4384,10 @@ rxi_ClearTransmitQueue(register struct rx_call *call, register int force)
        }
     } else {
 #endif /* AFS_GLOBAL_RXLOCK_KERNEL */
-       rxi_FreePackets(0, &call->tq);
+#ifdef DEBUG
+        call->tqc -=
+#endif /* DEBUG */
+            rxi_FreePackets(0, &call->tq);
 #ifdef AFS_GLOBAL_RXLOCK_KERNEL
        call->flags &= ~RX_CALL_TQ_CLEARME;
     }
@@ -4380,7 +4412,15 @@ void
 rxi_ClearReceiveQueue(register struct rx_call *call)
 {
     if (queue_IsNotEmpty(&call->rq)) {
-       rx_packetReclaims += rxi_FreePackets(0, &call->rq);
+        u_short count;
+        
+        count = rxi_FreePackets(0, &call->rq);
+       rx_packetReclaims += count;
+#ifdef DEBUG
+        call->rqc -= count;
+        if ( call->rqc != 0 ) 
+            dpf(("rxi_ClearReceiveQueue call %x rqc %u != 0", call, call->rqc));
+#endif
        call->flags &= ~(RX_CALL_RECEIVE_DONE | RX_CALL_HAVE_LAST);
     }
     if (call->state == RX_STATE_PRECALL) {
@@ -4524,6 +4564,9 @@ rxi_ConnectionError(register struct rx_connection *conn,
 void
 rxi_CallError(register struct rx_call *call, afs_int32 error)
 {
+#ifdef DEBUG
+    osirx_AssertMine(&call->lock, "rxi_CallError");
+#endif
     dpf(("rxi_CallError call %x error %d call->error %d", call, error, call->error));
     if (call->error)
        error = call->error;
@@ -4555,7 +4598,9 @@ rxi_ResetCall(register struct rx_call *call, register int newcall)
     register int flags;
     register struct rx_peer *peer;
     struct rx_packet *packet;
-
+#ifdef DEBUG
+    osirx_AssertMine(&call->lock, "rxi_ResetCall");
+#endif
     dpf(("rxi_ResetCall(call %x, newcall %d)\n", call, newcall));
 
     /* Notify anyone who is waiting for asynchronous packet arrival */
@@ -4633,12 +4678,19 @@ rxi_ResetCall(register struct rx_call *call, register int newcall)
     
     if (call->currentPacket) {
         call->currentPacket->flags &= ~RX_PKTFLAG_CP;
-       rxi_FreePacket(call->currentPacket);
-       call->currentPacket = (struct rx_packet *)0;
+        call->currentPacket->flags |= RX_PKTFLAG_IOVQ;
+        queue_Prepend(&call->iovq, call->currentPacket);
+#ifdef DEBUG
+        call->iovqc++;
+#endif /* DEBUG */
+        call->currentPacket = (struct rx_packet *)0;
     }
     call->curlen = call->nLeft = call->nFree = 0;
 
-    rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+    call->iovqc -= 
+#endif
+        rxi_FreePackets(0, &call->iovq);
 
     call->error = 0;
     call->twind = call->conn->twind[call->channel];
@@ -5407,6 +5459,9 @@ rxi_Start(struct rxevent *event,
                            && (p->flags & RX_PKTFLAG_ACKED)) {
                            queue_Remove(p);
                            p->flags &= ~RX_PKTFLAG_TQ;
+#ifdef DEBUG
+                            call->tqc--;
+#endif
                            rxi_FreePacket(p);
                        } else
                            missing = 1;
@@ -7957,5 +8012,66 @@ DllMain(HINSTANCE dllInstHandle, /* instance handle for this DLL module */
        return FALSE;
     }
 }
+
+#ifdef AFS_NT40_ENV
+int rx_DumpCalls(FILE *outputFile, char *cookie)
+{
+#ifdef DEBUG
+    int zilch;
+#ifdef KDUMP_RX_LOCK
+    struct rx_call_rx_lock *c;
+#else
+    struct rx_call *c;
+#endif
+    char output[2048];
+
+    sprintf(output, "%s - Start dumping all Rx Calls - count=%u\r\n", cookie, rx_stats.nCallStructs);
+    WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
+
+    for (c = rx_allCallsp; c; c = c->allNextp) {
+        u_short rqc, tqc, iovqc;
+        struct rx_packet *p, *np;
+
+        MUTEX_ENTER(&c->lock);
+        queue_Count(&c->rq, p, np, rx_packet, rqc);
+        queue_Count(&c->tq, p, np, rx_packet, tqc);
+        queue_Count(&c->iovq, p, np, rx_packet, iovqc);
+
+        sprintf(output, "%s - call=0x%p, id=%u, state=%u, mode=%u, conn=%p, epoch=%u, cid=%u, callNum=%u, connFlags=0x%x, flags=0x%x, "
+                "rqc=%u,%u, tqc=%u,%u, iovqc=%u,%u, "
+                "lstatus=%u, rstatus=%u, error=%d, timeout=%u, "
+                "resendEvent=%d, timeoutEvt=%d, keepAliveEvt=%d, delayedAckEvt=%d, delayedAbortEvt=%d, abortCode=%d, abortCount=%d, "
+                "lastSendTime=%u, lastRecvTime=%u, lastSendData=%u"
+#ifdef RX_ENABLE_LOCKS
+                ", refCount=%u"
+#endif
+#ifdef RX_REFCOUNT_CHECK
+                ", refCountBegin=%u, refCountResend=%u, refCountDelay=%u, "
+                "refCountAlive=%u, refCountPacket=%u, refCountSend=%u, refCountAckAll=%u, refCountAbort=%u"
+#endif
+                "\r\n",
+                cookie, c, c->call_id, (afs_uint32)c->state, (afs_uint32)c->mode, c->conn, c->conn?c->conn->epoch:0, c->conn?c->conn->cid:0,
+                c->callNumber?*c->callNumber:0, c->conn?c->conn->flags:0, c->flags,
+                (afs_uint32)c->rqc, (afs_uint32)rqc, (afs_uint32)c->tqc, (afs_uint32)tqc, (afs_uint32)c->iovqc, (afs_uint32)iovqc, 
+                (afs_uint32)c->localStatus, (afs_uint32)c->remoteStatus, c->error, c->timeout, 
+                c->resendEvent?1:0, c->timeoutEvent?1:0, c->keepAliveEvent?1:0, c->delayedAckEvent?1:0, c->delayedAbortEvent?1:0,
+                c->abortCode, c->abortCount, c->lastSendTime, c->lastReceiveTime, c->lastSendData
+#ifdef RX_ENABLE_LOCKS
+                , (afs_uint32)c->refCount
+#endif
+#ifdef RX_REFCOUNT_CHECK
+                , c->refCDebug[0],c->refCDebug[1],c->refCDebug[2],c->refCDebug[3],c->refCDebug[4],c->refCDebug[5],c->refCDebug[6],c->refCDebug[7]
+#endif
+                );
+        MUTEX_EXIT(&c->lock);
+
+        WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
+    }
+    sprintf(output, "%s - End dumping all Rx Calls\r\n", cookie);
+    WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
+#endif /* DEBUG */
+    return 0;
+}
+#endif /* AFS_NT40_ENV */
 #endif
 
index f1635d2..95c4089 100644 (file)
@@ -545,7 +545,7 @@ struct rx_call {
     u_short nCwindAcks;                /* Number acks received at current cwind */
     u_short ssthresh;          /* The slow start threshold */
     u_short nDgramPackets;     /* Packets per AFS 3.5 jumbogram */
-    u_short nAcks;             /* The number of consecttive acks */
+    u_short nAcks;             /* The number of consecutive acks */
     u_short nNacks;            /* Number packets acked that follow the
                                 * first negatively acked packet */
     u_short nSoftAcks;         /* The number of delayed soft acks */
@@ -595,6 +595,19 @@ struct rx_call {
     afs_hyper_t bytesSent;     /* Number bytes sent */
     afs_hyper_t bytesRcvd;     /* Number bytes received */
     u_short tqWaiters;
+
+#ifdef DEBUG
+    u_short tqc;                /* packet count in tq */
+    u_short rqc;                /* packet count in rq */
+    u_short iovqc;              /* packet count in iovq */
+
+#ifdef KDUMP_RX_LOCK
+    struct rx_call_rx_lock *allNextp;
+#else
+    struct rx_call *allNextp;
+#endif
+    afs_uint32 call_id;
+#endif
 };
 
 #ifndef KDUMP_RX_LOCK
@@ -1110,6 +1123,9 @@ typedef struct rx_interface_stat {
 
 #define RX_STATS_SERVICE_ID 409
 
+#ifdef AFS_NT40_ENV
+extern int rx_DumpCalls(FILE *outputFile, char *cookie);
+#endif /* AFS_NT40_ENV */
 #endif /* _RX_   End of rx.h */
 
 #ifdef KERNEL
index 60eb416..aabd633 100644 (file)
@@ -95,7 +95,10 @@ RCSID
 /* rxdb_fileID is used to identify the lock location, along with line#. */
 static int rxdb_fileID = RXDB_FILE_RX_PACKET;
 #endif /* RX_LOCKS_DB */
-struct rx_packet *rx_mallocedP = 0;
+static struct rx_packet *rx_mallocedP = 0;
+#ifdef DEBUG
+static afs_uint32       rx_packet_id = 0;
+#endif
 
 extern char cml_version_number[];
 
@@ -559,6 +562,10 @@ rxi_MorePackets(int apackets)
 
         NETPRI;
         MUTEX_ENTER(&rx_freePktQ_lock);
+#ifdef DEBUG
+        p->packetId = rx_packet_id++;
+        p->allNextp = rx_mallocedP;
+#endif /* DEBUG */
         rx_mallocedP = p;
         MUTEX_EXIT(&rx_freePktQ_lock);
         USERPRI;
@@ -600,6 +607,10 @@ rxi_MorePackets(int apackets)
        p->niovecs = 2;
 
        queue_Append(&rx_freePacketQueue, p);
+#ifdef DEBUG
+        p->packetId = rx_packet_id++;
+        p->allNextp = rx_mallocedP;
+#endif /* DEBUG */
        rx_mallocedP = p;
     }
 
@@ -642,6 +653,10 @@ rxi_MorePacketsTSFPQ(int apackets, int flush_global, int num_keep_local)
        
         NETPRI;
         MUTEX_ENTER(&rx_freePktQ_lock);
+#ifdef DEBUG
+        p->packetId = rx_packet_id++;
+        p->allNextp = rx_mallocedP;
+#endif /* DEBUG */
         rx_mallocedP = p;
         MUTEX_EXIT(&rx_freePktQ_lock);
         USERPRI;
@@ -699,6 +714,10 @@ rxi_MorePacketsNoLock(int apackets)
        p->niovecs = 2;
 
        queue_Append(&rx_freePacketQueue, p);
+#ifdef DEBUG
+        p->packetId = rx_packet_id++;
+        p->allNextp = rx_mallocedP;
+#endif /* DEBUG */
        rx_mallocedP = p;
     }
 
@@ -903,6 +922,8 @@ rxi_FreeDataBufsNoLock(struct rx_packet *p, afs_uint32 first)
  *
  * [IN] p             -- packet from which continuation buffers will be freed
  * [IN] first         -- iovec offset of first continuation buffer to free
+ *                       any value less than 2, the min number of iovecs,
+ *                       is treated as if it is 2.
  * [IN] flush_global  -- if nonzero, we will flush overquota packets to the
  *                       global free pool before returning
  *
@@ -2656,9 +2677,10 @@ rxi_PrepareSendPacket(register struct rx_call *call,
         MUTEX_EXIT(&rx_freePktQ_lock);
 #endif /* !RX_ENABLE_TSFPQ */
 
-       p->niovecs = i;
+        p->niovecs = i;
     }
-    p->wirevec[i - 1].iov_len += len;
+    if (len)
+        p->wirevec[i - 1].iov_len += len;
     RXS_PreparePacket(conn->securityObject, call, p);
 }
 
@@ -2716,3 +2738,42 @@ rxi_AdjustDgramPackets(int frags, int mtu)
     }
     return (2 + (maxMTU / (RX_JUMBOBUFFERSIZE + RX_JUMBOHEADERSIZE)));
 }
+
+#ifdef AFS_NT40_ENV
+/* 
+ * This function can be used by the Windows Cache Manager
+ * to dump the list of all rx packets so that we can determine
+ * where the packet leakage is.
+ */
+int rx_DumpPackets(FILE *outputFile, char *cookie)
+{
+#ifdef DEBUG
+    int zilch;
+    struct rx_packet *p;
+    char output[2048];
+
+    NETPRI;
+    MUTEX_ENTER(&rx_freePktQ_lock);
+    sprintf(output, "%s - Start dumping all Rx Packets - count=%u\r\n", cookie, rx_packet_id);
+    WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
+
+    for (p = rx_mallocedP; p; p = p->allNextp) {
+        sprintf(output, "%s - packet=0x%p, id=%u, firstSent=%u.%08u, timeSent=%u.%08u, retryTime=%u.%08u, firstSerial=%u, niovecs=%u, flags=0x%x, backoff=%u, length=%u  header: epoch=%u, cid=%u, callNum=%u, seq=%u, serial=%u, type=%u, flags=0x%x, userStatus=%u, securityIndex=%u, serviceId=%u\r\n",
+                cookie, p, p->packetId, p->firstSent.sec, p->firstSent.usec, p->timeSent.sec, p->timeSent.usec, p->retryTime.sec, p->retryTime.usec, 
+                p->firstSerial, p->niovecs, (afs_uint32)p->flags, (afs_uint32)p->backoff, (afs_uint32)p->length,
+                p->header.epoch, p->header.cid, p->header.callNumber, p->header.seq, p->header.serial,
+                (afs_uint32)p->header.type, (afs_uint32)p->header.flags, (afs_uint32)p->header.userStatus, 
+                (afs_uint32)p->header.securityIndex, (afs_uint32)p->header.serviceId);
+        WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
+    }
+
+    sprintf(output, "%s - End dumping all Rx Packets\r\n", cookie);
+    WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
+
+    MUTEX_EXIT(&rx_freePktQ_lock);
+    USERPRI;
+#endif /* DEBUG */
+    return 0;
+}
+#endif /* AFS_NT40_ENV */
+
index 718e7de..8e539a4 100644 (file)
@@ -271,6 +271,11 @@ struct rx_packet {
     afs_uint32 wirehead[RX_HEADER_SIZE / sizeof(afs_int32)];
     afs_uint32 localdata[RX_CBUFFERSIZE / sizeof(afs_int32)];
     afs_uint32 extradata[RX_EXTRABUFFERSIZE / sizeof(afs_int32)];
+#ifdef DEBUG
+    /* For debugging */
+    struct rx_packet *allNextp; /* A list of all packets */
+    afs_uint32  packetId;       /* An unique id number for debugging */
+#endif
 };
 
 /* Macro to convert continuation buffer pointers to packet pointers */
@@ -358,4 +363,9 @@ struct rx_packet {
 /* DEPRECATED */
 #define        rx_UserDataOf(conn, packet)     (((char *) (packet)->wirevec[1].iov_base) + (conn)->securityHeaderSize)
 
+#ifdef AFS_NT40_ENV
+/* Debugging for Windows Cache Manager - fs memdump */
+int rx_DumpPackets(FILE *outputFile, char *cookie);
+#endif /* AFS_NT40_ENV */
+
 #endif /* _RX_PACKET_ */
index 2c3b37b..5758def 100644 (file)
@@ -116,7 +116,10 @@ rxi_ReadProc(register struct rx_call *call, register char *buf,
 
     /* Free any packets from the last call to ReadvProc/WritevProc */
     if (queue_IsNotEmpty(&call->iovq)) {
-        rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+        call->iovqc -=
+#endif /* DEBUG */
+            rxi_FreePackets(0, &call->iovq);
     }
 
     do {
@@ -140,6 +143,9 @@ rxi_ReadProc(register struct rx_call *call, register char *buf,
                        register struct rx_connection *conn = call->conn;
                        queue_Remove(rp);
                        rp->flags &= ~RX_PKTFLAG_RQ;
+#ifdef DEBUG
+                        call->rqc--;
+#endif /* DEBUG */
 
                        /* RXS_CheckPacket called to undo RXS_PreparePacket's
                         * work.  It may reduce the length of the packet by up
@@ -313,7 +319,10 @@ rx_ReadProc(struct rx_call *call, char *buf, int nbytes)
      * ReadvProc/WritevProc.
      */
     if (!queue_IsEmpty(&call->iovq)) {
-        rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+        call->iovqc -=
+#endif /* DEBUG */
+            rxi_FreePackets(0, &call->iovq);
     }
 
     /*
@@ -370,7 +379,10 @@ rx_ReadProc32(struct rx_call *call, afs_int32 * value)
      * ReadvProc/WritevProc.
      */
     if (!queue_IsEmpty(&call->iovq)) {
-       rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+        call->iovqc -=
+#endif /* DEBUG */
+            rxi_FreePackets(0, &call->iovq);
     }
 
     /*
@@ -443,6 +455,9 @@ rxi_FillReadVec(struct rx_call *call, afs_uint32 serial)
                    register struct rx_connection *conn = call->conn;
                    queue_Remove(rp);
                    rp->flags &= ~RX_PKTFLAG_RQ;
+#ifdef DEBUG
+                    call->rqc--;
+#endif /* DEBUG */
 
                    /* RXS_CheckPacket called to undo RXS_PreparePacket's
                     * work.  It may reduce the length of the packet by up
@@ -524,6 +539,9 @@ rxi_FillReadVec(struct rx_call *call, afs_uint32 serial)
                 curp->flags &= ~RX_PKTFLAG_CP;
                 curp->flags |= RX_PKTFLAG_IOVQ;
                queue_Append(&call->iovq, curp);
+#ifdef DEBUG
+                call->iovqc++;
+#endif /* DEBUG */
                curp = call->currentPacket = (struct rx_packet *)0;
            } else if (!call->curlen) {
                /* need to get another struct iov */
@@ -533,6 +551,9 @@ rxi_FillReadVec(struct rx_call *call, afs_uint32 serial)
                    curp->flags &= ~RX_PKTFLAG_CP;
                    curp->flags |= RX_PKTFLAG_IOVQ;
                    queue_Append(&call->iovq, curp);
+#ifdef DEBUG
+                    call->iovqc++;
+#endif /* DEBUG */
                    curp = call->currentPacket = (struct rx_packet *)0;
                    call->nLeft = 0;
                } else {
@@ -592,7 +613,10 @@ rxi_ReadvProc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
 
     /* Free any packets from the last call to ReadvProc/WritevProc */
     if (queue_IsNotEmpty(&call->iovq)) {
-        rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+        call->iovqc -=
+#endif /* DEBUG */
+            rxi_FreePackets(0, &call->iovq);
     }
 
     if (call->mode == RX_MODE_SENDING) {
@@ -674,7 +698,10 @@ rxi_WriteProc(register struct rx_call *call, register char *buf,
 
     /* Free any packets from the last call to ReadvProc/WritevProc */
     if (queue_IsNotEmpty(&call->iovq)) {
-       rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+        call->iovqc -=
+#endif /* DEBUG */
+            rxi_FreePackets(0, &call->iovq);
     }
 
     if (call->mode != RX_MODE_SENDING) {
@@ -729,7 +756,10 @@ rxi_WriteProc(register struct rx_call *call, register char *buf,
                rxi_PrepareSendPacket(call, cp, 0);
                cp->flags |= RX_PKTFLAG_TQ;
                queue_Append(&call->tq, cp);
-               cp = (struct rx_packet *)0;
+#ifdef DEBUG
+                call->tqc++;
+#endif /* DEBUG */
+                cp = (struct rx_packet *)0;
                if (!
                    (call->
                     flags & (RX_CALL_FAST_RECOVER |
@@ -856,7 +886,10 @@ rx_WriteProc(struct rx_call *call, char *buf, int nbytes)
      * ReadvProc/WritevProc.
      */
     if (queue_IsNotEmpty(&call->iovq)) {
-       rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+        call->iovqc -=
+#endif /* DEBUG */
+            rxi_FreePackets(0, &call->iovq);
     }
 
     /*
@@ -903,7 +936,10 @@ rx_WriteProc32(register struct rx_call *call, register afs_int32 * value)
      * ReadvProc/WritevProc.
      */
     if (queue_IsNotEmpty(&call->iovq)) {
-       rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+        call->iovqc -=
+#endif /* DEBUG */
+            rxi_FreePackets(0, &call->iovq);
     }
 
     /*
@@ -963,7 +999,10 @@ rxi_WritevAlloc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
 
     /* Free any packets from the last call to ReadvProc/WritevProc */
     if (queue_IsNotEmpty(&call->iovq)) {
-        rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+        call->iovqc -=
+#endif /* DEBUG */
+            rxi_FreePackets(0, &call->iovq);
     }
 
     if (call->mode != RX_MODE_SENDING) {
@@ -1000,6 +1039,9 @@ rxi_WritevAlloc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
            }
            cp->flags |= RX_PKTFLAG_IOVQ;
            queue_Append(&call->iovq, cp);
+#ifdef DEBUG
+            call->iovqc++;
+#endif /* DEBUG */
            tnFree = cp->length;
            tcurvec = 1;
            tcurpos =
@@ -1077,9 +1119,13 @@ int
 rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
 {
     struct rx_packet *cp = call->currentPacket;
+    struct rx_call *p, *np;
     int nextio;
     int requestCount;
     struct rx_queue tmpq;
+#ifdef DEBUG
+    u_short tmpqc;
+#endif
 
     requestCount = nbytes;
     nextio = 0;
@@ -1105,9 +1151,15 @@ rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
             cp->flags &= ~RX_PKTFLAG_CP;
             cp->flags |= RX_PKTFLAG_IOVQ;
            queue_Prepend(&call->iovq, cp);
+#ifdef DEBUG
+            call->iovqc++;
+#endif /* DEBUG */
            cp = call->currentPacket = (struct rx_packet *)0;
        }
-       rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+        call->iovqc -=
+#endif /* DEBUG */
+            rxi_FreePackets(0, &call->iovq);
        return 0;
     }
 
@@ -1118,6 +1170,9 @@ rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
      * a zero length write will push a short packet. */
     nextio = 0;
     queue_Init(&tmpq);
+#ifdef DEBUG
+    tmpqc = 0;
+#endif /* DEBUG */
     do {
        if (call->nFree == 0 && cp) {
            clock_NewTime();    /* Bogus:  need new time package */
@@ -1127,20 +1182,28 @@ rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
             * conn->securityMaxTrailerSize */
            hadd32(call->bytesSent, cp->length);
            rxi_PrepareSendPacket(call, cp, 0);
-           cp->flags |= RX_PKTFLAG_TQ;
            queue_Append(&tmpq, cp);
+#ifdef DEBUG
+            tmpqc++;
+#endif /* DEBUG */
             cp = call->currentPacket = (struct rx_packet *)0;
 
            /* The head of the iovq is now the current packet */
            if (nbytes) {
                if (queue_IsEmpty(&call->iovq)) {
                    call->error = RX_PROTOCOL_ERROR;
-                   rxi_FreePackets(0, &tmpq);
+#ifdef DEBUG
+                    tmpqc -=
+#endif /* DEBUG */
+                        rxi_FreePackets(0, &tmpq);
                    return 0;
                }
                cp = queue_First(&call->iovq, rx_packet);
                queue_Remove(cp);
                 cp->flags &= ~RX_PKTFLAG_IOVQ;
+#ifdef DEBUG
+                call->iovqc--;
+#endif /* DEBUG */
                 cp->flags |= RX_PKTFLAG_CP;
                call->currentPacket = cp;
                call->nFree = cp->length;
@@ -1160,10 +1223,16 @@ rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
                call->error = RX_PROTOCOL_ERROR;
                if (cp) {
                    cp->flags &= ~RX_PKTFLAG_CP;
-                   queue_Prepend(&tmpq, cp);
+                    queue_Prepend(&tmpq, cp);
+#ifdef DEBUG
+                    tmpqc++;
+#endif /* DEBUG */
                     cp = call->currentPacket = (struct rx_packet *)0;
                }
-               rxi_FreePackets(0, &tmpq);
+#ifdef DEBUG
+                tmpqc -=
+#endif /* DEBUG */
+                    rxi_FreePackets(0, &tmpq);
                return 0;
            }
            nbytes -= iov[nextio].iov_len;
@@ -1184,6 +1253,15 @@ 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 KDUMP_RX_LOCK
+    for (queue_Scan(&tmpq, p, np, rx_call_rx_lock))
+#else
+    for (queue_Scan(&tmpq, p, np, rx_call))
+#endif
+    {
+        p->flags |= RX_PKTFLAG_TQ;
+    }
     queue_SpliceAppend(&call->tq, &tmpq);
 
     if (!(call->flags & (RX_CALL_FAST_RECOVER | RX_CALL_FAST_RECOVER_WAIT))) {
@@ -1238,7 +1316,10 @@ rxi_FlushWrite(register struct rx_call *call)
 
     /* Free any packets from the last call to ReadvProc/WritevProc */
     if (queue_IsNotEmpty(&call->iovq)) {
-       rxi_FreePackets(0, &call->iovq);
+#ifdef DEBUG
+        call->iovqc -=
+#endif /* DEBUG */
+            rxi_FreePackets(0, &call->iovq);
     }
 
     if (call->mode == RX_MODE_SENDING) {
@@ -1298,6 +1379,9 @@ rxi_FlushWrite(register struct rx_call *call)
        rxi_PrepareSendPacket(call, cp, 1);
        cp->flags |= RX_PKTFLAG_TQ;
        queue_Append(&call->tq, cp);
+#ifdef DEBUG
+        call->tqc++;
+#endif /* DEBUG */
        if (!
            (call->
             flags & (RX_CALL_FAST_RECOVER | RX_CALL_FAST_RECOVER_WAIT))) {