18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2007 The University of Aberdeen, Scotland, UK 48c2ecf20Sopenharmony_ci * Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * An implementation of the DCCP protocol 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This code has been developed by the University of Waikato WAND 98c2ecf20Sopenharmony_ci * research group. For further information please see https://www.wand.net.nz/ 108c2ecf20Sopenharmony_ci * or e-mail Ian McDonald - ian.mcdonald@jandi.co.nz 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This code also uses code from Lulea University, rereleased as GPL by its 138c2ecf20Sopenharmony_ci * authors: 148c2ecf20Sopenharmony_ci * Copyright (c) 2003 Nils-Erik Mattsson, Joacim Haggmark, Magnus Erixzon 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Changes to meet Linux coding standards, to make it meet latest ccid3 draft 178c2ecf20Sopenharmony_ci * and to make it work as a loadable module in the DCCP stack written by 188c2ecf20Sopenharmony_ci * Arnaldo Carvalho de Melo <acme@conectiva.com.br>. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br> 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/string.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include "packet_history.h" 268c2ecf20Sopenharmony_ci#include "../../dccp.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * Transmitter History Routines 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistatic struct kmem_cache *tfrc_tx_hist_slab; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciint __init tfrc_tx_packet_history_init(void) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci tfrc_tx_hist_slab = kmem_cache_create("tfrc_tx_hist", 368c2ecf20Sopenharmony_ci sizeof(struct tfrc_tx_hist_entry), 378c2ecf20Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, NULL); 388c2ecf20Sopenharmony_ci return tfrc_tx_hist_slab == NULL ? -ENOBUFS : 0; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_civoid tfrc_tx_packet_history_exit(void) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci if (tfrc_tx_hist_slab != NULL) { 448c2ecf20Sopenharmony_ci kmem_cache_destroy(tfrc_tx_hist_slab); 458c2ecf20Sopenharmony_ci tfrc_tx_hist_slab = NULL; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ciint tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct tfrc_tx_hist_entry *entry = kmem_cache_alloc(tfrc_tx_hist_slab, gfp_any()); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (entry == NULL) 548c2ecf20Sopenharmony_ci return -ENOBUFS; 558c2ecf20Sopenharmony_ci entry->seqno = seqno; 568c2ecf20Sopenharmony_ci entry->stamp = ktime_get_real(); 578c2ecf20Sopenharmony_ci entry->next = *headp; 588c2ecf20Sopenharmony_ci *headp = entry; 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_civoid tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct tfrc_tx_hist_entry *head = *headp; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci while (head != NULL) { 678c2ecf20Sopenharmony_ci struct tfrc_tx_hist_entry *next = head->next; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci kmem_cache_free(tfrc_tx_hist_slab, head); 708c2ecf20Sopenharmony_ci head = next; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci *headp = NULL; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * Receiver History Routines 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_cistatic struct kmem_cache *tfrc_rx_hist_slab; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ciint __init tfrc_rx_packet_history_init(void) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci tfrc_rx_hist_slab = kmem_cache_create("tfrc_rxh_cache", 848c2ecf20Sopenharmony_ci sizeof(struct tfrc_rx_hist_entry), 858c2ecf20Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, NULL); 868c2ecf20Sopenharmony_ci return tfrc_rx_hist_slab == NULL ? -ENOBUFS : 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_civoid tfrc_rx_packet_history_exit(void) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci if (tfrc_rx_hist_slab != NULL) { 928c2ecf20Sopenharmony_ci kmem_cache_destroy(tfrc_rx_hist_slab); 938c2ecf20Sopenharmony_ci tfrc_rx_hist_slab = NULL; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic inline void tfrc_rx_hist_entry_from_skb(struct tfrc_rx_hist_entry *entry, 988c2ecf20Sopenharmony_ci const struct sk_buff *skb, 998c2ecf20Sopenharmony_ci const u64 ndp) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci const struct dccp_hdr *dh = dccp_hdr(skb); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci entry->tfrchrx_seqno = DCCP_SKB_CB(skb)->dccpd_seq; 1048c2ecf20Sopenharmony_ci entry->tfrchrx_ccval = dh->dccph_ccval; 1058c2ecf20Sopenharmony_ci entry->tfrchrx_type = dh->dccph_type; 1068c2ecf20Sopenharmony_ci entry->tfrchrx_ndp = ndp; 1078c2ecf20Sopenharmony_ci entry->tfrchrx_tstamp = ktime_get_real(); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_civoid tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, 1118c2ecf20Sopenharmony_ci const struct sk_buff *skb, 1128c2ecf20Sopenharmony_ci const u64 ndp) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct tfrc_rx_hist_entry *entry = tfrc_rx_hist_last_rcv(h); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci tfrc_rx_hist_entry_from_skb(entry, skb, ndp); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* has the packet contained in skb been seen before? */ 1208c2ecf20Sopenharmony_ciint tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci const u64 seq = DCCP_SKB_CB(skb)->dccpd_seq; 1238c2ecf20Sopenharmony_ci int i; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (dccp_delta_seqno(tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno, seq) <= 0) 1268c2ecf20Sopenharmony_ci return 1; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci for (i = 1; i <= h->loss_count; i++) 1298c2ecf20Sopenharmony_ci if (tfrc_rx_hist_entry(h, i)->tfrchrx_seqno == seq) 1308c2ecf20Sopenharmony_ci return 1; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void tfrc_rx_hist_swap(struct tfrc_rx_hist *h, const u8 a, const u8 b) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci const u8 idx_a = tfrc_rx_hist_index(h, a), 1388c2ecf20Sopenharmony_ci idx_b = tfrc_rx_hist_index(h, b); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci swap(h->ring[idx_a], h->ring[idx_b]); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* 1448c2ecf20Sopenharmony_ci * Private helper functions for loss detection. 1458c2ecf20Sopenharmony_ci * 1468c2ecf20Sopenharmony_ci * In the descriptions, `Si' refers to the sequence number of entry number i, 1478c2ecf20Sopenharmony_ci * whose NDP count is `Ni' (lower case is used for variables). 1488c2ecf20Sopenharmony_ci * Note: All __xxx_loss functions expect that a test against duplicates has been 1498c2ecf20Sopenharmony_ci * performed already: the seqno of the skb must not be less than the seqno 1508c2ecf20Sopenharmony_ci * of loss_prev; and it must not equal that of any valid history entry. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_cistatic void __do_track_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u64 n1) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno, 1558c2ecf20Sopenharmony_ci s1 = DCCP_SKB_CB(skb)->dccpd_seq; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!dccp_loss_free(s0, s1, n1)) { /* gap between S0 and S1 */ 1588c2ecf20Sopenharmony_ci h->loss_count = 1; 1598c2ecf20Sopenharmony_ci tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n1); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void __one_after_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u32 n2) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno, 1668c2ecf20Sopenharmony_ci s1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_seqno, 1678c2ecf20Sopenharmony_ci s2 = DCCP_SKB_CB(skb)->dccpd_seq; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (likely(dccp_delta_seqno(s1, s2) > 0)) { /* S1 < S2 */ 1708c2ecf20Sopenharmony_ci h->loss_count = 2; 1718c2ecf20Sopenharmony_ci tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 2), skb, n2); 1728c2ecf20Sopenharmony_ci return; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* S0 < S2 < S1 */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (dccp_loss_free(s0, s2, n2)) { 1788c2ecf20Sopenharmony_ci u64 n1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_ndp; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (dccp_loss_free(s2, s1, n1)) { 1818c2ecf20Sopenharmony_ci /* hole is filled: S0, S2, and S1 are consecutive */ 1828c2ecf20Sopenharmony_ci h->loss_count = 0; 1838c2ecf20Sopenharmony_ci h->loss_start = tfrc_rx_hist_index(h, 1); 1848c2ecf20Sopenharmony_ci } else 1858c2ecf20Sopenharmony_ci /* gap between S2 and S1: just update loss_prev */ 1868c2ecf20Sopenharmony_ci tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_loss_prev(h), skb, n2); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci } else { /* gap between S0 and S2 */ 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * Reorder history to insert S2 between S0 and S1 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci tfrc_rx_hist_swap(h, 0, 3); 1938c2ecf20Sopenharmony_ci h->loss_start = tfrc_rx_hist_index(h, 3); 1948c2ecf20Sopenharmony_ci tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n2); 1958c2ecf20Sopenharmony_ci h->loss_count = 2; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* return 1 if a new loss event has been identified */ 2008c2ecf20Sopenharmony_cistatic int __two_after_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u32 n3) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno, 2038c2ecf20Sopenharmony_ci s1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_seqno, 2048c2ecf20Sopenharmony_ci s2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_seqno, 2058c2ecf20Sopenharmony_ci s3 = DCCP_SKB_CB(skb)->dccpd_seq; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (likely(dccp_delta_seqno(s2, s3) > 0)) { /* S2 < S3 */ 2088c2ecf20Sopenharmony_ci h->loss_count = 3; 2098c2ecf20Sopenharmony_ci tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 3), skb, n3); 2108c2ecf20Sopenharmony_ci return 1; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* S3 < S2 */ 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (dccp_delta_seqno(s1, s3) > 0) { /* S1 < S3 < S2 */ 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * Reorder history to insert S3 between S1 and S2 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci tfrc_rx_hist_swap(h, 2, 3); 2208c2ecf20Sopenharmony_ci tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 2), skb, n3); 2218c2ecf20Sopenharmony_ci h->loss_count = 3; 2228c2ecf20Sopenharmony_ci return 1; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* S0 < S3 < S1 */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (dccp_loss_free(s0, s3, n3)) { 2288c2ecf20Sopenharmony_ci u64 n1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_ndp; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (dccp_loss_free(s3, s1, n1)) { 2318c2ecf20Sopenharmony_ci /* hole between S0 and S1 filled by S3 */ 2328c2ecf20Sopenharmony_ci u64 n2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_ndp; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (dccp_loss_free(s1, s2, n2)) { 2358c2ecf20Sopenharmony_ci /* entire hole filled by S0, S3, S1, S2 */ 2368c2ecf20Sopenharmony_ci h->loss_start = tfrc_rx_hist_index(h, 2); 2378c2ecf20Sopenharmony_ci h->loss_count = 0; 2388c2ecf20Sopenharmony_ci } else { 2398c2ecf20Sopenharmony_ci /* gap remains between S1 and S2 */ 2408c2ecf20Sopenharmony_ci h->loss_start = tfrc_rx_hist_index(h, 1); 2418c2ecf20Sopenharmony_ci h->loss_count = 1; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci } else /* gap exists between S3 and S1, loss_count stays at 2 */ 2458c2ecf20Sopenharmony_ci tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_loss_prev(h), skb, n3); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* 2518c2ecf20Sopenharmony_ci * The remaining case: S0 < S3 < S1 < S2; gap between S0 and S3 2528c2ecf20Sopenharmony_ci * Reorder history to insert S3 between S0 and S1. 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci tfrc_rx_hist_swap(h, 0, 3); 2558c2ecf20Sopenharmony_ci h->loss_start = tfrc_rx_hist_index(h, 3); 2568c2ecf20Sopenharmony_ci tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n3); 2578c2ecf20Sopenharmony_ci h->loss_count = 3; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 1; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* recycle RX history records to continue loss detection if necessary */ 2638c2ecf20Sopenharmony_cistatic void __three_after_loss(struct tfrc_rx_hist *h) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci /* 2668c2ecf20Sopenharmony_ci * At this stage we know already that there is a gap between S0 and S1 2678c2ecf20Sopenharmony_ci * (since S0 was the highest sequence number received before detecting 2688c2ecf20Sopenharmony_ci * the loss). To recycle the loss record, it is thus only necessary to 2698c2ecf20Sopenharmony_ci * check for other possible gaps between S1/S2 and between S2/S3. 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci u64 s1 = tfrc_rx_hist_entry(h, 1)->tfrchrx_seqno, 2728c2ecf20Sopenharmony_ci s2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_seqno, 2738c2ecf20Sopenharmony_ci s3 = tfrc_rx_hist_entry(h, 3)->tfrchrx_seqno; 2748c2ecf20Sopenharmony_ci u64 n2 = tfrc_rx_hist_entry(h, 2)->tfrchrx_ndp, 2758c2ecf20Sopenharmony_ci n3 = tfrc_rx_hist_entry(h, 3)->tfrchrx_ndp; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (dccp_loss_free(s1, s2, n2)) { 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (dccp_loss_free(s2, s3, n3)) { 2808c2ecf20Sopenharmony_ci /* no gap between S2 and S3: entire hole is filled */ 2818c2ecf20Sopenharmony_ci h->loss_start = tfrc_rx_hist_index(h, 3); 2828c2ecf20Sopenharmony_ci h->loss_count = 0; 2838c2ecf20Sopenharmony_ci } else { 2848c2ecf20Sopenharmony_ci /* gap between S2 and S3 */ 2858c2ecf20Sopenharmony_ci h->loss_start = tfrc_rx_hist_index(h, 2); 2868c2ecf20Sopenharmony_ci h->loss_count = 1; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci } else { /* gap between S1 and S2 */ 2908c2ecf20Sopenharmony_ci h->loss_start = tfrc_rx_hist_index(h, 1); 2918c2ecf20Sopenharmony_ci h->loss_count = 2; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/** 2968c2ecf20Sopenharmony_ci * tfrc_rx_handle_loss - Loss detection and further processing 2978c2ecf20Sopenharmony_ci * @h: The non-empty RX history object 2988c2ecf20Sopenharmony_ci * @lh: Loss Intervals database to update 2998c2ecf20Sopenharmony_ci * @skb: Currently received packet 3008c2ecf20Sopenharmony_ci * @ndp: The NDP count belonging to @skb 3018c2ecf20Sopenharmony_ci * @calc_first_li: Caller-dependent computation of first loss interval in @lh 3028c2ecf20Sopenharmony_ci * @sk: Used by @calc_first_li (see tfrc_lh_interval_add) 3038c2ecf20Sopenharmony_ci * 3048c2ecf20Sopenharmony_ci * Chooses action according to pending loss, updates LI database when a new 3058c2ecf20Sopenharmony_ci * loss was detected, and does required post-processing. Returns 1 when caller 3068c2ecf20Sopenharmony_ci * should send feedback, 0 otherwise. 3078c2ecf20Sopenharmony_ci * Since it also takes care of reordering during loss detection and updates the 3088c2ecf20Sopenharmony_ci * records accordingly, the caller should not perform any more RX history 3098c2ecf20Sopenharmony_ci * operations when loss_count is greater than 0 after calling this function. 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_ciint tfrc_rx_handle_loss(struct tfrc_rx_hist *h, 3128c2ecf20Sopenharmony_ci struct tfrc_loss_hist *lh, 3138c2ecf20Sopenharmony_ci struct sk_buff *skb, const u64 ndp, 3148c2ecf20Sopenharmony_ci u32 (*calc_first_li)(struct sock *), struct sock *sk) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci int is_new_loss = 0; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (h->loss_count == 0) { 3198c2ecf20Sopenharmony_ci __do_track_loss(h, skb, ndp); 3208c2ecf20Sopenharmony_ci } else if (h->loss_count == 1) { 3218c2ecf20Sopenharmony_ci __one_after_loss(h, skb, ndp); 3228c2ecf20Sopenharmony_ci } else if (h->loss_count != 2) { 3238c2ecf20Sopenharmony_ci DCCP_BUG("invalid loss_count %d", h->loss_count); 3248c2ecf20Sopenharmony_ci } else if (__two_after_loss(h, skb, ndp)) { 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * Update Loss Interval database and recycle RX records 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci is_new_loss = tfrc_lh_interval_add(lh, h, calc_first_li, sk); 3298c2ecf20Sopenharmony_ci __three_after_loss(h); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci return is_new_loss; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ciint tfrc_rx_hist_alloc(struct tfrc_rx_hist *h) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci int i; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci for (i = 0; i <= TFRC_NDUPACK; i++) { 3398c2ecf20Sopenharmony_ci h->ring[i] = kmem_cache_alloc(tfrc_rx_hist_slab, GFP_ATOMIC); 3408c2ecf20Sopenharmony_ci if (h->ring[i] == NULL) 3418c2ecf20Sopenharmony_ci goto out_free; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci h->loss_count = h->loss_start = 0; 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ciout_free: 3488c2ecf20Sopenharmony_ci while (i-- != 0) { 3498c2ecf20Sopenharmony_ci kmem_cache_free(tfrc_rx_hist_slab, h->ring[i]); 3508c2ecf20Sopenharmony_ci h->ring[i] = NULL; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci return -ENOBUFS; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_civoid tfrc_rx_hist_purge(struct tfrc_rx_hist *h) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci int i; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci for (i = 0; i <= TFRC_NDUPACK; ++i) 3608c2ecf20Sopenharmony_ci if (h->ring[i] != NULL) { 3618c2ecf20Sopenharmony_ci kmem_cache_free(tfrc_rx_hist_slab, h->ring[i]); 3628c2ecf20Sopenharmony_ci h->ring[i] = NULL; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/** 3678c2ecf20Sopenharmony_ci * tfrc_rx_hist_rtt_last_s - reference entry to compute RTT samples against 3688c2ecf20Sopenharmony_ci * @h: The non-empty RX history object 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_cistatic inline struct tfrc_rx_hist_entry * 3718c2ecf20Sopenharmony_ci tfrc_rx_hist_rtt_last_s(const struct tfrc_rx_hist *h) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci return h->ring[0]; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/** 3778c2ecf20Sopenharmony_ci * tfrc_rx_hist_rtt_prev_s - previously suitable (wrt rtt_last_s) RTT-sampling entry 3788c2ecf20Sopenharmony_ci * @h: The non-empty RX history object 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_cistatic inline struct tfrc_rx_hist_entry * 3818c2ecf20Sopenharmony_ci tfrc_rx_hist_rtt_prev_s(const struct tfrc_rx_hist *h) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci return h->ring[h->rtt_sample_prev]; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/** 3878c2ecf20Sopenharmony_ci * tfrc_rx_hist_sample_rtt - Sample RTT from timestamp / CCVal 3888c2ecf20Sopenharmony_ci * Based on ideas presented in RFC 4342, 8.1. Returns 0 if it was not able 3898c2ecf20Sopenharmony_ci * to compute a sample with given data - calling function should check this. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ciu32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, const struct sk_buff *skb) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci u32 sample = 0, 3948c2ecf20Sopenharmony_ci delta_v = SUB16(dccp_hdr(skb)->dccph_ccval, 3958c2ecf20Sopenharmony_ci tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (delta_v < 1 || delta_v > 4) { /* unsuitable CCVal delta */ 3988c2ecf20Sopenharmony_ci if (h->rtt_sample_prev == 2) { /* previous candidate stored */ 3998c2ecf20Sopenharmony_ci sample = SUB16(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval, 4008c2ecf20Sopenharmony_ci tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); 4018c2ecf20Sopenharmony_ci if (sample) 4028c2ecf20Sopenharmony_ci sample = 4 / sample * 4038c2ecf20Sopenharmony_ci ktime_us_delta(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_tstamp, 4048c2ecf20Sopenharmony_ci tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp); 4058c2ecf20Sopenharmony_ci else /* 4068c2ecf20Sopenharmony_ci * FIXME: This condition is in principle not 4078c2ecf20Sopenharmony_ci * possible but occurs when CCID is used for 4088c2ecf20Sopenharmony_ci * two-way data traffic. I have tried to trace 4098c2ecf20Sopenharmony_ci * it, but the cause does not seem to be here. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci DCCP_BUG("please report to dccp@vger.kernel.org" 4128c2ecf20Sopenharmony_ci " => prev = %u, last = %u", 4138c2ecf20Sopenharmony_ci tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval, 4148c2ecf20Sopenharmony_ci tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); 4158c2ecf20Sopenharmony_ci } else if (delta_v < 1) { 4168c2ecf20Sopenharmony_ci h->rtt_sample_prev = 1; 4178c2ecf20Sopenharmony_ci goto keep_ref_for_next_time; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci } else if (delta_v == 4) /* optimal match */ 4218c2ecf20Sopenharmony_ci sample = ktime_to_us(net_timedelta(tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp)); 4228c2ecf20Sopenharmony_ci else { /* suboptimal match */ 4238c2ecf20Sopenharmony_ci h->rtt_sample_prev = 2; 4248c2ecf20Sopenharmony_ci goto keep_ref_for_next_time; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (unlikely(sample > DCCP_SANE_RTT_MAX)) { 4288c2ecf20Sopenharmony_ci DCCP_WARN("RTT sample %u too large, using max\n", sample); 4298c2ecf20Sopenharmony_ci sample = DCCP_SANE_RTT_MAX; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci h->rtt_sample_prev = 0; /* use current entry as next reference */ 4338c2ecf20Sopenharmony_cikeep_ref_for_next_time: 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return sample; 4368c2ecf20Sopenharmony_ci} 437