Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 1 | /* |
Ian McDonald | f3166c0 | 2006-08-26 19:01:03 -0700 | [diff] [blame] | 2 | * net/dccp/packet_history.c |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 3 | * |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 4 | * Copyright (c) 2007 The University of Aberdeen, Scotland, UK |
| 5 | * Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand. |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 6 | * |
| 7 | * An implementation of the DCCP protocol |
| 8 | * |
| 9 | * This code has been developed by the University of Waikato WAND |
| 10 | * research group. For further information please see http://www.wand.net.nz/ |
Ian McDonald | e6bccd3 | 2006-08-26 19:01:30 -0700 | [diff] [blame] | 11 | * or e-mail Ian McDonald - ian.mcdonald@jandi.co.nz |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 12 | * |
| 13 | * This code also uses code from Lulea University, rereleased as GPL by its |
| 14 | * authors: |
| 15 | * Copyright (c) 2003 Nils-Erik Mattsson, Joacim Haggmark, Magnus Erixzon |
| 16 | * |
| 17 | * Changes to meet Linux coding standards, to make it meet latest ccid3 draft |
| 18 | * and to make it work as a loadable module in the DCCP stack written by |
| 19 | * Arnaldo Carvalho de Melo <acme@conectiva.com.br>. |
| 20 | * |
| 21 | * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br> |
| 22 | * |
| 23 | * This program is free software; you can redistribute it and/or modify |
| 24 | * it under the terms of the GNU General Public License as published by |
| 25 | * the Free Software Foundation; either version 2 of the License, or |
| 26 | * (at your option) any later version. |
| 27 | * |
| 28 | * This program is distributed in the hope that it will be useful, |
| 29 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 30 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 31 | * GNU General Public License for more details. |
| 32 | * |
| 33 | * You should have received a copy of the GNU General Public License |
| 34 | * along with this program; if not, write to the Free Software |
| 35 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| 36 | */ |
| 37 | |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 38 | #include <linux/string.h> |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 39 | #include <linux/slab.h> |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 40 | #include "packet_history.h" |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 41 | #include "../../dccp.h" |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 42 | |
Arnaldo Carvalho de Melo | 9108d5f | 2007-11-29 22:47:15 -0200 | [diff] [blame] | 43 | /** |
| 44 | * tfrc_tx_hist_entry - Simple singly-linked TX history list |
| 45 | * @next: next oldest entry (LIFO order) |
| 46 | * @seqno: sequence number of this entry |
| 47 | * @stamp: send time of packet with sequence number @seqno |
| 48 | */ |
| 49 | struct tfrc_tx_hist_entry { |
| 50 | struct tfrc_tx_hist_entry *next; |
| 51 | u64 seqno; |
| 52 | ktime_t stamp; |
| 53 | }; |
| 54 | |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 55 | /* |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 56 | * Transmitter History Routines |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 57 | */ |
Arnaldo Carvalho de Melo | e9c8b24a | 2007-12-06 12:27:49 -0200 | [diff] [blame] | 58 | static struct kmem_cache *tfrc_tx_hist_slab; |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 59 | |
Gerrit Renker | df8f83f | 2007-12-12 12:24:49 -0200 | [diff] [blame] | 60 | int __init tfrc_tx_packet_history_init(void) |
| 61 | { |
| 62 | tfrc_tx_hist_slab = kmem_cache_create("tfrc_tx_hist", |
| 63 | sizeof(struct tfrc_tx_hist_entry), |
| 64 | 0, SLAB_HWCACHE_ALIGN, NULL); |
| 65 | return tfrc_tx_hist_slab == NULL ? -ENOBUFS : 0; |
| 66 | } |
| 67 | |
| 68 | void tfrc_tx_packet_history_exit(void) |
| 69 | { |
| 70 | if (tfrc_tx_hist_slab != NULL) { |
| 71 | kmem_cache_destroy(tfrc_tx_hist_slab); |
| 72 | tfrc_tx_hist_slab = NULL; |
| 73 | } |
| 74 | } |
| 75 | |
Arnaldo Carvalho de Melo | 9108d5f | 2007-11-29 22:47:15 -0200 | [diff] [blame] | 76 | static struct tfrc_tx_hist_entry * |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 77 | tfrc_tx_hist_find_entry(struct tfrc_tx_hist_entry *head, u64 seqno) |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 78 | { |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 79 | while (head != NULL && head->seqno != seqno) |
| 80 | head = head->next; |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 81 | |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 82 | return head; |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 83 | } |
| 84 | |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 85 | int tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno) |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 86 | { |
Arnaldo Carvalho de Melo | e9c8b24a | 2007-12-06 12:27:49 -0200 | [diff] [blame] | 87 | struct tfrc_tx_hist_entry *entry = kmem_cache_alloc(tfrc_tx_hist_slab, gfp_any()); |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 88 | |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 89 | if (entry == NULL) |
| 90 | return -ENOBUFS; |
| 91 | entry->seqno = seqno; |
| 92 | entry->stamp = ktime_get_real(); |
| 93 | entry->next = *headp; |
| 94 | *headp = entry; |
| 95 | return 0; |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 96 | } |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 97 | EXPORT_SYMBOL_GPL(tfrc_tx_hist_add); |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 98 | |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 99 | void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp) |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 100 | { |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 101 | struct tfrc_tx_hist_entry *head = *headp; |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 102 | |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 103 | while (head != NULL) { |
| 104 | struct tfrc_tx_hist_entry *next = head->next; |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 105 | |
Arnaldo Carvalho de Melo | e9c8b24a | 2007-12-06 12:27:49 -0200 | [diff] [blame] | 106 | kmem_cache_free(tfrc_tx_hist_slab, head); |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 107 | head = next; |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 108 | } |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 109 | |
| 110 | *headp = NULL; |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 111 | } |
Arnaldo Carvalho de Melo | 276f2ed | 2007-11-28 11:15:40 -0200 | [diff] [blame] | 112 | EXPORT_SYMBOL_GPL(tfrc_tx_hist_purge); |
Gerrit Renker | 7af5af3 | 2006-12-10 00:24:33 -0200 | [diff] [blame] | 113 | |
Arnaldo Carvalho de Melo | 9108d5f | 2007-11-29 22:47:15 -0200 | [diff] [blame] | 114 | u32 tfrc_tx_hist_rtt(struct tfrc_tx_hist_entry *head, const u64 seqno, |
| 115 | const ktime_t now) |
| 116 | { |
| 117 | u32 rtt = 0; |
| 118 | struct tfrc_tx_hist_entry *packet = tfrc_tx_hist_find_entry(head, seqno); |
| 119 | |
| 120 | if (packet != NULL) { |
| 121 | rtt = ktime_us_delta(now, packet->stamp); |
| 122 | /* |
| 123 | * Garbage-collect older (irrelevant) entries: |
| 124 | */ |
| 125 | tfrc_tx_hist_purge(&packet->next); |
| 126 | } |
| 127 | |
| 128 | return rtt; |
| 129 | } |
| 130 | EXPORT_SYMBOL_GPL(tfrc_tx_hist_rtt); |
| 131 | |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 132 | |
Gerrit Renker | 78282d2 | 2007-12-08 15:08:08 -0200 | [diff] [blame] | 133 | /* |
| 134 | * Receiver History Routines |
| 135 | */ |
| 136 | static struct kmem_cache *tfrc_rx_hist_slab; |
| 137 | |
Gerrit Renker | df8f83f | 2007-12-12 12:24:49 -0200 | [diff] [blame] | 138 | int __init tfrc_rx_packet_history_init(void) |
| 139 | { |
| 140 | tfrc_rx_hist_slab = kmem_cache_create("tfrc_rxh_cache", |
| 141 | sizeof(struct tfrc_rx_hist_entry), |
| 142 | 0, SLAB_HWCACHE_ALIGN, NULL); |
| 143 | return tfrc_rx_hist_slab == NULL ? -ENOBUFS : 0; |
| 144 | } |
| 145 | |
| 146 | void tfrc_rx_packet_history_exit(void) |
| 147 | { |
| 148 | if (tfrc_rx_hist_slab != NULL) { |
| 149 | kmem_cache_destroy(tfrc_rx_hist_slab); |
| 150 | tfrc_rx_hist_slab = NULL; |
| 151 | } |
| 152 | } |
| 153 | |
Gerrit Renker | 8a9c7e9 | 2007-12-12 13:50:51 -0200 | [diff] [blame^] | 154 | static inline void tfrc_rx_hist_entry_from_skb(struct tfrc_rx_hist_entry *entry, |
| 155 | const struct sk_buff *skb, |
| 156 | const u32 ndp) |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 157 | { |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 158 | const struct dccp_hdr *dh = dccp_hdr(skb); |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 159 | |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 160 | entry->tfrchrx_seqno = DCCP_SKB_CB(skb)->dccpd_seq; |
| 161 | entry->tfrchrx_ccval = dh->dccph_ccval; |
| 162 | entry->tfrchrx_type = dh->dccph_type; |
| 163 | entry->tfrchrx_ndp = ndp; |
| 164 | entry->tfrchrx_tstamp = ktime_get_real(); |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 165 | } |
Gerrit Renker | 8a9c7e9 | 2007-12-12 13:50:51 -0200 | [diff] [blame^] | 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 | } |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 175 | EXPORT_SYMBOL_GPL(tfrc_rx_hist_add_packet); |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 176 | |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 177 | /* has the packet contained in skb been seen before? */ |
| 178 | int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb) |
Arnaldo Carvalho de Melo | 072ab6c | 2005-08-28 01:19:14 -0300 | [diff] [blame] | 179 | { |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 180 | const u64 seq = DCCP_SKB_CB(skb)->dccpd_seq; |
| 181 | int i; |
Arnaldo Carvalho de Melo | 072ab6c | 2005-08-28 01:19:14 -0300 | [diff] [blame] | 182 | |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 183 | if (dccp_delta_seqno(tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno, seq) <= 0) |
| 184 | return 1; |
Arnaldo Carvalho de Melo | 072ab6c | 2005-08-28 01:19:14 -0300 | [diff] [blame] | 185 | |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 186 | for (i = 1; i <= h->loss_count; i++) |
| 187 | if (tfrc_rx_hist_entry(h, i)->tfrchrx_seqno == seq) |
| 188 | return 1; |
Arnaldo Carvalho de Melo | 072ab6c | 2005-08-28 01:19:14 -0300 | [diff] [blame] | 189 | |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 190 | return 0; |
| 191 | } |
| 192 | EXPORT_SYMBOL_GPL(tfrc_rx_hist_duplicate); |
| 193 | |
| 194 | /* initialise loss detection and disable RTT sampling */ |
| 195 | static inline void tfrc_rx_hist_loss_indicated(struct tfrc_rx_hist *h) |
| 196 | { |
| 197 | h->loss_count = 1; |
| 198 | } |
| 199 | |
| 200 | /* indicate whether previously a packet was detected missing */ |
| 201 | static inline int tfrc_rx_hist_loss_pending(const struct tfrc_rx_hist *h) |
| 202 | { |
| 203 | return h->loss_count; |
| 204 | } |
| 205 | |
| 206 | /* any data packets missing between last reception and skb ? */ |
| 207 | int tfrc_rx_hist_new_loss_indicated(struct tfrc_rx_hist *h, |
| 208 | const struct sk_buff *skb, u32 ndp) |
| 209 | { |
| 210 | int delta = dccp_delta_seqno(tfrc_rx_hist_last_rcv(h)->tfrchrx_seqno, |
| 211 | DCCP_SKB_CB(skb)->dccpd_seq); |
| 212 | |
| 213 | if (delta > 1 && ndp < delta) |
| 214 | tfrc_rx_hist_loss_indicated(h); |
| 215 | |
| 216 | return tfrc_rx_hist_loss_pending(h); |
| 217 | } |
| 218 | EXPORT_SYMBOL_GPL(tfrc_rx_hist_new_loss_indicated); |
| 219 | |
Gerrit Renker | 8a9c7e9 | 2007-12-12 13:50:51 -0200 | [diff] [blame^] | 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 | |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 422 | int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h) |
| 423 | { |
| 424 | int i; |
| 425 | |
| 426 | for (i = 0; i <= TFRC_NDUPACK; i++) { |
| 427 | h->ring[i] = kmem_cache_alloc(tfrc_rx_hist_slab, GFP_ATOMIC); |
| 428 | if (h->ring[i] == NULL) |
| 429 | goto out_free; |
Arnaldo Carvalho de Melo | 072ab6c | 2005-08-28 01:19:14 -0300 | [diff] [blame] | 430 | } |
Arnaldo Carvalho de Melo | 072ab6c | 2005-08-28 01:19:14 -0300 | [diff] [blame] | 431 | |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 432 | h->loss_count = h->loss_start = 0; |
| 433 | return 0; |
Arnaldo Carvalho de Melo | 072ab6c | 2005-08-28 01:19:14 -0300 | [diff] [blame] | 434 | |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 435 | out_free: |
| 436 | while (i-- != 0) { |
| 437 | kmem_cache_free(tfrc_rx_hist_slab, h->ring[i]); |
| 438 | h->ring[i] = NULL; |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 439 | } |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 440 | return -ENOBUFS; |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 441 | } |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 442 | EXPORT_SYMBOL_GPL(tfrc_rx_hist_alloc); |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 443 | |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 444 | void tfrc_rx_hist_purge(struct tfrc_rx_hist *h) |
| 445 | { |
| 446 | int i; |
| 447 | |
| 448 | for (i = 0; i <= TFRC_NDUPACK; ++i) |
| 449 | if (h->ring[i] != NULL) { |
| 450 | kmem_cache_free(tfrc_rx_hist_slab, h->ring[i]); |
| 451 | h->ring[i] = NULL; |
| 452 | } |
| 453 | } |
Arnaldo Carvalho de Melo | d58d1af | 2007-12-06 12:28:39 -0200 | [diff] [blame] | 454 | EXPORT_SYMBOL_GPL(tfrc_rx_hist_purge); |
Arnaldo Carvalho de Melo | 8c60f3f | 2005-08-10 12:59:38 -0300 | [diff] [blame] | 455 | |
Arnaldo Carvalho de Melo | b84a218 | 2007-12-06 13:18:11 -0200 | [diff] [blame] | 456 | /** |
| 457 | * tfrc_rx_hist_rtt_last_s - reference entry to compute RTT samples against |
| 458 | */ |
| 459 | static inline struct tfrc_rx_hist_entry * |
| 460 | tfrc_rx_hist_rtt_last_s(const struct tfrc_rx_hist *h) |
| 461 | { |
| 462 | return h->ring[0]; |
| 463 | } |
| 464 | |
| 465 | /** |
| 466 | * tfrc_rx_hist_rtt_prev_s: previously suitable (wrt rtt_last_s) RTT-sampling entry |
| 467 | */ |
| 468 | static inline struct tfrc_rx_hist_entry * |
| 469 | tfrc_rx_hist_rtt_prev_s(const struct tfrc_rx_hist *h) |
| 470 | { |
| 471 | return h->ring[h->rtt_sample_prev]; |
| 472 | } |
| 473 | |
| 474 | /** |
| 475 | * tfrc_rx_hist_sample_rtt - Sample RTT from timestamp / CCVal |
| 476 | * Based on ideas presented in RFC 4342, 8.1. Returns 0 if it was not able |
| 477 | * to compute a sample with given data - calling function should check this. |
| 478 | */ |
| 479 | u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, const struct sk_buff *skb) |
| 480 | { |
| 481 | u32 sample = 0, |
| 482 | delta_v = SUB16(dccp_hdr(skb)->dccph_ccval, |
| 483 | tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); |
| 484 | |
| 485 | if (delta_v < 1 || delta_v > 4) { /* unsuitable CCVal delta */ |
| 486 | if (h->rtt_sample_prev == 2) { /* previous candidate stored */ |
| 487 | sample = SUB16(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval, |
| 488 | tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); |
| 489 | if (sample) |
| 490 | sample = 4 / sample * |
| 491 | ktime_us_delta(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_tstamp, |
| 492 | tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp); |
| 493 | else /* |
| 494 | * FIXME: This condition is in principle not |
| 495 | * possible but occurs when CCID is used for |
| 496 | * two-way data traffic. I have tried to trace |
| 497 | * it, but the cause does not seem to be here. |
| 498 | */ |
| 499 | DCCP_BUG("please report to dccp@vger.kernel.org" |
| 500 | " => prev = %u, last = %u", |
| 501 | tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval, |
| 502 | tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); |
| 503 | } else if (delta_v < 1) { |
| 504 | h->rtt_sample_prev = 1; |
| 505 | goto keep_ref_for_next_time; |
| 506 | } |
| 507 | |
| 508 | } else if (delta_v == 4) /* optimal match */ |
| 509 | sample = ktime_to_us(net_timedelta(tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp)); |
| 510 | else { /* suboptimal match */ |
| 511 | h->rtt_sample_prev = 2; |
| 512 | goto keep_ref_for_next_time; |
| 513 | } |
| 514 | |
| 515 | if (unlikely(sample > DCCP_SANE_RTT_MAX)) { |
| 516 | DCCP_WARN("RTT sample %u too large, using max\n", sample); |
| 517 | sample = DCCP_SANE_RTT_MAX; |
| 518 | } |
| 519 | |
| 520 | h->rtt_sample_prev = 0; /* use current entry as next reference */ |
| 521 | keep_ref_for_next_time: |
| 522 | |
| 523 | return sample; |
| 524 | } |
| 525 | EXPORT_SYMBOL_GPL(tfrc_rx_hist_sample_rtt); |