rxi-newcall-avoid-race-20020115
[openafs.git] / src / rx / rx.c
index 83f7eb4..c5a68ae 100644 (file)
@@ -897,7 +897,12 @@ static void rxi_DestroyConnectionNoLock(conn)
                     * last reply packets */
                    rxevent_Cancel(call->delayedAckEvent, call,
                                   RX_CALL_REFCOUNT_DELAY);
-                   rxi_AckAll((struct rxevent *)0, call, 0);
+                   if (call->state == RX_STATE_PRECALL ||
+                       call->state == RX_STATE_ACTIVE) {
+                       rxi_SendDelayedAck(call->delayedAckEvent, call, 0);
+                   } else {
+                       rxi_AckAll((struct rxevent *)0, call, 0);
+                   }
                }
                MUTEX_EXIT(&call->lock);
            }
@@ -1001,6 +1006,21 @@ struct rx_call *rx_NewCall(conn)
     clock_GetTime(&queueTime);
     AFS_RXGLOCK();
     MUTEX_ENTER(&conn->conn_call_lock);
+
+    /*
+     * Check if there are others waiting for a new call.
+     * If so, let them go first to avoid starving them.
+     * This is a fairly simple scheme, and might not be
+     * a complete solution for large numbers of waiters.
+     */
+    if (conn->makeCallWaiters) {
+#ifdef RX_ENABLE_LOCKS
+       CV_WAIT(&conn->conn_call_cv, &conn->conn_call_lock);
+#else
+       osi_rxSleep(conn);
+#endif
+    }
+
     for (;;) {
        for (i=0; i<RX_MAXCALLS; i++) {
            call = conn->call[i];
@@ -1015,7 +1035,6 @@ struct rx_call *rx_NewCall(conn)
            }
            else {
                call = rxi_NewCall(conn, i);
-               MUTEX_ENTER(&call->lock);
                break;
            }
        }
@@ -1025,12 +1044,24 @@ struct rx_call *rx_NewCall(conn)
        MUTEX_ENTER(&conn->conn_data_lock);
        conn->flags |= RX_CONN_MAKECALL_WAITING;
        MUTEX_EXIT(&conn->conn_data_lock);
+
+       conn->makeCallWaiters++;
 #ifdef RX_ENABLE_LOCKS
        CV_WAIT(&conn->conn_call_cv, &conn->conn_call_lock);
 #else
        osi_rxSleep(conn);
 #endif
+       conn->makeCallWaiters--;
     }
+    /*
+     * Wake up anyone else who might be giving us a chance to
+     * run (see code above that avoids resource starvation).
+     */
+#ifdef RX_ENABLE_LOCKS
+    CV_BROADCAST(&conn->conn_call_cv);
+#else
+    osi_rxWakeup(conn);
+#endif
 
     CALL_HOLD(call, RX_CALL_REFCOUNT_BEGIN);
 
@@ -1918,7 +1949,7 @@ struct rx_service *rxi_FindService(socket, serviceId)
 
 /* 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. */
+ * the caller. Returns the call with mutex locked. */
 struct rx_call *rxi_NewCall(conn, channel)
     register struct rx_connection *conn;
     register int channel;
@@ -2000,7 +2031,6 @@ struct rx_call *rxi_NewCall(conn, channel)
        the call number is valid from the last time this channel was used */
     if (*call->callNumber == 0) *call->callNumber = 1;
 
-    MUTEX_EXIT(&call->lock);
     return call;
 }
 
