aboutsummaryrefslogtreecommitdiffstats
path: root/net/dccp
diff options
context:
space:
mode:
authorGerrit Renker <gerrit@erg.abdn.ac.uk>2007-12-12 10:50:51 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 17:57:18 -0500
commit8a9c7e92e0ca97632126feee32ba2698b4eb6c8f (patch)
treeb569d6e39f3630f7a973814a925502035c63904b /net/dccp
parent8995a238ef6869bc5c80240440bc58452c7af283 (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.c161
-rw-r--r--net/dccp/ccids/lib/loss_interval.h56
-rw-r--r--net/dccp/ccids/lib/packet_history.c218
-rw-r--r--net/dccp/ccids/lib/packet_history.h11
-rw-r--r--net/dccp/ccids/lib/tfrc.h3
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
26static struct kmem_cache *tfrc_lh_slab __read_mostly;
27/* Loss Interval weights from [RFC 3448, 5.4], scaled by 10 */
28static const int tfrc_lh_weights[NINTERVAL] = { 10, 10, 10, 10, 8, 6, 4, 2 };
29
30/* implements LIFO semantics on the array */
31static 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 */
37static 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 */
43static 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 */
52static 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
60void 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}
72EXPORT_SYMBOL_GPL(tfrc_lh_cleanup);
73
30static struct kmem_cache *dccp_li_cachep __read_mostly; 74static struct kmem_cache *dccp_li_cachep __read_mostly;
31 75
32static inline struct dccp_li_hist_entry *dccp_li_hist_entry_new(const gfp_t prio) 76static 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
99EXPORT_SYMBOL_GPL(dccp_li_hist_calc_i_mean); 143EXPORT_SYMBOL_GPL(dccp_li_hist_calc_i_mean);
100 144
145static 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 */
169u8 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}
202EXPORT_SYMBOL_GPL(tfrc_lh_update_i_mean);
203
101static int dccp_li_hist_interval_new(struct list_head *list, 204static 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
285EXPORT_SYMBOL_GPL(dccp_li_update_li); 388EXPORT_SYMBOL_GPL(dccp_li_update_li);
286 389
390/* Determine if `new_loss' does begin a new loss interval [RFC 4342, 10.2] */
391static 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 */
405int 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}
436EXPORT_SYMBOL_GPL(tfrc_lh_interval_add);
437
287int __init dccp_li_init(void) 438int __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 */
34struct 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 */
47struct tfrc_loss_hist {
48 struct tfrc_loss_interval *ring[LIH_SIZE];
49 u8 counter;
50 u32 i_mean;
51};
52
53static inline void tfrc_lh_init(struct tfrc_loss_hist *lh)
54{
55 memset(lh, 0, sizeof(struct tfrc_loss_hist));
56}
57
58static inline u8 tfrc_lh_is_initialised(struct tfrc_loss_hist *lh)
59{
60 return lh->counter > 0;
61}
62
63static inline u8 tfrc_lh_length(struct tfrc_loss_hist *lh)
64{
65 return min(lh->counter, (u8)LIH_SIZE);
66}
18 67
19extern void dccp_li_hist_purge(struct list_head *list); 68extern void dccp_li_hist_purge(struct list_head *list);
69struct tfrc_rx_hist;
70extern int tfrc_lh_interval_add(struct tfrc_loss_hist *, struct tfrc_rx_hist *,
71 u32 (*first_li)(struct sock *), struct sock *);
72extern u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *);
73extern void tfrc_lh_cleanup(struct tfrc_loss_hist *lh);
20 74
21extern u32 dccp_li_hist_calc_i_mean(struct list_head *list); 75extern 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
154void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, 154static 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
167void 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}
167EXPORT_SYMBOL_GPL(tfrc_rx_hist_add_packet); 175EXPORT_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}
210EXPORT_SYMBOL_GPL(tfrc_rx_hist_new_loss_indicated); 218EXPORT_SYMBOL_GPL(tfrc_rx_hist_new_loss_indicated);
211 219
220static 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 */
239static 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 */
279static 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 */
348static 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 */
357static 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 */
400int 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}
420EXPORT_SYMBOL_GPL(tfrc_rx_handle_loss);
421
212int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h) 422int 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"
42struct sk_buff;
43 42
44struct tfrc_tx_hist_entry; 43struct tfrc_tx_hist_entry;
45 44
@@ -125,6 +124,10 @@ extern void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h,
125extern int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb); 124extern int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb);
126extern int tfrc_rx_hist_new_loss_indicated(struct tfrc_rx_hist *h, 125extern 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);
127struct tfrc_loss_hist;
128extern 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 *);
128extern u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, 131extern u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h,
129 const struct sk_buff *skb); 132 const struct sk_buff *skb);
130extern int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h); 133extern 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
22extern int tfrc_debug; 25extern int tfrc_debug;