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