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