Path: blob/master/net/dccp/ccids/lib/loss_interval.c
15112 views
/*1* Copyright (c) 2007 The University of Aberdeen, Scotland, UK2* Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand.3* Copyright (c) 2005-7 Ian McDonald <[email protected]>4* Copyright (c) 2005 Arnaldo Carvalho de Melo <[email protected]>5*6* This program is free software; you can redistribute it and/or modify7* it under the terms of the GNU General Public License as published by8* the Free Software Foundation; either version 2 of the License, or9* (at your option) any later version.10*/11#include <net/sock.h>12#include "tfrc.h"1314static struct kmem_cache *tfrc_lh_slab __read_mostly;15/* Loss Interval weights from [RFC 3448, 5.4], scaled by 10 */16static const int tfrc_lh_weights[NINTERVAL] = { 10, 10, 10, 10, 8, 6, 4, 2 };1718/* implements LIFO semantics on the array */19static inline u8 LIH_INDEX(const u8 ctr)20{21return LIH_SIZE - 1 - (ctr % LIH_SIZE);22}2324/* the `counter' index always points at the next entry to be populated */25static inline struct tfrc_loss_interval *tfrc_lh_peek(struct tfrc_loss_hist *lh)26{27return lh->counter ? lh->ring[LIH_INDEX(lh->counter - 1)] : NULL;28}2930/* given i with 0 <= i <= k, return I_i as per the rfc3448bis notation */31static inline u32 tfrc_lh_get_interval(struct tfrc_loss_hist *lh, const u8 i)32{33BUG_ON(i >= lh->counter);34return lh->ring[LIH_INDEX(lh->counter - i - 1)]->li_length;35}3637/*38* On-demand allocation and de-allocation of entries39*/40static struct tfrc_loss_interval *tfrc_lh_demand_next(struct tfrc_loss_hist *lh)41{42if (lh->ring[LIH_INDEX(lh->counter)] == NULL)43lh->ring[LIH_INDEX(lh->counter)] = kmem_cache_alloc(tfrc_lh_slab,44GFP_ATOMIC);45return lh->ring[LIH_INDEX(lh->counter)];46}4748void tfrc_lh_cleanup(struct tfrc_loss_hist *lh)49{50if (!tfrc_lh_is_initialised(lh))51return;5253for (lh->counter = 0; lh->counter < LIH_SIZE; lh->counter++)54if (lh->ring[LIH_INDEX(lh->counter)] != NULL) {55kmem_cache_free(tfrc_lh_slab,56lh->ring[LIH_INDEX(lh->counter)]);57lh->ring[LIH_INDEX(lh->counter)] = NULL;58}59}6061static void tfrc_lh_calc_i_mean(struct tfrc_loss_hist *lh)62{63u32 i_i, i_tot0 = 0, i_tot1 = 0, w_tot = 0;64int i, k = tfrc_lh_length(lh) - 1; /* k is as in rfc3448bis, 5.4 */6566if (k <= 0)67return;6869for (i = 0; i <= k; i++) {70i_i = tfrc_lh_get_interval(lh, i);7172if (i < k) {73i_tot0 += i_i * tfrc_lh_weights[i];74w_tot += tfrc_lh_weights[i];75}76if (i > 0)77i_tot1 += i_i * tfrc_lh_weights[i-1];78}7980lh->i_mean = max(i_tot0, i_tot1) / w_tot;81}8283/**84* tfrc_lh_update_i_mean - Update the `open' loss interval I_085* For recomputing p: returns `true' if p > p_prev <=> 1/p < 1/p_prev86*/87u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *skb)88{89struct tfrc_loss_interval *cur = tfrc_lh_peek(lh);90u32 old_i_mean = lh->i_mean;91s64 len;9293if (cur == NULL) /* not initialised */94return 0;9596len = dccp_delta_seqno(cur->li_seqno, DCCP_SKB_CB(skb)->dccpd_seq) + 1;9798if (len - (s64)cur->li_length <= 0) /* duplicate or reordered */99return 0;100101if (SUB16(dccp_hdr(skb)->dccph_ccval, cur->li_ccval) > 4)102/*103* Implements RFC 4342, 10.2:104* If a packet S (skb) exists whose seqno comes `after' the one105* starting the current loss interval (cur) and if the modulo-16106* distance from C(cur) to C(S) is greater than 4, consider all107* subsequent packets as belonging to a new loss interval. This108* test is necessary since CCVal may wrap between intervals.109*/110cur->li_is_closed = 1;111112if (tfrc_lh_length(lh) == 1) /* due to RFC 3448, 6.3.1 */113return 0;114115cur->li_length = len;116tfrc_lh_calc_i_mean(lh);117118return lh->i_mean < old_i_mean;119}120121/* Determine if `new_loss' does begin a new loss interval [RFC 4342, 10.2] */122static inline u8 tfrc_lh_is_new_loss(struct tfrc_loss_interval *cur,123struct tfrc_rx_hist_entry *new_loss)124{125return dccp_delta_seqno(cur->li_seqno, new_loss->tfrchrx_seqno) > 0 &&126(cur->li_is_closed || SUB16(new_loss->tfrchrx_ccval, cur->li_ccval) > 4);127}128129/**130* tfrc_lh_interval_add - Insert new record into the Loss Interval database131* @lh: Loss Interval database132* @rh: Receive history containing a fresh loss event133* @calc_first_li: Caller-dependent routine to compute length of first interval134* @sk: Used by @calc_first_li in caller-specific way (subtyping)135* Updates I_mean and returns 1 if a new interval has in fact been added to @lh.136*/137int tfrc_lh_interval_add(struct tfrc_loss_hist *lh, struct tfrc_rx_hist *rh,138u32 (*calc_first_li)(struct sock *), struct sock *sk)139{140struct tfrc_loss_interval *cur = tfrc_lh_peek(lh), *new;141142if (cur != NULL && !tfrc_lh_is_new_loss(cur, tfrc_rx_hist_loss_prev(rh)))143return 0;144145new = tfrc_lh_demand_next(lh);146if (unlikely(new == NULL)) {147DCCP_CRIT("Cannot allocate/add loss record.");148return 0;149}150151new->li_seqno = tfrc_rx_hist_loss_prev(rh)->tfrchrx_seqno;152new->li_ccval = tfrc_rx_hist_loss_prev(rh)->tfrchrx_ccval;153new->li_is_closed = 0;154155if (++lh->counter == 1)156lh->i_mean = new->li_length = (*calc_first_li)(sk);157else {158cur->li_length = dccp_delta_seqno(cur->li_seqno, new->li_seqno);159new->li_length = dccp_delta_seqno(new->li_seqno,160tfrc_rx_hist_last_rcv(rh)->tfrchrx_seqno) + 1;161if (lh->counter > (2*LIH_SIZE))162lh->counter -= LIH_SIZE;163164tfrc_lh_calc_i_mean(lh);165}166return 1;167}168169int __init tfrc_li_init(void)170{171tfrc_lh_slab = kmem_cache_create("tfrc_li_hist",172sizeof(struct tfrc_loss_interval), 0,173SLAB_HWCACHE_ALIGN, NULL);174return tfrc_lh_slab == NULL ? -ENOBUFS : 0;175}176177void tfrc_li_exit(void)178{179if (tfrc_lh_slab != NULL) {180kmem_cache_destroy(tfrc_lh_slab);181tfrc_lh_slab = NULL;182}183}184185186