diff options
| -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; |
