2005-08-09 20:14:34 -07:00
/*
* net / dccp / ccids / ccid3 . c
*
* Copyright ( c ) 2005 The University of Waikato , Hamilton , New Zealand .
2005-08-20 00:23:43 -03:00
* Copyright ( c ) 2005 Ian McDonald < iam4 @ cs . waikato . ac . nz >
2005-08-09 20:14:34 -07:00
*
* An implementation of the DCCP protocol
*
* This code has been developed by the University of Waikato WAND
* research group . For further information please see http : //www.wand.net.nz/
*
* This code also uses code from Lulea University , rereleased as GPL by its
* authors :
* Copyright ( c ) 2003 Nils - Erik Mattsson , Joacim Haggmark , Magnus Erixzon
*
* Changes to meet Linux coding standards , to make it meet latest ccid3 draft
* and to make it work as a loadable module in the DCCP stack written by
* Arnaldo Carvalho de Melo < acme @ conectiva . com . br > .
*
* Copyright ( c ) 2005 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 .
*/
2005-08-10 12:59:38 -03:00
# include <linux/config.h>
2005-08-09 20:14:34 -07:00
# include "../ccid.h"
# include "../dccp.h"
2005-08-27 23:18:26 -03:00
# include "lib/packet_history.h"
2005-08-27 23:03:09 -03:00
# include "lib/loss_interval.h"
2005-08-28 00:47:15 -03:00
# include "lib/tfrc.h"
2005-08-09 20:14:34 -07:00
# include "ccid3.h"
2005-08-27 20:11:28 -03:00
/*
2005-09-09 02:28:47 -03:00
* Reason for maths here is to avoid 32 bit overflow when a is big .
* With this we get close to the limit .
2005-08-27 20:11:28 -03:00
*/
static inline u32 usecs_div ( const u32 a , const u32 b )
{
2005-09-09 02:28:47 -03:00
const u32 div = a < ( UINT_MAX / ( USEC_PER_SEC / 10 ) ) ? 10 :
a < ( UINT_MAX / ( USEC_PER_SEC / 50 ) ) ? 50 :
a < ( UINT_MAX / ( USEC_PER_SEC / 100 ) ) ? 100 :
a < ( UINT_MAX / ( USEC_PER_SEC / 500 ) ) ? 500 :
a < ( UINT_MAX / ( USEC_PER_SEC / 1000 ) ) ? 1000 :
a < ( UINT_MAX / ( USEC_PER_SEC / 5000 ) ) ? 5000 :
a < ( UINT_MAX / ( USEC_PER_SEC / 10000 ) ) ? 10000 :
a < ( UINT_MAX / ( USEC_PER_SEC / 50000 ) ) ? 50000 :
100000 ;
const u32 tmp = a * ( USEC_PER_SEC / div ) ;
return ( b > = 2 * div ) ? tmp / ( b / div ) : tmp ;
2005-08-27 20:11:28 -03:00
}
2005-08-27 20:20:37 -03:00
static int ccid3_debug ;
2005-08-09 20:14:34 -07:00
2005-08-27 20:20:37 -03:00
# ifdef CCID3_DEBUG
2005-08-09 20:14:34 -07:00
# define ccid3_pr_debug(format, a...) \
do { if ( ccid3_debug ) \
printk ( KERN_DEBUG " %s: " format , __FUNCTION__ , # # a ) ; \
} while ( 0 )
# else
# define ccid3_pr_debug(format, a...)
# endif
2005-08-13 22:42:25 -03:00
static struct dccp_tx_hist * ccid3_tx_hist ;
static struct dccp_rx_hist * ccid3_rx_hist ;
2005-08-27 23:03:09 -03:00
static struct dccp_li_hist * ccid3_li_hist ;
2005-08-09 20:14:34 -07:00
static int ccid3_init ( struct sock * sk )
{
ccid3_pr_debug ( " %s, sk=%p \n " , dccp_role ( sk ) , sk ) ;
return 0 ;
}
static void ccid3_exit ( struct sock * sk )
{
ccid3_pr_debug ( " %s, sk=%p \n " , dccp_role ( sk ) , sk ) ;
}
/* TFRC sender states */
enum ccid3_hc_tx_states {
TFRC_SSTATE_NO_SENT = 1 ,
TFRC_SSTATE_NO_FBACK ,
TFRC_SSTATE_FBACK ,
TFRC_SSTATE_TERM ,
} ;
# ifdef CCID3_DEBUG
static const char * ccid3_tx_state_name ( enum ccid3_hc_tx_states state )
{
static char * ccid3_state_names [ ] = {
[ TFRC_SSTATE_NO_SENT ] = " NO_SENT " ,
[ TFRC_SSTATE_NO_FBACK ] = " NO_FBACK " ,
[ TFRC_SSTATE_FBACK ] = " FBACK " ,
[ TFRC_SSTATE_TERM ] = " TERM " ,
} ;
return ccid3_state_names [ state ] ;
}
# endif
2005-08-27 03:51:58 -03:00
static inline void ccid3_hc_tx_set_state ( struct sock * sk ,
enum ccid3_hc_tx_states state )
2005-08-09 20:14:34 -07:00
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx = dp - > dccps_hc_tx_ccid_private ;
enum ccid3_hc_tx_states oldstate = hctx - > ccid3hctx_state ;
ccid3_pr_debug ( " %s(%p) %-8.8s -> %s \n " ,
2005-08-27 03:51:58 -03:00
dccp_role ( sk ) , sk , ccid3_tx_state_name ( oldstate ) ,
ccid3_tx_state_name ( state ) ) ;
2005-08-09 20:14:34 -07:00
WARN_ON ( state = = oldstate ) ;
hctx - > ccid3hctx_state = state ;
}
/* Calculate new t_ipi (inter packet interval) by t_ipi = s / X_inst */
static inline void ccid3_calc_new_t_ipi ( struct ccid3_hc_tx_sock * hctx )
{
2005-08-27 20:11:28 -03:00
/*
* If no feedback spec says t_ipi is 1 second ( set elsewhere and then
* doubles after every no feedback timer ( separate function )
*/
if ( hctx - > ccid3hctx_state ! = TFRC_SSTATE_NO_FBACK )
hctx - > ccid3hctx_t_ipi = usecs_div ( hctx - > ccid3hctx_s ,
hctx - > ccid3hctx_x ) ;
2005-08-09 20:14:34 -07:00
}
/* Calculate new delta by delta = min(t_ipi / 2, t_gran / 2) */
static inline void ccid3_calc_new_delta ( struct ccid3_hc_tx_sock * hctx )
{
2005-08-27 03:51:58 -03:00
hctx - > ccid3hctx_delta = min_t ( u32 , hctx - > ccid3hctx_t_ipi / 2 ,
TFRC_OPSYS_HALF_TIME_GRAN ) ;
2005-08-09 20:14:34 -07:00
}
/*
* Update X by
* If ( p > 0 )
* x_calc = calcX ( s , R , p ) ;
* X = max ( min ( X_calc , 2 * X_recv ) , s / t_mbi ) ;
* Else
* If ( now - tld > = R )
* X = max ( min ( 2 * X , 2 * X_recv ) , s / R ) ;
* tld = now ;
*/
static void ccid3_hc_tx_update_x ( struct sock * sk )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx = dp - > dccps_hc_tx_ccid_private ;
2005-08-27 03:51:58 -03:00
/* To avoid large error in calcX */
if ( hctx - > ccid3hctx_p > = TFRC_SMALLEST_P ) {
2005-08-28 00:47:15 -03:00
hctx - > ccid3hctx_x_calc = tfrc_calc_x ( hctx - > ccid3hctx_s ,
hctx - > ccid3hctx_rtt ,
hctx - > ccid3hctx_p ) ;
2005-08-27 03:51:58 -03:00
hctx - > ccid3hctx_x = max_t ( u32 , min_t ( u32 , hctx - > ccid3hctx_x_calc ,
2 * hctx - > ccid3hctx_x_recv ) ,
( hctx - > ccid3hctx_s /
TFRC_MAX_BACK_OFF_TIME ) ) ;
2005-08-27 18:18:18 -03:00
} else {
struct timeval now ;
do_gettimeofday ( & now ) ;
if ( timeval_delta ( & now , & hctx - > ccid3hctx_t_ld ) > =
hctx - > ccid3hctx_rtt ) {
2005-08-27 20:11:28 -03:00
hctx - > ccid3hctx_x = max_t ( u32 , min_t ( u32 , hctx - > ccid3hctx_x_recv ,
hctx - > ccid3hctx_x ) * 2 ,
usecs_div ( hctx - > ccid3hctx_s ,
hctx - > ccid3hctx_rtt ) ) ;
2005-08-27 18:18:18 -03:00
hctx - > ccid3hctx_t_ld = now ;
}
2005-08-09 20:14:34 -07:00
}
}
static void ccid3_hc_tx_no_feedback_timer ( unsigned long data )
{
struct sock * sk = ( struct sock * ) data ;
struct dccp_sock * dp = dccp_sk ( sk ) ;
unsigned long next_tmout = 0 ;
struct ccid3_hc_tx_sock * hctx = dp - > dccps_hc_tx_ccid_private ;
bh_lock_sock ( sk ) ;
if ( sock_owned_by_user ( sk ) ) {
/* Try again later. */
/* XXX: set some sensible MIB */
2005-08-27 03:51:58 -03:00
sk_reset_timer ( sk , & hctx - > ccid3hctx_no_feedback_timer ,
jiffies + HZ / 5 ) ;
2005-08-09 20:14:34 -07:00
goto out ;
}
ccid3_pr_debug ( " %s, sk=%p, state=%s \n " , dccp_role ( sk ) , sk ,
ccid3_tx_state_name ( hctx - > ccid3hctx_state ) ) ;
switch ( hctx - > ccid3hctx_state ) {
case TFRC_SSTATE_TERM :
goto out ;
case TFRC_SSTATE_NO_FBACK :
/* Halve send rate */
hctx - > ccid3hctx_x / = 2 ;
2005-08-27 20:11:28 -03:00
if ( hctx - > ccid3hctx_x < ( hctx - > ccid3hctx_s /
TFRC_MAX_BACK_OFF_TIME ) )
2005-08-27 03:51:58 -03:00
hctx - > ccid3hctx_x = ( hctx - > ccid3hctx_s /
TFRC_MAX_BACK_OFF_TIME ) ;
2005-08-09 20:14:34 -07:00
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, state=%s, updated tx rate to %d "
" bytes/s \n " ,
dccp_role ( sk ) , sk ,
ccid3_tx_state_name ( hctx - > ccid3hctx_state ) ,
2005-08-09 20:14:34 -07:00
hctx - > ccid3hctx_x ) ;
2005-08-27 20:11:28 -03:00
next_tmout = max_t ( u32 , 2 * usecs_div ( hctx - > ccid3hctx_s ,
hctx - > ccid3hctx_x ) ,
2005-08-27 03:51:58 -03:00
TFRC_INITIAL_TIMEOUT ) ;
/*
* FIXME - not sure above calculation is correct . See section
* 5 of CCID3 11 should adjust tx_t_ipi and double that to
* achieve it really
*/
2005-08-09 20:14:34 -07:00
break ;
case TFRC_SSTATE_FBACK :
2005-08-27 03:51:58 -03:00
/*
* Check if IDLE since last timeout and recv rate is less than
* 4 packets per RTT
*/
if ( ! hctx - > ccid3hctx_idle | |
2005-08-27 20:11:28 -03:00
( hctx - > ccid3hctx_x_recv > =
4 * usecs_div ( hctx - > ccid3hctx_s , hctx - > ccid3hctx_rtt ) ) ) {
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, state=%s, not idle \n " ,
dccp_role ( sk ) , sk ,
2005-08-09 20:14:34 -07:00
ccid3_tx_state_name ( hctx - > ccid3hctx_state ) ) ;
/* Halve sending rate */
/* If (X_calc > 2 * X_recv)
* X_recv = max ( X_recv / 2 , s / ( 2 * t_mbi ) ) ;
* Else
* X_recv = X_calc / 4 ;
*/
2005-08-27 03:51:58 -03:00
BUG_ON ( hctx - > ccid3hctx_p > = TFRC_SMALLEST_P & &
hctx - > ccid3hctx_x_calc = = 0 ) ;
2005-08-09 20:14:34 -07:00
/* check also if p is zero -> x_calc is infinity? */
if ( hctx - > ccid3hctx_p < TFRC_SMALLEST_P | |
hctx - > ccid3hctx_x_calc > 2 * hctx - > ccid3hctx_x_recv )
hctx - > ccid3hctx_x_recv = max_t ( u32 , hctx - > ccid3hctx_x_recv / 2 ,
hctx - > ccid3hctx_s / ( 2 * TFRC_MAX_BACK_OFF_TIME ) ) ;
else
hctx - > ccid3hctx_x_recv = hctx - > ccid3hctx_x_calc / 4 ;
/* Update sending rate */
ccid3_hc_tx_update_x ( sk ) ;
}
2005-08-27 20:11:28 -03:00
/*
* Schedule no feedback timer to expire in
* max ( 4 * R , 2 * s / X )
*/
2005-08-21 05:07:37 -03:00
next_tmout = max_t ( u32 , hctx - > ccid3hctx_t_rto ,
2005-08-27 20:11:28 -03:00
2 * usecs_div ( hctx - > ccid3hctx_s ,
hctx - > ccid3hctx_x ) ) ;
2005-08-09 20:14:34 -07:00
break ;
default :
printk ( KERN_CRIT " %s: %s, sk=%p, Illegal state (%d)! \n " ,
__FUNCTION__ , dccp_role ( sk ) , sk , hctx - > ccid3hctx_state ) ;
dump_stack ( ) ;
goto out ;
}
sk_reset_timer ( sk , & hctx - > ccid3hctx_no_feedback_timer ,
2005-08-27 03:51:58 -03:00
jiffies + max_t ( u32 , 1 , usecs_to_jiffies ( next_tmout ) ) ) ;
2005-08-09 20:14:34 -07:00
hctx - > ccid3hctx_idle = 1 ;
out :
bh_unlock_sock ( sk ) ;
sock_put ( sk ) ;
}
2005-08-09 20:30:56 -07:00
static int ccid3_hc_tx_send_packet ( struct sock * sk ,
struct sk_buff * skb , int len )
2005-08-09 20:14:34 -07:00
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx = dp - > dccps_hc_tx_ccid_private ;
2005-08-10 12:59:38 -03:00
struct dccp_tx_hist_entry * new_packet ;
2005-08-09 20:14:34 -07:00
struct timeval now ;
2005-08-09 20:30:56 -07:00
long delay ;
2005-08-09 20:14:34 -07:00
int rc = - ENOTCONN ;
2005-08-27 03:51:58 -03:00
/* Check if pure ACK or Terminating*/
2005-08-09 20:14:34 -07:00
/*
2005-08-27 03:51:58 -03:00
* XXX : We only call this function for DATA and DATAACK , on , these
* packets can have zero length , but why the comment about " pure ACK " ?
2005-08-09 20:14:34 -07:00
*/
2005-08-27 03:51:58 -03:00
if ( hctx = = NULL | | len = = 0 | |
hctx - > ccid3hctx_state = = TFRC_SSTATE_TERM )
2005-08-09 20:14:34 -07:00
goto out ;
/* See if last packet allocated was not sent */
2005-08-10 12:59:38 -03:00
new_packet = dccp_tx_hist_head ( & hctx - > ccid3hctx_hist ) ;
if ( new_packet = = NULL | | new_packet - > dccphtx_sent ) {
2005-08-27 03:51:58 -03:00
new_packet = dccp_tx_hist_entry_new ( ccid3_tx_hist ,
SLAB_ATOMIC ) ;
2005-08-09 20:14:34 -07:00
rc = - ENOBUFS ;
if ( new_packet = = NULL ) {
ccid3_pr_debug ( " %s, sk=%p, not enough mem to add "
2005-08-27 03:51:58 -03:00
" to history, send refused \n " ,
dccp_role ( sk ) , sk ) ;
2005-08-09 20:14:34 -07:00
goto out ;
}
2005-08-10 12:59:38 -03:00
dccp_tx_hist_add_entry ( & hctx - > ccid3hctx_hist , new_packet ) ;
2005-08-09 20:14:34 -07:00
}
do_gettimeofday ( & now ) ;
switch ( hctx - > ccid3hctx_state ) {
case TFRC_SSTATE_NO_SENT :
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, first packet(%llu) \n " ,
dccp_role ( sk ) , sk , dp - > dccps_gss ) ;
2005-08-09 20:14:34 -07:00
hctx - > ccid3hctx_no_feedback_timer . function = ccid3_hc_tx_no_feedback_timer ;
hctx - > ccid3hctx_no_feedback_timer . data = ( unsigned long ) sk ;
2005-08-27 03:51:58 -03:00
sk_reset_timer ( sk , & hctx - > ccid3hctx_no_feedback_timer ,
jiffies + usecs_to_jiffies ( TFRC_INITIAL_TIMEOUT ) ) ;
2005-08-09 20:14:34 -07:00
hctx - > ccid3hctx_last_win_count = 0 ;
hctx - > ccid3hctx_t_last_win_count = now ;
ccid3_hc_tx_set_state ( sk , TFRC_SSTATE_NO_FBACK ) ;
hctx - > ccid3hctx_t_ipi = TFRC_INITIAL_TIMEOUT ;
/* Set nominal send time for initial packet */
hctx - > ccid3hctx_t_nom = now ;
2005-08-27 18:18:18 -03:00
timeval_add_usecs ( & hctx - > ccid3hctx_t_nom ,
hctx - > ccid3hctx_t_ipi ) ;
2005-08-09 20:14:34 -07:00
ccid3_calc_new_delta ( hctx ) ;
rc = 0 ;
break ;
case TFRC_SSTATE_NO_FBACK :
case TFRC_SSTATE_FBACK :
2005-08-27 18:18:18 -03:00
delay = ( timeval_delta ( & now , & hctx - > ccid3hctx_t_nom ) -
hctx - > ccid3hctx_delta ) ;
2005-08-09 20:30:56 -07:00
ccid3_pr_debug ( " send_packet delay=%ld \n " , delay ) ;
delay / = - 1000 ;
2005-08-09 20:14:34 -07:00
/* divide by -1000 is to convert to ms and get sign right */
2005-08-27 03:06:35 -03:00
rc = delay > 0 ? delay : 0 ;
2005-08-09 20:14:34 -07:00
break ;
default :
printk ( KERN_CRIT " %s: %s, sk=%p, Illegal state (%d)! \n " ,
__FUNCTION__ , dccp_role ( sk ) , sk , hctx - > ccid3hctx_state ) ;
dump_stack ( ) ;
rc = - EINVAL ;
break ;
}
/* Can we send? if so add options and add to packet history */
2005-09-09 02:30:07 -03:00
if ( rc = = 0 ) {
dp - > dccps_hc_tx_insert_options = 1 ;
2005-08-13 20:34:23 -03:00
new_packet - > dccphtx_ccval =
2005-08-10 12:59:38 -03:00
DCCP_SKB_CB ( skb ) - > dccpd_ccval =
hctx - > ccid3hctx_last_win_count ;
2005-09-09 02:30:07 -03:00
}
2005-08-09 20:14:34 -07:00
out :
return rc ;
}
static void ccid3_hc_tx_packet_sent ( struct sock * sk , int more , int len )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx = dp - > dccps_hc_tx_ccid_private ;
struct timeval now ;
BUG_ON ( hctx = = NULL ) ;
if ( hctx - > ccid3hctx_state = = TFRC_SSTATE_TERM ) {
ccid3_pr_debug ( " %s, sk=%p, while state is TFRC_SSTATE_TERM! \n " ,
dccp_role ( sk ) , sk ) ;
return ;
}
do_gettimeofday ( & now ) ;
/* check if we have sent a data packet */
if ( len > 0 ) {
unsigned long quarter_rtt ;
2005-08-10 12:59:38 -03:00
struct dccp_tx_hist_entry * packet ;
2005-08-09 20:14:34 -07:00
2005-08-10 12:59:38 -03:00
packet = dccp_tx_hist_head ( & hctx - > ccid3hctx_hist ) ;
if ( packet = = NULL ) {
2005-08-27 03:51:58 -03:00
printk ( KERN_CRIT " %s: packet doesn't exists in "
" history! \n " , __FUNCTION__ ) ;
2005-08-09 20:14:34 -07:00
return ;
}
2005-08-10 12:59:38 -03:00
if ( packet - > dccphtx_sent ) {
2005-08-27 03:51:58 -03:00
printk ( KERN_CRIT " %s: no unsent packet in history! \n " ,
__FUNCTION__ ) ;
2005-08-09 20:14:34 -07:00
return ;
}
2005-08-10 12:59:38 -03:00
packet - > dccphtx_tstamp = now ;
packet - > dccphtx_seqno = dp - > dccps_gss ;
2005-08-09 20:14:34 -07:00
/*
2005-08-27 03:51:58 -03:00
* Check if win_count have changed
* Algorithm in " 8.1. Window Counter Valuer " in
* draft - ietf - dccp - ccid3 - 11. txt
2005-08-09 20:14:34 -07:00
*/
2005-08-27 20:11:28 -03:00
quarter_rtt = timeval_delta ( & now , & hctx - > ccid3hctx_t_last_win_count ) ;
if ( likely ( hctx - > ccid3hctx_rtt > 8 ) )
quarter_rtt / = hctx - > ccid3hctx_rtt / 4 ;
2005-08-09 20:14:34 -07:00
if ( quarter_rtt > 0 ) {
hctx - > ccid3hctx_t_last_win_count = now ;
hctx - > ccid3hctx_last_win_count = ( hctx - > ccid3hctx_last_win_count +
min_t ( unsigned long , quarter_rtt , 5 ) ) % 16 ;
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, window changed from "
" %u to %u! \n " ,
2005-08-09 20:14:34 -07:00
dccp_role ( sk ) , sk ,
2005-08-13 20:34:23 -03:00
packet - > dccphtx_ccval ,
2005-08-09 20:14:34 -07:00
hctx - > ccid3hctx_last_win_count ) ;
}
2005-08-27 03:51:58 -03:00
2005-08-09 20:14:34 -07:00
hctx - > ccid3hctx_idle = 0 ;
2005-08-13 20:34:23 -03:00
packet - > dccphtx_rtt = hctx - > ccid3hctx_rtt ;
2005-08-10 12:59:38 -03:00
packet - > dccphtx_sent = 1 ;
2005-08-09 20:14:34 -07:00
} else
ccid3_pr_debug ( " %s, sk=%p, seqno=%llu NOT inserted! \n " ,
dccp_role ( sk ) , sk , dp - > dccps_gss ) ;
switch ( hctx - > ccid3hctx_state ) {
case TFRC_SSTATE_NO_SENT :
/* if first wasn't pure ack */
if ( len ! = 0 )
2005-08-27 03:51:58 -03:00
printk ( KERN_CRIT " %s: %s, First packet sent is noted "
" as a data packet \n " ,
2005-08-09 20:14:34 -07:00
__FUNCTION__ , dccp_role ( sk ) ) ;
return ;
case TFRC_SSTATE_NO_FBACK :
case TFRC_SSTATE_FBACK :
if ( len > 0 ) {
hctx - > ccid3hctx_t_nom = now ;
ccid3_calc_new_t_ipi ( hctx ) ;
ccid3_calc_new_delta ( hctx ) ;
2005-08-27 18:18:18 -03:00
timeval_add_usecs ( & hctx - > ccid3hctx_t_nom ,
hctx - > ccid3hctx_t_ipi ) ;
2005-08-09 20:14:34 -07:00
}
break ;
default :
printk ( KERN_CRIT " %s: %s, sk=%p, Illegal state (%d)! \n " ,
__FUNCTION__ , dccp_role ( sk ) , sk , hctx - > ccid3hctx_state ) ;
dump_stack ( ) ;
break ;
}
}
static void ccid3_hc_tx_packet_recv ( struct sock * sk , struct sk_buff * skb )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx = dp - > dccps_hc_tx_ccid_private ;
struct ccid3_options_received * opt_recv ;
2005-08-10 12:59:38 -03:00
struct dccp_tx_hist_entry * packet ;
2005-08-09 20:14:34 -07:00
unsigned long next_tmout ;
2005-08-20 00:23:43 -03:00
u32 t_elapsed ;
2005-08-09 20:14:34 -07:00
u32 pinv ;
u32 x_recv ;
u32 r_sample ;
2005-08-27 03:51:58 -03:00
2005-08-09 20:14:34 -07:00
if ( hctx = = NULL )
return ;
if ( hctx - > ccid3hctx_state = = TFRC_SSTATE_TERM ) {
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, received a packet when "
" terminating! \n " , dccp_role ( sk ) , sk ) ;
2005-08-09 20:14:34 -07:00
return ;
}
/* we are only interested in ACKs */
if ( ! ( DCCP_SKB_CB ( skb ) - > dccpd_type = = DCCP_PKT_ACK | |
DCCP_SKB_CB ( skb ) - > dccpd_type = = DCCP_PKT_DATAACK ) )
return ;
opt_recv = & hctx - > ccid3hctx_options_received ;
2005-09-09 02:32:56 -03:00
t_elapsed = dp - > dccps_options_received . dccpor_elapsed_time * 10 ;
2005-08-09 20:14:34 -07:00
x_recv = opt_recv - > ccid3or_receive_rate ;
pinv = opt_recv - > ccid3or_loss_event_rate ;
switch ( hctx - > ccid3hctx_state ) {
case TFRC_SSTATE_NO_SENT :
/* FIXME: what to do here? */
return ;
case TFRC_SSTATE_NO_FBACK :
case TFRC_SSTATE_FBACK :
/* Calculate new round trip sample by
* R_sample = ( now - t_recvdata ) - t_delay */
/* get t_recvdata from history */
2005-08-10 12:59:38 -03:00
packet = dccp_tx_hist_find_entry ( & hctx - > ccid3hctx_hist ,
DCCP_SKB_CB ( skb ) - > dccpd_ack_seq ) ;
2005-08-09 20:14:34 -07:00
if ( packet = = NULL ) {
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, seqno %llu(%s) does't "
" exist in history! \n " ,
dccp_role ( sk ) , sk ,
DCCP_SKB_CB ( skb ) - > dccpd_ack_seq ,
2005-08-09 20:14:34 -07:00
dccp_packet_name ( DCCP_SKB_CB ( skb ) - > dccpd_type ) ) ;
return ;
}
/* Update RTT */
2005-08-27 18:18:18 -03:00
r_sample = timeval_now_delta ( & packet - > dccphtx_tstamp ) ;
2005-09-09 02:32:56 -03:00
if ( unlikely ( r_sample < = t_elapsed ) )
LIMIT_NETDEBUG ( KERN_WARNING
" %s: r_sample=%uus, t_elapsed=%uus \n " ,
__FUNCTION__ , r_sample , t_elapsed ) ;
else
r_sample - = t_elapsed ;
2005-08-09 20:14:34 -07:00
/* Update RTT estimate by
* If ( No feedback recv )
* R = R_sample ;
* Else
* R = q * R + ( 1 - q ) * R_sample ;
*
* q is a constant , RFC 3448 recomments 0.9
*/
if ( hctx - > ccid3hctx_state = = TFRC_SSTATE_NO_FBACK ) {
ccid3_hc_tx_set_state ( sk , TFRC_SSTATE_FBACK ) ;
hctx - > ccid3hctx_rtt = r_sample ;
} else
2005-08-27 03:51:58 -03:00
hctx - > ccid3hctx_rtt = ( hctx - > ccid3hctx_rtt * 9 ) / 10 +
r_sample / 10 ;
2005-08-09 20:14:34 -07:00
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, New RTT estimate=%uus, "
" r_sample=%us \n " , dccp_role ( sk ) , sk ,
hctx - > ccid3hctx_rtt , r_sample ) ;
2005-08-09 20:14:34 -07:00
/* Update timeout interval */
2005-08-21 05:07:37 -03:00
hctx - > ccid3hctx_t_rto = max_t ( u32 , 4 * hctx - > ccid3hctx_rtt ,
USEC_PER_SEC ) ;
2005-08-09 20:14:34 -07:00
/* Update receive rate */
2005-08-27 03:51:58 -03:00
hctx - > ccid3hctx_x_recv = x_recv ; /* X_recv in bytes per sec */
2005-08-09 20:14:34 -07:00
/* Update loss event rate */
if ( pinv = = ~ 0 | | pinv = = 0 )
hctx - > ccid3hctx_p = 0 ;
else {
hctx - > ccid3hctx_p = 1000000 / pinv ;
if ( hctx - > ccid3hctx_p < TFRC_SMALLEST_P ) {
hctx - > ccid3hctx_p = TFRC_SMALLEST_P ;
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, Smallest p used! \n " ,
dccp_role ( sk ) , sk ) ;
2005-08-09 20:14:34 -07:00
}
}
/* unschedule no feedback timer */
sk_stop_timer ( sk , & hctx - > ccid3hctx_no_feedback_timer ) ;
/* Update sending rate */
ccid3_hc_tx_update_x ( sk ) ;
/* Update next send time */
2005-08-27 18:18:18 -03:00
timeval_sub_usecs ( & hctx - > ccid3hctx_t_nom ,
hctx - > ccid3hctx_t_ipi ) ;
2005-08-09 20:14:34 -07:00
ccid3_calc_new_t_ipi ( hctx ) ;
2005-08-27 18:18:18 -03:00
timeval_add_usecs ( & hctx - > ccid3hctx_t_nom ,
hctx - > ccid3hctx_t_ipi ) ;
2005-08-09 20:14:34 -07:00
ccid3_calc_new_delta ( hctx ) ;
/* remove all packets older than the one acked from history */
2005-08-10 12:59:38 -03:00
dccp_tx_hist_purge_older ( ccid3_tx_hist ,
& hctx - > ccid3hctx_hist , packet ) ;
2005-08-29 02:15:54 -03:00
/*
* As we have calculated new ipi , delta , t_nom it is possible that
* we now can send a packet , so wake up dccp_wait_for_ccids .
*/
sk - > sk_write_space ( sk ) ;
2005-08-10 12:59:38 -03:00
2005-08-27 03:51:58 -03:00
/*
* Schedule no feedback timer to expire in
* max ( 4 * R , 2 * s / X )
*/
2005-08-21 05:07:37 -03:00
next_tmout = max ( hctx - > ccid3hctx_t_rto ,
2005-08-27 20:11:28 -03:00
2 * usecs_div ( hctx - > ccid3hctx_s ,
hctx - > ccid3hctx_x ) ) ;
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, Scheduled no feedback timer to "
" expire in %lu jiffies (%luus) \n " ,
dccp_role ( sk ) , sk ,
usecs_to_jiffies ( next_tmout ) , next_tmout ) ;
2005-08-09 20:14:34 -07:00
sk_reset_timer ( sk , & hctx - > ccid3hctx_no_feedback_timer ,
2005-08-27 03:51:58 -03:00
jiffies + max_t ( u32 , 1 , usecs_to_jiffies ( next_tmout ) ) ) ;
2005-08-09 20:14:34 -07:00
/* set idle flag */
hctx - > ccid3hctx_idle = 1 ;
break ;
default :
printk ( KERN_CRIT " %s: %s, sk=%p, Illegal state (%d)! \n " ,
__FUNCTION__ , dccp_role ( sk ) , sk , hctx - > ccid3hctx_state ) ;
dump_stack ( ) ;
break ;
}
}
static void ccid3_hc_tx_insert_options ( struct sock * sk , struct sk_buff * skb )
{
const struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx = dp - > dccps_hc_tx_ccid_private ;
2005-08-27 03:51:58 -03:00
if ( hctx = = NULL | | ! ( sk - > sk_state = = DCCP_OPEN | |
sk - > sk_state = = DCCP_PARTOPEN ) )
2005-08-09 20:14:34 -07:00
return ;
DCCP_SKB_CB ( skb ) - > dccpd_ccval = hctx - > ccid3hctx_last_win_count ;
}
static int ccid3_hc_tx_parse_options ( struct sock * sk , unsigned char option ,
2005-08-27 03:51:58 -03:00
unsigned char len , u16 idx ,
unsigned char * value )
2005-08-09 20:14:34 -07:00
{
int rc = 0 ;
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx = dp - > dccps_hc_tx_ccid_private ;
struct ccid3_options_received * opt_recv ;
if ( hctx = = NULL )
return 0 ;
opt_recv = & hctx - > ccid3hctx_options_received ;
if ( opt_recv - > ccid3or_seqno ! = dp - > dccps_gsr ) {
opt_recv - > ccid3or_seqno = dp - > dccps_gsr ;
opt_recv - > ccid3or_loss_event_rate = ~ 0 ;
opt_recv - > ccid3or_loss_intervals_idx = 0 ;
opt_recv - > ccid3or_loss_intervals_len = 0 ;
opt_recv - > ccid3or_receive_rate = 0 ;
}
switch ( option ) {
case TFRC_OPT_LOSS_EVENT_RATE :
if ( len ! = 4 ) {
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, invalid len for "
" TFRC_OPT_LOSS_EVENT_RATE \n " ,
2005-08-09 20:14:34 -07:00
dccp_role ( sk ) , sk ) ;
rc = - EINVAL ;
} else {
opt_recv - > ccid3or_loss_event_rate = ntohl ( * ( u32 * ) value ) ;
ccid3_pr_debug ( " %s, sk=%p, LOSS_EVENT_RATE=%u \n " ,
dccp_role ( sk ) , sk ,
opt_recv - > ccid3or_loss_event_rate ) ;
}
break ;
case TFRC_OPT_LOSS_INTERVALS :
opt_recv - > ccid3or_loss_intervals_idx = idx ;
opt_recv - > ccid3or_loss_intervals_len = len ;
ccid3_pr_debug ( " %s, sk=%p, LOSS_INTERVALS=(%u, %u) \n " ,
dccp_role ( sk ) , sk ,
opt_recv - > ccid3or_loss_intervals_idx ,
opt_recv - > ccid3or_loss_intervals_len ) ;
break ;
case TFRC_OPT_RECEIVE_RATE :
if ( len ! = 4 ) {
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, invalid len for "
" TFRC_OPT_RECEIVE_RATE \n " ,
2005-08-09 20:14:34 -07:00
dccp_role ( sk ) , sk ) ;
rc = - EINVAL ;
} else {
opt_recv - > ccid3or_receive_rate = ntohl ( * ( u32 * ) value ) ;
ccid3_pr_debug ( " %s, sk=%p, RECEIVE_RATE=%u \n " ,
dccp_role ( sk ) , sk ,
opt_recv - > ccid3or_receive_rate ) ;
}
break ;
}
return rc ;
}
static int ccid3_hc_tx_init ( struct sock * sk )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx ;
ccid3_pr_debug ( " %s, sk=%p \n " , dccp_role ( sk ) , sk ) ;
2005-08-27 03:51:58 -03:00
hctx = dp - > dccps_hc_tx_ccid_private = kmalloc ( sizeof ( * hctx ) ,
gfp_any ( ) ) ;
2005-08-09 20:14:34 -07:00
if ( hctx = = NULL )
return - ENOMEM ;
memset ( hctx , 0 , sizeof ( * hctx ) ) ;
2005-08-28 04:51:32 -03:00
if ( dp - > dccps_packet_size > = TFRC_MIN_PACKET_SIZE & &
dp - > dccps_packet_size < = TFRC_MAX_PACKET_SIZE )
hctx - > ccid3hctx_s = dp - > dccps_packet_size ;
2005-08-09 20:14:34 -07:00
else
hctx - > ccid3hctx_s = TFRC_STD_PACKET_SIZE ;
2005-08-27 03:51:58 -03:00
/* Set transmission rate to 1 packet per second */
hctx - > ccid3hctx_x = hctx - > ccid3hctx_s ;
2005-08-21 05:07:37 -03:00
hctx - > ccid3hctx_t_rto = USEC_PER_SEC ;
2005-08-09 20:14:34 -07:00
hctx - > ccid3hctx_state = TFRC_SSTATE_NO_SENT ;
INIT_LIST_HEAD ( & hctx - > ccid3hctx_hist ) ;
init_timer ( & hctx - > ccid3hctx_no_feedback_timer ) ;
return 0 ;
}
static void ccid3_hc_tx_exit ( struct sock * sk )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx = dp - > dccps_hc_tx_ccid_private ;
ccid3_pr_debug ( " %s, sk=%p \n " , dccp_role ( sk ) , sk ) ;
BUG_ON ( hctx = = NULL ) ;
ccid3_hc_tx_set_state ( sk , TFRC_SSTATE_TERM ) ;
sk_stop_timer ( sk , & hctx - > ccid3hctx_no_feedback_timer ) ;
/* Empty packet history */
2005-08-10 12:59:38 -03:00
dccp_tx_hist_purge ( ccid3_tx_hist , & hctx - > ccid3hctx_hist ) ;
2005-08-09 20:14:34 -07:00
kfree ( dp - > dccps_hc_tx_ccid_private ) ;
dp - > dccps_hc_tx_ccid_private = NULL ;
}
/*
* RX Half Connection methods
*/
/* TFRC receiver states */
enum ccid3_hc_rx_states {
TFRC_RSTATE_NO_DATA = 1 ,
TFRC_RSTATE_DATA ,
TFRC_RSTATE_TERM = 127 ,
} ;
# ifdef CCID3_DEBUG
static const char * ccid3_rx_state_name ( enum ccid3_hc_rx_states state )
{
static char * ccid3_rx_state_names [ ] = {
[ TFRC_RSTATE_NO_DATA ] = " NO_DATA " ,
[ TFRC_RSTATE_DATA ] = " DATA " ,
[ TFRC_RSTATE_TERM ] = " TERM " ,
} ;
return ccid3_rx_state_names [ state ] ;
}
# endif
2005-08-27 03:51:58 -03:00
static inline void ccid3_hc_rx_set_state ( struct sock * sk ,
enum ccid3_hc_rx_states state )
2005-08-09 20:14:34 -07:00
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_rx_sock * hcrx = dp - > dccps_hc_rx_ccid_private ;
enum ccid3_hc_rx_states oldstate = hcrx - > ccid3hcrx_state ;
ccid3_pr_debug ( " %s(%p) %-8.8s -> %s \n " ,
2005-08-27 03:51:58 -03:00
dccp_role ( sk ) , sk , ccid3_rx_state_name ( oldstate ) ,
ccid3_rx_state_name ( state ) ) ;
2005-08-09 20:14:34 -07:00
WARN_ON ( state = = oldstate ) ;
hcrx - > ccid3hcrx_state = state ;
}
static void ccid3_hc_rx_send_feedback ( struct sock * sk )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_rx_sock * hcrx = dp - > dccps_hc_rx_ccid_private ;
2005-08-10 12:59:38 -03:00
struct dccp_rx_hist_entry * packet ;
2005-08-27 18:18:18 -03:00
struct timeval now ;
2005-08-09 20:14:34 -07:00
ccid3_pr_debug ( " %s, sk=%p \n " , dccp_role ( sk ) , sk ) ;
2005-08-27 18:18:18 -03:00
do_gettimeofday ( & now ) ;
2005-08-09 20:14:34 -07:00
switch ( hcrx - > ccid3hcrx_state ) {
case TFRC_RSTATE_NO_DATA :
hcrx - > ccid3hcrx_x_recv = 0 ;
break ;
case TFRC_RSTATE_DATA : {
2005-08-27 18:18:18 -03:00
const u32 delta = timeval_delta ( & now ,
& hcrx - > ccid3hcrx_tstamp_last_feedback ) ;
2005-09-09 02:31:07 -03:00
hcrx - > ccid3hcrx_x_recv = usecs_div ( hcrx - > ccid3hcrx_bytes_recv ,
delta ) ;
2005-08-09 20:14:34 -07:00
}
break ;
default :
printk ( KERN_CRIT " %s: %s, sk=%p, Illegal state (%d)! \n " ,
__FUNCTION__ , dccp_role ( sk ) , sk , hcrx - > ccid3hcrx_state ) ;
dump_stack ( ) ;
return ;
}
2005-08-10 12:59:38 -03:00
packet = dccp_rx_hist_find_data_packet ( & hcrx - > ccid3hcrx_hist ) ;
2005-08-09 20:14:34 -07:00
if ( packet = = NULL ) {
printk ( KERN_CRIT " %s: %s, sk=%p, no data packet in history! \n " ,
__FUNCTION__ , dccp_role ( sk ) , sk ) ;
dump_stack ( ) ;
return ;
}
2005-08-27 18:18:18 -03:00
hcrx - > ccid3hcrx_tstamp_last_feedback = now ;
2005-08-13 20:34:23 -03:00
hcrx - > ccid3hcrx_last_counter = packet - > dccphrx_ccval ;
2005-08-10 12:59:38 -03:00
hcrx - > ccid3hcrx_seqno_last_counter = packet - > dccphrx_seqno ;
2005-08-09 20:14:34 -07:00
hcrx - > ccid3hcrx_bytes_recv = 0 ;
/* Convert to multiples of 10us */
2005-08-27 18:18:18 -03:00
hcrx - > ccid3hcrx_elapsed_time =
timeval_delta ( & now , & packet - > dccphrx_tstamp ) / 10 ;
2005-08-09 20:14:34 -07:00
if ( hcrx - > ccid3hcrx_p = = 0 )
hcrx - > ccid3hcrx_pinv = ~ 0 ;
else
hcrx - > ccid3hcrx_pinv = 1000000 / hcrx - > ccid3hcrx_p ;
2005-09-09 02:30:07 -03:00
dp - > dccps_hc_rx_insert_options = 1 ;
2005-08-09 20:14:34 -07:00
dccp_send_ack ( sk ) ;
}
static void ccid3_hc_rx_insert_options ( struct sock * sk , struct sk_buff * skb )
{
const struct dccp_sock * dp = dccp_sk ( sk ) ;
2005-08-23 21:51:59 -07:00
u32 x_recv , pinv ;
2005-08-09 20:14:34 -07:00
struct ccid3_hc_rx_sock * hcrx = dp - > dccps_hc_rx_ccid_private ;
2005-08-27 03:51:58 -03:00
if ( hcrx = = NULL | | ! ( sk - > sk_state = = DCCP_OPEN | |
sk - > sk_state = = DCCP_PARTOPEN ) )
2005-08-09 20:14:34 -07:00
return ;
DCCP_SKB_CB ( skb ) - > dccpd_ccval = hcrx - > ccid3hcrx_last_counter ;
2005-08-23 21:51:59 -07:00
if ( dccp_packet_without_ack ( skb ) )
return ;
if ( hcrx - > ccid3hcrx_elapsed_time ! = 0 )
dccp_insert_option_elapsed_time ( sk , skb ,
hcrx - > ccid3hcrx_elapsed_time ) ;
dccp_insert_option_timestamp ( sk , skb ) ;
x_recv = htonl ( hcrx - > ccid3hcrx_x_recv ) ;
pinv = htonl ( hcrx - > ccid3hcrx_pinv ) ;
dccp_insert_option ( sk , skb , TFRC_OPT_LOSS_EVENT_RATE ,
& pinv , sizeof ( pinv ) ) ;
dccp_insert_option ( sk , skb , TFRC_OPT_RECEIVE_RATE ,
& x_recv , sizeof ( x_recv ) ) ;
2005-08-09 20:14:34 -07:00
}
/* calculate first loss interval
*
* returns estimated loss interval in usecs */
static u32 ccid3_hc_rx_calc_first_li ( struct sock * sk )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_rx_sock * hcrx = dp - > dccps_hc_rx_ccid_private ;
2005-08-10 12:59:38 -03:00
struct dccp_rx_hist_entry * entry , * next , * tail = NULL ;
2005-08-09 20:14:34 -07:00
u32 rtt , delta , x_recv , fval , p , tmp2 ;
2005-08-27 18:18:18 -03:00
struct timeval tstamp = { 0 , } ;
2005-08-09 20:14:34 -07:00
int interval = 0 ;
int win_count = 0 ;
int step = 0 ;
u64 tmp1 ;
2005-08-10 12:59:38 -03:00
list_for_each_entry_safe ( entry , next , & hcrx - > ccid3hcrx_hist ,
dccphrx_node ) {
if ( dccp_rx_hist_entry_data_packet ( entry ) ) {
2005-08-09 20:14:34 -07:00
tail = entry ;
switch ( step ) {
case 0 :
2005-08-10 12:59:38 -03:00
tstamp = entry - > dccphrx_tstamp ;
2005-08-13 20:34:23 -03:00
win_count = entry - > dccphrx_ccval ;
2005-08-09 20:14:34 -07:00
step = 1 ;
break ;
case 1 :
2005-08-13 20:34:23 -03:00
interval = win_count - entry - > dccphrx_ccval ;
2005-08-09 20:14:34 -07:00
if ( interval < 0 )
interval + = TFRC_WIN_COUNT_LIMIT ;
if ( interval > 4 )
goto found ;
break ;
}
}
}
if ( step = = 0 ) {
2005-08-27 03:51:58 -03:00
printk ( KERN_CRIT " %s: %s, sk=%p, packet history contains no "
" data packets! \n " ,
2005-08-09 20:14:34 -07:00
__FUNCTION__ , dccp_role ( sk ) , sk ) ;
return ~ 0 ;
}
if ( interval = = 0 ) {
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, Could not find a win_count "
" interval > 0. Defaulting to 1 \n " ,
2005-08-09 20:14:34 -07:00
dccp_role ( sk ) , sk ) ;
interval = 1 ;
}
found :
2005-08-27 18:18:18 -03:00
rtt = timeval_delta ( & tstamp , & tail - > dccphrx_tstamp ) * 4 / interval ;
2005-08-09 20:14:34 -07:00
ccid3_pr_debug ( " %s, sk=%p, approximated RTT to %uus \n " ,
dccp_role ( sk ) , sk , rtt ) ;
if ( rtt = = 0 )
rtt = 1 ;
2005-08-27 18:18:18 -03:00
delta = timeval_now_delta ( & hcrx - > ccid3hcrx_tstamp_last_feedback ) ;
x_recv = hcrx - > ccid3hcrx_bytes_recv * USEC_PER_SEC ;
if ( likely ( delta > 1 ) )
x_recv / = delta ;
2005-08-09 20:14:34 -07:00
tmp1 = ( u64 ) x_recv * ( u64 ) rtt ;
do_div ( tmp1 , 10000000 ) ;
tmp2 = ( u32 ) tmp1 ;
fval = ( hcrx - > ccid3hcrx_s * 100000 ) / tmp2 ;
/* do not alter order above or you will get overflow on 32 bit */
2005-08-28 00:47:15 -03:00
p = tfrc_calc_x_reverse_lookup ( fval ) ;
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, receive rate=%u bytes/s, implied "
" loss rate=%u \n " , dccp_role ( sk ) , sk , x_recv , p ) ;
2005-08-09 20:14:34 -07:00
if ( p = = 0 )
return ~ 0 ;
else
return 1000000 / p ;
}
static void ccid3_hc_rx_update_li ( struct sock * sk , u64 seq_loss , u8 win_loss )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_rx_sock * hcrx = dp - > dccps_hc_rx_ccid_private ;
2005-08-27 23:03:09 -03:00
if ( seq_loss ! = DCCP_MAX_SEQNO + 1 & &
list_empty ( & hcrx - > ccid3hcrx_li_hist ) ) {
struct dccp_li_hist_entry * li_tail ;
2005-08-09 20:14:34 -07:00
2005-08-27 23:03:09 -03:00
li_tail = dccp_li_hist_interval_new ( ccid3_li_hist ,
& hcrx - > ccid3hcrx_li_hist ,
seq_loss , win_loss ) ;
if ( li_tail = = NULL )
return ;
li_tail - > dccplih_interval = ccid3_hc_rx_calc_first_li ( sk ) ;
2005-08-09 20:14:34 -07:00
}
/* FIXME: find end of interval */
}
static void ccid3_hc_rx_detect_loss ( struct sock * sk )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_rx_sock * hcrx = dp - > dccps_hc_rx_ccid_private ;
2005-08-28 02:00:28 -03:00
u8 win_loss ;
const u64 seq_loss = dccp_rx_hist_detect_loss ( & hcrx - > ccid3hcrx_hist ,
& hcrx - > ccid3hcrx_li_hist ,
& win_loss ) ;
2005-08-09 20:14:34 -07:00
ccid3_hc_rx_update_li ( sk , seq_loss , win_loss ) ;
}
static void ccid3_hc_rx_packet_recv ( struct sock * sk , struct sk_buff * skb )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_rx_sock * hcrx = dp - > dccps_hc_rx_ccid_private ;
2005-08-23 21:51:59 -07:00
const struct dccp_options_received * opt_recv ;
2005-08-10 12:59:38 -03:00
struct dccp_rx_hist_entry * packet ;
2005-08-09 20:14:34 -07:00
struct timeval now ;
u8 win_count ;
u32 p_prev ;
int ins ;
2005-08-27 03:51:58 -03:00
2005-08-09 20:14:34 -07:00
if ( hcrx = = NULL )
return ;
BUG_ON ( ! ( hcrx - > ccid3hcrx_state = = TFRC_RSTATE_NO_DATA | |
hcrx - > ccid3hcrx_state = = TFRC_RSTATE_DATA ) ) ;
2005-08-23 21:51:59 -07:00
opt_recv = & dp - > dccps_options_received ;
2005-08-09 20:14:34 -07:00
switch ( DCCP_SKB_CB ( skb ) - > dccpd_type ) {
case DCCP_PKT_ACK :
if ( hcrx - > ccid3hcrx_state = = TFRC_RSTATE_NO_DATA )
return ;
case DCCP_PKT_DATAACK :
2005-08-23 21:51:59 -07:00
if ( opt_recv - > dccpor_timestamp_echo = = 0 )
2005-08-09 20:14:34 -07:00
break ;
p_prev = hcrx - > ccid3hcrx_rtt ;
do_gettimeofday ( & now ) ;
2005-08-27 18:18:18 -03:00
hcrx - > ccid3hcrx_rtt = timeval_usecs ( & now ) -
2005-08-23 21:51:59 -07:00
( opt_recv - > dccpor_timestamp_echo -
opt_recv - > dccpor_elapsed_time ) * 10 ;
2005-08-09 20:14:34 -07:00
if ( p_prev ! = hcrx - > ccid3hcrx_rtt )
2005-08-23 21:51:59 -07:00
ccid3_pr_debug ( " %s, New RTT=%luus, elapsed time=%u \n " ,
dccp_role ( sk ) , hcrx - > ccid3hcrx_rtt ,
opt_recv - > dccpor_elapsed_time ) ;
2005-08-09 20:14:34 -07:00
break ;
case DCCP_PKT_DATA :
break ;
default :
ccid3_pr_debug ( " %s, sk=%p, not DATA/DATAACK/ACK packet(%s) \n " ,
dccp_role ( sk ) , sk ,
dccp_packet_name ( DCCP_SKB_CB ( skb ) - > dccpd_type ) ) ;
return ;
}
2005-08-23 21:51:59 -07:00
packet = dccp_rx_hist_entry_new ( ccid3_rx_hist , opt_recv - > dccpor_ndp ,
2005-08-10 12:59:38 -03:00
skb , SLAB_ATOMIC ) ;
2005-08-09 20:14:34 -07:00
if ( packet = = NULL ) {
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p, Not enough mem to add rx packet "
" to history (consider it lost)! " ,
2005-08-09 20:14:34 -07:00
dccp_role ( sk ) , sk ) ;
return ;
}
2005-08-13 20:34:23 -03:00
win_count = packet - > dccphrx_ccval ;
2005-08-09 20:14:34 -07:00
2005-08-28 01:19:14 -03:00
ins = dccp_rx_hist_add_packet ( ccid3_rx_hist , & hcrx - > ccid3hcrx_hist ,
& hcrx - > ccid3hcrx_li_hist , packet ) ;
2005-08-09 20:14:34 -07:00
if ( DCCP_SKB_CB ( skb ) - > dccpd_type = = DCCP_PKT_ACK )
return ;
switch ( hcrx - > ccid3hcrx_state ) {
case TFRC_RSTATE_NO_DATA :
2005-08-27 03:51:58 -03:00
ccid3_pr_debug ( " %s, sk=%p(%s), skb=%p, sending initial "
" feedback \n " ,
dccp_role ( sk ) , sk ,
dccp_state_name ( sk - > sk_state ) , skb ) ;
2005-08-09 20:14:34 -07:00
ccid3_hc_rx_send_feedback ( sk ) ;
ccid3_hc_rx_set_state ( sk , TFRC_RSTATE_DATA ) ;
return ;
case TFRC_RSTATE_DATA :
2005-08-27 03:51:58 -03:00
hcrx - > ccid3hcrx_bytes_recv + = skb - > len -
dccp_hdr ( skb ) - > dccph_doff * 4 ;
2005-08-27 18:18:18 -03:00
if ( ins ! = 0 )
break ;
do_gettimeofday ( & now ) ;
if ( timeval_delta ( & now , & hcrx - > ccid3hcrx_tstamp_last_ack ) > =
hcrx - > ccid3hcrx_rtt ) {
hcrx - > ccid3hcrx_tstamp_last_ack = now ;
ccid3_hc_rx_send_feedback ( sk ) ;
2005-08-09 20:14:34 -07:00
}
2005-08-27 18:18:18 -03:00
return ;
2005-08-09 20:14:34 -07:00
default :
printk ( KERN_CRIT " %s: %s, sk=%p, Illegal state (%d)! \n " ,
__FUNCTION__ , dccp_role ( sk ) , sk , hcrx - > ccid3hcrx_state ) ;
dump_stack ( ) ;
return ;
}
/* Dealing with packet loss */
2005-08-23 21:51:59 -07:00
ccid3_pr_debug ( " %s, sk=%p(%s), data loss! Reacting... \n " ,
dccp_role ( sk ) , sk , dccp_state_name ( sk - > sk_state ) ) ;
2005-08-09 20:14:34 -07:00
ccid3_hc_rx_detect_loss ( sk ) ;
p_prev = hcrx - > ccid3hcrx_p ;
/* Calculate loss event rate */
2005-08-27 23:03:09 -03:00
if ( ! list_empty ( & hcrx - > ccid3hcrx_li_hist ) )
2005-08-09 20:14:34 -07:00
/* Scaling up by 1000000 as fixed decimal */
2005-08-27 23:03:09 -03:00
hcrx - > ccid3hcrx_p = 1000000 / dccp_li_hist_calc_i_mean ( & hcrx - > ccid3hcrx_li_hist ) ;
2005-08-09 20:14:34 -07:00
if ( hcrx - > ccid3hcrx_p > p_prev ) {
ccid3_hc_rx_send_feedback ( sk ) ;
return ;
}
}
static int ccid3_hc_rx_init ( struct sock * sk )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_rx_sock * hcrx ;
ccid3_pr_debug ( " %s, sk=%p \n " , dccp_role ( sk ) , sk ) ;
2005-08-27 03:51:58 -03:00
hcrx = dp - > dccps_hc_rx_ccid_private = kmalloc ( sizeof ( * hcrx ) ,
gfp_any ( ) ) ;
2005-08-09 20:14:34 -07:00
if ( hcrx = = NULL )
return - ENOMEM ;
memset ( hcrx , 0 , sizeof ( * hcrx ) ) ;
2005-08-28 04:51:32 -03:00
if ( dp - > dccps_packet_size > = TFRC_MIN_PACKET_SIZE & &
dp - > dccps_packet_size < = TFRC_MAX_PACKET_SIZE )
hcrx - > ccid3hcrx_s = dp - > dccps_packet_size ;
2005-08-09 20:14:34 -07:00
else
hcrx - > ccid3hcrx_s = TFRC_STD_PACKET_SIZE ;
hcrx - > ccid3hcrx_state = TFRC_RSTATE_NO_DATA ;
INIT_LIST_HEAD ( & hcrx - > ccid3hcrx_hist ) ;
2005-08-27 23:03:09 -03:00
INIT_LIST_HEAD ( & hcrx - > ccid3hcrx_li_hist ) ;
2005-08-23 21:51:59 -07:00
/*
* XXX this seems to be paranoid , need to think more about this , for
* now start with something different than zero . - acme
*/
hcrx - > ccid3hcrx_rtt = USEC_PER_SEC / 5 ;
2005-08-09 20:14:34 -07:00
return 0 ;
}
static void ccid3_hc_rx_exit ( struct sock * sk )
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_rx_sock * hcrx = dp - > dccps_hc_rx_ccid_private ;
ccid3_pr_debug ( " %s, sk=%p \n " , dccp_role ( sk ) , sk ) ;
if ( hcrx = = NULL )
return ;
ccid3_hc_rx_set_state ( sk , TFRC_RSTATE_TERM ) ;
/* Empty packet history */
2005-08-10 12:59:38 -03:00
dccp_rx_hist_purge ( ccid3_rx_hist , & hcrx - > ccid3hcrx_hist ) ;
2005-08-09 20:14:34 -07:00
/* Empty loss interval history */
2005-08-27 23:03:09 -03:00
dccp_li_hist_purge ( ccid3_li_hist , & hcrx - > ccid3hcrx_li_hist ) ;
2005-08-09 20:14:34 -07:00
kfree ( dp - > dccps_hc_rx_ccid_private ) ;
dp - > dccps_hc_rx_ccid_private = NULL ;
}
[DCCP]: Introduce dccp_get_info
And also hc_tx and hc_rx get_info functions for the CCIDs to fill in
information that is specific to them.
For now reusing struct tcp_info, later I'll try to figure out a better
solution, for now its really nice to get this kind of info:
[root@qemu ~]# ./ss -danemi
State Recv-Q Send-Q Local Addr:Port Peer Addr:Port
LISTEN 0 0 *:5001 *:* ino:628 sk:c1340040
mem:(r0,w0,f0,t0) cwnd:0 ssthresh:0
ESTAB 0 0 172.20.0.2:5001 172.20.0.1:32785 ino:629 sk:c13409a0
mem:(r0,w0,f0,t0) ts rto:1000 rtt:0.004/0 cwnd:0 ssthresh:0 rcv_rtt:61.377
This, for instance, shows that we're not congestion controlling ACKs,
as the above output is in the ttcp receiving host, and ttcp is a one
way app, i.e. the received never calls sendmsg, so
ccid_hc_tx_send_packet is never called, so the TX half connection
stays in TFRC_SSTATE_NO_SENT state and hctx_rtt is never calculated,
stays with the value set in ccid3_hc_tx_init, 4us, as show above in
milliseconds (0.004ms), upcoming patches will fix this.
rcv_rtt seems sane tho, matching ping results :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-08-23 21:52:35 -07:00
static void ccid3_hc_rx_get_info ( struct sock * sk , struct tcp_info * info )
{
const struct dccp_sock * dp = dccp_sk ( sk ) ;
const struct ccid3_hc_rx_sock * hcrx = dp - > dccps_hc_rx_ccid_private ;
if ( hcrx = = NULL )
return ;
info - > tcpi_ca_state = hcrx - > ccid3hcrx_state ;
info - > tcpi_options | = TCPI_OPT_TIMESTAMPS ;
info - > tcpi_rcv_rtt = hcrx - > ccid3hcrx_rtt ;
}
static void ccid3_hc_tx_get_info ( struct sock * sk , struct tcp_info * info )
{
const struct dccp_sock * dp = dccp_sk ( sk ) ;
const struct ccid3_hc_tx_sock * hctx = dp - > dccps_hc_tx_ccid_private ;
if ( hctx = = NULL )
return ;
info - > tcpi_rto = hctx - > ccid3hctx_t_rto ;
info - > tcpi_rtt = hctx - > ccid3hctx_rtt ;
}
2005-08-09 20:14:34 -07:00
static struct ccid ccid3 = {
. ccid_id = 3 ,
. ccid_name = " ccid3 " ,
. ccid_owner = THIS_MODULE ,
. ccid_init = ccid3_init ,
. ccid_exit = ccid3_exit ,
. ccid_hc_tx_init = ccid3_hc_tx_init ,
. ccid_hc_tx_exit = ccid3_hc_tx_exit ,
. ccid_hc_tx_send_packet = ccid3_hc_tx_send_packet ,
. ccid_hc_tx_packet_sent = ccid3_hc_tx_packet_sent ,
. ccid_hc_tx_packet_recv = ccid3_hc_tx_packet_recv ,
. ccid_hc_tx_insert_options = ccid3_hc_tx_insert_options ,
. ccid_hc_tx_parse_options = ccid3_hc_tx_parse_options ,
. ccid_hc_rx_init = ccid3_hc_rx_init ,
. ccid_hc_rx_exit = ccid3_hc_rx_exit ,
. ccid_hc_rx_insert_options = ccid3_hc_rx_insert_options ,
. ccid_hc_rx_packet_recv = ccid3_hc_rx_packet_recv ,
[DCCP]: Introduce dccp_get_info
And also hc_tx and hc_rx get_info functions for the CCIDs to fill in
information that is specific to them.
For now reusing struct tcp_info, later I'll try to figure out a better
solution, for now its really nice to get this kind of info:
[root@qemu ~]# ./ss -danemi
State Recv-Q Send-Q Local Addr:Port Peer Addr:Port
LISTEN 0 0 *:5001 *:* ino:628 sk:c1340040
mem:(r0,w0,f0,t0) cwnd:0 ssthresh:0
ESTAB 0 0 172.20.0.2:5001 172.20.0.1:32785 ino:629 sk:c13409a0
mem:(r0,w0,f0,t0) ts rto:1000 rtt:0.004/0 cwnd:0 ssthresh:0 rcv_rtt:61.377
This, for instance, shows that we're not congestion controlling ACKs,
as the above output is in the ttcp receiving host, and ttcp is a one
way app, i.e. the received never calls sendmsg, so
ccid_hc_tx_send_packet is never called, so the TX half connection
stays in TFRC_SSTATE_NO_SENT state and hctx_rtt is never calculated,
stays with the value set in ccid3_hc_tx_init, 4us, as show above in
milliseconds (0.004ms), upcoming patches will fix this.
rcv_rtt seems sane tho, matching ping results :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-08-23 21:52:35 -07:00
. ccid_hc_rx_get_info = ccid3_hc_rx_get_info ,
. ccid_hc_tx_get_info = ccid3_hc_tx_get_info ,
2005-08-09 20:14:34 -07:00
} ;
module_param ( ccid3_debug , int , 0444 ) ;
MODULE_PARM_DESC ( ccid3_debug , " Enable debug messages " ) ;
static __init int ccid3_module_init ( void )
{
2005-08-10 12:59:38 -03:00
int rc = - ENOBUFS ;
2005-08-09 20:14:34 -07:00
2005-08-10 12:59:38 -03:00
ccid3_rx_hist = dccp_rx_hist_new ( " ccid3 " ) ;
if ( ccid3_rx_hist = = NULL )
2005-08-09 20:14:34 -07:00
goto out ;
2005-08-10 12:59:38 -03:00
ccid3_tx_hist = dccp_tx_hist_new ( " ccid3 " ) ;
if ( ccid3_tx_hist = = NULL )
goto out_free_rx ;
2005-08-09 20:14:34 -07:00
2005-08-27 23:03:09 -03:00
ccid3_li_hist = dccp_li_hist_new ( " ccid3 " ) ;
if ( ccid3_li_hist = = NULL )
2005-08-10 12:59:38 -03:00
goto out_free_tx ;
2005-08-09 20:14:34 -07:00
rc = ccid_register ( & ccid3 ) ;
if ( rc ! = 0 )
goto out_free_loss_interval_history ;
out :
return rc ;
2005-08-10 12:59:38 -03:00
2005-08-09 20:14:34 -07:00
out_free_loss_interval_history :
2005-08-27 23:03:09 -03:00
dccp_li_hist_delete ( ccid3_li_hist ) ;
ccid3_li_hist = NULL ;
2005-08-10 12:59:38 -03:00
out_free_tx :
dccp_tx_hist_delete ( ccid3_tx_hist ) ;
ccid3_tx_hist = NULL ;
out_free_rx :
dccp_rx_hist_delete ( ccid3_rx_hist ) ;
ccid3_rx_hist = NULL ;
2005-08-09 20:14:34 -07:00
goto out ;
}
module_init ( ccid3_module_init ) ;
static __exit void ccid3_module_exit ( void )
{
2005-08-13 20:35:39 -03:00
# ifdef CONFIG_IP_DCCP_UNLOAD_HACK
/*
* Hack to use while developing , so that we get rid of the control
* sock , that is what keeps a refcount on dccp . ko - acme
*/
extern void dccp_ctl_sock_exit ( void ) ;
dccp_ctl_sock_exit ( ) ;
# endif
2005-08-09 20:14:34 -07:00
ccid_unregister ( & ccid3 ) ;
2005-08-10 12:59:38 -03:00
if ( ccid3_tx_hist ! = NULL ) {
dccp_tx_hist_delete ( ccid3_tx_hist ) ;
ccid3_tx_hist = NULL ;
2005-08-09 20:14:34 -07:00
}
2005-08-10 12:59:38 -03:00
if ( ccid3_rx_hist ! = NULL ) {
dccp_rx_hist_delete ( ccid3_rx_hist ) ;
ccid3_rx_hist = NULL ;
2005-08-09 20:14:34 -07:00
}
2005-08-27 23:03:09 -03:00
if ( ccid3_li_hist ! = NULL ) {
dccp_li_hist_delete ( ccid3_li_hist ) ;
ccid3_li_hist = NULL ;
2005-08-09 20:14:34 -07:00
}
}
module_exit ( ccid3_module_exit ) ;
2005-08-27 03:51:58 -03:00
MODULE_AUTHOR ( " Ian McDonald <iam4@cs.waikato.ac.nz>, "
" Arnaldo Carvalho de Melo <acme@ghostprotocols.net> " ) ;
2005-08-09 20:14:34 -07:00
MODULE_DESCRIPTION ( " DCCP TFRC CCID3 CCID " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " net-dccp-ccid-3 " ) ;