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