1ae6706f0SArnaldo Carvalho de Melo /* 2ae6706f0SArnaldo Carvalho de Melo * net/dccp/ccids/lib/loss_interval.c 3ae6706f0SArnaldo Carvalho de Melo * 48a9c7e92SGerrit Renker * Copyright (c) 2007 The University of Aberdeen, Scotland, UK 5b2f41ff4SIan McDonald * Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand. 6b2f41ff4SIan McDonald * Copyright (c) 2005-7 Ian McDonald <ian.mcdonald@jandi.co.nz> 7ae6706f0SArnaldo Carvalho de Melo * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br> 8ae6706f0SArnaldo Carvalho de Melo * 9ae6706f0SArnaldo Carvalho de Melo * This program is free software; you can redistribute it and/or modify 10ae6706f0SArnaldo Carvalho de Melo * it under the terms of the GNU General Public License as published by 11ae6706f0SArnaldo Carvalho de Melo * the Free Software Foundation; either version 2 of the License, or 12ae6706f0SArnaldo Carvalho de Melo * (at your option) any later version. 13ae6706f0SArnaldo Carvalho de Melo */ 1466a377c5SIan McDonald #include <net/sock.h> 15cc0a910bSArnaldo Carvalho de Melo #include "tfrc.h" 16ae6706f0SArnaldo Carvalho de Melo 17dd36a9abSArnaldo Carvalho de Melo #define DCCP_LI_HIST_IVAL_F_LENGTH 8 18dd36a9abSArnaldo Carvalho de Melo 19dd36a9abSArnaldo Carvalho de Melo struct dccp_li_hist_entry { 20dd36a9abSArnaldo Carvalho de Melo struct list_head dccplih_node; 21dd36a9abSArnaldo Carvalho de Melo u64 dccplih_seqno:48, 22dd36a9abSArnaldo Carvalho de Melo dccplih_win_count:4; 23dd36a9abSArnaldo Carvalho de Melo u32 dccplih_interval; 24dd36a9abSArnaldo Carvalho de Melo }; 25dd36a9abSArnaldo Carvalho de Melo 268a9c7e92SGerrit Renker static struct kmem_cache *tfrc_lh_slab __read_mostly; 278a9c7e92SGerrit Renker /* Loss Interval weights from [RFC 3448, 5.4], scaled by 10 */ 288a9c7e92SGerrit Renker static const int tfrc_lh_weights[NINTERVAL] = { 10, 10, 10, 10, 8, 6, 4, 2 }; 298a9c7e92SGerrit Renker 308a9c7e92SGerrit Renker /* implements LIFO semantics on the array */ 318a9c7e92SGerrit Renker static inline u8 LIH_INDEX(const u8 ctr) 328a9c7e92SGerrit Renker { 338a9c7e92SGerrit Renker return (LIH_SIZE - 1 - (ctr % LIH_SIZE)); 348a9c7e92SGerrit Renker } 358a9c7e92SGerrit Renker 368a9c7e92SGerrit Renker /* the `counter' index always points at the next entry to be populated */ 378a9c7e92SGerrit Renker static inline struct tfrc_loss_interval *tfrc_lh_peek(struct tfrc_loss_hist *lh) 388a9c7e92SGerrit Renker { 398a9c7e92SGerrit Renker return lh->counter ? lh->ring[LIH_INDEX(lh->counter - 1)] : NULL; 408a9c7e92SGerrit Renker } 418a9c7e92SGerrit Renker 428a9c7e92SGerrit Renker /* given i with 0 <= i <= k, return I_i as per the rfc3448bis notation */ 438a9c7e92SGerrit Renker static inline u32 tfrc_lh_get_interval(struct tfrc_loss_hist *lh, const u8 i) 448a9c7e92SGerrit Renker { 458a9c7e92SGerrit Renker BUG_ON(i >= lh->counter); 468a9c7e92SGerrit Renker return lh->ring[LIH_INDEX(lh->counter - i - 1)]->li_length; 478a9c7e92SGerrit Renker } 488a9c7e92SGerrit Renker 498a9c7e92SGerrit Renker /* 508a9c7e92SGerrit Renker * On-demand allocation and de-allocation of entries 518a9c7e92SGerrit Renker */ 528a9c7e92SGerrit Renker static struct tfrc_loss_interval *tfrc_lh_demand_next(struct tfrc_loss_hist *lh) 538a9c7e92SGerrit Renker { 548a9c7e92SGerrit Renker if (lh->ring[LIH_INDEX(lh->counter)] == NULL) 558a9c7e92SGerrit Renker lh->ring[LIH_INDEX(lh->counter)] = kmem_cache_alloc(tfrc_lh_slab, 568a9c7e92SGerrit Renker GFP_ATOMIC); 578a9c7e92SGerrit Renker return lh->ring[LIH_INDEX(lh->counter)]; 588a9c7e92SGerrit Renker } 598a9c7e92SGerrit Renker 608a9c7e92SGerrit Renker void tfrc_lh_cleanup(struct tfrc_loss_hist *lh) 618a9c7e92SGerrit Renker { 628a9c7e92SGerrit Renker if (!tfrc_lh_is_initialised(lh)) 638a9c7e92SGerrit Renker return; 648a9c7e92SGerrit Renker 658a9c7e92SGerrit Renker for (lh->counter = 0; lh->counter < LIH_SIZE; lh->counter++) 668a9c7e92SGerrit Renker if (lh->ring[LIH_INDEX(lh->counter)] != NULL) { 678a9c7e92SGerrit Renker kmem_cache_free(tfrc_lh_slab, 688a9c7e92SGerrit Renker lh->ring[LIH_INDEX(lh->counter)]); 698a9c7e92SGerrit Renker lh->ring[LIH_INDEX(lh->counter)] = NULL; 708a9c7e92SGerrit Renker } 718a9c7e92SGerrit Renker } 728a9c7e92SGerrit Renker EXPORT_SYMBOL_GPL(tfrc_lh_cleanup); 738a9c7e92SGerrit Renker 744fda25a2SAdrian Bunk static struct kmem_cache *dccp_li_cachep __read_mostly; 75cc4d6a3aSArnaldo Carvalho de Melo 76cc4d6a3aSArnaldo Carvalho de Melo static inline struct dccp_li_hist_entry *dccp_li_hist_entry_new(const gfp_t prio) 77ae6706f0SArnaldo Carvalho de Melo { 78cc4d6a3aSArnaldo Carvalho de Melo return kmem_cache_alloc(dccp_li_cachep, prio); 79ae6706f0SArnaldo Carvalho de Melo } 80ae6706f0SArnaldo Carvalho de Melo 81cc4d6a3aSArnaldo Carvalho de Melo static inline void dccp_li_hist_entry_delete(struct dccp_li_hist_entry *entry) 82c70b729eSArnaldo Carvalho de Melo { 83c70b729eSArnaldo Carvalho de Melo if (entry != NULL) 84cc4d6a3aSArnaldo Carvalho de Melo kmem_cache_free(dccp_li_cachep, entry); 85c70b729eSArnaldo Carvalho de Melo } 86c70b729eSArnaldo Carvalho de Melo 87cc4d6a3aSArnaldo Carvalho de Melo void dccp_li_hist_purge(struct list_head *list) 88ae6706f0SArnaldo Carvalho de Melo { 89ae6706f0SArnaldo Carvalho de Melo struct dccp_li_hist_entry *entry, *next; 90ae6706f0SArnaldo Carvalho de Melo 91ae6706f0SArnaldo Carvalho de Melo list_for_each_entry_safe(entry, next, list, dccplih_node) { 92ae6706f0SArnaldo Carvalho de Melo list_del_init(&entry->dccplih_node); 93cc4d6a3aSArnaldo Carvalho de Melo kmem_cache_free(dccp_li_cachep, entry); 94ae6706f0SArnaldo Carvalho de Melo } 95ae6706f0SArnaldo Carvalho de Melo } 96ae6706f0SArnaldo Carvalho de Melo 97ae6706f0SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_li_hist_purge); 98ae6706f0SArnaldo Carvalho de Melo 99ae6706f0SArnaldo Carvalho de Melo /* Weights used to calculate loss event rate */ 100ae6706f0SArnaldo Carvalho de Melo /* 101ae6706f0SArnaldo Carvalho de Melo * These are integers as per section 8 of RFC3448. We can then divide by 4 * 102ae6706f0SArnaldo Carvalho de Melo * when we use it. 103ae6706f0SArnaldo Carvalho de Melo */ 104ae6706f0SArnaldo Carvalho de Melo static const int dccp_li_hist_w[DCCP_LI_HIST_IVAL_F_LENGTH] = { 105ae6706f0SArnaldo Carvalho de Melo 4, 4, 4, 4, 3, 2, 1, 1, 106ae6706f0SArnaldo Carvalho de Melo }; 107ae6706f0SArnaldo Carvalho de Melo 108ae6706f0SArnaldo Carvalho de Melo u32 dccp_li_hist_calc_i_mean(struct list_head *list) 109ae6706f0SArnaldo Carvalho de Melo { 110ae6706f0SArnaldo Carvalho de Melo struct dccp_li_hist_entry *li_entry, *li_next; 111ae6706f0SArnaldo Carvalho de Melo int i = 0; 112ae6706f0SArnaldo Carvalho de Melo u32 i_tot; 113ae6706f0SArnaldo Carvalho de Melo u32 i_tot0 = 0; 114ae6706f0SArnaldo Carvalho de Melo u32 i_tot1 = 0; 115ae6706f0SArnaldo Carvalho de Melo u32 w_tot = 0; 116ae6706f0SArnaldo Carvalho de Melo 117ae6706f0SArnaldo Carvalho de Melo list_for_each_entry_safe(li_entry, li_next, list, dccplih_node) { 118551dc5f7SIan McDonald if (li_entry->dccplih_interval != ~0U) { 119ae6706f0SArnaldo Carvalho de Melo i_tot0 += li_entry->dccplih_interval * dccp_li_hist_w[i]; 120ae6706f0SArnaldo Carvalho de Melo w_tot += dccp_li_hist_w[i]; 121ae6706f0SArnaldo Carvalho de Melo if (i != 0) 122ae6706f0SArnaldo Carvalho de Melo i_tot1 += li_entry->dccplih_interval * dccp_li_hist_w[i - 1]; 12366a377c5SIan McDonald } 12466a377c5SIan McDonald 125ae6706f0SArnaldo Carvalho de Melo 126ae6706f0SArnaldo Carvalho de Melo if (++i > DCCP_LI_HIST_IVAL_F_LENGTH) 127ae6706f0SArnaldo Carvalho de Melo break; 128ae6706f0SArnaldo Carvalho de Melo } 129ae6706f0SArnaldo Carvalho de Melo 130ae6706f0SArnaldo Carvalho de Melo if (i != DCCP_LI_HIST_IVAL_F_LENGTH) 131ae6706f0SArnaldo Carvalho de Melo return 0; 132ae6706f0SArnaldo Carvalho de Melo 133ae6706f0SArnaldo Carvalho de Melo i_tot = max(i_tot0, i_tot1); 134ae6706f0SArnaldo Carvalho de Melo 13566a377c5SIan McDonald if (!w_tot) { 13659348b19SGerrit Renker DCCP_WARN("w_tot = 0\n"); 13766a377c5SIan McDonald return 1; 13866a377c5SIan McDonald } 139ae6706f0SArnaldo Carvalho de Melo 14066a377c5SIan McDonald return i_tot / w_tot; 141ae6706f0SArnaldo Carvalho de Melo } 142ae6706f0SArnaldo Carvalho de Melo 143ae6706f0SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_li_hist_calc_i_mean); 144ae6706f0SArnaldo Carvalho de Melo 1458a9c7e92SGerrit Renker static void tfrc_lh_calc_i_mean(struct tfrc_loss_hist *lh) 1468a9c7e92SGerrit Renker { 1478a9c7e92SGerrit Renker u32 i_i, i_tot0 = 0, i_tot1 = 0, w_tot = 0; 1488a9c7e92SGerrit Renker int i, k = tfrc_lh_length(lh) - 1; /* k is as in rfc3448bis, 5.4 */ 1498a9c7e92SGerrit Renker 1508a9c7e92SGerrit Renker for (i=0; i <= k; i++) { 1518a9c7e92SGerrit Renker i_i = tfrc_lh_get_interval(lh, i); 1528a9c7e92SGerrit Renker 1538a9c7e92SGerrit Renker if (i < k) { 1548a9c7e92SGerrit Renker i_tot0 += i_i * tfrc_lh_weights[i]; 1558a9c7e92SGerrit Renker w_tot += tfrc_lh_weights[i]; 1568a9c7e92SGerrit Renker } 1578a9c7e92SGerrit Renker if (i > 0) 1588a9c7e92SGerrit Renker i_tot1 += i_i * tfrc_lh_weights[i-1]; 1598a9c7e92SGerrit Renker } 1608a9c7e92SGerrit Renker 1618a9c7e92SGerrit Renker BUG_ON(w_tot == 0); 1628a9c7e92SGerrit Renker lh->i_mean = max(i_tot0, i_tot1) / w_tot; 1638a9c7e92SGerrit Renker } 1648a9c7e92SGerrit Renker 1658a9c7e92SGerrit Renker /** 1668a9c7e92SGerrit Renker * tfrc_lh_update_i_mean - Update the `open' loss interval I_0 1678a9c7e92SGerrit Renker * For recomputing p: returns `true' if p > p_prev <=> 1/p < 1/p_prev 1688a9c7e92SGerrit Renker */ 1698a9c7e92SGerrit Renker u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *skb) 1708a9c7e92SGerrit Renker { 1718a9c7e92SGerrit Renker struct tfrc_loss_interval *cur = tfrc_lh_peek(lh); 1728a9c7e92SGerrit Renker u32 old_i_mean = lh->i_mean; 1738a9c7e92SGerrit Renker s64 length; 1748a9c7e92SGerrit Renker 1758a9c7e92SGerrit Renker if (cur == NULL) /* not initialised */ 1768a9c7e92SGerrit Renker return 0; 1778a9c7e92SGerrit Renker 1788a9c7e92SGerrit Renker length = dccp_delta_seqno(cur->li_seqno, DCCP_SKB_CB(skb)->dccpd_seq); 1798a9c7e92SGerrit Renker 1808a9c7e92SGerrit Renker if (length - cur->li_length <= 0) /* duplicate or reordered */ 1818a9c7e92SGerrit Renker return 0; 1828a9c7e92SGerrit Renker 1838a9c7e92SGerrit Renker if (SUB16(dccp_hdr(skb)->dccph_ccval, cur->li_ccval) > 4) 1848a9c7e92SGerrit Renker /* 1858a9c7e92SGerrit Renker * Implements RFC 4342, 10.2: 1868a9c7e92SGerrit Renker * If a packet S (skb) exists whose seqno comes `after' the one 1878a9c7e92SGerrit Renker * starting the current loss interval (cur) and if the modulo-16 1888a9c7e92SGerrit Renker * distance from C(cur) to C(S) is greater than 4, consider all 1898a9c7e92SGerrit Renker * subsequent packets as belonging to a new loss interval. This 1908a9c7e92SGerrit Renker * test is necessary since CCVal may wrap between intervals. 1918a9c7e92SGerrit Renker */ 1928a9c7e92SGerrit Renker cur->li_is_closed = 1; 1938a9c7e92SGerrit Renker 1948a9c7e92SGerrit Renker if (tfrc_lh_length(lh) == 1) /* due to RFC 3448, 6.3.1 */ 1958a9c7e92SGerrit Renker return 0; 1968a9c7e92SGerrit Renker 1978a9c7e92SGerrit Renker cur->li_length = length; 1988a9c7e92SGerrit Renker tfrc_lh_calc_i_mean(lh); 1998a9c7e92SGerrit Renker 2008a9c7e92SGerrit Renker return (lh->i_mean < old_i_mean); 2018a9c7e92SGerrit Renker } 2028a9c7e92SGerrit Renker EXPORT_SYMBOL_GPL(tfrc_lh_update_i_mean); 2038a9c7e92SGerrit Renker 204cc4d6a3aSArnaldo Carvalho de Melo static int dccp_li_hist_interval_new(struct list_head *list, 2058c281780SArnaldo Carvalho de Melo const u64 seq_loss, const u8 win_loss) 206ae6706f0SArnaldo Carvalho de Melo { 20766a377c5SIan McDonald struct dccp_li_hist_entry *entry; 208ae6706f0SArnaldo Carvalho de Melo int i; 209ae6706f0SArnaldo Carvalho de Melo 21066a377c5SIan McDonald for (i = 0; i < DCCP_LI_HIST_IVAL_F_LENGTH; i++) { 211cc4d6a3aSArnaldo Carvalho de Melo entry = dccp_li_hist_entry_new(GFP_ATOMIC); 212ae6706f0SArnaldo Carvalho de Melo if (entry == NULL) { 213cc4d6a3aSArnaldo Carvalho de Melo dccp_li_hist_purge(list); 21459348b19SGerrit Renker DCCP_BUG("loss interval list entry is NULL"); 21566a377c5SIan McDonald return 0; 216ae6706f0SArnaldo Carvalho de Melo } 21766a377c5SIan McDonald entry->dccplih_interval = ~0; 218ae6706f0SArnaldo Carvalho de Melo list_add(&entry->dccplih_node, list); 219ae6706f0SArnaldo Carvalho de Melo } 220ae6706f0SArnaldo Carvalho de Melo 221ae6706f0SArnaldo Carvalho de Melo entry->dccplih_seqno = seq_loss; 222ae6706f0SArnaldo Carvalho de Melo entry->dccplih_win_count = win_loss; 22366a377c5SIan McDonald return 1; 224ae6706f0SArnaldo Carvalho de Melo } 225ae6706f0SArnaldo Carvalho de Melo 226cc0a910bSArnaldo Carvalho de Melo /* calculate first loss interval 227cc0a910bSArnaldo Carvalho de Melo * 228cc0a910bSArnaldo Carvalho de Melo * returns estimated loss interval in usecs */ 229cc0a910bSArnaldo Carvalho de Melo static u32 dccp_li_calc_first_li(struct sock *sk, 230cc0a910bSArnaldo Carvalho de Melo struct list_head *hist_list, 231e7c23357SArnaldo Carvalho de Melo ktime_t last_feedback, 232cc0a910bSArnaldo Carvalho de Melo u16 s, u32 bytes_recv, 233cc0a910bSArnaldo Carvalho de Melo u32 previous_x_recv) 234cc0a910bSArnaldo Carvalho de Melo { 235b84a2189SArnaldo Carvalho de Melo /* 236b84a2189SArnaldo Carvalho de Melo * FIXME: 237b84a2189SArnaldo Carvalho de Melo * Will be rewritten in the upcoming new loss intervals code. 238b84a2189SArnaldo Carvalho de Melo * Has to be commented ou because it relies on the old rx history 239b84a2189SArnaldo Carvalho de Melo * data structures 240b84a2189SArnaldo Carvalho de Melo */ 241b84a2189SArnaldo Carvalho de Melo #if 0 242d58d1af0SArnaldo Carvalho de Melo struct tfrc_rx_hist_entry *entry, *next, *tail = NULL; 243cc0a910bSArnaldo Carvalho de Melo u32 x_recv, p; 244cc0a910bSArnaldo Carvalho de Melo suseconds_t rtt, delta; 245e7c23357SArnaldo Carvalho de Melo ktime_t tstamp = ktime_set(0, 0); 246cc0a910bSArnaldo Carvalho de Melo int interval = 0; 247cc0a910bSArnaldo Carvalho de Melo int win_count = 0; 248cc0a910bSArnaldo Carvalho de Melo int step = 0; 249cc0a910bSArnaldo Carvalho de Melo u64 fval; 250cc0a910bSArnaldo Carvalho de Melo 251d58d1af0SArnaldo Carvalho de Melo list_for_each_entry_safe(entry, next, hist_list, tfrchrx_node) { 252d58d1af0SArnaldo Carvalho de Melo if (tfrc_rx_hist_entry_data_packet(entry)) { 253cc0a910bSArnaldo Carvalho de Melo tail = entry; 254cc0a910bSArnaldo Carvalho de Melo 255cc0a910bSArnaldo Carvalho de Melo switch (step) { 256cc0a910bSArnaldo Carvalho de Melo case 0: 257d58d1af0SArnaldo Carvalho de Melo tstamp = entry->tfrchrx_tstamp; 258d58d1af0SArnaldo Carvalho de Melo win_count = entry->tfrchrx_ccval; 259cc0a910bSArnaldo Carvalho de Melo step = 1; 260cc0a910bSArnaldo Carvalho de Melo break; 261cc0a910bSArnaldo Carvalho de Melo case 1: 262d58d1af0SArnaldo Carvalho de Melo interval = win_count - entry->tfrchrx_ccval; 263cc0a910bSArnaldo Carvalho de Melo if (interval < 0) 264cc0a910bSArnaldo Carvalho de Melo interval += TFRC_WIN_COUNT_LIMIT; 265cc0a910bSArnaldo Carvalho de Melo if (interval > 4) 266cc0a910bSArnaldo Carvalho de Melo goto found; 267cc0a910bSArnaldo Carvalho de Melo break; 268cc0a910bSArnaldo Carvalho de Melo } 269cc0a910bSArnaldo Carvalho de Melo } 270cc0a910bSArnaldo Carvalho de Melo } 271cc0a910bSArnaldo Carvalho de Melo 272cc0a910bSArnaldo Carvalho de Melo if (unlikely(step == 0)) { 273cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("%s(%p), packet history has no data packets!\n", 274cc0a910bSArnaldo Carvalho de Melo dccp_role(sk), sk); 275cc0a910bSArnaldo Carvalho de Melo return ~0; 276cc0a910bSArnaldo Carvalho de Melo } 277cc0a910bSArnaldo Carvalho de Melo 278cc0a910bSArnaldo Carvalho de Melo if (unlikely(interval == 0)) { 279cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("%s(%p), Could not find a win_count interval > 0. " 280cc0a910bSArnaldo Carvalho de Melo "Defaulting to 1\n", dccp_role(sk), sk); 281cc0a910bSArnaldo Carvalho de Melo interval = 1; 282cc0a910bSArnaldo Carvalho de Melo } 283cc0a910bSArnaldo Carvalho de Melo found: 284cc0a910bSArnaldo Carvalho de Melo if (!tail) { 285cc0a910bSArnaldo Carvalho de Melo DCCP_CRIT("tail is null\n"); 286cc0a910bSArnaldo Carvalho de Melo return ~0; 287cc0a910bSArnaldo Carvalho de Melo } 288cc0a910bSArnaldo Carvalho de Melo 289d58d1af0SArnaldo Carvalho de Melo delta = ktime_us_delta(tstamp, tail->tfrchrx_tstamp); 290cc0a910bSArnaldo Carvalho de Melo DCCP_BUG_ON(delta < 0); 291cc0a910bSArnaldo Carvalho de Melo 292cc0a910bSArnaldo Carvalho de Melo rtt = delta * 4 / interval; 293cc0a910bSArnaldo Carvalho de Melo dccp_pr_debug("%s(%p), approximated RTT to %dus\n", 294cc0a910bSArnaldo Carvalho de Melo dccp_role(sk), sk, (int)rtt); 295cc0a910bSArnaldo Carvalho de Melo 296cc0a910bSArnaldo Carvalho de Melo /* 297cc0a910bSArnaldo Carvalho de Melo * Determine the length of the first loss interval via inverse lookup. 298cc0a910bSArnaldo Carvalho de Melo * Assume that X_recv can be computed by the throughput equation 299cc0a910bSArnaldo Carvalho de Melo * s 300cc0a910bSArnaldo Carvalho de Melo * X_recv = -------- 301cc0a910bSArnaldo Carvalho de Melo * R * fval 302cc0a910bSArnaldo Carvalho de Melo * Find some p such that f(p) = fval; return 1/p [RFC 3448, 6.3.1]. 303cc0a910bSArnaldo Carvalho de Melo */ 304cc0a910bSArnaldo Carvalho de Melo if (rtt == 0) { /* would result in divide-by-zero */ 305cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("RTT==0\n"); 306cc0a910bSArnaldo Carvalho de Melo return ~0; 307cc0a910bSArnaldo Carvalho de Melo } 308cc0a910bSArnaldo Carvalho de Melo 309e7c23357SArnaldo Carvalho de Melo delta = ktime_us_delta(ktime_get_real(), last_feedback); 310cc0a910bSArnaldo Carvalho de Melo DCCP_BUG_ON(delta <= 0); 311cc0a910bSArnaldo Carvalho de Melo 312cc0a910bSArnaldo Carvalho de Melo x_recv = scaled_div32(bytes_recv, delta); 313cc0a910bSArnaldo Carvalho de Melo if (x_recv == 0) { /* would also trigger divide-by-zero */ 314cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("X_recv==0\n"); 315cc0a910bSArnaldo Carvalho de Melo if (previous_x_recv == 0) { 316cc0a910bSArnaldo Carvalho de Melo DCCP_BUG("stored value of X_recv is zero"); 317cc0a910bSArnaldo Carvalho de Melo return ~0; 318cc0a910bSArnaldo Carvalho de Melo } 319cc0a910bSArnaldo Carvalho de Melo x_recv = previous_x_recv; 320cc0a910bSArnaldo Carvalho de Melo } 321cc0a910bSArnaldo Carvalho de Melo 322cc0a910bSArnaldo Carvalho de Melo fval = scaled_div(s, rtt); 323cc0a910bSArnaldo Carvalho de Melo fval = scaled_div32(fval, x_recv); 324cc0a910bSArnaldo Carvalho de Melo p = tfrc_calc_x_reverse_lookup(fval); 325cc0a910bSArnaldo Carvalho de Melo 326cc0a910bSArnaldo Carvalho de Melo dccp_pr_debug("%s(%p), receive rate=%u bytes/s, implied " 327cc0a910bSArnaldo Carvalho de Melo "loss rate=%u\n", dccp_role(sk), sk, x_recv, p); 328cc0a910bSArnaldo Carvalho de Melo 329b84a2189SArnaldo Carvalho de Melo if (p != 0) 330cc0a910bSArnaldo Carvalho de Melo return 1000000 / p; 331b84a2189SArnaldo Carvalho de Melo #endif 332b84a2189SArnaldo Carvalho de Melo return ~0; 333cc0a910bSArnaldo Carvalho de Melo } 334cc0a910bSArnaldo Carvalho de Melo 335cc4d6a3aSArnaldo Carvalho de Melo void dccp_li_update_li(struct sock *sk, 336cc0a910bSArnaldo Carvalho de Melo struct list_head *li_hist_list, 337cc0a910bSArnaldo Carvalho de Melo struct list_head *hist_list, 338e7c23357SArnaldo Carvalho de Melo ktime_t last_feedback, u16 s, u32 bytes_recv, 339cc0a910bSArnaldo Carvalho de Melo u32 previous_x_recv, u64 seq_loss, u8 win_loss) 340cc0a910bSArnaldo Carvalho de Melo { 341cc0a910bSArnaldo Carvalho de Melo struct dccp_li_hist_entry *head; 342cc0a910bSArnaldo Carvalho de Melo u64 seq_temp; 343cc0a910bSArnaldo Carvalho de Melo 344cc0a910bSArnaldo Carvalho de Melo if (list_empty(li_hist_list)) { 345cc4d6a3aSArnaldo Carvalho de Melo if (!dccp_li_hist_interval_new(li_hist_list, seq_loss, 346cc4d6a3aSArnaldo Carvalho de Melo win_loss)) 347cc0a910bSArnaldo Carvalho de Melo return; 348cc0a910bSArnaldo Carvalho de Melo 349cc0a910bSArnaldo Carvalho de Melo head = list_entry(li_hist_list->next, struct dccp_li_hist_entry, 350cc0a910bSArnaldo Carvalho de Melo dccplih_node); 351cc0a910bSArnaldo Carvalho de Melo head->dccplih_interval = dccp_li_calc_first_li(sk, hist_list, 352cc0a910bSArnaldo Carvalho de Melo last_feedback, 353cc0a910bSArnaldo Carvalho de Melo s, bytes_recv, 354cc0a910bSArnaldo Carvalho de Melo previous_x_recv); 355cc0a910bSArnaldo Carvalho de Melo } else { 356cc0a910bSArnaldo Carvalho de Melo struct dccp_li_hist_entry *entry; 357cc0a910bSArnaldo Carvalho de Melo struct list_head *tail; 358cc0a910bSArnaldo Carvalho de Melo 359cc0a910bSArnaldo Carvalho de Melo head = list_entry(li_hist_list->next, struct dccp_li_hist_entry, 360cc0a910bSArnaldo Carvalho de Melo dccplih_node); 361cc0a910bSArnaldo Carvalho de Melo /* FIXME win count check removed as was wrong */ 362cc0a910bSArnaldo Carvalho de Melo /* should make this check with receive history */ 363cc0a910bSArnaldo Carvalho de Melo /* and compare there as per section 10.2 of RFC4342 */ 364cc0a910bSArnaldo Carvalho de Melo 365cc0a910bSArnaldo Carvalho de Melo /* new loss event detected */ 366cc0a910bSArnaldo Carvalho de Melo /* calculate last interval length */ 367cc0a910bSArnaldo Carvalho de Melo seq_temp = dccp_delta_seqno(head->dccplih_seqno, seq_loss); 368cc4d6a3aSArnaldo Carvalho de Melo entry = dccp_li_hist_entry_new(GFP_ATOMIC); 369cc0a910bSArnaldo Carvalho de Melo 370cc0a910bSArnaldo Carvalho de Melo if (entry == NULL) { 371cc0a910bSArnaldo Carvalho de Melo DCCP_BUG("out of memory - can not allocate entry"); 372cc0a910bSArnaldo Carvalho de Melo return; 373cc0a910bSArnaldo Carvalho de Melo } 374cc0a910bSArnaldo Carvalho de Melo 375cc0a910bSArnaldo Carvalho de Melo list_add(&entry->dccplih_node, li_hist_list); 376cc0a910bSArnaldo Carvalho de Melo 377cc0a910bSArnaldo Carvalho de Melo tail = li_hist_list->prev; 378cc0a910bSArnaldo Carvalho de Melo list_del(tail); 379cc4d6a3aSArnaldo Carvalho de Melo kmem_cache_free(dccp_li_cachep, tail); 380cc0a910bSArnaldo Carvalho de Melo 381cc0a910bSArnaldo Carvalho de Melo /* Create the newest interval */ 382cc0a910bSArnaldo Carvalho de Melo entry->dccplih_seqno = seq_loss; 383cc0a910bSArnaldo Carvalho de Melo entry->dccplih_interval = seq_temp; 384cc0a910bSArnaldo Carvalho de Melo entry->dccplih_win_count = win_loss; 385cc0a910bSArnaldo Carvalho de Melo } 386cc0a910bSArnaldo Carvalho de Melo } 387cc0a910bSArnaldo Carvalho de Melo 388cc0a910bSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_li_update_li); 389cc4d6a3aSArnaldo Carvalho de Melo 3908a9c7e92SGerrit Renker /* Determine if `new_loss' does begin a new loss interval [RFC 4342, 10.2] */ 3918a9c7e92SGerrit Renker static inline u8 tfrc_lh_is_new_loss(struct tfrc_loss_interval *cur, 3928a9c7e92SGerrit Renker struct tfrc_rx_hist_entry *new_loss) 3938a9c7e92SGerrit Renker { 3948a9c7e92SGerrit Renker return dccp_delta_seqno(cur->li_seqno, new_loss->tfrchrx_seqno) > 0 && 3958a9c7e92SGerrit Renker (cur->li_is_closed || SUB16(new_loss->tfrchrx_ccval, cur->li_ccval) > 4); 3968a9c7e92SGerrit Renker } 3978a9c7e92SGerrit Renker 3988a9c7e92SGerrit Renker /** tfrc_lh_interval_add - Insert new record into the Loss Interval database 3998a9c7e92SGerrit Renker * @lh: Loss Interval database 4008a9c7e92SGerrit Renker * @rh: Receive history containing a fresh loss event 4018a9c7e92SGerrit Renker * @calc_first_li: Caller-dependent routine to compute length of first interval 4028a9c7e92SGerrit Renker * @sk: Used by @calc_first_li in caller-specific way (subtyping) 4038a9c7e92SGerrit Renker * Updates I_mean and returns 1 if a new interval has in fact been added to @lh. 4048a9c7e92SGerrit Renker */ 4058a9c7e92SGerrit Renker int tfrc_lh_interval_add(struct tfrc_loss_hist *lh, struct tfrc_rx_hist *rh, 4068a9c7e92SGerrit Renker u32 (*calc_first_li)(struct sock *), struct sock *sk) 4078a9c7e92SGerrit Renker { 4088a9c7e92SGerrit Renker struct tfrc_loss_interval *cur = tfrc_lh_peek(lh), *new; 4098a9c7e92SGerrit Renker 4108a9c7e92SGerrit Renker if (cur != NULL && !tfrc_lh_is_new_loss(cur, tfrc_rx_hist_loss_prev(rh))) 4118a9c7e92SGerrit Renker return 0; 4128a9c7e92SGerrit Renker 4138a9c7e92SGerrit Renker new = tfrc_lh_demand_next(lh); 4148a9c7e92SGerrit Renker if (unlikely(new == NULL)) { 4158a9c7e92SGerrit Renker DCCP_CRIT("Cannot allocate/add loss record."); 4168a9c7e92SGerrit Renker return 0; 4178a9c7e92SGerrit Renker } 4188a9c7e92SGerrit Renker 4198a9c7e92SGerrit Renker new->li_seqno = tfrc_rx_hist_loss_prev(rh)->tfrchrx_seqno; 4208a9c7e92SGerrit Renker new->li_ccval = tfrc_rx_hist_loss_prev(rh)->tfrchrx_ccval; 4218a9c7e92SGerrit Renker new->li_is_closed = 0; 4228a9c7e92SGerrit Renker 4238a9c7e92SGerrit Renker if (++lh->counter == 1) 4248a9c7e92SGerrit Renker lh->i_mean = new->li_length = (*calc_first_li)(sk); 4258a9c7e92SGerrit Renker else { 4268a9c7e92SGerrit Renker cur->li_length = dccp_delta_seqno(cur->li_seqno, new->li_seqno); 4278a9c7e92SGerrit Renker new->li_length = dccp_delta_seqno(new->li_seqno, 4288a9c7e92SGerrit Renker tfrc_rx_hist_last_rcv(rh)->tfrchrx_seqno); 4298a9c7e92SGerrit Renker if (lh->counter > (2*LIH_SIZE)) 4308a9c7e92SGerrit Renker lh->counter -= LIH_SIZE; 4318a9c7e92SGerrit Renker 4328a9c7e92SGerrit Renker tfrc_lh_calc_i_mean(lh); 4338a9c7e92SGerrit Renker } 4348a9c7e92SGerrit Renker return 1; 4358a9c7e92SGerrit Renker } 4368a9c7e92SGerrit Renker EXPORT_SYMBOL_GPL(tfrc_lh_interval_add); 4378a9c7e92SGerrit Renker 438*954c2db8SGerrit Renker int __init tfrc_li_init(void) 439cc4d6a3aSArnaldo Carvalho de Melo { 440*954c2db8SGerrit Renker tfrc_lh_slab = kmem_cache_create("tfrc_li_hist", 441*954c2db8SGerrit Renker sizeof(struct tfrc_loss_interval), 0, 442*954c2db8SGerrit Renker SLAB_HWCACHE_ALIGN, NULL); 443*954c2db8SGerrit Renker return tfrc_lh_slab == NULL ? -ENOBUFS : 0; 444cc4d6a3aSArnaldo Carvalho de Melo } 445cc4d6a3aSArnaldo Carvalho de Melo 446*954c2db8SGerrit Renker void tfrc_li_exit(void) 447cc4d6a3aSArnaldo Carvalho de Melo { 448*954c2db8SGerrit Renker if (tfrc_lh_slab != NULL) { 449*954c2db8SGerrit Renker kmem_cache_destroy(tfrc_lh_slab); 450*954c2db8SGerrit Renker tfrc_lh_slab = NULL; 451cc4d6a3aSArnaldo Carvalho de Melo } 452276f2edcSArnaldo Carvalho de Melo } 453