Checkpoint as of first successful tp connection, before posix, &
[unix-history] / usr / src / sys / netiso / tp_subr.c
CommitLineData
a2c3ecd0
KS
1/***********************************************************
2 Copyright IBM Corporation 1987
3
4 All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that the name of IBM not be
11used in advertising or publicity pertaining to distribution of the
12software without specific, written prior permission.
13
14IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
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
43static 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
a50e2bc0
KS
55#include "tp_ip.h"
56#include "iso.h"
57#include "argo_debug.h"
58#include "tp_timer.h"
59#include "tp_param.h"
60#include "tp_stat.h"
61#include "tp_pcb.h"
62#include "tp_tpdu.h"
63#include "tp_trace.h"
64#include "tp_meas.h"
65#include "tp_seq.h"
a2c3ecd0
KS
66
67int tp_emit();
68static 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 */
105int
106tp_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
156void
157tp_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 */
197int
198tp_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)
a50e2bc0 216 tpmeas(tpcb->tp_lref, TPtime_ack_rcvd, (struct timeval *)0, seq, 0, 0);
a2c3ecd0
KS
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 */
347static void
348tp_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 */
a2c3ecd0
KS
394tp_send(tpcb)
395 register struct tp_pcb *tpcb;
396{
397 register int len;
398 register struct mbuf *m; /* the one we're inspecting now */
399 struct mbuf *mb;/* beginning of this tpdu */
a2c3ecd0
KS
400 struct mbuf *nextrecord; /* NOT next tpdu but next sb record */
401 struct sockbuf *sb = &tpcb->tp_sock->so_snd;
402 int maxsize = tpcb->tp_l_tpdusize
403 - tp_headersize(DT_TPDU_type, tpcb)
404 - (tpcb->tp_use_checksum?4:0) ;
405 unsigned int eotsdu_reached=0;
406 SeqNum lowseq, highseq ;
407 SeqNum lowsave;
408#ifdef TP_PERF_MEAS
409 struct timeval send_start_time;
410#endif TP_PERF_MEAS
411
412 lowsave = lowseq = SEQ(tpcb, tpcb->tp_sndhiwat + 1);
413
414 ASSERT( tpcb->tp_cong_win > 0 && tpcb->tp_cong_win < 0xffff);
415
416 if( tpcb->tp_rx_strat & TPRX_USE_CW ) {
417 /*first hiseq is temp vbl*/
418 highseq = MIN(tpcb->tp_fcredit, tpcb->tp_cong_win);
419 } else {
420 highseq = tpcb->tp_fcredit;
421 }
422 highseq = SEQ(tpcb, tpcb->tp_snduna + highseq);
423
424 SEQ_DEC(tpcb, highseq);
425
426 IFDEBUG(D_DATA)
427 printf(
428 "tp_send enter tpcb 0x%x l %d -> h %d\ndump of sb_mb:\n",
429 tpcb, lowseq, highseq);
430 dump_mbuf(sb->sb_mb, "sb_mb:");
431 ENDDEBUG
432 IFTRACE(D_DATA)
433 tptraceTPCB( TPPTmisc, "tp_send lowsave sndhiwat snduna",
434 lowsave, tpcb->tp_sndhiwat, tpcb->tp_snduna, 0);
435 tptraceTPCB( TPPTmisc, "tp_send low high fcredit congwin",
436 lowseq, highseq, tpcb->tp_fcredit, tpcb->tp_cong_win);
437 ENDTRACE
438
439
440 if ( SEQ_GT(tpcb, lowseq, highseq) )
441 return ; /* don't send, don't change hiwat, don't set timers */
442
443 IFPERF(tpcb)
444 GET_CUR_TIME(&send_start_time);
445 ENDPERF
446
447 ASSERT( SEQ_LEQ(tpcb, lowseq, highseq) );
448 SEQ_DEC(tpcb, lowseq);
449
450 IFTRACE(D_DATA)
451 tptraceTPCB( TPPTmisc, "tp_send 2 low high fcredit congwin",
452 lowseq, highseq, tpcb->tp_fcredit, tpcb->tp_cong_win);
453 ENDTRACE
454
455 while( SEQ_LT( tpcb, lowseq, highseq ) ) {
456 mb = m = sb->sb_mb;
457 if (m == (struct mbuf *)0) {
458 break; /* empty socket buffer */
459 }
a50e2bc0 460 if (tpcb->tp_Xsnd.sb_mb) {
a2c3ecd0 461 register SeqNum Xuna = * (mtod(m, SeqNum *));
a2c3ecd0
KS
462 IFTRACE(D_XPD)
463 tptraceTPCB( TPPTmisc,
464 "tp_send XPD mark low high tpcb.Xuna",
465 Xuna, lowseq, highseq, tpcb->tp_Xuna);
466 ENDTRACE
a50e2bc0
KS
467 /* stop sending here because there are unacked XPD which were
468 * given to us before the next data were.
469 */
470 IncStat(ts_xpd_intheway);
471 break;
a2c3ecd0 472 }
a2c3ecd0 473 eotsdu_reached = 0;
a2c3ecd0 474 nextrecord = m->m_act;
a50e2bc0 475 for (len = 0; m; m = m->m_next) {
a2c3ecd0 476 len += m->m_len;
a50e2bc0 477 if (m->m_flags & M_EOR)
a2c3ecd0 478 eotsdu_reached = 1;
a2c3ecd0 479 sbfree(sb, m); /* reduce counts in socket buffer */
a2c3ecd0 480 }
a50e2bc0
KS
481 m = sb->sb_mb = nextrecord;
482 IFTRACE(D_STASH)
483 tptraceTPCB(TPPTmisc, "tp_send whole mbuf: m_len len maxsize",
484 0, mb->m_len, len, maxsize);
485 ENDTRACE
a2c3ecd0
KS
486
487 if ( len == 0 && !eotsdu_reached) {
488 /* THIS SHOULD NEVER HAPPEN! */
489 ASSERT( 0 );
490 goto done;
491 }
492
a2c3ecd0
KS
493 /* If we arrive here one of the following holds:
494 * 1. We have exactly <maxsize> octets of whole mbufs,
495 * 2. We accumulated <maxsize> octets using partial mbufs,
496 * 3. We found an TPMT_EOT or an XPD mark
497 * 4. We hit the end of a chain through m_next.
498 * In this case, we'd LIKE to continue with the next record,
499 * but for the time being, for simplicity, we'll stop here.
500 * In all cases, m points to mbuf containing first octet to be
501 * sent in the tpdu AFTER the one we're going to send now,
502 * or else m is null.
503 *
504 * The chain we're working on now begins at mb and has length <len>.
505 */
506
507 IFTRACE(D_STASH)
508 tptraceTPCB( TPPTmisc,
509 "tp_send mcopy low high eotsdu_reached len",
510 lowseq, highseq, eotsdu_reached, len);
511 ENDTRACE
512
513 /* make a copy - mb goes into the retransmission list
514 * while m gets emitted. m_copy won't copy a zero-length mbuf.
515 */
516 if(len) {
517 if( (m = m_copy(mb, 0, len )) == MNULL ) {
518 goto done;
519 }
520 } else {
521 /* eotsdu reached */
522 MGET(m, M_WAIT, TPMT_DATA);
523 if (m == NULL)
524 goto done;
525 m->m_len = 0;
526 m->m_act = MNULL;
527 }
528
529 SEQ_INC(tpcb,lowseq); /* it was decremented at the beginning */
530 {
531 struct tp_rtc *t;
532 /* make an rtc and put it at the end of the chain */
533
534 TP_MAKE_RTC( t, lowseq, eotsdu_reached, mb, len, lowseq,
535 TPMT_SNDRTC);
536 t->tprt_next = (struct tp_rtc *)0;
537
538 if ( tpcb->tp_sndhiwat_rtc != (struct tp_rtc *)0 )
539 tpcb->tp_sndhiwat_rtc->tprt_next = t;
540 else {
541 ASSERT( tpcb->tp_snduna_rtc == (struct tp_rtc *)0 );
542 tpcb->tp_snduna_rtc = t;
543 }
544
545 tpcb->tp_sndhiwat_rtc = t;
546 }
547
548 IFTRACE(D_DATA)
549 tptraceTPCB( TPPTmisc,
550 "tp_send emitting DT lowseq eotsdu_reached",
551 lowseq, eotsdu_reached, 0, 0);
552 ENDTRACE
553 if( tpcb->tp_sock->so_error =
554 tp_emit(DT_TPDU_type, tpcb, lowseq, eotsdu_reached, m) ) {
555 /* error */
556 SEQ_DEC(tpcb, lowseq);
557 goto done;
558 }
559 /* set the transmit-time for computation of round-trip times */
560 bcopy( (caddr_t)&time,
561 (caddr_t)&( tpcb->tp_rttemit[ lowseq & TP_RTT_NUM ] ),
562 sizeof(struct timeval));
563
564 }
565
566done:
567 IFPERF(tpcb)
568 {
569 register int npkts;
570 struct timeval send_end_time;
571 register struct timeval *t;
572
573 npkts = lowseq;
574 SEQ_INC(tpcb, npkts);
575 npkts = SEQ_SUB(tpcb, npkts, lowsave);
576
577 if(npkts > 0)
578 tpcb->tp_Nwindow++;
579
580 if (npkts > TP_PM_MAX)
581 npkts = TP_PM_MAX;
582
583 GET_TIME_SINCE(&send_start_time, &send_end_time);
584 t = &(tpcb->tp_p_meas->tps_sendtime[npkts]);
585 t->tv_sec =
586 SMOOTH( long, TP_RTT_ALPHA, t->tv_sec, send_end_time.tv_sec);
587 t->tv_usec =
588 SMOOTH( long, TP_RTT_ALPHA, t->tv_usec, send_end_time.tv_usec);
589
590 if ( SEQ_LT(tpcb, lowseq, highseq) ) {
591 IncPStat(tpcb, tps_win_lim_by_data[npkts] );
592 } else {
593 IncPStat(tpcb, tps_win_lim_by_cdt[npkts] );
594 /* not true with congestion-window being used */
595 }
596 tpmeas( tpcb->tp_lref,
597 TPsbsend, &send_end_time, lowsave, tpcb->tp_Nwindow, npkts);
598 }
599 ENDPERF
600
601 tpcb->tp_sndhiwat = lowseq;
602
603 if ( SEQ_LEQ(tpcb, lowsave, tpcb->tp_sndhiwat) &&
604 (tpcb->tp_class != TP_CLASS_0) )
605 tp_etimeout(tpcb->tp_refp, TM_data_retrans, lowsave,
606 tpcb->tp_sndhiwat,
607 (u_int)tpcb->tp_Nretrans, (int)tpcb->tp_dt_ticks);
608 IFTRACE(D_DATA)
609 tptraceTPCB( TPPTmisc,
610 "tp_send at end: sndhiwat lowseq eotsdu_reached error",
611 tpcb->tp_sndhiwat, lowseq, eotsdu_reached, tpcb->tp_sock->so_error);
612
613 ENDTRACE
614}
615
616/*
617 * NAME: tp_stash()
618 * CALLED FROM:
619 * tp.trans on arrival of a DT tpdu
620 * FUNCTION, ARGUMENTS, and RETURN VALUE:
621 * Returns 1 if
622 * a) something new arrived and it's got eotsdu_reached bit on,
623 * b) this arrival was caused other out-of-sequence things to be
624 * accepted, or
625 * c) this arrival is the highest seq # for which we last gave credit
626 * (sender just sent a whole window)
627 * In other words, returns 1 if tp should send an ack immediately, 0 if
628 * the ack can wait a while.
629 *
630 * Note: this implementation no longer renegs on credit, (except
631 * when debugging option D_RENEG is on, for the purpose of testing
632 * ack subsequencing), so we don't need to check for incoming tpdus
633 * being in a reneged portion of the window.
634 */
635
636int
637tp_stash( tpcb, e )
638 register struct tp_pcb *tpcb;
639 register struct tp_event *e;
640{
641 register int ack_reason= tpcb->tp_ack_strat & ACK_STRAT_EACH;
642 /* 0--> delay acks until full window */
643 /* 1--> ack each tpdu */
644 int newrec = 0;
645
646#ifndef lint
647#define E e->ATTR(DT_TPDU)
648#else lint
649#define E e->ev_union.EV_DT_TPDU
650#endif lint
651
652 if ( E.e_eot ) {
653 register struct mbuf *n = E.e_data;
a50e2bc0
KS
654 n->m_flags |= M_EOR;
655 }
a2c3ecd0 656 IFDEBUG(D_STASH)
a2c3ecd0
KS
657 dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
658 "stash: so_rcv before appending");
659 dump_mbuf(E.e_data,
660 "stash: e_data before appending");
661 ENDDEBUG
a2c3ecd0
KS
662
663 IFPERF(tpcb)
664 PStat(tpcb, Nb_from_ll) += E.e_datalen;
665 tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
a50e2bc0 666 E.e_seq, (u_int)PStat(tpcb, Nb_from_ll), (u_int)E.e_datalen);
a2c3ecd0
KS
667 ENDPERF
668
669 if( E.e_seq == tpcb->tp_rcvnxt ) {
670
671 IFDEBUG(D_STASH)
672 printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x\n",
673 E.e_seq, E.e_datalen, E.e_eot);
674 ENDDEBUG
675
676 IFTRACE(D_STASH)
677 tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
678 E.e_seq, E.e_datalen, E.e_eot, 0);
679 ENDTRACE
680
a50e2bc0
KS
681 sbappend(&tpcb->tp_sock->so_rcv, E.e_data);
682
a2c3ecd0
KS
683 if (newrec = E.e_eot ) /* ASSIGNMENT */
684 ack_reason |= ACK_EOT;
685
686 SEQ_INC( tpcb, tpcb->tp_rcvnxt );
687 /*
688 * move chains from the rtc list to the socket buffer
689 * and free the rtc header
690 */
691 {
692 register struct tp_rtc **r = &tpcb->tp_rcvnxt_rtc;
693 register struct tp_rtc *s = tpcb->tp_rcvnxt_rtc;
694
695 while (s != (struct tp_rtc *)0 && s->tprt_seq == tpcb->tp_rcvnxt) {
696 *r = s->tprt_next;
697
a50e2bc0 698 sbappend(&tpcb->tp_sock->so_rcv, s->tprt_data);
a2c3ecd0
KS
699
700 SEQ_INC( tpcb, tpcb->tp_rcvnxt );
701
702 (void) m_free( dtom( s ) );
703 s = *r;
704 ack_reason |= ACK_REORDER;
705 }
706 }
707 IFDEBUG(D_STASH)
708 dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
709 "stash: so_rcv after appending");
710 ENDDEBUG
711
712 } else {
713 register struct tp_rtc **s = &tpcb->tp_rcvnxt_rtc;
714 register struct tp_rtc *r = tpcb->tp_rcvnxt_rtc;
715 register struct tp_rtc *t;
716
717 IFTRACE(D_STASH)
718 tptraceTPCB(TPPTmisc, "stash Reseq: seq rcvnxt lcdt",
719 E.e_seq, tpcb->tp_rcvnxt, tpcb->tp_lcredit, 0);
720 ENDTRACE
721
722 r = tpcb->tp_rcvnxt_rtc;
723 while (r != (struct tp_rtc *)0 && SEQ_LT(tpcb, r->tprt_seq, E.e_seq)) {
724 s = &r->tprt_next;
725 r = r->tprt_next;
726 }
727
728 if (r == (struct tp_rtc *)0 || SEQ_GT(tpcb, r->tprt_seq, E.e_seq) ) {
729 IncStat(ts_dt_ooo);
730
731 IFTRACE(D_STASH)
732 tptrace(TPPTmisc,
733 "tp_stash OUT OF ORDER- MAKE RTC: seq, 1st seq in list\n",
734 E.e_seq, r->tprt_seq,0,0);
735 ENDTRACE
736 IFDEBUG(D_STASH)
737 printf("tp_stash OUT OF ORDER- MAKE RTC\n");
738 ENDDEBUG
739 TP_MAKE_RTC(t, E.e_seq, E.e_eot, E.e_data, E.e_datalen, 0,
740 TPMT_RCVRTC);
741
742 *s = t;
743 t->tprt_next = (struct tp_rtc *)r;
744 ack_reason = ACK_DONT;
745 goto done;
746 } else {
747 IFDEBUG(D_STASH)
748 printf("tp_stash - drop & ack\n");
749 ENDDEBUG
750
751 /* retransmission - drop it and force an ack */
752 IncStat(ts_dt_dup);
753 IFPERF(tpcb)
754 IncPStat(tpcb, tps_n_ack_cuz_dup);
755 ENDPERF
756
757 m_freem( E.e_data );
758 ack_reason |= ACK_DUP;
759 goto done;
760 }
761 }
762
763
764 /*
765 * an ack should be sent when at least one of the
766 * following holds:
767 * a) we've received a TPDU with EOTSDU set
768 * b) the TPDU that just arrived represents the
769 * full window last advertised, or
770 * c) when seq X arrives [ where
771 * X = last_sent_uwe - 1/2 last_lcredit_sent
772 * (the packet representing 1/2 the last advertised window) ]
773 * and lcredit at the time of X arrival > last_lcredit_sent/2
774 * In other words, if the last ack sent advertised cdt=8 and uwe = 8
775 * then when seq 4 arrives I'd like to send a new ack
776 * iff the credit at the time of 4's arrival is > 4.
777 * The other end thinks it has cdt of 4 so if local cdt
778 * is still 4 there's no point in sending an ack, but if
779 * my credit has increased because the receiver has taken
780 * some data out of the buffer (soreceive doesn't notify
781 * me until the SYSTEM CALL finishes), I'd like to tell
782 * the other end.
783 */
784
785done:
786 {
787 LOCAL_CREDIT(tpcb);
788
789 if ( E.e_seq == tpcb->tp_sent_uwe )
790 ack_reason |= ACK_STRAT_FULLWIN;
791
792 IFTRACE(D_STASH)
793 tptraceTPCB(TPPTmisc,
794 "end of stash, eot, ack_reason, sent_uwe ",
795 E.e_eot, ack_reason, tpcb->tp_sent_uwe, 0);
796 ENDTRACE
797
798 if ( ack_reason == ACK_DONT ) {
799 IncStat( ts_ackreason[ACK_DONT] );
800 return 0;
801 } else {
802 IFPERF(tpcb)
803 if(ack_reason & ACK_EOT) {
804 IncPStat(tpcb, tps_n_ack_cuz_eot);
805 }
806 if(ack_reason & ACK_STRAT_EACH) {
807 IncPStat(tpcb, tps_n_ack_cuz_strat);
808 } else if(ack_reason & ACK_STRAT_FULLWIN) {
809 IncPStat(tpcb, tps_n_ack_cuz_fullwin);
810 } else if(ack_reason & ACK_REORDER) {
811 IncPStat(tpcb, tps_n_ack_cuz_reorder);
812 }
813 tpmeas(tpcb->tp_lref, TPtime_ack_sent, 0,
814 SEQ_ADD(tpcb, E.e_seq, 1), 0, 0);
815 ENDPERF
816 {
817 register int i;
818
819 /* keep track of all reasons that apply */
820 for( i=1; i<_ACK_NUM_REASONS_ ;i++) {
821 if( ack_reason & (1<<i) )
822 IncStat( ts_ackreason[i] );
823 }
824 }
825 return 1;
826 }
827 }
828}
829
830/* class zero version */
831void
832tp0_stash( tpcb, e )
833 register struct tp_pcb *tpcb;
834 register struct tp_event *e;
835{
836#ifndef lint
837#define E e->ATTR(DT_TPDU)
838#else lint
839#define E e->ev_union.EV_DT_TPDU
840#endif lint
841
842 IFPERF(tpcb)
843 PStat(tpcb, Nb_from_ll) += E.e_datalen;
844 tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
845 E.e_seq, PStat(tpcb, Nb_from_ll), E.e_datalen);
846 ENDPERF
847
848 IFDEBUG(D_STASH)
849 printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x",
850 E.e_seq, E.e_datalen, E.e_eot);
851 ENDDEBUG
852
853 IFTRACE(D_STASH)
854 tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
855 E.e_seq, E.e_datalen, E.e_eot, 0);
856 ENDTRACE
857
858 if ( E.e_eot ) {
859 register struct mbuf *n = E.e_data;
860
861 /* sigh. have to go through this again! */
862 /* a kludgy optimization would be to take care of this in
863 * tp_input (oh, horrors!)
864 */
865 while (n->m_next )
866 n = n->m_next;
867
868 n->m_act = MNULL; /* set on tp_input */
869
a50e2bc0 870 n->m_flags |= M_EOR;
a2c3ecd0 871 }
a50e2bc0 872 sbappendrecord (&tpcb->tp_sock->so_rcv, E.e_data);
a2c3ecd0
KS
873 IFDEBUG(D_STASH)
874 dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
875 "stash 0: so_rcv after appending");
876 ENDDEBUG
877}