2006-03-21 04:41:47 +03:00
/*
* net / dccp / ccids / ccid2 . c
*
* Copyright ( c ) 2005 , 2006 Andrea Bittau < a . bittau @ cs . ucl . ac . uk >
*
* Changes to meet Linux coding standards , and DCCP infrastructure fixes .
*
* Copyright ( c ) 2006 Arnaldo Carvalho de Melo < acme @ conectiva . com . br >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
/*
* This implementation should follow : draft - ietf - dccp - ccid2 - 10. txt
*
* BUGS :
* - sequence number wrapping
*/
# include "../ccid.h"
# include "../dccp.h"
# include "ccid2.h"
static int ccid2_debug ;
2006-03-21 09:05:37 +03:00
# undef CCID2_DEBUG
2006-03-21 04:41:47 +03:00
# ifdef CCID2_DEBUG
# define ccid2_pr_debug(format, a...) \
do { if ( ccid2_debug ) \
printk ( KERN_DEBUG " %s: " format , __FUNCTION__ , # # a ) ; \
} while ( 0 )
# else
# define ccid2_pr_debug(format, a...)
# endif
static const int ccid2_seq_len = 128 ;
# ifdef CCID2_DEBUG
static void ccid2_hc_tx_check_sanity ( const struct ccid2_hc_tx_sock * hctx )
{
int len = 0 ;
int pipe = 0 ;
2006-03-21 09:05:37 +03:00
struct ccid2_seq * seqp = hctx - > ccid2hctx_seqh ;
2006-03-21 04:41:47 +03:00
/* there is data in the chain */
if ( seqp ! = hctx - > ccid2hctx_seqt ) {
seqp = seqp - > ccid2s_prev ;
len + + ;
if ( ! seqp - > ccid2s_acked )
pipe + + ;
while ( seqp ! = hctx - > ccid2hctx_seqt ) {
2006-03-21 09:05:37 +03:00
struct ccid2_seq * prev = seqp - > ccid2s_prev ;
2006-03-21 04:41:47 +03:00
len + + ;
if ( ! prev - > ccid2s_acked )
pipe + + ;
/* packets are sent sequentially */
BUG_ON ( seqp - > ccid2s_seq < = prev - > ccid2s_seq ) ;
2006-09-20 00:06:46 +04:00
BUG_ON ( time_before ( seqp - > ccid2s_sent ,
prev - > ccid2s_sent ) ) ;
2006-03-21 04:41:47 +03:00
BUG_ON ( len > ccid2_seq_len ) ;
seqp = prev ;
}
}
BUG_ON ( pipe ! = hctx - > ccid2hctx_pipe ) ;
ccid2_pr_debug ( " len of chain=%d \n " , len ) ;
do {
seqp = seqp - > ccid2s_prev ;
len + + ;
BUG_ON ( len > ccid2_seq_len ) ;
2006-03-21 09:05:37 +03:00
} while ( seqp ! = hctx - > ccid2hctx_seqh ) ;
2006-03-21 04:41:47 +03:00
BUG_ON ( len ! = ccid2_seq_len ) ;
ccid2_pr_debug ( " total len=%d \n " , len ) ;
}
# else
# define ccid2_hc_tx_check_sanity(hctx) do {} while (0)
# endif
static int ccid2_hc_tx_send_packet ( struct sock * sk ,
struct sk_buff * skb , int len )
{
struct ccid2_hc_tx_sock * hctx ;
switch ( DCCP_SKB_CB ( skb ) - > dccpd_type ) {
case 0 : /* XXX data packets from userland come through like this */
case DCCP_PKT_DATA :
case DCCP_PKT_DATAACK :
break ;
/* No congestion control on other packets */
default :
return 0 ;
}
hctx = ccid2_hc_tx_sk ( sk ) ;
ccid2_pr_debug ( " pipe=%d cwnd=%d \n " , hctx - > ccid2hctx_pipe ,
hctx - > ccid2hctx_cwnd ) ;
if ( hctx - > ccid2hctx_pipe < hctx - > ccid2hctx_cwnd ) {
/* OK we can send... make sure previous packet was sent off */
if ( ! hctx - > ccid2hctx_sendwait ) {
hctx - > ccid2hctx_sendwait = 1 ;
return 0 ;
}
}
2006-09-20 00:10:11 +04:00
return 1 ; /* XXX CCID should dequeue when ready instead of polling */
2006-03-21 04:41:47 +03:00
}
static void ccid2_change_l_ack_ratio ( struct sock * sk , int val )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
/*
* XXX I don ' t really agree with val ! = 2. If cwnd is 1 , ack ratio
* should be 1. . . it shouldn ' t be allowed to become 2.
* - sorbo .
*/
if ( val ! = 2 ) {
2006-03-21 09:05:37 +03:00
const struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
2006-03-21 04:41:47 +03:00
int max = hctx - > ccid2hctx_cwnd / 2 ;
/* round up */
if ( hctx - > ccid2hctx_cwnd & 1 )
max + + ;
if ( val > max )
val = max ;
}
ccid2_pr_debug ( " changing local ack ratio to %d \n " , val ) ;
WARN_ON ( val < = 0 ) ;
dp - > dccps_l_ack_ratio = val ;
}
static void ccid2_change_cwnd ( struct sock * sk , int val )
{
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
if ( val = = 0 )
val = 1 ;
/* XXX do we need to change ack ratio? */
ccid2_pr_debug ( " change cwnd to %d \n " , val ) ;
BUG_ON ( val < 1 ) ;
hctx - > ccid2hctx_cwnd = val ;
}
static void ccid2_start_rto_timer ( struct sock * sk ) ;
static void ccid2_hc_tx_rto_expire ( unsigned long data )
{
struct sock * sk = ( struct sock * ) data ;
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
long s ;
bh_lock_sock ( sk ) ;
if ( sock_owned_by_user ( sk ) ) {
sk_reset_timer ( sk , & hctx - > ccid2hctx_rtotimer ,
jiffies + HZ / 5 ) ;
goto out ;
}
ccid2_pr_debug ( " RTO_EXPIRE \n " ) ;
ccid2_hc_tx_check_sanity ( hctx ) ;
/* back-off timer */
hctx - > ccid2hctx_rto < < = 1 ;
s = hctx - > ccid2hctx_rto / HZ ;
if ( s > 60 )
hctx - > ccid2hctx_rto = 60 * HZ ;
ccid2_start_rto_timer ( sk ) ;
/* adjust pipe, cwnd etc */
hctx - > ccid2hctx_pipe = 0 ;
hctx - > ccid2hctx_ssthresh = hctx - > ccid2hctx_cwnd > > 1 ;
if ( hctx - > ccid2hctx_ssthresh < 2 )
hctx - > ccid2hctx_ssthresh = 2 ;
ccid2_change_cwnd ( sk , 1 ) ;
/* clear state about stuff we sent */
hctx - > ccid2hctx_seqt = hctx - > ccid2hctx_seqh ;
hctx - > ccid2hctx_ssacks = 0 ;
hctx - > ccid2hctx_acks = 0 ;
hctx - > ccid2hctx_sent = 0 ;
/* clear ack ratio state. */
hctx - > ccid2hctx_arsent = 0 ;
hctx - > ccid2hctx_ackloss = 0 ;
hctx - > ccid2hctx_rpseq = 0 ;
hctx - > ccid2hctx_rpdupack = - 1 ;
ccid2_change_l_ack_ratio ( sk , 1 ) ;
ccid2_hc_tx_check_sanity ( hctx ) ;
out :
bh_unlock_sock ( sk ) ;
2006-03-21 04:57:52 +03:00
sock_put ( sk ) ;
2006-03-21 04:41:47 +03:00
}
static void ccid2_start_rto_timer ( struct sock * sk )
{
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
ccid2_pr_debug ( " setting RTO timeout=%ld \n " , hctx - > ccid2hctx_rto ) ;
BUG_ON ( timer_pending ( & hctx - > ccid2hctx_rtotimer ) ) ;
sk_reset_timer ( sk , & hctx - > ccid2hctx_rtotimer ,
jiffies + hctx - > ccid2hctx_rto ) ;
}
static void ccid2_hc_tx_packet_sent ( struct sock * sk , int more , int len )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
u64 seq ;
ccid2_hc_tx_check_sanity ( hctx ) ;
BUG_ON ( ! hctx - > ccid2hctx_sendwait ) ;
hctx - > ccid2hctx_sendwait = 0 ;
hctx - > ccid2hctx_pipe + + ;
BUG_ON ( hctx - > ccid2hctx_pipe < 0 ) ;
/* There is an issue. What if another packet is sent between
* packet_send ( ) and packet_sent ( ) . Then the sequence number would be
* wrong .
* - sorbo .
*/
seq = dp - > dccps_gss ;
hctx - > ccid2hctx_seqh - > ccid2s_seq = seq ;
hctx - > ccid2hctx_seqh - > ccid2s_acked = 0 ;
hctx - > ccid2hctx_seqh - > ccid2s_sent = jiffies ;
hctx - > ccid2hctx_seqh = hctx - > ccid2hctx_seqh - > ccid2s_next ;
ccid2_pr_debug ( " cwnd=%d pipe=%d \n " , hctx - > ccid2hctx_cwnd ,
hctx - > ccid2hctx_pipe ) ;
if ( hctx - > ccid2hctx_seqh = = hctx - > ccid2hctx_seqt ) {
/* XXX allocate more space */
WARN_ON ( 1 ) ;
}
hctx - > ccid2hctx_sent + + ;
/* Ack Ratio. Need to maintain a concept of how many windows we sent */
hctx - > ccid2hctx_arsent + + ;
/* We had an ack loss in this window... */
if ( hctx - > ccid2hctx_ackloss ) {
if ( hctx - > ccid2hctx_arsent > = hctx - > ccid2hctx_cwnd ) {
2006-03-21 09:05:37 +03:00
hctx - > ccid2hctx_arsent = 0 ;
hctx - > ccid2hctx_ackloss = 0 ;
2006-03-21 04:41:47 +03:00
}
2006-03-21 09:05:37 +03:00
} else {
/* No acks lost up to now... */
2006-03-21 04:41:47 +03:00
/* decrease ack ratio if enough packets were sent */
if ( dp - > dccps_l_ack_ratio > 1 ) {
/* XXX don't calculate denominator each time */
2006-03-21 09:05:37 +03:00
int denom = dp - > dccps_l_ack_ratio * dp - > dccps_l_ack_ratio -
dp - > dccps_l_ack_ratio ;
2006-03-21 04:41:47 +03:00
denom = hctx - > ccid2hctx_cwnd * hctx - > ccid2hctx_cwnd / denom ;
if ( hctx - > ccid2hctx_arsent > = denom ) {
ccid2_change_l_ack_ratio ( sk , dp - > dccps_l_ack_ratio - 1 ) ;
hctx - > ccid2hctx_arsent = 0 ;
}
2006-03-21 09:05:37 +03:00
} else {
/* we can't increase ack ratio further [1] */
2006-03-21 04:41:47 +03:00
hctx - > ccid2hctx_arsent = 0 ; /* or maybe set it to cwnd*/
}
}
/* setup RTO timer */
2006-03-21 09:05:37 +03:00
if ( ! timer_pending ( & hctx - > ccid2hctx_rtotimer ) )
2006-03-21 04:41:47 +03:00
ccid2_start_rto_timer ( sk ) ;
2006-03-21 09:05:37 +03:00
2006-03-21 04:41:47 +03:00
# ifdef CCID2_DEBUG
ccid2_pr_debug ( " pipe=%d \n " , hctx - > ccid2hctx_pipe ) ;
ccid2_pr_debug ( " Sent: seq=%llu \n " , seq ) ;
do {
struct ccid2_seq * seqp = hctx - > ccid2hctx_seqt ;
while ( seqp ! = hctx - > ccid2hctx_seqh ) {
ccid2_pr_debug ( " out seq=%llu acked=%d time=%lu \n " ,
seqp - > ccid2s_seq , seqp - > ccid2s_acked ,
seqp - > ccid2s_sent ) ;
seqp = seqp - > ccid2s_next ;
}
2006-03-21 09:05:37 +03:00
} while ( 0 ) ;
2006-03-21 04:41:47 +03:00
ccid2_pr_debug ( " ========= \n " ) ;
ccid2_hc_tx_check_sanity ( hctx ) ;
# endif
}
/* XXX Lame code duplication!
* returns - 1 if none was found .
* else returns the next offset to use in the function call .
*/
static int ccid2_ackvector ( struct sock * sk , struct sk_buff * skb , int offset ,
unsigned char * * vec , unsigned char * veclen )
{
const struct dccp_hdr * dh = dccp_hdr ( skb ) ;
unsigned char * options = ( unsigned char * ) dh + dccp_hdr_len ( skb ) ;
unsigned char * opt_ptr ;
const unsigned char * opt_end = ( unsigned char * ) dh +
( dh - > dccph_doff * 4 ) ;
unsigned char opt , len ;
unsigned char * value ;
BUG_ON ( offset < 0 ) ;
options + = offset ;
opt_ptr = options ;
if ( opt_ptr > = opt_end )
return - 1 ;
while ( opt_ptr ! = opt_end ) {
opt = * opt_ptr + + ;
len = 0 ;
value = NULL ;
/* Check if this isn't a single byte option */
if ( opt > DCCPO_MAX_RESERVED ) {
if ( opt_ptr = = opt_end )
goto out_invalid_option ;
len = * opt_ptr + + ;
if ( len < 3 )
goto out_invalid_option ;
/*
* Remove the type and len fields , leaving
* just the value size
*/
len - = 2 ;
value = opt_ptr ;
opt_ptr + = len ;
if ( opt_ptr > opt_end )
goto out_invalid_option ;
}
switch ( opt ) {
case DCCPO_ACK_VECTOR_0 :
case DCCPO_ACK_VECTOR_1 :
* vec = value ;
* veclen = len ;
return offset + ( opt_ptr - options ) ;
}
}
return - 1 ;
out_invalid_option :
BUG_ON ( 1 ) ; /* should never happen... options were previously parsed ! */
return - 1 ;
}
2006-03-21 04:57:52 +03:00
static void ccid2_hc_tx_kill_rto_timer ( struct sock * sk )
2006-03-21 04:41:47 +03:00
{
2006-03-21 04:57:52 +03:00
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
sk_stop_timer ( sk , & hctx - > ccid2hctx_rtotimer ) ;
ccid2_pr_debug ( " deleted RTO timer \n " ) ;
2006-03-21 04:41:47 +03:00
}
static inline void ccid2_new_ack ( struct sock * sk ,
struct ccid2_seq * seqp ,
unsigned int * maxincr )
{
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
/* slow start */
if ( hctx - > ccid2hctx_cwnd < hctx - > ccid2hctx_ssthresh ) {
hctx - > ccid2hctx_acks = 0 ;
/* We can increase cwnd at most maxincr [ack_ratio/2] */
if ( * maxincr ) {
/* increase every 2 acks */
hctx - > ccid2hctx_ssacks + + ;
if ( hctx - > ccid2hctx_ssacks = = 2 ) {
ccid2_change_cwnd ( sk , hctx - > ccid2hctx_cwnd + 1 ) ;
hctx - > ccid2hctx_ssacks = 0 ;
* maxincr = * maxincr - 1 ;
}
2006-03-21 09:05:37 +03:00
} else {
/* increased cwnd enough for this single ack */
2006-03-21 04:41:47 +03:00
hctx - > ccid2hctx_ssacks = 0 ;
}
2006-03-21 09:05:37 +03:00
} else {
2006-03-21 04:41:47 +03:00
hctx - > ccid2hctx_ssacks = 0 ;
hctx - > ccid2hctx_acks + + ;
if ( hctx - > ccid2hctx_acks > = hctx - > ccid2hctx_cwnd ) {
ccid2_change_cwnd ( sk , hctx - > ccid2hctx_cwnd + 1 ) ;
hctx - > ccid2hctx_acks = 0 ;
}
}
/* update RTO */
if ( hctx - > ccid2hctx_srtt = = - 1 | |
2006-09-20 00:06:46 +04:00
time_after ( jiffies , hctx - > ccid2hctx_lastrtt + hctx - > ccid2hctx_srtt ) ) {
unsigned long r = ( long ) jiffies - ( long ) seqp - > ccid2s_sent ;
2006-03-21 04:41:47 +03:00
int s ;
/* first measurement */
if ( hctx - > ccid2hctx_srtt = = - 1 ) {
ccid2_pr_debug ( " R: %lu Time=%lu seq=%llu \n " ,
r , jiffies , seqp - > ccid2s_seq ) ;
hctx - > ccid2hctx_srtt = r ;
hctx - > ccid2hctx_rttvar = r > > 1 ;
2006-03-21 09:05:37 +03:00
} else {
2006-03-21 04:41:47 +03:00
/* RTTVAR */
long tmp = hctx - > ccid2hctx_srtt - r ;
if ( tmp < 0 )
tmp * = - 1 ;
tmp > > = 2 ;
hctx - > ccid2hctx_rttvar * = 3 ;
hctx - > ccid2hctx_rttvar > > = 2 ;
hctx - > ccid2hctx_rttvar + = tmp ;
/* SRTT */
hctx - > ccid2hctx_srtt * = 7 ;
hctx - > ccid2hctx_srtt > > = 3 ;
tmp = r > > 3 ;
hctx - > ccid2hctx_srtt + = tmp ;
}
s = hctx - > ccid2hctx_rttvar < < 2 ;
/* clock granularity is 1 when based on jiffies */
if ( ! s )
s = 1 ;
hctx - > ccid2hctx_rto = hctx - > ccid2hctx_srtt + s ;
/* must be at least a second */
s = hctx - > ccid2hctx_rto / HZ ;
/* DCCP doesn't require this [but I like it cuz my code sux] */
# if 1
if ( s < 1 )
hctx - > ccid2hctx_rto = HZ ;
# endif
/* max 60 seconds */
if ( s > 60 )
hctx - > ccid2hctx_rto = HZ * 60 ;
hctx - > ccid2hctx_lastrtt = jiffies ;
ccid2_pr_debug ( " srtt: %ld rttvar: %ld rto: %ld (HZ=%d) R=%lu \n " ,
hctx - > ccid2hctx_srtt , hctx - > ccid2hctx_rttvar ,
hctx - > ccid2hctx_rto , HZ , r ) ;
hctx - > ccid2hctx_sent = 0 ;
}
/* we got a new ack, so re-start RTO timer */
2006-03-21 04:57:52 +03:00
ccid2_hc_tx_kill_rto_timer ( sk ) ;
2006-03-21 04:41:47 +03:00
ccid2_start_rto_timer ( sk ) ;
}
2006-03-21 04:57:52 +03:00
static void ccid2_hc_tx_dec_pipe ( struct sock * sk )
2006-03-21 04:41:47 +03:00
{
2006-03-21 04:57:52 +03:00
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
2006-03-21 04:41:47 +03:00
hctx - > ccid2hctx_pipe - - ;
BUG_ON ( hctx - > ccid2hctx_pipe < 0 ) ;
if ( hctx - > ccid2hctx_pipe = = 0 )
2006-03-21 04:57:52 +03:00
ccid2_hc_tx_kill_rto_timer ( sk ) ;
2006-03-21 04:41:47 +03:00
}
static void ccid2_hc_tx_packet_recv ( struct sock * sk , struct sk_buff * skb )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
u64 ackno , seqno ;
struct ccid2_seq * seqp ;
unsigned char * vector ;
unsigned char veclen ;
int offset = 0 ;
int done = 0 ;
int loss = 0 ;
unsigned int maxincr = 0 ;
ccid2_hc_tx_check_sanity ( hctx ) ;
/* check reverse path congestion */
seqno = DCCP_SKB_CB ( skb ) - > dccpd_seq ;
/* XXX this whole "algorithm" is broken. Need to fix it to keep track
* of the seqnos of the dupacks so that rpseq and rpdupack are correct
* - sorbo .
*/
/* need to bootstrap */
if ( hctx - > ccid2hctx_rpdupack = = - 1 ) {
hctx - > ccid2hctx_rpdupack = 0 ;
hctx - > ccid2hctx_rpseq = seqno ;
2006-03-21 09:05:37 +03:00
} else {
2006-03-21 04:41:47 +03:00
/* check if packet is consecutive */
2006-03-21 09:05:37 +03:00
if ( ( hctx - > ccid2hctx_rpseq + 1 ) = = seqno )
2006-03-21 04:41:47 +03:00
hctx - > ccid2hctx_rpseq + + ;
/* it's a later packet */
else if ( after48 ( seqno , hctx - > ccid2hctx_rpseq ) ) {
hctx - > ccid2hctx_rpdupack + + ;
/* check if we got enough dupacks */
if ( hctx - > ccid2hctx_rpdupack > =
hctx - > ccid2hctx_numdupack ) {
hctx - > ccid2hctx_rpdupack = - 1 ; /* XXX lame */
hctx - > ccid2hctx_rpseq = 0 ;
ccid2_change_l_ack_ratio ( sk , dp - > dccps_l_ack_ratio < < 1 ) ;
}
}
}
/* check forward path congestion */
/* still didn't send out new data packets */
if ( hctx - > ccid2hctx_seqh = = hctx - > ccid2hctx_seqt )
return ;
switch ( DCCP_SKB_CB ( skb ) - > dccpd_type ) {
case DCCP_PKT_ACK :
case DCCP_PKT_DATAACK :
break ;
default :
return ;
}
ackno = DCCP_SKB_CB ( skb ) - > dccpd_ack_seq ;
seqp = hctx - > ccid2hctx_seqh - > ccid2s_prev ;
/* If in slow-start, cwnd can increase at most Ack Ratio / 2 packets for
* this single ack . I round up .
* - sorbo .
*/
maxincr = dp - > dccps_l_ack_ratio > > 1 ;
maxincr + + ;
/* go through all ack vectors */
while ( ( offset = ccid2_ackvector ( sk , skb , offset ,
& vector , & veclen ) ) ! = - 1 ) {
/* go through this ack vector */
while ( veclen - - ) {
const u8 rl = * vector & DCCP_ACKVEC_LEN_MASK ;
u64 ackno_end_rl ;
dccp_set_seqno ( & ackno_end_rl , ackno - rl ) ;
ccid2_pr_debug ( " ackvec start:%llu end:%llu \n " , ackno ,
ackno_end_rl ) ;
/* if the seqno we are analyzing is larger than the
* current ackno , then move towards the tail of our
* seqnos .
*/
while ( after48 ( seqp - > ccid2s_seq , ackno ) ) {
if ( seqp = = hctx - > ccid2hctx_seqt ) {
done = 1 ;
break ;
}
seqp = seqp - > ccid2s_prev ;
}
if ( done )
break ;
/* check all seqnos in the range of the vector
* run length
*/
while ( between48 ( seqp - > ccid2s_seq , ackno_end_rl , ackno ) ) {
2006-09-20 00:05:35 +04:00
const u8 state = * vector &
DCCP_ACKVEC_STATE_MASK ;
2006-03-21 04:41:47 +03:00
/* new packet received or marked */
if ( state ! = DCCP_ACKVEC_STATE_NOT_RECEIVED & &
! seqp - > ccid2s_acked ) {
if ( state = =
DCCP_ACKVEC_STATE_ECN_MARKED ) {
loss = 1 ;
2006-03-21 09:05:37 +03:00
} else
2006-03-21 04:41:47 +03:00
ccid2_new_ack ( sk , seqp ,
& maxincr ) ;
seqp - > ccid2s_acked = 1 ;
ccid2_pr_debug ( " Got ack for %llu \n " ,
seqp - > ccid2s_seq ) ;
2006-03-21 04:57:52 +03:00
ccid2_hc_tx_dec_pipe ( sk ) ;
2006-03-21 04:41:47 +03:00
}
if ( seqp = = hctx - > ccid2hctx_seqt ) {
done = 1 ;
break ;
}
seqp = seqp - > ccid2s_next ;
}
if ( done )
break ;
dccp_set_seqno ( & ackno , ackno_end_rl - 1 ) ;
vector + + ;
}
if ( done )
break ;
}
/* The state about what is acked should be correct now
* Check for NUMDUPACK
*/
seqp = hctx - > ccid2hctx_seqh - > ccid2s_prev ;
done = 0 ;
while ( 1 ) {
if ( seqp - > ccid2s_acked ) {
done + + ;
2006-03-21 09:05:37 +03:00
if ( done = = hctx - > ccid2hctx_numdupack )
2006-03-21 04:41:47 +03:00
break ;
}
2006-03-21 09:05:37 +03:00
if ( seqp = = hctx - > ccid2hctx_seqt )
2006-03-21 04:41:47 +03:00
break ;
seqp = seqp - > ccid2s_prev ;
}
/* If there are at least 3 acknowledgements, anything unacknowledged
* below the last sequence number is considered lost
*/
if ( done = = hctx - > ccid2hctx_numdupack ) {
struct ccid2_seq * last_acked = seqp ;
/* check for lost packets */
while ( 1 ) {
if ( ! seqp - > ccid2s_acked ) {
loss = 1 ;
2006-03-21 04:57:52 +03:00
ccid2_hc_tx_dec_pipe ( sk ) ;
2006-03-21 04:41:47 +03:00
}
if ( seqp = = hctx - > ccid2hctx_seqt )
break ;
seqp = seqp - > ccid2s_prev ;
}
hctx - > ccid2hctx_seqt = last_acked ;
}
/* trim acked packets in tail */
while ( hctx - > ccid2hctx_seqt ! = hctx - > ccid2hctx_seqh ) {
if ( ! hctx - > ccid2hctx_seqt - > ccid2s_acked )
break ;
hctx - > ccid2hctx_seqt = hctx - > ccid2hctx_seqt - > ccid2s_next ;
}
if ( loss ) {
/* XXX do bit shifts guarantee a 0 as the new bit? */
ccid2_change_cwnd ( sk , hctx - > ccid2hctx_cwnd > > 1 ) ;
hctx - > ccid2hctx_ssthresh = hctx - > ccid2hctx_cwnd ;
if ( hctx - > ccid2hctx_ssthresh < 2 )
hctx - > ccid2hctx_ssthresh = 2 ;
}
ccid2_hc_tx_check_sanity ( hctx ) ;
}
2006-03-21 06:21:44 +03:00
static int ccid2_hc_tx_init ( struct ccid * ccid , struct sock * sk )
2006-03-21 04:41:47 +03:00
{
2006-03-21 06:21:44 +03:00
struct ccid2_hc_tx_sock * hctx = ccid_priv ( ccid ) ;
2006-03-21 04:41:47 +03:00
int seqcount = ccid2_seq_len ;
int i ;
hctx - > ccid2hctx_cwnd = 1 ;
2006-09-20 00:07:20 +04:00
/* Initialize ssthresh to infinity. This means that we will exit the
* initial slow - start after the first packet loss . This is what we
* want .
*/
hctx - > ccid2hctx_ssthresh = ~ 0 ;
2006-03-21 04:41:47 +03:00
hctx - > ccid2hctx_numdupack = 3 ;
/* XXX init ~ to window size... */
hctx - > ccid2hctx_seqbuf = kmalloc ( sizeof ( * hctx - > ccid2hctx_seqbuf ) *
seqcount , gfp_any ( ) ) ;
2006-03-21 06:21:44 +03:00
if ( hctx - > ccid2hctx_seqbuf = = NULL )
2006-03-21 04:41:47 +03:00
return - ENOMEM ;
2006-03-21 06:21:44 +03:00
2006-03-21 04:41:47 +03:00
for ( i = 0 ; i < ( seqcount - 1 ) ; i + + ) {
hctx - > ccid2hctx_seqbuf [ i ] . ccid2s_next =
& hctx - > ccid2hctx_seqbuf [ i + 1 ] ;
hctx - > ccid2hctx_seqbuf [ i + 1 ] . ccid2s_prev =
& hctx - > ccid2hctx_seqbuf [ i ] ;
}
hctx - > ccid2hctx_seqbuf [ seqcount - 1 ] . ccid2s_next =
hctx - > ccid2hctx_seqbuf ;
hctx - > ccid2hctx_seqbuf - > ccid2s_prev =
& hctx - > ccid2hctx_seqbuf [ seqcount - 1 ] ;
hctx - > ccid2hctx_seqh = hctx - > ccid2hctx_seqbuf ;
hctx - > ccid2hctx_seqt = hctx - > ccid2hctx_seqh ;
hctx - > ccid2hctx_sent = 0 ;
hctx - > ccid2hctx_rto = 3 * HZ ;
hctx - > ccid2hctx_srtt = - 1 ;
hctx - > ccid2hctx_rttvar = - 1 ;
hctx - > ccid2hctx_lastrtt = 0 ;
hctx - > ccid2hctx_rpdupack = - 1 ;
hctx - > ccid2hctx_rtotimer . function = & ccid2_hc_tx_rto_expire ;
hctx - > ccid2hctx_rtotimer . data = ( unsigned long ) sk ;
init_timer ( & hctx - > ccid2hctx_rtotimer ) ;
ccid2_hc_tx_check_sanity ( hctx ) ;
return 0 ;
}
static void ccid2_hc_tx_exit ( struct sock * sk )
{
2006-03-21 04:57:52 +03:00
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
2006-03-21 04:41:47 +03:00
2006-03-21 04:57:52 +03:00
ccid2_hc_tx_kill_rto_timer ( sk ) ;
2006-03-21 04:41:47 +03:00
kfree ( hctx - > ccid2hctx_seqbuf ) ;
2006-03-21 06:21:44 +03:00
hctx - > ccid2hctx_seqbuf = NULL ;
2006-03-21 04:41:47 +03:00
}
static void ccid2_hc_rx_packet_recv ( struct sock * sk , struct sk_buff * skb )
{
const struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid2_hc_rx_sock * hcrx = ccid2_hc_rx_sk ( sk ) ;
switch ( DCCP_SKB_CB ( skb ) - > dccpd_type ) {
case DCCP_PKT_DATA :
case DCCP_PKT_DATAACK :
hcrx - > ccid2hcrx_data + + ;
if ( hcrx - > ccid2hcrx_data > = dp - > dccps_r_ack_ratio ) {
dccp_send_ack ( sk ) ;
hcrx - > ccid2hcrx_data = 0 ;
}
break ;
}
}
2006-03-21 06:21:44 +03:00
static struct ccid_operations ccid2 = {
2006-03-21 04:41:47 +03:00
. ccid_id = 2 ,
. ccid_name = " ccid2 " ,
. ccid_owner = THIS_MODULE ,
2006-03-21 06:21:44 +03:00
. ccid_hc_tx_obj_size = sizeof ( struct ccid2_hc_tx_sock ) ,
2006-03-21 04:41:47 +03:00
. ccid_hc_tx_init = ccid2_hc_tx_init ,
. ccid_hc_tx_exit = ccid2_hc_tx_exit ,
. ccid_hc_tx_send_packet = ccid2_hc_tx_send_packet ,
. ccid_hc_tx_packet_sent = ccid2_hc_tx_packet_sent ,
. ccid_hc_tx_packet_recv = ccid2_hc_tx_packet_recv ,
2006-03-21 06:21:44 +03:00
. ccid_hc_rx_obj_size = sizeof ( struct ccid2_hc_rx_sock ) ,
2006-03-21 04:41:47 +03:00
. ccid_hc_rx_packet_recv = ccid2_hc_rx_packet_recv ,
} ;
module_param ( ccid2_debug , int , 0444 ) ;
MODULE_PARM_DESC ( ccid2_debug , " Enable debug messages " ) ;
static __init int ccid2_module_init ( void )
{
return ccid_register ( & ccid2 ) ;
}
module_init ( ccid2_module_init ) ;
static __exit void ccid2_module_exit ( void )
{
ccid_unregister ( & ccid2 ) ;
}
module_exit ( ccid2_module_exit ) ;
MODULE_AUTHOR ( " Andrea Bittau <a.bittau@cs.ucl.ac.uk> " ) ;
2006-03-21 09:05:37 +03:00
MODULE_DESCRIPTION ( " DCCP TCP-Like (CCID2) CCID " ) ;
2006-03-21 04:41:47 +03:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " net-dccp-ccid-2 " ) ;