: Ack Vector implementation + fixes
Subsystems (2)
A message that is not in the archive (sent off-list or not yet received): dccp_ackvector_rfc_patch_set
cover : Ack Vector implementation + fixes
This set of patches adds functionality to the existing DCCP Ack Vector implementation, extends it to a full circular buffer, and fixes two previously undiscovered problems which otherwise result in a corrupted buffer state.
It is important that Ack Vectors run reliably since otherwise problems in other parts (in particular CCID2) can not be fixed.
Quite some testing has been performed so that I am almost convinced it is bug free. To make 100% sure that it contains no further hidden issues as before, I would like to post this as RFC, to be eventually submitted next year.
Patch #1: Fixes the problem that CCID2 ignores Ack Vectors on some packets. Patch #2: Adds a tail pointer to the buffer and makes the size configurable. Patch #3: Decouples the use of Elapsed Time from the use of Ack Vectors. Patch #4: Provides inlines for Ack Vector state and run length. Patch #5: Removes redundancies from the allocation / de-allocation routines. Patch #6: Splits Ack Vector specific code from option-specific code. Patch #7: Avoids doing unnecessary work when clearing state: the sender of
Ack Vectors is only interested in the highest-received Ack Number when clearing state. Hence parsing any Ack Vectors for this purpose (which only contain lower Ack Numbers) is entirely unnecessary.
Patch #8: Replaces #defines for Ack Vector states with enum.
Patch #9: Provides two extensions required for robustness: overflow handling
(indicated by a flag) and tracking the lowest-unacknowledged sequence number (tail_ackno)
Patch #10: Inlines for buffer index manipulation, dynamic buffer length calculation. Patch #11: Implements algorithm to clear buffer state (non-trivial). Patch #12: Updates the code which registers incoming packets in the buffer. Patch #13: Aggregates Ack-Vector specific code into one function, thus making it
easier/simpler for the main DCCP module to use Ack Vectors.
Patch #14: Cleans up old and now unused bits.
The above patches have been uploaded to
git://eden-feed.erg.abdn.ac.uk/dccp_exp [dccp]
but please note that the server will be switched off from 21/12 ... 01/08.
Detailed information and documentation can be found on
http://www.erg.abdn.ac.uk/users/gerrit/dccp/notes/ack_vectors/
PATCH 1/14 : Ack Vectors can happen on most packets
CCID2 feedback only considers Ack/DataAck packets, but Ack Vectors can appear on other packets, too. In RFC 4340, 11.4 the only restriction is that they must not appear on Data or Request packets.
A possible case is for instance a Sync packet used for feature-negotiation (as used e.g in section 6.6.1 of RFC 4340). In this case the host must answer with a SyncAck (7.5.4). Now, if the Sync packet carried an Ack Vector then the SyncAck acknowledges the receipt of that Ack Vector (thus changing the Acknowledgment Window, cf. 11.4.2); but the current code would not allow to parse the received Ack Vector.
The fix is in parsing Ack Vectors on all packet types which are allowed to carry an Ack Vector.
--- net/dccp/ccids/ccid2.c | 7 +------ 1 files changed, 1 insertions(+), 6 deletions(-)
net/dccp/ccids/ccid2.c +1 -6
@@ -549,13 +549,8 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)if (hctx->ccid2hctx_seqh = hctx->ccid2hctx_seqt)return ; -switch (DCCP_SKB_CB (skb)->dccpd_type) { -case DCCP_PKT_ACK : -case DCCP_PKT_DATAACK : -break ; -default : +if (dccp_packet_without_ack (skb))return ; - } ackno =DCCP_SKB_CB (skb)->dccpd_ack_seq;if (after48 (ackno, hctx->ccid2hctx_high_ack))
Signature · 1 line
1.5.3.GIT
PATCH 2/14 : Update Ack Vector fields
This patch updates the fields used by the Ack Vector structures:
* buf_tail was missing and has been added as struct member (support for updating this field follows in subsequent patches);
* the buf_nonce is now an array, each element covering up to 253 bytes of buffer state - this simplifies computing the ECN nonce for large Ack Vectors.
In particular, there are the following changes:
* since buf_nonce and buffer size use the same number, introduced a constant to set the maximum number of Ack Vectors in the buffer (tried Kconfig, but with low Ack Ratio rates, a size of 1 is fully sufficient, 2 seems to work for a wide range of cases; Kconfig remains an option for later);
* removed the field dccpav_ack_nonce from struct dccp_ackvec, since this is already redundantly stored in the `dccpavr_ack_nonce' (Ack Vector record);
* replaced semantics of dccpavr_sent_len with dccpavr_ack_runlen, since the code needs to be able to remember the old run length;
* also updated the documentation on sending Ack Vectors, it was outdated (ack_runlen was not considered);
* all sequence numbers now truncated to 48 bits (the assignment of 2^48 to buf_ackno in dccp_ackvec_alloc() has been removed, since dccp_ackvec_add() overrides this value each time a new packet is added to the buffer).
--- net/dccp/ackvec.c | 48 ++++++++++++++++---------------- net/dccp/ackvec.h | 77 +++++++++++++++++++++++++++-------------------------- 2 files changed, 63 insertions(+), 62 deletions(-)
net/dccp/ackvec.h +39 -38
@@ -18,8 +18,14 @@/* maximum size of a single TLV-encoded option (sans type/len bytes) */ #define DCCP_SINGLE_OPT_MAXLEN 253 -/* We can spread an ack vector across multiple options */ -#define DCCP_MAX_ACKVEC_LEN (DCCP_SINGLE_OPT_MAXLEN * 2) +/* +* Ack Vector buffer space is static, in multiples of %DCCP_SINGLE_OPT_MAXLEN, +* the maximum size of a single Ack Vector. Setting %DCCPAV_NUM_ACKVECS to 1 +* will be sufficient for most cases of low Ack Ratios, using a value of 2 gives +* more headroom if Ack Ratio is higher or when the sender acknowledges slowly. +*/ +#define DCCPAV_NUM_ACKVECS 2 +#define DCCPAV_MAX_ACKVEC_LEN (DCCP_SINGLE_OPT_MAXLEN * DCCPAV_NUM_ACKVECS)#define DCCP_ACKVEC_STATE_RECEIVED 0#define DCCP_ACKVEC_STATE_ECN_MARKED (1 << 6)
@@ -28,58 +34,53 @@#define DCCP_ACKVEC_STATE_MASK 0xC0/* 11000000 */ #define DCCP_ACKVEC_LEN_MASK 0x3F/* 00111111 */ -/** struct dccp_ackvec - ack vector -* -* This data structure is the one defined in RFC 4340, Appendix A. -* -* @av_buf_head - circular buffer head -* @av_buf_tail - circular buffer tail -* @av_buf_ackno - ack # of the most recent packet acknowledgeable in the -* buffer (i.e. %av_buf_head) -* @av_buf_nonce - the one-bit sum of the ECN Nonces on all packets acked -* by the buffer with State 0 +/** struct dccp_ackvec - Ack Vector main data structure * -* Additionally, the HC-Receiver must keep some information about the -* Ack Vectors it has recently sent. For each packet sent carrying an -* Ack Vector, it remembers four variables: +* This implements a fixed-size circular buffer within an array and is largely +* based on Appendix A of RFC 4340. * -* @av_records - list of dccp_ackvec_record -* @av_ack_nonce - the one-bit sum of the ECN Nonces for all State 0. -* -* @av_time - the time in usecs -* @av_buf - circular buffer of acknowledgeable packets +* @av_buf: circular buffer storage area +* @av_buf_head: head index; begin of live portion in @av_buf +* @av_buf_tail: tail index; first index _after_ the live portion in @av_buf +* @av_buf_ackno: highest seqno of acknowledgeable packet recorded in @av_buf +* @av_buf_nonce: ECN nonce sums, each covering subsequent segments of up to +* %DCCP_SINGLE_OPT_MAXLEN cells in the live portion of @av_buf +* @av_records: list of %dccp_ackvec_record (Ack Vectors sent previously) +* @av_time: the time in usecs +* @av_veclen: length of the live portion of @av_buf */ struct dccp_ackvec { -u64 av_buf_ackno; +u8 av_buf[DCCPAV_MAX_ACKVEC_LEN ]; +u16 av_buf_head; +u16 av_buf_tail; +u64 av_buf_ackno:48 ; +bool av_buf_nonce[DCCPAV_NUM_ACKVECS ];struct list_head av_records;ktime_t av_time; -u16 av_buf_head;u16 av_vec_len; -u8 av_buf_nonce; -u8 av_ack_nonce; -u8 av_buf[DCCP_MAX_ACKVEC_LEN ]; }; -/** struct dccp_ackvec_record - ack vector record +/** struct dccp_ackvec_record - Records information about sent Ack Vectors * -* ACK vector record as defined in Appendix A of spec. +* These list entries define the additional information which the HC-Receiver +* keeps about recently-sent Ack Vectors; again refer to RFC 4340, Appendix A. * -* The list is sorted by avr_ack_seqno +* @avr_node: the list node in @av_records +* @avr_ack_seqno: sequence number of the packet the Ack Vector was sent on +* @avr_ack_ackno: the Ack number that this record/Ack Vector refers to +* @avr_ack_ptr: pointer into @av_buf where this record starts +* @avr_ack_runlen: run length of @avr_ack_ptr at the time of sending +* @avr_ack_nonce: the sum of @av_buf_nonce's at the time this record was sent * -* @avr_node - node in av_records -* @avr_ack_seqno - sequence number of the packet this record was sent on -* @avr_ack_ackno - sequence number being acknowledged -* @avr_ack_ptr - pointer into av_buf where this record starts -* @avr_ack_nonce - av_ack_nonce at the time this record was sent -* @avr_sent_len - lenght of the record in av_buf +* The list as a whole is sorted in descending order by @avr_ack_seqno. */ struct dccp_ackvec_record {struct list_head avr_node; -u64 avr_ack_seqno; -u64 avr_ack_ackno; +u64 avr_ack_seqno:48 ; +u64 avr_ack_ackno:48 ;u16 avr_ack_ptr; -u16 avr_sent_len; -u8 avr_ack_nonce; +u8 avr_ack_runlen; +u8 avr_ack_nonce:1 ; };struct sock ;
net/dccp/ackvec.c +24 -24
116 lines not on this page · raw message
PATCH 3/14 : Use Elapsed Time separately
This decouples the use of Elapsed Time options from the use of Ack Vectors, so that Elapsed Time options are no longer added automatically to each Ack Vector.
There are three reasons for this:
1. The Elapsed Time information is nowhere used in the code.
2. DCCP does not implement rate-based pacing of acknowledgments. The only recommendation for always including Elapsed Time is in section 11.3 of RFC 4340: "Receivers that rate-pace acknowledgements SHOULD [...] include Elapsed Time options". But such is not the case here.
3. It does not really improve estimation accuracy. The Elapsed Time field only records the time between the arrival of the last acknowledgeable packet and the time the Ack Vector is sent out. Since Linux does not (yet) implement delayed Acks, the time difference will typically be small, since often the arrival of a data packet triggers sending feedback at the HC-receiver. If the Ack Vector has a wide coverage (up to 16192) then the Elapsed Time is not very meaningful for older packets.
If indeed elapsed time is required by the endpoint or CCID, then using Timestamp options can provide the same information (elapsed time field in the Timestamp Echo option).
--- net/dccp/ackvec.c | 11 ----------- net/dccp/ackvec.h | 3 --- 2 files changed, 0 insertions(+), 14 deletions(-)
net/dccp/ackvec.h +0 -3
@@ -12,7 +12,6 @@*/ #include <linux/compiler.h> -#include <linux/ktime.h> #include <linux/list.h> #include <linux/types.h>
@@ -46,7 +45,6 @@* @av_buf_nonce: ECN nonce sums, each covering subsequent segments of up to * %DCCP_SINGLE_OPT_MAXLEN cells in the live portion of @av_buf * @av_records: list of %dccp_ackvec_record (Ack Vectors sent previously) -* @av_time: the time in usecs * @av_veclen: length of the live portion of @av_buf */ struct dccp_ackvec {
@@ -56,7 +54,6 @@ struct dccp_ackvec { u64 av_buf_ackno:48;bool av_buf_nonce[DCCPAV_NUM_ACKVECS ];struct list_head av_records; -ktime_t av_time;u16 av_vec_len; };
net/dccp/ackvec.c +0 -11
@@ -71,18 +71,9 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)const u16 nr_opts =DIV_ROUND_UP (av->av_vec_len,DCCP_SINGLE_OPT_MAXLEN );u16 len = av->av_vec_len +2 * nr_opts;u8 i, nonce =0 ; -u32 elapsed_time;const unsigned char *tail, *from;unsigned char *to;struct dccp_ackvec_record *avr; -suseconds_t delta; - - delta =ktime_us_delta (ktime_get_real (), av->av_time); - elapsed_time = delta /10 ; - -if (elapsed_time !=0 && -dccp_insert_option_elapsed_time (sk, skb, elapsed_time)) -return -1 ;if (DCCP_SKB_CB (skb)->dccpd_opt_len + len >DCCP_MAX_OPT_LEN ) {/*
@@ -162,7 +153,6 @@ struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority)if (av !=NULL ) { av->av_buf_head =DCCPAV_MAX_ACKVEC_LEN -1 ; - av->av_time =ktime_set (0 ,0 ); av->av_vec_len =0 ;memset (av->av_buf_nonce,0 ,sizeof (av->av_buf_nonce));INIT_LIST_HEAD (&av->av_records);
@@ -321,7 +311,6 @@ int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk, } av->av_buf_ackno = ackno; - av->av_time =ktime_get_real (); out:return 0 ;
PATCH 4/14 : Inlines for run length and state
This provides inlines for Ack Vector run length and state, which allow to wrap several instances of the same code into function calls.
The unused function dccp_ackvec_print() (which also relied on the older constants), has been removed.
--- net/dccp/ackvec.c | 71 +++++++++++------------------------------------- net/dccp/ackvec.h | 13 +++++++- net/dccp/ccids/ccid2.c | 7 ++--- 3 files changed, 30 insertions(+), 61 deletions(-)
net/dccp/ackvec.h +11 -2
@@ -30,8 +30,17 @@#define DCCP_ACKVEC_STATE_ECN_MARKED (1 << 6)#define DCCP_ACKVEC_STATE_NOT_RECEIVED (3 << 6) -#define DCCP_ACKVEC_STATE_MASK 0xC0/* 11000000 */ -#define DCCP_ACKVEC_LEN_MASK 0x3F/* 00111111 */ +#define DCCPAV_MAX_RUNLEN 0x3F + +static inline u8 dccp_ackvec_runlen (const u8 *cell) +{ +return *cell &DCCPAV_MAX_RUNLEN ; +} + +static inline u8 dccp_ackvec_state (const u8 *cell) +{ +return *cell & ~DCCPAV_MAX_RUNLEN ; +}/** struct dccp_ackvec - Ack Vector main data structure *
net/dccp/ackvec.c +15 -55
138 lines not on this page · raw message
net/dccp/ccids/ccid2.c +3 -4
@@ -578,8 +578,8 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) &vector, &veclen)) != -1) {/* go through this ack vector */ while (veclen--) { -const u8 rl = *vector &DCCP_ACKVEC_LEN_MASK ; -u64 ackno_end_rl =SUB48 (ackno, rl); +u64 ackno_end_rl =SUB48 (ackno, +dccp_ackvec_runlen (vector)); ccid2_pr_debug("ackvec start:%llu end:%llu\n", (unsigned long long)ackno,
@@ -602,8 +602,7 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)* run length */ while (between48 (seqp->ccid2s_seq,ackno_end_rl,ackno)) { -const u8 state = *vector & -DCCP_ACKVEC_STATE_MASK ; +const u8 state =dccp_ackvec_state (vector);/* new packet received or marked */ if (state != DCCP_ACKVEC_STATE_NOT_RECEIVED &&
Signature · 1 line
1.5.3.GIT
PATCH 5/14 : Smaller allocation/deallocation routines
This patch removes redundant bits, implementing the same functionality with less code.
The details are:
* The INIT_LIST_HEAD in dccp_ackvec_record_new was redundant, since the list pointers were later overwritten when the node was added via list_add().
* dccp_ackvec_record_new() was called only in one instance in the entire ackvec code.
* The calls to list_del_init() before calling dccp_ackvec_record_delete() were redundant, since subsequently the entire element was freed anyway.
* Similarly, since all calls to dccp_ackvec_record_delete() were preceded to a call to list_del_init(), the WARN_ON test would never evaluate to true.
* Also, since all calls to dccp_ackvec_record_delete() were made from within list_for each_entry_safe(), the test for avr = NULL was redundant.
* The list_empty() test in ackvec_free was redundant, since the same condition is embedded in the loop condition of the subsequent list_for_each_entry_safe().
--- net/dccp/ackvec.c | 78 +++++++++++++++++++---------------------------------- 1 files changed, 28 insertions(+), 50 deletions(-)
net/dccp/ackvec.c +28 -48
@@ -24,24 +24,34 @@static struct kmem_cache *dccp_ackvec_slab;static struct kmem_cache *dccp_ackvec_record_slab; -static struct dccp_ackvec_record *dccp_ackvec_record_new (void ) +struct dccp_ackvec *dccp_ackvec_alloc (const gfp_t priority) { - struct dccp_ackvec_record *avr - kmem_cache_alloc(dccp_ackvec_record_slab, GFP_ATOMIC); +struct dccp_ackvec *av =kmem_cache_alloc (dccp_ackvec_slab, priority); + +if (av !=NULL ) { + av->av_buf_head =DCCPAV_MAX_ACKVEC_LEN -1 ; + av->av_vec_len =0 ; +memset (av->av_buf_nonce,0 ,sizeof (av->av_buf_nonce)); +INIT_LIST_HEAD (&av->av_records); + } +return av; +} -if (avr !=NULL ) -INIT_LIST_HEAD (&avr->avr_node); +static void dccp_ackvec_purge_records (struct dccp_ackvec *av) +{ +struct dccp_ackvec_record *cur, *next; -return avr; +list_for_each_entry_safe (cur, next, &av->av_records, avr_node) +kmem_cache_free (dccp_ackvec_record_slab, cur); +INIT_LIST_HEAD (&av->av_records); } -static void dccp_ackvec_record_delete (struct dccp_ackvec_record *avr) +void dccp_ackvec_free (struct dccp_ackvec *av) { -if (unlikely (avr =NULL )) -return ; -/* Check if deleting a linked record */ -WARN_ON (!list_empty (&avr->avr_node)); -kmem_cache_free (dccp_ackvec_record_slab, avr); +if (likely (av !=NULL )) { +dccp_ackvec_purge_records (av); +kmem_cache_free (dccp_ackvec_slab, av); + } } static void dccp_ackvec_insert_avr(struct dccp_ackvec *av,
@@ -85,7 +95,7 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)return -1 ; } - avr =dccp_ackvec_record_new (); + avr =kmem_cache_alloc (dccp_ackvec_record_slab,GFP_ATOMIC );if (avr =NULL )return -1 ;
@@ -147,37 +157,6 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)return 0 ; } -struct dccp_ackvec *dccp_ackvec_alloc (const gfp_t priority) -{ -struct dccp_ackvec *av =kmem_cache_alloc (dccp_ackvec_slab, priority); - -if (av !=NULL ) { - av->av_buf_head =DCCPAV_MAX_ACKVEC_LEN -1 ; - av->av_vec_len =0 ; -memset (av->av_buf_nonce,0 ,sizeof (av->av_buf_nonce)); -INIT_LIST_HEAD (&av->av_records); - } - -return av; -} - -void dccp_ackvec_free (struct dccp_ackvec *av) -{ -if (unlikely (av =NULL )) -return ; - -if (!list_empty (&av->av_records)) { -struct dccp_ackvec_record *avr, *next; - -list_for_each_entry_safe (avr, next, &av->av_records, avr_node) { -list_del_init (&avr->avr_node); -dccp_ackvec_record_delete (avr); - } - } - -kmem_cache_free (dccp_ackvec_slab, av); -} -/* * If several packets are missing, the HC-Receiver may prefer to enter multiple * bytes with run length 0, rather than a single byte with a larger run length;
@@ -321,8 +300,8 @@ static void dccp_ackvec_throw_record(struct dccp_ackvec *av,/* free records */ list_for_each_entry_safe_from (avr, next, &av->av_records, avr_node) { -list_del_init (&avr->avr_node); -dccp_ackvec_record_delete (avr); +list_del (&avr->avr_node); +kmem_cache_free (dccp_ackvec_record_slab, avr); } }
@@ -431,10 +410,9 @@ int __init dccp_ackvec_init(void)if (dccp_ackvec_slab =NULL ) goto out_err; - dccp_ackvec_record_slab -kmem_cache_create ("dccp_ackvec_record" , -sizeof (struct dccp_ackvec_record ), -0 ,SLAB_HWCACHE_ALIGN ,NULL ); + dccp_ackvec_record_slab =kmem_cache_create ("dccp_ackvec_record" , +sizeof (struct dccp_ackvec_record ), +0 ,SLAB_HWCACHE_ALIGN ,NULL );if (dccp_ackvec_record_slab =NULL ) goto out_destroy_slab;
Signature · 1 line
1.5.3.GIT
PATCH 6/14 : Simplify adding Ack Vector records, split option-specific code
This patch
* simplifies the dccp_ackvec_insert_avr() routine: the BUG_ON was redundant, since the list is automatically arranged in descending order of ack_no;
* separates Ack Vector housekeeping code from option-specific code;
* shifts option-specific code into options.c.
--- net/dccp/ackvec.c | 107 +++++++++------------------------------------------- net/dccp/ackvec.h | 5 +- net/dccp/options.c | 65 +++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 92 deletions(-)
net/dccp/ackvec.h +2 -3
@@ -108,7 +108,7 @@ extern int dccp_ackvec_parse(struct sock *sk, const struct sk_buff *skb, u64 *ackno, const u8 opt, const u8 *value, const u8 len); -extern int dccp_insert_option_ackvec (struct sock *sk,struct sk_buff *skb); +extern int dccp_ackvec_update_records (struct dccp_ackvec *av,u64 seq,u8 sum);static inline int dccp_ackvec_pending (const struct dccp_ackvec *av) {
@@ -151,8 +151,7 @@ static inline int dccp_ackvec_parse(struct sock *sk, const struct sk_buff *skb,return -1 ; } -static inline int dccp_insert_option_ackvec (const struct sock *sk, -const struct sk_buff *skb) +static inline int dccp_ackvec_update_records (struct dccp_ackvec *av,u64 seq,u8 nonce) {return -1 ; }
net/dccp/ackvec.c +18 -89
124 lines not on this page · raw message
+++ b/net/dccp/options.c
@@ -433,6 +433,71 @@ static int dccp_insert_option_timestamp_echo(struct dccp_sock *dp,
return 0;
}
+int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) +{
+ struct dccp_ackvec *av = dccp_sk(sk)->dccps_hc_rx_ackvec;
+ /* Figure out how many options do we need to represent the ackvec */
+ const u16 nr_opts = DIV_ROUND_UP(av->av_vec_len, DCCP_SINGLE_OPT_MAXLEN);
+ u16 len = av->av_vec_len + 2 * nr_opts;
+ u8 i, nonce = 0;
+ const unsigned char *tail, *from;
+ unsigned char *to;
+
+ if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) {
+ /*
+ * FIXME: when running out of option space while piggybacking on
+ * DataAck, return 0 here and schedule a separate Ack instead.
+ */
+ DCCP_WARN("Lacking space for %u bytes on %s packet\n", len,
+ dccp_packet_name(DCCP_SKB_CB(skb)->dccpd_type));
+ return -1;
+ }
+ DCCP_SKB_CB(skb)->dccpd_opt_len += len;
+
+ to = skb_push(skb, len);
+ len = av->av_vec_len;
+ from = av->av_buf + av->av_buf_head;
+ tail = av->av_buf + DCCPAV_MAX_ACKVEC_LEN;
+
+ for (i = 0; i < nr_opts; ++i) {
+ int copylen = len;
+
+ if (len > DCCP_SINGLE_OPT_MAXLEN)
+ copylen = DCCP_SINGLE_OPT_MAXLEN;
+
+ /*
+ * RFC 4340, 12.2: Encode the Nonce Echo for this Ack Vector via
+ * its type; ack_nonce is the sum of all individual buf_nonce's.
+ */
+ nonce ^= av->av_buf_nonce[i];
+
+ *to++ = DCCPO_ACK_VECTOR_0 + av->av_buf_nonce[i];
+ *to++ = copylen + 2;
+
+ /* Check if buf_head wraps */
+ if (from + copylen > tail) {
+ const u16 tailsize = tail - from;
+
+ memcpy(to, from, tailsize);
+ to += tailsize;
+ len -= tailsize;
+ copylen -= tailsize;
+ from = av->av_buf;
+ }
+
+ memcpy(to, from, copylen);
+ from += copylen;
+ to += copylen;
+ len -= copylen;
+ }
+ /*
+ * Each sent Ack Vector is recorded in the list, as per A.2 of RFC 4340.
+ */
+ if (dccp_ackvec_update_records(av, DCCP_SKB_CB(skb)->dccpd_seq, nonce))
+ return -ENOBUFS;
+ return 0;
+}
+
/**
* dccp_insert_option_mandatory - Mandatory option (5.8.2)
* Note that since we are using skb_push, this function needs to be called
Signature · 1 line
1.5.3.GIT
PATCH 7/14 : Unnecessary to parse Ack Vectors when clearing HC-receiver state
This removes code clearing Ack Vector state when it is acknowledged via an Ack Vector by the peer. That code is redundant, since
* the receiver always puts the full acknowledgment window (groups 2,3 in 11.4.2) into the Ack Vectors it sends; hence the HC-receiver is only interested in the highest state that the HC-sender received;
* this means that the acknowledgment number on the (Data)Ack from the HC-sender is sufficient; and work done in parsing earlier state is not necessary, since the later state subsumes the earlier one (see also RFC 4340, A.4).
Other (minor) changes:
* uses the suggestion made in the code comment, to traverse from oldest record to youngest - allowing faster hits when the list is large;
* removed the unused argument variable `sk' from check_rcv_ackno;
* renamed check_rcv_ackno => clear_state, since then it is clearer what is done by the function (also same name as in A.3 of RFC 4340);
* the variable `ackno' now becomes unused in options.c, and therefore is now used for other (space-saving) purposes.
--- net/dccp/ackvec.c | 114 ++++++++++------------------------------------------ net/dccp/ackvec.h | 17 +------- net/dccp/input.c | 4 +- net/dccp/options.c | 12 ++---- 4 files changed, 30 insertions(+), 117 deletions(-)
net/dccp/ackvec.h +2 -15
@@ -102,13 +102,8 @@ extern void dccp_ackvec_free(struct dccp_ackvec *av);extern int dccp_ackvec_add (struct dccp_ackvec *av,const struct sock *sk,const u64 ackno,const u8 state); -extern void dccp_ackvec_check_rcv_ackno (struct dccp_ackvec *av, -struct sock *sk,const u64 ackno); -extern int dccp_ackvec_parse (struct sock *sk,const struct sk_buff *skb, -u64 *ackno,const u8 opt, -const u8 *value,const u8 len); -extern int dccp_ackvec_update_records (struct dccp_ackvec *av,u64 seq,u8 sum); +extern void dccp_ackvec_clear_state (struct dccp_ackvec *av,const u64 ackno);static inline int dccp_ackvec_pending (const struct dccp_ackvec *av) {
@@ -139,18 +134,10 @@ static inline int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk,return -1 ; } -static inline void dccp_ackvec_check_rcv_ackno (struct dccp_ackvec *av, -struct sock *sk,const u64 ackno) +static inline void dccp_ackvec_clear_state (struct dccp_ackvec *av,const u64 ackno) { } -static inline int dccp_ackvec_parse (struct sock *sk,const struct sk_buff *skb, -const u64 *ackno,const u8 opt, -const u8 *value,const u8 len) -{ -return -1 ; -} -static inline int dccp_ackvec_update_records (struct dccp_ackvec *av,u64 seq,u8 nonce) {return -1 ;
net/dccp/ackvec.c +22 -92
129 lines not on this page · raw message
+++ b/net/dccp/input.c
@@ -164,8 +164,8 @@ static void dccp_event_ack_recv(struct sock *sk, struct sk_buff *skb)
struct dccp_sock *dp = dccp_sk(sk);
if (dp->dccps_hc_rx_ackvec != NULL)
- dccp_ackvec_check_rcv_ackno(dp->dccps_hc_rx_ackvec, sk,
- DCCP_SKB_CB(skb)->dccpd_ack_seq);
+ dccp_ackvec_clear_state(dp->dccps_hc_rx_ackvec,
+ DCCP_SKB_CB(skb)->dccpd_ack_seq);
}
static void dccp_deliver_input_to_ccids(struct sock *sk, struct sk_buff *skb)
net/dccp/options.c +4 -8
@@ -138,9 +138,8 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,case DCCPO_ACK_VECTOR_1 :if (dccp_packet_without_ack (skb))/* RFC 4340, 11.4 */ break ; -if (dp->dccps_hc_rx_ackvec !=NULL && -dccp_ackvec_parse (sk, skb, &ackno, opt, value, len)) - goto out_invalid_option; +dccp_pr_debug ("%s Ack Vector (len=%u)\n" ,dccp_role (sk), + len);break ;case DCCPO_TIMESTAMP :if (len !=4 )
@@ -166,8 +165,7 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, }dccp_pr_debug ("%s rx opt: TIMESTAMP=%u, ackno=%llu\n" ,dccp_role (sk),ntohl (opt_val), - (unsigned long long ) -DCCP_SKB_CB (skb)->dccpd_ack_seq); + (unsigned long long )ackno);/* schedule an Ack in case this sender is quiescent */ dccp_schedule_ack (sk);break ;
@@ -181,9 +179,7 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,dccp_pr_debug ("%s rx opt: TIMESTAMP_ECHO=%u, len=%d, " "ackno=%llu" ,dccp_role (sk), opt_recv->dccpor_timestamp_echo, - len +2 , - (unsigned long long ) -DCCP_SKB_CB (skb)->dccpd_ack_seq); + len +2 , (unsigned long long )ackno); value +=4 ;
Signature · 1 line
1.5.3.GIT
PATCH 8/14 : Use enum to enumerate Ack Vector states
This replaces 3 #defines with an enum containing all possible Ack Vector states as per RFC 4340, 11.4. This helps to reduce the length of several expressions.
--- net/dccp/ackvec.c | 7 +++---- net/dccp/ackvec.h | 10 ++++++---- net/dccp/ccids/ccid2.c | 7 +++---- net/dccp/input.c | 9 +++------ 4 files changed, 15 insertions(+), 18 deletions(-)
net/dccp/ackvec.h +6 -4
@@ -26,10 +26,12 @@#define DCCPAV_NUM_ACKVECS 2#define DCCPAV_MAX_ACKVEC_LEN (DCCP_SINGLE_OPT_MAXLEN * DCCPAV_NUM_ACKVECS) -#define DCCP_ACKVEC_STATE_RECEIVED 0 -#define DCCP_ACKVEC_STATE_ECN_MARKED (1 << 6) -#define DCCP_ACKVEC_STATE_NOT_RECEIVED (3 << 6) - +enum dccp_ackvec_states { +DCCPAV_RECEIVED =0x00 , +DCCPAV_ECN_MARKED =0x40 , +DCCPAV_RESERVED =0x80 , +DCCPAV_NOT_RECEIVED =0xC0 +};#define DCCPAV_MAX_RUNLEN 0x3Fstatic inline u8 dccp_ackvec_runlen (const u8 *cell)
net/dccp/ackvec.c +3 -4
@@ -124,7 +124,7 @@ static inline int dccp_ackvec_set_buf_head_state(struct dccp_ackvec *av,if (new_head <0 ) {if (gap >0 ) { -memset (av->av_buf,DCCP_ACKVEC_STATE_NOT_RECEIVED , +memset (av->av_buf,DCCPAV_NOT_RECEIVED , gap + new_head +1 ); gap = -new_head; }
@@ -135,7 +135,7 @@ static inline int dccp_ackvec_set_buf_head_state(struct dccp_ackvec *av,if (gap >0 )memset (av->av_buf + av->av_buf_head +1 , -DCCP_ACKVEC_STATE_NOT_RECEIVED , gap); +DCCPAV_NOT_RECEIVED , gap); av->av_buf[av->av_buf_head] = state; av->av_vec_len += packets;
@@ -205,8 +205,7 @@ int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk,* valid packets not yet in av_buf have a reserved * entry, with a len equal to 0. */ -if (*cur_head =DCCP_ACKVEC_STATE_NOT_RECEIVED && - delta =0 ) { +if (*cur_head =DCCPAV_NOT_RECEIVED && delta =0 ) {dccp_pr_debug ("Found %llu reserved seat!\n" , (unsigned long long )ackno); *cur_head = state;
net/dccp/ccids/ccid2.c +3 -4
@@ -605,13 +605,12 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)const u8 state =dccp_ackvec_state (vector);/* new packet received or marked */ -if (state !=DCCP_ACKVEC_STATE_NOT_RECEIVED && +if (state !=DCCPAV_NOT_RECEIVED && !seqp->ccid2s_acked) { -if (state = -DCCP_ACKVEC_STATE_ECN_MARKED ) { +if (state =DCCPAV_ECN_MARKED )ccid2_congestion_event (sk, seqp); - }else +else ccid2_new_ack (sk, seqp, &maxincr);
net/dccp/input.c +3 -6
@@ -377,8 +377,7 @@ int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,if (dp->dccps_hc_rx_ackvec !=NULL &&dccp_ackvec_add (dp->dccps_hc_rx_ackvec, sk, -DCCP_SKB_CB (skb)->dccpd_seq, -DCCP_ACKVEC_STATE_RECEIVED )) +DCCP_SKB_CB (skb)->dccpd_seq,DCCPAV_RECEIVED )) goto discard;dccp_deliver_input_to_ccids (sk, skb);
@@ -511,8 +510,7 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,if (dp->dccps_hc_rx_ackvec !=NULL &&dccp_ackvec_add (dp->dccps_hc_rx_ackvec, sk, -DCCP_SKB_CB (skb)->dccpd_seq, -DCCP_ACKVEC_STATE_RECEIVED )) +DCCP_SKB_CB (skb)->dccpd_seq,DCCPAV_RECEIVED )) goto unable_to_proceed;dccp_send_ack (sk);
@@ -639,8 +637,7 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,if (dp->dccps_hc_rx_ackvec !=NULL &&dccp_ackvec_add (dp->dccps_hc_rx_ackvec, sk, -DCCP_SKB_CB (skb)->dccpd_seq, -DCCP_ACKVEC_STATE_RECEIVED )) +DCCP_SKB_CB (skb)->dccpd_seq,DCCPAV_RECEIVED )) goto discard;dccp_deliver_input_to_ccids (sk, skb);
Signature · 1 line
1.5.3.GIT
PATCH 9/14 : Support for circular Ack Vector buffer with overflow handling
This adds missing bits to complete the implementation of a circular buffer for Ack Vector information, with the following changes:
(a) An `overflow' flag to deal with the case of overflow. As before, dynamic growth of the buffer will not be supported; but code will be added to deal more gracefully with overflowing Ack Vector buffers (if that ever happens).
(b) A `tail_seqno' field. When naively implementing the algorithm of Appendix A in RFC 4340, problems arise whenever subsequent Ack Vector records overlap, which can bring the entire run length calculation completely out of synch. (This is documented on http://www.erg.abdn.ac.uk/users/gerrit/dccp/notes/\
ack_vectors/tracking_tail_ackno/ .)
This further allowed to simplify dccp_ackvec_pending() - the #ifdef is no longer necessary since buf_empty is always true when IP_DCCP_ACKVEC is not configured.
Note on overflow handling:
-------------------------
The Ack Vector code previously simply started to drop packets when the Ack Vector buffer overflowed. This means that the userspace application will not be able to get its data, only because of an Ack Vector problem.
Furthermore, overflow may be transient, so that the application later recovers from the overflow. Recovering from lost packets is more difficult (e.g. video key frames).
Hence the patch uses a different policy: when the buffer overflows, the oldest entries are subsequently overwritten. This has a higher chance of recovery. Details are on http://www.erg.abdn.ac.uk/users/gerrit/dccp/notes/ack_vectors/
--- net/dccp/ackvec.c | 11 ++++++++++- net/dccp/ackvec.h | 12 ++++++++---- net/dccp/dccp.h | 14 +++++++------- net/dccp/options.c | 5 ++--- 4 files changed, 27 insertions(+), 15 deletions(-)
net/dccp/ackvec.h +8 -4
@@ -53,8 +53,10 @@ static inline u8 dccp_ackvec_state(const u8 *cell)* @av_buf_head: head index; begin of live portion in @av_buf * @av_buf_tail: tail index; first index _after_ the live portion in @av_buf * @av_buf_ackno: highest seqno of acknowledgeable packet recorded in @av_buf +* @av_tail_ackno: lowest seqno of acknowledgeable packet recorded in @av_buf * @av_buf_nonce: ECN nonce sums, each covering subsequent segments of up to * %DCCP_SINGLE_OPT_MAXLEN cells in the live portion of @av_buf +* @av_overflow: if 1 then buf_head = buf_tail indicates buffer wraparound * @av_records: list of %dccp_ackvec_record (Ack Vectors sent previously) * @av_veclen: length of the live portion of @av_buf */
@@ -63,7 +65,9 @@ struct dccp_ackvec {u16 av_buf_head;u16 av_buf_tail; u64 av_buf_ackno:48; + u64 av_tail_ackno:48;bool av_buf_nonce[DCCPAV_NUM_ACKVECS ]; + u8 av_overflow:1;struct list_head av_records;u16 av_vec_len; };
@@ -107,9 +111,9 @@ extern int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk,extern int dccp_ackvec_update_records (struct dccp_ackvec *av,u64 seq,u8 sum);extern void dccp_ackvec_clear_state (struct dccp_ackvec *av,const u64 ackno); -static inline int dccp_ackvec_pending (const struct dccp_ackvec *av) +static inline bool dccp_ackvec_is_empty (const struct dccp_ackvec *av) { -return av->av_vec_len; +return av->av_overflow =0 && av->av_buf_head = av->av_buf_tail; }#else /* CONFIG_IP_DCCP_ACKVEC */ static inline int dccp_ackvec_init (void )
@@ -145,9 +149,9 @@ static inline int dccp_ackvec_update_records(struct dccp_ackvec *av, u64 seq, u8return -1 ; } -static inline int dccp_ackvec_pending (const struct dccp_ackvec *av) +static inline bool dccp_ackvec_is_empty (const struct dccp_ackvec *av) { -return 0 ; +return true; }#endif /* CONFIG_IP_DCCP_ACKVEC */ #endif /* _ACKVEC_H */
net/dccp/ackvec.c +10 -1
@@ -29,7 +29,8 @@ struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority)struct dccp_ackvec *av =kmem_cache_alloc (dccp_ackvec_slab, priority);if (av !=NULL ) { - av->av_buf_head =DCCPAV_MAX_ACKVEC_LEN -1 ; + av->av_buf_head = av->av_buf_tail =DCCPAV_MAX_ACKVEC_LEN -1 ; + av->av_overflow =0 ; av->av_vec_len =0 ;memset (av->av_buf_nonce,0 ,sizeof (av->av_buf_nonce));INIT_LIST_HEAD (&av->av_records);
@@ -74,6 +75,14 @@ int dccp_ackvec_update_records(struct dccp_ackvec *av, u64 seqno, u8 nonce_sum) avr->avr_ack_nonce = nonce_sum; avr->avr_ack_runlen =dccp_ackvec_runlen (av->av_buf + av->av_buf_head);/* +* When the buffer overflows, we keep no more than one record. This is +* the simplest way of disambiguating sender-Acks dating from before the +* overflow from sender-Acks which refer to after the overflow; a simple +* solution is preferable here since we are handling an exception. +*/ +if (av->av_overflow) +dccp_ackvec_purge_records (av); +/* * Since GSS is incremented for each packet, the list is automatically * arranged in descending order of @ack_seqno. */
net/dccp/dccp.h +7 -7
@@ -410,15 +410,15 @@ static inline void dccp_schedule_ack(struct sock *sk)inet_csk_schedule_ack (sk); } +static inline int dccp_ackvec_pending (const struct sock *sk) +{ +return dccp_sk (sk)->dccps_hc_rx_ackvec !=NULL && + !dccp_ackvec_is_empty (dccp_sk (sk)->dccps_hc_rx_ackvec); +} +static inline int dccp_ack_pending (const struct sock *sk) { -const struct dccp_sock *dp =dccp_sk (sk); -return -#ifdef CONFIG_IP_DCCP_ACKVEC - (dp->dccps_hc_rx_ackvec !=NULL && -dccp_ackvec_pending (dp->dccps_hc_rx_ackvec)) || -#endif -inet_csk_ack_scheduled (sk); +return dccp_ackvec_pending (sk) ||inet_csk_ack_scheduled (sk); }extern int dccp_feat_signal_nn_change (struct sock *sk,u8 feat,u64 nn_val);
net/dccp/options.c +2 -3
@@ -589,9 +589,8 @@ int dccp_insert_options(struct sock *sk, struct sk_buff *skb)if (dccp_insert_option_timestamp (sk, skb))return -1 ; - }else if (dp->dccps_hc_rx_ackvec !=NULL && -dccp_ackvec_pending (dp->dccps_hc_rx_ackvec) && -dccp_insert_option_ackvec (sk, skb) ) { + }else if (dccp_ackvec_pending (sk) && +dccp_insert_option_ackvec (sk, skb)) {return -1 ; } }
Signature · 1 line
1.5.3.GIT
PATCH 10/14 : Determine buffer length dynamically
The length of the circular Ack Vector buffer is now determined dynamically, as the span between head to tail.
--- net/dccp/ackvec.c | 21 +++++++++++++++++++++ net/dccp/ackvec.h | 7 +++++++ net/dccp/options.c | 7 ++++--- 3 files changed, 32 insertions(+), 3 deletions(-)
net/dccp/ackvec.h +7 -0
@@ -22,6 +22,7 @@* the maximum size of a single Ack Vector. Setting %DCCPAV_NUM_ACKVECS to 1 * will be sufficient for most cases of low Ack Ratios, using a value of 2 gives * more headroom if Ack Ratio is higher or when the sender acknowledges slowly. +* The maximum value is bounded by the u16 types for indices and functions. */ #define DCCPAV_NUM_ACKVECS 2#define DCCPAV_MAX_ACKVEC_LEN (DCCP_SINGLE_OPT_MAXLEN * DCCPAV_NUM_ACKVECS)
@@ -110,6 +111,7 @@ extern int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk,extern int dccp_ackvec_update_records (struct dccp_ackvec *av,u64 seq,u8 sum);extern void dccp_ackvec_clear_state (struct dccp_ackvec *av,const u64 ackno); +extern u16 dccp_ackvec_buflen (const struct dccp_ackvec *av);static inline bool dccp_ackvec_is_empty (const struct dccp_ackvec *av) {
@@ -149,6 +151,11 @@ static inline int dccp_ackvec_update_records(struct dccp_ackvec *av, u64 seq, u8return -1 ; } +static inline u16 dccp_ackvec_buflen (const struct dccp_ackvec *av) +{ +return 0 ; +} +static inline bool dccp_ackvec_is_empty (const struct dccp_ackvec *av) {return true;
net/dccp/ackvec.c +21 -0
@@ -114,6 +114,27 @@ static struct dccp_ackvec_record *dccp_ackvec_lookup(struct list_head *av_list, }/* +* Buffer index and length computation using modulo-buffersize arithmetic. +* Note that, as pointers move from right to left, head is `before' tail. +*/ +static inline u16 dccp_ackvec_idx_add (const u16 a,const u16 b) +{ +return (a + b) %DCCPAV_MAX_ACKVEC_LEN ; +} + +static inline u16 dccp_ackvec_idx_sub (const u16 a,const u16 b) +{ +return dccp_ackvec_idx_add (a,DCCPAV_MAX_ACKVEC_LEN - b); +} + +u16 dccp_ackvec_buflen (const struct dccp_ackvec *av) +{ +if (unlikely (av->av_overflow)) +return DCCPAV_MAX_ACKVEC_LEN ; +return dccp_ackvec_idx_sub (av->av_buf_tail, av->av_buf_head); +} + +/* * If several packets are missing, the HC-Receiver may prefer to enter multiple * bytes with run length 0, rather than a single byte with a larger run length; * this simplifies table updates if one of the missing packets arrives.
net/dccp/options.c +4 -3
@@ -432,9 +432,10 @@ static int dccp_insert_option_timestamp_echo(struct dccp_sock *dp,int dccp_insert_option_ackvec (struct sock *sk,struct sk_buff *skb) {struct dccp_ackvec *av =dccp_sk (sk)->dccps_hc_rx_ackvec; +const u16 buflen =dccp_ackvec_buflen (av);/* Figure out how many options do we need to represent the ackvec */ -const u16 nr_opts =DIV_ROUND_UP (av->av_vec_len,DCCP_SINGLE_OPT_MAXLEN ); -u16 len = av->av_vec_len +2 * nr_opts; +const u16 nr_opts =DIV_ROUND_UP (buflen,DCCP_SINGLE_OPT_MAXLEN ); +u16 len = buflen +2 * nr_opts;u8 i, nonce =0 ;const unsigned char *tail, *from;unsigned char *to;
@@ -451,7 +452,7 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)DCCP_SKB_CB (skb)->dccpd_opt_len += len; to =skb_push (skb, len); - len = av->av_vec_len; + len = buflen; from = av->av_buf + av->av_buf_head; tail = av->av_buf +DCCPAV_MAX_ACKVEC_LEN ;
Signature · 1 line
1.5.3.GIT
PATCH 11/14 : Implement algorithm to update buffer state
This implements an algorithm to consistently update the buffer state when the peer acknowledges receipt of Ack Vectors; updating state in the list of Ack Vectors as well as in the circular buffer.
The algorithm
* deals with HC-sender acknowledging to HC-receiver and vice versa,
* keeps track of the last unacknowledged but received seqno in tail_ackno,
* has special cases to reset the overflow condition when appropriate,
* is protected against receiving older information (would mess up buffer state).
--- net/dccp/ackvec.c | 82 ++++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 62 insertions(+), 20 deletions(-)
net/dccp/ackvec.c +62 -20
@@ -262,34 +262,76 @@ out_duplicate:return -EILSEQ ; } -static void dccp_ackvec_throw_record (struct dccp_ackvec *av, -struct dccp_ackvec_record *avr) -{ -struct dccp_ackvec_record *next; +/** +* dccp_ackvec_clear_state - Perform house-keeping / garbage-collection +* This routine is called when the peer acknowledges the receipt of Ack Vectors +* up to and including @ackno. While based on on section A.3 of RFC 4340, here +* are additional precautions to prevent corrupted buffer state. In particular, +* we use tail_ackno to identify outdated records; it always marks the earliest +* packet of group (2) in 11.4.2. +*/ +void dccp_ackvec_clear_state (struct dccp_ackvec *av,const u64 ackno) + { +struct dccp_ackvec_record *avr, *next; +u8 runlen_now, eff_runlen; +s64 delta; -/* sort out vector length */ -if (av->av_buf_head <= avr->avr_ack_ptr) - av->av_vec_len = avr->avr_ack_ptr - av->av_buf_head; -else - av->av_vec_len =DCCPAV_MAX_ACKVEC_LEN -1 - - av->av_buf_head + avr->avr_ack_ptr; + avr =dccp_ackvec_lookup (&av->av_records, ackno); +if (avr =NULL ) +return ; +/* +* Deal with outdated acknowledgments: this arises when e.g. there are +* several old records and the acks from the peer come in slowly. In +* that case we may still have records that pre-date tail_ackno. +*/ + delta =dccp_delta_seqno (av->av_tail_ackno, avr->avr_ack_ackno); +if (delta <0 ) + goto free_records; +/* +* Deal with overlapping Ack Vectors: don't subtract more than the +* number of packets between tail_ackno and ack_ackno. +*/ + eff_runlen = delta < avr->avr_ack_runlen ? delta : avr->avr_ack_runlen; -/* free records */ + runlen_now =dccp_ackvec_runlen (av->av_buf + avr->avr_ack_ptr); +/* +* The run length of Ack Vector cells does not decrease over time. If +* the run length is the same as at the time the Ack Vector was sent, we +* free the ack_ptr cell. That cell can however not be freed if the run +* length has increased: in this case we need to move the tail pointer +* backwards (towards higher indices), to its next-oldest neighbour. +*/ +if (runlen_now > eff_runlen) { + + av->av_buf[avr->avr_ack_ptr] -= eff_runlen +1 ; + av->av_buf_tail =dccp_ackvec_idx_add (avr->avr_ack_ptr,1 ); + +/* This move may not have cleared the overflow flag. */ +if (av->av_overflow) + av->av_overflow = (av->av_buf_head = av->av_buf_tail); + }else { + av->av_buf_tail = avr->avr_ack_ptr; +/* +* We have made sure that avr points to a valid cell within the +* buffer. This cell is either older than head, or equals head +* (empty buffer): in both cases we no longer have any overflow. +*/ + av->av_overflow =0 ; + } + +/* +* The peer has acknowledged up to and including ack_ackno. Hence the +* first packet in group (2) of 11.4.2 is the successor of ack_ackno. +*/ + av->av_tail_ackno =ADD48 (avr->avr_ack_ackno,1 ); + +free_records:list_for_each_entry_safe_from (avr, next, &av->av_records, avr_node) {list_del (&avr->avr_node);kmem_cache_free (dccp_ackvec_record_slab, avr); } } -void dccp_ackvec_clear_state (struct dccp_ackvec *av,const u64 ackno) -{ -struct dccp_ackvec_record *avr; - - avr =dccp_ackvec_lookup (&av->av_records, ackno); -if (avr !=NULL ) -dccp_ackvec_throw_record (av, avr); -} -int __init dccp_ackvec_init (void ) { dccp_ackvec_slab = kmem_cache_create("dccp_ackvec",
Signature · 1 line
1.5.3.GIT
PATCH 12/14 : Update Ack Vector input routine
This patch updates the code which registers new packets as received, using the new circular buffer interface, it now
* supports both tail/head pointers and buffer wrap-around,
* deals with overflow (head/tail move in lock-step), and
* uses dynamic buffer-length computation.
The updated code is also partioned differently, into
1. dealing with the empty buffer,
2. adding new packets into non-empty buffer,
3. reserving space when encountering a `hole' in the sequence space,
4. updating old state and deciding when old state is irrelevant.
Much of the old code and naming is reused.
--- net/dccp/ackvec.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++ net/dccp/ackvec.h | 6 +++ 2 files changed, 129 insertions(+), 0 deletions(-)
net/dccp/ackvec.h +6 -0
@@ -109,6 +109,7 @@ extern void dccp_ackvec_free(struct dccp_ackvec *av);extern int dccp_ackvec_add (struct dccp_ackvec *av,const struct sock *sk,const u64 ackno,const u8 state); +extern void dccp_ackvec_input (struct dccp_ackvec *av,u64 seqno,u8 state);extern int dccp_ackvec_update_records (struct dccp_ackvec *av,u64 seq,u8 sum);extern void dccp_ackvec_clear_state (struct dccp_ackvec *av,const u64 ackno);extern u16 dccp_ackvec_buflen (const struct dccp_ackvec *av);
@@ -136,6 +137,11 @@ static inline void dccp_ackvec_free(struct dccp_ackvec *av) { } +static inline void dccp_ackvec_input (struct dccp_ackvec *av,u64 seq,u8 state) +{ + +} +static inline int dccp_ackvec_add (struct dccp_ackvec *av,const struct sock *sk,const u64 ackno,const u8 state) {
net/dccp/ackvec.c +123 -0
129 lines not on this page · raw message
Signature · 1 line
1.5.3.GIT
PATCH 13/14 : Aggregate Ack-Vector related processing into single function
This aggregates Ack Vector processing (handling input and clearing old state) into one function, for the following reasons and benefits:
* duplicated code is removed;
* all Ack Vector-specific processing is now in one place;
* sanity: from an Ack Vector point of view, it is better to clear the old state first before entering new state;
* Ack Event handling happens mostly within the CCIDs, not the main DCCP module.
--- net/dccp/input.c | 38 +++++++++++--------------------------- 1 files changed, 11 insertions(+), 27 deletions(-)
net/dccp/input.c +11 -27
@@ -159,13 +159,16 @@ static void dccp_rcv_reset(struct sock *sk, struct sk_buff *skb)dccp_time_wait (sk,DCCP_TIME_WAIT ,0 ); } -static void dccp_event_ack_recv (struct sock *sk,struct sk_buff *skb) +static void dccp_handle_ackvec_processing (struct sock *sk,struct sk_buff *skb) { -struct dccp_sock *dp =dccp_sk (sk); +struct dccp_ackvec *av =dccp_sk (sk)->dccps_hc_rx_ackvec; +struct dccp_skb_cb *dcb =DCCP_SKB_CB (skb); -if (dp->dccps_hc_rx_ackvec !=NULL ) -dccp_ackvec_clear_state (dp->dccps_hc_rx_ackvec, -DCCP_SKB_CB (skb)->dccpd_ack_seq); +if (av !=NULL ) { +if (dcb->dccpd_ack_seq !=DCCP_PKT_WITHOUT_ACK_SEQ ) +dccp_ackvec_clear_state (av, dcb->dccpd_ack_seq); +dccp_ackvec_input (av, dcb->dccpd_seq,DCCPAV_RECEIVED ); + } }static void dccp_deliver_input_to_ccids (struct sock *sk,struct sk_buff *skb)
@@ -364,21 +367,13 @@ discard:int dccp_rcv_established (struct sock *sk,struct sk_buff *skb,const struct dccp_hdr *dh,const unsigned len) { -struct dccp_sock *dp =dccp_sk (sk); -if (dccp_check_seqno (sk, skb)) goto discard;if (dccp_parse_options (sk,NULL , skb)) goto discard; -if (DCCP_SKB_CB (skb)->dccpd_ack_seq !=DCCP_PKT_WITHOUT_ACK_SEQ ) -dccp_event_ack_recv (sk, skb); - -if (dp->dccps_hc_rx_ackvec !=NULL && -dccp_ackvec_add (dp->dccps_hc_rx_ackvec, sk, -DCCP_SKB_CB (skb)->dccpd_seq,DCCPAV_RECEIVED )) - goto discard; +dccp_handle_ackvec_processing (sk, skb);dccp_deliver_input_to_ccids (sk, skb);return __dccp_rcv_established (sk, skb, dh, len);
@@ -507,11 +502,7 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,*/ if (dccp_feat_activate_values (sk, &dp->dccps_featneg)) goto unable_to_proceed; - -if (dp->dccps_hc_rx_ackvec !=NULL && -dccp_ackvec_add (dp->dccps_hc_rx_ackvec, sk, -DCCP_SKB_CB (skb)->dccpd_seq,DCCPAV_RECEIVED )) - goto unable_to_proceed; +dccp_handle_ackvec_processing (sk, skb);dccp_send_ack (sk);return -1 ;
@@ -632,14 +623,7 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,if (dccp_parse_options (sk,NULL , skb)) goto discard; -if (dcb->dccpd_ack_seq !=DCCP_PKT_WITHOUT_ACK_SEQ ) -dccp_event_ack_recv (sk, skb); - -if (dp->dccps_hc_rx_ackvec !=NULL && -dccp_ackvec_add (dp->dccps_hc_rx_ackvec, sk, -DCCP_SKB_CB (skb)->dccpd_seq,DCCPAV_RECEIVED )) - goto discard; - +dccp_handle_ackvec_processing (sk, skb);dccp_deliver_input_to_ccids (sk, skb); }
Signature · 1 line
1.5.3.GIT
PATCH 14/14 : Remove old infrastructure
This removes
* functions for which updates have been provided in the preceding patches and
* the @av_vec_len field - it is no longer necessary since the buffer length is now always computed dynamically;
* conditional debugging code (CONFIG_IP_DCCP_ACKVEC).
The reason for removing the latter is that Ack Vectors are an almost inevitable necessity - RFC 4341 says if you use CCID2, Ack Vectors must be used. Furthermore, the code would be only interesting for going bug-hunting. Having dealt with several bugs/problems in the code already, I am positive that the old debugging code is no longer necessary.
Also updated copyrights.
--- net/dccp/Kconfig | 3 - net/dccp/Makefile | 5 +- net/dccp/ackvec.c | 141 +----------------------------------------------- net/dccp/ackvec.h | 64 +--------------------- net/dccp/ccids/Kconfig | 1 - 5 files changed, 7 insertions(+), 207 deletions(-)
net/dccp/Kconfig +0 -3
@@ -25,9 +25,6 @@ config INET_DCCP_DIAG def_tristate y if (IP_DCCP = y && INET_DIAG = y) def_tristate m -config IP_DCCP_ACKVEC - bool - source "net/dccp/ccids/Kconfig" menu "DCCP Kernel Hacking"
net/dccp/Makefile +2 -3
@@ -1,6 +1,7 @@ obj-$(CONFIG_IP_DCCP) += dccp.o dccp_ipv4.o -dccp-y := ccid.o feat.o input.o minisocks.o options.o output.o proto.o timer.o +dccp-y := ccid.o feat.o input.o minisocks.o options.o \ + output.o proto.o timer.o ackvec.o dccp_ipv4-y := ipv4.o
@@ -8,8 +9,6 @@ dccp_ipv4-y := ipv4.o obj-$(subst y,$(CONFIG_IP_DCCP),$(CONFIG_IPV6)) += dccp_ipv6.o dccp_ipv6-y := ipv6.o -dccp-$(CONFIG_IP_DCCP_ACKVEC) += ackvec.o - obj-$(CONFIG_INET_DCCP_DIAG) += dccp_diag.o obj-$(CONFIG_NET_DCCPPROBE) += dccp_probe.o
net/dccp/ackvec.c +2 -139
169 lines not on this page · raw message
net/dccp/ackvec.h +3 -61
98 lines not on this page · raw message
net/dccp/ccids/Kconfig +0 -1
@@ -4,7 +4,6 @@ menu "DCCP CCIDs Configuration (EXPERIMENTAL)" config IP_DCCP_CCID2 tristate "CCID2 (TCP-Like) (EXPERIMENTAL)" def_tristate IP_DCCP - select IP_DCCP_ACKVEC ---help--- CCID 2, TCP-like Congestion Control, denotes Additive Increase, Multiplicative Decrease (AIMD) congestion control with behavior
Signature · 1 line
1.5.3.GIT