📄 tcpsend.c
字号:
#ifndef TCP_RETRANSMIT_ENHANCEMENTS seqno = MU32(svp->sv_suna);#else seqno = MU32(svp->sv_rexmt_seqno);#endif /* calculate the amount of data that could be sent, up * to one maximum segment */ nsend = M32U(svp->sv_snxt, -, seqno);#ifdef TCP_RETRANSMIT_ENHANCEMENTS nsend = min( ((int) svp->sv_rexmt_gaplen), nsend); #endif /* TCP_RETRANSMIT_ENHANCEMENTS */ if (svp->sv_mss && nsend > svp->sv_mss) { nsend = svp->sv_mss; /* just one segment */ } /* if */#ifdef FAST_REXMT_DEBUG os_printf("(%d) tcp_sqxmit: retransmitting sequence # %u, suna=%u\n", t_time, seqno,MU32(svp->sv_suna));#endif } else { /* not retransmitting */ seqno = MU32(svp->sv_spsh); /* transmit all new data */ nsend = M32U(svp->sv_snxt, -, seqno); /* initialize at all not-yet-sent data */ if (nmp == (m *)0 && MC32M(svp->sv_surg, !=, svp->sv_suna)) { if (UC32M(seqno, >=, svp->sv_surg)) { /* past the urgent pointer; include the last * URGENT byte (notification purposes) */ seqno = MU32(svp->sv_surg) - 1; } } else if (nsend != 0) { /* There is some not-yet-sent data */ /* We never want to send more than an MSS-worth of data in a single segment, if the MSS is known yet at this point */ if ( (svp->sv_mss) && (nsend > svp->sv_mss) ) { nsend = svp->sv_mss; } /* At this point, "nsend" is the lesser of the mss or the amount of unsent data in the send buffer. Normally we want to delay sending segments less than a full MSS in size (Nagle algorithm). Before applying the Nagle algorithm, check various other conditions which override the Nagle algorithm. */ if (svp->sv_flags & (SV_TSYNFLG|SV_TFINFLG)) { /* sending a SYN or a FIN */ /* no point in delaying any data now */ skip;#if 0 /* The stuff conditionally uncompiled here is old legacy code that seems to be using IP layer options within the TCP layer. These options are only intended to set fields within the IP header. They seem to be used here in a way that is similar to enabling or disabling the Nagle algorithm via the TCP_NODELAY socket option, which was just implemented for Fusion 6.0. So I don't think we need to reference here the IP_DELAY/IP_THROUGHPUT options any more. Leaving it here for a while conditioned out just in case ... */ } else if (svp->sv_tos & IP_DELAY) { /* ULP has asked to push data as * quickly as it comes from ULP, so send * whatever is queued */ /* Note from MBM: I'm not sure that this condition should be checked here in the TCP layer. But out of caution about removing legacy code that I don't fully understand the intent of, I leave it here for now. */ skip; } else if (!(svp->sv_tos & IP_THROUGHPUT) && MC32U(svp->sv_suna, ==, seqno)) { /* All data sent has been ACKed. */ /* ULP has not promised to give data continuously, so since another ACKnowledgment can't be counted upon, send whatever is queued */ /* Note from MBM: I'm not sure that this condition should be checked here in the TCP layer. This seems to have the same intent as checking whether the Nagle algorithm is disabled (TCP_NODELAY option) but this was written long before that socket option was implemented. But out of caution (timidity) about removing legacy code that I don't fully understand the intent of, I leave it here for now. */ skip;#endif } else if (svp->sv_mss) { /* If we have less than an MSS worth of data to send, and the Nagle algorithm is enabled, and there is outstanding un-ACKed data, don't send the data now. */ if ( (nsend < svp->sv_mss) && (!(svp->sv_flags2 & SV_NAGLE_DISABLED)) && (MC32U(svp->sv_suna, !=, seqno)) ) { nsend = 0L; } } /* if MSS is known */ } /* there is unsent data */ } /* transmitting or not retransmitting */ if (nsend == 0L) { /* no data to send, this would just be an ACK */ normal; return; } /* if */ trace2(tcp_tsqxmit, "tcp_sqxmit: %s from 0x%x\n", (retransmitting) ? "retransmission" : "new data", seqno); /* inner block to skip over send queue bytes which are not being * sent in this packet */ { /*SKIP*/ fast i32 nskip, swind; /* calculate number of bytes to skip */ nskip = U32M(seqno, -, svp->sv_suna);#ifndef TCP_SS_CA_FRETR_FREC swind = svp->sv_swnd; /* send window */#else /* When slow-start/congestion avoidance is enabled, the operational send window is the minimum of the current congestion window and peer-advertised receive window. */ if (svp->sv_flags2 & SV_SLOW_START_CA) { swind = TCP_OPERATIONAL_SEND_WINDOW(svp); } else { swind = svp->sv_swnd; /* send window */ } /* if-else */#endif if (swind == 0L) { if ((svp->sv_flags & SV_NOPROBE) == 0) { /* closed, allow 1 byte (SYN & window probes) */ swind = 1; } } swind -= nskip; /* calculate # of bytes in win. past skip pnt. */ if (swind <= 0L) { trace2(tcp_tsqxmit, "tcp_sqxmit: %d skip fills %d window\n", nskip, swind); /* if the user requested expeditious delivery, and we have * managed to send the entire window (in whatever mode we're * currently in), cause an immediate retransmission at the * next timer tick (user may be pounding on the keyboard, * and he/she wants to be heard!) */ if ((svp->sv_tos & IP_DELAY) && (t_stopped(svp->sv_rextcb) || t_period(svp->sv_rextcb) > MS_PER_TICK)) { /* don't interfere with the normal timeout process */ if (svp->sv_abort) { ++svp->sv_abort; } (void) t_start(svp->sv_rextcb, IMMEDIATE_REX_DELAY); } normal; return; } finflg = false; synflg = false; if (nsend > swind) { /* more bytes to send than window allows? */ nsend = swind; /* reduce to size of window */ } else if ((svp->sv_flags & SV_TFINFLG) && UC32M(seqno + nsend, ==, svp->sv_snxt)) { finflg = true; --nsend; /* not really data */ } if (svp->sv_flags & SV_TSYNFLG) { if (nskip) { --nskip; /* gets skipped */ } else { synflg = true; --nsend; /* not really data */ } } /* skip over bytes in send queue to the first byte to be sent */ trace1(tcp_tsqxmit, "tcp_sqxmit: skipping %d\n", nskip); if (sop) { for (mp = (m *)sop->so_sq.gq_q.q_next; !is_header(&mp->m_q); mp = (m *)mp->m_q.q_next) { if (nskip < (i1 = m_dsize(mp))) { dp = mp->m_hp + nskip; /* partially eaten */ break; } nskip -= i1; /* fully eaten */ } /* for */ } trace3(tcp_tsqxmit, "tcp_sqxmit: %d%s%s to send\n", nsend, synflg ? " SYN" : "", finflg ? " FIN" : "" ); } /*SKIP*/ /* inner block to actually construct the packet beginning at * 'seqno'; 'mp' is now pointing at the 'm' structure which * contains 'seqno', and 'dp' points at the byte which is 'seqno' */ { /*SEND*/ fast TCPH_T * tcphp; fast int i2; fast u16 flags; int hsize; int seg_optsize = 0; u16 seg_which_options; /* determine the actual packet size; no more than what is really * there or the maximum packet size */ hsize = (int)nc_hsize; if (sop) { hsize += sop->so_hsize; } else { /* no socket -- guess */ hsize += IP_MHL + SIZEOF_TCPH_T; } /* MBM note: I think at this point, "hsize" has the combined sizes of all protocol headers (link layer/ethernet, IP, and the standard TCP header, not including TCP options. */ /* Determine the length of the part of the segment that follows the standard TCP header. This includes the user data plus the length of the TCP options, if any. "i1" will contain that value. */ if (synflg) { seg_optsize = tcp_seg_optsize(svp,1,&seg_which_options);#ifdef TCP_TRANSACTION_TCP /* For transaction TCP, make sure we don't follow an initial SYN with additional segments unless we know that the peer understands transaction TCP. Stevens TCP/IP vol 3, section 3.6 */ if (svp->sv_t_tcp_flags & SV_TRANSACTION) { if ( (svp->sv_state == SYN_SENT) && (seg_which_options & TCP_OPT_INCLUDE_CCNEW) ) { loop_again = false; } }#endif /* TCP_TRANSACTION_TCP */ } else { seg_optsize = (svp->sv_mss - svp->sv_mssd); seg_which_options = svp->sv_dseg_options;#ifdef TCP_SELACK_ENHANCEMENTS /* If we need to send a selective ack, adjust the option size according to how large that will be. */ if (svp->sv_flags2 & SV_OUT_SACKS_ENABLED) { tcp_selective_ack_needed(svp,&seg_optsize,&seg_which_options); } /* if */#endif } i1 = min(svp->sv_mss, (nsend + seg_optsize) ); /* if the segment we want to send is mss-sized and the message size we need corresponds to that size of segment, see if we have any saved from previous usage -- otherwise, allocate one */ nmp = (m *) 0; if ( M_NEW_ALLOCLEN_FOR_SIZE(hsize + (int)i1) == svp->sv_mss_m_len ) { if (svp->sv_num_mss_sized_msgs > 0) { nmp = svp->sv_tcp_mss_sized_msgs[--(svp->sv_num_mss_sized_msgs)]; /* Initialize any necessary fields here that would ordinarily be done within m_new, either explicitly or via the MEMSET to 0. Only initiaslize those that are necessary. */ { q_init(&nmp->m_q, F_Q_LOCKED); q_init(&nmp->m_wq, 0); q_initparent(&nmp->m_mq, 0, (char *)nmp); nmp->m_ctime = t_time; nmp->m_hp = nmp->m_tp = &((char *)&nmp[1])[hsize + (int)i1]; nmp->m_type = 0; nmp->m_owners = 1; nmp->m_outndp = (struct netdev *) 0; nmp->m_ndp = (struct netdev *) 0; } } /* if */ } /* if */ /* If we can't re-use a message, allocate one from the heap */ if (nmp == (m *) 0) { trace1(tcp_tsqxmit, "tcp_sqxmit: packet data is %d\n", i1); /* now try to allocate a reasonable size packet */ while ((nmp = m_new(hsize + (int)i1, (int *)0, (u16)0)) == (m *)0) { i1 >>= 1; /* try 1/2 size */ if (i1 < seg_optsize) { /* cannot allocate a useful size packet */ trace0(tcp_tsqxmit, "tcp_sqxmit: m_new failed\n"); svp->sv_abort = 0; (void) t_start(svp->sv_rextcb, HALLOC_REX_DELAY); normal; return; /* timer will come back */ } trace2(tcp_tsqxmit, "tcp_sqxmit: %d was too big, trying %d\n", i1 << 1, i1); } /* while */ /* bind the message to the state vector */ (void)sv_m_bind(svp, nmp, tcp_sqxmit_termfn); } /* if */ /* Normally this value would be passed to m_new and within m_new, them_flags field would be set to this value. But m_new also uses it to decide whether to block until it can allocate a message of the specified size, and TCP doesn't want that, because of the "attempt to allocate decreasing sizes until we succeed" strategy above. So, we pass a zero to m_new so it won't block, and set these flags here instead. */ /* Note: The reason its necessary to set these flags is because, in ip_xmt2, if it can't route the packet, it calls smerr, which will record an error code in the message for examination by TCP's disposal function, but only if the blocking or nonblocking flag is set (the "want_status" macro) */ nmp->m_flags = (u16) give_bs(sop->so_flags); nmp->m_hp -= i1; nmp->m_tp = nmp->m_hp; /* fill the new packet */ flags = 0; /* attach TCP options */ tcp_construct_seg_options(svp, (char *) m_ptr(nmp,TCPO_T), seg_optsize, seg_which_options); i1 -= seg_optsize; nmp->m_tp += seg_optsize; if (synflg) { flags = SYN; synflg = false; /* consumed the SYN */ } /* if */ /* We started with nsend equal to all of the data in the transmit queue that has never been sent, i.e., sv_snxt - sv_spsh, then we see how much we can send based on the MSS or window size, and we reduce nsend by that amount which we'll use later to see if we can send a FIN now should we need to. */ nsend -= i1; while (i1 > 0L) { /* take bytes from send queue packets */ assert(!is_header(&mp->m_q), "tcp_sqxmit: tried to send sq header\n"); i2 = (int)(min(mp->m_tp - dp, i1));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -