1ae6706f0SArnaldo Carvalho de Melo /* 2ae6706f0SArnaldo Carvalho de Melo * net/dccp/ccids/lib/loss_interval.c 3ae6706f0SArnaldo Carvalho de Melo * 4b2f41ff4SIan McDonald * Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand. 5b2f41ff4SIan McDonald * Copyright (c) 2005-7 Ian McDonald <ian.mcdonald@jandi.co.nz> 6ae6706f0SArnaldo Carvalho de Melo * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br> 7ae6706f0SArnaldo Carvalho de Melo * 8ae6706f0SArnaldo Carvalho de Melo * This program is free software; you can redistribute it and/or modify 9ae6706f0SArnaldo Carvalho de Melo * it under the terms of the GNU General Public License as published by 10ae6706f0SArnaldo Carvalho de Melo * the Free Software Foundation; either version 2 of the License, or 11ae6706f0SArnaldo Carvalho de Melo * (at your option) any later version. 12ae6706f0SArnaldo Carvalho de Melo */ 13ae6706f0SArnaldo Carvalho de Melo 14ae6706f0SArnaldo Carvalho de Melo #include <linux/module.h> 1566a377c5SIan McDonald #include <net/sock.h> 1659348b19SGerrit Renker #include "../../dccp.h" 17ae6706f0SArnaldo Carvalho de Melo #include "loss_interval.h" 18cc0a910bSArnaldo Carvalho de Melo #include "packet_history.h" 19cc0a910bSArnaldo Carvalho de Melo #include "tfrc.h" 20ae6706f0SArnaldo Carvalho de Melo 21*cc4d6a3aSArnaldo Carvalho de Melo struct kmem_cache *dccp_li_cachep __read_mostly; 22*cc4d6a3aSArnaldo Carvalho de Melo 23*cc4d6a3aSArnaldo Carvalho de Melo static inline struct dccp_li_hist_entry *dccp_li_hist_entry_new(const gfp_t prio) 24ae6706f0SArnaldo Carvalho de Melo { 25*cc4d6a3aSArnaldo Carvalho de Melo return kmem_cache_alloc(dccp_li_cachep, prio); 26ae6706f0SArnaldo Carvalho de Melo } 27ae6706f0SArnaldo Carvalho de Melo 28*cc4d6a3aSArnaldo Carvalho de Melo static inline void dccp_li_hist_entry_delete(struct dccp_li_hist_entry *entry) 29c70b729eSArnaldo Carvalho de Melo { 30c70b729eSArnaldo Carvalho de Melo if (entry != NULL) 31*cc4d6a3aSArnaldo Carvalho de Melo kmem_cache_free(dccp_li_cachep, entry); 32c70b729eSArnaldo Carvalho de Melo } 33c70b729eSArnaldo Carvalho de Melo 34*cc4d6a3aSArnaldo Carvalho de Melo void dccp_li_hist_purge(struct list_head *list) 35ae6706f0SArnaldo Carvalho de Melo { 36ae6706f0SArnaldo Carvalho de Melo struct dccp_li_hist_entry *entry, *next; 37ae6706f0SArnaldo Carvalho de Melo 38ae6706f0SArnaldo Carvalho de Melo list_for_each_entry_safe(entry, next, list, dccplih_node) { 39ae6706f0SArnaldo Carvalho de Melo list_del_init(&entry->dccplih_node); 40*cc4d6a3aSArnaldo Carvalho de Melo kmem_cache_free(dccp_li_cachep, entry); 41ae6706f0SArnaldo Carvalho de Melo } 42ae6706f0SArnaldo Carvalho de Melo } 43ae6706f0SArnaldo Carvalho de Melo 44ae6706f0SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_li_hist_purge); 45ae6706f0SArnaldo Carvalho de Melo 46ae6706f0SArnaldo Carvalho de Melo /* Weights used to calculate loss event rate */ 47ae6706f0SArnaldo Carvalho de Melo /* 48ae6706f0SArnaldo Carvalho de Melo * These are integers as per section 8 of RFC3448. We can then divide by 4 * 49ae6706f0SArnaldo Carvalho de Melo * when we use it. 50ae6706f0SArnaldo Carvalho de Melo */ 51ae6706f0SArnaldo Carvalho de Melo static const int dccp_li_hist_w[DCCP_LI_HIST_IVAL_F_LENGTH] = { 52ae6706f0SArnaldo Carvalho de Melo 4, 4, 4, 4, 3, 2, 1, 1, 53ae6706f0SArnaldo Carvalho de Melo }; 54ae6706f0SArnaldo Carvalho de Melo 55ae6706f0SArnaldo Carvalho de Melo u32 dccp_li_hist_calc_i_mean(struct list_head *list) 56ae6706f0SArnaldo Carvalho de Melo { 57ae6706f0SArnaldo Carvalho de Melo struct dccp_li_hist_entry *li_entry, *li_next; 58ae6706f0SArnaldo Carvalho de Melo int i = 0; 59ae6706f0SArnaldo Carvalho de Melo u32 i_tot; 60ae6706f0SArnaldo Carvalho de Melo u32 i_tot0 = 0; 61ae6706f0SArnaldo Carvalho de Melo u32 i_tot1 = 0; 62ae6706f0SArnaldo Carvalho de Melo u32 w_tot = 0; 63ae6706f0SArnaldo Carvalho de Melo 64ae6706f0SArnaldo Carvalho de Melo list_for_each_entry_safe(li_entry, li_next, list, dccplih_node) { 65551dc5f7SIan McDonald if (li_entry->dccplih_interval != ~0U) { 66ae6706f0SArnaldo Carvalho de Melo i_tot0 += li_entry->dccplih_interval * dccp_li_hist_w[i]; 67ae6706f0SArnaldo Carvalho de Melo w_tot += dccp_li_hist_w[i]; 68ae6706f0SArnaldo Carvalho de Melo if (i != 0) 69ae6706f0SArnaldo Carvalho de Melo i_tot1 += li_entry->dccplih_interval * dccp_li_hist_w[i - 1]; 7066a377c5SIan McDonald } 7166a377c5SIan McDonald 72ae6706f0SArnaldo Carvalho de Melo 73ae6706f0SArnaldo Carvalho de Melo if (++i > DCCP_LI_HIST_IVAL_F_LENGTH) 74ae6706f0SArnaldo Carvalho de Melo break; 75ae6706f0SArnaldo Carvalho de Melo } 76ae6706f0SArnaldo Carvalho de Melo 77ae6706f0SArnaldo Carvalho de Melo if (i != DCCP_LI_HIST_IVAL_F_LENGTH) 78ae6706f0SArnaldo Carvalho de Melo return 0; 79ae6706f0SArnaldo Carvalho de Melo 80ae6706f0SArnaldo Carvalho de Melo i_tot = max(i_tot0, i_tot1); 81ae6706f0SArnaldo Carvalho de Melo 8266a377c5SIan McDonald if (!w_tot) { 8359348b19SGerrit Renker DCCP_WARN("w_tot = 0\n"); 8466a377c5SIan McDonald return 1; 8566a377c5SIan McDonald } 86ae6706f0SArnaldo Carvalho de Melo 8766a377c5SIan McDonald return i_tot / w_tot; 88ae6706f0SArnaldo Carvalho de Melo } 89ae6706f0SArnaldo Carvalho de Melo 90ae6706f0SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_li_hist_calc_i_mean); 91ae6706f0SArnaldo Carvalho de Melo 92*cc4d6a3aSArnaldo Carvalho de Melo static int dccp_li_hist_interval_new(struct list_head *list, 938c281780SArnaldo Carvalho de Melo const u64 seq_loss, const u8 win_loss) 94ae6706f0SArnaldo Carvalho de Melo { 9566a377c5SIan McDonald struct dccp_li_hist_entry *entry; 96ae6706f0SArnaldo Carvalho de Melo int i; 97ae6706f0SArnaldo Carvalho de Melo 9866a377c5SIan McDonald for (i = 0; i < DCCP_LI_HIST_IVAL_F_LENGTH; i++) { 99*cc4d6a3aSArnaldo Carvalho de Melo entry = dccp_li_hist_entry_new(GFP_ATOMIC); 100ae6706f0SArnaldo Carvalho de Melo if (entry == NULL) { 101*cc4d6a3aSArnaldo Carvalho de Melo dccp_li_hist_purge(list); 10259348b19SGerrit Renker DCCP_BUG("loss interval list entry is NULL"); 10366a377c5SIan McDonald return 0; 104ae6706f0SArnaldo Carvalho de Melo } 10566a377c5SIan McDonald entry->dccplih_interval = ~0; 106ae6706f0SArnaldo Carvalho de Melo list_add(&entry->dccplih_node, list); 107ae6706f0SArnaldo Carvalho de Melo } 108ae6706f0SArnaldo Carvalho de Melo 109ae6706f0SArnaldo Carvalho de Melo entry->dccplih_seqno = seq_loss; 110ae6706f0SArnaldo Carvalho de Melo entry->dccplih_win_count = win_loss; 11166a377c5SIan McDonald return 1; 112ae6706f0SArnaldo Carvalho de Melo } 113ae6706f0SArnaldo Carvalho de Melo 114cc0a910bSArnaldo Carvalho de Melo /* calculate first loss interval 115cc0a910bSArnaldo Carvalho de Melo * 116cc0a910bSArnaldo Carvalho de Melo * returns estimated loss interval in usecs */ 117cc0a910bSArnaldo Carvalho de Melo static u32 dccp_li_calc_first_li(struct sock *sk, 118cc0a910bSArnaldo Carvalho de Melo struct list_head *hist_list, 119cc0a910bSArnaldo Carvalho de Melo struct timeval *last_feedback, 120cc0a910bSArnaldo Carvalho de Melo u16 s, u32 bytes_recv, 121cc0a910bSArnaldo Carvalho de Melo u32 previous_x_recv) 122cc0a910bSArnaldo Carvalho de Melo { 123cc0a910bSArnaldo Carvalho de Melo struct dccp_rx_hist_entry *entry, *next, *tail = NULL; 124cc0a910bSArnaldo Carvalho de Melo u32 x_recv, p; 125cc0a910bSArnaldo Carvalho de Melo suseconds_t rtt, delta; 126cc0a910bSArnaldo Carvalho de Melo struct timeval tstamp = { 0, 0 }; 127cc0a910bSArnaldo Carvalho de Melo int interval = 0; 128cc0a910bSArnaldo Carvalho de Melo int win_count = 0; 129cc0a910bSArnaldo Carvalho de Melo int step = 0; 130cc0a910bSArnaldo Carvalho de Melo u64 fval; 131cc0a910bSArnaldo Carvalho de Melo 132cc0a910bSArnaldo Carvalho de Melo list_for_each_entry_safe(entry, next, hist_list, dccphrx_node) { 133cc0a910bSArnaldo Carvalho de Melo if (dccp_rx_hist_entry_data_packet(entry)) { 134cc0a910bSArnaldo Carvalho de Melo tail = entry; 135cc0a910bSArnaldo Carvalho de Melo 136cc0a910bSArnaldo Carvalho de Melo switch (step) { 137cc0a910bSArnaldo Carvalho de Melo case 0: 138cc0a910bSArnaldo Carvalho de Melo tstamp = entry->dccphrx_tstamp; 139cc0a910bSArnaldo Carvalho de Melo win_count = entry->dccphrx_ccval; 140cc0a910bSArnaldo Carvalho de Melo step = 1; 141cc0a910bSArnaldo Carvalho de Melo break; 142cc0a910bSArnaldo Carvalho de Melo case 1: 143cc0a910bSArnaldo Carvalho de Melo interval = win_count - entry->dccphrx_ccval; 144cc0a910bSArnaldo Carvalho de Melo if (interval < 0) 145cc0a910bSArnaldo Carvalho de Melo interval += TFRC_WIN_COUNT_LIMIT; 146cc0a910bSArnaldo Carvalho de Melo if (interval > 4) 147cc0a910bSArnaldo Carvalho de Melo goto found; 148cc0a910bSArnaldo Carvalho de Melo break; 149cc0a910bSArnaldo Carvalho de Melo } 150cc0a910bSArnaldo Carvalho de Melo } 151cc0a910bSArnaldo Carvalho de Melo } 152cc0a910bSArnaldo Carvalho de Melo 153cc0a910bSArnaldo Carvalho de Melo if (unlikely(step == 0)) { 154cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("%s(%p), packet history has no data packets!\n", 155cc0a910bSArnaldo Carvalho de Melo dccp_role(sk), sk); 156cc0a910bSArnaldo Carvalho de Melo return ~0; 157cc0a910bSArnaldo Carvalho de Melo } 158cc0a910bSArnaldo Carvalho de Melo 159cc0a910bSArnaldo Carvalho de Melo if (unlikely(interval == 0)) { 160cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("%s(%p), Could not find a win_count interval > 0." 161cc0a910bSArnaldo Carvalho de Melo "Defaulting to 1\n", dccp_role(sk), sk); 162cc0a910bSArnaldo Carvalho de Melo interval = 1; 163cc0a910bSArnaldo Carvalho de Melo } 164cc0a910bSArnaldo Carvalho de Melo found: 165cc0a910bSArnaldo Carvalho de Melo if (!tail) { 166cc0a910bSArnaldo Carvalho de Melo DCCP_CRIT("tail is null\n"); 167cc0a910bSArnaldo Carvalho de Melo return ~0; 168cc0a910bSArnaldo Carvalho de Melo } 169cc0a910bSArnaldo Carvalho de Melo 170cc0a910bSArnaldo Carvalho de Melo delta = timeval_delta(&tstamp, &tail->dccphrx_tstamp); 171cc0a910bSArnaldo Carvalho de Melo DCCP_BUG_ON(delta < 0); 172cc0a910bSArnaldo Carvalho de Melo 173cc0a910bSArnaldo Carvalho de Melo rtt = delta * 4 / interval; 174cc0a910bSArnaldo Carvalho de Melo dccp_pr_debug("%s(%p), approximated RTT to %dus\n", 175cc0a910bSArnaldo Carvalho de Melo dccp_role(sk), sk, (int)rtt); 176cc0a910bSArnaldo Carvalho de Melo 177cc0a910bSArnaldo Carvalho de Melo /* 178cc0a910bSArnaldo Carvalho de Melo * Determine the length of the first loss interval via inverse lookup. 179cc0a910bSArnaldo Carvalho de Melo * Assume that X_recv can be computed by the throughput equation 180cc0a910bSArnaldo Carvalho de Melo * s 181cc0a910bSArnaldo Carvalho de Melo * X_recv = -------- 182cc0a910bSArnaldo Carvalho de Melo * R * fval 183cc0a910bSArnaldo Carvalho de Melo * Find some p such that f(p) = fval; return 1/p [RFC 3448, 6.3.1]. 184cc0a910bSArnaldo Carvalho de Melo */ 185cc0a910bSArnaldo Carvalho de Melo if (rtt == 0) { /* would result in divide-by-zero */ 186cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("RTT==0\n"); 187cc0a910bSArnaldo Carvalho de Melo return ~0; 188cc0a910bSArnaldo Carvalho de Melo } 189cc0a910bSArnaldo Carvalho de Melo 190cc0a910bSArnaldo Carvalho de Melo dccp_timestamp(sk, &tstamp); 191cc0a910bSArnaldo Carvalho de Melo delta = timeval_delta(&tstamp, last_feedback); 192cc0a910bSArnaldo Carvalho de Melo DCCP_BUG_ON(delta <= 0); 193cc0a910bSArnaldo Carvalho de Melo 194cc0a910bSArnaldo Carvalho de Melo x_recv = scaled_div32(bytes_recv, delta); 195cc0a910bSArnaldo Carvalho de Melo if (x_recv == 0) { /* would also trigger divide-by-zero */ 196cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("X_recv==0\n"); 197cc0a910bSArnaldo Carvalho de Melo if (previous_x_recv == 0) { 198cc0a910bSArnaldo Carvalho de Melo DCCP_BUG("stored value of X_recv is zero"); 199cc0a910bSArnaldo Carvalho de Melo return ~0; 200cc0a910bSArnaldo Carvalho de Melo } 201cc0a910bSArnaldo Carvalho de Melo x_recv = previous_x_recv; 202cc0a910bSArnaldo Carvalho de Melo } 203cc0a910bSArnaldo Carvalho de Melo 204cc0a910bSArnaldo Carvalho de Melo fval = scaled_div(s, rtt); 205cc0a910bSArnaldo Carvalho de Melo fval = scaled_div32(fval, x_recv); 206cc0a910bSArnaldo Carvalho de Melo p = tfrc_calc_x_reverse_lookup(fval); 207cc0a910bSArnaldo Carvalho de Melo 208cc0a910bSArnaldo Carvalho de Melo dccp_pr_debug("%s(%p), receive rate=%u bytes/s, implied " 209cc0a910bSArnaldo Carvalho de Melo "loss rate=%u\n", dccp_role(sk), sk, x_recv, p); 210cc0a910bSArnaldo Carvalho de Melo 211cc0a910bSArnaldo Carvalho de Melo if (p == 0) 212cc0a910bSArnaldo Carvalho de Melo return ~0; 213cc0a910bSArnaldo Carvalho de Melo else 214cc0a910bSArnaldo Carvalho de Melo return 1000000 / p; 215cc0a910bSArnaldo Carvalho de Melo } 216cc0a910bSArnaldo Carvalho de Melo 217*cc4d6a3aSArnaldo Carvalho de Melo void dccp_li_update_li(struct sock *sk, 218cc0a910bSArnaldo Carvalho de Melo struct list_head *li_hist_list, 219cc0a910bSArnaldo Carvalho de Melo struct list_head *hist_list, 220cc0a910bSArnaldo Carvalho de Melo struct timeval *last_feedback, u16 s, u32 bytes_recv, 221cc0a910bSArnaldo Carvalho de Melo u32 previous_x_recv, u64 seq_loss, u8 win_loss) 222cc0a910bSArnaldo Carvalho de Melo { 223cc0a910bSArnaldo Carvalho de Melo struct dccp_li_hist_entry *head; 224cc0a910bSArnaldo Carvalho de Melo u64 seq_temp; 225cc0a910bSArnaldo Carvalho de Melo 226cc0a910bSArnaldo Carvalho de Melo if (list_empty(li_hist_list)) { 227*cc4d6a3aSArnaldo Carvalho de Melo if (!dccp_li_hist_interval_new(li_hist_list, seq_loss, 228*cc4d6a3aSArnaldo Carvalho de Melo win_loss)) 229cc0a910bSArnaldo Carvalho de Melo return; 230cc0a910bSArnaldo Carvalho de Melo 231cc0a910bSArnaldo Carvalho de Melo head = list_entry(li_hist_list->next, struct dccp_li_hist_entry, 232cc0a910bSArnaldo Carvalho de Melo dccplih_node); 233cc0a910bSArnaldo Carvalho de Melo head->dccplih_interval = dccp_li_calc_first_li(sk, hist_list, 234cc0a910bSArnaldo Carvalho de Melo last_feedback, 235cc0a910bSArnaldo Carvalho de Melo s, bytes_recv, 236cc0a910bSArnaldo Carvalho de Melo previous_x_recv); 237cc0a910bSArnaldo Carvalho de Melo } else { 238cc0a910bSArnaldo Carvalho de Melo struct dccp_li_hist_entry *entry; 239cc0a910bSArnaldo Carvalho de Melo struct list_head *tail; 240cc0a910bSArnaldo Carvalho de Melo 241cc0a910bSArnaldo Carvalho de Melo head = list_entry(li_hist_list->next, struct dccp_li_hist_entry, 242cc0a910bSArnaldo Carvalho de Melo dccplih_node); 243cc0a910bSArnaldo Carvalho de Melo /* FIXME win count check removed as was wrong */ 244cc0a910bSArnaldo Carvalho de Melo /* should make this check with receive history */ 245cc0a910bSArnaldo Carvalho de Melo /* and compare there as per section 10.2 of RFC4342 */ 246cc0a910bSArnaldo Carvalho de Melo 247cc0a910bSArnaldo Carvalho de Melo /* new loss event detected */ 248cc0a910bSArnaldo Carvalho de Melo /* calculate last interval length */ 249cc0a910bSArnaldo Carvalho de Melo seq_temp = dccp_delta_seqno(head->dccplih_seqno, seq_loss); 250*cc4d6a3aSArnaldo Carvalho de Melo entry = dccp_li_hist_entry_new(GFP_ATOMIC); 251cc0a910bSArnaldo Carvalho de Melo 252cc0a910bSArnaldo Carvalho de Melo if (entry == NULL) { 253cc0a910bSArnaldo Carvalho de Melo DCCP_BUG("out of memory - can not allocate entry"); 254cc0a910bSArnaldo Carvalho de Melo return; 255cc0a910bSArnaldo Carvalho de Melo } 256cc0a910bSArnaldo Carvalho de Melo 257cc0a910bSArnaldo Carvalho de Melo list_add(&entry->dccplih_node, li_hist_list); 258cc0a910bSArnaldo Carvalho de Melo 259cc0a910bSArnaldo Carvalho de Melo tail = li_hist_list->prev; 260cc0a910bSArnaldo Carvalho de Melo list_del(tail); 261*cc4d6a3aSArnaldo Carvalho de Melo kmem_cache_free(dccp_li_cachep, tail); 262cc0a910bSArnaldo Carvalho de Melo 263cc0a910bSArnaldo Carvalho de Melo /* Create the newest interval */ 264cc0a910bSArnaldo Carvalho de Melo entry->dccplih_seqno = seq_loss; 265cc0a910bSArnaldo Carvalho de Melo entry->dccplih_interval = seq_temp; 266cc0a910bSArnaldo Carvalho de Melo entry->dccplih_win_count = win_loss; 267cc0a910bSArnaldo Carvalho de Melo } 268cc0a910bSArnaldo Carvalho de Melo } 269cc0a910bSArnaldo Carvalho de Melo 270cc0a910bSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_li_update_li); 271*cc4d6a3aSArnaldo Carvalho de Melo 272*cc4d6a3aSArnaldo Carvalho de Melo static __init int dccp_li_init(void) 273*cc4d6a3aSArnaldo Carvalho de Melo { 274*cc4d6a3aSArnaldo Carvalho de Melo dccp_li_cachep = kmem_cache_create("dccp_li_hist", 275*cc4d6a3aSArnaldo Carvalho de Melo sizeof(struct dccp_li_hist_entry), 276*cc4d6a3aSArnaldo Carvalho de Melo 0, SLAB_HWCACHE_ALIGN, NULL, NULL); 277*cc4d6a3aSArnaldo Carvalho de Melo return dccp_li_cachep == NULL ? -ENOBUFS : 0; 278*cc4d6a3aSArnaldo Carvalho de Melo } 279*cc4d6a3aSArnaldo Carvalho de Melo 280*cc4d6a3aSArnaldo Carvalho de Melo static __exit void dccp_li_exit(void) 281*cc4d6a3aSArnaldo Carvalho de Melo { 282*cc4d6a3aSArnaldo Carvalho de Melo kmem_cache_destroy(dccp_li_cachep); 283*cc4d6a3aSArnaldo Carvalho de Melo } 284*cc4d6a3aSArnaldo Carvalho de Melo 285*cc4d6a3aSArnaldo Carvalho de Melo module_init(dccp_li_init); 286*cc4d6a3aSArnaldo Carvalho de Melo module_exit(dccp_li_exit); 287