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 "../netiso/tp_ip.h" 56 #include "../netiso/iso.h" 57 #include "../netiso/argo_debug.h" 58 #include "../netiso/tp_timer.h" 59 #include "../netiso/tp_param.h" 60 #include "../netiso/tp_stat.h" 61 #include "../netiso/tp_pcb.h" 62 #include "../netiso/tp_tpdu.h" 63 #include "../netiso/tp_trace.h" 64 #include "../netiso/tp_meas.h" 65 #include "../netiso/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, 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 395 /* For xpd marks we use mbufs of a special type with length 0; 396 * the m_next field is really the seq number of the xpd tpdu that 397 * must be acked before more normal data may be sent 398 */ 399 400 tp_send(tpcb) 401 register struct tp_pcb *tpcb; 402 { 403 register int len; 404 register struct mbuf *m; /* the one we're inspecting now */ 405 struct mbuf *mb;/* beginning of this tpdu */ 406 register struct mbuf **n;/* link field we'll be modifying when we 407 take mb-->m out of the socket buffer */ 408 struct mbuf *nextrecord; /* NOT next tpdu but next sb record */ 409 struct sockbuf *sb = &tpcb->tp_sock->so_snd; 410 int maxsize = tpcb->tp_l_tpdusize 411 - tp_headersize(DT_TPDU_type, tpcb) 412 - (tpcb->tp_use_checksum?4:0) ; 413 unsigned int eotsdu_reached=0; 414 SeqNum lowseq, highseq ; 415 SeqNum lowsave; 416 #ifdef TP_PERF_MEAS 417 struct timeval send_start_time; 418 #endif TP_PERF_MEAS 419 420 lowsave = lowseq = SEQ(tpcb, tpcb->tp_sndhiwat + 1); 421 422 ASSERT( tpcb->tp_cong_win > 0 && tpcb->tp_cong_win < 0xffff); 423 424 if( tpcb->tp_rx_strat & TPRX_USE_CW ) { 425 /*first hiseq is temp vbl*/ 426 highseq = MIN(tpcb->tp_fcredit, tpcb->tp_cong_win); 427 } else { 428 highseq = tpcb->tp_fcredit; 429 } 430 highseq = SEQ(tpcb, tpcb->tp_snduna + highseq); 431 432 SEQ_DEC(tpcb, highseq); 433 434 IFDEBUG(D_DATA) 435 printf( 436 "tp_send enter tpcb 0x%x l %d -> h %d\ndump of sb_mb:\n", 437 tpcb, lowseq, highseq); 438 dump_mbuf(sb->sb_mb, "sb_mb:"); 439 ENDDEBUG 440 IFTRACE(D_DATA) 441 tptraceTPCB( TPPTmisc, "tp_send lowsave sndhiwat snduna", 442 lowsave, tpcb->tp_sndhiwat, tpcb->tp_snduna, 0); 443 tptraceTPCB( TPPTmisc, "tp_send low high fcredit congwin", 444 lowseq, highseq, tpcb->tp_fcredit, tpcb->tp_cong_win); 445 ENDTRACE 446 447 448 if ( SEQ_GT(tpcb, lowseq, highseq) ) 449 return ; /* don't send, don't change hiwat, don't set timers */ 450 451 IFPERF(tpcb) 452 GET_CUR_TIME(&send_start_time); 453 ENDPERF 454 455 ASSERT( SEQ_LEQ(tpcb, lowseq, highseq) ); 456 SEQ_DEC(tpcb, lowseq); 457 458 IFTRACE(D_DATA) 459 tptraceTPCB( TPPTmisc, "tp_send 2 low high fcredit congwin", 460 lowseq, highseq, tpcb->tp_fcredit, tpcb->tp_cong_win); 461 ENDTRACE 462 463 while( SEQ_LT( tpcb, lowseq, highseq ) ) { 464 mb = m = sb->sb_mb; 465 if (m == (struct mbuf *)0) { 466 break; /* empty socket buffer */ 467 } 468 if ( m->m_type == TPMT_XPD ) { 469 register SeqNum Xuna = * (mtod(m, SeqNum *)); 470 register struct mbuf *mnext = MNULL; 471 IFTRACE(D_XPD) 472 tptraceTPCB( TPPTmisc, 473 "tp_send XPD mark low high tpcb.Xuna", 474 Xuna, lowseq, highseq, tpcb->tp_Xuna); 475 ENDTRACE 476 if( SEQ_GEQ(tpcb, Xuna, tpcb->tp_Xuna)) { 477 /* stop sending here because there are unacked XPD which were 478 * given to us before the next data were. Leave mark in place. 479 */ 480 IncStat(ts_xpd_intheway); 481 break; 482 } 483 /* otherwise, mark is obsolete; delete it */ 484 sbfree(sb, m); /* have to do this to delete the sb_mbcnt */ 485 sb->sb_mb = m->m_act; 486 IncStat(ts_xpdmark_del); 487 if( mnext = m_free(m) ) { 488 IFTRACE(D_XPD) 489 tptraceTPCB( TPPTmisc, 490 "tp_send XPD mark deleted mnext old_act new_act", 491 mnext, sb->sb_mb, mnext->m_act, 0); 492 ENDTRACE 493 IFDEBUG(D_XPD) 494 printf( 495 "tp_send XPD mark deleted mnext 0x%x old act 0x%x new act 0x%x\n", 496 mnext, sb->sb_mb, mnext->m_act, 0); 497 ENDDEBUG 498 mnext->m_act = sb->sb_mb; 499 sb->sb_mb = mnext; 500 } 501 continue; 502 } 503 n = &sb->sb_mb; 504 eotsdu_reached = 0; 505 len = 0; 506 nextrecord = m->m_act; 507 while ( eotsdu_reached == 0 && len < maxsize && m != MNULL) { 508 *n = m; /* meaningless first time through the loop */ 509 len += m->m_len; 510 if ( len > maxsize ) { 511 /* 512 * Won't use the whole mbuf - split into 2 mbufs. 513 */ 514 int amount = m->m_len + maxsize - len; 515 struct mbuf *mx; 516 517 /* copy the part we are NOT using and put that back in the 518 * socket buf; leave m with this tpdu chain; adjust its fields 519 */ 520 IFTRACE(D_STASH) 521 tptraceTPCB(TPPTmisc, 522 "tp_send SPLIT len, amount, m->m_len, tpdusize", 523 len, amount, m->m_len, maxsize); 524 ENDTRACE 525 mx = m_copy(m, amount, m->m_len - amount); /* preserves type */ 526 mx->m_next = m->m_next; 527 mx->m_act = m->m_act; /* preserve */ 528 529 CHANGE_MTYPE(m, TPMT_DATA); 530 m->m_next = (struct mbuf *)0; 531 m->m_act = (struct mbuf *)0; /* not strictly necessary */ 532 m->m_len = amount; 533 534 /* would do an sbfree but don't want the mbcnt to be 535 * decremented since it was never sballoced 536 */ 537 sb->sb_cc -= amount; 538 len = maxsize; 539 m = mx; 540 break; 541 } 542 543 /* going to use the whole mbuf */ 544 IFTRACE(D_STASH) 545 tptraceTPCB(TPPTmisc, "tp_send whole mbuf: m_len len maxsize", 546 0, m->m_len, len, maxsize); 547 ENDTRACE 548 549 if ( m->m_type == TPMT_EOT ) 550 eotsdu_reached = 1; 551 552 sbfree(sb, m); /* reduce counts in socket buffer */ 553 n = &m->m_next; 554 m = m->m_next; 555 556 *n = (struct mbuf *)0; /* unlink the to-be-sent stuff from 557 the stuff still in the sb_mb so when we do the m_free 558 it won't clobber part of the socket buffer */ 559 } 560 561 if ( len == 0 && !eotsdu_reached) { 562 /* THIS SHOULD NEVER HAPPEN! */ 563 ASSERT( 0 ); 564 goto done; 565 } 566 567 /* sb_mb is non-null */ 568 if(m) { 569 sb->sb_mb = m; 570 if(nextrecord != m) 571 m->m_act = nextrecord; 572 } else 573 sb->sb_mb = nextrecord; 574 575 /* If we arrive here one of the following holds: 576 * 1. We have exactly <maxsize> octets of whole mbufs, 577 * 2. We accumulated <maxsize> octets using partial mbufs, 578 * 3. We found an TPMT_EOT or an XPD mark 579 * 4. We hit the end of a chain through m_next. 580 * In this case, we'd LIKE to continue with the next record, 581 * but for the time being, for simplicity, we'll stop here. 582 * In all cases, m points to mbuf containing first octet to be 583 * sent in the tpdu AFTER the one we're going to send now, 584 * or else m is null. 585 * 586 * The chain we're working on now begins at mb and has length <len>. 587 */ 588 589 IFTRACE(D_STASH) 590 tptraceTPCB( TPPTmisc, 591 "tp_send mcopy low high eotsdu_reached len", 592 lowseq, highseq, eotsdu_reached, len); 593 ENDTRACE 594 595 /* make a copy - mb goes into the retransmission list 596 * while m gets emitted. m_copy won't copy a zero-length mbuf. 597 */ 598 if(len) { 599 if( (m = m_copy(mb, 0, len )) == MNULL ) { 600 goto done; 601 } 602 } else { 603 /* eotsdu reached */ 604 MGET(m, M_WAIT, TPMT_DATA); 605 if (m == NULL) 606 goto done; 607 m->m_len = 0; 608 m->m_act = MNULL; 609 } 610 611 SEQ_INC(tpcb,lowseq); /* it was decremented at the beginning */ 612 { 613 struct tp_rtc *t; 614 /* make an rtc and put it at the end of the chain */ 615 616 TP_MAKE_RTC( t, lowseq, eotsdu_reached, mb, len, lowseq, 617 TPMT_SNDRTC); 618 t->tprt_next = (struct tp_rtc *)0; 619 620 if ( tpcb->tp_sndhiwat_rtc != (struct tp_rtc *)0 ) 621 tpcb->tp_sndhiwat_rtc->tprt_next = t; 622 else { 623 ASSERT( tpcb->tp_snduna_rtc == (struct tp_rtc *)0 ); 624 tpcb->tp_snduna_rtc = t; 625 } 626 627 tpcb->tp_sndhiwat_rtc = t; 628 } 629 630 IFTRACE(D_DATA) 631 tptraceTPCB( TPPTmisc, 632 "tp_send emitting DT lowseq eotsdu_reached", 633 lowseq, eotsdu_reached, 0, 0); 634 ENDTRACE 635 if( tpcb->tp_sock->so_error = 636 tp_emit(DT_TPDU_type, tpcb, lowseq, eotsdu_reached, m) ) { 637 /* error */ 638 SEQ_DEC(tpcb, lowseq); 639 goto done; 640 } 641 /* set the transmit-time for computation of round-trip times */ 642 bcopy( (caddr_t)&time, 643 (caddr_t)&( tpcb->tp_rttemit[ lowseq & TP_RTT_NUM ] ), 644 sizeof(struct timeval)); 645 646 } 647 648 done: 649 IFPERF(tpcb) 650 { 651 register int npkts; 652 struct timeval send_end_time; 653 register struct timeval *t; 654 655 npkts = lowseq; 656 SEQ_INC(tpcb, npkts); 657 npkts = SEQ_SUB(tpcb, npkts, lowsave); 658 659 if(npkts > 0) 660 tpcb->tp_Nwindow++; 661 662 if (npkts > TP_PM_MAX) 663 npkts = TP_PM_MAX; 664 665 GET_TIME_SINCE(&send_start_time, &send_end_time); 666 t = &(tpcb->tp_p_meas->tps_sendtime[npkts]); 667 t->tv_sec = 668 SMOOTH( long, TP_RTT_ALPHA, t->tv_sec, send_end_time.tv_sec); 669 t->tv_usec = 670 SMOOTH( long, TP_RTT_ALPHA, t->tv_usec, send_end_time.tv_usec); 671 672 if ( SEQ_LT(tpcb, lowseq, highseq) ) { 673 IncPStat(tpcb, tps_win_lim_by_data[npkts] ); 674 } else { 675 IncPStat(tpcb, tps_win_lim_by_cdt[npkts] ); 676 /* not true with congestion-window being used */ 677 } 678 tpmeas( tpcb->tp_lref, 679 TPsbsend, &send_end_time, lowsave, tpcb->tp_Nwindow, npkts); 680 } 681 ENDPERF 682 683 tpcb->tp_sndhiwat = lowseq; 684 685 if ( SEQ_LEQ(tpcb, lowsave, tpcb->tp_sndhiwat) && 686 (tpcb->tp_class != TP_CLASS_0) ) 687 tp_etimeout(tpcb->tp_refp, TM_data_retrans, lowsave, 688 tpcb->tp_sndhiwat, 689 (u_int)tpcb->tp_Nretrans, (int)tpcb->tp_dt_ticks); 690 IFTRACE(D_DATA) 691 tptraceTPCB( TPPTmisc, 692 "tp_send at end: sndhiwat lowseq eotsdu_reached error", 693 tpcb->tp_sndhiwat, lowseq, eotsdu_reached, tpcb->tp_sock->so_error); 694 695 ENDTRACE 696 } 697 698 /* 699 * NAME: tp_stash() 700 * CALLED FROM: 701 * tp.trans on arrival of a DT tpdu 702 * FUNCTION, ARGUMENTS, and RETURN VALUE: 703 * Returns 1 if 704 * a) something new arrived and it's got eotsdu_reached bit on, 705 * b) this arrival was caused other out-of-sequence things to be 706 * accepted, or 707 * c) this arrival is the highest seq # for which we last gave credit 708 * (sender just sent a whole window) 709 * In other words, returns 1 if tp should send an ack immediately, 0 if 710 * the ack can wait a while. 711 * 712 * Note: this implementation no longer renegs on credit, (except 713 * when debugging option D_RENEG is on, for the purpose of testing 714 * ack subsequencing), so we don't need to check for incoming tpdus 715 * being in a reneged portion of the window. 716 */ 717 718 int 719 tp_stash( tpcb, e ) 720 register struct tp_pcb *tpcb; 721 register struct tp_event *e; 722 { 723 register int ack_reason= tpcb->tp_ack_strat & ACK_STRAT_EACH; 724 /* 0--> delay acks until full window */ 725 /* 1--> ack each tpdu */ 726 int newrec = 0; 727 728 #ifndef lint 729 #define E e->ATTR(DT_TPDU) 730 #else lint 731 #define E e->ev_union.EV_DT_TPDU 732 #endif lint 733 734 if ( E.e_eot ) { 735 register struct mbuf *n = E.e_data; 736 737 /* sigh. have to go through this again! */ 738 /* a kludgy optimization would be to take care of this in 739 * tp_input (oh, horrors!) 740 * BTW, don't set ack_reason here because we don't know if the 741 * sequence number is right 742 */ 743 while (n->m_next ) 744 n = n->m_next; 745 746 n->m_act = MNULL; /* set on tp_input */ 747 CHANGE_MTYPE(n, TPMT_EOT); 748 749 IFDEBUG(D_STASH) 750 printf("EOT! changing m_type of m 0x%x\n", n); 751 dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb, 752 "stash: so_rcv before appending"); 753 dump_mbuf(E.e_data, 754 "stash: e_data before appending"); 755 ENDDEBUG 756 } 757 758 IFPERF(tpcb) 759 PStat(tpcb, Nb_from_ll) += E.e_datalen; 760 tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time, 761 E.e_seq, PStat(tpcb, Nb_from_ll), E.e_datalen); 762 ENDPERF 763 764 if( E.e_seq == tpcb->tp_rcvnxt ) { 765 766 IFDEBUG(D_STASH) 767 printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x\n", 768 E.e_seq, E.e_datalen, E.e_eot); 769 ENDDEBUG 770 771 IFTRACE(D_STASH) 772 tptraceTPCB(TPPTmisc, "stash EQ: seq len eot", 773 E.e_seq, E.e_datalen, E.e_eot, 0); 774 ENDTRACE 775 776 if( E.e_datalen == 0 && E.e_eot ) { 777 IFDEBUG(D_STASH) 778 printf("stash EQ: appendrec\n"); 779 ENDDEBUG 780 sbappendrecord (&tpcb->tp_sock->so_rcv, E.e_data); 781 /* 'cause sbappend won't append something of length zero */ 782 } else { 783 IFDEBUG(D_STASH) 784 printf("stash EQ: plain old append\n"); 785 ENDDEBUG 786 sbappend(&tpcb->tp_sock->so_rcv, E.e_data); 787 } 788 if (newrec = E.e_eot ) /* ASSIGNMENT */ 789 ack_reason |= ACK_EOT; 790 791 SEQ_INC( tpcb, tpcb->tp_rcvnxt ); 792 /* 793 * move chains from the rtc list to the socket buffer 794 * and free the rtc header 795 */ 796 { 797 register struct tp_rtc **r = &tpcb->tp_rcvnxt_rtc; 798 register struct tp_rtc *s = tpcb->tp_rcvnxt_rtc; 799 800 while (s != (struct tp_rtc *)0 && s->tprt_seq == tpcb->tp_rcvnxt) { 801 *r = s->tprt_next; 802 803 if ( newrec ) { 804 sbappendrecord(&tpcb->tp_sock->so_rcv, s->tprt_data); 805 } else 806 sbappend(&tpcb->tp_sock->so_rcv, s->tprt_data); 807 newrec = s->tprt_eot; 808 809 SEQ_INC( tpcb, tpcb->tp_rcvnxt ); 810 811 (void) m_free( dtom( s ) ); 812 s = *r; 813 ack_reason |= ACK_REORDER; 814 } 815 } 816 IFDEBUG(D_STASH) 817 dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb, 818 "stash: so_rcv after appending"); 819 ENDDEBUG 820 821 } else { 822 register struct tp_rtc **s = &tpcb->tp_rcvnxt_rtc; 823 register struct tp_rtc *r = tpcb->tp_rcvnxt_rtc; 824 register struct tp_rtc *t; 825 826 IFTRACE(D_STASH) 827 tptraceTPCB(TPPTmisc, "stash Reseq: seq rcvnxt lcdt", 828 E.e_seq, tpcb->tp_rcvnxt, tpcb->tp_lcredit, 0); 829 ENDTRACE 830 831 r = tpcb->tp_rcvnxt_rtc; 832 while (r != (struct tp_rtc *)0 && SEQ_LT(tpcb, r->tprt_seq, E.e_seq)) { 833 s = &r->tprt_next; 834 r = r->tprt_next; 835 } 836 837 if (r == (struct tp_rtc *)0 || SEQ_GT(tpcb, r->tprt_seq, E.e_seq) ) { 838 IncStat(ts_dt_ooo); 839 840 IFTRACE(D_STASH) 841 tptrace(TPPTmisc, 842 "tp_stash OUT OF ORDER- MAKE RTC: seq, 1st seq in list\n", 843 E.e_seq, r->tprt_seq,0,0); 844 ENDTRACE 845 IFDEBUG(D_STASH) 846 printf("tp_stash OUT OF ORDER- MAKE RTC\n"); 847 ENDDEBUG 848 TP_MAKE_RTC(t, E.e_seq, E.e_eot, E.e_data, E.e_datalen, 0, 849 TPMT_RCVRTC); 850 851 *s = t; 852 t->tprt_next = (struct tp_rtc *)r; 853 ack_reason = ACK_DONT; 854 goto done; 855 } else { 856 IFDEBUG(D_STASH) 857 printf("tp_stash - drop & ack\n"); 858 ENDDEBUG 859 860 /* retransmission - drop it and force an ack */ 861 IncStat(ts_dt_dup); 862 IFPERF(tpcb) 863 IncPStat(tpcb, tps_n_ack_cuz_dup); 864 ENDPERF 865 866 m_freem( E.e_data ); 867 ack_reason |= ACK_DUP; 868 goto done; 869 } 870 } 871 872 873 /* 874 * an ack should be sent when at least one of the 875 * following holds: 876 * a) we've received a TPDU with EOTSDU set 877 * b) the TPDU that just arrived represents the 878 * full window last advertised, or 879 * c) when seq X arrives [ where 880 * X = last_sent_uwe - 1/2 last_lcredit_sent 881 * (the packet representing 1/2 the last advertised window) ] 882 * and lcredit at the time of X arrival > last_lcredit_sent/2 883 * In other words, if the last ack sent advertised cdt=8 and uwe = 8 884 * then when seq 4 arrives I'd like to send a new ack 885 * iff the credit at the time of 4's arrival is > 4. 886 * The other end thinks it has cdt of 4 so if local cdt 887 * is still 4 there's no point in sending an ack, but if 888 * my credit has increased because the receiver has taken 889 * some data out of the buffer (soreceive doesn't notify 890 * me until the SYSTEM CALL finishes), I'd like to tell 891 * the other end. 892 */ 893 894 done: 895 { 896 LOCAL_CREDIT(tpcb); 897 898 if ( E.e_seq == tpcb->tp_sent_uwe ) 899 ack_reason |= ACK_STRAT_FULLWIN; 900 901 IFTRACE(D_STASH) 902 tptraceTPCB(TPPTmisc, 903 "end of stash, eot, ack_reason, sent_uwe ", 904 E.e_eot, ack_reason, tpcb->tp_sent_uwe, 0); 905 ENDTRACE 906 907 if ( ack_reason == ACK_DONT ) { 908 IncStat( ts_ackreason[ACK_DONT] ); 909 return 0; 910 } else { 911 IFPERF(tpcb) 912 if(ack_reason & ACK_EOT) { 913 IncPStat(tpcb, tps_n_ack_cuz_eot); 914 } 915 if(ack_reason & ACK_STRAT_EACH) { 916 IncPStat(tpcb, tps_n_ack_cuz_strat); 917 } else if(ack_reason & ACK_STRAT_FULLWIN) { 918 IncPStat(tpcb, tps_n_ack_cuz_fullwin); 919 } else if(ack_reason & ACK_REORDER) { 920 IncPStat(tpcb, tps_n_ack_cuz_reorder); 921 } 922 tpmeas(tpcb->tp_lref, TPtime_ack_sent, 0, 923 SEQ_ADD(tpcb, E.e_seq, 1), 0, 0); 924 ENDPERF 925 { 926 register int i; 927 928 /* keep track of all reasons that apply */ 929 for( i=1; i<_ACK_NUM_REASONS_ ;i++) { 930 if( ack_reason & (1<<i) ) 931 IncStat( ts_ackreason[i] ); 932 } 933 } 934 return 1; 935 } 936 } 937 } 938 939 /* class zero version */ 940 void 941 tp0_stash( tpcb, e ) 942 register struct tp_pcb *tpcb; 943 register struct tp_event *e; 944 { 945 #ifndef lint 946 #define E e->ATTR(DT_TPDU) 947 #else lint 948 #define E e->ev_union.EV_DT_TPDU 949 #endif lint 950 951 IFPERF(tpcb) 952 PStat(tpcb, Nb_from_ll) += E.e_datalen; 953 tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time, 954 E.e_seq, PStat(tpcb, Nb_from_ll), E.e_datalen); 955 ENDPERF 956 957 IFDEBUG(D_STASH) 958 printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x", 959 E.e_seq, E.e_datalen, E.e_eot); 960 ENDDEBUG 961 962 IFTRACE(D_STASH) 963 tptraceTPCB(TPPTmisc, "stash EQ: seq len eot", 964 E.e_seq, E.e_datalen, E.e_eot, 0); 965 ENDTRACE 966 967 if ( E.e_eot ) { 968 register struct mbuf *n = E.e_data; 969 970 /* sigh. have to go through this again! */ 971 /* a kludgy optimization would be to take care of this in 972 * tp_input (oh, horrors!) 973 */ 974 while (n->m_next ) 975 n = n->m_next; 976 977 n->m_act = MNULL; /* set on tp_input */ 978 979 CHANGE_MTYPE(n, TPMT_EOT); 980 } 981 if( E.e_datalen == 0 && E.e_eot ) { 982 sbappendrecord (&tpcb->tp_sock->so_rcv, E.e_data); 983 } else { 984 sbappend(&tpcb->tp_sock->so_rcv, E.e_data); 985 } 986 IFDEBUG(D_STASH) 987 dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb, 988 "stash 0: so_rcv after appending"); 989 ENDDEBUG 990 } 991 992