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