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