@@ -2526,7 +2556,6 @@ struct rx_packet *rxi_ReceivePacket(np, socket, host, port, tnop, newcallp)
        }
        if (!call) {
            call = rxi_NewCall(conn, channel);
-           MUTEX_ENTER(&call->lock);
            *call->callNumber = np->header.callNumber;
            call->state = RX_STATE_PRECALL;
            clock_GetTime(&call->queueTime);
@@ -3326,12 +3355,12 @@ struct rx_packet *rxi_ReceiveAckPacket(call, np, istack)
      * set the ack bits in the packets and have rxi_Start remove the packets
      * when it's done transmitting.
      */
-       if (!tp->acked) {
+       if (!(tp->flags & RX_PKTFLAG_ACKED)) {
            newAckCount++;
        }
        if (call->flags & RX_CALL_TQ_BUSY) {
 #ifdef RX_ENABLE_LOCKS
-           tp->acked = 1;
+           tp->flags |= RX_PKTFLAG_ACKED;
            call->flags |= RX_CALL_TQ_SOME_ACKED;
 #else /* RX_ENABLE_LOCKS */
            break;
@@ -3400,17 +3429,17 @@ struct rx_packet *rxi_ReceiveAckPacket(call, np, istack)
         * out of sequence. */
        if (tp->header.seq < first) {
            /* Implicit ack information */
-           if (!tp->acked) {
+           if (!(tp->flags & RX_PKTFLAG_ACKED)) {
                newAckCount++;
            }
-           tp->acked = 1;
+           tp->flags |= RX_PKTFLAG_ACKED;
        }
        else if (tp->header.seq < first + nAcks) {
            /* Explicit ack information:  set it in the packet appropriately */
            if (ap->acks[tp->header.seq - first] == RX_ACK_TYPE_ACK) {
-               if (!tp->acked) {
+               if (!(tp->flags & RX_PKTFLAG_ACKED)) {
                    newAckCount++;
-                   tp->acked = 1;
+                   tp->flags |= RX_PKTFLAG_ACKED;
                }
                if (missing) {
                    nNacked++;
@@ -3418,12 +3447,12 @@ struct rx_packet *rxi_ReceiveAckPacket(call, np, istack)
                    call->nSoftAcked++;
                }
            } else {
-               tp->acked = 0;
+               tp->flags &= ~RX_PKTFLAG_ACKED;
                missing = 1;
            }
        }
        else {
-           tp->acked = 0;
+           tp->flags &= ~RX_PKTFLAG_ACKED;
            missing = 1;
        }
 
@@ -3432,7 +3461,7 @@ struct rx_packet *rxi_ReceiveAckPacket(call, np, istack)
         * ie, this should readjust the retransmit timer for all outstanding 
         * packets...  So we don't just retransmit when we should know better*/
 
-       if (!tp->acked && !clock_IsZero(&tp->retryTime)) {
+       if (!(tp->flags & RX_PKTFLAG_ACKED) && !clock_IsZero(&tp->retryTime)) {
          tp->retryTime = tp->timeSent;
          clock_Add(&tp->retryTime, &peer->timeout);
          /* shift by eight because one quarter-sec ~ 256 milliseconds */
@@ -3460,7 +3489,7 @@ struct rx_packet *rxi_ReceiveAckPacket(call, np, istack)
 
     /* if the ack packet has a receivelen field hanging off it,
      * update our state */
-    if ( np->length >= rx_AckDataSize(ap->nAcks) +sizeof(afs_int32)) {
+    if ( np->length >= rx_AckDataSize(ap->nAcks) + 2*sizeof(afs_int32)) {
       afs_uint32 tSize;
 
       /* If the ack packet has a "recommended" size that is less than 
@@ -3625,10 +3654,10 @@ struct rx_packet *rxi_ReceiveAckPacket(call, np, istack)
         * so we will retransmit as soon as the window permits*/
        for(acked = 0, queue_ScanBackwards(&call->tq, tp, nxp, rx_packet)) {
            if (acked) {
-               if (!tp->acked) {
+               if (!(tp->flags & RX_PKTFLAG_ACKED)) {
                    clock_Zero(&tp->retryTime);
                }
-           } else if (tp->acked) {
+           } else if (tp->flags & RX_PKTFLAG_ACKED) {
                acked = 1;
            }
        }
@@ -3938,7 +3967,7 @@ static void rxi_SetAcksInTransmitQueue(call)
      for (queue_Scan(&call->tq, p, tp, rx_packet)) {
         if (!p) 
             break;
-        p->acked = 1;
+        p->flags |= RX_PKTFLAG_ACKED;
         someAcked = 1;
      }
      if (someAcked) {
@@ -3975,7 +4004,7 @@ void rxi_ClearTransmitQueue(call, force)
        for (queue_Scan(&call->tq, p, tp, rx_packet)) {
          if (!p) 
             break;
-         p->acked = 1;
+         p->flags |= RX_PKTFLAG_ACKED;
          someAcked = 1;
        }
        if (someAcked) {
@@ -4628,7 +4657,7 @@ static void rxi_SendXmitList(call, list, len, istack, now, retryTime, resending)
        /* Does the current packet force us to flush the current list? */
        if (cnt > 0
            && (list[i]->header.serial
-               || list[i]->acked
+               || (list[i]->flags & RX_PKTFLAG_ACKED)
                || list[i]->length > RX_JUMBOBUFFERSIZE)) {
            if (lastCnt > 0) {
                rxi_SendList(call, lastP, lastCnt, istack, 1, now, retryTime, resending);
@@ -4644,7 +4673,7 @@ static void rxi_SendXmitList(call, list, len, istack, now, retryTime, resending)
        }
        /* 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]->acked) {
+       if (!(list[i]->flags & RX_PKTFLAG_ACKED)) {
            cnt++;
            /* Do we need to flush the list? */
            if (cnt >= (int)peer->maxDgramPackets
@@ -4684,7 +4713,7 @@ static void rxi_SendXmitList(call, list, len, istack, now, retryTime, resending)
         * 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]->acked) {
+       if (cnt > 0 && !(listP[0]->flags & RX_PKTFLAG_ACKED)) {
            morePackets = 1;
        }
        if (lastCnt > 0) {
@@ -4789,7 +4818,7 @@ void rxi_Start(event, call, istack)
         * than recovery rates.
         */
        for(queue_Scan(&call->tq, p, nxp, rx_packet)) {
-           if (!p->acked) {
+           if (!(p->flags & RX_PKTFLAG_ACKED)) {
                clock_Zero(&p->retryTime);
            }
        }
@@ -4852,14 +4881,14 @@ void rxi_Start(event, call, istack)
             /* Only send one packet during fast recovery */
             break;
          }
-         if ((p->header.flags == RX_FREE_PACKET) ||
+         if ((p->flags & RX_PKTFLAG_FREE) ||
              (!queue_IsEnd(&call->tq, nxp)
-              && (nxp->header.flags == RX_FREE_PACKET)) ||
+              && (nxp->flags & RX_PKTFLAG_FREE)) ||
              (p == (struct rx_packet *)&rx_freePacketQueue) ||
              (nxp == (struct rx_packet *)&rx_freePacketQueue)) {
              osi_Panic("rxi_Start: xmit queue clobbered");
          }
-         if (p->acked) {
+         if (p->flags & RX_PKTFLAG_ACKED) {
            MUTEX_ENTER(&rx_stats_mutex);
            rx_stats.ignoreAckedPacket++;
            MUTEX_EXIT(&rx_stats_mutex);
@@ -4942,7 +4971,7 @@ void rxi_Start(event, call, istack)
             * the transmit queue.
             */
            for (missing = 0, queue_Scan(&call->tq, p, nxp, rx_packet)) {
-               if (p->header.seq < call->tfirst && p->acked) {
+               if (p->header.seq < call->tfirst && (p->flags & RX_PKTFLAG_ACKED)) {
                    queue_Remove(p);
                    rxi_FreePacket(p);
                }
@@ -4978,7 +5007,7 @@ void rxi_Start(event, call, istack)
                break;
            }
 
-           if (!p->acked && !clock_IsZero(&p->retryTime)) {
+           if (!(p->flags & RX_PKTFLAG_ACKED) && !clock_IsZero(&p->retryTime)) {
                haveEvent = 1;
                retryTime = p->retryTime;
                break;