xref: /original-bsd/sys/netiso/tp_subr.c (revision 02e832b2)
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.20 (Berkeley) 10/11/92
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 <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/mbuf.h>
54 #include <sys/socket.h>
55 #include <sys/socketvar.h>
56 #include <sys/protosw.h>
57 #include <sys/errno.h>
58 #include <sys/time.h>
59 #include <sys/kernel.h>
60 
61 #include <netiso/tp_ip.h>
62 #include <netiso/iso.h>
63 #include <netiso/argo_debug.h>
64 #include <netiso/tp_timer.h>
65 #include <netiso/tp_param.h>
66 #include <netiso/tp_stat.h>
67 #include <netiso/tp_pcb.h>
68 #include <netiso/tp_tpdu.h>
69 #include <netiso/tp_trace.h>
70 #include <netiso/tp_meas.h>
71 #include <netiso/tp_seq.h>
72 
73 int		tp_emit(), tp_sbdrop();
74 int		tprexmtthresh = 3;
75 extern int	ticks;
76 void	tp_send();
77 
78 /*
79  * CALLED FROM:
80  *	tp.trans, when an XAK arrives
81  * FUNCTION and ARGUMENTS:
82  * 	Determines if the sequence number (seq) from the XAK
83  * 	acks anything new.  If so, drop the appropriate tpdu
84  * 	from the XPD send queue.
85  * RETURN VALUE:
86  * 	Returns 1 if it did this, 0 if the ack caused no action.
87  */
88 int
89 tp_goodXack(tpcb, seq)
90 	struct tp_pcb	*tpcb;
91 	SeqNum 			seq;
92 {
93 
94 	IFTRACE(D_XPD)
95 		tptraceTPCB(TPPTgotXack,
96 			seq, tpcb->tp_Xuna, tpcb->tp_Xsndnxt, tpcb->tp_sndnew,
97 			tpcb->tp_snduna);
98 	ENDTRACE
99 
100 	if ( seq == tpcb->tp_Xuna ) {
101 			tpcb->tp_Xuna = tpcb->tp_Xsndnxt;
102 
103 			/* DROP 1 packet from the Xsnd socket buf - just so happens
104 			 * that only one packet can be there at any time
105 			 * so drop the whole thing.  If you allow > 1 packet
106 			 * the socket buffer, then you'll have to keep
107 			 * track of how many characters went w/ each XPD tpdu, so this
108 			 * will get messier
109 			 */
110 			IFDEBUG(D_XPD)
111 				dump_mbuf(tpcb->tp_Xsnd.sb_mb,
112 					"tp_goodXack Xsnd before sbdrop");
113 			ENDDEBUG
114 
115 			IFTRACE(D_XPD)
116 				tptraceTPCB(TPPTmisc,
117 					"goodXack: dropping cc ",
118 					(int)(tpcb->tp_Xsnd.sb_cc),
119 					0,0,0);
120 			ENDTRACE
121 			sbdroprecord(&tpcb->tp_Xsnd);
122 			return 1;
123 	}
124 	return 0;
125 }
126 
127 /*
128  * CALLED FROM:
129  *  tp_good_ack()
130  * FUNCTION and ARGUMENTS:
131  *  updates
132  *  smoothed average round trip time (*rtt)
133  *  roundtrip time variance (*rtv) - actually deviation, not variance
134  *  given the new value (diff)
135  * RETURN VALUE:
136  * void
137  */
138 
139 void
140 tp_rtt_rtv(tpcb)
141 register struct tp_pcb *tpcb;
142 {
143 	int old = tpcb->tp_rtt;
144 	int delta, elapsed = ticks - tpcb->tp_rttemit;
145 
146 	if (tpcb->tp_rtt != 0) {
147 		/*
148 		 * rtt is the smoothed round trip time in machine clock ticks (hz).
149 		 * It is stored as a fixed point number, unscaled (unlike the tcp
150 		 * srtt).  The rationale here is that it is only significant to the
151 		 * nearest unit of slowtimo, which is at least 8 machine clock ticks
152 		 * so there is no need to scale.  The smoothing is done according
153 		 * to the same formula as TCP (rtt = rtt*7/8 + measured_rtt/8).
154 		 */
155 		delta = elapsed - tpcb->tp_rtt;
156 		if ((tpcb->tp_rtt += (delta >> TP_RTT_ALPHA)) <= 0)
157 			tpcb->tp_rtt = 1;
158 		/*
159 		 * rtv is a smoothed accumulated mean difference, unscaled
160 		 * for reasons expressed above.
161 		 * It is smoothed with an alpha of .75, and the round trip timer
162 		 * will be set to rtt + 4*rtv, also as TCP does.
163 		 */
164 		if (delta < 0)
165 			delta = -delta;
166 		if ((tpcb->tp_rtv += ((delta - tpcb->tp_rtv) >> TP_RTV_ALPHA)) <= 0)
167 			tpcb->tp_rtv = 1;
168 	} else {
169 		/*
170 		 * No rtt measurement yet - use the unsmoothed rtt.
171 		 * Set the variance to half the rtt (so our first
172 		 * retransmit happens at 3*rtt)
173 		 */
174 		tpcb->tp_rtt = elapsed;
175 		tpcb->tp_rtv = elapsed >> 1;
176 	}
177 	tpcb->tp_rttemit = 0;
178 	tpcb->tp_rxtshift = 0;
179 	/*
180 	 * Quoting TCP: "the retransmit should happen at rtt + 4 * rttvar.
181 	 * Because of the way we do the smoothing, srtt and rttvar
182 	 * will each average +1/2 tick of bias.  When we compute
183 	 * the retransmit timer, we want 1/2 tick of rounding and
184 	 * 1 extra tick because of +-1/2 tick uncertainty in the
185 	 * firing of the timer.  The bias will give us exactly the
186 	 * 1.5 tick we need.  But, because the bias is
187 	 * statistical, we have to test that we don't drop below
188 	 * the minimum feasible timer (which is 2 ticks)."
189 	 */
190 	TP_RANGESET(tpcb->tp_dt_ticks, TP_REXMTVAL(tpcb),
191 		tpcb->tp_peer_acktime, 128 /* XXX */);
192 	IFDEBUG(D_RTT)
193 		printf("%s tpcb 0x%x, elapsed %d, delta %d, rtt %d, rtv %d, old %d\n",
194 			"tp_rtt_rtv:",tpcb,elapsed,delta,tpcb->tp_rtt,tpcb->tp_rtv,old);
195 	ENDDEBUG
196 	tpcb->tp_rxtcur = tpcb->tp_dt_ticks;
197 }
198 
199 /*
200  * CALLED FROM:
201  *  tp.trans when an AK arrives
202  * FUNCTION and ARGUMENTS:
203  * 	Given (cdt), the credit from the AK tpdu, and
204  *	(seq), the sequence number from the AK tpdu,
205  *  tp_goodack() determines if the AK acknowledges something in the send
206  * 	window, and if so, drops the appropriate packets from the retransmission
207  *  list, computes the round trip time, and updates the retransmission timer
208  *  based on the new smoothed round trip time.
209  * RETURN VALUE:
210  * 	Returns 1 if
211  * 	EITHER it actually acked something heretofore unacknowledged
212  * 	OR no news but the credit should be processed.
213  * 	If something heretofore unacked was acked with this sequence number,
214  * 	the appropriate tpdus are dropped from the retransmission control list,
215  * 	by calling tp_sbdrop().
216  * 	No need to see the tpdu itself.
217  */
218 int
219 tp_goodack(tpcb, cdt, seq, subseq)
220 	register struct tp_pcb	*tpcb;
221 	u_int					cdt;
222 	register SeqNum			seq;
223 	u_int					subseq;
224 {
225 	int 	old_fcredit;
226 	int 	bang = 0; 	/* bang --> ack for something heretofore unacked */
227 	u_int	bytes_acked;
228 
229 	IFDEBUG(D_ACKRECV)
230 		printf("goodack tpcb 0x%x seq 0x%x cdt %d una 0x%x new 0x%x nxt 0x%x\n",
231 			tpcb, seq, cdt, tpcb->tp_snduna, tpcb->tp_sndnew, tpcb->tp_sndnxt);
232 	ENDDEBUG
233 	IFTRACE(D_ACKRECV)
234 		tptraceTPCB(TPPTgotack,
235 			seq,cdt, tpcb->tp_snduna,tpcb->tp_sndnew,subseq);
236 	ENDTRACE
237 
238 	IFPERF(tpcb)
239 		tpmeas(tpcb->tp_lref, TPtime_ack_rcvd, (struct timeval *)0, seq, 0, 0);
240 	ENDPERF
241 
242 	if (seq == tpcb->tp_snduna) {
243 		if (subseq < tpcb->tp_r_subseq ||
244 			(subseq == tpcb->tp_r_subseq && cdt <= tpcb->tp_fcredit)) {
245 		discard_the_ack:
246 			IFDEBUG(D_ACKRECV)
247 				printf("goodack discard : tpcb 0x%x subseq %d r_subseq %d\n",
248 					tpcb, subseq, tpcb->tp_r_subseq);
249 			ENDDEBUG
250 			goto done;
251 		}
252 		if (cdt == tpcb->tp_fcredit /*&& thus subseq > tpcb->tp_r_subseq */) {
253 			tpcb->tp_r_subseq = subseq;
254 			if (tpcb->tp_timer[TM_data_retrans] == 0)
255 				tpcb->tp_dupacks = 0;
256 			else if (++tpcb->tp_dupacks == tprexmtthresh) {
257 				/* partner went out of his way to signal with different
258 				   subsequences that he has the same lack of an expected
259 				   packet.  This may be an early indiciation of a loss */
260 
261 				SeqNum onxt = tpcb->tp_sndnxt;
262 				struct mbuf *onxt_m = tpcb->tp_sndnxt_m;
263 				u_int win = min(tpcb->tp_fcredit,
264 							tpcb->tp_cong_win / tpcb->tp_l_tpdusize) / 2;
265 				IFDEBUG(D_ACKRECV)
266 					printf("%s tpcb 0x%x seq 0x%x rttseq 0x%x onxt 0x%x\n",
267 						"goodack dupacks:", tpcb, seq, tpcb->tp_rttseq, onxt);
268 				ENDDEBUG
269 				if (win < 2)
270 					win = 2;
271 				tpcb->tp_ssthresh = win * tpcb->tp_l_tpdusize;
272 				tpcb->tp_timer[TM_data_retrans] = 0;
273 				tpcb->tp_rttemit = 0;
274 				tpcb->tp_sndnxt = tpcb->tp_snduna;
275 				tpcb->tp_sndnxt_m = 0;
276 				tpcb->tp_cong_win = tpcb->tp_l_tpdusize;
277 				tp_send(tpcb);
278 				tpcb->tp_cong_win = tpcb->tp_ssthresh +
279 					tpcb->tp_dupacks * tpcb->tp_l_tpdusize;
280 				if (SEQ_GT(tpcb, onxt, tpcb->tp_sndnxt)) {
281 					tpcb->tp_sndnxt = onxt;
282 					tpcb->tp_sndnxt_m = onxt_m;
283 				}
284 
285 			} else if (tpcb->tp_dupacks > tprexmtthresh) {
286 				tpcb->tp_cong_win += tpcb->tp_l_tpdusize;
287 			}
288 			goto done;
289 		}
290 	} else if (SEQ_LT(tpcb, seq, tpcb->tp_snduna))
291 		goto discard_the_ack;
292 	/*
293 	 * If the congestion window was inflated to account
294 	 * for the other side's cached packets, retract it.
295 	 */
296 	if (tpcb->tp_dupacks > tprexmtthresh &&
297 		tpcb->tp_cong_win > tpcb->tp_ssthresh)
298 			tpcb->tp_cong_win = tpcb->tp_ssthresh;
299 	tpcb->tp_r_subseq = subseq;
300 	old_fcredit = tpcb->tp_fcredit;
301 	tpcb->tp_fcredit = cdt;
302 	if (cdt > tpcb->tp_maxfcredit)
303 		tpcb->tp_maxfcredit = cdt;
304 	tpcb->tp_dupacks = 0;
305 
306 	if (IN_SWINDOW(tpcb, seq, tpcb->tp_snduna, tpcb->tp_sndnew)) {
307 
308 		tpsbcheck(tpcb, 0);
309 		bytes_acked = tp_sbdrop(tpcb, seq);
310 		tpsbcheck(tpcb, 1);
311 		/*
312 		 * If transmit timer is running and timed sequence
313 		 * number was acked, update smoothed round trip time.
314 		 * Since we now have an rtt measurement, cancel the
315 		 * timer backoff (cf., Phil Karn's retransmit alg.).
316 		 * Recompute the initial retransmit timer.
317 		 */
318 		if (tpcb->tp_rttemit && SEQ_GT(tpcb, seq, tpcb->tp_rttseq))
319 			tp_rtt_rtv(tpcb);
320 		/*
321 		 * If all outstanding data is acked, stop retransmit timer.
322 		 * If there is more data to be acked, restart retransmit
323 		 * timer, using current (possibly backed-off) value.
324 		 * OSI combines the keepalive and persistance functions.
325 		 * So, there is no persistance timer per se, to restart.
326 		 */
327 		if (tpcb->tp_class != TP_CLASS_0)
328 			tpcb->tp_timer[TM_data_retrans] =
329 				(seq == tpcb->tp_sndnew) ? 0 : tpcb->tp_rxtcur;
330 		/*
331 		 * When new data is acked, open the congestion window.
332 		 * If the window gives us less than ssthresh packets
333 		 * in flight, open exponentially (maxseg per packet).
334 		 * Otherwise open linearly: maxseg per window
335 		 * (maxseg^2 / cwnd per packet), plus a constant
336 		 * fraction of a packet (maxseg/8) to help larger windows
337 		 * open quickly enough.
338 		 */
339 		{
340 			u_int cw = tpcb->tp_cong_win, incr = tpcb->tp_l_tpdusize;
341 
342 			incr = min(incr, bytes_acked);
343 			if (cw > tpcb->tp_ssthresh)
344 				incr = incr * incr / cw + incr / 8;
345 			tpcb->tp_cong_win =
346 				min(cw + incr, tpcb->tp_sock->so_snd.sb_hiwat);
347 		}
348 		tpcb->tp_snduna = seq;
349 		if (SEQ_LT(tpcb, tpcb->tp_sndnxt, seq)) {
350 				tpcb->tp_sndnxt = seq;
351 				tpcb->tp_sndnxt_m = 0;
352 		}
353 		bang++;
354 	}
355 
356 	if( cdt != 0 && old_fcredit == 0 ) {
357 		tpcb->tp_sendfcc = 1;
358 	}
359 	if (cdt == 0) {
360 		if (old_fcredit != 0)
361 			IncStat(ts_zfcdt);
362 		/* The following might mean that the window shrunk */
363 		if (tpcb->tp_timer[TM_data_retrans]) {
364 			tpcb->tp_timer[TM_data_retrans] = 0;
365 			tpcb->tp_timer[TM_sendack] = tpcb->tp_dt_ticks;
366 			if (tpcb->tp_sndnxt != tpcb->tp_snduna) {
367 				tpcb->tp_sndnxt = tpcb->tp_snduna;
368 				tpcb->tp_sndnxt_m = 0;
369 			}
370 		}
371 	}
372 	tpcb->tp_fcredit = cdt;
373 	bang |= (old_fcredit < cdt);
374 
375 done:
376 	IFDEBUG(D_ACKRECV)
377 		printf("goodack returns 0x%x, cdt 0x%x ocdt 0x%x cwin 0x%x\n",
378 			bang, cdt, old_fcredit, tpcb->tp_cong_win);
379 	ENDDEBUG
380 	/* if (bang) XXXXX Very bad to remove this test, but somethings broken */
381 		tp_send(tpcb);
382 	return (bang);
383 }
384 
385 /*
386  * CALLED FROM:
387  *  tp_goodack()
388  * FUNCTION and ARGUMENTS:
389  *  drops everything up TO but not INCLUDING seq # (seq)
390  *  from the retransmission queue.
391  */
392 tp_sbdrop(tpcb, seq)
393 	register struct 	tp_pcb 			*tpcb;
394 	SeqNum					seq;
395 {
396 	struct sockbuf *sb = &tpcb->tp_sock->so_snd;
397 	register int i = SEQ_SUB(tpcb, seq, tpcb->tp_snduna);
398 	int	oldcc = sb->sb_cc, oldi = i;
399 
400 	if (i >= tpcb->tp_seqhalf)
401 		printf("tp_spdropping too much -- should panic");
402 	while (i-- > 0)
403 		sbdroprecord(sb);
404 	IFDEBUG(D_ACKRECV)
405 		printf("tp_sbdroping %d pkts %d bytes on %x at 0x%x\n",
406 			oldi, oldcc - sb->sb_cc, tpcb, seq);
407 	ENDDEBUG
408 	if (sb->sb_flags & SB_NOTIFY)
409 		sowwakeup(tpcb->tp_sock);
410 	return (oldcc - sb->sb_cc);
411 }
412 
413 /*
414  * CALLED FROM:
415  * 	tp.trans on user send request, arrival of AK and arrival of XAK
416  * FUNCTION and ARGUMENTS:
417  * 	Emits tpdus starting at sequence number (tpcb->tp_sndnxt).
418  * 	Emits until a) runs out of data, or  b) runs into an XPD mark, or
419  * 			c) it hits seq number (highseq) limited by cong or credit.
420  *
421  * 	If you want XPD to buffer > 1 du per socket buffer, you can
422  * 	modifiy this to issue XPD tpdus also, but then it'll have
423  * 	to take some argument(s) to distinguish between the type of DU to
424  * 	hand tp_emit.
425  *
426  * 	When something is sent for the first time, its time-of-send
427  * 	is stashed (in system clock ticks rather than pf_slowtimo ticks).
428  *  When the ack arrives, the smoothed round-trip time is figured
429  *  using this value.
430  */
431 void
432 tp_send(tpcb)
433 	register struct tp_pcb	*tpcb;
434 {
435 	register int			len;
436 	register struct mbuf	*m;
437 	struct mbuf				*mb = 0;
438 	struct 	sockbuf			*sb = &tpcb->tp_sock->so_snd;
439 	unsigned int			eotsdu = 0;
440 	SeqNum					highseq, checkseq;
441 	int						idle, idleticks, off, cong_win;
442 #ifdef TP_PERF_MEAS
443 	int			 			send_start_time = ticks;
444 	SeqNum					oldnxt = tpcb->tp_sndnxt;
445 #endif TP_PERF_MEAS
446 
447 	idle = (tpcb->tp_snduna == tpcb->tp_sndnew);
448 	if (idle) {
449 		idleticks = tpcb->tp_inact_ticks - tpcb->tp_timer[TM_inact];
450 		if (idleticks > tpcb->tp_dt_ticks)
451 			/*
452 			 * We have been idle for "a while" and no acks are
453 			 * expected to clock out any data we send --
454 			 * slow start to get ack "clock" running again.
455 			 */
456 			tpcb->tp_cong_win = tpcb->tp_l_tpdusize;
457 	}
458 
459 	cong_win = tpcb->tp_cong_win;
460 	highseq = SEQ(tpcb, tpcb->tp_fcredit + tpcb->tp_snduna);
461 	if (tpcb->tp_Xsnd.sb_mb)
462 		highseq = SEQ_MIN(tpcb, highseq, tpcb->tp_sndnew);
463 
464 	IFDEBUG(D_DATA)
465 		printf("tp_send enter tpcb 0x%x nxt 0x%x win %d high 0x%x\n",
466 				tpcb, tpcb->tp_sndnxt, cong_win, highseq);
467 	ENDDEBUG
468 	IFTRACE(D_DATA)
469 		tptraceTPCB( TPPTmisc, "tp_send sndnew snduna",
470 			tpcb->tp_sndnew,  tpcb->tp_snduna, 0, 0);
471 		tptraceTPCB( TPPTmisc, "tp_send tpcb->tp_sndnxt win fcredit congwin",
472 			tpcb->tp_sndnxt, cong_win, tpcb->tp_fcredit, tpcb->tp_cong_win);
473 	ENDTRACE
474 	IFTRACE(D_DATA)
475 		tptraceTPCB( TPPTmisc, "tp_send 2 nxt high fcredit congwin",
476 			tpcb->tp_sndnxt, highseq, tpcb->tp_fcredit, cong_win);
477 	ENDTRACE
478 
479 	if (tpcb->tp_sndnxt_m)
480 		m = tpcb->tp_sndnxt_m;
481 	else {
482 		off = SEQ_SUB(tpcb, tpcb->tp_sndnxt, tpcb->tp_snduna);
483 		for (m = sb->sb_mb; m && off > 0; m = m->m_next)
484 			off--;
485 	}
486 send:
487 	/*
488 	 * Avoid silly window syndrome here . . . figure out how!
489 	 */
490 	checkseq = tpcb->tp_sndnum;
491 	if (idle && SEQ_LT(tpcb, tpcb->tp_sndnum, highseq))
492 		checkseq = highseq; /* i.e. DON'T retain highest assigned packet */
493 
494 	while ((SEQ_LT(tpcb, tpcb->tp_sndnxt, highseq)) && m && cong_win > 0) {
495 
496 		eotsdu = (m->m_flags & M_EOR) != 0;
497 		len = m->m_pkthdr.len;
498 		if (tpcb->tp_sndnxt == checkseq && eotsdu == 0 &&
499 			len < (tpcb->tp_l_tpdusize / 2))
500 				break;  /* Nagle . . . . . */
501 		cong_win -= len;
502 		/* make a copy - mb goes into the retransmission list
503 		 * while m gets emitted.  m_copy won't copy a zero-length mbuf.
504 		 */
505 		mb = m;
506 		m = m_copy(mb, 0, M_COPYALL);
507 		if (m == MNULL)
508 				break;
509 		IFTRACE(D_STASH)
510 			tptraceTPCB( TPPTmisc,
511 				"tp_send mcopy nxt high eotsdu len",
512 				tpcb->tp_sndnxt, highseq, eotsdu, len);
513 		ENDTRACE
514 
515 		IFDEBUG(D_DATA)
516 			printf("tp_sending tpcb 0x%x nxt 0x%x\n",
517 				tpcb, tpcb->tp_sndnxt);
518 		ENDDEBUG
519 		/* when headers are precomputed, may need to fill
520 			   in checksum here */
521 		if (tpcb->tp_sock->so_error =
522 			tp_emit(DT_TPDU_type, tpcb, tpcb->tp_sndnxt, eotsdu, m)) {
523 			/* error */
524 			break;
525 		}
526 		m = mb->m_nextpkt;
527 		tpcb->tp_sndnxt_m = m;
528 		if (tpcb->tp_sndnxt == tpcb->tp_sndnew) {
529 			SEQ_INC(tpcb, tpcb->tp_sndnew);
530 			/*
531 			 * Time this transmission if not a retransmission and
532 			 * not currently timing anything.
533 			 */
534 			if (tpcb->tp_rttemit == 0) {
535 				tpcb->tp_rttemit = ticks;
536 				tpcb->tp_rttseq = tpcb->tp_sndnxt;
537 			}
538 			tpcb->tp_sndnxt = tpcb->tp_sndnew;
539 		} else
540 			SEQ_INC(tpcb, tpcb->tp_sndnxt);
541 		/*
542 		 * Set retransmit timer if not currently set.
543 		 * Initial value for retransmit timer is smoothed
544 		 * round-trip time + 2 * round-trip time variance.
545 		 * Initialize shift counter which is used for backoff
546 		 * of retransmit time.
547 		 */
548 		if (tpcb->tp_timer[TM_data_retrans] == 0 &&
549 			tpcb->tp_class != TP_CLASS_0) {
550 			tpcb->tp_timer[TM_data_retrans] = tpcb->tp_dt_ticks;
551 			tpcb->tp_timer[TM_sendack] = tpcb->tp_keepalive_ticks;
552 			tpcb->tp_rxtshift = 0;
553 		}
554 	}
555 	if (SEQ_GT(tpcb, tpcb->tp_sndnew, tpcb->tp_sndnum))
556 		tpcb->tp_oktonagle = 0;
557 #ifdef TP_PERF_MEAS
558 	IFPERF(tpcb)
559 		{
560 			register int npkts;
561 			int	 elapsed = ticks - send_start_time, *t;
562 			struct timeval now;
563 
564 			npkts = SEQ_SUB(tpcb, tpcb->tp_sndnxt, oldnxt);
565 
566 			if (npkts > 0)
567 				tpcb->tp_Nwindow++;
568 
569 			if (npkts > TP_PM_MAX)
570 				npkts = TP_PM_MAX;
571 
572 			t = &(tpcb->tp_p_meas->tps_sendtime[npkts]);
573 			*t += (t - elapsed) >> TP_RTT_ALPHA;
574 
575 			if (mb == 0) {
576 				IncPStat(tpcb, tps_win_lim_by_data[npkts] );
577 			} else {
578 				IncPStat(tpcb, tps_win_lim_by_cdt[npkts] );
579 				/* not true with congestion-window being used */
580 			}
581 			now.tv_sec = elapsed / hz;
582 			now.tv_usec = (elapsed - (hz * now.tv_sec)) * 1000000 / hz;
583 			tpmeas( tpcb->tp_lref,
584 					TPsbsend, &elapsed, newseq, tpcb->tp_Nwindow, npkts);
585 		}
586 	ENDPERF
587 #endif TP_PERF_MEAS
588 
589 
590 	IFTRACE(D_DATA)
591 		tptraceTPCB( TPPTmisc,
592 			"tp_send at end: new nxt eotsdu error",
593 			tpcb->tp_sndnew, tpcb->tp_sndnxt, eotsdu, tpcb->tp_sock->so_error);
594 
595 	ENDTRACE
596 }
597 
598 int TPNagleok;
599 int TPNagled;
600 
601 tp_packetize(tpcb, m, eotsdu)
602 register struct tp_pcb *tpcb;
603 register struct mbuf *m;
604 int eotsdu;
605 {
606 	register struct mbuf *n;
607 	register struct sockbuf *sb = &tpcb->tp_sock->so_snd;
608 	int	maxsize = tpcb->tp_l_tpdusize
609 			- tp_headersize(DT_TPDU_type, tpcb)
610 			- (tpcb->tp_use_checksum?4:0) ;
611 	int totlen = m->m_pkthdr.len;
612 	struct mbuf *m_split();
613 	/*
614 	 * Pre-packetize the data in the sockbuf
615 	 * according to negotiated mtu.  Do it here
616 	 * where we can safely wait for mbufs.
617 	 *
618 	 * This presumes knowledge of sockbuf conventions.
619 	 * TODO: allocate space for header and fill it in (once!).
620 	 */
621 	IFDEBUG(D_DATA)
622 		printf("SEND BF: maxsize %d totlen %d eotsdu %d sndnum 0x%x\n",
623 			maxsize, totlen, eotsdu, tpcb->tp_sndnum);
624 	ENDTRACE
625 	if (tpcb->tp_oktonagle) {
626 		if ((n = sb->sb_mb) == 0)
627 			panic("tp_packetize");
628 		while (n->m_act)
629 			n = n->m_act;
630 		if (n->m_flags & M_EOR)
631 			panic("tp_packetize 2");
632 		SEQ_INC(tpcb, tpcb->tp_sndnum);
633 		if (totlen + n->m_pkthdr.len < maxsize) {
634 			/* There is an unsent packet with space, combine data */
635 			struct mbuf *old_n = n;
636 			tpsbcheck(tpcb,3);
637 			n->m_pkthdr.len += totlen;
638 			while (n->m_next)
639 				n = n->m_next;
640 			sbcompress(sb, m, n);
641 			tpsbcheck(tpcb,4);
642 			n = old_n;
643 			TPNagled++;
644 			goto out;
645 		}
646 	}
647 	while (m) {
648 		n = m;
649 		if (totlen > maxsize) {
650 			if ((m = m_split(n, maxsize, M_WAIT)) == 0)
651 				panic("tp_packetize");
652 		} else
653 			m = 0;
654 		totlen -= maxsize;
655 		tpsbcheck(tpcb, 5);
656 		sbappendrecord(sb, n);
657 		tpsbcheck(tpcb, 6);
658 		SEQ_INC(tpcb, tpcb->tp_sndnum);
659 	}
660 out:
661 	if (eotsdu) {
662 		n->m_flags |= M_EOR;  /* XXX belongs at end */
663 		tpcb->tp_oktonagle = 0;
664 	} else {
665 		SEQ_DEC(tpcb, tpcb->tp_sndnum);
666 		tpcb->tp_oktonagle = 1;
667 		TPNagleok++;
668 	}
669 	IFDEBUG(D_DATA)
670 		printf("SEND out: oktonagle %d sndnum 0x%x\n",
671 			tpcb->tp_oktonagle, tpcb->tp_sndnum);
672 	ENDTRACE
673 	return 0;
674 }
675 
676 
677 /*
678  * NAME: tp_stash()
679  * CALLED FROM:
680  *	tp.trans on arrival of a DT tpdu
681  * FUNCTION, ARGUMENTS, and RETURN VALUE:
682  * 	Returns 1 if
683  *		a) something new arrived and it's got eotsdu_reached bit on,
684  * 		b) this arrival was caused other out-of-sequence things to be
685  *    	accepted, or
686  * 		c) this arrival is the highest seq # for which we last gave credit
687  *   	(sender just sent a whole window)
688  *  In other words, returns 1 if tp should send an ack immediately, 0 if
689  *  the ack can wait a while.
690  *
691  * Note: this implementation no longer renegs on credit, (except
692  * when debugging option D_RENEG is on, for the purpose of testing
693  * ack subsequencing), so we don't  need to check for incoming tpdus
694  * being in a reneged portion of the window.
695  */
696 
697 tp_stash(tpcb, e)
698 	register struct tp_pcb		*tpcb;
699 	register struct tp_event	*e;
700 {
701 	register int		ack_reason= tpcb->tp_ack_strat & ACK_STRAT_EACH;
702 									/* 0--> delay acks until full window */
703 									/* 1--> ack each tpdu */
704 #ifndef lint
705 #define E e->ATTR(DT_TPDU)
706 #else lint
707 #define E e->ev_union.EV_DT_TPDU
708 #endif lint
709 
710 	if ( E.e_eot ) {
711 		register struct mbuf *n = E.e_data;
712 		n->m_flags |= M_EOR;
713 		n->m_act = 0;
714 	}
715 		IFDEBUG(D_STASH)
716 			dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
717 				"stash: so_rcv before appending");
718 			dump_mbuf(E.e_data,
719 				"stash: e_data before appending");
720 		ENDDEBUG
721 
722 	IFPERF(tpcb)
723 		PStat(tpcb, Nb_from_ll) += E.e_datalen;
724 		tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
725 			E.e_seq, (u_int)PStat(tpcb, Nb_from_ll), (u_int)E.e_datalen);
726 	ENDPERF
727 
728 	if (E.e_seq == tpcb->tp_rcvnxt) {
729 
730 		IFDEBUG(D_STASH)
731 			printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x\n",
732 			E.e_seq, E.e_datalen, E.e_eot);
733 		ENDDEBUG
734 
735 		IFTRACE(D_STASH)
736 			tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
737 			E.e_seq, E.e_datalen, E.e_eot, 0);
738 		ENDTRACE
739 
740 		SET_DELACK(tpcb);
741 
742 		sbappend(&tpcb->tp_sock->so_rcv, E.e_data);
743 
744 		SEQ_INC( tpcb, tpcb->tp_rcvnxt );
745 		/*
746 		 * move chains from the reassembly queue to the socket buffer
747 		 */
748 		if (tpcb->tp_rsycnt) {
749 			register struct mbuf **mp;
750 			struct mbuf **mplim;
751 
752 			mp = tpcb->tp_rsyq + (tpcb->tp_rcvnxt % tpcb->tp_maxlcredit);
753 			mplim = tpcb->tp_rsyq + tpcb->tp_maxlcredit;
754 
755 			while (tpcb->tp_rsycnt && *mp) {
756 				sbappend(&tpcb->tp_sock->so_rcv, *mp);
757 				tpcb->tp_rsycnt--;
758 				*mp = 0;
759 				SEQ_INC(tpcb, tpcb->tp_rcvnxt);
760 				ack_reason |= ACK_REORDER;
761 				if (++mp == mplim)
762 					mp = tpcb->tp_rsyq;
763 			}
764 		}
765 		IFDEBUG(D_STASH)
766 			dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
767 				"stash: so_rcv after appending");
768 		ENDDEBUG
769 
770 	} else {
771 		register struct mbuf **mp;
772 		SeqNum uwe;
773 
774 		IFTRACE(D_STASH)
775 			tptraceTPCB(TPPTmisc, "stash Reseq: seq rcvnxt lcdt",
776 			E.e_seq, tpcb->tp_rcvnxt, tpcb->tp_lcredit, 0);
777 		ENDTRACE
778 
779 		if (tpcb->tp_rsyq == 0)
780 			tp_rsyset(tpcb);
781 		uwe = SEQ(tpcb, tpcb->tp_rcvnxt + tpcb->tp_maxlcredit);
782 		if (tpcb->tp_rsyq == 0 ||
783 						!IN_RWINDOW(tpcb, E.e_seq, tpcb->tp_rcvnxt, uwe)) {
784 			ack_reason = ACK_DONT;
785 			m_freem(E.e_data);
786 		} else if (*(mp = tpcb->tp_rsyq + (E.e_seq % tpcb->tp_maxlcredit))) {
787 			IFDEBUG(D_STASH)
788 				printf("tp_stash - drop & ack\n");
789 			ENDDEBUG
790 
791 			/* retransmission - drop it and force an ack */
792 			IncStat(ts_dt_dup);
793 			IFPERF(tpcb)
794 				IncPStat(tpcb, tps_n_ack_cuz_dup);
795 			ENDPERF
796 
797 			m_freem(E.e_data);
798 			ack_reason |= ACK_DUP;
799 		} else {
800 			*mp = E.e_data;
801 			tpcb->tp_rsycnt++;
802 			ack_reason = ACK_DONT;
803 		}
804 	}
805 	/* there were some comments of historical interest here. */
806 	{
807 		LOCAL_CREDIT(tpcb);
808 
809 		if ( E.e_seq ==  tpcb->tp_sent_uwe )
810 			ack_reason |= ACK_STRAT_FULLWIN;
811 
812 		IFTRACE(D_STASH)
813 			tptraceTPCB(TPPTmisc,
814 				"end of stash, eot, ack_reason, sent_uwe ",
815 				E.e_eot, ack_reason, tpcb->tp_sent_uwe, 0);
816 		ENDTRACE
817 
818 		if ( ack_reason == ACK_DONT ) {
819 			IncStat( ts_ackreason[ACK_DONT] );
820 			return 0;
821 		} else {
822 			IFPERF(tpcb)
823 				if(ack_reason & ACK_STRAT_EACH) {
824 					IncPStat(tpcb, tps_n_ack_cuz_strat);
825 				} else if(ack_reason & ACK_STRAT_FULLWIN) {
826 					IncPStat(tpcb, tps_n_ack_cuz_fullwin);
827 				} else if(ack_reason & ACK_REORDER) {
828 					IncPStat(tpcb, tps_n_ack_cuz_reorder);
829 				}
830 				tpmeas(tpcb->tp_lref, TPtime_ack_sent, 0,
831 							SEQ_ADD(tpcb, E.e_seq, 1), 0, 0);
832 			ENDPERF
833 			{
834 				register int i;
835 
836 				/* keep track of all reasons that apply */
837 				for( i=1; i<_ACK_NUM_REASONS_ ;i++) {
838 					if( ack_reason & (1<<i) )
839 						IncStat( ts_ackreason[i] );
840 				}
841 			}
842 			return 1;
843 		}
844 	}
845 }
846 
847 /*
848  * tp_rsyflush - drop all the packets on the reassembly queue.
849  * Do this when closing the socket, or when somebody has changed
850  * the space avaible in the receive socket (XXX).
851  */
852 tp_rsyflush(tpcb)
853 register struct tp_pcb *tpcb;
854 {
855 	register struct mbuf *m, **mp;
856 	if (tpcb->tp_rsycnt) {
857 		for (mp == tpcb->tp_rsyq + tpcb->tp_maxlcredit;
858 									 --mp >= tpcb->tp_rsyq; )
859 			if (*mp) {
860 				tpcb->tp_rsycnt--;
861 				m_freem(*mp);
862 			}
863 		if (tpcb->tp_rsycnt) {
864 			printf("tp_rsyflush %x\n", tpcb);
865 			tpcb->tp_rsycnt = 0;
866 		}
867 	}
868 	free((caddr_t)tpcb->tp_rsyq, M_PCB);
869 	tpcb->tp_rsyq = 0;
870 }
871 
872 tp_rsyset(tpcb)
873 register struct tp_pcb *tpcb;
874 {
875 	register struct socket *so = tpcb->tp_sock;
876 	int maxcredit  = tpcb->tp_xtd_format ? 0xffff : 0xf;
877 	int old_credit = tpcb->tp_maxlcredit;
878 	caddr_t	rsyq;
879 
880 	tpcb->tp_maxlcredit = maxcredit = min(maxcredit,
881 		  (so->so_rcv.sb_hiwat + tpcb->tp_l_tpdusize)/ tpcb->tp_l_tpdusize);
882 
883 	if (old_credit == tpcb->tp_maxlcredit && tpcb->tp_rsyq != 0)
884 		return;
885 	maxcredit *= sizeof(struct mbuf *);
886 	if (tpcb->tp_rsyq)
887 		tp_rsyflush(tpcb);
888 	if (rsyq = (caddr_t)malloc(maxcredit, M_PCB, M_NOWAIT))
889 		bzero(rsyq, maxcredit);
890 	tpcb->tp_rsyq = (struct mbuf **)rsyq;
891 }
892 
893 tpsbcheck(tpcb, i)
894 struct tp_pcb *tpcb;
895 {
896 	register struct mbuf *n, *m;
897 	register int len = 0, mbcnt = 0, pktlen;
898 	struct sockbuf *sb = &tpcb->tp_sock->so_snd;
899 
900 	for (n = sb->sb_mb; n; n = n->m_nextpkt) {
901 		if ((n->m_flags & M_PKTHDR) == 0)
902 			panic("tpsbcheck nohdr");
903 		pktlen = len + n->m_pkthdr.len;
904 	    for (m = n; m; m = m->m_next) {
905 			len += m->m_len;
906 			mbcnt += MSIZE;
907 			if (m->m_flags & M_EXT)
908 				mbcnt += m->m_ext.ext_size;
909 		}
910 		if (len != pktlen) {
911 			printf("test %d; len %d != pktlen %d on mbuf 0x%x\n",
912 				i, len, pktlen, n);
913 			panic("tpsbcheck short");
914 		}
915 	}
916 	if (len != sb->sb_cc || mbcnt != sb->sb_mbcnt) {
917 		printf("test %d: cc %d != %d || mbcnt %d != %d\n", i, len, sb->sb_cc,
918 		    mbcnt, sb->sb_mbcnt);
919 		panic("tpsbcheck");
920 	}
921 }
922