xref: /csrg-svn/sys/netiso/tp_subr.c (revision 36413)
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 "../netiso/tp_ip.h"
56 #include "../netiso/iso.h"
57 #include "../netiso/argo_debug.h"
58 #include "../netiso/tp_timer.h"
59 #include "../netiso/tp_param.h"
60 #include "../netiso/tp_stat.h"
61 #include "../netiso/tp_pcb.h"
62 #include "../netiso/tp_tpdu.h"
63 #include "../netiso/tp_trace.h"
64 #include "../netiso/tp_meas.h"
65 #include "../netiso/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, 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 
395 /* For xpd marks we use mbufs of a special type with length 0;
396  * the m_next field is really the seq number of the xpd tpdu that
397  * must be acked before more normal data may be sent
398  */
399 
400 tp_send(tpcb)
401 	register struct tp_pcb	*tpcb;
402 {
403 	register int			len;
404 	register struct mbuf	*m; /* the one we're inspecting now */
405 	struct mbuf				*mb;/* beginning of this tpdu */
406 	register struct mbuf	**n;/* link field we'll be modifying when we
407 								take mb-->m out of the socket buffer */
408 	struct mbuf 			*nextrecord; /* NOT next tpdu but next sb record */
409 	struct 	sockbuf			*sb = &tpcb->tp_sock->so_snd;
410 	int						maxsize = tpcb->tp_l_tpdusize
411 										- tp_headersize(DT_TPDU_type, tpcb)
412 										- (tpcb->tp_use_checksum?4:0) ;
413 	unsigned int			eotsdu_reached=0;
414 	SeqNum					lowseq, highseq ;
415 	SeqNum					lowsave;
416 #ifdef TP_PERF_MEAS
417 	struct timeval 			send_start_time;
418 #endif TP_PERF_MEAS
419 
420 	lowsave =  lowseq = SEQ(tpcb, tpcb->tp_sndhiwat + 1);
421 
422 	ASSERT( tpcb->tp_cong_win > 0 && tpcb->tp_cong_win < 0xffff);
423 
424 	if( tpcb->tp_rx_strat & TPRX_USE_CW ) {
425 			/*first hiseq is temp vbl*/
426 		highseq = MIN(tpcb->tp_fcredit, tpcb->tp_cong_win);
427 	} else {
428 		highseq = tpcb->tp_fcredit;
429 	}
430 	highseq = SEQ(tpcb, tpcb->tp_snduna + highseq);
431 
432 	SEQ_DEC(tpcb, highseq);
433 
434 	IFDEBUG(D_DATA)
435 		printf(
436 			"tp_send enter tpcb 0x%x l %d -> h %d\ndump of sb_mb:\n",
437 			tpcb, lowseq, highseq);
438 		dump_mbuf(sb->sb_mb, "sb_mb:");
439 	ENDDEBUG
440 	IFTRACE(D_DATA)
441 		tptraceTPCB( TPPTmisc, "tp_send lowsave sndhiwat snduna",
442 			lowsave, tpcb->tp_sndhiwat,  tpcb->tp_snduna, 0);
443 		tptraceTPCB( TPPTmisc, "tp_send low high fcredit congwin",
444 			lowseq, highseq, tpcb->tp_fcredit,  tpcb->tp_cong_win);
445 	ENDTRACE
446 
447 
448 	if	( SEQ_GT(tpcb, lowseq, highseq) )
449 			return ; /* don't send, don't change hiwat, don't set timers */
450 
451 	IFPERF(tpcb)
452 		GET_CUR_TIME(&send_start_time);
453 	ENDPERF
454 
455 	ASSERT( SEQ_LEQ(tpcb, lowseq, highseq) );
456 	SEQ_DEC(tpcb, lowseq);
457 
458 	IFTRACE(D_DATA)
459 		tptraceTPCB( TPPTmisc, "tp_send 2 low high fcredit congwin",
460 			lowseq, highseq, tpcb->tp_fcredit,  tpcb->tp_cong_win);
461 	ENDTRACE
462 
463 	while( SEQ_LT( tpcb, lowseq, highseq ) ) {
464 		mb = m = sb->sb_mb;
465 		if (m == (struct mbuf *)0) {
466 			break; /* empty socket buffer */
467 		}
468 		if ( m->m_type == TPMT_XPD ) {
469 			register SeqNum Xuna = * (mtod(m, SeqNum *));
470 			register struct mbuf *mnext = MNULL;
471 			IFTRACE(D_XPD)
472 				tptraceTPCB( TPPTmisc,
473 					"tp_send XPD mark low high tpcb.Xuna",
474 					Xuna, lowseq, highseq, tpcb->tp_Xuna);
475 			ENDTRACE
476 			if( SEQ_GEQ(tpcb, Xuna, tpcb->tp_Xuna))  {
477 				/* stop sending here because there are unacked XPD which were
478 				 * given to us before the next data were. Leave mark in place.
479 				 */
480 				IncStat(ts_xpd_intheway);
481 				break;
482 			}
483 			/* otherwise, mark is obsolete; delete it */
484 			sbfree(sb, m); /* have to do this to delete the sb_mbcnt */
485 			sb->sb_mb = m->m_act;
486 			IncStat(ts_xpdmark_del);
487 			if( mnext = m_free(m) ) {
488 				IFTRACE(D_XPD)
489 					tptraceTPCB( TPPTmisc,
490 						"tp_send XPD mark deleted mnext old_act new_act",
491 						mnext, sb->sb_mb, mnext->m_act, 0);
492 				ENDTRACE
493 				IFDEBUG(D_XPD)
494 					printf(
495 			"tp_send XPD mark deleted mnext 0x%x old act 0x%x new act 0x%x\n",
496 						mnext, sb->sb_mb, mnext->m_act, 0);
497 				ENDDEBUG
498 				mnext->m_act = sb->sb_mb;
499 				sb->sb_mb = mnext;
500 			}
501 			continue;
502 		}
503 		n  = &sb->sb_mb;
504 		eotsdu_reached = 0;
505 		len =  0;
506 		nextrecord = m->m_act;
507 		while ( eotsdu_reached == 0  &&  len < maxsize  && m != MNULL) {
508 			*n = m; /* meaningless first time through the loop */
509 			len += m->m_len;
510 			if ( len > maxsize ) {
511 				/*
512 				 * Won't use the whole mbuf - split into 2 mbufs.
513 				 */
514 				int amount = m->m_len + maxsize - len;
515 				struct mbuf *mx;
516 
517 				/* copy the part we are NOT using and put that back in the
518 				 * socket buf; leave m with this tpdu chain; adjust its fields
519 				 */
520 				IFTRACE(D_STASH)
521 					tptraceTPCB(TPPTmisc,
522 					"tp_send SPLIT len, amount, m->m_len, tpdusize",
523 						len, amount, m->m_len, maxsize);
524 				ENDTRACE
525 				mx = m_copy(m, amount, m->m_len - amount); /* preserves type */
526 				mx->m_next = m->m_next;
527 				mx->m_act = m->m_act; /* preserve */
528 
529 				CHANGE_MTYPE(m, TPMT_DATA);
530 				m->m_next = (struct mbuf *)0;
531 				m->m_act = (struct mbuf *)0; /* not strictly necessary */
532 				m->m_len = amount;
533 
534 				/* would do an sbfree but don't want the mbcnt to be
535 				 * decremented since it was never sballoced
536 				 */
537 				sb->sb_cc -= amount;
538 				len = maxsize;
539 				m = mx;
540 				break;
541 			}
542 
543 			/* going to use the whole mbuf */
544 			IFTRACE(D_STASH)
545 				tptraceTPCB(TPPTmisc, "tp_send whole mbuf: m_len len maxsize",
546 					 0, m->m_len, len, maxsize);
547 			ENDTRACE
548 
549 			if ( m->m_type == TPMT_EOT )
550 				eotsdu_reached = 1;
551 
552 			sbfree(sb, m); /* reduce counts in socket buffer */
553 			n = &m->m_next;
554 			m = m->m_next;
555 
556 			*n = (struct mbuf *)0;  /* unlink the to-be-sent stuff from
557 				the stuff still in the sb_mb so when we do the m_free
558 				it won't clobber part of the socket buffer */
559 		}
560 
561 		if ( len == 0 && !eotsdu_reached) {
562 			/* THIS SHOULD NEVER HAPPEN! */
563 			ASSERT( 0 );
564 			goto done;
565 		}
566 
567 		/* sb_mb is non-null */
568 		if(m) {
569 			sb->sb_mb = m;
570 			if(nextrecord != m)
571 				m->m_act = nextrecord;
572 		} else
573 			sb->sb_mb = nextrecord;
574 
575 		/* If we arrive here one of the following holds:
576 		 * 1. We have exactly <maxsize> octets of whole mbufs,
577 		 * 2. We accumulated <maxsize> octets using partial mbufs,
578 		 * 3. We found an TPMT_EOT or an XPD mark
579 		 * 4. We hit the end of a chain through m_next.
580 		 *    In this case, we'd LIKE to continue with the next record,
581 		 *    but for the time being, for simplicity, we'll stop here.
582 		 * In all cases, m points to mbuf containing first octet to be
583 		 * sent in the tpdu AFTER the one we're going to send now,
584 		 * or else m is null.
585 		 *
586 		 * The chain we're working on now begins at mb and has length <len>.
587 		 */
588 
589 		IFTRACE(D_STASH)
590 			tptraceTPCB( TPPTmisc,
591 				"tp_send mcopy low high eotsdu_reached len",
592 				lowseq, highseq, eotsdu_reached, len);
593 		ENDTRACE
594 
595 		/* make a copy - mb goes into the retransmission list
596 		 * while m gets emitted.  m_copy won't copy a zero-length mbuf.
597 		 */
598 		if(len) {
599 			if( (m = m_copy(mb, 0, len )) == MNULL ) {
600 				goto done;
601 			}
602 		} else {
603 			/* eotsdu reached */
604 			MGET(m, M_WAIT, TPMT_DATA);
605 			if (m == NULL)
606 				goto done;
607 			m->m_len = 0;
608 			m->m_act = MNULL;
609 		}
610 
611 		SEQ_INC(tpcb,lowseq);	/* it was decremented at the beginning */
612 		{
613 			struct tp_rtc *t;
614 			/* make an rtc and put it at the end of the chain */
615 
616 			TP_MAKE_RTC( t, lowseq, eotsdu_reached, mb, len, lowseq,
617 				TPMT_SNDRTC);
618 			t->tprt_next = (struct tp_rtc *)0;
619 
620 			if ( tpcb->tp_sndhiwat_rtc != (struct tp_rtc *)0 )
621 				tpcb->tp_sndhiwat_rtc->tprt_next = t;
622 			else {
623 				ASSERT( tpcb->tp_snduna_rtc == (struct tp_rtc *)0 );
624 				tpcb->tp_snduna_rtc = t;
625 			}
626 
627 			tpcb->tp_sndhiwat_rtc = t;
628 		}
629 
630 		IFTRACE(D_DATA)
631 			tptraceTPCB( TPPTmisc,
632 				"tp_send emitting DT lowseq eotsdu_reached",
633 				lowseq, eotsdu_reached, 0, 0);
634 		ENDTRACE
635 		if( tpcb->tp_sock->so_error =
636 			tp_emit(DT_TPDU_type, tpcb, lowseq, eotsdu_reached, m) )  {
637 			/* error */
638 			SEQ_DEC(tpcb, lowseq);
639 			goto done;
640 		}
641 		/* set the transmit-time for computation of round-trip times */
642 		bcopy( (caddr_t)&time,
643 				(caddr_t)&( tpcb->tp_rttemit[ lowseq & TP_RTT_NUM ] ),
644 				sizeof(struct timeval));
645 
646 	}
647 
648 done:
649 	IFPERF(tpcb)
650 		{
651 			register int npkts;
652 			struct timeval send_end_time;
653 			register struct timeval *t;
654 
655 			npkts = lowseq;
656 			SEQ_INC(tpcb, npkts);
657 			npkts = SEQ_SUB(tpcb, npkts, lowsave);
658 
659 			if(npkts > 0)
660 				tpcb->tp_Nwindow++;
661 
662 			if (npkts > TP_PM_MAX)
663 				npkts = TP_PM_MAX;
664 
665 			GET_TIME_SINCE(&send_start_time, &send_end_time);
666 			t = &(tpcb->tp_p_meas->tps_sendtime[npkts]);
667 			t->tv_sec =
668 				SMOOTH( long, TP_RTT_ALPHA, t->tv_sec, send_end_time.tv_sec);
669 			t->tv_usec =
670 				SMOOTH( long, TP_RTT_ALPHA, t->tv_usec, send_end_time.tv_usec);
671 
672 			if ( SEQ_LT(tpcb, lowseq, highseq) ) {
673 				IncPStat(tpcb, tps_win_lim_by_data[npkts] );
674 			} else {
675 				IncPStat(tpcb, tps_win_lim_by_cdt[npkts] );
676 				/* not true with congestion-window being used */
677 			}
678 			tpmeas( tpcb->tp_lref,
679 					TPsbsend, &send_end_time, lowsave, tpcb->tp_Nwindow, npkts);
680 		}
681 	ENDPERF
682 
683 	tpcb->tp_sndhiwat = lowseq;
684 
685 	if ( SEQ_LEQ(tpcb, lowsave, tpcb->tp_sndhiwat)  &&
686 			(tpcb->tp_class != TP_CLASS_0) )
687 			tp_etimeout(tpcb->tp_refp, TM_data_retrans, lowsave,
688 				tpcb->tp_sndhiwat,
689 				(u_int)tpcb->tp_Nretrans, (int)tpcb->tp_dt_ticks);
690 	IFTRACE(D_DATA)
691 		tptraceTPCB( TPPTmisc,
692 			"tp_send at end: sndhiwat lowseq eotsdu_reached error",
693 			tpcb->tp_sndhiwat, lowseq, eotsdu_reached, tpcb->tp_sock->so_error);
694 
695 	ENDTRACE
696 }
697 
698 /*
699  * NAME: tp_stash()
700  * CALLED FROM:
701  *	tp.trans on arrival of a DT tpdu
702  * FUNCTION, ARGUMENTS, and RETURN VALUE:
703  * 	Returns 1 if
704  *		a) something new arrived and it's got eotsdu_reached bit on,
705  * 		b) this arrival was caused other out-of-sequence things to be
706  *    	accepted, or
707  * 		c) this arrival is the highest seq # for which we last gave credit
708  *   	(sender just sent a whole window)
709  *  In other words, returns 1 if tp should send an ack immediately, 0 if
710  *  the ack can wait a while.
711  *
712  * Note: this implementation no longer renegs on credit, (except
713  * when debugging option D_RENEG is on, for the purpose of testing
714  * ack subsequencing), so we don't  need to check for incoming tpdus
715  * being in a reneged portion of the window.
716  */
717 
718 int
719 tp_stash( tpcb, e )
720 	register struct tp_pcb		*tpcb;
721 	register struct tp_event	*e;
722 {
723 	register int		ack_reason= tpcb->tp_ack_strat & ACK_STRAT_EACH;
724 									/* 0--> delay acks until full window */
725 									/* 1--> ack each tpdu */
726 	int		newrec = 0;
727 
728 #ifndef lint
729 #define E e->ATTR(DT_TPDU)
730 #else lint
731 #define E e->ev_union.EV_DT_TPDU
732 #endif lint
733 
734 	if ( E.e_eot ) {
735 		register struct mbuf *n = E.e_data;
736 
737 		/* sigh. have to go through this again! */
738 		/* a kludgy optimization would be to take care of this in
739 		 * tp_input (oh, horrors!)
740 		 * BTW, don't set ack_reason here because we don't know if the
741 		 * sequence number is right
742 		 */
743 		while (n->m_next )
744 			n = n->m_next;
745 
746 		n->m_act = MNULL; /* set on tp_input */
747 		CHANGE_MTYPE(n, TPMT_EOT);
748 
749 		IFDEBUG(D_STASH)
750 			printf("EOT! changing m_type of m 0x%x\n",  n);
751 			dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
752 				"stash: so_rcv before appending");
753 			dump_mbuf(E.e_data,
754 				"stash: e_data before appending");
755 		ENDDEBUG
756 	}
757 
758 	IFPERF(tpcb)
759 		PStat(tpcb, Nb_from_ll) += E.e_datalen;
760 		tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
761 				E.e_seq, PStat(tpcb, Nb_from_ll), E.e_datalen);
762 	ENDPERF
763 
764 	if( E.e_seq == tpcb->tp_rcvnxt ) {
765 
766 		IFDEBUG(D_STASH)
767 			printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x\n",
768 			E.e_seq, E.e_datalen, E.e_eot);
769 		ENDDEBUG
770 
771 		IFTRACE(D_STASH)
772 			tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
773 			E.e_seq, E.e_datalen, E.e_eot, 0);
774 		ENDTRACE
775 
776 		if( E.e_datalen == 0 && E.e_eot ) {
777 			IFDEBUG(D_STASH)
778 				printf("stash EQ: appendrec\n");
779 			ENDDEBUG
780 			sbappendrecord (&tpcb->tp_sock->so_rcv, E.e_data);
781 			/* 'cause sbappend won't append something of length zero */
782 		} else {
783 			IFDEBUG(D_STASH)
784 				printf("stash EQ: plain old append\n");
785 			ENDDEBUG
786 			sbappend(&tpcb->tp_sock->so_rcv, E.e_data);
787 		}
788 		if (newrec = E.e_eot ) /* ASSIGNMENT */
789 			ack_reason |= ACK_EOT;
790 
791 		SEQ_INC( tpcb, tpcb->tp_rcvnxt );
792 		/*
793 		 * move chains from the rtc list to the socket buffer
794 		 * and free the rtc header
795 		 */
796 		{
797 			register struct tp_rtc	**r =  &tpcb->tp_rcvnxt_rtc;
798 			register struct tp_rtc	*s = tpcb->tp_rcvnxt_rtc;
799 
800 			while (s != (struct tp_rtc *)0 && s->tprt_seq == tpcb->tp_rcvnxt) {
801 				*r = s->tprt_next;
802 
803 				if ( newrec ) {
804 					sbappendrecord(&tpcb->tp_sock->so_rcv, s->tprt_data);
805 				} else
806 					sbappend(&tpcb->tp_sock->so_rcv, s->tprt_data);
807 				newrec = s->tprt_eot;
808 
809 				SEQ_INC( tpcb, tpcb->tp_rcvnxt );
810 
811 				(void) m_free( dtom( s ) );
812 				s = *r;
813 				ack_reason |= ACK_REORDER;
814 			}
815 		}
816 		IFDEBUG(D_STASH)
817 			dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
818 				"stash: so_rcv after appending");
819 		ENDDEBUG
820 
821 	} else {
822 		register struct tp_rtc **s = &tpcb->tp_rcvnxt_rtc;
823 		register struct tp_rtc *r = tpcb->tp_rcvnxt_rtc;
824 		register struct tp_rtc *t;
825 
826 		IFTRACE(D_STASH)
827 			tptraceTPCB(TPPTmisc, "stash Reseq: seq rcvnxt lcdt",
828 			E.e_seq, tpcb->tp_rcvnxt, tpcb->tp_lcredit, 0);
829 		ENDTRACE
830 
831 		r = tpcb->tp_rcvnxt_rtc;
832 		while (r != (struct tp_rtc *)0  && SEQ_LT(tpcb, r->tprt_seq, E.e_seq)) {
833 			s = &r->tprt_next;
834 			r = r->tprt_next;
835 		}
836 
837 		if (r == (struct tp_rtc *)0 || SEQ_GT(tpcb, r->tprt_seq, E.e_seq) ) {
838 			IncStat(ts_dt_ooo);
839 
840 			IFTRACE(D_STASH)
841 				tptrace(TPPTmisc,
842 				"tp_stash OUT OF ORDER- MAKE RTC: seq, 1st seq in list\n",
843 					E.e_seq, r->tprt_seq,0,0);
844 			ENDTRACE
845 			IFDEBUG(D_STASH)
846 				printf("tp_stash OUT OF ORDER- MAKE RTC\n");
847 			ENDDEBUG
848 			TP_MAKE_RTC(t, E.e_seq, E.e_eot, E.e_data, E.e_datalen, 0,
849 				TPMT_RCVRTC);
850 
851 			*s = t;
852 			t->tprt_next = (struct tp_rtc *)r;
853 			ack_reason = ACK_DONT;
854 			goto done;
855 		} else {
856 			IFDEBUG(D_STASH)
857 				printf("tp_stash - drop & ack\n");
858 			ENDDEBUG
859 
860 			/* retransmission - drop it and force an ack */
861 			IncStat(ts_dt_dup);
862 			IFPERF(tpcb)
863 				IncPStat(tpcb, tps_n_ack_cuz_dup);
864 			ENDPERF
865 
866 			m_freem( E.e_data );
867 			ack_reason |= ACK_DUP;
868 			goto done;
869 		}
870 	}
871 
872 
873 	/*
874 	 * an ack should be sent when at least one of the
875 	 * following holds:
876 	 * a) we've received a TPDU with EOTSDU set
877 	 * b) the TPDU that just arrived represents the
878 	 *    full window last advertised, or
879 	 * c) when seq X arrives [ where
880 	 * 		X = last_sent_uwe - 1/2 last_lcredit_sent
881 	 * 		(the packet representing 1/2 the last advertised window) ]
882 	 * 		and lcredit at the time of X arrival >  last_lcredit_sent/2
883 	 * 		In other words, if the last ack sent advertised cdt=8 and uwe = 8
884 	 * 		then when seq 4 arrives I'd like to send a new ack
885 	 * 		iff the credit at the time of 4's arrival is > 4.
886 	 * 		The other end thinks it has cdt of 4 so if local cdt
887 	 * 		is still 4 there's no point in sending an ack, but if
888 	 * 		my credit has increased because the receiver has taken
889 	 * 		some data out of the buffer (soreceive doesn't notify
890 	 * 		me until the SYSTEM CALL finishes), I'd like to tell
891 	 * 		the other end.
892 	 */
893 
894 done:
895 	{
896 		LOCAL_CREDIT(tpcb);
897 
898 		if ( E.e_seq ==  tpcb->tp_sent_uwe )
899 			ack_reason |= ACK_STRAT_FULLWIN;
900 
901 		IFTRACE(D_STASH)
902 			tptraceTPCB(TPPTmisc,
903 				"end of stash, eot, ack_reason, sent_uwe ",
904 				E.e_eot, ack_reason, tpcb->tp_sent_uwe, 0);
905 		ENDTRACE
906 
907 		if ( ack_reason == ACK_DONT ) {
908 			IncStat( ts_ackreason[ACK_DONT] );
909 			return 0;
910 		} else {
911 			IFPERF(tpcb)
912 				if(ack_reason & ACK_EOT) {
913 					IncPStat(tpcb, tps_n_ack_cuz_eot);
914 				}
915 				if(ack_reason & ACK_STRAT_EACH) {
916 					IncPStat(tpcb, tps_n_ack_cuz_strat);
917 				} else if(ack_reason & ACK_STRAT_FULLWIN) {
918 					IncPStat(tpcb, tps_n_ack_cuz_fullwin);
919 				} else if(ack_reason & ACK_REORDER) {
920 					IncPStat(tpcb, tps_n_ack_cuz_reorder);
921 				}
922 				tpmeas(tpcb->tp_lref, TPtime_ack_sent, 0,
923 							SEQ_ADD(tpcb, E.e_seq, 1), 0, 0);
924 			ENDPERF
925 			{
926 				register int i;
927 
928 				/* keep track of all reasons that apply */
929 				for( i=1; i<_ACK_NUM_REASONS_ ;i++) {
930 					if( ack_reason & (1<<i) )
931 						IncStat( ts_ackreason[i] );
932 				}
933 			}
934 			return 1;
935 		}
936 	}
937 }
938 
939 /* class zero version */
940 void
941 tp0_stash( tpcb, e )
942 	register struct tp_pcb		*tpcb;
943 	register struct tp_event	*e;
944 {
945 #ifndef lint
946 #define E e->ATTR(DT_TPDU)
947 #else lint
948 #define E e->ev_union.EV_DT_TPDU
949 #endif lint
950 
951 	IFPERF(tpcb)
952 		PStat(tpcb, Nb_from_ll) += E.e_datalen;
953 		tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
954 				E.e_seq, PStat(tpcb, Nb_from_ll), E.e_datalen);
955 	ENDPERF
956 
957 	IFDEBUG(D_STASH)
958 		printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x",
959 		E.e_seq, E.e_datalen, E.e_eot);
960 	ENDDEBUG
961 
962 	IFTRACE(D_STASH)
963 		tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
964 		E.e_seq, E.e_datalen, E.e_eot, 0);
965 	ENDTRACE
966 
967 	if ( E.e_eot ) {
968 		register struct mbuf *n = E.e_data;
969 
970 		/* sigh. have to go through this again! */
971 		/* a kludgy optimization would be to take care of this in
972 		 * tp_input (oh, horrors!)
973 		 */
974 		while (n->m_next )
975 			n = n->m_next;
976 
977 		n->m_act = MNULL; /* set on tp_input */
978 
979 		CHANGE_MTYPE(n, TPMT_EOT);
980 	}
981 	if( E.e_datalen == 0 && E.e_eot ) {
982 		sbappendrecord (&tpcb->tp_sock->so_rcv, E.e_data);
983 	} else {
984 		sbappend(&tpcb->tp_sock->so_rcv, E.e_data);
985 	}
986 	IFDEBUG(D_STASH)
987 		dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
988 			"stash 0: so_rcv after appending");
989 	ENDDEBUG
990 }
991 
992