xref: /csrg-svn/sys/netiso/tp_subr.c (revision 37469)
1 /***********************************************************
2 		Copyright IBM Corporation 1987
3 
4                       All Rights Reserved
5 
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted,
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in
10 supporting documentation, and that the name of IBM not be
11 used in advertising or publicity pertaining to distribution of the
12 software without specific, written prior permission.
13 
14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21 
22 ******************************************************************/
23 
24 /*
25  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
26  */
27 /*
28  * ARGO TP
29  *
30  * $Header: tp_subr.c,v 5.3 88/11/18 17:28:43 nhall Exp $
31  * $Source: /usr/argo/sys/netiso/RCS/tp_subr.c,v $
32  *
33  * The main work of data transfer is done here.
34  * These routines are called from tp.trans.
35  * They include the routines that check the validity of acks and Xacks,
36  * (tp_goodack() and tp_goodXack() )
37  * take packets from socket buffers and send them (tp_send()),
38  * drop the data from the socket buffers (tp_sbdrop()),
39  * and put incoming packet data into socket buffers (tp_stash()).
40  */
41 
42 #ifndef lint
43 static char *rcsid = "$Header: tp_subr.c,v 5.3 88/11/18 17:28:43 nhall Exp $";
44 #endif lint
45 
46 #include "param.h"
47 #include "mbuf.h"
48 #include "socket.h"
49 #include "socketvar.h"
50 #include "protosw.h"
51 #include "errno.h"
52 #include "types.h"
53 #include "time.h"
54 
55 #include "tp_ip.h"
56 #include "iso.h"
57 #include "argo_debug.h"
58 #include "tp_timer.h"
59 #include "tp_param.h"
60 #include "tp_stat.h"
61 #include "tp_pcb.h"
62 #include "tp_tpdu.h"
63 #include "tp_trace.h"
64 #include "tp_meas.h"
65 #include "tp_seq.h"
66 
67 int 		tp_emit();
68 static void tp_sbdrop();
69 
70 #define SMOOTH(newtype, alpha, old, new) \
71 	(newtype) (((new - old)>>alpha) + (old))
72 
73 #define ABS(type, val) \
74 	(type) (((int)(val)<0)?-(val):(val))
75 
76 #define TP_MAKE_RTC( Xreg, Xseq, Xeot, Xdata, Xlen, Xretval, Xtype) \
77 { 	struct mbuf *xxn;\
78 	MGET(xxn, M_DONTWAIT, Xtype);\
79 	if( xxn == (struct mbuf *)0 ) {\
80 		printf("MAKE RTC FAILED: ENOBUFS\n");\
81 		return (int)Xretval;\
82 	}\
83 	xxn->m_act=MNULL;\
84 	Xreg = mtod(xxn, struct tp_rtc *);\
85 	if( Xreg == (struct tp_rtc *)0 ) {\
86 		return (int)Xretval;\
87 	}\
88 	Xreg->tprt_eot = Xeot;\
89 	Xreg->tprt_seq = Xseq;\
90 	Xreg->tprt_data = Xdata;\
91 	Xreg->tprt_octets = Xlen;\
92 }
93 
94 
95 /*
96  * CALLED FROM:
97  *	tp.trans, when an XAK arrives
98  * FUNCTION and ARGUMENTS:
99  * 	Determines if the sequence number (seq) from the XAK
100  * 	acks anything new.  If so, drop the appropriate tpdu
101  * 	from the XPD send queue.
102  * RETURN VALUE:
103  * 	Returns 1 if it did this, 0 if the ack caused no action.
104  */
105 int
106 tp_goodXack(tpcb, seq)
107 	struct tp_pcb	*tpcb;
108 	SeqNum 			seq;
109 {
110 
111 	IFTRACE(D_XPD)
112 		tptraceTPCB(TPPTgotXack,
113 			seq, tpcb->tp_Xuna, tpcb->tp_Xsndnxt, tpcb->tp_sndhiwat,
114 			tpcb->tp_snduna);
115 	ENDTRACE
116 
117 	if ( seq == tpcb->tp_Xuna ) {
118 			tpcb->tp_Xuna = tpcb->tp_Xsndnxt;
119 
120 			/* DROP 1 packet from the Xsnd socket buf - just so happens
121 			 * that only one packet can be there at any time
122 			 * so drop the whole thing.  If you allow > 1 packet
123 			 * the socket buffer, then you'll have to keep
124 			 * track of how many characters went w/ each XPD tpdu, so this
125 			 * will get messier
126 			 */
127 			IFDEBUG(D_XPD)
128 				dump_mbuf(tpcb->tp_Xsnd.sb_mb,
129 					"tp_goodXack Xsnd before sbdrop");
130 			ENDDEBUG
131 
132 			IFTRACE(D_XPD)
133 				tptraceTPCB(TPPTmisc,
134 					"goodXack: dropping cc ",
135 					(int)(tpcb->tp_Xsnd.sb_cc),
136 					0,0,0);
137 			ENDTRACE
138 			sbdrop( &tpcb->tp_Xsnd, (int)(tpcb->tp_Xsnd.sb_cc));
139 			return 1;
140 	}
141 	return 0;
142 }
143 
144 /*
145  * CALLED FROM:
146  *  tp_good_ack()
147  * FUNCTION and ARGUMENTS:
148  *  updates
149  *  smoothed average round trip time (base_rtt)
150  *  roundtrip time variance (base_rtv) - actually deviation, not variance
151  *  given the new value (diff)
152  * RETURN VALUE:
153  * void
154  */
155 
156 void
157 tp_rtt_rtv( base_rtt, base_rtv, newmeas )
158 	struct 	timeval *base_rtt, *base_rtv, *newmeas;
159 {
160 	/* update  rt variance (really just the deviation):
161 	 * 	rtv.smooth_ave =  SMOOTH( | oldrtt.smooth_avg - rtt.this_instance | )
162 	 */
163 	base_rtv->tv_sec =
164 		SMOOTH( long,  TP_RTV_ALPHA, base_rtv->tv_sec,
165 			ABS( long, base_rtv->tv_sec - newmeas->tv_sec ));
166 	base_rtv->tv_usec =
167 		SMOOTH( long,  TP_RTV_ALPHA, base_rtv->tv_usec,
168 			ABS(long, base_rtv->tv_usec - newmeas->tv_usec ));
169 
170 	/* update smoothed average rtt */
171 	base_rtt->tv_sec =
172 		SMOOTH( long,  TP_RTT_ALPHA, base_rtt->tv_sec, newmeas->tv_sec);
173 	base_rtt->tv_usec =
174 		SMOOTH( long,  TP_RTT_ALPHA, base_rtt->tv_usec, newmeas->tv_usec);
175 
176 }
177 
178 /*
179  * CALLED FROM:
180  *  tp.trans when an AK arrives
181  * FUNCTION and ARGUMENTS:
182  * 	Given (cdt), the credit from the AK tpdu, and
183  *	(seq), the sequence number from the AK tpdu,
184  *  tp_goodack() determines if the AK acknowledges something in the send
185  * 	window, and if so, drops the appropriate packets from the retransmission
186  *  list, computes the round trip time, and updates the retransmission timer
187  *  based on the new smoothed round trip time.
188  * RETURN VALUE:
189  * 	Returns 1 if
190  * 	EITHER it actually acked something heretofore unacknowledged
191  * 	OR no news but the credit should be processed.
192  * 	If something heretofore unacked was acked with this sequence number,
193  * 	the appropriate tpdus are dropped from the retransmission control list,
194  * 	by calling tp_sbdrop().
195  * 	No need to see the tpdu itself.
196  */
197 int
198 tp_goodack(tpcb, cdt, seq, subseq)
199 	register struct tp_pcb	*tpcb;
200 	u_int					cdt;
201 	register SeqNum			seq, subseq;
202 {
203 	int 	old_fcredit = tpcb->tp_fcredit;
204 	int 	bang = 0; 	/* bang --> ack for something heretofore unacked */
205 
206 	IFDEBUG(D_ACKRECV)
207 		printf("goodack seq 0x%x cdt 0x%x snduna 0x%x sndhiwat 0x%x\n",
208 			seq, cdt, tpcb->tp_snduna, tpcb->tp_sndhiwat);
209 	ENDDEBUG
210 	IFTRACE(D_ACKRECV)
211 		tptraceTPCB(TPPTgotack,
212 			seq,cdt, tpcb->tp_snduna,tpcb->tp_sndhiwat,subseq);
213 	ENDTRACE
214 
215 	IFPERF(tpcb)
216 		tpmeas(tpcb->tp_lref, TPtime_ack_rcvd, (struct timeval *)0, seq, 0, 0);
217 	ENDPERF
218 
219 	if ( subseq != 0 && (subseq <= tpcb->tp_r_subseq) ) {
220 		/* discard the ack */
221 		IFTRACE(D_ACKRECV)
222 			tptraceTPCB(TPPTmisc, "goodack discard : subseq tp_r_subseq",
223 				subseq, tpcb->tp_r_subseq, 0, 0);
224 		ENDTRACE
225 		return 0;
226 	} else {
227 		tpcb->tp_r_subseq = subseq;
228 	}
229 
230 	if ( IN_SWINDOW(tpcb, seq,
231 			tpcb->tp_snduna, SEQ(tpcb, tpcb->tp_sndhiwat+1)) ) {
232 
233 		IFDEBUG(D_XPD)
234 			dump_mbuf(tpcb->tp_sock->so_snd.sb_mb,
235 				"tp_goodack snd before sbdrop");
236 		ENDDEBUG
237 		tp_sbdrop(tpcb, SEQ_SUB(tpcb, seq, 1) );
238 
239 		/* increase congestion window but don't let it get too big */
240 		{
241 			register int maxcdt = tpcb->tp_xtd_format?0xffff:0xf;
242 
243 			if( ++tpcb->tp_cong_win > maxcdt )
244 				tpcb->tp_cong_win = maxcdt;
245 		}
246 
247 		/* Compute smoothed round trip time.
248 		 * Only measure rtt for tp_snduna if tp_snduna was among
249 		 * the last TP_RTT_NUM seq numbers sent.
250 		 */
251 		if (SEQ_GEQ(tpcb, tpcb->tp_snduna,
252 			SEQ(tpcb, tpcb->tp_sndhiwat - TP_RTT_NUM))) {
253 
254 			struct timeval *t = &tpcb->tp_rttemit[tpcb->tp_snduna & TP_RTT_NUM];
255 			struct timeval x;
256 
257 			GET_TIME_SINCE(t, &x);
258 
259 			tp_rtt_rtv( &(tpcb->tp_rtt), &(tpcb->tp_rtv), &x );
260 
261 			{	/* update the global rtt, rtv stats */
262 				register int i =
263 				   (int) tpcb->tp_flags & (TPF_PEER_ON_SAMENET | TPF_NLQOS_PDN);
264 				tp_rtt_rtv( &(tp_stat.ts_rtt[i]), &(tp_stat.ts_rtv[i]), &x );
265 
266 				IFTRACE(D_RTT)
267 					tptraceTPCB(TPPTmisc, "Global rtt, rtv: i", i, 0, 0, 0);
268 				ENDTRACE
269 			}
270 
271 			IFTRACE(D_RTT)
272 				tptraceTPCB(TPPTmisc,
273 				"Smoothed rtt: tp_snduna, (time.sec, time.usec), peer_acktime",
274 				tpcb->tp_snduna, time.tv_sec, time.tv_usec,
275 					tpcb->tp_peer_acktime);
276 
277 				tptraceTPCB(TPPTmisc,
278 				"(secs): emittime diff(x) rtt, rtv",
279 					t->tv_sec,
280 					x.tv_sec,
281 					tpcb->tp_rtt.tv_sec,
282 					tpcb->tp_rtv.tv_sec);
283 				tptraceTPCB(TPPTmisc,
284 				"(usecs): emittime diff(x) rtt rtv",
285 					t->tv_usec,
286 					x.tv_usec,
287 					tpcb->tp_rtt.tv_usec,
288 					tpcb->tp_rtv.tv_usec);
289 			ENDTRACE
290 
291 			{
292 				/* Update data retransmission timer based on the smoothed
293 				 * round trip time, peer ack time, and the pseudo-arbitrary
294 				 * number 4.
295 				 * new ticks: avg rtt + 2*dev
296 				 * rtt, rtv are in microsecs, and ticks are 500 ms
297 				 * so 1 tick = 500*1000 us = 500000 us
298 				 * so ticks = (rtt + 2 rtv)/500000
299 				 * with ticks no les than peer ack time and no less than 4
300 				 */
301 
302 				int rtt = tpcb->tp_rtt.tv_usec +
303 					tpcb->tp_rtt.tv_sec*1000000;
304 				int rtv = tpcb->tp_rtv.tv_usec +
305 					tpcb->tp_rtv.tv_sec*1000000;
306 
307 				IFTRACE(D_RTT)
308 					tptraceTPCB(TPPTmisc, "oldticks ,rtv, rtt, newticks",
309 						tpcb->tp_dt_ticks,
310 						rtv, rtt,
311 						(rtt/500000 + (2 * rtv)/500000));
312 				ENDTRACE
313 				tpcb->tp_dt_ticks = (rtt+ (2 * rtv))/500000;
314 				tpcb->tp_dt_ticks = MAX( tpcb->tp_dt_ticks,
315 					tpcb->tp_peer_acktime);
316 				tpcb->tp_dt_ticks = MAX( tpcb->tp_dt_ticks,  4);
317 			}
318 		}
319 		tpcb->tp_snduna = seq;
320 
321 		bang++;
322 	}
323 
324 	if( cdt != 0 && old_fcredit == 0 ) {
325 		tpcb->tp_sendfcc = 1;
326 	}
327 	if( cdt == 0 && old_fcredit != 0 ) {
328 		IncStat(ts_zfcdt);
329 	}
330 	tpcb->tp_fcredit = cdt;
331 
332 	IFDEBUG(D_ACKRECV)
333 		printf("goodack returning 0x%x, bang 0x%x cdt 0x%x old_fcredit 0x%x\n",
334 			(bang || (old_fcredit < cdt) ), bang, cdt, old_fcredit );
335 	ENDDEBUG
336 
337 	return (bang || (old_fcredit < cdt)) ;
338 }
339 
340 /*
341  * CALLED FROM:
342  *  tp_goodack()
343  * FUNCTION and ARGUMENTS:
344  *  drops everything up TO and INCLUDING seq # (seq)
345  *  from the retransmission queue.
346  */
347 static void
348 tp_sbdrop(tpcb, seq)
349 	struct 	tp_pcb 			*tpcb;
350 	SeqNum					seq;
351 {
352 	register struct tp_rtc	*s = tpcb->tp_snduna_rtc;
353 
354 	IFDEBUG(D_ACKRECV)
355 		printf("tp_sbdrop up through seq 0x%x\n", seq);
356 	ENDDEBUG
357 	while (s != (struct tp_rtc *)0 && (SEQ_LEQ(tpcb, s->tprt_seq, seq))) {
358 		m_freem( s->tprt_data );
359 		tpcb->tp_snduna_rtc = s->tprt_next;
360 		(void) m_free( dtom( s ) );
361 		s = tpcb->tp_snduna_rtc;
362 	}
363 	if(tpcb->tp_snduna_rtc == (struct tp_rtc *)0)
364 		tpcb->tp_sndhiwat_rtc = (struct tp_rtc *) 0;
365 
366 }
367 
368 /*
369  * CALLED FROM:
370  * 	tp.trans on user send request, arrival of AK and arrival of XAK
371  * FUNCTION and ARGUMENTS:
372  * 	Emits tpdus starting at sequence number (lowseq).
373  * 	Emits until a) runs out of data, or  b) runs into an XPD mark, or
374  * 			c) it hits seq number (highseq)
375  * 	Removes the octets from the front of the socket buffer
376  * 	and repackages them in one mbuf chain per tpdu.
377  * 	Moves the mbuf chain to the doubly linked list that runs from
378  * 	tpcb->tp_sndhiwat_rtc to tpcb->tp_snduna_rtc.
379  *
380  * 	Creates tpdus that are no larger than <tpcb->tp_l_tpdusize - headersize>,
381  *
382  * 	If you want XPD to buffer > 1 du per socket buffer, you can
383  * 	modifiy this to issue XPD tpdus also, but then it'll have
384  * 	to take some argument(s) to distinguish between the type of DU to
385  * 	hand tp_emit, the socket buffer from which to get the data, and
386  * 	the chain of tp_rtc structures on which to put the data sent.
387  *
388  * 	When something is sent for the first time, its time-of-send
389  * 	is stashed (the last RTT_NUM of them are stashed).  When the
390  * 	ack arrives, the smoothed round-trip time is figured using this value.
391  * RETURN VALUE:
392  * 	the highest seq # sent successfully.
393  */
394 tp_send(tpcb)
395 	register struct tp_pcb	*tpcb;
396 {
397 	register int			len;
398 	register struct mbuf	*m; /* the one we're inspecting now */
399 	struct mbuf				*mb;/* beginning of this tpdu */
400 	struct mbuf 			*nextrecord; /* NOT next tpdu but next sb record */
401 	struct 	sockbuf			*sb = &tpcb->tp_sock->so_snd;
402 	int						maxsize = tpcb->tp_l_tpdusize
403 										- tp_headersize(DT_TPDU_type, tpcb)
404 										- (tpcb->tp_use_checksum?4:0) ;
405 	unsigned int			eotsdu_reached=0;
406 	SeqNum					lowseq, highseq ;
407 	SeqNum					lowsave;
408 #ifdef TP_PERF_MEAS
409 	struct timeval 			send_start_time;
410 #endif TP_PERF_MEAS
411 
412 	lowsave =  lowseq = SEQ(tpcb, tpcb->tp_sndhiwat + 1);
413 
414 	ASSERT( tpcb->tp_cong_win > 0 && tpcb->tp_cong_win < 0xffff);
415 
416 	if( tpcb->tp_rx_strat & TPRX_USE_CW ) {
417 			/*first hiseq is temp vbl*/
418 		highseq = MIN(tpcb->tp_fcredit, tpcb->tp_cong_win);
419 	} else {
420 		highseq = tpcb->tp_fcredit;
421 	}
422 	highseq = SEQ(tpcb, tpcb->tp_snduna + highseq);
423 
424 	SEQ_DEC(tpcb, highseq);
425 
426 	IFDEBUG(D_DATA)
427 		printf(
428 			"tp_send enter tpcb 0x%x l %d -> h %d\ndump of sb_mb:\n",
429 			tpcb, lowseq, highseq);
430 		dump_mbuf(sb->sb_mb, "sb_mb:");
431 	ENDDEBUG
432 	IFTRACE(D_DATA)
433 		tptraceTPCB( TPPTmisc, "tp_send lowsave sndhiwat snduna",
434 			lowsave, tpcb->tp_sndhiwat,  tpcb->tp_snduna, 0);
435 		tptraceTPCB( TPPTmisc, "tp_send low high fcredit congwin",
436 			lowseq, highseq, tpcb->tp_fcredit,  tpcb->tp_cong_win);
437 	ENDTRACE
438 
439 
440 	if	( SEQ_GT(tpcb, lowseq, highseq) )
441 			return ; /* don't send, don't change hiwat, don't set timers */
442 
443 	IFPERF(tpcb)
444 		GET_CUR_TIME(&send_start_time);
445 	ENDPERF
446 
447 	ASSERT( SEQ_LEQ(tpcb, lowseq, highseq) );
448 	SEQ_DEC(tpcb, lowseq);
449 
450 	IFTRACE(D_DATA)
451 		tptraceTPCB( TPPTmisc, "tp_send 2 low high fcredit congwin",
452 			lowseq, highseq, tpcb->tp_fcredit,  tpcb->tp_cong_win);
453 	ENDTRACE
454 
455 	while( SEQ_LT( tpcb, lowseq, highseq ) ) {
456 		mb = m = sb->sb_mb;
457 		if (m == (struct mbuf *)0) {
458 			break; /* empty socket buffer */
459 		}
460 		if (tpcb->tp_Xsnd.sb_mb) {
461 			register SeqNum Xuna = * (mtod(m, SeqNum *));
462 			IFTRACE(D_XPD)
463 				tptraceTPCB( TPPTmisc,
464 					"tp_send XPD mark low high tpcb.Xuna",
465 					Xuna, lowseq, highseq, tpcb->tp_Xuna);
466 			ENDTRACE
467 			/* stop sending here because there are unacked XPD which were
468 			 * given to us before the next data were.
469 			 */
470 			IncStat(ts_xpd_intheway);
471 			break;
472 		}
473 		eotsdu_reached = 0;
474 		nextrecord = m->m_act;
475 		for (len = 0; m; m = m->m_next) {
476 			len += m->m_len;
477 			if (m->m_flags & M_EOR)
478 				eotsdu_reached = 1;
479 			sbfree(sb, m); /* reduce counts in socket buffer */
480 		}
481 		m = sb->sb_mb = nextrecord;
482 		IFTRACE(D_STASH)
483 			tptraceTPCB(TPPTmisc, "tp_send whole mbuf: m_len len maxsize",
484 				 0, mb->m_len, len, maxsize);
485 		ENDTRACE
486 
487 		if ( len == 0 && !eotsdu_reached) {
488 			/* THIS SHOULD NEVER HAPPEN! */
489 			ASSERT( 0 );
490 			goto done;
491 		}
492 
493 		/* If we arrive here one of the following holds:
494 		 * 1. We have exactly <maxsize> octets of whole mbufs,
495 		 * 2. We accumulated <maxsize> octets using partial mbufs,
496 		 * 3. We found an TPMT_EOT or an XPD mark
497 		 * 4. We hit the end of a chain through m_next.
498 		 *    In this case, we'd LIKE to continue with the next record,
499 		 *    but for the time being, for simplicity, we'll stop here.
500 		 * In all cases, m points to mbuf containing first octet to be
501 		 * sent in the tpdu AFTER the one we're going to send now,
502 		 * or else m is null.
503 		 *
504 		 * The chain we're working on now begins at mb and has length <len>.
505 		 */
506 
507 		IFTRACE(D_STASH)
508 			tptraceTPCB( TPPTmisc,
509 				"tp_send mcopy low high eotsdu_reached len",
510 				lowseq, highseq, eotsdu_reached, len);
511 		ENDTRACE
512 
513 		/* make a copy - mb goes into the retransmission list
514 		 * while m gets emitted.  m_copy won't copy a zero-length mbuf.
515 		 */
516 		if(len) {
517 			if( (m = m_copy(mb, 0, len )) == MNULL ) {
518 				goto done;
519 			}
520 		} else {
521 			/* eotsdu reached */
522 			MGET(m, M_WAIT, TPMT_DATA);
523 			if (m == NULL)
524 				goto done;
525 			m->m_len = 0;
526 			m->m_act = MNULL;
527 		}
528 
529 		SEQ_INC(tpcb,lowseq);	/* it was decremented at the beginning */
530 		{
531 			struct tp_rtc *t;
532 			/* make an rtc and put it at the end of the chain */
533 
534 			TP_MAKE_RTC( t, lowseq, eotsdu_reached, mb, len, lowseq,
535 				TPMT_SNDRTC);
536 			t->tprt_next = (struct tp_rtc *)0;
537 
538 			if ( tpcb->tp_sndhiwat_rtc != (struct tp_rtc *)0 )
539 				tpcb->tp_sndhiwat_rtc->tprt_next = t;
540 			else {
541 				ASSERT( tpcb->tp_snduna_rtc == (struct tp_rtc *)0 );
542 				tpcb->tp_snduna_rtc = t;
543 			}
544 
545 			tpcb->tp_sndhiwat_rtc = t;
546 		}
547 
548 		IFTRACE(D_DATA)
549 			tptraceTPCB( TPPTmisc,
550 				"tp_send emitting DT lowseq eotsdu_reached",
551 				lowseq, eotsdu_reached, 0, 0);
552 		ENDTRACE
553 		if( tpcb->tp_sock->so_error =
554 			tp_emit(DT_TPDU_type, tpcb, lowseq, eotsdu_reached, m) )  {
555 			/* error */
556 			SEQ_DEC(tpcb, lowseq);
557 			goto done;
558 		}
559 		/* set the transmit-time for computation of round-trip times */
560 		bcopy( (caddr_t)&time,
561 				(caddr_t)&( tpcb->tp_rttemit[ lowseq & TP_RTT_NUM ] ),
562 				sizeof(struct timeval));
563 
564 	}
565 
566 done:
567 	IFPERF(tpcb)
568 		{
569 			register int npkts;
570 			struct timeval send_end_time;
571 			register struct timeval *t;
572 
573 			npkts = lowseq;
574 			SEQ_INC(tpcb, npkts);
575 			npkts = SEQ_SUB(tpcb, npkts, lowsave);
576 
577 			if(npkts > 0)
578 				tpcb->tp_Nwindow++;
579 
580 			if (npkts > TP_PM_MAX)
581 				npkts = TP_PM_MAX;
582 
583 			GET_TIME_SINCE(&send_start_time, &send_end_time);
584 			t = &(tpcb->tp_p_meas->tps_sendtime[npkts]);
585 			t->tv_sec =
586 				SMOOTH( long, TP_RTT_ALPHA, t->tv_sec, send_end_time.tv_sec);
587 			t->tv_usec =
588 				SMOOTH( long, TP_RTT_ALPHA, t->tv_usec, send_end_time.tv_usec);
589 
590 			if ( SEQ_LT(tpcb, lowseq, highseq) ) {
591 				IncPStat(tpcb, tps_win_lim_by_data[npkts] );
592 			} else {
593 				IncPStat(tpcb, tps_win_lim_by_cdt[npkts] );
594 				/* not true with congestion-window being used */
595 			}
596 			tpmeas( tpcb->tp_lref,
597 					TPsbsend, &send_end_time, lowsave, tpcb->tp_Nwindow, npkts);
598 		}
599 	ENDPERF
600 
601 	tpcb->tp_sndhiwat = lowseq;
602 
603 	if ( SEQ_LEQ(tpcb, lowsave, tpcb->tp_sndhiwat)  &&
604 			(tpcb->tp_class != TP_CLASS_0) )
605 			tp_etimeout(tpcb->tp_refp, TM_data_retrans, lowsave,
606 				tpcb->tp_sndhiwat,
607 				(u_int)tpcb->tp_Nretrans, (int)tpcb->tp_dt_ticks);
608 	IFTRACE(D_DATA)
609 		tptraceTPCB( TPPTmisc,
610 			"tp_send at end: sndhiwat lowseq eotsdu_reached error",
611 			tpcb->tp_sndhiwat, lowseq, eotsdu_reached, tpcb->tp_sock->so_error);
612 
613 	ENDTRACE
614 }
615 
616 /*
617  * NAME: tp_stash()
618  * CALLED FROM:
619  *	tp.trans on arrival of a DT tpdu
620  * FUNCTION, ARGUMENTS, and RETURN VALUE:
621  * 	Returns 1 if
622  *		a) something new arrived and it's got eotsdu_reached bit on,
623  * 		b) this arrival was caused other out-of-sequence things to be
624  *    	accepted, or
625  * 		c) this arrival is the highest seq # for which we last gave credit
626  *   	(sender just sent a whole window)
627  *  In other words, returns 1 if tp should send an ack immediately, 0 if
628  *  the ack can wait a while.
629  *
630  * Note: this implementation no longer renegs on credit, (except
631  * when debugging option D_RENEG is on, for the purpose of testing
632  * ack subsequencing), so we don't  need to check for incoming tpdus
633  * being in a reneged portion of the window.
634  */
635 
636 int
637 tp_stash( tpcb, e )
638 	register struct tp_pcb		*tpcb;
639 	register struct tp_event	*e;
640 {
641 	register int		ack_reason= tpcb->tp_ack_strat & ACK_STRAT_EACH;
642 									/* 0--> delay acks until full window */
643 									/* 1--> ack each tpdu */
644 	int		newrec = 0;
645 
646 #ifndef lint
647 #define E e->ATTR(DT_TPDU)
648 #else lint
649 #define E e->ev_union.EV_DT_TPDU
650 #endif lint
651 
652 	if ( E.e_eot ) {
653 		register struct mbuf *n = E.e_data;
654 		n->m_flags |= M_EOR;
655 	}
656 		IFDEBUG(D_STASH)
657 			dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
658 				"stash: so_rcv before appending");
659 			dump_mbuf(E.e_data,
660 				"stash: e_data before appending");
661 		ENDDEBUG
662 
663 	IFPERF(tpcb)
664 		PStat(tpcb, Nb_from_ll) += E.e_datalen;
665 		tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
666 			E.e_seq, (u_int)PStat(tpcb, Nb_from_ll), (u_int)E.e_datalen);
667 	ENDPERF
668 
669 	if( E.e_seq == tpcb->tp_rcvnxt ) {
670 
671 		IFDEBUG(D_STASH)
672 			printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x\n",
673 			E.e_seq, E.e_datalen, E.e_eot);
674 		ENDDEBUG
675 
676 		IFTRACE(D_STASH)
677 			tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
678 			E.e_seq, E.e_datalen, E.e_eot, 0);
679 		ENDTRACE
680 
681 		sbappend(&tpcb->tp_sock->so_rcv, E.e_data);
682 
683 		if (newrec = E.e_eot ) /* ASSIGNMENT */
684 			ack_reason |= ACK_EOT;
685 
686 		SEQ_INC( tpcb, tpcb->tp_rcvnxt );
687 		/*
688 		 * move chains from the rtc list to the socket buffer
689 		 * and free the rtc header
690 		 */
691 		{
692 			register struct tp_rtc	**r =  &tpcb->tp_rcvnxt_rtc;
693 			register struct tp_rtc	*s = tpcb->tp_rcvnxt_rtc;
694 
695 			while (s != (struct tp_rtc *)0 && s->tprt_seq == tpcb->tp_rcvnxt) {
696 				*r = s->tprt_next;
697 
698 				sbappend(&tpcb->tp_sock->so_rcv, s->tprt_data);
699 
700 				SEQ_INC( tpcb, tpcb->tp_rcvnxt );
701 
702 				(void) m_free( dtom( s ) );
703 				s = *r;
704 				ack_reason |= ACK_REORDER;
705 			}
706 		}
707 		IFDEBUG(D_STASH)
708 			dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
709 				"stash: so_rcv after appending");
710 		ENDDEBUG
711 
712 	} else {
713 		register struct tp_rtc **s = &tpcb->tp_rcvnxt_rtc;
714 		register struct tp_rtc *r = tpcb->tp_rcvnxt_rtc;
715 		register struct tp_rtc *t;
716 
717 		IFTRACE(D_STASH)
718 			tptraceTPCB(TPPTmisc, "stash Reseq: seq rcvnxt lcdt",
719 			E.e_seq, tpcb->tp_rcvnxt, tpcb->tp_lcredit, 0);
720 		ENDTRACE
721 
722 		r = tpcb->tp_rcvnxt_rtc;
723 		while (r != (struct tp_rtc *)0  && SEQ_LT(tpcb, r->tprt_seq, E.e_seq)) {
724 			s = &r->tprt_next;
725 			r = r->tprt_next;
726 		}
727 
728 		if (r == (struct tp_rtc *)0 || SEQ_GT(tpcb, r->tprt_seq, E.e_seq) ) {
729 			IncStat(ts_dt_ooo);
730 
731 			IFTRACE(D_STASH)
732 				tptrace(TPPTmisc,
733 				"tp_stash OUT OF ORDER- MAKE RTC: seq, 1st seq in list\n",
734 					E.e_seq, r->tprt_seq,0,0);
735 			ENDTRACE
736 			IFDEBUG(D_STASH)
737 				printf("tp_stash OUT OF ORDER- MAKE RTC\n");
738 			ENDDEBUG
739 			TP_MAKE_RTC(t, E.e_seq, E.e_eot, E.e_data, E.e_datalen, 0,
740 				TPMT_RCVRTC);
741 
742 			*s = t;
743 			t->tprt_next = (struct tp_rtc *)r;
744 			ack_reason = ACK_DONT;
745 			goto done;
746 		} else {
747 			IFDEBUG(D_STASH)
748 				printf("tp_stash - drop & ack\n");
749 			ENDDEBUG
750 
751 			/* retransmission - drop it and force an ack */
752 			IncStat(ts_dt_dup);
753 			IFPERF(tpcb)
754 				IncPStat(tpcb, tps_n_ack_cuz_dup);
755 			ENDPERF
756 
757 			m_freem( E.e_data );
758 			ack_reason |= ACK_DUP;
759 			goto done;
760 		}
761 	}
762 
763 
764 	/*
765 	 * an ack should be sent when at least one of the
766 	 * following holds:
767 	 * a) we've received a TPDU with EOTSDU set
768 	 * b) the TPDU that just arrived represents the
769 	 *    full window last advertised, or
770 	 * c) when seq X arrives [ where
771 	 * 		X = last_sent_uwe - 1/2 last_lcredit_sent
772 	 * 		(the packet representing 1/2 the last advertised window) ]
773 	 * 		and lcredit at the time of X arrival >  last_lcredit_sent/2
774 	 * 		In other words, if the last ack sent advertised cdt=8 and uwe = 8
775 	 * 		then when seq 4 arrives I'd like to send a new ack
776 	 * 		iff the credit at the time of 4's arrival is > 4.
777 	 * 		The other end thinks it has cdt of 4 so if local cdt
778 	 * 		is still 4 there's no point in sending an ack, but if
779 	 * 		my credit has increased because the receiver has taken
780 	 * 		some data out of the buffer (soreceive doesn't notify
781 	 * 		me until the SYSTEM CALL finishes), I'd like to tell
782 	 * 		the other end.
783 	 */
784 
785 done:
786 	{
787 		LOCAL_CREDIT(tpcb);
788 
789 		if ( E.e_seq ==  tpcb->tp_sent_uwe )
790 			ack_reason |= ACK_STRAT_FULLWIN;
791 
792 		IFTRACE(D_STASH)
793 			tptraceTPCB(TPPTmisc,
794 				"end of stash, eot, ack_reason, sent_uwe ",
795 				E.e_eot, ack_reason, tpcb->tp_sent_uwe, 0);
796 		ENDTRACE
797 
798 		if ( ack_reason == ACK_DONT ) {
799 			IncStat( ts_ackreason[ACK_DONT] );
800 			return 0;
801 		} else {
802 			IFPERF(tpcb)
803 				if(ack_reason & ACK_EOT) {
804 					IncPStat(tpcb, tps_n_ack_cuz_eot);
805 				}
806 				if(ack_reason & ACK_STRAT_EACH) {
807 					IncPStat(tpcb, tps_n_ack_cuz_strat);
808 				} else if(ack_reason & ACK_STRAT_FULLWIN) {
809 					IncPStat(tpcb, tps_n_ack_cuz_fullwin);
810 				} else if(ack_reason & ACK_REORDER) {
811 					IncPStat(tpcb, tps_n_ack_cuz_reorder);
812 				}
813 				tpmeas(tpcb->tp_lref, TPtime_ack_sent, 0,
814 							SEQ_ADD(tpcb, E.e_seq, 1), 0, 0);
815 			ENDPERF
816 			{
817 				register int i;
818 
819 				/* keep track of all reasons that apply */
820 				for( i=1; i<_ACK_NUM_REASONS_ ;i++) {
821 					if( ack_reason & (1<<i) )
822 						IncStat( ts_ackreason[i] );
823 				}
824 			}
825 			return 1;
826 		}
827 	}
828 }
829 
830 /* class zero version */
831 void
832 tp0_stash( tpcb, e )
833 	register struct tp_pcb		*tpcb;
834 	register struct tp_event	*e;
835 {
836 #ifndef lint
837 #define E e->ATTR(DT_TPDU)
838 #else lint
839 #define E e->ev_union.EV_DT_TPDU
840 #endif lint
841 
842 	IFPERF(tpcb)
843 		PStat(tpcb, Nb_from_ll) += E.e_datalen;
844 		tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
845 				E.e_seq, PStat(tpcb, Nb_from_ll), E.e_datalen);
846 	ENDPERF
847 
848 	IFDEBUG(D_STASH)
849 		printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x",
850 		E.e_seq, E.e_datalen, E.e_eot);
851 	ENDDEBUG
852 
853 	IFTRACE(D_STASH)
854 		tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
855 		E.e_seq, E.e_datalen, E.e_eot, 0);
856 	ENDTRACE
857 
858 	if ( E.e_eot ) {
859 		register struct mbuf *n = E.e_data;
860 
861 		/* sigh. have to go through this again! */
862 		/* a kludgy optimization would be to take care of this in
863 		 * tp_input (oh, horrors!)
864 		 */
865 		while (n->m_next )
866 			n = n->m_next;
867 
868 		n->m_act = MNULL; /* set on tp_input */
869 
870 		n->m_flags |= M_EOR;
871 	}
872 	sbappendrecord (&tpcb->tp_sock->so_rcv, E.e_data);
873 	IFDEBUG(D_STASH)
874 		dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
875 			"stash 0: so_rcv after appending");
876 	ENDDEBUG
877 }
878