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