rx: Use a structure for the xmit list
[openafs.git] / src / rx / rx.c
index 5d2e484..312d746 100644 (file)
@@ -129,7 +129,7 @@ int (*swapNameProgram) (PROCESS, const char *, char *) = 0;
 
 /* Local static routines */
 static void rxi_DestroyConnectionNoLock(struct rx_connection *conn);
-static void rxi_ComputeRoundTripTime(struct rx_packet *, struct clock *,
+static void rxi_ComputeRoundTripTime(struct rx_packet *, struct rx_ackPacket *,
                                     struct rx_peer *, struct clock *);
 
 #ifdef RX_ENABLE_LOCKS
@@ -4061,11 +4061,8 @@ rxi_ReceiveAckPacket(struct rx_call *call, struct rx_packet *np,
 
        if (!(tp->flags & RX_PKTFLAG_ACKED)) {
            newAckCount++;
-           if (ap->reason != RX_ACK_DELAY &&
-               clock_Eq(&tp->timeSent, &tp->firstSent)) {
-               rxi_ComputeRoundTripTime(tp, &tp->timeSent, call->conn->peer,
-                                        &now);
-           }
+
+           rxi_ComputeRoundTripTime(tp, ap, call->conn->peer, &now);
        }
 
 #ifdef ADAPT_WINDOW
@@ -4142,11 +4139,7 @@ rxi_ReceiveAckPacket(struct rx_call *call, struct rx_packet *np,
                    newAckCount++;
                    tp->flags |= RX_PKTFLAG_ACKED;
 
-                   if (ap->reason != RX_ACK_DELAY &&
-                       clock_Eq(&tp->timeSent, &tp->firstSent)) {
-                       rxi_ComputeRoundTripTime(tp, &tp->timeSent,
-                                                call->conn->peer, &now);
-                   }
+                   rxi_ComputeRoundTripTime(tp, ap, call->conn->peer, &now);
 #ifdef ADAPT_WINDOW
                    rxi_ComputeRate(call->conn->peer, call, tp, np,
                                    ap->reason);
@@ -4297,7 +4290,6 @@ rxi_ReceiveAckPacket(struct rx_call *call, struct rx_packet *np,
            maxDgramPackets = MIN(maxDgramPackets, rxi_nDgramPackets);
            maxDgramPackets =
                MIN(maxDgramPackets, (int)(peer->ifDgramPackets));
-           maxDgramPackets = MIN(maxDgramPackets, tSize);
            if (maxDgramPackets > 1) {
                peer->maxDgramPackets = maxDgramPackets;
                call->MTU = RX_JUMBOBUFFERSIZE + RX_HEADER_SIZE;
@@ -5382,9 +5374,14 @@ rxi_SendAck(struct rx_call *call,
     return optionalPacket;     /* Return packet for re-use by caller */
 }
 
+struct xmitlist {
+   struct rx_packet **list;
+   int len;
+};
+
 /* Send all of the packets in the list in single datagram */
 static void
-rxi_SendList(struct rx_call *call, struct rx_packet **list, int len,
+rxi_SendList(struct rx_call *call, struct xmitlist *xmit,
             int istack, int moreFlag, struct clock *now,
             struct clock *retryTime, int resending)
 {
@@ -5395,73 +5392,76 @@ rxi_SendList(struct rx_call *call, struct rx_packet **list, int len,
     struct rx_peer *peer = conn->peer;
 
     MUTEX_ENTER(&peer->peer_lock);
-    peer->nSent += len;
+    peer->nSent += xmit->len;
     if (resending)
-       peer->reSends += len;
+       peer->reSends += xmit->len;
     MUTEX_EXIT(&peer->peer_lock);
 
     if (rx_stats_active) {
         if (resending)
-            rx_atomic_add(&rx_stats.dataPacketsReSent, len);
+            rx_atomic_add(&rx_stats.dataPacketsReSent, xmit->len);
         else
-            rx_atomic_add(&rx_stats.dataPacketsSent, len);
+            rx_atomic_add(&rx_stats.dataPacketsSent, xmit->len);
     }
 
-    if (list[len - 1]->header.flags & RX_LAST_PACKET) {
+    if (xmit->list[xmit->len - 1]->header.flags & RX_LAST_PACKET) {
        lastPacket = 1;
     }
 
     /* Set the packet flags and schedule the resend events */
     /* Only request an ack for the last packet in the list */
-    for (i = 0; i < len; i++) {
-       list[i]->retryTime = *retryTime;
-       if (list[i]->header.serial) {
+    for (i = 0; i < xmit->len; i++) {
+       struct rx_packet *packet = xmit->list[i];
+
+       packet->retryTime = *retryTime;
+       if (packet->header.serial) {
            /* Exponentially backoff retry times */
-           if (list[i]->backoff < MAXBACKOFF) {
+           if (packet->backoff < MAXBACKOFF) {
                /* so it can't stay == 0 */
-               list[i]->backoff = (list[i]->backoff << 1) + 1;
+               packet->backoff = (packet->backoff << 1) + 1;
            } else
-               list[i]->backoff++;
-           clock_Addmsec(&(list[i]->retryTime),
-                         ((afs_uint32) list[i]->backoff) << 8);
+               packet->backoff++;
+           clock_Addmsec(&(packet->retryTime),
+                         ((afs_uint32) packet->backoff) << 8);
        }
 
        /* Wait a little extra for the ack on the last packet */
-       if (lastPacket && !(list[i]->header.flags & RX_CLIENT_INITIATED)) {
-           clock_Addmsec(&(list[i]->retryTime), 400);
+       if (lastPacket 
+           && !(packet->header.flags & RX_CLIENT_INITIATED)) {
+           clock_Addmsec(&(packet->retryTime), 400);
        }
 
        /* Record the time sent */
-       list[i]->timeSent = *now;
+       packet->timeSent = *now;
 
        /* Ask for an ack on retransmitted packets,  on every other packet
         * if the peer doesn't support slow start. Ask for an ack on every
         * packet until the congestion window reaches the ack rate. */
-       if (list[i]->header.serial) {
+       if (packet->header.serial) {
            requestAck = 1;
        } else {
            /* improved RTO calculation- not Karn */
-           list[i]->firstSent = *now;
+           packet->firstSent = *now;
            if (!lastPacket && (call->cwind <= (u_short) (conn->ackRate + 1)
                                || (!(call->flags & RX_CALL_SLOW_START_OK)
-                                   && (list[i]->header.seq & 1)))) {
+                                   && (packet->header.seq & 1)))) {
                requestAck = 1;
            }
        }
 
        /* Tag this packet as not being the last in this group,
         * for the receiver's benefit */
-       if (i < len - 1 || moreFlag) {
-           list[i]->header.flags |= RX_MORE_PACKETS;
+       if (i < xmit->len - 1 || moreFlag) {
+           packet->header.flags |= RX_MORE_PACKETS;
        }
 
        /* Install the new retransmit time for the packet, and
         * record the time sent */
-       list[i]->timeSent = *now;
+       packet->timeSent = *now;
     }
 
     if (requestAck) {
-       list[len - 1]->header.flags |= RX_REQUEST_ACK;
+       xmit->list[xmit->len - 1]->header.flags |= RX_REQUEST_ACK;
     }
 
     /* Since we're about to send a data packet to the peer, it's
@@ -5472,10 +5472,10 @@ rxi_SendList(struct rx_call *call, struct rx_packet **list, int len,
     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);
+    if (xmit->len > 1) {
+       rxi_SendPacketList(call, conn, xmit->list, xmit->len, istack);
     } else {
-       rxi_SendPacket(call, conn, list[0], istack);
+       rxi_SendPacket(call, conn, xmit->list[0], istack);
     }
     MUTEX_ENTER(&call->lock);
     MUTEX_ENTER(&rx_refcnt_mutex);
@@ -5499,45 +5499,55 @@ rxi_SendList(struct rx_call *call, struct rx_packet **list, int len,
  * We always keep the last list we should have sent so we
  * can set the RX_MORE_PACKETS flags correctly.
  */
+
 static void
 rxi_SendXmitList(struct rx_call *call, struct rx_packet **list, int len,
                 int istack, struct clock *now, struct clock *retryTime,
                 int resending)
 {
-    int i, cnt, lastCnt = 0;
-    struct rx_packet **listP, **lastP = 0;
+    int i;
+    struct xmitlist working;
+    struct xmitlist last = {NULL, 0};
+
     struct rx_peer *peer = call->conn->peer;
     int morePackets = 0;
 
-    for (cnt = 0, listP = &list[0], i = 0; i < len; i++) {
+    memset(&last, 0, sizeof(struct xmitlist));
+    working.list = &list[0];
+    working.len = 0;
+
+    for (i = 0; i < len; i++) {
        /* Does the current packet force us to flush the current list? */
-       if (cnt > 0
+       if (working.len > 0
            && (list[i]->header.serial || (list[i]->flags & RX_PKTFLAG_ACKED)
                || list[i]->length > RX_JUMBOBUFFERSIZE)) {
-           if (lastCnt > 0) {
-               rxi_SendList(call, lastP, lastCnt, istack, 1, now, retryTime,
-                            resending);
+
+           /* This sends the 'last' list and then rolls the current working
+            * set into the 'last' one, and resets the working set */
+
+           if (last.len > 0) {
+               rxi_SendList(call, &last, istack, 1, now, retryTime, resending);
                /* If the call enters an error state stop sending, or if
                 * we entered congestion recovery mode, stop sending */
                if (call->error || (call->flags & RX_CALL_FAST_RECOVER_WAIT))
                    return;
            }
-           lastP = listP;
-           lastCnt = cnt;
-           listP = &list[i];
-           cnt = 0;
+           last = working;
+           working.len = 0;
+           working.list = &list[i];
        }
        /* Add the current packet to the list if it hasn't been acked.
         * Otherwise adjust the list pointer to skip the current packet.  */
        if (!(list[i]->flags & RX_PKTFLAG_ACKED)) {
-           cnt++;
+           working.len++;
            /* Do we need to flush the list? */
-           if (cnt >= (int)peer->maxDgramPackets
-               || cnt >= (int)call->nDgramPackets || cnt >= (int)call->cwind
+           if (working.len >= (int)peer->maxDgramPackets
+               || working.len >= (int)call->nDgramPackets 
+               || working.len >= (int)call->cwind
                || list[i]->header.serial
                || list[i]->length != RX_JUMBOBUFFERSIZE) {
-               if (lastCnt > 0) {
-                   rxi_SendList(call, lastP, lastCnt, istack, 1, now,
+               if (last.len > 0) {
+                   rxi_SendList(call, &last, istack, 1, now,
                                 retryTime, resending);
                    /* If the call enters an error state stop sending, or if
                     * we entered congestion recovery mode, stop sending */
@@ -5545,16 +5555,15 @@ rxi_SendXmitList(struct rx_call *call, struct rx_packet **list, int len,
                        || (call->flags & RX_CALL_FAST_RECOVER_WAIT))
                        return;
                }
-               lastP = listP;
-               lastCnt = cnt;
-               listP = &list[i + 1];
-               cnt = 0;
+               last = working;
+               working.len = 0;
+               working.list = &list[i + 1];
            }
        } else {
-           if (cnt != 0) {
+           if (working.len != 0) {
                osi_Panic("rxi_SendList error");
            }
-           listP = &list[i + 1];
+           working.list = &list[i + 1];
        }
     }
 
@@ -5568,11 +5577,11 @@ rxi_SendXmitList(struct rx_call *call, struct rx_packet **list, int len,
         * an acked packet. Since we always send retransmissions
         * in a separate packet, we only need to check the first
         * packet in the list */
-       if (cnt > 0 && !(listP[0]->flags & RX_PKTFLAG_ACKED)) {
+       if (working.len > 0 && !(working.list[0]->flags & RX_PKTFLAG_ACKED)) {
            morePackets = 1;
        }
-       if (lastCnt > 0) {
-           rxi_SendList(call, lastP, lastCnt, istack, morePackets, now,
+       if (last.len > 0) {
+           rxi_SendList(call, &last, istack, morePackets, now,
                         retryTime, resending);
            /* If the call enters an error state stop sending, or if
             * we entered congestion recovery mode, stop sending */
@@ -5580,12 +5589,12 @@ rxi_SendXmitList(struct rx_call *call, struct rx_packet **list, int len,
                return;
        }
        if (morePackets) {
-           rxi_SendList(call, listP, cnt, istack, 0, now, retryTime,
+           rxi_SendList(call, &working, istack, 0, now, retryTime,
                         resending);
        }
-    } else if (lastCnt > 0) {
-       rxi_SendList(call, lastP, lastCnt, istack, 0, now, retryTime,
-                    resending);
+    } else if (last.len > 0) {
+       rxi_SendList(call, &last, istack, 0, now, retryTime, resending);
+       /* Packets which are in 'working' are not sent by this call */
     }
 }
 
@@ -6532,50 +6541,81 @@ rxi_ChallengeOn(struct rx_connection *conn)
 }
 
 
-/* Compute round trip time of the packet provided, in *rttp.
- */
-
 /* rxi_ComputeRoundTripTime is called with peer locked. */
-/* sentp and/or peer may be null */
+/* peer may be null */
 static void
 rxi_ComputeRoundTripTime(struct rx_packet *p,
-                        struct clock *sentp,
+                        struct rx_ackPacket *ack,
                         struct rx_peer *peer,
                         struct clock *now)
 {
-    struct clock thisRtt, *rttp = &thisRtt;
+    struct clock thisRtt, *sentp;
     int rtt_timeout;
+    int serial;
+
+    /* If the ACK is delayed, then do nothing */
+    if (ack->reason == RX_ACK_DELAY)
+       return;
+
+    /* On the wire, jumbograms are a single UDP packet. We shouldn't count
+     * their RTT multiple times, so only include the RTT of the last packet
+     * in a jumbogram */
+    if (p->flags & RX_JUMBO_PACKET)
+       return;
+
+    /* Use the serial number to determine which transmission the ACK is for,
+     * and set the sent time to match this. If we have no serial number, then
+     * only use the ACK for RTT calculations if the packet has not been
+     * retransmitted
+     */
+
+    serial = ntohl(ack->serial);
+    if (serial) {
+       if (serial == p->header.serial) {
+           sentp = &p->timeSent;
+       } else if (serial == p->firstSerial) {
+           sentp = &p->firstSent;
+       } else if (clock_Eq(&p->timeSent, &p->firstSent)) {
+           sentp = &p->firstSent;
+       } else
+           return;
+    } else {
+       if (clock_Eq(&p->timeSent, &p->firstSent)) {
+           sentp = &p->firstSent;
+       } else
+           return;
+    }
 
     thisRtt = *now;
 
-    if (clock_Lt(rttp, sentp))
+    if (clock_Lt(&thisRtt, sentp))
        return;                 /* somebody set the clock back, don't count this time. */
 
-    clock_Sub(rttp, sentp);
+    clock_Sub(&thisRtt, sentp);
     dpf(("rxi_ComputeRoundTripTime(call=%d packet=%"AFS_PTR_FMT" rttp=%d.%06d sec)\n",
-          p->header.callNumber, p, rttp->sec, rttp->usec));
+          p->header.callNumber, p, thisRtt.sec, thisRtt.usec));
 
-    if (rttp->sec == 0 && rttp->usec == 0) {
+    if (clock_IsZero(&thisRtt)) {
         /*
          * The actual round trip time is shorter than the
          * clock_GetTime resolution.  It is most likely 1ms or 100ns.
          * Since we can't tell which at the moment we will assume 1ms.
          */
-        rttp->usec = 1000;
+        thisRtt.usec = 1000;
     }
 
     if (rx_stats_active) {
         MUTEX_ENTER(&rx_stats_mutex);
-        if (clock_Lt(rttp, &rx_stats.minRtt))
-            rx_stats.minRtt = *rttp;
-        if (clock_Gt(rttp, &rx_stats.maxRtt)) {
-            if (rttp->sec > 60) {
+        if (clock_Lt(&thisRtt, &rx_stats.minRtt))
+            rx_stats.minRtt = thisRtt;
+        if (clock_Gt(&thisRtt, &rx_stats.maxRtt)) {
+            if (thisRtt.sec > 60) {
                 MUTEX_EXIT(&rx_stats_mutex);
                 return;                /* somebody set the clock ahead */
             }
-            rx_stats.maxRtt = *rttp;
+            rx_stats.maxRtt = thisRtt;
         }
-        clock_Add(&rx_stats.totalRtt, rttp);
+        clock_Add(&rx_stats.totalRtt, &thisRtt);
         rx_atomic_inc(&rx_stats.nRttSamples);
         MUTEX_EXIT(&rx_stats_mutex);
     }
@@ -6598,7 +6638,7 @@ rxi_ComputeRoundTripTime(struct rx_packet *p,
          * srtt' = srtt + (rtt - srtt)/8
         */
 
-       delta = _8THMSEC(rttp) - peer->rtt;
+       delta = _8THMSEC(&thisRtt) - peer->rtt;
        peer->rtt += (delta >> 3);
 
        /*
@@ -6631,14 +6671,14 @@ rxi_ComputeRoundTripTime(struct rx_packet *p,
         * little, and I set deviance to half the rtt.  In practice,
         * deviance tends to approach something a little less than
         * half the smoothed rtt. */
-       peer->rtt = _8THMSEC(rttp) + 8;
+       peer->rtt = _8THMSEC(&thisRtt) + 8;
        peer->rtt_dev = peer->rtt >> 2; /* rtt/2: they're scaled differently */
     }
-    /* the timeout is RTT + 4*MDEV but no less than rx_minPeerTimeout msec.
+    /* the timeout is RTT + 4*MDEV + rx_minPeerTimeout msec.
      * This is because one end or the other of these connections is usually
      * in a user process, and can be switched and/or swapped out.  So on fast,
      * reliable networks, the timeout would otherwise be too short. */
-    rtt_timeout = MAX(((peer->rtt >> 3) + peer->rtt_dev), rx_minPeerTimeout);
+    rtt_timeout = ((peer->rtt >> 3) + peer->rtt_dev) + rx_minPeerTimeout;
     clock_Zero(&(peer->timeout));
     clock_Addmsec(&(peer->timeout), rtt_timeout);
 
@@ -6646,7 +6686,7 @@ rxi_ComputeRoundTripTime(struct rx_packet *p,
     peer->backedOff = 0;
 
     dpf(("rxi_ComputeRoundTripTime(call=%d packet=%"AFS_PTR_FMT" rtt=%d ms, srtt=%d ms, rtt_dev=%d ms, timeout=%d.%06d sec)\n",
-          p->header.callNumber, p, MSEC(rttp), peer->rtt >> 3, peer->rtt_dev >> 2, (peer->timeout.sec), (peer->timeout.usec)));
+          p->header.callNumber, p, MSEC(&thisRtt), peer->rtt >> 3, peer->rtt_dev >> 2, (peer->timeout.sec), (peer->timeout.usec)));
 }
 
 
@@ -7075,9 +7115,7 @@ rx_DebugOnOff(int on)
 void
 rx_StatsOnOff(int on)
 {
-#ifdef RXDEBUG
     rx_stats_active = on;
-#endif
 }