1 /*********************************************************** 2 Copyright IBM Corporation 1987 3 4 All Rights Reserved 5 6 Permission to use, copy, modify, and distribute this software and its 7 documentation for any purpose and without fee is hereby granted, 8 provided that the above copyright notice appear in all copies and that 9 both that copyright notice and this permission notice appear in 10 supporting documentation, and that the name of IBM not be 11 used in advertising or publicity pertaining to distribution of the 12 software without specific, written prior permission. 13 14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20 SOFTWARE. 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 43 static 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 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" 66 67 int tp_emit(); 68 static 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 */ 105 int 106 tp_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 156 void 157 tp_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 */ 197 int 198 tp_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) 216 tpmeas(tpcb->tp_lref, TPtime_ack_rcvd, (struct timeval *)0, seq, 0, 0); 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 */ 347 static void 348 tp_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 */ 394 tp_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 */ 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 } 460 if (tpcb->tp_Xsnd.sb_mb) { 461 register SeqNum Xuna = * (mtod(m, SeqNum *)); 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 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; 472 } 473 eotsdu_reached = 0; 474 nextrecord = m->m_act; 475 for (len = 0; m; m = m->m_next) { 476 len += m->m_len; 477 if (m->m_flags & M_EOR) 478 eotsdu_reached = 1; 479 sbfree(sb, m); /* reduce counts in socket buffer */ 480 } 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 486 487 if ( len == 0 && !eotsdu_reached) { 488 /* THIS SHOULD NEVER HAPPEN! */ 489 ASSERT( 0 ); 490 goto done; 491 } 492 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 566 done: 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 636 int 637 tp_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; 654 n->m_flags |= M_EOR; 655 } 656 IFDEBUG(D_STASH) 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 662 663 IFPERF(tpcb) 664 PStat(tpcb, Nb_from_ll) += E.e_datalen; 665 tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time, 666 E.e_seq, (u_int)PStat(tpcb, Nb_from_ll), (u_int)E.e_datalen); 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 681 sbappend(&tpcb->tp_sock->so_rcv, E.e_data); 682 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 698 sbappend(&tpcb->tp_sock->so_rcv, s->tprt_data); 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 785 done: 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 */ 831 void 832 tp0_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 870 n->m_flags |= M_EOR; 871 } 872 sbappendrecord (&tpcb->tp_sock->so_rcv, E.e_data); 873 IFDEBUG(D_STASH) 874 dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb, 875 "stash 0: so_rcv after appending"); 876 ENDDEBUG 877 } 878