856 lines
25 KiB
C
856 lines
25 KiB
C
/*-
|
|
* Copyright (c) 1991 The Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* from: @(#)tp_subr.c 7.9 (Berkeley) 6/27/91
|
|
* $Id: tp_subr.c,v 1.3 1993/12/18 00:43:57 mycroft Exp $
|
|
*/
|
|
|
|
/***********************************************************
|
|
Copyright IBM Corporation 1987
|
|
|
|
All Rights Reserved
|
|
|
|
Permission to use, copy, modify, and distribute this software and its
|
|
documentation for any purpose and without fee is hereby granted,
|
|
provided that the above copyright notice appear in all copies and that
|
|
both that copyright notice and this permission notice appear in
|
|
supporting documentation, and that the name of IBM not be
|
|
used in advertising or publicity pertaining to distribution of the
|
|
software without specific, written prior permission.
|
|
|
|
IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
|
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
|
|
IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
|
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
SOFTWARE.
|
|
|
|
******************************************************************/
|
|
|
|
/*
|
|
* ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
|
|
*/
|
|
/*
|
|
* ARGO TP
|
|
*
|
|
* The main work of data transfer is done here.
|
|
* These routines are called from tp.trans.
|
|
* They include the routines that check the validity of acks and Xacks,
|
|
* (tp_goodack() and tp_goodXack() )
|
|
* take packets from socket buffers and send them (tp_send()),
|
|
* drop the data from the socket buffers (tp_sbdrop()),
|
|
* and put incoming packet data into socket buffers (tp_stash()).
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <netiso/tp_ip.h>
|
|
#include <netiso/iso.h>
|
|
#include <netiso/argo_debug.h>
|
|
#include <netiso/tp_timer.h>
|
|
#include <netiso/tp_param.h>
|
|
#include <netiso/tp_stat.h>
|
|
#include <netiso/tp_pcb.h>
|
|
#include <netiso/tp_tpdu.h>
|
|
#include <netiso/tp_trace.h>
|
|
#include <netiso/tp_meas.h>
|
|
#include <netiso/tp_seq.h>
|
|
|
|
int tp_emit();
|
|
static void tp_sbdrop();
|
|
|
|
#define SMOOTH(newtype, alpha, old, new) \
|
|
(newtype) (((new - old)>>alpha) + (old))
|
|
|
|
#define ABS(type, val) \
|
|
(type) (((int)(val)<0)?-(val):(val))
|
|
|
|
#define TP_MAKE_RTC( Xreg, Xseq, Xeot, Xdata, Xlen, Xretval, Xtype) \
|
|
{ struct mbuf *xxn;\
|
|
MGET(xxn, M_DONTWAIT, Xtype);\
|
|
if( xxn == (struct mbuf *)0 ) {\
|
|
printf("MAKE RTC FAILED: ENOBUFS\n");\
|
|
return (int)Xretval;\
|
|
}\
|
|
xxn->m_act=MNULL;\
|
|
Xreg = mtod(xxn, struct tp_rtc *);\
|
|
if( Xreg == (struct tp_rtc *)0 ) {\
|
|
return (int)Xretval;\
|
|
}\
|
|
Xreg->tprt_eot = Xeot;\
|
|
Xreg->tprt_seq = Xseq;\
|
|
Xreg->tprt_data = Xdata;\
|
|
Xreg->tprt_octets = Xlen;\
|
|
}
|
|
|
|
|
|
/*
|
|
* CALLED FROM:
|
|
* tp.trans, when an XAK arrives
|
|
* FUNCTION and ARGUMENTS:
|
|
* Determines if the sequence number (seq) from the XAK
|
|
* acks anything new. If so, drop the appropriate tpdu
|
|
* from the XPD send queue.
|
|
* RETURN VALUE:
|
|
* Returns 1 if it did this, 0 if the ack caused no action.
|
|
*/
|
|
int
|
|
tp_goodXack(tpcb, seq)
|
|
struct tp_pcb *tpcb;
|
|
SeqNum seq;
|
|
{
|
|
|
|
IFTRACE(D_XPD)
|
|
tptraceTPCB(TPPTgotXack,
|
|
seq, tpcb->tp_Xuna, tpcb->tp_Xsndnxt, tpcb->tp_sndhiwat,
|
|
tpcb->tp_snduna);
|
|
ENDTRACE
|
|
|
|
if ( seq == tpcb->tp_Xuna ) {
|
|
tpcb->tp_Xuna = tpcb->tp_Xsndnxt;
|
|
|
|
/* DROP 1 packet from the Xsnd socket buf - just so happens
|
|
* that only one packet can be there at any time
|
|
* so drop the whole thing. If you allow > 1 packet
|
|
* the socket buffer, then you'll have to keep
|
|
* track of how many characters went w/ each XPD tpdu, so this
|
|
* will get messier
|
|
*/
|
|
IFDEBUG(D_XPD)
|
|
dump_mbuf(tpcb->tp_Xsnd.sb_mb,
|
|
"tp_goodXack Xsnd before sbdrop");
|
|
ENDDEBUG
|
|
|
|
IFTRACE(D_XPD)
|
|
tptraceTPCB(TPPTmisc,
|
|
"goodXack: dropping cc ",
|
|
(int)(tpcb->tp_Xsnd.sb_cc),
|
|
0,0,0);
|
|
ENDTRACE
|
|
sbdrop( &tpcb->tp_Xsnd, (int)(tpcb->tp_Xsnd.sb_cc));
|
|
CONG_ACK(tpcb, seq);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* CALLED FROM:
|
|
* tp_good_ack()
|
|
* FUNCTION and ARGUMENTS:
|
|
* updates
|
|
* smoothed average round trip time (base_rtt)
|
|
* roundtrip time variance (base_rtv) - actually deviation, not variance
|
|
* given the new value (diff)
|
|
* RETURN VALUE:
|
|
* void
|
|
*/
|
|
|
|
void
|
|
tp_rtt_rtv( base_rtt, base_rtv, newmeas )
|
|
struct timeval *base_rtt, *base_rtv, *newmeas;
|
|
{
|
|
/* update rt variance (really just the deviation):
|
|
* rtv.smooth_ave = SMOOTH( | oldrtt.smooth_avg - rtt.this_instance | )
|
|
*/
|
|
base_rtv->tv_sec =
|
|
SMOOTH( long, TP_RTV_ALPHA, base_rtv->tv_sec,
|
|
ABS( long, base_rtt->tv_sec - newmeas->tv_sec ));
|
|
base_rtv->tv_usec =
|
|
SMOOTH( long, TP_RTV_ALPHA, base_rtv->tv_usec,
|
|
ABS(long, base_rtt->tv_usec - newmeas->tv_usec ));
|
|
|
|
/* update smoothed average rtt */
|
|
base_rtt->tv_sec =
|
|
SMOOTH( long, TP_RTT_ALPHA, base_rtt->tv_sec, newmeas->tv_sec);
|
|
base_rtt->tv_usec =
|
|
SMOOTH( long, TP_RTT_ALPHA, base_rtt->tv_usec, newmeas->tv_usec);
|
|
|
|
}
|
|
|
|
/*
|
|
* CALLED FROM:
|
|
* tp.trans when an AK arrives
|
|
* FUNCTION and ARGUMENTS:
|
|
* Given (cdt), the credit from the AK tpdu, and
|
|
* (seq), the sequence number from the AK tpdu,
|
|
* tp_goodack() determines if the AK acknowledges something in the send
|
|
* window, and if so, drops the appropriate packets from the retransmission
|
|
* list, computes the round trip time, and updates the retransmission timer
|
|
* based on the new smoothed round trip time.
|
|
* RETURN VALUE:
|
|
* Returns 1 if
|
|
* EITHER it actually acked something heretofore unacknowledged
|
|
* OR no news but the credit should be processed.
|
|
* If something heretofore unacked was acked with this sequence number,
|
|
* the appropriate tpdus are dropped from the retransmission control list,
|
|
* by calling tp_sbdrop().
|
|
* No need to see the tpdu itself.
|
|
*/
|
|
int
|
|
tp_goodack(tpcb, cdt, seq, subseq)
|
|
register struct tp_pcb *tpcb;
|
|
u_int cdt;
|
|
register SeqNum seq, subseq;
|
|
{
|
|
int old_fcredit = tpcb->tp_fcredit;
|
|
int bang = 0; /* bang --> ack for something heretofore unacked */
|
|
|
|
IFDEBUG(D_ACKRECV)
|
|
printf("goodack seq 0x%x cdt 0x%x snduna 0x%x sndhiwat 0x%x\n",
|
|
seq, cdt, tpcb->tp_snduna, tpcb->tp_sndhiwat);
|
|
ENDDEBUG
|
|
IFTRACE(D_ACKRECV)
|
|
tptraceTPCB(TPPTgotack,
|
|
seq,cdt, tpcb->tp_snduna,tpcb->tp_sndhiwat,subseq);
|
|
ENDTRACE
|
|
|
|
IFPERF(tpcb)
|
|
tpmeas(tpcb->tp_lref, TPtime_ack_rcvd, (struct timeval *)0, seq, 0, 0);
|
|
ENDPERF
|
|
|
|
if ( subseq != 0 && (subseq <= tpcb->tp_r_subseq) ) {
|
|
/* discard the ack */
|
|
IFTRACE(D_ACKRECV)
|
|
tptraceTPCB(TPPTmisc, "goodack discard : subseq tp_r_subseq",
|
|
subseq, tpcb->tp_r_subseq, 0, 0);
|
|
ENDTRACE
|
|
return 0;
|
|
} else {
|
|
tpcb->tp_r_subseq = subseq;
|
|
}
|
|
|
|
if ( IN_SWINDOW(tpcb, seq,
|
|
tpcb->tp_snduna, SEQ(tpcb, tpcb->tp_sndhiwat+1)) ) {
|
|
|
|
IFDEBUG(D_XPD)
|
|
dump_mbuf(tpcb->tp_sock->so_snd.sb_mb,
|
|
"tp_goodack snd before sbdrop");
|
|
ENDDEBUG
|
|
tp_sbdrop(tpcb, SEQ_SUB(tpcb, seq, 1) );
|
|
|
|
/* increase congestion window but don't let it get too big */
|
|
{
|
|
register int maxcdt = tpcb->tp_xtd_format?0xffff:0xf;
|
|
CONG_ACK(tpcb, seq);
|
|
}
|
|
|
|
/* Compute smoothed round trip time.
|
|
* Only measure rtt for tp_snduna if tp_snduna was among
|
|
* the last TP_RTT_NUM seq numbers sent, and if the data
|
|
* were not retransmitted.
|
|
*/
|
|
if (SEQ_GEQ(tpcb, tpcb->tp_snduna,
|
|
SEQ(tpcb, tpcb->tp_sndhiwat - TP_RTT_NUM))
|
|
&& SEQ_GT(tpcb, seq, SEQ_ADD(tpcb, tpcb->tp_retrans_hiwat, 1))) {
|
|
|
|
struct timeval *t = &tpcb->tp_rttemit[tpcb->tp_snduna & TP_RTT_NUM];
|
|
struct timeval x;
|
|
|
|
GET_TIME_SINCE(t, &x);
|
|
|
|
tp_rtt_rtv( &(tpcb->tp_rtt), &(tpcb->tp_rtv), &x );
|
|
|
|
{ /* update the global rtt, rtv stats */
|
|
register int i =
|
|
(int) tpcb->tp_flags & (TPF_PEER_ON_SAMENET | TPF_NLQOS_PDN);
|
|
tp_rtt_rtv( &(tp_stat.ts_rtt[i]), &(tp_stat.ts_rtv[i]), &x );
|
|
|
|
IFTRACE(D_RTT)
|
|
tptraceTPCB(TPPTmisc, "Global rtt, rtv: i", i, 0, 0, 0);
|
|
ENDTRACE
|
|
}
|
|
|
|
IFTRACE(D_RTT)
|
|
tptraceTPCB(TPPTmisc,
|
|
"Smoothed rtt: tp_snduna, (time.sec, time.usec), peer_acktime",
|
|
tpcb->tp_snduna, time.tv_sec, time.tv_usec,
|
|
tpcb->tp_peer_acktime);
|
|
|
|
tptraceTPCB(TPPTmisc,
|
|
"(secs): emittime diff(x) rtt, rtv",
|
|
t->tv_sec,
|
|
x.tv_sec,
|
|
tpcb->tp_rtt.tv_sec,
|
|
tpcb->tp_rtv.tv_sec);
|
|
tptraceTPCB(TPPTmisc,
|
|
"(usecs): emittime diff(x) rtt rtv",
|
|
t->tv_usec,
|
|
x.tv_usec,
|
|
tpcb->tp_rtt.tv_usec,
|
|
tpcb->tp_rtv.tv_usec);
|
|
ENDTRACE
|
|
|
|
{
|
|
/* Update data retransmission timer based on the smoothed
|
|
* round trip time, peer ack time, and the pseudo-arbitrary
|
|
* number 4.
|
|
* new ticks: avg rtt + 2*dev
|
|
* rtt, rtv are in microsecs, and ticks are 500 ms
|
|
* so 1 tick = 500*1000 us = 500000 us
|
|
* so ticks = (rtt + 2 rtv)/500000
|
|
* with ticks no les than peer ack time and no less than 4
|
|
*/
|
|
|
|
int rtt = tpcb->tp_rtt.tv_usec +
|
|
tpcb->tp_rtt.tv_sec*1000000;
|
|
int rtv = tpcb->tp_rtv.tv_usec +
|
|
tpcb->tp_rtv.tv_sec*1000000;
|
|
|
|
IFTRACE(D_RTT)
|
|
tptraceTPCB(TPPTmisc, "oldticks ,rtv, rtt, newticks",
|
|
tpcb->tp_dt_ticks,
|
|
rtv, rtt,
|
|
(rtt/500000 + (2 * rtv)/500000));
|
|
ENDTRACE
|
|
tpcb->tp_dt_ticks = (rtt+ (2 * rtv))/500000;
|
|
tpcb->tp_dt_ticks = MAX( tpcb->tp_dt_ticks,
|
|
tpcb->tp_peer_acktime);
|
|
tpcb->tp_dt_ticks = MAX( tpcb->tp_dt_ticks, 4);
|
|
}
|
|
}
|
|
tpcb->tp_snduna = seq;
|
|
tpcb->tp_retrans = tpcb->tp_Nretrans; /* CE_BIT */
|
|
|
|
bang++;
|
|
}
|
|
|
|
if( cdt != 0 && old_fcredit == 0 ) {
|
|
tpcb->tp_sendfcc = 1;
|
|
}
|
|
if( cdt == 0 && old_fcredit != 0 ) {
|
|
IncStat(ts_zfcdt);
|
|
}
|
|
tpcb->tp_fcredit = cdt;
|
|
|
|
IFDEBUG(D_ACKRECV)
|
|
printf("goodack returning 0x%x, bang 0x%x cdt 0x%x old_fcredit 0x%x\n",
|
|
(bang || (old_fcredit < cdt) ), bang, cdt, old_fcredit );
|
|
ENDDEBUG
|
|
|
|
return (bang || (old_fcredit < cdt)) ;
|
|
}
|
|
|
|
/*
|
|
* CALLED FROM:
|
|
* tp_goodack()
|
|
* FUNCTION and ARGUMENTS:
|
|
* drops everything up TO and INCLUDING seq # (seq)
|
|
* from the retransmission queue.
|
|
*/
|
|
static void
|
|
tp_sbdrop(tpcb, seq)
|
|
struct tp_pcb *tpcb;
|
|
SeqNum seq;
|
|
{
|
|
register struct tp_rtc *s = tpcb->tp_snduna_rtc;
|
|
|
|
IFDEBUG(D_ACKRECV)
|
|
printf("tp_sbdrop up through seq 0x%x\n", seq);
|
|
ENDDEBUG
|
|
while (s != (struct tp_rtc *)0 && (SEQ_LEQ(tpcb, s->tprt_seq, seq))) {
|
|
m_freem( s->tprt_data );
|
|
tpcb->tp_snduna_rtc = s->tprt_next;
|
|
(void) m_free( dtom( s ) );
|
|
s = tpcb->tp_snduna_rtc;
|
|
}
|
|
if(tpcb->tp_snduna_rtc == (struct tp_rtc *)0)
|
|
tpcb->tp_sndhiwat_rtc = (struct tp_rtc *) 0;
|
|
|
|
}
|
|
|
|
/*
|
|
* CALLED FROM:
|
|
* tp.trans on user send request, arrival of AK and arrival of XAK
|
|
* FUNCTION and ARGUMENTS:
|
|
* Emits tpdus starting at sequence number (lowseq).
|
|
* Emits until a) runs out of data, or b) runs into an XPD mark, or
|
|
* c) it hits seq number (highseq)
|
|
* Removes the octets from the front of the socket buffer
|
|
* and repackages them in one mbuf chain per tpdu.
|
|
* Moves the mbuf chain to the doubly linked list that runs from
|
|
* tpcb->tp_sndhiwat_rtc to tpcb->tp_snduna_rtc.
|
|
*
|
|
* Creates tpdus that are no larger than <tpcb->tp_l_tpdusize - headersize>,
|
|
*
|
|
* If you want XPD to buffer > 1 du per socket buffer, you can
|
|
* modifiy this to issue XPD tpdus also, but then it'll have
|
|
* to take some argument(s) to distinguish between the type of DU to
|
|
* hand tp_emit, the socket buffer from which to get the data, and
|
|
* the chain of tp_rtc structures on which to put the data sent.
|
|
*
|
|
* When something is sent for the first time, its time-of-send
|
|
* is stashed (the last RTT_NUM of them are stashed). When the
|
|
* ack arrives, the smoothed round-trip time is figured using this value.
|
|
* RETURN VALUE:
|
|
* the highest seq # sent successfully.
|
|
*/
|
|
tp_send(tpcb)
|
|
register struct tp_pcb *tpcb;
|
|
{
|
|
register int len;
|
|
register struct mbuf *m; /* the one we're inspecting now */
|
|
struct mbuf *mb;/* beginning of this tpdu */
|
|
struct mbuf *nextrecord; /* NOT next tpdu but next sb record */
|
|
struct sockbuf *sb = &tpcb->tp_sock->so_snd;
|
|
int maxsize = tpcb->tp_l_tpdusize
|
|
- tp_headersize(DT_TPDU_type, tpcb)
|
|
- (tpcb->tp_use_checksum?4:0) ;
|
|
unsigned int eotsdu_reached=0;
|
|
SeqNum lowseq, highseq ;
|
|
SeqNum lowsave;
|
|
#ifdef TP_PERF_MEAS
|
|
|
|
struct timeval send_start_time;
|
|
IFPERF(tpcb)
|
|
GET_CUR_TIME(&send_start_time);
|
|
ENDPERF
|
|
#endif TP_PERF_MEAS
|
|
|
|
lowsave = lowseq = SEQ(tpcb, tpcb->tp_sndhiwat + 1);
|
|
|
|
ASSERT( tpcb->tp_cong_win > 0 && tpcb->tp_cong_win < 0xffff);
|
|
|
|
if( tpcb->tp_rx_strat & TPRX_USE_CW ) {
|
|
/*first hiseq is temp vbl*/
|
|
highseq = MIN(tpcb->tp_fcredit, tpcb->tp_cong_win);
|
|
} else {
|
|
highseq = tpcb->tp_fcredit;
|
|
}
|
|
highseq = SEQ(tpcb, tpcb->tp_snduna + highseq);
|
|
|
|
SEQ_DEC(tpcb, highseq);
|
|
|
|
IFDEBUG(D_DATA)
|
|
printf(
|
|
"tp_send enter tpcb 0x%x l %d -> h %d\ndump of sb_mb:\n",
|
|
tpcb, lowseq, highseq);
|
|
dump_mbuf(sb->sb_mb, "sb_mb:");
|
|
ENDDEBUG
|
|
IFTRACE(D_DATA)
|
|
tptraceTPCB( TPPTmisc, "tp_send lowsave sndhiwat snduna",
|
|
lowsave, tpcb->tp_sndhiwat, tpcb->tp_snduna, 0);
|
|
tptraceTPCB( TPPTmisc, "tp_send low high fcredit congwin",
|
|
lowseq, highseq, tpcb->tp_fcredit, tpcb->tp_cong_win);
|
|
ENDTRACE
|
|
|
|
|
|
if ( SEQ_GT(tpcb, lowseq, highseq) )
|
|
return ; /* don't send, don't change hiwat, don't set timers */
|
|
|
|
ASSERT( SEQ_LEQ(tpcb, lowseq, highseq) );
|
|
SEQ_DEC(tpcb, lowseq);
|
|
|
|
IFTRACE(D_DATA)
|
|
tptraceTPCB( TPPTmisc, "tp_send 2 low high fcredit congwin",
|
|
lowseq, highseq, tpcb->tp_fcredit, tpcb->tp_cong_win);
|
|
ENDTRACE
|
|
|
|
while ((SEQ_LT(tpcb, lowseq, highseq)) && (mb = m = sb->sb_mb)) {
|
|
if (tpcb->tp_Xsnd.sb_mb) {
|
|
IFTRACE(D_XPD)
|
|
tptraceTPCB( TPPTmisc,
|
|
"tp_send XPD mark low high tpcb.Xuna",
|
|
lowseq, highseq, tpcb->tp_Xsnd.sb_mb, 0);
|
|
ENDTRACE
|
|
/* stop sending here because there are unacked XPD which were
|
|
* given to us before the next data were.
|
|
*/
|
|
IncStat(ts_xpd_intheway);
|
|
break;
|
|
}
|
|
eotsdu_reached = 0;
|
|
nextrecord = m->m_act;
|
|
for (len = 0; m; m = m->m_next) {
|
|
len += m->m_len;
|
|
if (m->m_flags & M_EOR)
|
|
eotsdu_reached = 1;
|
|
sbfree(sb, m); /* reduce counts in socket buffer */
|
|
}
|
|
sb->sb_mb = nextrecord;
|
|
IFTRACE(D_STASH)
|
|
tptraceTPCB(TPPTmisc, "tp_send whole mbuf: m_len len maxsize",
|
|
0, mb->m_len, len, maxsize);
|
|
ENDTRACE
|
|
|
|
if ( len == 0 && !eotsdu_reached) {
|
|
/* THIS SHOULD NEVER HAPPEN! */
|
|
ASSERT( 0 );
|
|
goto done;
|
|
}
|
|
|
|
/* If we arrive here one of the following holds:
|
|
* 1. We have exactly <maxsize> octets of whole mbufs,
|
|
* 2. We accumulated <maxsize> octets using partial mbufs,
|
|
* 3. We found an TPMT_EOT or an XPD mark
|
|
* 4. We hit the end of a chain through m_next.
|
|
* In this case, we'd LIKE to continue with the next record,
|
|
* but for the time being, for simplicity, we'll stop here.
|
|
* In all cases, m points to mbuf containing first octet to be
|
|
* sent in the tpdu AFTER the one we're going to send now,
|
|
* or else m is null.
|
|
*
|
|
* The chain we're working on now begins at mb and has length <len>.
|
|
*/
|
|
|
|
IFTRACE(D_STASH)
|
|
tptraceTPCB( TPPTmisc,
|
|
"tp_send mcopy low high eotsdu_reached len",
|
|
lowseq, highseq, eotsdu_reached, len);
|
|
ENDTRACE
|
|
|
|
/* make a copy - mb goes into the retransmission list
|
|
* while m gets emitted. m_copy won't copy a zero-length mbuf.
|
|
*/
|
|
if (len) {
|
|
if ((m = m_copy(mb, 0, len )) == MNULL)
|
|
goto done;
|
|
} else {
|
|
/* eotsdu reached */
|
|
MGET(m, M_WAIT, TPMT_DATA);
|
|
if (m == MNULL)
|
|
goto done;
|
|
m->m_len = 0;
|
|
}
|
|
|
|
SEQ_INC(tpcb,lowseq); /* it was decremented at the beginning */
|
|
{
|
|
struct tp_rtc *t;
|
|
/* make an rtc and put it at the end of the chain */
|
|
|
|
TP_MAKE_RTC( t, lowseq, eotsdu_reached, mb, len, lowseq,
|
|
TPMT_SNDRTC);
|
|
t->tprt_next = (struct tp_rtc *)0;
|
|
|
|
if ( tpcb->tp_sndhiwat_rtc != (struct tp_rtc *)0 )
|
|
tpcb->tp_sndhiwat_rtc->tprt_next = t;
|
|
else {
|
|
ASSERT( tpcb->tp_snduna_rtc == (struct tp_rtc *)0 );
|
|
tpcb->tp_snduna_rtc = t;
|
|
}
|
|
|
|
tpcb->tp_sndhiwat_rtc = t;
|
|
}
|
|
|
|
IFTRACE(D_DATA)
|
|
tptraceTPCB( TPPTmisc,
|
|
"tp_send emitting DT lowseq eotsdu_reached len",
|
|
lowseq, eotsdu_reached, len, 0);
|
|
ENDTRACE
|
|
if( tpcb->tp_sock->so_error =
|
|
tp_emit(DT_TPDU_type, tpcb, lowseq, eotsdu_reached, m) ) {
|
|
/* error */
|
|
SEQ_DEC(tpcb, lowseq);
|
|
goto done;
|
|
}
|
|
/* set the transmit-time for computation of round-trip times */
|
|
bcopy( (caddr_t)&time,
|
|
(caddr_t)&( tpcb->tp_rttemit[ lowseq & TP_RTT_NUM ] ),
|
|
sizeof(struct timeval));
|
|
|
|
}
|
|
|
|
done:
|
|
#ifdef TP_PERF_MEAS
|
|
IFPERF(tpcb)
|
|
{
|
|
register int npkts;
|
|
struct timeval send_end_time;
|
|
register struct timeval *t;
|
|
|
|
npkts = lowseq;
|
|
SEQ_INC(tpcb, npkts);
|
|
npkts = SEQ_SUB(tpcb, npkts, lowsave);
|
|
|
|
if(npkts > 0)
|
|
tpcb->tp_Nwindow++;
|
|
|
|
if (npkts > TP_PM_MAX)
|
|
npkts = TP_PM_MAX;
|
|
|
|
GET_TIME_SINCE(&send_start_time, &send_end_time);
|
|
t = &(tpcb->tp_p_meas->tps_sendtime[npkts]);
|
|
t->tv_sec =
|
|
SMOOTH( long, TP_RTT_ALPHA, t->tv_sec, send_end_time.tv_sec);
|
|
t->tv_usec =
|
|
SMOOTH( long, TP_RTT_ALPHA, t->tv_usec, send_end_time.tv_usec);
|
|
|
|
if ( SEQ_LT(tpcb, lowseq, highseq) ) {
|
|
IncPStat(tpcb, tps_win_lim_by_data[npkts] );
|
|
} else {
|
|
IncPStat(tpcb, tps_win_lim_by_cdt[npkts] );
|
|
/* not true with congestion-window being used */
|
|
}
|
|
tpmeas( tpcb->tp_lref,
|
|
TPsbsend, &send_end_time, lowsave, tpcb->tp_Nwindow, npkts);
|
|
}
|
|
ENDPERF
|
|
#endif TP_PERF_MEAS
|
|
|
|
tpcb->tp_sndhiwat = lowseq;
|
|
|
|
if ( SEQ_LEQ(tpcb, lowsave, tpcb->tp_sndhiwat) &&
|
|
(tpcb->tp_class != TP_CLASS_0) )
|
|
tp_etimeout(tpcb->tp_refp, TM_data_retrans, lowsave,
|
|
tpcb->tp_sndhiwat,
|
|
(u_int)tpcb->tp_Nretrans, (int)tpcb->tp_dt_ticks);
|
|
IFTRACE(D_DATA)
|
|
tptraceTPCB( TPPTmisc,
|
|
"tp_send at end: sndhiwat lowseq eotsdu_reached error",
|
|
tpcb->tp_sndhiwat, lowseq, eotsdu_reached, tpcb->tp_sock->so_error);
|
|
|
|
ENDTRACE
|
|
}
|
|
|
|
/*
|
|
* NAME: tp_stash()
|
|
* CALLED FROM:
|
|
* tp.trans on arrival of a DT tpdu
|
|
* FUNCTION, ARGUMENTS, and RETURN VALUE:
|
|
* Returns 1 if
|
|
* a) something new arrived and it's got eotsdu_reached bit on,
|
|
* b) this arrival was caused other out-of-sequence things to be
|
|
* accepted, or
|
|
* c) this arrival is the highest seq # for which we last gave credit
|
|
* (sender just sent a whole window)
|
|
* In other words, returns 1 if tp should send an ack immediately, 0 if
|
|
* the ack can wait a while.
|
|
*
|
|
* Note: this implementation no longer renegs on credit, (except
|
|
* when debugging option D_RENEG is on, for the purpose of testing
|
|
* ack subsequencing), so we don't need to check for incoming tpdus
|
|
* being in a reneged portion of the window.
|
|
*/
|
|
|
|
int
|
|
tp_stash( tpcb, e )
|
|
register struct tp_pcb *tpcb;
|
|
register struct tp_event *e;
|
|
{
|
|
register int ack_reason= tpcb->tp_ack_strat & ACK_STRAT_EACH;
|
|
/* 0--> delay acks until full window */
|
|
/* 1--> ack each tpdu */
|
|
int newrec = 0;
|
|
|
|
#ifndef lint
|
|
#define E e->ATTR(DT_TPDU)
|
|
#else lint
|
|
#define E e->ev_union.EV_DT_TPDU
|
|
#endif lint
|
|
|
|
if ( E.e_eot ) {
|
|
register struct mbuf *n = E.e_data;
|
|
n->m_flags |= M_EOR;
|
|
n->m_act = 0;
|
|
}
|
|
IFDEBUG(D_STASH)
|
|
dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
|
|
"stash: so_rcv before appending");
|
|
dump_mbuf(E.e_data,
|
|
"stash: e_data before appending");
|
|
ENDDEBUG
|
|
|
|
IFPERF(tpcb)
|
|
PStat(tpcb, Nb_from_ll) += E.e_datalen;
|
|
tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
|
|
E.e_seq, (u_int)PStat(tpcb, Nb_from_ll), (u_int)E.e_datalen);
|
|
ENDPERF
|
|
|
|
if( E.e_seq == tpcb->tp_rcvnxt ) {
|
|
|
|
IFDEBUG(D_STASH)
|
|
printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x\n",
|
|
E.e_seq, E.e_datalen, E.e_eot);
|
|
ENDDEBUG
|
|
|
|
IFTRACE(D_STASH)
|
|
tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
|
|
E.e_seq, E.e_datalen, E.e_eot, 0);
|
|
ENDTRACE
|
|
|
|
sbappend(&tpcb->tp_sock->so_rcv, E.e_data);
|
|
|
|
if (newrec = E.e_eot ) /* ASSIGNMENT */
|
|
ack_reason |= ACK_EOT;
|
|
|
|
SEQ_INC( tpcb, tpcb->tp_rcvnxt );
|
|
/*
|
|
* move chains from the rtc list to the socket buffer
|
|
* and free the rtc header
|
|
*/
|
|
{
|
|
register struct tp_rtc **r = &tpcb->tp_rcvnxt_rtc;
|
|
register struct tp_rtc *s = tpcb->tp_rcvnxt_rtc;
|
|
|
|
while (s != (struct tp_rtc *)0 && s->tprt_seq == tpcb->tp_rcvnxt) {
|
|
*r = s->tprt_next;
|
|
|
|
sbappend(&tpcb->tp_sock->so_rcv, s->tprt_data);
|
|
|
|
SEQ_INC( tpcb, tpcb->tp_rcvnxt );
|
|
|
|
(void) m_free( dtom( s ) );
|
|
s = *r;
|
|
ack_reason |= ACK_REORDER;
|
|
}
|
|
}
|
|
IFDEBUG(D_STASH)
|
|
dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
|
|
"stash: so_rcv after appending");
|
|
ENDDEBUG
|
|
|
|
} else {
|
|
register struct tp_rtc **s = &tpcb->tp_rcvnxt_rtc;
|
|
register struct tp_rtc *r = tpcb->tp_rcvnxt_rtc;
|
|
register struct tp_rtc *t;
|
|
|
|
IFTRACE(D_STASH)
|
|
tptraceTPCB(TPPTmisc, "stash Reseq: seq rcvnxt lcdt",
|
|
E.e_seq, tpcb->tp_rcvnxt, tpcb->tp_lcredit, 0);
|
|
ENDTRACE
|
|
|
|
r = tpcb->tp_rcvnxt_rtc;
|
|
while (r != (struct tp_rtc *)0 && SEQ_LT(tpcb, r->tprt_seq, E.e_seq)) {
|
|
s = &r->tprt_next;
|
|
r = r->tprt_next;
|
|
}
|
|
|
|
if (r == (struct tp_rtc *)0 || SEQ_GT(tpcb, r->tprt_seq, E.e_seq) ) {
|
|
IncStat(ts_dt_ooo);
|
|
|
|
IFTRACE(D_STASH)
|
|
tptrace(TPPTmisc,
|
|
"tp_stash OUT OF ORDER- MAKE RTC: seq, 1st seq in list\n",
|
|
E.e_seq, r->tprt_seq,0,0);
|
|
ENDTRACE
|
|
IFDEBUG(D_STASH)
|
|
printf("tp_stash OUT OF ORDER- MAKE RTC\n");
|
|
ENDDEBUG
|
|
TP_MAKE_RTC(t, E.e_seq, E.e_eot, E.e_data, E.e_datalen, 0,
|
|
TPMT_RCVRTC);
|
|
|
|
*s = t;
|
|
t->tprt_next = (struct tp_rtc *)r;
|
|
ack_reason = ACK_DONT;
|
|
goto done;
|
|
} else {
|
|
IFDEBUG(D_STASH)
|
|
printf("tp_stash - drop & ack\n");
|
|
ENDDEBUG
|
|
|
|
/* retransmission - drop it and force an ack */
|
|
IncStat(ts_dt_dup);
|
|
IFPERF(tpcb)
|
|
IncPStat(tpcb, tps_n_ack_cuz_dup);
|
|
ENDPERF
|
|
|
|
m_freem( E.e_data );
|
|
ack_reason |= ACK_DUP;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* an ack should be sent when at least one of the
|
|
* following holds:
|
|
* a) we've received a TPDU with EOTSDU set
|
|
* b) the TPDU that just arrived represents the
|
|
* full window last advertised, or
|
|
* c) when seq X arrives [ where
|
|
* X = last_sent_uwe - 1/2 last_lcredit_sent
|
|
* (the packet representing 1/2 the last advertised window) ]
|
|
* and lcredit at the time of X arrival > last_lcredit_sent/2
|
|
* In other words, if the last ack sent advertised cdt=8 and uwe = 8
|
|
* then when seq 4 arrives I'd like to send a new ack
|
|
* iff the credit at the time of 4's arrival is > 4.
|
|
* The other end thinks it has cdt of 4 so if local cdt
|
|
* is still 4 there's no point in sending an ack, but if
|
|
* my credit has increased because the receiver has taken
|
|
* some data out of the buffer (soreceive doesn't notify
|
|
* me until the SYSTEM CALL finishes), I'd like to tell
|
|
* the other end.
|
|
*/
|
|
|
|
done:
|
|
{
|
|
LOCAL_CREDIT(tpcb);
|
|
|
|
if ( E.e_seq == tpcb->tp_sent_uwe )
|
|
ack_reason |= ACK_STRAT_FULLWIN;
|
|
|
|
IFTRACE(D_STASH)
|
|
tptraceTPCB(TPPTmisc,
|
|
"end of stash, eot, ack_reason, sent_uwe ",
|
|
E.e_eot, ack_reason, tpcb->tp_sent_uwe, 0);
|
|
ENDTRACE
|
|
|
|
if ( ack_reason == ACK_DONT ) {
|
|
IncStat( ts_ackreason[ACK_DONT] );
|
|
return 0;
|
|
} else {
|
|
IFPERF(tpcb)
|
|
if(ack_reason & ACK_EOT) {
|
|
IncPStat(tpcb, tps_n_ack_cuz_eot);
|
|
}
|
|
if(ack_reason & ACK_STRAT_EACH) {
|
|
IncPStat(tpcb, tps_n_ack_cuz_strat);
|
|
} else if(ack_reason & ACK_STRAT_FULLWIN) {
|
|
IncPStat(tpcb, tps_n_ack_cuz_fullwin);
|
|
} else if(ack_reason & ACK_REORDER) {
|
|
IncPStat(tpcb, tps_n_ack_cuz_reorder);
|
|
}
|
|
tpmeas(tpcb->tp_lref, TPtime_ack_sent, 0,
|
|
SEQ_ADD(tpcb, E.e_seq, 1), 0, 0);
|
|
ENDPERF
|
|
{
|
|
register int i;
|
|
|
|
/* keep track of all reasons that apply */
|
|
for( i=1; i<_ACK_NUM_REASONS_ ;i++) {
|
|
if( ack_reason & (1<<i) )
|
|
IncStat( ts_ackreason[i] );
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
}
|