2006-03-20 17:41:47 -08: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 .
*/
/*
2006-10-24 16:17:51 -07:00
* This implementation should follow RFC 4341
2006-03-20 17:41:47 -08:00
*/
# include "../ccid.h"
# include "../dccp.h"
# include "ccid2.h"
2006-09-19 13:12:44 -07:00
# ifdef CONFIG_IP_DCCP_CCID2_DEBUG
2006-11-20 18:26:03 -02:00
static int ccid2_debug ;
# define ccid2_pr_debug(format, a...) DCCP_PR_DEBUG(ccid2_debug, format, ##a)
2006-03-20 17:41:47 -08:00
static void ccid2_hc_tx_check_sanity ( const struct ccid2_hc_tx_sock * hctx )
{
int len = 0 ;
int pipe = 0 ;
2006-03-20 22:05:37 -08:00
struct ccid2_seq * seqp = hctx - > ccid2hctx_seqh ;
2006-03-20 17:41:47 -08: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-20 22:05:37 -08:00
struct ccid2_seq * prev = seqp - > ccid2s_prev ;
2006-03-20 17:41:47 -08:00
len + + ;
if ( ! prev - > ccid2s_acked )
pipe + + ;
/* packets are sent sequentially */
2007-10-04 14:43:09 -07:00
BUG_ON ( dccp_delta_seqno ( seqp - > ccid2s_seq ,
prev - > ccid2s_seq ) > = 0 ) ;
2006-09-19 13:06:46 -07:00
BUG_ON ( time_before ( seqp - > ccid2s_sent ,
prev - > ccid2s_sent ) ) ;
2006-03-20 17:41:47 -08:00
seqp = prev ;
}
}
BUG_ON ( pipe ! = hctx - > ccid2hctx_pipe ) ;
ccid2_pr_debug ( " len of chain=%d \n " , len ) ;
do {
seqp = seqp - > ccid2s_prev ;
len + + ;
2006-03-20 22:05:37 -08:00
} while ( seqp ! = hctx - > ccid2hctx_seqh ) ;
2006-03-20 17:41:47 -08:00
ccid2_pr_debug ( " total len=%d \n " , len ) ;
2006-09-19 13:13:37 -07:00
BUG_ON ( len ! = hctx - > ccid2hctx_seqbufc * CCID2_SEQBUF_LEN ) ;
2006-03-20 17:41:47 -08:00
}
# else
2006-11-20 18:26:03 -02:00
# define ccid2_pr_debug(format, a...)
# define ccid2_hc_tx_check_sanity(hctx)
2006-03-20 17:41:47 -08:00
# endif
2007-10-04 14:41:00 -07:00
static int ccid2_hc_tx_alloc_seq ( struct ccid2_hc_tx_sock * hctx )
2006-09-19 13:13:37 -07:00
{
struct ccid2_seq * seqp ;
int i ;
/* check if we have space to preserve the pointer to the buffer */
if ( hctx - > ccid2hctx_seqbufc > = ( sizeof ( hctx - > ccid2hctx_seqbuf ) /
sizeof ( struct ccid2_seq * ) ) )
return - ENOMEM ;
/* allocate buffer and initialize linked list */
2007-10-04 14:41:00 -07:00
seqp = kmalloc ( CCID2_SEQBUF_LEN * sizeof ( struct ccid2_seq ) , gfp_any ( ) ) ;
2006-09-19 13:13:37 -07:00
if ( seqp = = NULL )
return - ENOMEM ;
2007-10-04 14:41:00 -07:00
for ( i = 0 ; i < ( CCID2_SEQBUF_LEN - 1 ) ; i + + ) {
2006-09-19 13:13:37 -07:00
seqp [ i ] . ccid2s_next = & seqp [ i + 1 ] ;
seqp [ i + 1 ] . ccid2s_prev = & seqp [ i ] ;
}
2007-10-04 14:41:00 -07:00
seqp [ CCID2_SEQBUF_LEN - 1 ] . ccid2s_next = seqp ;
seqp - > ccid2s_prev = & seqp [ CCID2_SEQBUF_LEN - 1 ] ;
2006-09-19 13:13:37 -07:00
/* This is the first allocation. Initiate the head and tail. */
if ( hctx - > ccid2hctx_seqbufc = = 0 )
hctx - > ccid2hctx_seqh = hctx - > ccid2hctx_seqt = seqp ;
else {
/* link the existing list with the one we just created */
hctx - > ccid2hctx_seqh - > ccid2s_next = seqp ;
seqp - > ccid2s_prev = hctx - > ccid2hctx_seqh ;
2007-10-04 14:41:00 -07:00
hctx - > ccid2hctx_seqt - > ccid2s_prev = & seqp [ CCID2_SEQBUF_LEN - 1 ] ;
seqp [ CCID2_SEQBUF_LEN - 1 ] . ccid2s_next = hctx - > ccid2hctx_seqt ;
2006-09-19 13:13:37 -07:00
}
/* store the original pointer to the buffer so we can free it */
hctx - > ccid2hctx_seqbuf [ hctx - > ccid2hctx_seqbufc ] = seqp ;
hctx - > ccid2hctx_seqbufc + + ;
return 0 ;
}
2006-11-28 19:55:06 -02:00
static int ccid2_hc_tx_send_packet ( struct sock * sk , struct sk_buff * skb )
2006-03-20 17:41:47 -08:00
{
2007-10-04 14:42:19 -07:00
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
2006-03-20 17:41:47 -08:00
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-19 13:10:11 -07:00
return 1 ; /* XXX CCID should dequeue when ready instead of polling */
2006-03-20 17:41:47 -08:00
}
2007-11-24 21:32:53 -02:00
static void ccid2_change_l_ack_ratio ( struct sock * sk , u32 val )
2006-03-20 17:41:47 -08:00
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
2007-11-24 21:40:24 -02:00
u32 max_ratio = DIV_ROUND_UP ( ccid2_hc_tx_sk ( sk ) - > ccid2hctx_cwnd , 2 ) ;
2006-03-20 17:41:47 -08:00
/*
2007-11-24 21:40:24 -02:00
* Ensure that Ack Ratio does not exceed ceil ( cwnd / 2 ) , which is ( 2 ) from
* RFC 4341 , 6.1 .2 . We ignore the statement that Ack Ratio 2 is always
* acceptable since this causes starvation / deadlock whenever cwnd < 2.
* The same problem arises when Ack Ratio is 0 ( ie . Ack Ratio disabled ) .
2006-03-20 17:41:47 -08:00
*/
2007-11-24 21:40:24 -02:00
if ( val = = 0 | | val > max_ratio ) {
DCCP_WARN ( " Limiting Ack Ratio (%u) to %u \n " , val , max_ratio ) ;
val = max_ratio ;
2006-03-20 17:41:47 -08:00
}
2007-11-24 21:32:53 -02:00
if ( val > 0xFFFF ) /* RFC 4340, 11.3 */
val = 0xFFFF ;
2006-03-20 17:41:47 -08:00
2007-11-24 21:40:24 -02:00
if ( val = = dp - > dccps_l_ack_ratio )
return ;
2007-11-24 21:32:53 -02:00
ccid2_pr_debug ( " changing local ack ratio to %u \n " , val ) ;
2006-03-20 17:41:47 -08:00
dp - > dccps_l_ack_ratio = val ;
}
2007-10-04 14:41:55 -07:00
static void ccid2_change_cwnd ( struct ccid2_hc_tx_sock * hctx , u32 val )
2006-03-20 17:41:47 -08:00
{
2007-10-04 14:41:55 -07:00
hctx - > ccid2hctx_cwnd = val ? : 1 ;
ccid2_pr_debug ( " changed cwnd to %u \n " , hctx - > ccid2hctx_cwnd ) ;
2006-03-20 17:41:47 -08:00
}
2006-09-19 13:15:33 -07:00
static void ccid2_change_srtt ( struct ccid2_hc_tx_sock * hctx , long val )
{
ccid2_pr_debug ( " change SRTT to %ld \n " , val ) ;
hctx - > ccid2hctx_srtt = val ;
}
static void ccid2_change_pipe ( struct ccid2_hc_tx_sock * hctx , long val )
{
hctx - > ccid2hctx_pipe = val ;
}
2006-03-20 17:41:47 -08:00
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 */
2006-09-19 13:15:33 -07:00
ccid2_change_pipe ( hctx , 0 ) ;
2006-03-20 17:41:47 -08:00
hctx - > ccid2hctx_ssthresh = hctx - > ccid2hctx_cwnd > > 1 ;
if ( hctx - > ccid2hctx_ssthresh < 2 )
hctx - > ccid2hctx_ssthresh = 2 ;
2006-09-19 13:14:43 -07:00
ccid2_change_cwnd ( hctx , 1 ) ;
2006-03-20 17:41:47 -08:00
/* 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-20 17:57:52 -08:00
sock_put ( sk ) ;
2006-03-20 17:41:47 -08: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 ) ;
}
2006-11-28 19:55:06 -02:00
static void ccid2_hc_tx_packet_sent ( struct sock * sk , int more , unsigned int len )
2006-03-20 17:41:47 -08:00
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
2006-09-19 13:13:37 -07:00
struct ccid2_seq * next ;
2006-03-20 17:41:47 -08:00
u64 seq ;
ccid2_hc_tx_check_sanity ( hctx ) ;
BUG_ON ( ! hctx - > ccid2hctx_sendwait ) ;
hctx - > ccid2hctx_sendwait = 0 ;
2006-09-19 13:15:33 -07:00
ccid2_change_pipe ( hctx , hctx - > ccid2hctx_pipe + 1 ) ;
2006-03-20 17:41:47 -08:00
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 ;
2006-09-19 13:13:37 -07:00
next = hctx - > ccid2hctx_seqh - > ccid2s_next ;
/* check if we need to alloc more space */
if ( next = = hctx - > ccid2hctx_seqt ) {
2007-10-04 14:41:26 -07:00
if ( ccid2_hc_tx_alloc_seq ( hctx ) ) {
DCCP_CRIT ( " packet history - out of memory! " ) ;
/* FIXME: find a more graceful way to bail out */
return ;
}
2006-09-19 13:13:37 -07:00
next = hctx - > ccid2hctx_seqh - > ccid2s_next ;
BUG_ON ( next = = hctx - > ccid2hctx_seqt ) ;
2006-03-20 17:41:47 -08:00
}
2006-09-19 13:13:37 -07:00
hctx - > ccid2hctx_seqh = next ;
ccid2_pr_debug ( " cwnd=%d pipe=%d \n " , hctx - > ccid2hctx_cwnd ,
hctx - > ccid2hctx_pipe ) ;
2006-03-20 17:41:47 -08:00
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-20 22:05:37 -08:00
hctx - > ccid2hctx_arsent = 0 ;
hctx - > ccid2hctx_ackloss = 0 ;
2006-03-20 17:41:47 -08:00
}
2006-03-20 22:05:37 -08:00
} else {
/* No acks lost up to now... */
2006-03-20 17:41:47 -08: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-20 22:05:37 -08:00
int denom = dp - > dccps_l_ack_ratio * dp - > dccps_l_ack_ratio -
dp - > dccps_l_ack_ratio ;
2006-03-20 17:41:47 -08: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-20 22:05:37 -08:00
} else {
/* we can't increase ack ratio further [1] */
2006-03-20 17:41:47 -08:00
hctx - > ccid2hctx_arsent = 0 ; /* or maybe set it to cwnd*/
}
}
/* setup RTO timer */
2006-03-20 22:05:37 -08:00
if ( ! timer_pending ( & hctx - > ccid2hctx_rtotimer ) )
2006-03-20 17:41:47 -08:00
ccid2_start_rto_timer ( sk ) ;
2006-03-20 22:05:37 -08:00
2006-09-19 13:12:44 -07:00
# ifdef CONFIG_IP_DCCP_CCID2_DEBUG
2006-03-20 17:41:47 -08:00
ccid2_pr_debug ( " pipe=%d \n " , hctx - > ccid2hctx_pipe ) ;
2006-10-29 16:03:30 -08:00
ccid2_pr_debug ( " Sent: seq=%llu \n " , ( unsigned long long ) seq ) ;
2006-03-20 17:41:47 -08:00
do {
struct ccid2_seq * seqp = hctx - > ccid2hctx_seqt ;
while ( seqp ! = hctx - > ccid2hctx_seqh ) {
ccid2_pr_debug ( " out seq=%llu acked=%d time=%lu \n " ,
2006-12-10 16:01:18 -02:00
( unsigned long long ) seqp - > ccid2s_seq ,
2006-10-29 16:03:30 -08:00
seqp - > ccid2s_acked , seqp - > ccid2s_sent ) ;
2006-03-20 17:41:47 -08:00
seqp = seqp - > ccid2s_next ;
}
2006-03-20 22:05:37 -08:00
} while ( 0 ) ;
2006-03-20 17:41:47 -08: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 )
{
2007-02-09 23:24:38 +09:00
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 ;
2006-03-20 17:41:47 -08:00
BUG_ON ( offset < 0 ) ;
options + = offset ;
opt_ptr = options ;
if ( opt_ptr > = opt_end )
return - 1 ;
while ( opt_ptr ! = opt_end ) {
2007-02-09 23:24:38 +09:00
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 ;
}
2006-03-20 17:41:47 -08:00
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 :
2006-11-20 18:39:23 -02:00
DCCP_BUG ( " Invalid option - this should not happen (previous parsing)! " ) ;
2006-03-20 17:41:47 -08:00
return - 1 ;
}
2006-03-20 17:57:52 -08:00
static void ccid2_hc_tx_kill_rto_timer ( struct sock * sk )
2006-03-20 17:41:47 -08:00
{
2006-03-20 17:57:52 -08: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-20 17:41:47 -08:00
}
static inline void ccid2_new_ack ( struct sock * sk ,
2007-02-09 23:24:38 +09:00
struct ccid2_seq * seqp ,
2006-03-20 17:41:47 -08:00
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 ) {
2006-09-19 13:14:43 -07:00
ccid2_change_cwnd ( hctx , hctx - > ccid2hctx_cwnd + 1 ) ;
2006-03-20 17:41:47 -08:00
hctx - > ccid2hctx_ssacks = 0 ;
* maxincr = * maxincr - 1 ;
}
2006-03-20 22:05:37 -08:00
} else {
/* increased cwnd enough for this single ack */
2006-03-20 17:41:47 -08:00
hctx - > ccid2hctx_ssacks = 0 ;
}
2006-03-20 22:05:37 -08:00
} else {
2006-03-20 17:41:47 -08:00
hctx - > ccid2hctx_ssacks = 0 ;
hctx - > ccid2hctx_acks + + ;
if ( hctx - > ccid2hctx_acks > = hctx - > ccid2hctx_cwnd ) {
2006-09-19 13:14:43 -07:00
ccid2_change_cwnd ( hctx , hctx - > ccid2hctx_cwnd + 1 ) ;
2006-03-20 17:41:47 -08:00
hctx - > ccid2hctx_acks = 0 ;
}
}
/* update RTO */
if ( hctx - > ccid2hctx_srtt = = - 1 | |
2006-09-19 13:06:46 -07:00
time_after ( jiffies , hctx - > ccid2hctx_lastrtt + hctx - > ccid2hctx_srtt ) ) {
unsigned long r = ( long ) jiffies - ( long ) seqp - > ccid2s_sent ;
2006-03-20 17:41:47 -08:00
int s ;
/* first measurement */
if ( hctx - > ccid2hctx_srtt = = - 1 ) {
ccid2_pr_debug ( " R: %lu Time=%lu seq=%llu \n " ,
2006-12-10 16:01:18 -02:00
r , jiffies ,
2006-10-29 16:03:30 -08:00
( unsigned long long ) seqp - > ccid2s_seq ) ;
2006-09-19 13:15:33 -07:00
ccid2_change_srtt ( hctx , r ) ;
2006-03-20 17:41:47 -08:00
hctx - > ccid2hctx_rttvar = r > > 1 ;
2006-03-20 22:05:37 -08:00
} else {
2006-03-20 17:41:47 -08:00
/* RTTVAR */
long tmp = hctx - > ccid2hctx_srtt - r ;
2006-09-19 13:15:33 -07:00
long srtt ;
2006-03-20 17:41:47 -08:00
if ( tmp < 0 )
tmp * = - 1 ;
tmp > > = 2 ;
hctx - > ccid2hctx_rttvar * = 3 ;
hctx - > ccid2hctx_rttvar > > = 2 ;
hctx - > ccid2hctx_rttvar + = tmp ;
/* SRTT */
2006-09-19 13:15:33 -07:00
srtt = hctx - > ccid2hctx_srtt ;
srtt * = 7 ;
srtt > > = 3 ;
2006-03-20 17:41:47 -08:00
tmp = r > > 3 ;
2006-09-19 13:15:33 -07:00
srtt + = tmp ;
ccid2_change_srtt ( hctx , srtt ) ;
2006-03-20 17:41:47 -08:00
}
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 " ,
2006-12-10 16:01:18 -02:00
hctx - > ccid2hctx_srtt , hctx - > ccid2hctx_rttvar ,
hctx - > ccid2hctx_rto , HZ , r ) ;
2006-03-20 17:41:47 -08:00
hctx - > ccid2hctx_sent = 0 ;
}
/* we got a new ack, so re-start RTO timer */
2006-03-20 17:57:52 -08:00
ccid2_hc_tx_kill_rto_timer ( sk ) ;
2006-03-20 17:41:47 -08:00
ccid2_start_rto_timer ( sk ) ;
}
2006-03-20 17:57:52 -08:00
static void ccid2_hc_tx_dec_pipe ( struct sock * sk )
2006-03-20 17:41:47 -08:00
{
2006-03-20 17:57:52 -08:00
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
2006-09-19 13:15:33 -07:00
ccid2_change_pipe ( hctx , hctx - > ccid2hctx_pipe - 1 ) ;
2006-03-20 17:41:47 -08:00
BUG_ON ( hctx - > ccid2hctx_pipe < 0 ) ;
if ( hctx - > ccid2hctx_pipe = = 0 )
2006-03-20 17:57:52 -08:00
ccid2_hc_tx_kill_rto_timer ( sk ) ;
2006-03-20 17:41:47 -08:00
}
2007-11-24 21:40:24 -02:00
static void ccid2_congestion_event ( struct sock * sk , struct ccid2_seq * seqp )
2006-09-19 13:14:43 -07:00
{
2007-11-24 21:40:24 -02:00
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
2006-09-19 13:14:43 -07:00
if ( time_before ( seqp - > ccid2s_sent , hctx - > ccid2hctx_last_cong ) ) {
ccid2_pr_debug ( " Multiple losses in an RTT---treating as one \n " ) ;
return ;
}
hctx - > ccid2hctx_last_cong = jiffies ;
ccid2_change_cwnd ( hctx , hctx - > ccid2hctx_cwnd > > 1 ) ;
hctx - > ccid2hctx_ssthresh = hctx - > ccid2hctx_cwnd ;
if ( hctx - > ccid2hctx_ssthresh < 2 )
hctx - > ccid2hctx_ssthresh = 2 ;
2007-11-24 21:40:24 -02:00
/* Avoid spurious timeouts resulting from Ack Ratio > cwnd */
if ( dccp_sk ( sk ) - > dccps_l_ack_ratio > hctx - > ccid2hctx_cwnd )
ccid2_change_l_ack_ratio ( sk , hctx - > ccid2hctx_cwnd ) ;
2006-09-19 13:14:43 -07:00
}
2006-03-20 17:41:47 -08: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 ;
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-20 22:05:37 -08:00
} else {
2006-03-20 17:41:47 -08:00
/* check if packet is consecutive */
2007-10-04 14:43:09 -07:00
if ( dccp_delta_seqno ( hctx - > ccid2hctx_rpseq , seqno ) = = 1 )
hctx - > ccid2hctx_rpseq = seqno ;
2006-03-20 17:41:47 -08:00
/* 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 ;
2007-11-24 21:32:53 -02:00
ccid2_change_l_ack_ratio ( sk , 2 * dp - > dccps_l_ack_ratio ) ;
2006-03-20 17:41:47 -08:00
}
}
}
/* 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 ;
2006-11-16 14:28:40 -02:00
if ( after48 ( ackno , hctx - > ccid2hctx_high_ack ) )
hctx - > ccid2hctx_high_ack = ackno ;
seqp = hctx - > ccid2hctx_seqt ;
while ( before48 ( seqp - > ccid2s_seq , ackno ) ) {
seqp = seqp - > ccid2s_next ;
if ( seqp = = hctx - > ccid2hctx_seqh ) {
seqp = hctx - > ccid2hctx_seqh - > ccid2s_prev ;
break ;
}
}
2006-03-20 17:41:47 -08:00
/* 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 ;
2007-11-24 20:43:59 -02:00
u64 ackno_end_rl = SUB48 ( ackno , rl ) ;
2006-03-20 17:41:47 -08:00
2006-10-29 16:03:30 -08:00
ccid2_pr_debug ( " ackvec start:%llu end:%llu \n " ,
( unsigned long long ) ackno ,
( unsigned long long ) ackno_end_rl ) ;
2006-03-20 17:41:47 -08:00
/* 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-19 13:05:35 -07:00
const u8 state = * vector &
DCCP_ACKVEC_STATE_MASK ;
2006-03-20 17:41:47 -08:00
/* new packet received or marked */
if ( state ! = DCCP_ACKVEC_STATE_NOT_RECEIVED & &
! seqp - > ccid2s_acked ) {
2006-12-10 16:01:18 -02:00
if ( state = =
2006-03-20 17:41:47 -08:00
DCCP_ACKVEC_STATE_ECN_MARKED ) {
2007-11-24 21:40:24 -02:00
ccid2_congestion_event ( sk ,
2006-09-19 13:14:43 -07:00
seqp ) ;
2006-03-20 22:05:37 -08:00
} else
2006-03-20 17:41:47 -08:00
ccid2_new_ack ( sk , seqp ,
& maxincr ) ;
seqp - > ccid2s_acked = 1 ;
ccid2_pr_debug ( " Got ack for %llu \n " ,
2006-10-29 16:03:30 -08:00
( unsigned long long ) seqp - > ccid2s_seq ) ;
2006-03-20 17:57:52 -08:00
ccid2_hc_tx_dec_pipe ( sk ) ;
2006-03-20 17:41:47 -08:00
}
if ( seqp = = hctx - > ccid2hctx_seqt ) {
done = 1 ;
break ;
}
2007-11-24 20:37:48 -02:00
seqp = seqp - > ccid2s_prev ;
2006-03-20 17:41:47 -08:00
}
if ( done )
break ;
2007-11-24 20:43:59 -02:00
ackno = SUB48 ( ackno_end_rl , 1 ) ;
2006-03-20 17:41:47 -08:00
vector + + ;
}
if ( done )
break ;
}
/* The state about what is acked should be correct now
* Check for NUMDUPACK
*/
2006-11-16 14:28:40 -02:00
seqp = hctx - > ccid2hctx_seqt ;
while ( before48 ( seqp - > ccid2s_seq , hctx - > ccid2hctx_high_ack ) ) {
seqp = seqp - > ccid2s_next ;
if ( seqp = = hctx - > ccid2hctx_seqh ) {
seqp = hctx - > ccid2hctx_seqh - > ccid2s_prev ;
break ;
}
}
2006-03-20 17:41:47 -08:00
done = 0 ;
while ( 1 ) {
if ( seqp - > ccid2s_acked ) {
done + + ;
2006-03-20 22:05:37 -08:00
if ( done = = hctx - > ccid2hctx_numdupack )
2006-03-20 17:41:47 -08:00
break ;
}
2006-03-20 22:05:37 -08:00
if ( seqp = = hctx - > ccid2hctx_seqt )
2006-03-20 17:41:47 -08: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 ) {
2006-09-19 13:14:43 -07:00
ccid2_pr_debug ( " Packet lost: %llu \n " ,
2006-10-29 16:03:30 -08:00
( unsigned long long ) seqp - > ccid2s_seq ) ;
2006-09-19 13:14:43 -07:00
/* XXX need to traverse from tail -> head in
* order to detect multiple congestion events in
* one ack vector .
*/
2007-11-24 21:40:24 -02:00
ccid2_congestion_event ( sk , seqp ) ;
2006-03-20 17:57:52 -08:00
ccid2_hc_tx_dec_pipe ( sk ) ;
2006-03-20 17:41:47 -08: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 ;
}
ccid2_hc_tx_check_sanity ( hctx ) ;
}
2006-03-20 19:21:44 -08:00
static int ccid2_hc_tx_init ( struct ccid * ccid , struct sock * sk )
2006-03-20 17:41:47 -08:00
{
2007-02-09 23:24:38 +09:00
struct ccid2_hc_tx_sock * hctx = ccid_priv ( ccid ) ;
2007-11-24 21:44:30 -02:00
struct dccp_sock * dp = dccp_sk ( sk ) ;
u32 max_ratio ;
2006-03-20 17:41:47 -08:00
2007-11-24 21:44:30 -02:00
/* RFC 4341, 5: initialise ssthresh to arbitrarily high (max) value */
2006-09-19 13:07:20 -07:00
hctx - > ccid2hctx_ssthresh = ~ 0 ;
2006-03-20 17:41:47 -08:00
hctx - > ccid2hctx_numdupack = 3 ;
2007-11-24 21:44:30 -02:00
/*
* RFC 4341 , 5 : " The cwnd parameter is initialized to at most four
* packets for new connections , following the rules from [ RFC3390 ] " .
* We need to convert the bytes of RFC3390 into the packets of RFC 4341.
*/
hctx - > ccid2hctx_cwnd = min ( 4U , max ( 2U , 4380U / dp - > dccps_mss_cache ) ) ;
/* Make sure that Ack Ratio is enabled and within bounds. */
max_ratio = DIV_ROUND_UP ( hctx - > ccid2hctx_cwnd , 2 ) ;
if ( dp - > dccps_l_ack_ratio = = 0 | | dp - > dccps_l_ack_ratio > max_ratio )
dp - > dccps_l_ack_ratio = max_ratio ;
2006-03-20 17:41:47 -08:00
/* XXX init ~ to window size... */
2007-10-04 14:41:00 -07:00
if ( ccid2_hc_tx_alloc_seq ( hctx ) )
2006-03-20 17:41:47 -08:00
return - ENOMEM ;
2006-03-20 19:21:44 -08:00
2006-03-20 17:41:47 -08:00
hctx - > ccid2hctx_rto = 3 * HZ ;
2006-09-19 13:15:33 -07:00
ccid2_change_srtt ( hctx , - 1 ) ;
2006-03-20 17:41:47 -08:00
hctx - > ccid2hctx_rttvar = - 1 ;
hctx - > ccid2hctx_rpdupack = - 1 ;
2006-09-19 13:14:43 -07:00
hctx - > ccid2hctx_last_cong = jiffies ;
2008-01-23 21:20:07 -08:00
setup_timer ( & hctx - > ccid2hctx_rtotimer , ccid2_hc_tx_rto_expire ,
( unsigned long ) sk ) ;
2006-03-20 17:41:47 -08:00
ccid2_hc_tx_check_sanity ( hctx ) ;
return 0 ;
}
static void ccid2_hc_tx_exit ( struct sock * sk )
{
2007-02-09 23:24:38 +09:00
struct ccid2_hc_tx_sock * hctx = ccid2_hc_tx_sk ( sk ) ;
2006-09-19 13:13:37 -07:00
int i ;
2006-03-20 17:41:47 -08:00
2006-03-20 17:57:52 -08:00
ccid2_hc_tx_kill_rto_timer ( sk ) ;
2006-09-19 13:13:37 -07:00
for ( i = 0 ; i < hctx - > ccid2hctx_seqbufc ; i + + )
kfree ( hctx - > ccid2hctx_seqbuf [ i ] ) ;
hctx - > ccid2hctx_seqbufc = 0 ;
2006-03-20 17:41:47 -08: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-20 19:21:44 -08:00
static struct ccid_operations ccid2 = {
2006-09-22 14:26:44 +12:00
. ccid_id = DCCPC_CCID2 ,
2006-03-20 17:41:47 -08:00
. ccid_name = " ccid2 " ,
. ccid_owner = THIS_MODULE ,
2006-03-20 19:21:44 -08:00
. ccid_hc_tx_obj_size = sizeof ( struct ccid2_hc_tx_sock ) ,
2006-03-20 17:41:47 -08: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-20 19:21:44 -08:00
. ccid_hc_rx_obj_size = sizeof ( struct ccid2_hc_rx_sock ) ,
2006-03-20 17:41:47 -08:00
. ccid_hc_rx_packet_recv = ccid2_hc_rx_packet_recv ,
} ;
2006-11-20 18:26:03 -02:00
# ifdef CONFIG_IP_DCCP_CCID2_DEBUG
2007-10-04 14:39:53 -07:00
module_param ( ccid2_debug , bool , 0444 ) ;
2006-03-20 17:41:47 -08:00
MODULE_PARM_DESC ( ccid2_debug , " Enable debug messages " ) ;
2006-11-20 18:26:03 -02:00
# endif
2006-03-20 17:41:47 -08:00
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-20 22:05:37 -08:00
MODULE_DESCRIPTION ( " DCCP TCP-Like (CCID2) CCID " ) ;
2006-03-20 17:41:47 -08:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " net-dccp-ccid-2 " ) ;