* 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);
}
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];
}
else {
call = rxi_NewCall(conn, i);
- MUTEX_ENTER(&call->lock);
break;
}
}
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);
/* 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;
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;
}
}
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);
* 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;
* 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++;
call->nSoftAcked++;
}
} else {
- tp->acked = 0;
+ tp->flags &= ~RX_PKTFLAG_ACKED;
missing = 1;
}
}
else {
- tp->acked = 0;
+ tp->flags &= ~RX_PKTFLAG_ACKED;
missing = 1;
}
* 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 */
/* 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
* 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;
}
}
for (queue_Scan(&call->tq, p, tp, rx_packet)) {
if (!p)
break;
- p->acked = 1;
+ p->flags |= RX_PKTFLAG_ACKED;
someAcked = 1;
}
if (someAcked) {
for (queue_Scan(&call->tq, p, tp, rx_packet)) {
if (!p)
break;
- p->acked = 1;
+ p->flags |= RX_PKTFLAG_ACKED;
someAcked = 1;
}
if (someAcked) {
/* 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);
}
/* 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
* 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) {
* 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);
}
}
/* 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);
* 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);
}
break;
}
- if (!p->acked && !clock_IsZero(&p->retryTime)) {
+ if (!(p->flags & RX_PKTFLAG_ACKED) && !clock_IsZero(&p->retryTime)) {
haveEvent = 1;
retryTime = p->retryTime;
break;