linux/net/dccp/ccids/lib/packet_history.c
<<
>>
Prefs
   1/*
   2 *  Copyright (c) 2007   The University of Aberdeen, Scotland, UK
   3 *  Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand.
   4 *
   5 *  An implementation of the DCCP protocol
   6 *
   7 *  This code has been developed by the University of Waikato WAND
   8 *  research group. For further information please see http://www.wand.net.nz/
   9 *  or e-mail Ian McDonald - ian.mcdonald@jandi.co.nz
  10 *
  11 *  This code also uses code from Lulea University, rereleased as GPL by its
  12 *  authors:
  13 *  Copyright (c) 2003 Nils-Erik Mattsson, Joacim Haggmark, Magnus Erixzon
  14 *
  15 *  Changes to meet Linux coding standards, to make it meet latest ccid3 draft
  16 *  and to make it work as a loadable module in the DCCP stack written by
  17 *  Arnaldo Carvalho de Melo <acme@conectiva.com.br>.
  18 *
  19 *  Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  20 *
  21 *  This program is free software; you can redistribute it and/or modify
  22 *  it under the terms of the GNU General Public License as published by
  23 *  the Free Software Foundation; either version 2 of the License, or
  24 *  (at your option) any later version.
  25 *
  26 *  This program is distributed in the hope that it will be useful,
  27 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  28 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  29 *  GNU General Public License for more details.
  30 *
  31 *  You should have received a copy of the GNU General Public License
  32 *  along with this program; if not, write to the Free Software
  33 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  34 */
  35
  36#include <linux/string.h>
  37#include <linux/slab.h>
  38#include "packet_history.h"
  39#include "../../dccp.h"
  40
  41/*
  42 * Transmitter History Routines
  43 */
  44static struct kmem_cache *tfrc_tx_hist_slab;
  45
  46int __init tfrc_tx_packet_history_init(void)
  47{
  48        tfrc_tx_hist_slab = kmem_cache_create("tfrc_tx_hist",
  49                                              sizeof(struct tfrc_tx_hist_entry),
  50                                              0, SLAB_HWCACHE_ALIGN, NULL);
  51        return tfrc_tx_hist_slab == NULL ? -ENOBUFS : 0;
  52}
  53
  54void tfrc_tx_packet_history_exit(void)
  55{
  56        if (tfrc_tx_hist_slab != NULL) {
  57                kmem_cache_destroy(tfrc_tx_hist_slab);
  58                tfrc_tx_hist_slab = NULL;
  59        }
  60}
  61
  62int tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno)
  63{
  64        struct tfrc_tx_hist_entry *entry = kmem_cache_alloc(tfrc_tx_hist_slab, gfp_any());
  65
  66        if (entry == NULL)
  67                return -ENOBUFS;
  68        entry->seqno = seqno;
  69        entry->stamp = ktime_get_real();
  70        entry->next  = *headp;
  71        *headp       = entry;
  72        return 0;
  73}
  74
  75void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp)
  76{
  77        struct tfrc_tx_hist_entry *head = *headp;
  78
  79        while (head != NULL) {
  80                struct tfrc_tx_hist_entry *next = head->next;
  81
  82                kmem_cache_free(tfrc_tx_hist_slab, head);
  83                head = next;
  84        }
  85
  86        *headp = NULL;
  87}
  88
  89/*
  90 *      Receiver History Routines
  91 */
  92static struct kmem_cache *tfrc_rx_hist_slab;
  93
  94int __init tfrc_rx_packet_history_init(void)
  95{
  96        tfrc_rx_hist_slab = kmem_cache_create("tfrc_rxh_cache",
  97                                              sizeof(struct tfrc_rx_hist_entry),
  98                                              0, SLAB_HWCACHE_ALIGN, NULL);
  99        return tfrc_rx_hist_slab == NULL ? -ENOBUFS : 0;
 100}
 101
 102void tfrc_rx_packet_history_exit(void)
 103{
 104        if (tfrc_rx_hist_slab != NULL) {
 105                kmem_cache_destroy(tfrc_rx_hist_slab);
 106                tfrc_rx_hist_slab = NULL;
 107        }
 108}
 109
 110static inline void tfrc_rx_hist_entry_from_skb(struct tfrc_rx_hist_entry *entry,
 111                                               const struct sk_buff *skb,
 112                                               const u64 ndp)
 113{
 114        const struct dccp_hdr *dh = dccp_hdr(skb);
 115
 116        entry->tfrchrx_seqno = DCCP_SKB_CB(skb)->dccpd_seq;
 117        entry->tfrchrx_ccval = dh->dccph_ccval;
 118        entry->tfrchrx_type  = dh->dccph_type;
 119        entry->tfrchrx_ndp   = ndp;
 120        entry->tfrchrx_tstamp = ktime_get_real();
 121}
 122
 123void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h,
 124                             const struct sk_buff *skb,
 125                             const u64 ndp)
 126{
 127        struct tfrc_rx_hist_entry *entry = tfrc_rx_hist_last_rcv(h);
 128
 129        tfrc_rx_hist_entry_from_skb(entry, skb, ndp);
 130}
 131
 132/* has the packet contained in skb been seen before? */
 133int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb)
 134{
 135        const u64 seq = DCCP_SKB_CB(skb)->dccpd_seq;
 136        int i;
 137
 138        if (dccp_delta_seqno(tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno, seq) <= 0)
 139                return 1;
 140
 141        for (i = 1; i <= h->loss_count; i++)
 142                if (tfrc_rx_hist_entry(h, i)->tfrchrx_seqno == seq)
 143                        return 1;
 144
 145        return 0;
 146}
 147
 148static void tfrc_rx_hist_swap(struct tfrc_rx_hist *h, const u8 a, const u8 b)
 149{
 150        const u8 idx_a = tfrc_rx_hist_index(h, a),
 151                 idx_b = tfrc_rx_hist_index(h, b);
 152        struct tfrc_rx_hist_entry *tmp = h->ring[idx_a];
 153
 154        h->ring[idx_a] = h->ring[idx_b];
 155        h->ring[idx_b] = tmp;
 156}
 157
 158/*
 159 * Private helper functions for loss detection.
 160 *
 161 * In the descriptions, `Si' refers to the sequence number of entry number i,
 162 * whose NDP count is `Ni' (lower case is used for variables).
 163 * Note: All __xxx_loss functions expect that a test against duplicates has been
 164 *       performed already: the seqno of the skb must not be less than the seqno
 165 *       of loss_prev; and it must not equal that of any valid history entry.
 166 */
 167static void __do_track_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u64 n1)
 168{
 169        u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno,
 170            s1 = DCCP_SKB_CB(skb)->dccpd_seq;
 171
 172        if (!dccp_loss_free(s0, s1, n1)) {      /* gap between S0 and S1 */
 173                h->loss_count = 1;
 174                tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n1);
 175        }
 176}
 177
 178static void __one_after_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u32 n2)
 179{
 180        u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno,
 181            s1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_seqno,
 182            s2 = DCCP_SKB_CB(skb)->dccpd_seq;
 183
 184        if (likely(dccp_delta_seqno(s1, s2) > 0)) {     /* S1  <  S2 */
 185                h->loss_count = 2;
 186                tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 2), skb, n2);
 187                return;
 188        }
 189
 190        /* S0  <  S2  <  S1 */
 191
 192        if (dccp_loss_free(s0, s2, n2)) {
 193                u64 n1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_ndp;
 194
 195                if (dccp_loss_free(s2, s1, n1)) {
 196                        /* hole is filled: S0, S2, and S1 are consecutive */
 197                        h->loss_count = 0;
 198                        h->loss_start = tfrc_rx_hist_index(h, 1);
 199                } else
 200                        /* gap between S2 and S1: just update loss_prev */
 201                        tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_loss_prev(h), skb, n2);
 202
 203        } else {        /* gap between S0 and S2 */
 204                /*
 205                 * Reorder history to insert S2 between S0 and S1
 206                 */
 207                tfrc_rx_hist_swap(h, 0, 3);
 208                h->loss_start = tfrc_rx_hist_index(h, 3);
 209                tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n2);
 210                h->loss_count = 2;
 211        }
 212}
 213
 214/* return 1 if a new loss event has been identified */
 215static int __two_after_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u32 n3)
 216{
 217        u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno,
 218            s1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_seqno,
 219            s2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_seqno,
 220            s3 = DCCP_SKB_CB(skb)->dccpd_seq;
 221
 222        if (likely(dccp_delta_seqno(s2, s3) > 0)) {     /* S2  <  S3 */
 223                h->loss_count = 3;
 224                tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 3), skb, n3);
 225                return 1;
 226        }
 227
 228        /* S3  <  S2 */
 229
 230        if (dccp_delta_seqno(s1, s3) > 0) {             /* S1  <  S3  <  S2 */
 231                /*
 232                 * Reorder history to insert S3 between S1 and S2
 233                 */
 234                tfrc_rx_hist_swap(h, 2, 3);
 235                tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 2), skb, n3);
 236                h->loss_count = 3;
 237                return 1;
 238        }
 239
 240        /* S0  <  S3  <  S1 */
 241
 242        if (dccp_loss_free(s0, s3, n3)) {
 243                u64 n1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_ndp;
 244
 245                if (dccp_loss_free(s3, s1, n1)) {
 246                        /* hole between S0 and S1 filled by S3 */
 247                        u64 n2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_ndp;
 248
 249                        if (dccp_loss_free(s1, s2, n2)) {
 250                                /* entire hole filled by S0, S3, S1, S2 */
 251                                h->loss_start = tfrc_rx_hist_index(h, 2);
 252                                h->loss_count = 0;
 253                        } else {
 254                                /* gap remains between S1 and S2 */
 255                                h->loss_start = tfrc_rx_hist_index(h, 1);
 256                                h->loss_count = 1;
 257                        }
 258
 259                } else /* gap exists between S3 and S1, loss_count stays at 2 */
 260                        tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_loss_prev(h), skb, n3);
 261
 262                return 0;
 263        }
 264
 265        /*
 266         * The remaining case:  S0  <  S3  <  S1  <  S2;  gap between S0 and S3
 267         * Reorder history to insert S3 between S0 and S1.
 268         */
 269        tfrc_rx_hist_swap(h, 0, 3);
 270        h->loss_start = tfrc_rx_hist_index(h, 3);
 271        tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n3);
 272        h->loss_count = 3;
 273
 274        return 1;
 275}
 276
 277/* recycle RX history records to continue loss detection if necessary */
 278static void __three_after_loss(struct tfrc_rx_hist *h)
 279{
 280        /*
 281         * At this stage we know already that there is a gap between S0 and S1
 282         * (since S0 was the highest sequence number received before detecting
 283         * the loss). To recycle the loss record, it is thus only necessary to
 284         * check for other possible gaps between S1/S2 and between S2/S3.
 285         */
 286        u64 s1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_seqno,
 287            s2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_seqno,
 288            s3 = tfrc_rx_hist_entry(h, 3)->tfrchrx_seqno;
 289        u64 n2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_ndp,
 290            n3 = tfrc_rx_hist_entry(h, 3)->tfrchrx_ndp;
 291
 292        if (dccp_loss_free(s1, s2, n2)) {
 293
 294                if (dccp_loss_free(s2, s3, n3)) {
 295                        /* no gap between S2 and S3: entire hole is filled */
 296                        h->loss_start = tfrc_rx_hist_index(h, 3);
 297                        h->loss_count = 0;
 298                } else {
 299                        /* gap between S2 and S3 */
 300                        h->loss_start = tfrc_rx_hist_index(h, 2);
 301                        h->loss_count = 1;
 302                }
 303
 304        } else {        /* gap between S1 and S2 */
 305                h->loss_start = tfrc_rx_hist_index(h, 1);
 306                h->loss_count = 2;
 307        }
 308}
 309
 310/**
 311 *  tfrc_rx_handle_loss  -  Loss detection and further processing
 312 *  @h:             The non-empty RX history object
 313 *  @lh:            Loss Intervals database to update
 314 *  @skb:           Currently received packet
 315 *  @ndp:           The NDP count belonging to @skb
 316 *  @calc_first_li: Caller-dependent computation of first loss interval in @lh
 317 *  @sk:            Used by @calc_first_li (see tfrc_lh_interval_add)
 318 *
 319 *  Chooses action according to pending loss, updates LI database when a new
 320 *  loss was detected, and does required post-processing. Returns 1 when caller
 321 *  should send feedback, 0 otherwise.
 322 *  Since it also takes care of reordering during loss detection and updates the
 323 *  records accordingly, the caller should not perform any more RX history
 324 *  operations when loss_count is greater than 0 after calling this function.
 325 */
 326int tfrc_rx_handle_loss(struct tfrc_rx_hist *h,
 327                        struct tfrc_loss_hist *lh,
 328                        struct sk_buff *skb, const u64 ndp,
 329                        u32 (*calc_first_li)(struct sock *), struct sock *sk)
 330{
 331        int is_new_loss = 0;
 332
 333        if (h->loss_count == 0) {
 334                __do_track_loss(h, skb, ndp);
 335        } else if (h->loss_count == 1) {
 336                __one_after_loss(h, skb, ndp);
 337        } else if (h->loss_count != 2) {
 338                DCCP_BUG("invalid loss_count %d", h->loss_count);
 339        } else if (__two_after_loss(h, skb, ndp)) {
 340                /*
 341                 * Update Loss Interval database and recycle RX records
 342                 */
 343                is_new_loss = tfrc_lh_interval_add(lh, h, calc_first_li, sk);
 344                __three_after_loss(h);
 345        }
 346        return is_new_loss;
 347}
 348
 349int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h)
 350{
 351        int i;
 352
 353        for (i = 0; i <= TFRC_NDUPACK; i++) {
 354                h->ring[i] = kmem_cache_alloc(tfrc_rx_hist_slab, GFP_ATOMIC);
 355                if (h->ring[i] == NULL)
 356                        goto out_free;
 357        }
 358
 359        h->loss_count = h->loss_start = 0;
 360        return 0;
 361
 362out_free:
 363        while (i-- != 0) {
 364                kmem_cache_free(tfrc_rx_hist_slab, h->ring[i]);
 365                h->ring[i] = NULL;
 366        }
 367        return -ENOBUFS;
 368}
 369
 370void tfrc_rx_hist_purge(struct tfrc_rx_hist *h)
 371{
 372        int i;
 373
 374        for (i = 0; i <= TFRC_NDUPACK; ++i)
 375                if (h->ring[i] != NULL) {
 376                        kmem_cache_free(tfrc_rx_hist_slab, h->ring[i]);
 377                        h->ring[i] = NULL;
 378                }
 379}
 380
 381/**
 382 * tfrc_rx_hist_rtt_last_s - reference entry to compute RTT samples against
 383 */
 384static inline struct tfrc_rx_hist_entry *
 385                        tfrc_rx_hist_rtt_last_s(const struct tfrc_rx_hist *h)
 386{
 387        return h->ring[0];
 388}
 389
 390/**
 391 * tfrc_rx_hist_rtt_prev_s - previously suitable (wrt rtt_last_s) RTT-sampling entry
 392 */
 393static inline struct tfrc_rx_hist_entry *
 394                        tfrc_rx_hist_rtt_prev_s(const struct tfrc_rx_hist *h)
 395{
 396        return h->ring[h->rtt_sample_prev];
 397}
 398
 399/**
 400 * tfrc_rx_hist_sample_rtt  -  Sample RTT from timestamp / CCVal
 401 * Based on ideas presented in RFC 4342, 8.1. Returns 0 if it was not able
 402 * to compute a sample with given data - calling function should check this.
 403 */
 404u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, const struct sk_buff *skb)
 405{
 406        u32 sample = 0,
 407            delta_v = SUB16(dccp_hdr(skb)->dccph_ccval,
 408                            tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval);
 409
 410        if (delta_v < 1 || delta_v > 4) {       /* unsuitable CCVal delta */
 411                if (h->rtt_sample_prev == 2) {  /* previous candidate stored */
 412                        sample = SUB16(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval,
 413                                       tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval);
 414                        if (sample)
 415                                sample = 4 / sample *
 416                                         ktime_us_delta(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_tstamp,
 417                                                        tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp);
 418                        else    /*
 419                                 * FIXME: This condition is in principle not
 420                                 * possible but occurs when CCID is used for
 421                                 * two-way data traffic. I have tried to trace
 422                                 * it, but the cause does not seem to be here.
 423                                 */
 424                                DCCP_BUG("please report to dccp@vger.kernel.org"
 425                                         " => prev = %u, last = %u",
 426                                         tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval,
 427                                         tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval);
 428                } else if (delta_v < 1) {
 429                        h->rtt_sample_prev = 1;
 430                        goto keep_ref_for_next_time;
 431                }
 432
 433        } else if (delta_v == 4) /* optimal match */
 434                sample = ktime_to_us(net_timedelta(tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp));
 435        else {                   /* suboptimal match */
 436                h->rtt_sample_prev = 2;
 437                goto keep_ref_for_next_time;
 438        }
 439
 440        if (unlikely(sample > DCCP_SANE_RTT_MAX)) {
 441                DCCP_WARN("RTT sample %u too large, using max\n", sample);
 442                sample = DCCP_SANE_RTT_MAX;
 443        }
 444
 445        h->rtt_sample_prev = 0;        /* use current entry as next reference */
 446keep_ref_for_next_time:
 447
 448        return sample;
 449}
 450