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
 153        swap(h->ring[idx_a], h->ring[idx_b]);
 154}
 155
 156/*
 157 * Private helper functions for loss detection.
 158 *
 159 * In the descriptions, `Si' refers to the sequence number of entry number i,
 160 * whose NDP count is `Ni' (lower case is used for variables).
 161 * Note: All __xxx_loss functions expect that a test against duplicates has been
 162 *       performed already: the seqno of the skb must not be less than the seqno
 163 *       of loss_prev; and it must not equal that of any valid history entry.
 164 */
 165static void __do_track_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u64 n1)
 166{
 167        u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno,
 168            s1 = DCCP_SKB_CB(skb)->dccpd_seq;
 169
 170        if (!dccp_loss_free(s0, s1, n1)) {      /* gap between S0 and S1 */
 171                h->loss_count = 1;
 172                tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n1);
 173        }
 174}
 175
 176static void __one_after_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u32 n2)
 177{
 178        u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno,
 179            s1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_seqno,
 180            s2 = DCCP_SKB_CB(skb)->dccpd_seq;
 181
 182        if (likely(dccp_delta_seqno(s1, s2) > 0)) {     /* S1  <  S2 */
 183                h->loss_count = 2;
 184                tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 2), skb, n2);
 185                return;
 186        }
 187
 188        /* S0  <  S2  <  S1 */
 189
 190        if (dccp_loss_free(s0, s2, n2)) {
 191                u64 n1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_ndp;
 192
 193                if (dccp_loss_free(s2, s1, n1)) {
 194                        /* hole is filled: S0, S2, and S1 are consecutive */
 195                        h->loss_count = 0;
 196                        h->loss_start = tfrc_rx_hist_index(h, 1);
 197                } else
 198                        /* gap between S2 and S1: just update loss_prev */
 199                        tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_loss_prev(h), skb, n2);
 200
 201        } else {        /* gap between S0 and S2 */
 202                /*
 203                 * Reorder history to insert S2 between S0 and S1
 204                 */
 205                tfrc_rx_hist_swap(h, 0, 3);
 206                h->loss_start = tfrc_rx_hist_index(h, 3);
 207                tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n2);
 208                h->loss_count = 2;
 209        }
 210}
 211
 212/* return 1 if a new loss event has been identified */
 213static int __two_after_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u32 n3)
 214{
 215        u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno,
 216            s1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_seqno,
 217            s2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_seqno,
 218            s3 = DCCP_SKB_CB(skb)->dccpd_seq;
 219
 220        if (likely(dccp_delta_seqno(s2, s3) > 0)) {     /* S2  <  S3 */
 221                h->loss_count = 3;
 222                tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 3), skb, n3);
 223                return 1;
 224        }
 225
 226        /* S3  <  S2 */
 227
 228        if (dccp_delta_seqno(s1, s3) > 0) {             /* S1  <  S3  <  S2 */
 229                /*
 230                 * Reorder history to insert S3 between S1 and S2
 231                 */
 232                tfrc_rx_hist_swap(h, 2, 3);
 233                tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 2), skb, n3);
 234                h->loss_count = 3;
 235                return 1;
 236        }
 237
 238        /* S0  <  S3  <  S1 */
 239
 240        if (dccp_loss_free(s0, s3, n3)) {
 241                u64 n1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_ndp;
 242
 243                if (dccp_loss_free(s3, s1, n1)) {
 244                        /* hole between S0 and S1 filled by S3 */
 245                        u64 n2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_ndp;
 246
 247                        if (dccp_loss_free(s1, s2, n2)) {
 248                                /* entire hole filled by S0, S3, S1, S2 */
 249                                h->loss_start = tfrc_rx_hist_index(h, 2);
 250                                h->loss_count = 0;
 251                        } else {
 252                                /* gap remains between S1 and S2 */
 253                                h->loss_start = tfrc_rx_hist_index(h, 1);
 254                                h->loss_count = 1;
 255                        }
 256
 257                } else /* gap exists between S3 and S1, loss_count stays at 2 */
 258                        tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_loss_prev(h), skb, n3);
 259
 260                return 0;
 261        }
 262
 263        /*
 264         * The remaining case:  S0  <  S3  <  S1  <  S2;  gap between S0 and S3
 265         * Reorder history to insert S3 between S0 and S1.
 266         */
 267        tfrc_rx_hist_swap(h, 0, 3);
 268        h->loss_start = tfrc_rx_hist_index(h, 3);
 269        tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n3);
 270        h->loss_count = 3;
 271
 272        return 1;
 273}
 274
 275/* recycle RX history records to continue loss detection if necessary */
 276static void __three_after_loss(struct tfrc_rx_hist *h)
 277{
 278        /*
 279         * At this stage we know already that there is a gap between S0 and S1
 280         * (since S0 was the highest sequence number received before detecting
 281         * the loss). To recycle the loss record, it is thus only necessary to
 282         * check for other possible gaps between S1/S2 and between S2/S3.
 283         */
 284        u64 s1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_seqno,
 285            s2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_seqno,
 286            s3 = tfrc_rx_hist_entry(h, 3)->tfrchrx_seqno;
 287        u64 n2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_ndp,
 288            n3 = tfrc_rx_hist_entry(h, 3)->tfrchrx_ndp;
 289
 290        if (dccp_loss_free(s1, s2, n2)) {
 291
 292                if (dccp_loss_free(s2, s3, n3)) {
 293                        /* no gap between S2 and S3: entire hole is filled */
 294                        h->loss_start = tfrc_rx_hist_index(h, 3);
 295                        h->loss_count = 0;
 296                } else {
 297                        /* gap between S2 and S3 */
 298                        h->loss_start = tfrc_rx_hist_index(h, 2);
 299                        h->loss_count = 1;
 300                }
 301
 302        } else {        /* gap between S1 and S2 */
 303                h->loss_start = tfrc_rx_hist_index(h, 1);
 304                h->loss_count = 2;
 305        }
 306}
 307
 308/**
 309 *  tfrc_rx_handle_loss  -  Loss detection and further processing
 310 *  @h:             The non-empty RX history object
 311 *  @lh:            Loss Intervals database to update
 312 *  @skb:           Currently received packet
 313 *  @ndp:           The NDP count belonging to @skb
 314 *  @calc_first_li: Caller-dependent computation of first loss interval in @lh
 315 *  @sk:            Used by @calc_first_li (see tfrc_lh_interval_add)
 316 *
 317 *  Chooses action according to pending loss, updates LI database when a new
 318 *  loss was detected, and does required post-processing. Returns 1 when caller
 319 *  should send feedback, 0 otherwise.
 320 *  Since it also takes care of reordering during loss detection and updates the
 321 *  records accordingly, the caller should not perform any more RX history
 322 *  operations when loss_count is greater than 0 after calling this function.
 323 */
 324int tfrc_rx_handle_loss(struct tfrc_rx_hist *h,
 325                        struct tfrc_loss_hist *lh,
 326                        struct sk_buff *skb, const u64 ndp,
 327                        u32 (*calc_first_li)(struct sock *), struct sock *sk)
 328{
 329        int is_new_loss = 0;
 330
 331        if (h->loss_count == 0) {
 332                __do_track_loss(h, skb, ndp);
 333        } else if (h->loss_count == 1) {
 334                __one_after_loss(h, skb, ndp);
 335        } else if (h->loss_count != 2) {
 336                DCCP_BUG("invalid loss_count %d", h->loss_count);
 337        } else if (__two_after_loss(h, skb, ndp)) {
 338                /*
 339                 * Update Loss Interval database and recycle RX records
 340                 */
 341                is_new_loss = tfrc_lh_interval_add(lh, h, calc_first_li, sk);
 342                __three_after_loss(h);
 343        }
 344        return is_new_loss;
 345}
 346
 347int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h)
 348{
 349        int i;
 350
 351        for (i = 0; i <= TFRC_NDUPACK; i++) {
 352                h->ring[i] = kmem_cache_alloc(tfrc_rx_hist_slab, GFP_ATOMIC);
 353                if (h->ring[i] == NULL)
 354                        goto out_free;
 355        }
 356
 357        h->loss_count = h->loss_start = 0;
 358        return 0;
 359
 360out_free:
 361        while (i-- != 0) {
 362                kmem_cache_free(tfrc_rx_hist_slab, h->ring[i]);
 363                h->ring[i] = NULL;
 364        }
 365        return -ENOBUFS;
 366}
 367
 368void tfrc_rx_hist_purge(struct tfrc_rx_hist *h)
 369{
 370        int i;
 371
 372        for (i = 0; i <= TFRC_NDUPACK; ++i)
 373                if (h->ring[i] != NULL) {
 374                        kmem_cache_free(tfrc_rx_hist_slab, h->ring[i]);
 375                        h->ring[i] = NULL;
 376                }
 377}
 378
 379/**
 380 * tfrc_rx_hist_rtt_last_s - reference entry to compute RTT samples against
 381 */
 382static inline struct tfrc_rx_hist_entry *
 383                        tfrc_rx_hist_rtt_last_s(const struct tfrc_rx_hist *h)
 384{
 385        return h->ring[0];
 386}
 387
 388/**
 389 * tfrc_rx_hist_rtt_prev_s - previously suitable (wrt rtt_last_s) RTT-sampling entry
 390 */
 391static inline struct tfrc_rx_hist_entry *
 392                        tfrc_rx_hist_rtt_prev_s(const struct tfrc_rx_hist *h)
 393{
 394        return h->ring[h->rtt_sample_prev];
 395}
 396
 397/**
 398 * tfrc_rx_hist_sample_rtt  -  Sample RTT from timestamp / CCVal
 399 * Based on ideas presented in RFC 4342, 8.1. Returns 0 if it was not able
 400 * to compute a sample with given data - calling function should check this.
 401 */
 402u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, const struct sk_buff *skb)
 403{
 404        u32 sample = 0,
 405            delta_v = SUB16(dccp_hdr(skb)->dccph_ccval,
 406                            tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval);
 407
 408        if (delta_v < 1 || delta_v > 4) {       /* unsuitable CCVal delta */
 409                if (h->rtt_sample_prev == 2) {  /* previous candidate stored */
 410                        sample = SUB16(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval,
 411                                       tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval);
 412                        if (sample)
 413                                sample = 4 / sample *
 414                                         ktime_us_delta(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_tstamp,
 415                                                        tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp);
 416                        else    /*
 417                                 * FIXME: This condition is in principle not
 418                                 * possible but occurs when CCID is used for
 419                                 * two-way data traffic. I have tried to trace
 420                                 * it, but the cause does not seem to be here.
 421                                 */
 422                                DCCP_BUG("please report to dccp@vger.kernel.org"
 423                                         " => prev = %u, last = %u",
 424                                         tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval,
 425                                         tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval);
 426                } else if (delta_v < 1) {
 427                        h->rtt_sample_prev = 1;
 428                        goto keep_ref_for_next_time;
 429                }
 430
 431        } else if (delta_v == 4) /* optimal match */
 432                sample = ktime_to_us(net_timedelta(tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp));
 433        else {                   /* suboptimal match */
 434                h->rtt_sample_prev = 2;
 435                goto keep_ref_for_next_time;
 436        }
 437
 438        if (unlikely(sample > DCCP_SANE_RTT_MAX)) {
 439                DCCP_WARN("RTT sample %u too large, using max\n", sample);
 440                sample = DCCP_SANE_RTT_MAX;
 441        }
 442
 443        h->rtt_sample_prev = 0;        /* use current entry as next reference */
 444keep_ref_for_next_time:
 445
 446        return sample;
 447}
 448