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