2005-08-10 07:14:34 +04:00
/*
* net / dccp / ccids / ccid3 . c
*
* Copyright ( c ) 2005 The University of Waikato , Hamilton , New Zealand .
2006-08-27 06:01:30 +04:00
* Copyright ( c ) 2005 - 6 Ian McDonald < ian . mcdonald @ jandi . co . nz >
2005-08-10 07:14:34 +04: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 .
*/
# include "../ccid.h"
# include "../dccp.h"
2005-08-28 06:18:26 +04:00
# include "lib/packet_history.h"
2005-08-28 06:03:09 +04:00
# include "lib/loss_interval.h"
2005-08-28 07:47:15 +04:00
# include "lib/tfrc.h"
2005-08-10 07:14:34 +04:00
# include "ccid3.h"
2006-11-20 23:28:09 +03:00
# ifdef CONFIG_IP_DCCP_CCID3_DEBUG
static int ccid3_debug ;
# define ccid3_pr_debug(format, a...) DCCP_PR_DEBUG(ccid3_debug, format, ##a)
2005-08-10 07:14:34 +04:00
# else
# define ccid3_pr_debug(format, a...)
# endif
2005-08-14 05:42:25 +04:00
static struct dccp_tx_hist * ccid3_tx_hist ;
static struct dccp_rx_hist * ccid3_rx_hist ;
2005-08-28 06:03:09 +04:00
static struct dccp_li_hist * ccid3_li_hist ;
2005-08-10 07:14:34 +04:00
2006-11-20 23:28:09 +03:00
# ifdef CONFIG_IP_DCCP_CCID3_DEBUG
2005-08-10 07:14:34 +04:00
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
2006-03-21 08:58:56 +03:00
static void ccid3_hc_tx_set_state ( struct sock * sk ,
enum ccid3_hc_tx_states state )
2005-08-10 07:14:34 +04:00
{
2005-09-09 09:40:58 +04:00
struct ccid3_hc_tx_sock * hctx = ccid3_hc_tx_sk ( sk ) ;
2005-08-10 07:14:34 +04:00
enum ccid3_hc_tx_states oldstate = hctx - > ccid3hctx_state ;
ccid3_pr_debug ( " %s(%p) %-8.8s -> %s \n " ,
2005-08-27 10:51:58 +04:00
dccp_role ( sk ) , sk , ccid3_tx_state_name ( oldstate ) ,
ccid3_tx_state_name ( state ) ) ;
2005-08-10 07:14:34 +04:00
WARN_ON ( state = = oldstate ) ;
hctx - > ccid3hctx_state = state ;
}
2006-11-28 01:31:33 +03:00
/*
* Recalculate scheduled nominal send time t_nom , inter - packet interval
* t_ipi , and delta value . Should be called after each change to X .
*/
static inline void ccid3_update_send_time ( struct ccid3_hc_tx_sock * hctx )
2005-08-10 07:14:34 +04:00
{
2006-11-28 01:31:33 +03:00
timeval_sub_usecs ( & hctx - > ccid3hctx_t_nom , hctx - > ccid3hctx_t_ipi ) ;
2006-12-10 05:02:12 +03:00
/* Calculate new t_ipi = s / X_inst (X_inst is in 64 * bytes/second) */
hctx - > ccid3hctx_t_ipi = scaled_div ( hctx - > ccid3hctx_s ,
hctx - > ccid3hctx_x > > 6 ) ;
2005-08-10 07:14:34 +04:00
2006-11-28 01:31:33 +03:00
/* Update nominal send time with regard to the new t_ipi */
timeval_add_usecs ( & hctx - > ccid3hctx_t_nom , hctx - > ccid3hctx_t_ipi ) ;
/* Calculate new delta by delta = min(t_ipi / 2, t_gran / 2) */
2005-08-27 10:51:58 +04:00
hctx - > ccid3hctx_delta = min_t ( u32 , hctx - > ccid3hctx_t_ipi / 2 ,
TFRC_OPSYS_HALF_TIME_GRAN ) ;
2005-08-10 07:14:34 +04:00
}
/*
* Update X by
* If ( p > 0 )
2006-12-03 19:50:56 +03:00
* X_calc = calcX ( s , R , p ) ;
2005-08-10 07:14:34 +04:00
* 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 ;
2006-12-03 19:50:56 +03:00
*
2006-12-10 05:02:12 +03:00
* Note : X and X_recv are both stored in units of 64 * bytes / second , to support
* fine - grained resolution of sending rates . This requires scaling by 2 ^ 6
* throughout the code . Only X_calc is unscaled ( in bytes / second ) .
*
2006-12-03 19:50:56 +03:00
* If X has changed , we also update the scheduled send time t_now ,
* the inter - packet interval t_ipi , and the delta value .
2006-12-10 05:02:12 +03:00
*/
2006-11-29 00:51:42 +03:00
static void ccid3_hc_tx_update_x ( struct sock * sk , struct timeval * now )
2005-08-10 07:14:34 +04:00
{
2005-09-09 09:40:58 +04:00
struct ccid3_hc_tx_sock * hctx = ccid3_hc_tx_sk ( sk ) ;
2006-12-10 05:02:12 +03:00
const __u64 old_x = hctx - > ccid3hctx_x ;
2005-08-10 07:14:34 +04:00
2006-12-03 19:53:07 +03:00
if ( hctx - > ccid3hctx_p > 0 ) {
2006-12-10 05:02:12 +03:00
hctx - > ccid3hctx_x = min_t ( u64 , hctx - > ccid3hctx_x_calc < < 6 ,
hctx - > ccid3hctx_x_recv * 2 ) ;
hctx - > ccid3hctx_x = max_t ( u64 , hctx - > ccid3hctx_x ,
( hctx - > ccid3hctx_s < < 6 ) / TFRC_T_MBI ) ;
2006-11-29 00:51:42 +03:00
2006-12-10 05:03:51 +03:00
} else if ( timeval_delta ( now , & hctx - > ccid3hctx_t_ld ) -
( suseconds_t ) hctx - > ccid3hctx_rtt > = 0 ) {
2006-12-10 05:02:12 +03:00
hctx - > ccid3hctx_x = max ( 2 * min ( hctx - > ccid3hctx_x ,
hctx - > ccid3hctx_x_recv ) ,
scaled_div ( hctx - > ccid3hctx_s < < 6 ,
hctx - > ccid3hctx_rtt ) ) ;
2006-11-29 00:51:42 +03:00
hctx - > ccid3hctx_t_ld = * now ;
2006-12-10 05:00:14 +03:00
}
2005-08-28 01:18:18 +04:00
2006-11-28 01:31:33 +03:00
if ( hctx - > ccid3hctx_x ! = old_x )
ccid3_update_send_time ( hctx ) ;
2005-08-10 07:14:34 +04:00
}
2006-11-29 00:22:33 +03:00
/*
* Track the mean packet size ` s ' ( cf . RFC 4342 , 5.3 and RFC 3448 , 4.1 )
* @ len : DCCP packet payload size in bytes
*/
static inline void ccid3_hc_tx_update_s ( struct ccid3_hc_tx_sock * hctx , int len )
{
if ( unlikely ( len = = 0 ) )
ccid3_pr_debug ( " Packet payload length is 0 - not updating \n " ) ;
else
hctx - > ccid3hctx_s = hctx - > ccid3hctx_s = = 0 ? len :
( 9 * hctx - > ccid3hctx_s + len ) / 10 ;
/*
* Note : We could do a potential optimisation here - when ` s ' changes ,
* recalculate sending rate and consequently t_ipi , t_delta , and
* t_now . This is however non - standard , and the benefits are not
* clear , so it is currently left out .
*/
}
2005-08-10 07:14:34 +04:00
static void ccid3_hc_tx_no_feedback_timer ( unsigned long data )
{
struct sock * sk = ( struct sock * ) data ;
2005-09-09 09:40:58 +04:00
struct ccid3_hc_tx_sock * hctx = ccid3_hc_tx_sk ( sk ) ;
2006-11-28 23:34:34 +03:00
unsigned long t_nfb = USEC_PER_SEC / 5 ;
2005-08-10 07:14:34 +04:00
bh_lock_sock ( sk ) ;
if ( sock_owned_by_user ( sk ) ) {
/* Try again later. */
/* XXX: set some sensible MIB */
2006-11-28 01:29:27 +03:00
goto restart_timer ;
2005-08-10 07:14:34 +04:00
}
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_NO_FBACK :
2006-11-29 00:51:42 +03:00
/* RFC 3448, 4.4: Halve send rate directly */
2006-12-10 05:01:22 +03:00
hctx - > ccid3hctx_x = max_t ( u32 , hctx - > ccid3hctx_x / 2 ,
2006-12-10 05:02:12 +03:00
( hctx - > ccid3hctx_s < < 6 ) / TFRC_T_MBI ) ;
2005-08-10 07:14:34 +04:00
2006-12-10 05:02:12 +03:00
ccid3_pr_debug ( " %s, sk=%p, state=%s, updated tx rate to %u "
2005-08-27 10:51:58 +04:00
" bytes/s \n " ,
dccp_role ( sk ) , sk ,
ccid3_tx_state_name ( hctx - > ccid3hctx_state ) ,
2006-12-10 05:02:12 +03:00
( unsigned ) ( hctx - > ccid3hctx_x > > 6 ) ) ;
2006-11-28 01:29:27 +03:00
/* The value of R is still undefined and so we can not recompute
* the timout value . Keep initial value as per [ RFC 4342 , 5 ] . */
2006-11-28 23:34:34 +03:00
t_nfb = TFRC_INITIAL_TIMEOUT ;
2006-11-28 01:31:33 +03:00
ccid3_update_send_time ( hctx ) ;
2005-08-10 07:14:34 +04:00
break ;
case TFRC_SSTATE_FBACK :
2005-08-27 10:51:58 +04:00
/*
* Check if IDLE since last timeout and recv rate is less than
2006-12-10 05:02:12 +03:00
* 4 packets ( in units of 64 * bytes / sec ) per RTT
2005-08-27 10:51:58 +04:00
*/
if ( ! hctx - > ccid3hctx_idle | |
2006-12-10 05:02:12 +03:00
( hctx - > ccid3hctx_x_recv > = 4 *
scaled_div ( hctx - > ccid3hctx_s < < 6 , hctx - > ccid3hctx_rtt ) ) ) {
2006-11-29 00:51:42 +03:00
struct timeval now ;
2005-08-27 10:51:58 +04:00
ccid3_pr_debug ( " %s, sk=%p, state=%s, not idle \n " ,
dccp_role ( sk ) , sk ,
2005-08-10 07:14:34 +04:00
ccid3_tx_state_name ( hctx - > ccid3hctx_state ) ) ;
2006-12-10 05:00:14 +03:00
/*
* Modify the cached value of X_recv [ RFC 3448 , 4.4 ]
*
* If ( p = = 0 | | X_calc > 2 * X_recv )
2005-08-10 07:14:34 +04:00
* X_recv = max ( X_recv / 2 , s / ( 2 * t_mbi ) ) ;
* Else
* X_recv = X_calc / 4 ;
2006-12-10 05:02:12 +03:00
*
* Note that X_recv is scaled by 2 ^ 6 while X_calc is not
2005-08-10 07:14:34 +04:00
*/
2006-12-03 19:53:07 +03:00
BUG_ON ( hctx - > ccid3hctx_p & & ! hctx - > ccid3hctx_x_calc ) ;
2005-08-10 07:14:34 +04:00
2006-12-03 19:53:07 +03:00
if ( hctx - > ccid3hctx_p = = 0 | |
2006-12-10 05:02:12 +03:00
hctx - > ccid3hctx_x_calc > ( hctx - > ccid3hctx_x_recv > > 5 ) ) {
hctx - > ccid3hctx_x_recv =
max_t ( u64 , hctx - > ccid3hctx_x_recv / 2 ,
( hctx - > ccid3hctx_s < < 6 ) /
( 2 * TFRC_T_MBI ) ) ;
2006-12-10 05:00:14 +03:00
if ( hctx - > ccid3hctx_p = = 0 )
dccp_timestamp ( sk , & now ) ;
} else
2006-12-10 05:02:12 +03:00
hctx - > ccid3hctx_x_recv = hctx - > ccid3hctx_x_calc < < 4 ;
2005-08-10 07:14:34 +04:00
2006-12-10 05:00:14 +03:00
/* Now recalculate X [RFC 3448, 4.3, step (4)] */
2006-11-29 00:51:42 +03:00
ccid3_hc_tx_update_x ( sk , & now ) ;
2005-08-10 07:14:34 +04:00
}
2005-08-28 03:11:28 +04:00
/*
* Schedule no feedback timer to expire in
2006-12-03 19:50:23 +03:00
* max ( t_RTO , 2 * s / X ) = max ( t_RTO , 2 * t_ipi )
* See comments in packet_recv ( ) regarding the value of t_RTO .
2005-08-28 03:11:28 +04:00
*/
2006-12-03 19:50:23 +03:00
t_nfb = max ( hctx - > ccid3hctx_t_rto , 2 * hctx - > ccid3hctx_t_ipi ) ;
2005-08-10 07:14:34 +04:00
break ;
2006-11-20 23:39:23 +03:00
case TFRC_SSTATE_NO_SENT :
DCCP_BUG ( " Illegal %s state NO_SENT, sk=%p " , dccp_role ( sk ) , sk ) ;
/* fall through */
case TFRC_SSTATE_TERM :
2005-08-10 07:14:34 +04:00
goto out ;
}
hctx - > ccid3hctx_idle = 1 ;
2006-11-28 01:29:27 +03:00
restart_timer :
sk_reset_timer ( sk , & hctx - > ccid3hctx_no_feedback_timer ,
2006-11-28 23:34:34 +03:00
jiffies + usecs_to_jiffies ( t_nfb ) ) ;
2005-08-10 07:14:34 +04:00
out :
bh_unlock_sock ( sk ) ;
sock_put ( sk ) ;
}
2006-11-27 17:26:03 +03:00
/*
* returns
* > 0 : delay ( in msecs ) that should pass before actually sending
* = 0 : can send immediately
* < 0 : error condition ; do not send packet
*/
2006-11-29 00:55:06 +03:00
static int ccid3_hc_tx_send_packet ( struct sock * sk , struct sk_buff * skb )
2005-08-10 07:14:34 +04:00
{
struct dccp_sock * dp = dccp_sk ( sk ) ;
2005-09-09 09:40:58 +04:00
struct ccid3_hc_tx_sock * hctx = ccid3_hc_tx_sk ( sk ) ;
2005-08-10 19:59:38 +04:00
struct dccp_tx_hist_entry * new_packet ;
2005-08-10 07:14:34 +04:00
struct timeval now ;
2006-12-10 05:03:51 +03:00
suseconds_t delay ;
2005-08-10 07:14:34 +04:00
2006-11-20 23:39:23 +03:00
BUG_ON ( hctx = = NULL ) ;
2005-08-27 10:51:58 +04:00
2005-08-10 07:14:34 +04:00
/*
2006-11-27 17:26:57 +03:00
* This function is called only for Data and DataAck packets . Sending
* zero - sized Data ( Ack ) s is theoretically possible , but for congestion
* control this case is pathological - ignore it .
2005-08-10 07:14:34 +04:00
*/
2006-11-29 00:55:06 +03:00
if ( unlikely ( skb - > len = = 0 ) )
2006-11-27 17:26:57 +03:00
return - EBADMSG ;
2005-08-10 07:14:34 +04:00
/* See if last packet allocated was not sent */
2005-08-10 19:59:38 +04:00
new_packet = dccp_tx_hist_head ( & hctx - > ccid3hctx_hist ) ;
if ( new_packet = = NULL | | new_packet - > dccphtx_sent ) {
2005-08-27 10:51:58 +04:00
new_packet = dccp_tx_hist_entry_new ( ccid3_tx_hist ,
2006-12-07 07:33:16 +03:00
GFP_ATOMIC ) ;
2005-08-10 07:14:34 +04:00
2005-09-10 03:01:25 +04:00
if ( unlikely ( new_packet = = NULL ) ) {
2006-11-20 23:39:23 +03:00
DCCP_WARN ( " %s, sk=%p, not enough mem to add to history, "
" send refused \n " , dccp_role ( sk ) , sk ) ;
2006-11-27 17:26:03 +03:00
return - ENOBUFS ;
2005-08-10 07:14:34 +04:00
}
2005-08-10 19:59:38 +04:00
dccp_tx_hist_add_entry ( & hctx - > ccid3hctx_hist , new_packet ) ;
2005-08-10 07:14:34 +04:00
}
2005-09-09 09:38:35 +04:00
dccp_timestamp ( sk , & now ) ;
2005-08-10 07:14:34 +04:00
switch ( hctx - > ccid3hctx_state ) {
case TFRC_SSTATE_NO_SENT :
2005-08-27 10:51:58 +04:00
sk_reset_timer ( sk , & hctx - > ccid3hctx_no_feedback_timer ,
jiffies + usecs_to_jiffies ( TFRC_INITIAL_TIMEOUT ) ) ;
2005-08-10 07:14:34 +04:00
hctx - > ccid3hctx_last_win_count = 0 ;
hctx - > ccid3hctx_t_last_win_count = now ;
ccid3_hc_tx_set_state ( sk , TFRC_SSTATE_NO_FBACK ) ;
2006-12-10 05:02:12 +03:00
/* Set initial sending rate X/s to 1pps (X is scaled by 2^6) */
2006-11-29 00:55:06 +03:00
ccid3_hc_tx_update_s ( hctx , skb - > len ) ;
2006-12-10 05:02:12 +03:00
hctx - > ccid3hctx_x = hctx - > ccid3hctx_s < < 6 ;
2006-11-29 00:22:33 +03:00
2006-11-27 17:13:38 +03:00
/* First timeout, according to [RFC 3448, 4.2], is 1 second */
hctx - > ccid3hctx_t_ipi = USEC_PER_SEC ;
/* Initial delta: minimum of 0.5 sec and t_gran/2 */
hctx - > ccid3hctx_delta = TFRC_OPSYS_HALF_TIME_GRAN ;
/* Set t_0 for initial packet */
2005-08-10 07:14:34 +04:00
hctx - > ccid3hctx_t_nom = now ;
break ;
case TFRC_SSTATE_NO_FBACK :
case TFRC_SSTATE_FBACK :
2006-11-27 17:25:10 +03:00
delay = timeval_delta ( & hctx - > ccid3hctx_t_nom , & now ) ;
/*
* Scheduling of packet transmissions [ RFC 3448 , 4.6 ]
*
* if ( t_now > t_nom - delta )
* // send the packet now
* else
* // send the packet in (t_nom - t_now) milliseconds.
*/
2006-12-10 05:03:51 +03:00
if ( delay - ( suseconds_t ) hctx - > ccid3hctx_delta > = 0 )
2006-11-27 17:26:03 +03:00
return delay / 1000L ;
2005-08-10 07:14:34 +04:00
break ;
2006-11-20 23:39:23 +03:00
case TFRC_SSTATE_TERM :
DCCP_BUG ( " Illegal %s state TERM, sk=%p " , dccp_role ( sk ) , sk ) ;
2006-11-27 17:26:03 +03:00
return - EINVAL ;
2005-08-10 07:14:34 +04:00
}
2006-11-27 17:26:03 +03:00
/* prepare to send now (add options etc.) */
dp - > dccps_hc_tx_insert_options = 1 ;
new_packet - > dccphtx_ccval = DCCP_SKB_CB ( skb ) - > dccpd_ccval =
hctx - > ccid3hctx_last_win_count ;
timeval_add_usecs ( & hctx - > ccid3hctx_t_nom , hctx - > ccid3hctx_t_ipi ) ;
return 0 ;
2005-08-10 07:14:34 +04:00
}
2006-11-29 00:55:06 +03:00
static void ccid3_hc_tx_packet_sent ( struct sock * sk , int more , unsigned int len )
2005-08-10 07:14:34 +04:00
{
2005-09-09 09:40:58 +04:00
const struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx = ccid3_hc_tx_sk ( sk ) ;
2005-08-10 07:14:34 +04:00
struct timeval now ;
2006-12-10 05:03:51 +03:00
suseconds_t quarter_rtt ;
2006-11-29 00:55:06 +03:00
struct dccp_tx_hist_entry * packet ;
2005-08-10 07:14:34 +04:00
2006-11-20 23:39:23 +03:00
BUG_ON ( hctx = = NULL ) ;
2005-08-10 07:14:34 +04:00
2005-09-09 09:38:35 +04:00
dccp_timestamp ( sk , & now ) ;
2005-08-10 07:14:34 +04:00
2006-11-29 00:55:06 +03:00
ccid3_hc_tx_update_s ( hctx , len ) ;
2005-08-10 07:14:34 +04:00
2006-11-29 00:55:06 +03:00
packet = dccp_tx_hist_head ( & hctx - > ccid3hctx_hist ) ;
if ( unlikely ( packet = = NULL ) ) {
DCCP_WARN ( " packet doesn't exist in history! \n " ) ;
return ;
}
if ( unlikely ( packet - > dccphtx_sent ) ) {
DCCP_WARN ( " no unsent packet in history! \n " ) ;
return ;
}
packet - > dccphtx_tstamp = now ;
packet - > dccphtx_seqno = dp - > dccps_gss ;
/*
* Check if win_count have changed
* Algorithm in " 8.1. Window Counter Value " in RFC 4342.
*/
quarter_rtt = timeval_delta ( & now , & hctx - > ccid3hctx_t_last_win_count ) ;
if ( likely ( hctx - > ccid3hctx_rtt > 8 ) )
quarter_rtt / = hctx - > ccid3hctx_rtt / 4 ;
2006-11-29 00:22:33 +03:00
2006-11-29 00:55:06 +03: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 ;
ccid3_pr_debug ( " %s, sk=%p, window changed from "
" %u to %u! \n " ,
dccp_role ( sk ) , sk ,
packet - > dccphtx_ccval ,
hctx - > ccid3hctx_last_win_count ) ;
}
2005-08-27 10:51:58 +04:00
2006-11-29 00:55:06 +03:00
hctx - > ccid3hctx_idle = 0 ;
packet - > dccphtx_rtt = hctx - > ccid3hctx_rtt ;
packet - > dccphtx_sent = 1 ;
2005-08-10 07:14:34 +04:00
}
static void ccid3_hc_tx_packet_recv ( struct sock * sk , struct sk_buff * skb )
{
2005-09-09 09:40:58 +04:00
const struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx = ccid3_hc_tx_sk ( sk ) ;
2005-08-10 07:14:34 +04:00
struct ccid3_options_received * opt_recv ;
2005-08-10 19:59:38 +04:00
struct dccp_tx_hist_entry * packet ;
2005-09-09 09:38:35 +04:00
struct timeval now ;
2006-11-28 23:34:34 +03:00
unsigned long t_nfb ;
2005-08-10 07:14:34 +04:00
u32 pinv ;
2006-12-03 19:50:56 +03:00
long r_sample , t_elapsed ;
2005-08-27 10:51:58 +04:00
2006-11-20 23:39:23 +03:00
BUG_ON ( hctx = = NULL ) ;
2005-08-10 07:14:34 +04:00
/* 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 ;
switch ( hctx - > ccid3hctx_state ) {
case TFRC_SSTATE_NO_FBACK :
case TFRC_SSTATE_FBACK :
2006-12-03 19:50:56 +03:00
/* get packet from history to look up t_recvdata */
2005-08-10 19:59:38 +04:00
packet = dccp_tx_hist_find_entry ( & hctx - > ccid3hctx_hist ,
DCCP_SKB_CB ( skb ) - > dccpd_ack_seq ) ;
2005-09-10 03:01:25 +04:00
if ( unlikely ( packet = = NULL ) ) {
2006-12-03 19:50:56 +03:00
DCCP_WARN ( " %s(%p), seqno %llu(%s) doesn't exist "
2006-11-20 23:39:23 +03:00
" in history! \n " , dccp_role ( sk ) , sk ,
2005-09-10 03:01:25 +04:00
( unsigned long long ) DCCP_SKB_CB ( skb ) - > dccpd_ack_seq ,
2006-11-20 23:39:23 +03:00
dccp_packet_name ( DCCP_SKB_CB ( skb ) - > dccpd_type ) ) ;
2005-08-10 07:14:34 +04:00
return ;
}
2006-12-10 05:02:12 +03:00
/* Update receive rate in units of 64 * bytes/second */
hctx - > ccid3hctx_x_recv = opt_recv - > ccid3or_receive_rate < < 6 ;
2006-12-03 19:50:56 +03:00
/* Update loss event rate */
pinv = opt_recv - > ccid3or_loss_event_rate ;
2006-12-10 04:59:14 +03:00
if ( pinv = = ~ 0U | | pinv = = 0 ) /* see RFC 4342, 8.5 */
2006-12-03 19:50:56 +03:00
hctx - > ccid3hctx_p = 0 ;
2006-12-10 04:59:14 +03:00
else /* can not exceed 100% */
2006-12-03 19:53:07 +03:00
hctx - > ccid3hctx_p = 1000000 / pinv ;
2006-12-03 19:50:56 +03:00
2005-09-09 09:38:35 +04:00
dccp_timestamp ( sk , & now ) ;
2006-12-03 19:50:56 +03:00
/*
* Calculate new round trip sample as per [ RFC 3448 , 4.3 ] by
* R_sample = ( now - t_recvdata ) - t_elapsed
*/
r_sample = timeval_delta ( & now , & packet - > dccphtx_tstamp ) ;
t_elapsed = dp - > dccps_options_received . dccpor_elapsed_time * 10 ;
2006-12-10 05:03:51 +03:00
DCCP_BUG_ON ( r_sample < 0 ) ;
if ( unlikely ( r_sample < = t_elapsed ) )
2006-12-03 19:50:56 +03:00
DCCP_WARN ( " WARNING: r_sample=%ldus <= t_elapsed=%ldus \n " ,
2006-11-20 23:39:23 +03:00
r_sample , t_elapsed ) ;
2005-09-09 09:32:56 +04:00
else
r_sample - = t_elapsed ;
2006-12-10 05:06:32 +03:00
CCID3_RTT_SANITY_CHECK ( r_sample ) ;
2005-08-10 07:14:34 +04:00
2006-12-10 05:06:32 +03:00
/* Update RTT estimate by
2005-08-10 07:14:34 +04:00
* 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 ) {
2006-12-10 05:02:12 +03:00
/*
* Larger Initial Windows [ RFC 4342 , sec . 5 ]
* We deviate in that we use ` s ' instead of ` MSS ' .
*/
2006-12-10 05:01:22 +03:00
u16 w_init = min ( 4 * hctx - > ccid3hctx_s ,
2006-11-29 00:51:42 +03:00
max ( 2 * hctx - > ccid3hctx_s , 4380 ) ) ;
hctx - > ccid3hctx_rtt = r_sample ;
2006-12-10 05:02:12 +03:00
hctx - > ccid3hctx_x = scaled_div ( w_init < < 6 , r_sample ) ;
2006-11-29 00:51:42 +03:00
hctx - > ccid3hctx_t_ld = now ;
ccid3_update_send_time ( hctx ) ;
2005-08-10 07:14:34 +04:00
2006-12-03 19:50:56 +03:00
ccid3_pr_debug ( " %s(%p), s=%u, w_init=%u, "
" R_sample=%ldus, X=%u \n " , dccp_role ( sk ) ,
sk , hctx - > ccid3hctx_s , w_init , r_sample ,
2006-12-10 05:02:12 +03:00
( unsigned ) ( hctx - > ccid3hctx_x > > 6 ) ) ;
2005-08-10 07:14:34 +04:00
2006-12-03 19:50:56 +03:00
ccid3_hc_tx_set_state ( sk , TFRC_SSTATE_FBACK ) ;
} else {
hctx - > ccid3hctx_rtt = ( 9 * hctx - > ccid3hctx_rtt +
( u32 ) r_sample ) / 10 ;
2005-08-10 07:14:34 +04:00
2006-12-10 05:00:14 +03:00
/* Update sending rate (step 4 of [RFC 3448, 4.3]) */
if ( hctx - > ccid3hctx_p > 0 )
hctx - > ccid3hctx_x_calc =
tfrc_calc_x ( hctx - > ccid3hctx_s ,
hctx - > ccid3hctx_rtt ,
hctx - > ccid3hctx_p ) ;
2006-12-03 19:50:56 +03:00
ccid3_hc_tx_update_x ( sk , & now ) ;
2005-08-10 07:14:34 +04:00
2006-12-03 19:50:56 +03:00
ccid3_pr_debug ( " %s(%p), RTT=%uus (sample=%ldus), s=%u, "
" p=%u, X_calc=%u, X=%u \n " , dccp_role ( sk ) ,
sk , hctx - > ccid3hctx_rtt , r_sample ,
hctx - > ccid3hctx_s , hctx - > ccid3hctx_p ,
hctx - > ccid3hctx_x_calc ,
2006-12-10 05:02:12 +03:00
( unsigned ) ( hctx - > ccid3hctx_x > > 6 ) ) ;
2005-08-10 07:14:34 +04:00
}
/* unschedule no feedback timer */
sk_stop_timer ( sk , & hctx - > ccid3hctx_no_feedback_timer ) ;
/* remove all packets older than the one acked from history */
2005-08-10 19:59:38 +04:00
dccp_tx_hist_purge_older ( ccid3_tx_hist ,
& hctx - > ccid3hctx_hist , packet ) ;
2005-08-29 09:15:54 +04:00
/*
* As we have calculated new ipi , delta , t_nom it is possible that
2006-11-28 01:32:37 +03:00
* we now can send a packet , so wake up dccp_wait_for_ccid
2005-08-29 09:15:54 +04:00
*/
sk - > sk_write_space ( sk ) ;
2005-08-10 19:59:38 +04:00
2006-12-03 19:50:23 +03:00
/*
* Update timeout interval for the nofeedback timer .
* We use a configuration option to increase the lower bound .
* This can help avoid triggering the nofeedback timer too often
* ( ' spinning ' ) on LANs with small RTTs .
*/
2006-11-28 01:32:37 +03:00
hctx - > ccid3hctx_t_rto = max_t ( u32 , 4 * hctx - > ccid3hctx_rtt ,
2006-12-03 19:50:23 +03:00
CONFIG_IP_DCCP_CCID3_RTO *
( USEC_PER_SEC / 1000 ) ) ;
2005-08-27 10:51:58 +04:00
/*
* Schedule no feedback timer to expire in
2006-12-03 19:50:23 +03:00
* max ( t_RTO , 2 * s / X ) = max ( t_RTO , 2 * t_ipi )
2005-08-27 10:51:58 +04:00
*/
2006-12-03 19:50:23 +03:00
t_nfb = max ( hctx - > ccid3hctx_t_rto , 2 * hctx - > ccid3hctx_t_ipi ) ;
2005-08-28 03:11:28 +04:00
2005-08-27 10:51:58 +04:00
ccid3_pr_debug ( " %s, sk=%p, Scheduled no feedback timer to "
" expire in %lu jiffies (%luus) \n " ,
dccp_role ( sk ) , sk ,
2006-11-28 23:34:34 +03:00
usecs_to_jiffies ( t_nfb ) , t_nfb ) ;
2005-08-10 07:14:34 +04:00
sk_reset_timer ( sk , & hctx - > ccid3hctx_no_feedback_timer ,
2006-11-28 23:34:34 +03:00
jiffies + usecs_to_jiffies ( t_nfb ) ) ;
2005-08-10 07:14:34 +04:00
/* set idle flag */
2006-12-10 05:04:43 +03:00
hctx - > ccid3hctx_idle = 1 ;
2005-08-10 07:14:34 +04:00
break ;
2006-11-27 17:28:48 +03:00
case TFRC_SSTATE_NO_SENT :
2006-12-10 05:04:43 +03:00
/* XXX when implementing bidirectional rx/tx check this again */
DCCP_WARN ( " Illegal ACK received - no packet sent \n " ) ;
2006-11-27 17:28:48 +03:00
/* fall through */
case TFRC_SSTATE_TERM : /* ignore feedback when closing */
2005-08-10 07:14:34 +04:00
break ;
}
}
static int ccid3_hc_tx_parse_options ( struct sock * sk , unsigned char option ,
2005-08-27 10:51:58 +04:00
unsigned char len , u16 idx ,
unsigned char * value )
2005-08-10 07:14:34 +04:00
{
int rc = 0 ;
2005-09-09 09:40:58 +04:00
const struct dccp_sock * dp = dccp_sk ( sk ) ;
struct ccid3_hc_tx_sock * hctx = ccid3_hc_tx_sk ( sk ) ;
2005-08-10 07:14:34 +04:00
struct ccid3_options_received * opt_recv ;
2005-09-10 03:01:25 +04:00
BUG_ON ( hctx = = NULL ) ;
2005-08-10 07:14:34 +04:00
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 :
2005-09-10 03:01:25 +04:00
if ( unlikely ( len ! = 4 ) ) {
2006-11-20 23:39:23 +03:00
DCCP_WARN ( " %s, sk=%p, invalid len %d "
" for TFRC_OPT_LOSS_EVENT_RATE \n " ,
dccp_role ( sk ) , sk , len ) ;
2005-08-10 07:14:34 +04:00
rc = - EINVAL ;
} else {
2006-03-21 06:23:32 +03:00
opt_recv - > ccid3or_loss_event_rate = ntohl ( * ( __be32 * ) value ) ;
2005-08-10 07:14:34 +04:00
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 :
2005-09-10 03:01:25 +04:00
if ( unlikely ( len ! = 4 ) ) {
2006-11-20 23:39:23 +03:00
DCCP_WARN ( " %s, sk=%p, invalid len %d "
" for TFRC_OPT_RECEIVE_RATE \n " ,
dccp_role ( sk ) , sk , len ) ;
2005-08-10 07:14:34 +04:00
rc = - EINVAL ;
} else {
2006-03-21 06:23:32 +03:00
opt_recv - > ccid3or_receive_rate = ntohl ( * ( __be32 * ) value ) ;
2005-08-10 07:14:34 +04:00
ccid3_pr_debug ( " %s, sk=%p, RECEIVE_RATE=%u \n " ,
dccp_role ( sk ) , sk ,
opt_recv - > ccid3or_receive_rate ) ;
}
break ;
}
return rc ;
}
2006-03-21 06:21:44 +03:00
static int ccid3_hc_tx_init ( struct ccid * ccid , struct sock * sk )
2005-08-10 07:14:34 +04:00
{
2006-03-21 06:21:44 +03:00
struct ccid3_hc_tx_sock * hctx = ccid_priv ( ccid ) ;
2005-08-10 07:14:34 +04:00
2006-11-29 00:22:33 +03:00
hctx - > ccid3hctx_s = 0 ;
2006-12-10 05:06:01 +03:00
hctx - > ccid3hctx_rtt = 0 ;
2005-08-10 07:14:34 +04:00
hctx - > ccid3hctx_state = TFRC_SSTATE_NO_SENT ;
INIT_LIST_HEAD ( & hctx - > ccid3hctx_hist ) ;
2006-03-21 04:35:13 +03:00
hctx - > ccid3hctx_no_feedback_timer . function = ccid3_hc_tx_no_feedback_timer ;
hctx - > ccid3hctx_no_feedback_timer . data = ( unsigned long ) sk ;
2005-08-10 07:14:34 +04:00
init_timer ( & hctx - > ccid3hctx_no_feedback_timer ) ;
return 0 ;
}
static void ccid3_hc_tx_exit ( struct sock * sk )
{
2005-09-09 09:40:58 +04:00
struct ccid3_hc_tx_sock * hctx = ccid3_hc_tx_sk ( sk ) ;
2005-08-10 07:14:34 +04:00
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 19:59:38 +04:00
dccp_tx_hist_purge ( ccid3_tx_hist , & hctx - > ccid3hctx_hist ) ;
2005-08-10 07:14:34 +04:00
}
/*
* RX Half Connection methods
*/
2006-11-20 23:28:09 +03:00
# ifdef CONFIG_IP_DCCP_CCID3_DEBUG
2005-08-10 07:14:34 +04:00
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
2006-03-21 08:58:56 +03:00
static void ccid3_hc_rx_set_state ( struct sock * sk ,
enum ccid3_hc_rx_states state )
2005-08-10 07:14:34 +04:00
{
2005-09-09 09:40:58 +04:00
struct ccid3_hc_rx_sock * hcrx = ccid3_hc_rx_sk ( sk ) ;
2005-08-10 07:14:34 +04:00
enum ccid3_hc_rx_states oldstate = hcrx - > ccid3hcrx_state ;
ccid3_pr_debug ( " %s(%p) %-8.8s -> %s \n " ,
2005-08-27 10:51:58 +04:00
dccp_role ( sk ) , sk , ccid3_rx_state_name ( oldstate ) ,
ccid3_rx_state_name ( state ) ) ;
2005-08-10 07:14:34 +04:00
WARN_ON ( state = = oldstate ) ;
hcrx - > ccid3hcrx_state = state ;
}
2006-11-29 00:22:33 +03:00
static inline void ccid3_hc_rx_update_s ( struct ccid3_hc_rx_sock * hcrx , int len )
{
if ( unlikely ( len = = 0 ) ) /* don't update on empty packets (e.g. ACKs) */
ccid3_pr_debug ( " Packet payload length is 0 - not updating \n " ) ;
else
hcrx - > ccid3hcrx_s = hcrx - > ccid3hcrx_s = = 0 ? len :
( 9 * hcrx - > ccid3hcrx_s + len ) / 10 ;
}
2005-08-10 07:14:34 +04:00
static void ccid3_hc_rx_send_feedback ( struct sock * sk )
{
2005-09-09 09:40:58 +04:00
struct ccid3_hc_rx_sock * hcrx = ccid3_hc_rx_sk ( sk ) ;
2005-08-10 07:14:34 +04:00
struct dccp_sock * dp = dccp_sk ( sk ) ;
2005-08-10 19:59:38 +04:00
struct dccp_rx_hist_entry * packet ;
2005-08-28 01:18:18 +04:00
struct timeval now ;
2006-12-10 05:03:51 +03:00
suseconds_t delta ;
2005-08-10 07:14:34 +04:00
ccid3_pr_debug ( " %s, sk=%p \n " , dccp_role ( sk ) , sk ) ;
2005-09-09 09:38:35 +04:00
dccp_timestamp ( sk , & now ) ;
2005-08-28 01:18:18 +04:00
2005-08-10 07:14:34 +04:00
switch ( hcrx - > ccid3hcrx_state ) {
case TFRC_RSTATE_NO_DATA :
hcrx - > ccid3hcrx_x_recv = 0 ;
break ;
2006-12-10 05:03:51 +03:00
case TFRC_RSTATE_DATA :
delta = timeval_delta ( & now ,
& hcrx - > ccid3hcrx_tstamp_last_feedback ) ;
DCCP_BUG_ON ( delta < 0 ) ;
2006-12-10 05:02:51 +03:00
hcrx - > ccid3hcrx_x_recv =
scaled_div32 ( hcrx - > ccid3hcrx_bytes_recv , delta ) ;
2005-08-10 07:14:34 +04:00
break ;
2006-11-20 23:39:23 +03:00
case TFRC_RSTATE_TERM :
DCCP_BUG ( " Illegal %s state TERM, sk=%p " , dccp_role ( sk ) , sk ) ;
2005-08-10 07:14:34 +04:00
return ;
}
2005-08-10 19:59:38 +04:00
packet = dccp_rx_hist_find_data_packet ( & hcrx - > ccid3hcrx_hist ) ;
2005-09-10 03:01:25 +04:00
if ( unlikely ( packet = = NULL ) ) {
2006-11-20 23:39:23 +03:00
DCCP_WARN ( " %s, sk=%p, no data packet in history! \n " ,
dccp_role ( sk ) , sk ) ;
2005-08-10 07:14:34 +04:00
return ;
}
2005-08-28 01:18:18 +04:00
hcrx - > ccid3hcrx_tstamp_last_feedback = now ;
2006-08-27 10:40:50 +04:00
hcrx - > ccid3hcrx_ccval_last_counter = packet - > dccphrx_ccval ;
2005-08-10 07:14:34 +04:00
hcrx - > ccid3hcrx_bytes_recv = 0 ;
2006-12-10 05:03:51 +03:00
/* Elapsed time information [RFC 4340, 13.2] in units of 10 * usecs */
delta = timeval_delta ( & now , & packet - > dccphrx_tstamp ) ;
DCCP_BUG_ON ( delta < 0 ) ;
hcrx - > ccid3hcrx_elapsed_time = delta / 10 ;
2006-12-10 04:59:14 +03:00
2005-08-10 07:14:34 +04:00
if ( hcrx - > ccid3hcrx_p = = 0 )
2006-12-10 04:59:14 +03:00
hcrx - > ccid3hcrx_pinv = ~ 0U ; /* see RFC 4342, 8.5 */
else if ( hcrx - > ccid3hcrx_p > 1000000 ) {
DCCP_WARN ( " p (%u) > 100%% \n " , hcrx - > ccid3hcrx_p ) ;
hcrx - > ccid3hcrx_pinv = 1 ; /* use 100% in this case */
} else
2005-08-10 07:14:34 +04:00
hcrx - > ccid3hcrx_pinv = 1000000 / hcrx - > ccid3hcrx_p ;
2006-12-10 04:59:14 +03:00
2005-09-09 09:30:07 +04:00
dp - > dccps_hc_rx_insert_options = 1 ;
2005-08-10 07:14:34 +04:00
dccp_send_ack ( sk ) ;
}
2006-03-21 09:32:06 +03:00
static int ccid3_hc_rx_insert_options ( struct sock * sk , struct sk_buff * skb )
2005-08-10 07:14:34 +04:00
{
2005-09-10 03:01:25 +04:00
const struct ccid3_hc_rx_sock * hcrx = ccid3_hc_rx_sk ( sk ) ;
2006-03-21 06:23:32 +03:00
__be32 x_recv , pinv ;
2005-08-10 07:14:34 +04:00
2005-09-10 03:01:25 +04:00
BUG_ON ( hcrx = = NULL ) ;
if ( ! ( sk - > sk_state = = DCCP_OPEN | | sk - > sk_state = = DCCP_PARTOPEN ) )
2006-03-21 09:32:06 +03:00
return 0 ;
2005-08-10 07:14:34 +04:00
2006-08-27 10:40:50 +04:00
DCCP_SKB_CB ( skb ) - > dccpd_ccval = hcrx - > ccid3hcrx_ccval_last_counter ;
2005-08-24 08:51:59 +04:00
if ( dccp_packet_without_ack ( skb ) )
2006-03-21 09:32:06 +03:00
return 0 ;
2005-08-24 08:51:59 +04:00
x_recv = htonl ( hcrx - > ccid3hcrx_x_recv ) ;
pinv = htonl ( hcrx - > ccid3hcrx_pinv ) ;
2006-03-21 09:32:06 +03:00
if ( ( hcrx - > ccid3hcrx_elapsed_time ! = 0 & &
dccp_insert_option_elapsed_time ( sk , skb ,
hcrx - > ccid3hcrx_elapsed_time ) ) | |
dccp_insert_option_timestamp ( sk , skb ) | |
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 ) ) )
return - 1 ;
return 0 ;
2005-08-10 07:14:34 +04:00
}
/* calculate first loss interval
*
* returns estimated loss interval in usecs */
static u32 ccid3_hc_rx_calc_first_li ( struct sock * sk )
{
2005-09-09 09:40:58 +04:00
struct ccid3_hc_rx_sock * hcrx = ccid3_hc_rx_sk ( sk ) ;
2005-08-10 19:59:38 +04:00
struct dccp_rx_hist_entry * entry , * next , * tail = NULL ;
2006-12-10 05:03:51 +03:00
u32 x_recv , p ;
suseconds_t rtt , delta ;
2005-08-28 01:18:18 +04:00
struct timeval tstamp = { 0 , } ;
2005-08-10 07:14:34 +04:00
int interval = 0 ;
int win_count = 0 ;
int step = 0 ;
2006-12-10 05:03:30 +03:00
u64 fval ;
2005-08-10 07:14:34 +04:00
2005-08-10 19:59:38 +04:00
list_for_each_entry_safe ( entry , next , & hcrx - > ccid3hcrx_hist ,
dccphrx_node ) {
if ( dccp_rx_hist_entry_data_packet ( entry ) ) {
2005-08-10 07:14:34 +04:00
tail = entry ;
switch ( step ) {
case 0 :
2005-08-10 19:59:38 +04:00
tstamp = entry - > dccphrx_tstamp ;
2005-08-14 03:34:23 +04:00
win_count = entry - > dccphrx_ccval ;
2005-08-10 07:14:34 +04:00
step = 1 ;
break ;
case 1 :
2005-08-14 03:34:23 +04:00
interval = win_count - entry - > dccphrx_ccval ;
2005-08-10 07:14:34 +04:00
if ( interval < 0 )
interval + = TFRC_WIN_COUNT_LIMIT ;
if ( interval > 4 )
goto found ;
break ;
}
}
}
2005-09-10 03:01:25 +04:00
if ( unlikely ( step = = 0 ) ) {
2006-11-20 23:39:23 +03:00
DCCP_WARN ( " %s, sk=%p, packet history has no data packets! \n " ,
dccp_role ( sk ) , sk ) ;
2005-08-10 07:14:34 +04:00
return ~ 0 ;
}
2005-09-10 03:01:25 +04:00
if ( unlikely ( interval = = 0 ) ) {
2006-11-20 23:39:23 +03:00
DCCP_WARN ( " %s, sk=%p, Could not find a win_count interval > 0. "
" Defaulting to 1 \n " , dccp_role ( sk ) , sk ) ;
2005-08-10 07:14:34 +04:00
interval = 1 ;
}
found :
2006-08-27 10:40:50 +04:00
if ( ! tail ) {
2006-11-20 23:39:23 +03:00
DCCP_CRIT ( " tail is null \n " ) ;
2006-08-27 10:40:50 +04:00
return ~ 0 ;
}
2006-12-10 05:03:51 +03:00
delta = timeval_delta ( & tstamp , & tail - > dccphrx_tstamp ) ;
DCCP_BUG_ON ( delta < 0 ) ;
rtt = delta * 4 / interval ;
ccid3_pr_debug ( " %s, sk=%p, approximated RTT to %ldus \n " ,
2005-08-10 07:14:34 +04:00
dccp_role ( sk ) , sk , rtt ) ;
2006-11-20 23:39:23 +03:00
2006-12-10 05:03:30 +03:00
/*
* Determine the length of the first loss interval via inverse lookup .
* Assume that X_recv can be computed by the throughput equation
* s
* X_recv = - - - - - - - -
* R * fval
* Find some p such that f ( p ) = fval ; return 1 / p [ RFC 3448 , 6.3 .1 ] .
*/
if ( rtt = = 0 ) { /* would result in divide-by-zero */
DCCP_WARN ( " RTT==0, returning 1/p = 1 \n " ) ;
return 1000000 ;
2006-11-20 23:39:23 +03:00
}
2005-08-10 07:14:34 +04:00
2005-09-09 09:38:35 +04:00
dccp_timestamp ( sk , & tstamp ) ;
delta = timeval_delta ( & tstamp , & hcrx - > ccid3hcrx_tstamp_last_feedback ) ;
2006-12-10 05:03:51 +03:00
DCCP_BUG_ON ( delta < = 0 ) ;
2005-08-10 07:14:34 +04:00
2006-12-10 05:03:51 +03:00
x_recv = scaled_div32 ( hcrx - > ccid3hcrx_bytes_recv , delta ) ;
2006-12-10 05:03:30 +03:00
if ( x_recv = = 0 ) { /* would also trigger divide-by-zero */
DCCP_WARN ( " X_recv==0 \n " ) ;
if ( ( x_recv = hcrx - > ccid3hcrx_x_recv ) = = 0 ) {
DCCP_BUG ( " stored value of X_recv is zero " ) ;
return 1000000 ;
}
2006-08-27 10:40:50 +04:00
}
2006-12-10 05:03:30 +03:00
fval = scaled_div ( hcrx - > ccid3hcrx_s , rtt ) ;
fval = scaled_div32 ( fval , x_recv ) ;
2005-08-28 07:47:15 +04:00
p = tfrc_calc_x_reverse_lookup ( fval ) ;
2006-12-10 05:03:30 +03:00
2005-08-27 10:51:58 +04: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-10 07:14:34 +04: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 )
{
2005-09-09 09:40:58 +04:00
struct ccid3_hc_rx_sock * hcrx = ccid3_hc_rx_sk ( sk ) ;
2006-08-30 04:50:19 +04:00
struct dccp_li_hist_entry * head ;
2006-08-27 10:40:50 +04:00
u64 seq_temp ;
2005-08-10 07:14:34 +04:00
2006-08-27 10:40:50 +04:00
if ( list_empty ( & hcrx - > ccid3hcrx_li_hist ) ) {
if ( ! dccp_li_hist_interval_new ( ccid3_li_hist ,
& hcrx - > ccid3hcrx_li_hist , seq_loss , win_loss ) )
return ;
2005-08-10 07:14:34 +04:00
2006-08-30 04:50:19 +04:00
head = list_entry ( hcrx - > ccid3hcrx_li_hist . next ,
struct dccp_li_hist_entry , dccplih_node ) ;
head - > dccplih_interval = ccid3_hc_rx_calc_first_li ( sk ) ;
2006-08-27 10:40:50 +04:00
} else {
struct dccp_li_hist_entry * entry ;
struct list_head * tail ;
2006-08-30 04:50:19 +04:00
head = list_entry ( hcrx - > ccid3hcrx_li_hist . next ,
struct dccp_li_hist_entry , dccplih_node ) ;
2006-08-27 10:40:50 +04:00
/* FIXME win count check removed as was wrong */
/* should make this check with receive history */
/* and compare there as per section 10.2 of RFC4342 */
/* new loss event detected */
/* calculate last interval length */
seq_temp = dccp_delta_seqno ( head - > dccplih_seqno , seq_loss ) ;
2006-12-07 07:33:16 +03:00
entry = dccp_li_hist_entry_new ( ccid3_li_hist , GFP_ATOMIC ) ;
2006-08-27 10:40:50 +04:00
if ( entry = = NULL ) {
2006-11-20 23:39:23 +03:00
DCCP_BUG ( " out of memory - can not allocate entry " ) ;
2005-08-28 06:03:09 +04:00
return ;
2006-08-27 10:40:50 +04:00
}
list_add ( & entry - > dccplih_node , & hcrx - > ccid3hcrx_li_hist ) ;
tail = hcrx - > ccid3hcrx_li_hist . prev ;
list_del ( tail ) ;
kmem_cache_free ( ccid3_li_hist - > dccplih_slab , tail ) ;
/* Create the newest interval */
entry - > dccplih_seqno = seq_loss ;
entry - > dccplih_interval = seq_temp ;
entry - > dccplih_win_count = win_loss ;
}
2005-08-10 07:14:34 +04:00
}
2006-08-27 10:40:50 +04:00
static int ccid3_hc_rx_detect_loss ( struct sock * sk ,
struct dccp_rx_hist_entry * packet )
2005-08-10 07:14:34 +04:00
{
2005-09-09 09:40:58 +04:00
struct ccid3_hc_rx_sock * hcrx = ccid3_hc_rx_sk ( sk ) ;
2006-08-27 10:40:50 +04:00
struct dccp_rx_hist_entry * rx_hist = dccp_rx_hist_head ( & hcrx - > ccid3hcrx_hist ) ;
u64 seqno = packet - > dccphrx_seqno ;
u64 tmp_seqno ;
int loss = 0 ;
u8 ccval ;
tmp_seqno = hcrx - > ccid3hcrx_seqno_nonloss ;
if ( ! rx_hist | |
follows48 ( packet - > dccphrx_seqno , hcrx - > ccid3hcrx_seqno_nonloss ) ) {
hcrx - > ccid3hcrx_seqno_nonloss = seqno ;
hcrx - > ccid3hcrx_ccval_nonloss = packet - > dccphrx_ccval ;
goto detect_out ;
}
2005-08-10 07:14:34 +04:00
2006-08-27 10:40:50 +04:00
while ( dccp_delta_seqno ( hcrx - > ccid3hcrx_seqno_nonloss , seqno )
> TFRC_RECV_NUM_LATE_LOSS ) {
loss = 1 ;
ccid3_hc_rx_update_li ( sk , hcrx - > ccid3hcrx_seqno_nonloss ,
hcrx - > ccid3hcrx_ccval_nonloss ) ;
tmp_seqno = hcrx - > ccid3hcrx_seqno_nonloss ;
dccp_inc_seqno ( & tmp_seqno ) ;
hcrx - > ccid3hcrx_seqno_nonloss = tmp_seqno ;
dccp_inc_seqno ( & tmp_seqno ) ;
while ( dccp_rx_hist_find_entry ( & hcrx - > ccid3hcrx_hist ,
tmp_seqno , & ccval ) ) {
hcrx - > ccid3hcrx_seqno_nonloss = tmp_seqno ;
hcrx - > ccid3hcrx_ccval_nonloss = ccval ;
dccp_inc_seqno ( & tmp_seqno ) ;
}
}
/* FIXME - this code could be simplified with above while */
/* but works at moment */
if ( follows48 ( packet - > dccphrx_seqno , hcrx - > ccid3hcrx_seqno_nonloss ) ) {
hcrx - > ccid3hcrx_seqno_nonloss = seqno ;
hcrx - > ccid3hcrx_ccval_nonloss = packet - > dccphrx_ccval ;
}
detect_out :
dccp_rx_hist_add_packet ( ccid3_rx_hist , & hcrx - > ccid3hcrx_hist ,
& hcrx - > ccid3hcrx_li_hist , packet ,
hcrx - > ccid3hcrx_seqno_nonloss ) ;
return loss ;
2005-08-10 07:14:34 +04:00
}
static void ccid3_hc_rx_packet_recv ( struct sock * sk , struct sk_buff * skb )
{
2005-09-09 09:40:58 +04:00
struct ccid3_hc_rx_sock * hcrx = ccid3_hc_rx_sk ( sk ) ;
2005-08-24 08:51:59 +04:00
const struct dccp_options_received * opt_recv ;
2005-08-10 19:59:38 +04:00
struct dccp_rx_hist_entry * packet ;
2005-08-10 07:14:34 +04:00
struct timeval now ;
2006-12-10 05:03:51 +03:00
u32 p_prev , rtt_prev ;
suseconds_t r_sample , t_elapsed ;
2006-11-29 00:22:33 +03:00
int loss , payload_size ;
2005-08-27 10:51:58 +04:00
2006-11-20 23:39:23 +03:00
BUG_ON ( hcrx = = NULL ) ;
2005-08-10 07:14:34 +04:00
2005-09-09 09:40:58 +04:00
opt_recv = & dccp_sk ( sk ) - > dccps_options_received ;
2005-08-24 08:51:59 +04:00
2005-08-10 07:14:34 +04: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-24 08:51:59 +04:00
if ( opt_recv - > dccpor_timestamp_echo = = 0 )
2005-08-10 07:14:34 +04:00
break ;
2006-08-27 10:40:50 +04:00
rtt_prev = hcrx - > ccid3hcrx_rtt ;
2005-09-09 09:38:35 +04:00
dccp_timestamp ( sk , & now ) ;
2005-09-09 09:34:10 +04:00
timeval_sub_usecs ( & now , opt_recv - > dccpor_timestamp_echo * 10 ) ;
r_sample = timeval_usecs ( & now ) ;
t_elapsed = opt_recv - > dccpor_elapsed_time * 10 ;
2006-12-10 05:03:51 +03:00
DCCP_BUG_ON ( r_sample < 0 ) ;
2005-09-09 09:34:10 +04:00
if ( unlikely ( r_sample < = t_elapsed ) )
2006-12-10 05:03:51 +03:00
DCCP_WARN ( " r_sample=%ldus, t_elapsed=%ldus \n " ,
2006-11-20 23:39:23 +03:00
r_sample , t_elapsed ) ;
2005-09-09 09:34:10 +04:00
else
r_sample - = t_elapsed ;
2006-12-10 05:06:32 +03:00
CCID3_RTT_SANITY_CHECK ( r_sample ) ;
2005-09-09 09:34:10 +04:00
if ( hcrx - > ccid3hcrx_state = = TFRC_RSTATE_NO_DATA )
hcrx - > ccid3hcrx_rtt = r_sample ;
else
hcrx - > ccid3hcrx_rtt = ( hcrx - > ccid3hcrx_rtt * 9 ) / 10 +
r_sample / 10 ;
2006-08-27 10:40:50 +04:00
if ( rtt_prev ! = hcrx - > ccid3hcrx_rtt )
ccid3_pr_debug ( " %s, New RTT=%uus, elapsed time=%u \n " ,
2005-08-24 08:51:59 +04:00
dccp_role ( sk ) , hcrx - > ccid3hcrx_rtt ,
opt_recv - > dccpor_elapsed_time ) ;
2005-08-10 07:14:34 +04:00
break ;
case DCCP_PKT_DATA :
break ;
2005-09-10 03:01:25 +04:00
default : /* We're not interested in other packet types, move along */
2005-08-10 07:14:34 +04:00
return ;
}
2005-09-09 09:38:35 +04:00
packet = dccp_rx_hist_entry_new ( ccid3_rx_hist , sk , opt_recv - > dccpor_ndp ,
2006-12-07 07:33:16 +03:00
skb , GFP_ATOMIC ) ;
2005-09-10 03:01:25 +04:00
if ( unlikely ( packet = = NULL ) ) {
2006-11-20 23:39:23 +03:00
DCCP_WARN ( " %s, sk=%p, Not enough mem to add rx packet "
" to history, consider it lost! \n " , dccp_role ( sk ) , sk ) ;
2005-08-10 07:14:34 +04:00
return ;
}
2006-08-27 10:40:50 +04:00
loss = ccid3_hc_rx_detect_loss ( sk , packet ) ;
2005-08-10 07:14:34 +04:00
if ( DCCP_SKB_CB ( skb ) - > dccpd_type = = DCCP_PKT_ACK )
return ;
2006-11-29 00:22:33 +03:00
payload_size = skb - > len - dccp_hdr ( skb ) - > dccph_doff * 4 ;
ccid3_hc_rx_update_s ( hcrx , payload_size ) ;
2005-08-10 07:14:34 +04:00
switch ( hcrx - > ccid3hcrx_state ) {
case TFRC_RSTATE_NO_DATA :
2005-08-27 10:51:58 +04: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-10 07:14:34 +04:00
ccid3_hc_rx_send_feedback ( sk ) ;
ccid3_hc_rx_set_state ( sk , TFRC_RSTATE_DATA ) ;
return ;
case TFRC_RSTATE_DATA :
2006-11-29 00:22:33 +03:00
hcrx - > ccid3hcrx_bytes_recv + = payload_size ;
2006-08-27 10:40:50 +04:00
if ( loss )
2005-08-28 01:18:18 +04:00
break ;
2005-09-09 09:38:35 +04:00
dccp_timestamp ( sk , & now ) ;
2006-12-10 05:03:51 +03:00
if ( timeval_delta ( & now , & hcrx - > ccid3hcrx_tstamp_last_ack ) -
( suseconds_t ) hcrx - > ccid3hcrx_rtt > = 0 ) {
2005-08-28 01:18:18 +04:00
hcrx - > ccid3hcrx_tstamp_last_ack = now ;
ccid3_hc_rx_send_feedback ( sk ) ;
2005-08-10 07:14:34 +04:00
}
2005-08-28 01:18:18 +04:00
return ;
2006-11-20 23:39:23 +03:00
case TFRC_RSTATE_TERM :
DCCP_BUG ( " Illegal %s state TERM, sk=%p " , dccp_role ( sk ) , sk ) ;
2005-08-10 07:14:34 +04:00
return ;
}
/* Dealing with packet loss */
2005-08-24 08:51:59 +04:00
ccid3_pr_debug ( " %s, sk=%p(%s), data loss! Reacting... \n " ,
dccp_role ( sk ) , sk , dccp_state_name ( sk - > sk_state ) ) ;
2005-08-10 07:14:34 +04:00
p_prev = hcrx - > ccid3hcrx_p ;
/* Calculate loss event rate */
2006-03-04 04:54:46 +03:00
if ( ! list_empty ( & hcrx - > ccid3hcrx_li_hist ) ) {
u32 i_mean = dccp_li_hist_calc_i_mean ( & hcrx - > ccid3hcrx_li_hist ) ;
2005-08-10 07:14:34 +04:00
/* Scaling up by 1000000 as fixed decimal */
2006-03-04 04:54:46 +03:00
if ( i_mean ! = 0 )
hcrx - > ccid3hcrx_p = 1000000 / i_mean ;
2006-11-20 23:39:23 +03:00
} else
DCCP_BUG ( " empty loss history " ) ;
2005-08-10 07:14:34 +04:00
if ( hcrx - > ccid3hcrx_p > p_prev ) {
ccid3_hc_rx_send_feedback ( sk ) ;
return ;
}
}
2006-03-21 06:21:44 +03:00
static int ccid3_hc_rx_init ( struct ccid * ccid , struct sock * sk )
2005-08-10 07:14:34 +04:00
{
2006-03-21 06:21:44 +03:00
struct ccid3_hc_rx_sock * hcrx = ccid_priv ( ccid ) ;
2005-08-10 07:14:34 +04:00
ccid3_pr_debug ( " %s, sk=%p \n " , dccp_role ( sk ) , sk ) ;
hcrx - > ccid3hcrx_state = TFRC_RSTATE_NO_DATA ;
INIT_LIST_HEAD ( & hcrx - > ccid3hcrx_hist ) ;
2005-08-28 06:03:09 +04:00
INIT_LIST_HEAD ( & hcrx - > ccid3hcrx_li_hist ) ;
2005-09-09 09:38:35 +04:00
dccp_timestamp ( sk , & hcrx - > ccid3hcrx_tstamp_last_ack ) ;
2005-09-09 09:37:05 +04:00
hcrx - > ccid3hcrx_tstamp_last_feedback = hcrx - > ccid3hcrx_tstamp_last_ack ;
2006-11-29 00:22:33 +03:00
hcrx - > ccid3hcrx_s = 0 ;
2006-12-10 05:06:01 +03:00
hcrx - > ccid3hcrx_rtt = 0 ;
2005-08-10 07:14:34 +04:00
return 0 ;
}
static void ccid3_hc_rx_exit ( struct sock * sk )
{
2005-09-09 09:40:58 +04:00
struct ccid3_hc_rx_sock * hcrx = ccid3_hc_rx_sk ( sk ) ;
2005-08-10 07:14:34 +04:00
2005-09-10 03:01:25 +04:00
BUG_ON ( hcrx = = NULL ) ;
2005-08-10 07:14:34 +04:00
ccid3_hc_rx_set_state ( sk , TFRC_RSTATE_TERM ) ;
/* Empty packet history */
2005-08-10 19:59:38 +04:00
dccp_rx_hist_purge ( ccid3_rx_hist , & hcrx - > ccid3hcrx_hist ) ;
2005-08-10 07:14:34 +04:00
/* Empty loss interval history */
2005-08-28 06:03:09 +04:00
dccp_li_hist_purge ( ccid3_li_hist , & hcrx - > ccid3hcrx_li_hist ) ;
2005-08-10 07:14:34 +04:00
}
[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-24 08:52:35 +04:00
static void ccid3_hc_rx_get_info ( struct sock * sk , struct tcp_info * info )
{
2005-09-09 09:40:58 +04:00
const struct ccid3_hc_rx_sock * hcrx = ccid3_hc_rx_sk ( sk ) ;
[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-24 08:52:35 +04:00
2005-09-13 01:16:58 +04:00
/* Listen socks doesn't have a private CCID block */
if ( sk - > sk_state = = DCCP_LISTEN )
return ;
2005-09-10 03:01:25 +04:00
BUG_ON ( hcrx = = 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-24 08:52:35 +04:00
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 )
{
2005-09-09 09:40:58 +04:00
const struct ccid3_hc_tx_sock * hctx = ccid3_hc_tx_sk ( sk ) ;
[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-24 08:52:35 +04:00
2005-09-13 01:16:58 +04:00
/* Listen socks doesn't have a private CCID block */
if ( sk - > sk_state = = DCCP_LISTEN )
return ;
2005-09-10 03:01:25 +04:00
BUG_ON ( hctx = = 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-24 08:52:35 +04:00
info - > tcpi_rto = hctx - > ccid3hctx_t_rto ;
info - > tcpi_rtt = hctx - > ccid3hctx_rtt ;
}
2005-09-18 11:19:32 +04:00
static int ccid3_hc_rx_getsockopt ( struct sock * sk , const int optname , int len ,
u32 __user * optval , int __user * optlen )
{
const struct ccid3_hc_rx_sock * hcrx = ccid3_hc_rx_sk ( sk ) ;
const void * val ;
/* Listen socks doesn't have a private CCID block */
if ( sk - > sk_state = = DCCP_LISTEN )
return - EINVAL ;
switch ( optname ) {
case DCCP_SOCKOPT_CCID_RX_INFO :
if ( len < sizeof ( hcrx - > ccid3hcrx_tfrc ) )
return - EINVAL ;
len = sizeof ( hcrx - > ccid3hcrx_tfrc ) ;
val = & hcrx - > ccid3hcrx_tfrc ;
break ;
default :
return - ENOPROTOOPT ;
}
if ( put_user ( len , optlen ) | | copy_to_user ( optval , val , len ) )
return - EFAULT ;
return 0 ;
}
static int ccid3_hc_tx_getsockopt ( struct sock * sk , const int optname , int len ,
u32 __user * optval , int __user * optlen )
{
const struct ccid3_hc_tx_sock * hctx = ccid3_hc_tx_sk ( sk ) ;
const void * val ;
/* Listen socks doesn't have a private CCID block */
if ( sk - > sk_state = = DCCP_LISTEN )
return - EINVAL ;
switch ( optname ) {
case DCCP_SOCKOPT_CCID_TX_INFO :
if ( len < sizeof ( hctx - > ccid3hctx_tfrc ) )
return - EINVAL ;
len = sizeof ( hctx - > ccid3hctx_tfrc ) ;
val = & hctx - > ccid3hctx_tfrc ;
break ;
default :
return - ENOPROTOOPT ;
}
if ( put_user ( len , optlen ) | | copy_to_user ( optval , val , len ) )
return - EFAULT ;
return 0 ;
}
2006-03-21 06:21:44 +03:00
static struct ccid_operations ccid3 = {
2006-09-22 06:26:44 +04:00
. ccid_id = DCCPC_CCID3 ,
2005-08-10 07:14:34 +04:00
. ccid_name = " ccid3 " ,
. ccid_owner = THIS_MODULE ,
2006-03-21 06:21:44 +03:00
. ccid_hc_tx_obj_size = sizeof ( struct ccid3_hc_tx_sock ) ,
2005-08-10 07:14:34 +04:00
. 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_parse_options = ccid3_hc_tx_parse_options ,
2006-03-21 06:21:44 +03:00
. ccid_hc_rx_obj_size = sizeof ( struct ccid3_hc_rx_sock ) ,
2005-08-10 07:14:34 +04:00
. 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-24 08:52:35 +04:00
. ccid_hc_rx_get_info = ccid3_hc_rx_get_info ,
. ccid_hc_tx_get_info = ccid3_hc_tx_get_info ,
2005-09-18 11:19:32 +04:00
. ccid_hc_rx_getsockopt = ccid3_hc_rx_getsockopt ,
. ccid_hc_tx_getsockopt = ccid3_hc_tx_getsockopt ,
2005-08-10 07:14:34 +04:00
} ;
2006-11-20 23:28:09 +03:00
# ifdef CONFIG_IP_DCCP_CCID3_DEBUG
2005-08-10 07:14:34 +04:00
module_param ( ccid3_debug , int , 0444 ) ;
MODULE_PARM_DESC ( ccid3_debug , " Enable debug messages " ) ;
2006-11-20 23:28:09 +03:00
# endif
2005-08-10 07:14:34 +04:00
static __init int ccid3_module_init ( void )
{
2005-08-10 19:59:38 +04:00
int rc = - ENOBUFS ;
2005-08-10 07:14:34 +04:00
2005-08-10 19:59:38 +04:00
ccid3_rx_hist = dccp_rx_hist_new ( " ccid3 " ) ;
if ( ccid3_rx_hist = = NULL )
2005-08-10 07:14:34 +04:00
goto out ;
2005-08-10 19:59:38 +04:00
ccid3_tx_hist = dccp_tx_hist_new ( " ccid3 " ) ;
if ( ccid3_tx_hist = = NULL )
goto out_free_rx ;
2005-08-10 07:14:34 +04:00
2005-08-28 06:03:09 +04:00
ccid3_li_hist = dccp_li_hist_new ( " ccid3 " ) ;
if ( ccid3_li_hist = = NULL )
2005-08-10 19:59:38 +04:00
goto out_free_tx ;
2005-08-10 07:14:34 +04:00
rc = ccid_register ( & ccid3 ) ;
if ( rc ! = 0 )
goto out_free_loss_interval_history ;
out :
return rc ;
2005-08-10 19:59:38 +04:00
2005-08-10 07:14:34 +04:00
out_free_loss_interval_history :
2005-08-28 06:03:09 +04:00
dccp_li_hist_delete ( ccid3_li_hist ) ;
ccid3_li_hist = NULL ;
2005-08-10 19:59:38 +04: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-10 07:14:34 +04:00
goto out ;
}
module_init ( ccid3_module_init ) ;
static __exit void ccid3_module_exit ( void )
{
ccid_unregister ( & ccid3 ) ;
2005-08-10 19:59:38 +04:00
if ( ccid3_tx_hist ! = NULL ) {
dccp_tx_hist_delete ( ccid3_tx_hist ) ;
ccid3_tx_hist = NULL ;
2005-08-10 07:14:34 +04:00
}
2005-08-10 19:59:38 +04:00
if ( ccid3_rx_hist ! = NULL ) {
dccp_rx_hist_delete ( ccid3_rx_hist ) ;
ccid3_rx_hist = NULL ;
2005-08-10 07:14:34 +04:00
}
2005-08-28 06:03:09 +04:00
if ( ccid3_li_hist ! = NULL ) {
dccp_li_hist_delete ( ccid3_li_hist ) ;
ccid3_li_hist = NULL ;
2005-08-10 07:14:34 +04:00
}
}
module_exit ( ccid3_module_exit ) ;
2006-08-27 06:01:30 +04:00
MODULE_AUTHOR ( " Ian McDonald <ian.mcdonald@jandi.co.nz>, "
2005-08-27 10:51:58 +04:00
" Arnaldo Carvalho de Melo <acme@ghostprotocols.net> " ) ;
2005-08-10 07:14:34 +04:00
MODULE_DESCRIPTION ( " DCCP TFRC CCID3 CCID " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " net-dccp-ccid-3 " ) ;