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