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