2005-08-09 20:14:34 -07:00
/*
* net / dccp / ccids / ccid3 . h
*
2007-12-06 13:18:11 -02:00
* Copyright ( c ) 2005 - 7 The University of Waikato , Hamilton , New Zealand .
* Copyright ( c ) 2007 The University of Aberdeen , Scotland , UK
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/
2006-08-26 19:01:30 -07:00
* or e - mail Ian McDonald - ian . mcdonald @ jandi . co . nz
2005-08-09 20:14:34 -07:00
*
* 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 .
*/
# ifndef _DCCP_CCID3_H_
# define _DCCP_CCID3_H_
2007-06-16 13:34:02 -03:00
# include <linux/ktime.h>
2005-08-09 20:14:34 -07:00
# include <linux/list.h>
2005-08-27 20:20:37 -03:00
# include <linux/types.h>
2005-09-18 00:18:32 -07:00
# include <linux/tfrc.h>
2007-12-12 14:06:14 -02:00
# include "lib/tfrc.h"
2006-03-20 19:21:44 -08:00
# include "../ccid.h"
2005-08-27 20:20:37 -03:00
2006-11-27 20:29:27 -02:00
/* Two seconds as per RFC 3448 4.2 */
2005-08-27 20:20:37 -03:00
# define TFRC_INITIAL_TIMEOUT (2 * USEC_PER_SEC)
2006-11-28 19:51:42 -02:00
/* Parameter t_mbi from [RFC 3448, 4.3]: backoff interval in seconds */
# define TFRC_T_MBI 64
2005-08-27 20:20:37 -03:00
dccp ccid-3: Bug fix for the inter-packet scheduling algorithm
This fixes a subtle bug in the calculation of the inter-packet gap and shows
that t_delta, as it is currently used, is not needed. And hence replaced.
The algorithm from RFC 3448, 4.6 below continually computes a send time t_nom,
which is initialised with the current time t_now; t_gran = 1E6 / HZ specifies
the scheduling granularity, s the packet size, and X the sending rate:
t_distance = t_nom - t_now; // in microseconds
t_delta = min(t_ipi, t_gran) / 2; // `delta' parameter in microseconds
if (t_distance >= t_delta) {
reschedule after (t_distance / 1000) milliseconds;
} else {
t_ipi = s / X; // inter-packet interval in usec
t_nom += t_ipi; // compute the next send time
send packet now;
}
1) Description of the bug
-------------------------
Rescheduling requires a conversion into milliseconds, due to this call chain:
* ccid3_hc_tx_send_packet() returns a timeout in milliseconds,
* this value is converted by msecs_to_jiffies() in dccp_write_xmit(),
* and finally used as jiffy-expires-value for sk_reset_timer().
The highest jiffy resolution with HZ=1000 is 1 millisecond, so using a higher
granularity does not make much sense here.
As a consequence, values of t_distance < 1000 are truncated to 0. This issue
has so far been resolved by using instead
if (t_distance >= t_delta + 1000)
reschedule after (t_distance / 1000) milliseconds;
The bug is in artificially inflating t_delta to t_delta' = t_delta + 1000. This
is unnecessarily large, a more adequate value is t_delta' = max(t_delta, 1000).
2) Consequences of using the corrected t_delta'
-----------------------------------------------
Since t_delta <= t_gran/2 = 10^6/(2*HZ), we have t_delta <= 1000 as long as
HZ >= 500. This means that t_delta' = max(1000, t_delta) is constant at 1000.
On the other hand, when using a coarse HZ value of HZ < 500, we have three
sub-cases that can all be reduced to using another constant of t_gran/2.
(a) The first case arises when t_ipi > t_gran. Here t_delta' is the constant
t_delta' = max(1000, t_gran/2) = t_gran/2.
(b) If t_ipi <= 2000 < t_gran = 10^6/HZ usec, then t_delta = t_ipi/2 <= 1000,
so that t_delta' = max(1000, t_delta) = 1000 < t_gran/2.
(c) If 2000 < t_ipi <= t_gran, we have t_delta' = max(t_delta, 1000) = t_ipi/2.
In the second and third cases we have delay values less than t_gran/2, which is
in the order of less than or equal to half a jiffy.
How these are treated depends on how fractions of a jiffy are handled: they
are either always rounded down to 0, or always rounded up to 1 jiffy (assuming
non-zero values). In both cases the error is on average in the order of 50%.
Thus we are not increasing the error when in the second/third case we replace
a value less than t_gran/2 with 0, by setting t_delta' to the constant t_gran/2.
3) Summary
----------
Fixing (1) and considering (2), the patch replaces t_delta with a constant,
whose value depends on CONFIG_HZ, changing the above algorithm to:
if (t_distance >= t_delta')
reschedule after (t_distance / 1000) milliseconds;
where t_delta' = 10^6/(2*HZ) if HZ < 500, and t_delta' = 1000 otherwise.
Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk>
2008-09-04 07:30:19 +02:00
/*
* The t_delta parameter ( RFC 3448 , 4.6 ) : delays of less than % USEC_PER_MSEC are
* rounded down to 0 , since sk_reset_timer ( ) here uses millisecond granularity .
* Hence we can use a constant t_delta = % USEC_PER_MSEC when HZ > = 500. A coarse
* resolution of HZ < 500 means that the error is below one timer tick ( t_gran )
* when using the constant t_delta = t_gran / 2 = % USEC_PER_SEC / ( 2 * HZ ) .
*/
# if (HZ >= 500)
# define TFRC_T_DELTA USEC_PER_MSEC
# else
# define TFRC_T_DELTA (USEC_PER_SEC / (2 * HZ))
# warning Coarse CONFIG_HZ resolution -- higher value recommended for TFRC.
# endif
2005-08-27 20:20:37 -03:00
enum ccid3_options {
TFRC_OPT_LOSS_EVENT_RATE = 192 ,
TFRC_OPT_LOSS_INTERVALS = 193 ,
TFRC_OPT_RECEIVE_RATE = 194 ,
} ;
2005-08-09 20:14:34 -07:00
2006-11-20 18:40:42 -02:00
/** struct ccid3_hc_tx_sock - CCID3 sender half-connection socket
2005-08-09 20:14:34 -07:00
*
2008-09-04 07:30:19 +02:00
* @ x - Current sending rate in 64 * bytes per second
* @ x_recv - Receive rate in 64 * bytes per second
* @ x_calc - Calculated rate in bytes per second
* @ rtt - Estimate of current round trip time in usecs
* @ p - Current loss event rate ( 0 - 1 ) scaled by 1000000
* @ s - Packet size in bytes
* @ t_rto - Nofeedback Timer setting in usecs
* @ t_ipi - Interpacket ( send ) interval ( RFC 3448 , 4.6 ) in usecs
2008-09-04 07:30:19 +02:00
* @ feedback - Whether feedback has been received or not
2008-09-04 07:30:19 +02:00
* @ last_win_count - Last window counter sent
* @ t_last_win_count - Timestamp of earliest packet with
* last_win_count value sent
* @ no_feedback_timer - Handle to no feedback timer
* @ t_ld - Time last doubled during slow start
* @ t_nom - Nominal send time of next packet
* @ hist - Packet history
2006-11-20 18:40:42 -02:00
*/
2005-08-09 20:14:34 -07:00
struct ccid3_hc_tx_sock {
2008-09-04 07:30:19 +02:00
u64 x ;
u64 x_recv ;
u32 x_calc ;
u32 rtt ;
u32 p ;
u32 t_rto ;
u32 t_ipi ;
u16 s ;
2008-09-04 07:30:19 +02:00
bool feedback : 1 ;
2008-09-04 07:30:19 +02:00
u8 last_win_count ;
ktime_t t_last_win_count ;
struct timer_list no_feedback_timer ;
ktime_t t_ld ;
ktime_t t_nom ;
struct tfrc_tx_hist_entry * hist ;
2005-08-09 20:14:34 -07:00
} ;
2007-09-26 11:24:28 -03:00
static inline struct ccid3_hc_tx_sock * ccid3_hc_tx_sk ( const struct sock * sk )
{
struct ccid3_hc_tx_sock * hctx = ccid_priv ( dccp_sk ( sk ) - > dccps_hc_tx_ccid ) ;
BUG_ON ( hctx = = NULL ) ;
return hctx ;
}
2006-11-15 21:27:47 -02:00
/* TFRC receiver states */
enum ccid3_hc_rx_states {
2006-12-10 16:01:18 -02:00
TFRC_RSTATE_NO_DATA = 1 ,
2006-11-15 21:27:47 -02:00
TFRC_RSTATE_DATA ,
} ;
2006-11-20 18:40:42 -02:00
/** struct ccid3_hc_rx_sock - CCID3 receiver half-connection socket
*
2008-09-04 07:30:19 +02:00
* @ last_counter - Tracks window counter ( RFC 4342 , 8.1 )
* @ state - Receiver state , one of % ccid3_hc_rx_states
* @ x_recv - Receiver estimate of send rate ( RFC 3448 , sec . 4.3 )
* @ rtt - Receiver estimate of RTT
* @ tstamp_last_feedback - Time at which last feedback was sent
* @ hist - Packet history ( loss detection + RTT sampling )
* @ li_hist - Loss Interval database
* @ p_inverse - Inverse of Loss Event Rate ( RFC 4342 , sec . 8.5 )
2006-11-20 18:40:42 -02:00
*/
2005-08-09 20:14:34 -07:00
struct ccid3_hc_rx_sock {
2008-09-04 07:30:19 +02:00
u8 last_counter : 4 ;
enum ccid3_hc_rx_states state : 8 ;
u32 x_recv ;
u32 rtt ;
ktime_t tstamp_last_feedback ;
struct tfrc_rx_hist hist ;
struct tfrc_loss_hist li_hist ;
# define p_inverse li_hist.i_mean
2005-08-09 20:14:34 -07:00
} ;
2005-09-09 02:40:58 -03:00
static inline struct ccid3_hc_rx_sock * ccid3_hc_rx_sk ( const struct sock * sk )
{
2007-09-26 11:24:28 -03:00
struct ccid3_hc_rx_sock * hcrx = ccid_priv ( dccp_sk ( sk ) - > dccps_hc_rx_ccid ) ;
BUG_ON ( hcrx = = NULL ) ;
return hcrx ;
2005-09-09 02:40:58 -03:00
}
2005-08-09 20:14:34 -07:00
# endif /* _DCCP_CCID3_H_ */