xref: /csrg-svn/sys/netiso/tp_subr.c (revision 50236)
149268Sbostic /*-
249268Sbostic  * Copyright (c) 1991 The Regents of the University of California.
349268Sbostic  * All rights reserved.
449268Sbostic  *
549268Sbostic  * %sccs.include.redist.c%
649268Sbostic  *
7*50236Ssklower  *	@(#)tp_subr.c	7.9 (Berkeley) 06/27/91
849268Sbostic  */
949268Sbostic 
1036413Ssklower /***********************************************************
1136413Ssklower 		Copyright IBM Corporation 1987
1236413Ssklower 
1336413Ssklower                       All Rights Reserved
1436413Ssklower 
1536413Ssklower Permission to use, copy, modify, and distribute this software and its
1636413Ssklower documentation for any purpose and without fee is hereby granted,
1736413Ssklower provided that the above copyright notice appear in all copies and that
1836413Ssklower both that copyright notice and this permission notice appear in
1936413Ssklower supporting documentation, and that the name of IBM not be
2036413Ssklower used in advertising or publicity pertaining to distribution of the
2136413Ssklower software without specific, written prior permission.
2236413Ssklower 
2336413Ssklower IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
2436413Ssklower ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
2536413Ssklower IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
2636413Ssklower ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
2736413Ssklower WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2836413Ssklower ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2936413Ssklower SOFTWARE.
3036413Ssklower 
3136413Ssklower ******************************************************************/
3236413Ssklower 
3336413Ssklower /*
3436413Ssklower  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
3536413Ssklower  */
3636413Ssklower /*
3736413Ssklower  * ARGO TP
3836413Ssklower  *
3936413Ssklower  * $Header: tp_subr.c,v 5.3 88/11/18 17:28:43 nhall Exp $
4036413Ssklower  * $Source: /usr/argo/sys/netiso/RCS/tp_subr.c,v $
4136413Ssklower  *
4236413Ssklower  * The main work of data transfer is done here.
4336413Ssklower  * These routines are called from tp.trans.
4436413Ssklower  * They include the routines that check the validity of acks and Xacks,
4536413Ssklower  * (tp_goodack() and tp_goodXack() )
4636413Ssklower  * take packets from socket buffers and send them (tp_send()),
4736413Ssklower  * drop the data from the socket buffers (tp_sbdrop()),
4836413Ssklower  * and put incoming packet data into socket buffers (tp_stash()).
4936413Ssklower  */
5036413Ssklower 
5136413Ssklower #include "param.h"
5236413Ssklower #include "mbuf.h"
5336413Ssklower #include "socket.h"
5436413Ssklower #include "socketvar.h"
5536413Ssklower #include "protosw.h"
5636413Ssklower #include "errno.h"
5736413Ssklower #include "types.h"
5836413Ssklower #include "time.h"
5936413Ssklower 
6037469Ssklower #include "tp_ip.h"
6137469Ssklower #include "iso.h"
6237469Ssklower #include "argo_debug.h"
6337469Ssklower #include "tp_timer.h"
6437469Ssklower #include "tp_param.h"
6537469Ssklower #include "tp_stat.h"
6637469Ssklower #include "tp_pcb.h"
6737469Ssklower #include "tp_tpdu.h"
6837469Ssklower #include "tp_trace.h"
6937469Ssklower #include "tp_meas.h"
7037469Ssklower #include "tp_seq.h"
7136413Ssklower 
7236413Ssklower int 		tp_emit();
7336413Ssklower static void tp_sbdrop();
7436413Ssklower 
7536413Ssklower #define SMOOTH(newtype, alpha, old, new) \
7636413Ssklower 	(newtype) (((new - old)>>alpha) + (old))
7736413Ssklower 
7836413Ssklower #define ABS(type, val) \
7936413Ssklower 	(type) (((int)(val)<0)?-(val):(val))
8036413Ssklower 
8136413Ssklower #define TP_MAKE_RTC( Xreg, Xseq, Xeot, Xdata, Xlen, Xretval, Xtype) \
8236413Ssklower { 	struct mbuf *xxn;\
8336413Ssklower 	MGET(xxn, M_DONTWAIT, Xtype);\
8436413Ssklower 	if( xxn == (struct mbuf *)0 ) {\
8536413Ssklower 		printf("MAKE RTC FAILED: ENOBUFS\n");\
8636413Ssklower 		return (int)Xretval;\
8736413Ssklower 	}\
8836413Ssklower 	xxn->m_act=MNULL;\
8936413Ssklower 	Xreg = mtod(xxn, struct tp_rtc *);\
9036413Ssklower 	if( Xreg == (struct tp_rtc *)0 ) {\
9136413Ssklower 		return (int)Xretval;\
9236413Ssklower 	}\
9336413Ssklower 	Xreg->tprt_eot = Xeot;\
9436413Ssklower 	Xreg->tprt_seq = Xseq;\
9536413Ssklower 	Xreg->tprt_data = Xdata;\
9636413Ssklower 	Xreg->tprt_octets = Xlen;\
9736413Ssklower }
9836413Ssklower 
9936413Ssklower 
10036413Ssklower /*
10136413Ssklower  * CALLED FROM:
10236413Ssklower  *	tp.trans, when an XAK arrives
10336413Ssklower  * FUNCTION and ARGUMENTS:
10436413Ssklower  * 	Determines if the sequence number (seq) from the XAK
10536413Ssklower  * 	acks anything new.  If so, drop the appropriate tpdu
10636413Ssklower  * 	from the XPD send queue.
10736413Ssklower  * RETURN VALUE:
10836413Ssklower  * 	Returns 1 if it did this, 0 if the ack caused no action.
10936413Ssklower  */
11036413Ssklower int
11136413Ssklower tp_goodXack(tpcb, seq)
11236413Ssklower 	struct tp_pcb	*tpcb;
11336413Ssklower 	SeqNum 			seq;
11436413Ssklower {
11536413Ssklower 
11636413Ssklower 	IFTRACE(D_XPD)
11736413Ssklower 		tptraceTPCB(TPPTgotXack,
11836413Ssklower 			seq, tpcb->tp_Xuna, tpcb->tp_Xsndnxt, tpcb->tp_sndhiwat,
11936413Ssklower 			tpcb->tp_snduna);
12036413Ssklower 	ENDTRACE
12136413Ssklower 
12236413Ssklower 	if ( seq == tpcb->tp_Xuna ) {
12336413Ssklower 			tpcb->tp_Xuna = tpcb->tp_Xsndnxt;
12436413Ssklower 
12536413Ssklower 			/* DROP 1 packet from the Xsnd socket buf - just so happens
12636413Ssklower 			 * that only one packet can be there at any time
12736413Ssklower 			 * so drop the whole thing.  If you allow > 1 packet
12836413Ssklower 			 * the socket buffer, then you'll have to keep
12936413Ssklower 			 * track of how many characters went w/ each XPD tpdu, so this
13036413Ssklower 			 * will get messier
13136413Ssklower 			 */
13236413Ssklower 			IFDEBUG(D_XPD)
13336413Ssklower 				dump_mbuf(tpcb->tp_Xsnd.sb_mb,
13436413Ssklower 					"tp_goodXack Xsnd before sbdrop");
13536413Ssklower 			ENDDEBUG
13636413Ssklower 
13736413Ssklower 			IFTRACE(D_XPD)
13836413Ssklower 				tptraceTPCB(TPPTmisc,
13936413Ssklower 					"goodXack: dropping cc ",
14036413Ssklower 					(int)(tpcb->tp_Xsnd.sb_cc),
14136413Ssklower 					0,0,0);
14236413Ssklower 			ENDTRACE
14336413Ssklower 			sbdrop( &tpcb->tp_Xsnd, (int)(tpcb->tp_Xsnd.sb_cc));
14439922Ssklower 			CONG_ACK(tpcb, seq);
14536413Ssklower 			return 1;
14636413Ssklower 	}
14736413Ssklower 	return 0;
14836413Ssklower }
14936413Ssklower 
15036413Ssklower /*
15136413Ssklower  * CALLED FROM:
15236413Ssklower  *  tp_good_ack()
15336413Ssklower  * FUNCTION and ARGUMENTS:
15436413Ssklower  *  updates
15536413Ssklower  *  smoothed average round trip time (base_rtt)
15636413Ssklower  *  roundtrip time variance (base_rtv) - actually deviation, not variance
15736413Ssklower  *  given the new value (diff)
15836413Ssklower  * RETURN VALUE:
15936413Ssklower  * void
16036413Ssklower  */
16136413Ssklower 
16236413Ssklower void
16336413Ssklower tp_rtt_rtv( base_rtt, base_rtv, newmeas )
16436413Ssklower 	struct 	timeval *base_rtt, *base_rtv, *newmeas;
16536413Ssklower {
16636413Ssklower 	/* update  rt variance (really just the deviation):
16736413Ssklower 	 * 	rtv.smooth_ave =  SMOOTH( | oldrtt.smooth_avg - rtt.this_instance | )
16836413Ssklower 	 */
16936413Ssklower 	base_rtv->tv_sec =
17036413Ssklower 		SMOOTH( long,  TP_RTV_ALPHA, base_rtv->tv_sec,
17144948Ssklower 			ABS( long, base_rtt->tv_sec - newmeas->tv_sec ));
17236413Ssklower 	base_rtv->tv_usec =
17336413Ssklower 		SMOOTH( long,  TP_RTV_ALPHA, base_rtv->tv_usec,
17444948Ssklower 			ABS(long, base_rtt->tv_usec - newmeas->tv_usec ));
17536413Ssklower 
17636413Ssklower 	/* update smoothed average rtt */
17736413Ssklower 	base_rtt->tv_sec =
17836413Ssklower 		SMOOTH( long,  TP_RTT_ALPHA, base_rtt->tv_sec, newmeas->tv_sec);
17936413Ssklower 	base_rtt->tv_usec =
18036413Ssklower 		SMOOTH( long,  TP_RTT_ALPHA, base_rtt->tv_usec, newmeas->tv_usec);
18136413Ssklower 
18236413Ssklower }
18336413Ssklower 
18436413Ssklower /*
18536413Ssklower  * CALLED FROM:
18636413Ssklower  *  tp.trans when an AK arrives
18736413Ssklower  * FUNCTION and ARGUMENTS:
18836413Ssklower  * 	Given (cdt), the credit from the AK tpdu, and
18936413Ssklower  *	(seq), the sequence number from the AK tpdu,
19036413Ssklower  *  tp_goodack() determines if the AK acknowledges something in the send
19136413Ssklower  * 	window, and if so, drops the appropriate packets from the retransmission
19236413Ssklower  *  list, computes the round trip time, and updates the retransmission timer
19336413Ssklower  *  based on the new smoothed round trip time.
19436413Ssklower  * RETURN VALUE:
19536413Ssklower  * 	Returns 1 if
19636413Ssklower  * 	EITHER it actually acked something heretofore unacknowledged
19736413Ssklower  * 	OR no news but the credit should be processed.
19836413Ssklower  * 	If something heretofore unacked was acked with this sequence number,
19936413Ssklower  * 	the appropriate tpdus are dropped from the retransmission control list,
20036413Ssklower  * 	by calling tp_sbdrop().
20136413Ssklower  * 	No need to see the tpdu itself.
20236413Ssklower  */
20336413Ssklower int
20436413Ssklower tp_goodack(tpcb, cdt, seq, subseq)
20536413Ssklower 	register struct tp_pcb	*tpcb;
20636413Ssklower 	u_int					cdt;
20736413Ssklower 	register SeqNum			seq, subseq;
20836413Ssklower {
20936413Ssklower 	int 	old_fcredit = tpcb->tp_fcredit;
21036413Ssklower 	int 	bang = 0; 	/* bang --> ack for something heretofore unacked */
21136413Ssklower 
21236413Ssklower 	IFDEBUG(D_ACKRECV)
21336413Ssklower 		printf("goodack seq 0x%x cdt 0x%x snduna 0x%x sndhiwat 0x%x\n",
21436413Ssklower 			seq, cdt, tpcb->tp_snduna, tpcb->tp_sndhiwat);
21536413Ssklower 	ENDDEBUG
21636413Ssklower 	IFTRACE(D_ACKRECV)
21736413Ssklower 		tptraceTPCB(TPPTgotack,
21836413Ssklower 			seq,cdt, tpcb->tp_snduna,tpcb->tp_sndhiwat,subseq);
21936413Ssklower 	ENDTRACE
22036413Ssklower 
22136413Ssklower 	IFPERF(tpcb)
22237469Ssklower 		tpmeas(tpcb->tp_lref, TPtime_ack_rcvd, (struct timeval *)0, seq, 0, 0);
22336413Ssklower 	ENDPERF
22436413Ssklower 
22536413Ssklower 	if ( subseq != 0 && (subseq <= tpcb->tp_r_subseq) ) {
22636413Ssklower 		/* discard the ack */
22736413Ssklower 		IFTRACE(D_ACKRECV)
22836413Ssklower 			tptraceTPCB(TPPTmisc, "goodack discard : subseq tp_r_subseq",
22936413Ssklower 				subseq, tpcb->tp_r_subseq, 0, 0);
23036413Ssklower 		ENDTRACE
23136413Ssklower 		return 0;
23236413Ssklower 	} else {
23336413Ssklower 		tpcb->tp_r_subseq = subseq;
23436413Ssklower 	}
23536413Ssklower 
23636413Ssklower 	if ( IN_SWINDOW(tpcb, seq,
23736413Ssklower 			tpcb->tp_snduna, SEQ(tpcb, tpcb->tp_sndhiwat+1)) ) {
23836413Ssklower 
23936413Ssklower 		IFDEBUG(D_XPD)
24036413Ssklower 			dump_mbuf(tpcb->tp_sock->so_snd.sb_mb,
24136413Ssklower 				"tp_goodack snd before sbdrop");
24236413Ssklower 		ENDDEBUG
24336413Ssklower 		tp_sbdrop(tpcb, SEQ_SUB(tpcb, seq, 1) );
24436413Ssklower 
24536413Ssklower 		/* increase congestion window but don't let it get too big */
24636413Ssklower 		{
24736413Ssklower 			register int maxcdt = tpcb->tp_xtd_format?0xffff:0xf;
24839922Ssklower 			CONG_ACK(tpcb, seq);
24936413Ssklower 		}
25036413Ssklower 
25136413Ssklower 		/* Compute smoothed round trip time.
25236413Ssklower 		 * Only measure rtt for tp_snduna if tp_snduna was among
25339922Ssklower 		 * the last TP_RTT_NUM seq numbers sent, and if the data
25439922Ssklower 		 * were not retransmitted.
25536413Ssklower 		 */
25636413Ssklower 		if (SEQ_GEQ(tpcb, tpcb->tp_snduna,
25739922Ssklower 			SEQ(tpcb, tpcb->tp_sndhiwat - TP_RTT_NUM))
25839922Ssklower 			&& SEQ_GT(tpcb, seq, SEQ_ADD(tpcb, tpcb->tp_retrans_hiwat, 1))) {
25936413Ssklower 
26036413Ssklower 			struct timeval *t = &tpcb->tp_rttemit[tpcb->tp_snduna & TP_RTT_NUM];
26136413Ssklower 			struct timeval x;
26236413Ssklower 
26336413Ssklower 			GET_TIME_SINCE(t, &x);
26436413Ssklower 
26536413Ssklower 			tp_rtt_rtv( &(tpcb->tp_rtt), &(tpcb->tp_rtv), &x );
26636413Ssklower 
26736413Ssklower 			{	/* update the global rtt, rtv stats */
26836413Ssklower 				register int i =
26936413Ssklower 				   (int) tpcb->tp_flags & (TPF_PEER_ON_SAMENET | TPF_NLQOS_PDN);
27036413Ssklower 				tp_rtt_rtv( &(tp_stat.ts_rtt[i]), &(tp_stat.ts_rtv[i]), &x );
27136413Ssklower 
27236413Ssklower 				IFTRACE(D_RTT)
27336413Ssklower 					tptraceTPCB(TPPTmisc, "Global rtt, rtv: i", i, 0, 0, 0);
27436413Ssklower 				ENDTRACE
27536413Ssklower 			}
27636413Ssklower 
27736413Ssklower 			IFTRACE(D_RTT)
27836413Ssklower 				tptraceTPCB(TPPTmisc,
27936413Ssklower 				"Smoothed rtt: tp_snduna, (time.sec, time.usec), peer_acktime",
28036413Ssklower 				tpcb->tp_snduna, time.tv_sec, time.tv_usec,
28136413Ssklower 					tpcb->tp_peer_acktime);
28236413Ssklower 
28336413Ssklower 				tptraceTPCB(TPPTmisc,
28436413Ssklower 				"(secs): emittime diff(x) rtt, rtv",
28536413Ssklower 					t->tv_sec,
28636413Ssklower 					x.tv_sec,
28736413Ssklower 					tpcb->tp_rtt.tv_sec,
28836413Ssklower 					tpcb->tp_rtv.tv_sec);
28936413Ssklower 				tptraceTPCB(TPPTmisc,
29036413Ssklower 				"(usecs): emittime diff(x) rtt rtv",
29136413Ssklower 					t->tv_usec,
29236413Ssklower 					x.tv_usec,
29336413Ssklower 					tpcb->tp_rtt.tv_usec,
29436413Ssklower 					tpcb->tp_rtv.tv_usec);
29536413Ssklower 			ENDTRACE
29636413Ssklower 
29736413Ssklower 			{
29836413Ssklower 				/* Update data retransmission timer based on the smoothed
29936413Ssklower 				 * round trip time, peer ack time, and the pseudo-arbitrary
30036413Ssklower 				 * number 4.
30136413Ssklower 				 * new ticks: avg rtt + 2*dev
30236413Ssklower 				 * rtt, rtv are in microsecs, and ticks are 500 ms
30336413Ssklower 				 * so 1 tick = 500*1000 us = 500000 us
30436413Ssklower 				 * so ticks = (rtt + 2 rtv)/500000
30536413Ssklower 				 * with ticks no les than peer ack time and no less than 4
30636413Ssklower 				 */
30736413Ssklower 
30836413Ssklower 				int rtt = tpcb->tp_rtt.tv_usec +
30936413Ssklower 					tpcb->tp_rtt.tv_sec*1000000;
31036413Ssklower 				int rtv = tpcb->tp_rtv.tv_usec +
31136413Ssklower 					tpcb->tp_rtv.tv_sec*1000000;
31236413Ssklower 
31336413Ssklower 				IFTRACE(D_RTT)
31436413Ssklower 					tptraceTPCB(TPPTmisc, "oldticks ,rtv, rtt, newticks",
31536413Ssklower 						tpcb->tp_dt_ticks,
31636413Ssklower 						rtv, rtt,
31736413Ssklower 						(rtt/500000 + (2 * rtv)/500000));
31836413Ssklower 				ENDTRACE
31936413Ssklower 				tpcb->tp_dt_ticks = (rtt+ (2 * rtv))/500000;
32036413Ssklower 				tpcb->tp_dt_ticks = MAX( tpcb->tp_dt_ticks,
32136413Ssklower 					tpcb->tp_peer_acktime);
32236413Ssklower 				tpcb->tp_dt_ticks = MAX( tpcb->tp_dt_ticks,  4);
32336413Ssklower 			}
32436413Ssklower 		}
32536413Ssklower 		tpcb->tp_snduna = seq;
32639922Ssklower 		tpcb->tp_retrans = tpcb->tp_Nretrans; /* CE_BIT */
32736413Ssklower 
32836413Ssklower 		bang++;
32936413Ssklower 	}
33036413Ssklower 
33136413Ssklower 	if( cdt != 0 && old_fcredit == 0 ) {
33236413Ssklower 		tpcb->tp_sendfcc = 1;
33336413Ssklower 	}
33436413Ssklower 	if( cdt == 0 && old_fcredit != 0 ) {
33536413Ssklower 		IncStat(ts_zfcdt);
33636413Ssklower 	}
33736413Ssklower 	tpcb->tp_fcredit = cdt;
33836413Ssklower 
33936413Ssklower 	IFDEBUG(D_ACKRECV)
34036413Ssklower 		printf("goodack returning 0x%x, bang 0x%x cdt 0x%x old_fcredit 0x%x\n",
34136413Ssklower 			(bang || (old_fcredit < cdt) ), bang, cdt, old_fcredit );
34236413Ssklower 	ENDDEBUG
34336413Ssklower 
34436413Ssklower 	return (bang || (old_fcredit < cdt)) ;
34536413Ssklower }
34636413Ssklower 
34736413Ssklower /*
34836413Ssklower  * CALLED FROM:
34936413Ssklower  *  tp_goodack()
35036413Ssklower  * FUNCTION and ARGUMENTS:
35136413Ssklower  *  drops everything up TO and INCLUDING seq # (seq)
35236413Ssklower  *  from the retransmission queue.
35336413Ssklower  */
35436413Ssklower static void
35536413Ssklower tp_sbdrop(tpcb, seq)
35636413Ssklower 	struct 	tp_pcb 			*tpcb;
35736413Ssklower 	SeqNum					seq;
35836413Ssklower {
35936413Ssklower 	register struct tp_rtc	*s = tpcb->tp_snduna_rtc;
36036413Ssklower 
36136413Ssklower 	IFDEBUG(D_ACKRECV)
36236413Ssklower 		printf("tp_sbdrop up through seq 0x%x\n", seq);
36336413Ssklower 	ENDDEBUG
36436413Ssklower 	while (s != (struct tp_rtc *)0 && (SEQ_LEQ(tpcb, s->tprt_seq, seq))) {
36536413Ssklower 		m_freem( s->tprt_data );
36636413Ssklower 		tpcb->tp_snduna_rtc = s->tprt_next;
36736413Ssklower 		(void) m_free( dtom( s ) );
36836413Ssklower 		s = tpcb->tp_snduna_rtc;
36936413Ssklower 	}
37036413Ssklower 	if(tpcb->tp_snduna_rtc == (struct tp_rtc *)0)
37136413Ssklower 		tpcb->tp_sndhiwat_rtc = (struct tp_rtc *) 0;
37236413Ssklower 
37336413Ssklower }
37436413Ssklower 
37536413Ssklower /*
37636413Ssklower  * CALLED FROM:
37736413Ssklower  * 	tp.trans on user send request, arrival of AK and arrival of XAK
37836413Ssklower  * FUNCTION and ARGUMENTS:
37936413Ssklower  * 	Emits tpdus starting at sequence number (lowseq).
38036413Ssklower  * 	Emits until a) runs out of data, or  b) runs into an XPD mark, or
38136413Ssklower  * 			c) it hits seq number (highseq)
38236413Ssklower  * 	Removes the octets from the front of the socket buffer
38336413Ssklower  * 	and repackages them in one mbuf chain per tpdu.
38436413Ssklower  * 	Moves the mbuf chain to the doubly linked list that runs from
38536413Ssklower  * 	tpcb->tp_sndhiwat_rtc to tpcb->tp_snduna_rtc.
38636413Ssklower  *
38736413Ssklower  * 	Creates tpdus that are no larger than <tpcb->tp_l_tpdusize - headersize>,
38836413Ssklower  *
38936413Ssklower  * 	If you want XPD to buffer > 1 du per socket buffer, you can
39036413Ssklower  * 	modifiy this to issue XPD tpdus also, but then it'll have
39136413Ssklower  * 	to take some argument(s) to distinguish between the type of DU to
39236413Ssklower  * 	hand tp_emit, the socket buffer from which to get the data, and
39336413Ssklower  * 	the chain of tp_rtc structures on which to put the data sent.
39436413Ssklower  *
39536413Ssklower  * 	When something is sent for the first time, its time-of-send
39636413Ssklower  * 	is stashed (the last RTT_NUM of them are stashed).  When the
39736413Ssklower  * 	ack arrives, the smoothed round-trip time is figured using this value.
39836413Ssklower  * RETURN VALUE:
39936413Ssklower  * 	the highest seq # sent successfully.
40036413Ssklower  */
40136413Ssklower tp_send(tpcb)
40236413Ssklower 	register struct tp_pcb	*tpcb;
40336413Ssklower {
40436413Ssklower 	register int			len;
40536413Ssklower 	register struct mbuf	*m; /* the one we're inspecting now */
40636413Ssklower 	struct mbuf				*mb;/* beginning of this tpdu */
40736413Ssklower 	struct mbuf 			*nextrecord; /* NOT next tpdu but next sb record */
40836413Ssklower 	struct 	sockbuf			*sb = &tpcb->tp_sock->so_snd;
40936413Ssklower 	int						maxsize = tpcb->tp_l_tpdusize
41036413Ssklower 										- tp_headersize(DT_TPDU_type, tpcb)
41136413Ssklower 										- (tpcb->tp_use_checksum?4:0) ;
41236413Ssklower 	unsigned int			eotsdu_reached=0;
41336413Ssklower 	SeqNum					lowseq, highseq ;
41436413Ssklower 	SeqNum					lowsave;
41536413Ssklower #ifdef TP_PERF_MEAS
416*50236Ssklower 
41736413Ssklower 	struct timeval 			send_start_time;
418*50236Ssklower 	IFPERF(tpcb)
419*50236Ssklower 		GET_CUR_TIME(&send_start_time);
420*50236Ssklower 	ENDPERF
42136413Ssklower #endif TP_PERF_MEAS
42236413Ssklower 
42336413Ssklower 	lowsave =  lowseq = SEQ(tpcb, tpcb->tp_sndhiwat + 1);
42436413Ssklower 
42536413Ssklower 	ASSERT( tpcb->tp_cong_win > 0 && tpcb->tp_cong_win < 0xffff);
42636413Ssklower 
42736413Ssklower 	if( tpcb->tp_rx_strat & TPRX_USE_CW ) {
42836413Ssklower 			/*first hiseq is temp vbl*/
42936413Ssklower 		highseq = MIN(tpcb->tp_fcredit, tpcb->tp_cong_win);
43036413Ssklower 	} else {
43136413Ssklower 		highseq = tpcb->tp_fcredit;
43236413Ssklower 	}
43336413Ssklower 	highseq = SEQ(tpcb, tpcb->tp_snduna + highseq);
43436413Ssklower 
43536413Ssklower 	SEQ_DEC(tpcb, highseq);
43636413Ssklower 
43736413Ssklower 	IFDEBUG(D_DATA)
43836413Ssklower 		printf(
43936413Ssklower 			"tp_send enter tpcb 0x%x l %d -> h %d\ndump of sb_mb:\n",
44036413Ssklower 			tpcb, lowseq, highseq);
44136413Ssklower 		dump_mbuf(sb->sb_mb, "sb_mb:");
44236413Ssklower 	ENDDEBUG
44336413Ssklower 	IFTRACE(D_DATA)
44436413Ssklower 		tptraceTPCB( TPPTmisc, "tp_send lowsave sndhiwat snduna",
44536413Ssklower 			lowsave, tpcb->tp_sndhiwat,  tpcb->tp_snduna, 0);
44636413Ssklower 		tptraceTPCB( TPPTmisc, "tp_send low high fcredit congwin",
44736413Ssklower 			lowseq, highseq, tpcb->tp_fcredit,  tpcb->tp_cong_win);
44836413Ssklower 	ENDTRACE
44936413Ssklower 
45036413Ssklower 
45136413Ssklower 	if	( SEQ_GT(tpcb, lowseq, highseq) )
45236413Ssklower 			return ; /* don't send, don't change hiwat, don't set timers */
45336413Ssklower 
45436413Ssklower 	ASSERT( SEQ_LEQ(tpcb, lowseq, highseq) );
45536413Ssklower 	SEQ_DEC(tpcb, lowseq);
45636413Ssklower 
45736413Ssklower 	IFTRACE(D_DATA)
45836413Ssklower 		tptraceTPCB( TPPTmisc, "tp_send 2 low high fcredit congwin",
45936413Ssklower 			lowseq, highseq, tpcb->tp_fcredit,  tpcb->tp_cong_win);
46036413Ssklower 	ENDTRACE
46136413Ssklower 
46238841Ssklower 	while ((SEQ_LT(tpcb, lowseq, highseq)) && (mb = m = sb->sb_mb)) {
46337469Ssklower 		if (tpcb->tp_Xsnd.sb_mb) {
46436413Ssklower 			IFTRACE(D_XPD)
46536413Ssklower 				tptraceTPCB( TPPTmisc,
46636413Ssklower 					"tp_send XPD mark low high tpcb.Xuna",
46738841Ssklower 					lowseq, highseq, tpcb->tp_Xsnd.sb_mb, 0);
46836413Ssklower 			ENDTRACE
46937469Ssklower 			/* stop sending here because there are unacked XPD which were
47037469Ssklower 			 * given to us before the next data were.
47137469Ssklower 			 */
47237469Ssklower 			IncStat(ts_xpd_intheway);
47337469Ssklower 			break;
47436413Ssklower 		}
47536413Ssklower 		eotsdu_reached = 0;
47636413Ssklower 		nextrecord = m->m_act;
47737469Ssklower 		for (len = 0; m; m = m->m_next) {
47836413Ssklower 			len += m->m_len;
47937469Ssklower 			if (m->m_flags & M_EOR)
48036413Ssklower 				eotsdu_reached = 1;
48136413Ssklower 			sbfree(sb, m); /* reduce counts in socket buffer */
48236413Ssklower 		}
48338841Ssklower 		sb->sb_mb = nextrecord;
48437469Ssklower 		IFTRACE(D_STASH)
48537469Ssklower 			tptraceTPCB(TPPTmisc, "tp_send whole mbuf: m_len len maxsize",
48637469Ssklower 				 0, mb->m_len, len, maxsize);
48737469Ssklower 		ENDTRACE
48836413Ssklower 
48936413Ssklower 		if ( len == 0 && !eotsdu_reached) {
49036413Ssklower 			/* THIS SHOULD NEVER HAPPEN! */
49136413Ssklower 			ASSERT( 0 );
49236413Ssklower 			goto done;
49336413Ssklower 		}
49436413Ssklower 
49536413Ssklower 		/* If we arrive here one of the following holds:
49636413Ssklower 		 * 1. We have exactly <maxsize> octets of whole mbufs,
49736413Ssklower 		 * 2. We accumulated <maxsize> octets using partial mbufs,
49836413Ssklower 		 * 3. We found an TPMT_EOT or an XPD mark
49936413Ssklower 		 * 4. We hit the end of a chain through m_next.
50036413Ssklower 		 *    In this case, we'd LIKE to continue with the next record,
50136413Ssklower 		 *    but for the time being, for simplicity, we'll stop here.
50236413Ssklower 		 * In all cases, m points to mbuf containing first octet to be
50336413Ssklower 		 * sent in the tpdu AFTER the one we're going to send now,
50436413Ssklower 		 * or else m is null.
50536413Ssklower 		 *
50636413Ssklower 		 * The chain we're working on now begins at mb and has length <len>.
50736413Ssklower 		 */
50836413Ssklower 
50936413Ssklower 		IFTRACE(D_STASH)
51036413Ssklower 			tptraceTPCB( TPPTmisc,
51136413Ssklower 				"tp_send mcopy low high eotsdu_reached len",
51236413Ssklower 				lowseq, highseq, eotsdu_reached, len);
51336413Ssklower 		ENDTRACE
51436413Ssklower 
51536413Ssklower 		/* make a copy - mb goes into the retransmission list
51636413Ssklower 		 * while m gets emitted.  m_copy won't copy a zero-length mbuf.
51736413Ssklower 		 */
51838841Ssklower 		if (len) {
51938841Ssklower 			if ((m = m_copy(mb, 0, len )) == MNULL)
52036413Ssklower 				goto done;
52136413Ssklower 		} else {
52236413Ssklower 			/* eotsdu reached */
52336413Ssklower 			MGET(m, M_WAIT, TPMT_DATA);
52438841Ssklower 			if (m == MNULL)
52536413Ssklower 				goto done;
52636413Ssklower 			m->m_len = 0;
52736413Ssklower 		}
52836413Ssklower 
52936413Ssklower 		SEQ_INC(tpcb,lowseq);	/* it was decremented at the beginning */
53036413Ssklower 		{
53136413Ssklower 			struct tp_rtc *t;
53236413Ssklower 			/* make an rtc and put it at the end of the chain */
53336413Ssklower 
53436413Ssklower 			TP_MAKE_RTC( t, lowseq, eotsdu_reached, mb, len, lowseq,
53536413Ssklower 				TPMT_SNDRTC);
53636413Ssklower 			t->tprt_next = (struct tp_rtc *)0;
53736413Ssklower 
53836413Ssklower 			if ( tpcb->tp_sndhiwat_rtc != (struct tp_rtc *)0 )
53936413Ssklower 				tpcb->tp_sndhiwat_rtc->tprt_next = t;
54036413Ssklower 			else {
54136413Ssklower 				ASSERT( tpcb->tp_snduna_rtc == (struct tp_rtc *)0 );
54236413Ssklower 				tpcb->tp_snduna_rtc = t;
54336413Ssklower 			}
54436413Ssklower 
54536413Ssklower 			tpcb->tp_sndhiwat_rtc = t;
54636413Ssklower 		}
54736413Ssklower 
54836413Ssklower 		IFTRACE(D_DATA)
54936413Ssklower 			tptraceTPCB( TPPTmisc,
55039196Ssklower 				"tp_send emitting DT lowseq eotsdu_reached len",
55139196Ssklower 				lowseq, eotsdu_reached, len, 0);
55236413Ssklower 		ENDTRACE
55336413Ssklower 		if( tpcb->tp_sock->so_error =
55436413Ssklower 			tp_emit(DT_TPDU_type, tpcb, lowseq, eotsdu_reached, m) )  {
55536413Ssklower 			/* error */
55636413Ssklower 			SEQ_DEC(tpcb, lowseq);
55736413Ssklower 			goto done;
55836413Ssklower 		}
55936413Ssklower 		/* set the transmit-time for computation of round-trip times */
56036413Ssklower 		bcopy( (caddr_t)&time,
56136413Ssklower 				(caddr_t)&( tpcb->tp_rttemit[ lowseq & TP_RTT_NUM ] ),
56236413Ssklower 				sizeof(struct timeval));
56336413Ssklower 
56436413Ssklower 	}
56536413Ssklower 
56636413Ssklower done:
567*50236Ssklower #ifdef TP_PERF_MEAS
56836413Ssklower 	IFPERF(tpcb)
56936413Ssklower 		{
57036413Ssklower 			register int npkts;
57136413Ssklower 			struct timeval send_end_time;
57236413Ssklower 			register struct timeval *t;
57336413Ssklower 
57436413Ssklower 			npkts = lowseq;
57536413Ssklower 			SEQ_INC(tpcb, npkts);
57636413Ssklower 			npkts = SEQ_SUB(tpcb, npkts, lowsave);
57736413Ssklower 
57836413Ssklower 			if(npkts > 0)
57936413Ssklower 				tpcb->tp_Nwindow++;
58036413Ssklower 
58136413Ssklower 			if (npkts > TP_PM_MAX)
58236413Ssklower 				npkts = TP_PM_MAX;
58336413Ssklower 
58436413Ssklower 			GET_TIME_SINCE(&send_start_time, &send_end_time);
58536413Ssklower 			t = &(tpcb->tp_p_meas->tps_sendtime[npkts]);
58636413Ssklower 			t->tv_sec =
58736413Ssklower 				SMOOTH( long, TP_RTT_ALPHA, t->tv_sec, send_end_time.tv_sec);
58836413Ssklower 			t->tv_usec =
58936413Ssklower 				SMOOTH( long, TP_RTT_ALPHA, t->tv_usec, send_end_time.tv_usec);
59036413Ssklower 
59136413Ssklower 			if ( SEQ_LT(tpcb, lowseq, highseq) ) {
59236413Ssklower 				IncPStat(tpcb, tps_win_lim_by_data[npkts] );
59336413Ssklower 			} else {
59436413Ssklower 				IncPStat(tpcb, tps_win_lim_by_cdt[npkts] );
59536413Ssklower 				/* not true with congestion-window being used */
59636413Ssklower 			}
59736413Ssklower 			tpmeas( tpcb->tp_lref,
59836413Ssklower 					TPsbsend, &send_end_time, lowsave, tpcb->tp_Nwindow, npkts);
59936413Ssklower 		}
60036413Ssklower 	ENDPERF
601*50236Ssklower #endif TP_PERF_MEAS
60236413Ssklower 
60336413Ssklower 	tpcb->tp_sndhiwat = lowseq;
60436413Ssklower 
60536413Ssklower 	if ( SEQ_LEQ(tpcb, lowsave, tpcb->tp_sndhiwat)  &&
60636413Ssklower 			(tpcb->tp_class != TP_CLASS_0) )
60736413Ssklower 			tp_etimeout(tpcb->tp_refp, TM_data_retrans, lowsave,
60836413Ssklower 				tpcb->tp_sndhiwat,
60936413Ssklower 				(u_int)tpcb->tp_Nretrans, (int)tpcb->tp_dt_ticks);
61036413Ssklower 	IFTRACE(D_DATA)
61136413Ssklower 		tptraceTPCB( TPPTmisc,
61236413Ssklower 			"tp_send at end: sndhiwat lowseq eotsdu_reached error",
61336413Ssklower 			tpcb->tp_sndhiwat, lowseq, eotsdu_reached, tpcb->tp_sock->so_error);
61436413Ssklower 
61536413Ssklower 	ENDTRACE
61636413Ssklower }
61736413Ssklower 
61836413Ssklower /*
61936413Ssklower  * NAME: tp_stash()
62036413Ssklower  * CALLED FROM:
62136413Ssklower  *	tp.trans on arrival of a DT tpdu
62236413Ssklower  * FUNCTION, ARGUMENTS, and RETURN VALUE:
62336413Ssklower  * 	Returns 1 if
62436413Ssklower  *		a) something new arrived and it's got eotsdu_reached bit on,
62536413Ssklower  * 		b) this arrival was caused other out-of-sequence things to be
62636413Ssklower  *    	accepted, or
62736413Ssklower  * 		c) this arrival is the highest seq # for which we last gave credit
62836413Ssklower  *   	(sender just sent a whole window)
62936413Ssklower  *  In other words, returns 1 if tp should send an ack immediately, 0 if
63036413Ssklower  *  the ack can wait a while.
63136413Ssklower  *
63236413Ssklower  * Note: this implementation no longer renegs on credit, (except
63336413Ssklower  * when debugging option D_RENEG is on, for the purpose of testing
63436413Ssklower  * ack subsequencing), so we don't  need to check for incoming tpdus
63536413Ssklower  * being in a reneged portion of the window.
63636413Ssklower  */
63736413Ssklower 
63836413Ssklower int
63936413Ssklower tp_stash( tpcb, e )
64036413Ssklower 	register struct tp_pcb		*tpcb;
64136413Ssklower 	register struct tp_event	*e;
64236413Ssklower {
64336413Ssklower 	register int		ack_reason= tpcb->tp_ack_strat & ACK_STRAT_EACH;
64436413Ssklower 									/* 0--> delay acks until full window */
64536413Ssklower 									/* 1--> ack each tpdu */
64636413Ssklower 	int		newrec = 0;
64736413Ssklower 
64836413Ssklower #ifndef lint
64936413Ssklower #define E e->ATTR(DT_TPDU)
65036413Ssklower #else lint
65136413Ssklower #define E e->ev_union.EV_DT_TPDU
65236413Ssklower #endif lint
65336413Ssklower 
65436413Ssklower 	if ( E.e_eot ) {
65536413Ssklower 		register struct mbuf *n = E.e_data;
65637469Ssklower 		n->m_flags |= M_EOR;
65738841Ssklower 		n->m_act = 0;
65837469Ssklower 	}
65936413Ssklower 		IFDEBUG(D_STASH)
66036413Ssklower 			dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
66136413Ssklower 				"stash: so_rcv before appending");
66236413Ssklower 			dump_mbuf(E.e_data,
66336413Ssklower 				"stash: e_data before appending");
66436413Ssklower 		ENDDEBUG
66536413Ssklower 
66636413Ssklower 	IFPERF(tpcb)
66736413Ssklower 		PStat(tpcb, Nb_from_ll) += E.e_datalen;
66836413Ssklower 		tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
66937469Ssklower 			E.e_seq, (u_int)PStat(tpcb, Nb_from_ll), (u_int)E.e_datalen);
67036413Ssklower 	ENDPERF
67136413Ssklower 
67236413Ssklower 	if( E.e_seq == tpcb->tp_rcvnxt ) {
67336413Ssklower 
67436413Ssklower 		IFDEBUG(D_STASH)
67536413Ssklower 			printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x\n",
67636413Ssklower 			E.e_seq, E.e_datalen, E.e_eot);
67736413Ssklower 		ENDDEBUG
67836413Ssklower 
67936413Ssklower 		IFTRACE(D_STASH)
68036413Ssklower 			tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
68136413Ssklower 			E.e_seq, E.e_datalen, E.e_eot, 0);
68236413Ssklower 		ENDTRACE
68336413Ssklower 
68437469Ssklower 		sbappend(&tpcb->tp_sock->so_rcv, E.e_data);
68537469Ssklower 
68636413Ssklower 		if (newrec = E.e_eot ) /* ASSIGNMENT */
68736413Ssklower 			ack_reason |= ACK_EOT;
68836413Ssklower 
68936413Ssklower 		SEQ_INC( tpcb, tpcb->tp_rcvnxt );
69036413Ssklower 		/*
69136413Ssklower 		 * move chains from the rtc list to the socket buffer
69236413Ssklower 		 * and free the rtc header
69336413Ssklower 		 */
69436413Ssklower 		{
69536413Ssklower 			register struct tp_rtc	**r =  &tpcb->tp_rcvnxt_rtc;
69636413Ssklower 			register struct tp_rtc	*s = tpcb->tp_rcvnxt_rtc;
69736413Ssklower 
69836413Ssklower 			while (s != (struct tp_rtc *)0 && s->tprt_seq == tpcb->tp_rcvnxt) {
69936413Ssklower 				*r = s->tprt_next;
70036413Ssklower 
70137469Ssklower 				sbappend(&tpcb->tp_sock->so_rcv, s->tprt_data);
70236413Ssklower 
70336413Ssklower 				SEQ_INC( tpcb, tpcb->tp_rcvnxt );
70436413Ssklower 
70536413Ssklower 				(void) m_free( dtom( s ) );
70636413Ssklower 				s = *r;
70736413Ssklower 				ack_reason |= ACK_REORDER;
70836413Ssklower 			}
70936413Ssklower 		}
71036413Ssklower 		IFDEBUG(D_STASH)
71136413Ssklower 			dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
71236413Ssklower 				"stash: so_rcv after appending");
71336413Ssklower 		ENDDEBUG
71436413Ssklower 
71536413Ssklower 	} else {
71636413Ssklower 		register struct tp_rtc **s = &tpcb->tp_rcvnxt_rtc;
71736413Ssklower 		register struct tp_rtc *r = tpcb->tp_rcvnxt_rtc;
71836413Ssklower 		register struct tp_rtc *t;
71936413Ssklower 
72036413Ssklower 		IFTRACE(D_STASH)
72136413Ssklower 			tptraceTPCB(TPPTmisc, "stash Reseq: seq rcvnxt lcdt",
72236413Ssklower 			E.e_seq, tpcb->tp_rcvnxt, tpcb->tp_lcredit, 0);
72336413Ssklower 		ENDTRACE
72436413Ssklower 
72536413Ssklower 		r = tpcb->tp_rcvnxt_rtc;
72636413Ssklower 		while (r != (struct tp_rtc *)0  && SEQ_LT(tpcb, r->tprt_seq, E.e_seq)) {
72736413Ssklower 			s = &r->tprt_next;
72836413Ssklower 			r = r->tprt_next;
72936413Ssklower 		}
73036413Ssklower 
73136413Ssklower 		if (r == (struct tp_rtc *)0 || SEQ_GT(tpcb, r->tprt_seq, E.e_seq) ) {
73236413Ssklower 			IncStat(ts_dt_ooo);
73336413Ssklower 
73436413Ssklower 			IFTRACE(D_STASH)
73536413Ssklower 				tptrace(TPPTmisc,
73636413Ssklower 				"tp_stash OUT OF ORDER- MAKE RTC: seq, 1st seq in list\n",
73736413Ssklower 					E.e_seq, r->tprt_seq,0,0);
73836413Ssklower 			ENDTRACE
73936413Ssklower 			IFDEBUG(D_STASH)
74036413Ssklower 				printf("tp_stash OUT OF ORDER- MAKE RTC\n");
74136413Ssklower 			ENDDEBUG
74236413Ssklower 			TP_MAKE_RTC(t, E.e_seq, E.e_eot, E.e_data, E.e_datalen, 0,
74336413Ssklower 				TPMT_RCVRTC);
74436413Ssklower 
74536413Ssklower 			*s = t;
74636413Ssklower 			t->tprt_next = (struct tp_rtc *)r;
74736413Ssklower 			ack_reason = ACK_DONT;
74836413Ssklower 			goto done;
74936413Ssklower 		} else {
75036413Ssklower 			IFDEBUG(D_STASH)
75136413Ssklower 				printf("tp_stash - drop & ack\n");
75236413Ssklower 			ENDDEBUG
75336413Ssklower 
75436413Ssklower 			/* retransmission - drop it and force an ack */
75536413Ssklower 			IncStat(ts_dt_dup);
75636413Ssklower 			IFPERF(tpcb)
75736413Ssklower 				IncPStat(tpcb, tps_n_ack_cuz_dup);
75836413Ssklower 			ENDPERF
75936413Ssklower 
76036413Ssklower 			m_freem( E.e_data );
76136413Ssklower 			ack_reason |= ACK_DUP;
76236413Ssklower 			goto done;
76336413Ssklower 		}
76436413Ssklower 	}
76536413Ssklower 
76636413Ssklower 
76736413Ssklower 	/*
76836413Ssklower 	 * an ack should be sent when at least one of the
76936413Ssklower 	 * following holds:
77036413Ssklower 	 * a) we've received a TPDU with EOTSDU set
77136413Ssklower 	 * b) the TPDU that just arrived represents the
77236413Ssklower 	 *    full window last advertised, or
77336413Ssklower 	 * c) when seq X arrives [ where
77436413Ssklower 	 * 		X = last_sent_uwe - 1/2 last_lcredit_sent
77536413Ssklower 	 * 		(the packet representing 1/2 the last advertised window) ]
77636413Ssklower 	 * 		and lcredit at the time of X arrival >  last_lcredit_sent/2
77736413Ssklower 	 * 		In other words, if the last ack sent advertised cdt=8 and uwe = 8
77836413Ssklower 	 * 		then when seq 4 arrives I'd like to send a new ack
77936413Ssklower 	 * 		iff the credit at the time of 4's arrival is > 4.
78036413Ssklower 	 * 		The other end thinks it has cdt of 4 so if local cdt
78136413Ssklower 	 * 		is still 4 there's no point in sending an ack, but if
78236413Ssklower 	 * 		my credit has increased because the receiver has taken
78336413Ssklower 	 * 		some data out of the buffer (soreceive doesn't notify
78436413Ssklower 	 * 		me until the SYSTEM CALL finishes), I'd like to tell
78536413Ssklower 	 * 		the other end.
78636413Ssklower 	 */
78736413Ssklower 
78836413Ssklower done:
78936413Ssklower 	{
79036413Ssklower 		LOCAL_CREDIT(tpcb);
79136413Ssklower 
79236413Ssklower 		if ( E.e_seq ==  tpcb->tp_sent_uwe )
79336413Ssklower 			ack_reason |= ACK_STRAT_FULLWIN;
79436413Ssklower 
79536413Ssklower 		IFTRACE(D_STASH)
79636413Ssklower 			tptraceTPCB(TPPTmisc,
79736413Ssklower 				"end of stash, eot, ack_reason, sent_uwe ",
79836413Ssklower 				E.e_eot, ack_reason, tpcb->tp_sent_uwe, 0);
79936413Ssklower 		ENDTRACE
80036413Ssklower 
80136413Ssklower 		if ( ack_reason == ACK_DONT ) {
80236413Ssklower 			IncStat( ts_ackreason[ACK_DONT] );
80336413Ssklower 			return 0;
80436413Ssklower 		} else {
80536413Ssklower 			IFPERF(tpcb)
80636413Ssklower 				if(ack_reason & ACK_EOT) {
80736413Ssklower 					IncPStat(tpcb, tps_n_ack_cuz_eot);
80836413Ssklower 				}
80936413Ssklower 				if(ack_reason & ACK_STRAT_EACH) {
81036413Ssklower 					IncPStat(tpcb, tps_n_ack_cuz_strat);
81136413Ssklower 				} else if(ack_reason & ACK_STRAT_FULLWIN) {
81236413Ssklower 					IncPStat(tpcb, tps_n_ack_cuz_fullwin);
81336413Ssklower 				} else if(ack_reason & ACK_REORDER) {
81436413Ssklower 					IncPStat(tpcb, tps_n_ack_cuz_reorder);
81536413Ssklower 				}
81636413Ssklower 				tpmeas(tpcb->tp_lref, TPtime_ack_sent, 0,
81736413Ssklower 							SEQ_ADD(tpcb, E.e_seq, 1), 0, 0);
81836413Ssklower 			ENDPERF
81936413Ssklower 			{
82036413Ssklower 				register int i;
82136413Ssklower 
82236413Ssklower 				/* keep track of all reasons that apply */
82336413Ssklower 				for( i=1; i<_ACK_NUM_REASONS_ ;i++) {
82436413Ssklower 					if( ack_reason & (1<<i) )
82536413Ssklower 						IncStat( ts_ackreason[i] );
82636413Ssklower 				}
82736413Ssklower 			}
82836413Ssklower 			return 1;
82936413Ssklower 		}
83036413Ssklower 	}
83136413Ssklower }
832