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