diff options
author | Gerrit Renker <gerrit@erg.abdn.ac.uk> | 2007-12-12 10:50:51 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 17:57:18 -0500 |
commit | 8a9c7e92e0ca97632126feee32ba2698b4eb6c8f (patch) | |
tree | b569d6e39f3630f7a973814a925502035c63904b /net/dccp | |
parent | 8995a238ef6869bc5c80240440bc58452c7af283 (diff) |
[TFRC]: Ringbuffer to track loss interval history
A ringbuffer-based implementation of loss interval history is easier to
maintain, allocate, and update.
The `swap' routine to keep the RX history sorted is due to and was written
by Arnaldo Carvalho de Melo, simplifying an earlier macro-based variant.
Details:
* access to the Loss Interval Records via macro wrappers (with safety checks);
* simplified, on-demand allocation of entries (no extra memory consumption on
lossless links); cache allocation is local to the module / exported as service;
* provision of RFC-compliant algorithm to re-compute average loss interval;
* provision of comprehensive, new loss detection algorithm
- support for all cases of loss, including re-ordered/duplicate packets;
- waiting for NDUPACK=3 packets to fill the hole;
- updating loss records when a late-arriving packet fills a hole.
Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk>
Signed-off-by: Ian McDonald <ian.mcdonald@jandi.co.nz>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dccp')
-rw-r--r-- | net/dccp/ccids/lib/loss_interval.c | 161 | ||||
-rw-r--r-- | net/dccp/ccids/lib/loss_interval.h | 56 | ||||
-rw-r--r-- | net/dccp/ccids/lib/packet_history.c | 218 | ||||
-rw-r--r-- | net/dccp/ccids/lib/packet_history.h | 11 | ||||
-rw-r--r-- | net/dccp/ccids/lib/tfrc.h | 3 |
5 files changed, 435 insertions, 14 deletions
diff --git a/net/dccp/ccids/lib/loss_interval.c b/net/dccp/ccids/lib/loss_interval.c index c0a933a1d28e..39980d1f5352 100644 --- a/net/dccp/ccids/lib/loss_interval.c +++ b/net/dccp/ccids/lib/loss_interval.c | |||
@@ -1,6 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * net/dccp/ccids/lib/loss_interval.c | 2 | * net/dccp/ccids/lib/loss_interval.c |
3 | * | 3 | * |
4 | * Copyright (c) 2007 The University of Aberdeen, Scotland, UK | ||
4 | * Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand. | 5 | * Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand. |
5 | * Copyright (c) 2005-7 Ian McDonald <ian.mcdonald@jandi.co.nz> | 6 | * Copyright (c) 2005-7 Ian McDonald <ian.mcdonald@jandi.co.nz> |
6 | * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br> | 7 | * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br> |
@@ -10,12 +11,7 @@ | |||
10 | * the Free Software Foundation; either version 2 of the License, or | 11 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 12 | * (at your option) any later version. |
12 | */ | 13 | */ |
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <net/sock.h> | 14 | #include <net/sock.h> |
16 | #include "../../dccp.h" | ||
17 | #include "loss_interval.h" | ||
18 | #include "packet_history.h" | ||
19 | #include "tfrc.h" | 15 | #include "tfrc.h" |
20 | 16 | ||
21 | #define DCCP_LI_HIST_IVAL_F_LENGTH 8 | 17 | #define DCCP_LI_HIST_IVAL_F_LENGTH 8 |
@@ -27,6 +23,54 @@ struct dccp_li_hist_entry { | |||
27 | u32 dccplih_interval; | 23 | u32 dccplih_interval; |
28 | }; | 24 | }; |
29 | 25 | ||
26 | static struct kmem_cache *tfrc_lh_slab __read_mostly; | ||
27 | /* Loss Interval weights from [RFC 3448, 5.4], scaled by 10 */ | ||
28 | static const int tfrc_lh_weights[NINTERVAL] = { 10, 10, 10, 10, 8, 6, 4, 2 }; | ||
29 | |||
30 | /* implements LIFO semantics on the array */ | ||
31 | static inline u8 LIH_INDEX(const u8 ctr) | ||
32 | { | ||
33 | return (LIH_SIZE - 1 - (ctr % LIH_SIZE)); | ||
34 | } | ||
35 | |||
36 | /* the `counter' index always points at the next entry to be populated */ | ||
37 | static inline struct tfrc_loss_interval *tfrc_lh_peek(struct tfrc_loss_hist *lh) | ||
38 | { | ||
39 | return lh->counter ? lh->ring[LIH_INDEX(lh->counter - 1)] : NULL; | ||
40 | } | ||
41 | |||
42 | /* given i with 0 <= i <= k, return I_i as per the rfc3448bis notation */ | ||
43 | static inline u32 tfrc_lh_get_interval(struct tfrc_loss_hist *lh, const u8 i) | ||
44 | { | ||
45 | BUG_ON(i >= lh->counter); | ||
46 | return lh->ring[LIH_INDEX(lh->counter - i - 1)]->li_length; | ||
47 | } | ||
48 | |||
49 | /* | ||
50 | * On-demand allocation and de-allocation of entries | ||
51 | */ | ||
52 | static struct tfrc_loss_interval *tfrc_lh_demand_next(struct tfrc_loss_hist *lh) | ||
53 | { | ||
54 | if (lh->ring[LIH_INDEX(lh->counter)] == NULL) | ||
55 | lh->ring[LIH_INDEX(lh->counter)] = kmem_cache_alloc(tfrc_lh_slab, | ||
56 | GFP_ATOMIC); | ||
57 | return lh->ring[LIH_INDEX(lh->counter)]; | ||
58 | } | ||
59 | |||
60 | void tfrc_lh_cleanup(struct tfrc_loss_hist *lh) | ||
61 | { | ||
62 | if (!tfrc_lh_is_initialised(lh)) | ||
63 | return; | ||
64 | |||
65 | for (lh->counter = 0; lh->counter < LIH_SIZE; lh->counter++) | ||
66 | if (lh->ring[LIH_INDEX(lh->counter)] != NULL) { | ||
67 | kmem_cache_free(tfrc_lh_slab, | ||
68 | lh->ring[LIH_INDEX(lh->counter)]); | ||
69 | lh->ring[LIH_INDEX(lh->counter)] = NULL; | ||
70 | } | ||
71 | } | ||
72 | EXPORT_SYMBOL_GPL(tfrc_lh_cleanup); | ||
73 | |||
30 | static struct kmem_cache *dccp_li_cachep __read_mostly; | 74 | static struct kmem_cache *dccp_li_cachep __read_mostly; |
31 | 75 | ||
32 | static inline struct dccp_li_hist_entry *dccp_li_hist_entry_new(const gfp_t prio) | 76 | static inline struct dccp_li_hist_entry *dccp_li_hist_entry_new(const gfp_t prio) |
@@ -98,6 +142,65 @@ u32 dccp_li_hist_calc_i_mean(struct list_head *list) | |||
98 | 142 | ||
99 | EXPORT_SYMBOL_GPL(dccp_li_hist_calc_i_mean); | 143 | EXPORT_SYMBOL_GPL(dccp_li_hist_calc_i_mean); |
100 | 144 | ||
145 | static void tfrc_lh_calc_i_mean(struct tfrc_loss_hist *lh) | ||
146 | { | ||
147 | u32 i_i, i_tot0 = 0, i_tot1 = 0, w_tot = 0; | ||
148 | int i, k = tfrc_lh_length(lh) - 1; /* k is as in rfc3448bis, 5.4 */ | ||
149 | |||
150 | for (i=0; i <= k; i++) { | ||
151 | i_i = tfrc_lh_get_interval(lh, i); | ||
152 | |||
153 | if (i < k) { | ||
154 | i_tot0 += i_i * tfrc_lh_weights[i]; | ||
155 | w_tot += tfrc_lh_weights[i]; | ||
156 | } | ||
157 | if (i > 0) | ||
158 | i_tot1 += i_i * tfrc_lh_weights[i-1]; | ||
159 | } | ||
160 | |||
161 | BUG_ON(w_tot == 0); | ||
162 | lh->i_mean = max(i_tot0, i_tot1) / w_tot; | ||
163 | } | ||
164 | |||
165 | /** | ||
166 | * tfrc_lh_update_i_mean - Update the `open' loss interval I_0 | ||
167 | * For recomputing p: returns `true' if p > p_prev <=> 1/p < 1/p_prev | ||
168 | */ | ||
169 | u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *skb) | ||
170 | { | ||
171 | struct tfrc_loss_interval *cur = tfrc_lh_peek(lh); | ||
172 | u32 old_i_mean = lh->i_mean; | ||
173 | s64 length; | ||
174 | |||
175 | if (cur == NULL) /* not initialised */ | ||
176 | return 0; | ||
177 | |||
178 | length = dccp_delta_seqno(cur->li_seqno, DCCP_SKB_CB(skb)->dccpd_seq); | ||
179 | |||
180 | if (length - cur->li_length <= 0) /* duplicate or reordered */ | ||
181 | return 0; | ||
182 | |||
183 | if (SUB16(dccp_hdr(skb)->dccph_ccval, cur->li_ccval) > 4) | ||
184 | /* | ||
185 | * Implements RFC 4342, 10.2: | ||
186 | * If a packet S (skb) exists whose seqno comes `after' the one | ||
187 | * starting the current loss interval (cur) and if the modulo-16 | ||
188 | * distance from C(cur) to C(S) is greater than 4, consider all | ||
189 | * subsequent packets as belonging to a new loss interval. This | ||
190 | * test is necessary since CCVal may wrap between intervals. | ||
191 | */ | ||
192 | cur->li_is_closed = 1; | ||
193 | |||
194 | if (tfrc_lh_length(lh) == 1) /* due to RFC 3448, 6.3.1 */ | ||
195 | return 0; | ||
196 | |||
197 | cur->li_length = length; | ||
198 | tfrc_lh_calc_i_mean(lh); | ||
199 | |||
200 | return (lh->i_mean < old_i_mean); | ||
201 | } | ||
202 | EXPORT_SYMBOL_GPL(tfrc_lh_update_i_mean); | ||
203 | |||
101 | static int dccp_li_hist_interval_new(struct list_head *list, | 204 | static int dccp_li_hist_interval_new(struct list_head *list, |
102 | const u64 seq_loss, const u8 win_loss) | 205 | const u64 seq_loss, const u8 win_loss) |
103 | { | 206 | { |
@@ -284,6 +387,54 @@ void dccp_li_update_li(struct sock *sk, | |||
284 | 387 | ||
285 | EXPORT_SYMBOL_GPL(dccp_li_update_li); | 388 | EXPORT_SYMBOL_GPL(dccp_li_update_li); |
286 | 389 | ||
390 | /* Determine if `new_loss' does begin a new loss interval [RFC 4342, 10.2] */ | ||
391 | static inline u8 tfrc_lh_is_new_loss(struct tfrc_loss_interval *cur, | ||
392 | struct tfrc_rx_hist_entry *new_loss) | ||
393 | { | ||
394 | return dccp_delta_seqno(cur->li_seqno, new_loss->tfrchrx_seqno) > 0 && | ||
395 | (cur->li_is_closed || SUB16(new_loss->tfrchrx_ccval, cur->li_ccval) > 4); | ||
396 | } | ||
397 | |||
398 | /** tfrc_lh_interval_add - Insert new record into the Loss Interval database | ||
399 | * @lh: Loss Interval database | ||
400 | * @rh: Receive history containing a fresh loss event | ||
401 | * @calc_first_li: Caller-dependent routine to compute length of first interval | ||
402 | * @sk: Used by @calc_first_li in caller-specific way (subtyping) | ||
403 | * Updates I_mean and returns 1 if a new interval has in fact been added to @lh. | ||
404 | */ | ||
405 | int tfrc_lh_interval_add(struct tfrc_loss_hist *lh, struct tfrc_rx_hist *rh, | ||
406 | u32 (*calc_first_li)(struct sock *), struct sock *sk) | ||
407 | { | ||
408 | struct tfrc_loss_interval *cur = tfrc_lh_peek(lh), *new; | ||
409 | |||
410 | if (cur != NULL && !tfrc_lh_is_new_loss(cur, tfrc_rx_hist_loss_prev(rh))) | ||
411 | return 0; | ||
412 | |||
413 | new = tfrc_lh_demand_next(lh); | ||
414 | if (unlikely(new == NULL)) { | ||
415 | DCCP_CRIT("Cannot allocate/add loss record."); | ||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | new->li_seqno = tfrc_rx_hist_loss_prev(rh)->tfrchrx_seqno; | ||
420 | new->li_ccval = tfrc_rx_hist_loss_prev(rh)->tfrchrx_ccval; | ||
421 | new->li_is_closed = 0; | ||
422 | |||
423 | if (++lh->counter == 1) | ||
424 | lh->i_mean = new->li_length = (*calc_first_li)(sk); | ||
425 | else { | ||
426 | cur->li_length = dccp_delta_seqno(cur->li_seqno, new->li_seqno); | ||
427 | new->li_length = dccp_delta_seqno(new->li_seqno, | ||
428 | tfrc_rx_hist_last_rcv(rh)->tfrchrx_seqno); | ||
429 | if (lh->counter > (2*LIH_SIZE)) | ||
430 | lh->counter -= LIH_SIZE; | ||
431 | |||
432 | tfrc_lh_calc_i_mean(lh); | ||
433 | } | ||
434 | return 1; | ||
435 | } | ||
436 | EXPORT_SYMBOL_GPL(tfrc_lh_interval_add); | ||
437 | |||
287 | int __init dccp_li_init(void) | 438 | int __init dccp_li_init(void) |
288 | { | 439 | { |
289 | dccp_li_cachep = kmem_cache_create("dccp_li_hist", | 440 | dccp_li_cachep = kmem_cache_create("dccp_li_hist", |
diff --git a/net/dccp/ccids/lib/loss_interval.h b/net/dccp/ccids/lib/loss_interval.h index 27bee92dae13..5e3c5c54a495 100644 --- a/net/dccp/ccids/lib/loss_interval.h +++ b/net/dccp/ccids/lib/loss_interval.h | |||
@@ -3,6 +3,7 @@ | |||
3 | /* | 3 | /* |
4 | * net/dccp/ccids/lib/loss_interval.h | 4 | * net/dccp/ccids/lib/loss_interval.h |
5 | * | 5 | * |
6 | * Copyright (c) 2007 The University of Aberdeen, Scotland, UK | ||
6 | * Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand. | 7 | * Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand. |
7 | * Copyright (c) 2005-7 Ian McDonald <ian.mcdonald@jandi.co.nz> | 8 | * Copyright (c) 2005-7 Ian McDonald <ian.mcdonald@jandi.co.nz> |
8 | * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br> | 9 | * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br> |
@@ -12,11 +13,64 @@ | |||
12 | * Software Foundation; either version 2 of the License, or (at your option) | 13 | * Software Foundation; either version 2 of the License, or (at your option) |
13 | * any later version. | 14 | * any later version. |
14 | */ | 15 | */ |
15 | |||
16 | #include <linux/ktime.h> | 16 | #include <linux/ktime.h> |
17 | #include <linux/list.h> | 17 | #include <linux/list.h> |
18 | #include <linux/slab.h> | ||
19 | |||
20 | /* | ||
21 | * Number of loss intervals (RFC 4342, 8.6.1). The history size is one more than | ||
22 | * NINTERVAL, since the `open' interval I_0 is always stored as the first entry. | ||
23 | */ | ||
24 | #define NINTERVAL 8 | ||
25 | #define LIH_SIZE (NINTERVAL + 1) | ||
26 | |||
27 | /** | ||
28 | * tfrc_loss_interval - Loss history record for TFRC-based protocols | ||
29 | * @li_seqno: Highest received seqno before the start of loss | ||
30 | * @li_ccval: The CCVal belonging to @li_seqno | ||
31 | * @li_is_closed: Whether @li_seqno is older than 1 RTT | ||
32 | * @li_length: Loss interval sequence length | ||
33 | */ | ||
34 | struct tfrc_loss_interval { | ||
35 | u64 li_seqno:48, | ||
36 | li_ccval:4, | ||
37 | li_is_closed:1; | ||
38 | u32 li_length; | ||
39 | }; | ||
40 | |||
41 | /** | ||
42 | * tfrc_loss_hist - Loss record database | ||
43 | * @ring: Circular queue managed in LIFO manner | ||
44 | * @counter: Current count of entries (can be more than %LIH_SIZE) | ||
45 | * @i_mean: Current Average Loss Interval [RFC 3448, 5.4] | ||
46 | */ | ||
47 | struct tfrc_loss_hist { | ||
48 | struct tfrc_loss_interval *ring[LIH_SIZE]; | ||
49 | u8 counter; | ||
50 | u32 i_mean; | ||
51 | }; | ||
52 | |||
53 | static inline void tfrc_lh_init(struct tfrc_loss_hist *lh) | ||
54 | { | ||
55 | memset(lh, 0, sizeof(struct tfrc_loss_hist)); | ||
56 | } | ||
57 | |||
58 | static inline u8 tfrc_lh_is_initialised(struct tfrc_loss_hist *lh) | ||
59 | { | ||
60 | return lh->counter > 0; | ||
61 | } | ||
62 | |||
63 | static inline u8 tfrc_lh_length(struct tfrc_loss_hist *lh) | ||
64 | { | ||
65 | return min(lh->counter, (u8)LIH_SIZE); | ||
66 | } | ||
18 | 67 | ||
19 | extern void dccp_li_hist_purge(struct list_head *list); | 68 | extern void dccp_li_hist_purge(struct list_head *list); |
69 | struct tfrc_rx_hist; | ||
70 | extern int tfrc_lh_interval_add(struct tfrc_loss_hist *, struct tfrc_rx_hist *, | ||
71 | u32 (*first_li)(struct sock *), struct sock *); | ||
72 | extern u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *); | ||
73 | extern void tfrc_lh_cleanup(struct tfrc_loss_hist *lh); | ||
20 | 74 | ||
21 | extern u32 dccp_li_hist_calc_i_mean(struct list_head *list); | 75 | extern u32 dccp_li_hist_calc_i_mean(struct list_head *list); |
22 | 76 | ||
diff --git a/net/dccp/ccids/lib/packet_history.c b/net/dccp/ccids/lib/packet_history.c index dd2cf2d6b8fc..5b10a1ecf138 100644 --- a/net/dccp/ccids/lib/packet_history.c +++ b/net/dccp/ccids/lib/packet_history.c | |||
@@ -151,11 +151,10 @@ void tfrc_rx_packet_history_exit(void) | |||
151 | } | 151 | } |
152 | } | 152 | } |
153 | 153 | ||
154 | void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, | 154 | static inline void tfrc_rx_hist_entry_from_skb(struct tfrc_rx_hist_entry *entry, |
155 | const struct sk_buff *skb, | 155 | const struct sk_buff *skb, |
156 | const u32 ndp) | 156 | const u32 ndp) |
157 | { | 157 | { |
158 | struct tfrc_rx_hist_entry *entry = tfrc_rx_hist_last_rcv(h); | ||
159 | const struct dccp_hdr *dh = dccp_hdr(skb); | 158 | const struct dccp_hdr *dh = dccp_hdr(skb); |
160 | 159 | ||
161 | entry->tfrchrx_seqno = DCCP_SKB_CB(skb)->dccpd_seq; | 160 | entry->tfrchrx_seqno = DCCP_SKB_CB(skb)->dccpd_seq; |
@@ -164,6 +163,15 @@ void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, | |||
164 | entry->tfrchrx_ndp = ndp; | 163 | entry->tfrchrx_ndp = ndp; |
165 | entry->tfrchrx_tstamp = ktime_get_real(); | 164 | entry->tfrchrx_tstamp = ktime_get_real(); |
166 | } | 165 | } |
166 | |||
167 | void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, | ||
168 | const struct sk_buff *skb, | ||
169 | const u32 ndp) | ||
170 | { | ||
171 | struct tfrc_rx_hist_entry *entry = tfrc_rx_hist_last_rcv(h); | ||
172 | |||
173 | tfrc_rx_hist_entry_from_skb(entry, skb, ndp); | ||
174 | } | ||
167 | EXPORT_SYMBOL_GPL(tfrc_rx_hist_add_packet); | 175 | EXPORT_SYMBOL_GPL(tfrc_rx_hist_add_packet); |
168 | 176 | ||
169 | /* has the packet contained in skb been seen before? */ | 177 | /* has the packet contained in skb been seen before? */ |
@@ -209,6 +217,208 @@ int tfrc_rx_hist_new_loss_indicated(struct tfrc_rx_hist *h, | |||
209 | } | 217 | } |
210 | EXPORT_SYMBOL_GPL(tfrc_rx_hist_new_loss_indicated); | 218 | EXPORT_SYMBOL_GPL(tfrc_rx_hist_new_loss_indicated); |
211 | 219 | ||
220 | static void tfrc_rx_hist_swap(struct tfrc_rx_hist *h, const u8 a, const u8 b) | ||
221 | { | ||
222 | const u8 idx_a = tfrc_rx_hist_index(h, a), | ||
223 | idx_b = tfrc_rx_hist_index(h, b); | ||
224 | struct tfrc_rx_hist_entry *tmp = h->ring[idx_a]; | ||
225 | |||
226 | h->ring[idx_a] = h->ring[idx_b]; | ||
227 | h->ring[idx_b] = tmp; | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * Private helper functions for loss detection. | ||
232 | * | ||
233 | * In the descriptions, `Si' refers to the sequence number of entry number i, | ||
234 | * whose NDP count is `Ni' (lower case is used for variables). | ||
235 | * Note: All __after_loss functions expect that a test against duplicates has | ||
236 | * been performed already: the seqno of the skb must not be less than the | ||
237 | * seqno of loss_prev; and it must not equal that of any valid hist_entry. | ||
238 | */ | ||
239 | static void __one_after_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u32 n2) | ||
240 | { | ||
241 | u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno, | ||
242 | s1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_seqno, | ||
243 | s2 = DCCP_SKB_CB(skb)->dccpd_seq; | ||
244 | int n1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_ndp, | ||
245 | d12 = dccp_delta_seqno(s1, s2), d2; | ||
246 | |||
247 | if (d12 > 0) { /* S1 < S2 */ | ||
248 | h->loss_count = 2; | ||
249 | tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 2), skb, n2); | ||
250 | return; | ||
251 | } | ||
252 | |||
253 | /* S0 < S2 < S1 */ | ||
254 | d2 = dccp_delta_seqno(s0, s2); | ||
255 | |||
256 | if (d2 == 1 || n2 >= d2) { /* S2 is direct successor of S0 */ | ||
257 | int d21 = -d12; | ||
258 | |||
259 | if (d21 == 1 || n1 >= d21) { | ||
260 | /* hole is filled: S0, S2, and S1 are consecutive */ | ||
261 | h->loss_count = 0; | ||
262 | h->loss_start = tfrc_rx_hist_index(h, 1); | ||
263 | } else | ||
264 | /* gap between S2 and S1: just update loss_prev */ | ||
265 | tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_loss_prev(h), skb, n2); | ||
266 | |||
267 | } else { /* hole between S0 and S2 */ | ||
268 | /* | ||
269 | * Reorder history to insert S2 between S0 and s1 | ||
270 | */ | ||
271 | tfrc_rx_hist_swap(h, 0, 3); | ||
272 | h->loss_start = tfrc_rx_hist_index(h, 3); | ||
273 | tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n2); | ||
274 | h->loss_count = 2; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | /* return 1 if a new loss event has been identified */ | ||
279 | static int __two_after_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u32 n3) | ||
280 | { | ||
281 | u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno, | ||
282 | s1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_seqno, | ||
283 | s2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_seqno, | ||
284 | s3 = DCCP_SKB_CB(skb)->dccpd_seq; | ||
285 | int n1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_ndp, | ||
286 | d23 = dccp_delta_seqno(s2, s3), d13, d3, d31; | ||
287 | |||
288 | if (d23 > 0) { /* S2 < S3 */ | ||
289 | h->loss_count = 3; | ||
290 | tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 3), skb, n3); | ||
291 | return 1; | ||
292 | } | ||
293 | |||
294 | /* S3 < S2 */ | ||
295 | d13 = dccp_delta_seqno(s1, s3); | ||
296 | |||
297 | if (d13 > 0) { | ||
298 | /* | ||
299 | * The sequence number order is S1, S3, S2 | ||
300 | * Reorder history to insert entry between S1 and S2 | ||
301 | */ | ||
302 | tfrc_rx_hist_swap(h, 2, 3); | ||
303 | tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 2), skb, n3); | ||
304 | h->loss_count = 3; | ||
305 | return 1; | ||
306 | } | ||
307 | |||
308 | /* S0 < S3 < S1 */ | ||
309 | d31 = -d13; | ||
310 | d3 = dccp_delta_seqno(s0, s3); | ||
311 | |||
312 | if (d3 == 1 || n3 >= d3) { /* S3 is a successor of S0 */ | ||
313 | |||
314 | if (d31 == 1 || n1 >= d31) { | ||
315 | /* hole between S0 and S1 filled by S3 */ | ||
316 | int d2 = dccp_delta_seqno(s1, s2), | ||
317 | n2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_ndp; | ||
318 | |||
319 | if (d2 == 1 || n2 >= d2) { | ||
320 | /* entire hole filled by S0, S3, S1, S2 */ | ||
321 | h->loss_start = tfrc_rx_hist_index(h, 2); | ||
322 | h->loss_count = 0; | ||
323 | } else { | ||
324 | /* gap remains between S1 and S2 */ | ||
325 | h->loss_start = tfrc_rx_hist_index(h, 1); | ||
326 | h->loss_count = 1; | ||
327 | } | ||
328 | |||
329 | } else /* gap exists between S3 and S1, loss_count stays at 2 */ | ||
330 | tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_loss_prev(h), skb, n3); | ||
331 | |||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * The remaining case: S3 is not a successor of S0. | ||
337 | * Sequence order is S0, S3, S1, S2; reorder to insert between S0 and S1 | ||
338 | */ | ||
339 | tfrc_rx_hist_swap(h, 0, 3); | ||
340 | h->loss_start = tfrc_rx_hist_index(h, 3); | ||
341 | tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n3); | ||
342 | h->loss_count = 3; | ||
343 | |||
344 | return 1; | ||
345 | } | ||
346 | |||
347 | /* return the signed modulo-2^48 sequence number distance from entry e1 to e2 */ | ||
348 | static s64 tfrc_rx_hist_delta_seqno(struct tfrc_rx_hist *h, u8 e1, u8 e2) | ||
349 | { | ||
350 | DCCP_BUG_ON(e1 > h->loss_count || e2 > h->loss_count); | ||
351 | |||
352 | return dccp_delta_seqno(tfrc_rx_hist_entry(h, e1)->tfrchrx_seqno, | ||
353 | tfrc_rx_hist_entry(h, e2)->tfrchrx_seqno); | ||
354 | } | ||
355 | |||
356 | /* recycle RX history records to continue loss detection if necessary */ | ||
357 | static void __three_after_loss(struct tfrc_rx_hist *h) | ||
358 | { | ||
359 | /* | ||
360 | * The distance between S0 and S1 is always greater than 1 and the NDP | ||
361 | * count of S1 is smaller than this distance. Otherwise there would | ||
362 | * have been no loss. Hence it is only necessary to see whether there | ||
363 | * are further missing data packets between S1/S2 and S2/S3. | ||
364 | */ | ||
365 | int d2 = tfrc_rx_hist_delta_seqno(h, 1, 2), | ||
366 | d3 = tfrc_rx_hist_delta_seqno(h, 2, 3), | ||
367 | n2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_ndp, | ||
368 | n3 = tfrc_rx_hist_entry(h, 3)->tfrchrx_ndp; | ||
369 | |||
370 | if (d2 == 1 || n2 >= d2) { /* S2 is successor to S1 */ | ||
371 | |||
372 | if (d3 == 1 || n3 >= d3) { | ||
373 | /* S3 is successor of S2: entire hole is filled */ | ||
374 | h->loss_start = tfrc_rx_hist_index(h, 3); | ||
375 | h->loss_count = 0; | ||
376 | } else { | ||
377 | /* gap between S2 and S3 */ | ||
378 | h->loss_start = tfrc_rx_hist_index(h, 2); | ||
379 | h->loss_count = 1; | ||
380 | } | ||
381 | |||
382 | } else { /* gap between S1 and S2 */ | ||
383 | h->loss_start = tfrc_rx_hist_index(h, 1); | ||
384 | h->loss_count = 2; | ||
385 | } | ||
386 | } | ||
387 | |||
388 | /** | ||
389 | * tfrc_rx_handle_loss - Loss detection and further processing | ||
390 | * @h: The non-empty RX history object | ||
391 | * @lh: Loss Intervals database to update | ||
392 | * @skb: Currently received packet | ||
393 | * @ndp: The NDP count belonging to @skb | ||
394 | * @calc_first_li: Caller-dependent computation of first loss interval in @lh | ||
395 | * @sk: Used by @calc_first_li (see tfrc_lh_interval_add) | ||
396 | * Chooses action according to pending loss, updates LI database when a new | ||
397 | * loss was detected, and does required post-processing. Returns 1 when caller | ||
398 | * should send feedback, 0 otherwise. | ||
399 | */ | ||
400 | int tfrc_rx_handle_loss(struct tfrc_rx_hist *h, | ||
401 | struct tfrc_loss_hist *lh, | ||
402 | struct sk_buff *skb, u32 ndp, | ||
403 | u32 (*calc_first_li)(struct sock *), struct sock *sk) | ||
404 | { | ||
405 | int is_new_loss = 0; | ||
406 | |||
407 | if (h->loss_count == 1) { | ||
408 | __one_after_loss(h, skb, ndp); | ||
409 | } else if (h->loss_count != 2) { | ||
410 | DCCP_BUG("invalid loss_count %d", h->loss_count); | ||
411 | } else if (__two_after_loss(h, skb, ndp)) { | ||
412 | /* | ||
413 | * Update Loss Interval database and recycle RX records | ||
414 | */ | ||
415 | is_new_loss = tfrc_lh_interval_add(lh, h, calc_first_li, sk); | ||
416 | __three_after_loss(h); | ||
417 | } | ||
418 | return is_new_loss; | ||
419 | } | ||
420 | EXPORT_SYMBOL_GPL(tfrc_rx_handle_loss); | ||
421 | |||
212 | int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h) | 422 | int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h) |
213 | { | 423 | { |
214 | int i; | 424 | int i; |
diff --git a/net/dccp/ccids/lib/packet_history.h b/net/dccp/ccids/lib/packet_history.h index e58b0fcdc6ae..24edd8df54b9 100644 --- a/net/dccp/ccids/lib/packet_history.h +++ b/net/dccp/ccids/lib/packet_history.h | |||
@@ -36,10 +36,9 @@ | |||
36 | #ifndef _DCCP_PKT_HIST_ | 36 | #ifndef _DCCP_PKT_HIST_ |
37 | #define _DCCP_PKT_HIST_ | 37 | #define _DCCP_PKT_HIST_ |
38 | 38 | ||
39 | #include <linux/ktime.h> | 39 | #include <linux/list.h> |
40 | #include <linux/types.h> | 40 | #include <linux/slab.h> |
41 | 41 | #include "tfrc.h" | |
42 | struct sk_buff; | ||
43 | 42 | ||
44 | struct tfrc_tx_hist_entry; | 43 | struct tfrc_tx_hist_entry; |
45 | 44 | ||
@@ -125,6 +124,10 @@ extern void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, | |||
125 | extern int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb); | 124 | extern int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb); |
126 | extern int tfrc_rx_hist_new_loss_indicated(struct tfrc_rx_hist *h, | 125 | extern int tfrc_rx_hist_new_loss_indicated(struct tfrc_rx_hist *h, |
127 | const struct sk_buff *skb, u32 ndp); | 126 | const struct sk_buff *skb, u32 ndp); |
127 | struct tfrc_loss_hist; | ||
128 | extern int tfrc_rx_handle_loss(struct tfrc_rx_hist *, struct tfrc_loss_hist *, | ||
129 | struct sk_buff *skb, u32 ndp, | ||
130 | u32 (*first_li)(struct sock *), struct sock *); | ||
128 | extern u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, | 131 | extern u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, |
129 | const struct sk_buff *skb); | 132 | const struct sk_buff *skb); |
130 | extern int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h); | 133 | extern int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h); |
diff --git a/net/dccp/ccids/lib/tfrc.h b/net/dccp/ccids/lib/tfrc.h index ab8848c0f8c9..1fb1187bbf1c 100644 --- a/net/dccp/ccids/lib/tfrc.h +++ b/net/dccp/ccids/lib/tfrc.h | |||
@@ -17,6 +17,9 @@ | |||
17 | #include <linux/types.h> | 17 | #include <linux/types.h> |
18 | #include <asm/div64.h> | 18 | #include <asm/div64.h> |
19 | #include "../../dccp.h" | 19 | #include "../../dccp.h" |
20 | /* internal includes that this module exports: */ | ||
21 | #include "loss_interval.h" | ||
22 | #include "packet_history.h" | ||
20 | 23 | ||
21 | #ifdef CONFIG_IP_DCCP_TFRC_DEBUG | 24 | #ifdef CONFIG_IP_DCCP_TFRC_DEBUG |
22 | extern int tfrc_debug; | 25 | extern int tfrc_debug; |