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 21ae6706f0SArnaldo Carvalho de Melo struct dccp_li_hist *dccp_li_hist_new(const char *name) 22ae6706f0SArnaldo Carvalho de Melo { 23ae6706f0SArnaldo Carvalho de Melo struct dccp_li_hist *hist = kmalloc(sizeof(*hist), GFP_ATOMIC); 24ae6706f0SArnaldo Carvalho de Melo static const char dccp_li_hist_mask[] = "li_hist_%s"; 25ae6706f0SArnaldo Carvalho de Melo char *slab_name; 26ae6706f0SArnaldo Carvalho de Melo 27ae6706f0SArnaldo Carvalho de Melo if (hist == NULL) 28ae6706f0SArnaldo Carvalho de Melo goto out; 29ae6706f0SArnaldo Carvalho de Melo 30ae6706f0SArnaldo Carvalho de Melo slab_name = kmalloc(strlen(name) + sizeof(dccp_li_hist_mask) - 1, 31ae6706f0SArnaldo Carvalho de Melo GFP_ATOMIC); 32ae6706f0SArnaldo Carvalho de Melo if (slab_name == NULL) 33ae6706f0SArnaldo Carvalho de Melo goto out_free_hist; 34ae6706f0SArnaldo Carvalho de Melo 35ae6706f0SArnaldo Carvalho de Melo sprintf(slab_name, dccp_li_hist_mask, name); 36ae6706f0SArnaldo Carvalho de Melo hist->dccplih_slab = kmem_cache_create(slab_name, 37ae6706f0SArnaldo Carvalho de Melo sizeof(struct dccp_li_hist_entry), 38ae6706f0SArnaldo Carvalho de Melo 0, SLAB_HWCACHE_ALIGN, 39ae6706f0SArnaldo Carvalho de Melo NULL, NULL); 40ae6706f0SArnaldo Carvalho de Melo if (hist->dccplih_slab == NULL) 41ae6706f0SArnaldo Carvalho de Melo goto out_free_slab_name; 42ae6706f0SArnaldo Carvalho de Melo out: 43ae6706f0SArnaldo Carvalho de Melo return hist; 44ae6706f0SArnaldo Carvalho de Melo out_free_slab_name: 45ae6706f0SArnaldo Carvalho de Melo kfree(slab_name); 46ae6706f0SArnaldo Carvalho de Melo out_free_hist: 47ae6706f0SArnaldo Carvalho de Melo kfree(hist); 48ae6706f0SArnaldo Carvalho de Melo hist = NULL; 49ae6706f0SArnaldo Carvalho de Melo goto out; 50ae6706f0SArnaldo Carvalho de Melo } 51ae6706f0SArnaldo Carvalho de Melo 52ae6706f0SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_li_hist_new); 53ae6706f0SArnaldo Carvalho de Melo 54ae6706f0SArnaldo Carvalho de Melo void dccp_li_hist_delete(struct dccp_li_hist *hist) 55ae6706f0SArnaldo Carvalho de Melo { 56ae6706f0SArnaldo Carvalho de Melo const char* name = kmem_cache_name(hist->dccplih_slab); 57ae6706f0SArnaldo Carvalho de Melo 58ae6706f0SArnaldo Carvalho de Melo kmem_cache_destroy(hist->dccplih_slab); 59ae6706f0SArnaldo Carvalho de Melo kfree(name); 60ae6706f0SArnaldo Carvalho de Melo kfree(hist); 61ae6706f0SArnaldo Carvalho de Melo } 62ae6706f0SArnaldo Carvalho de Melo 63ae6706f0SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_li_hist_delete); 64ae6706f0SArnaldo Carvalho de Melo 65ae6706f0SArnaldo Carvalho de Melo void dccp_li_hist_purge(struct dccp_li_hist *hist, struct list_head *list) 66ae6706f0SArnaldo Carvalho de Melo { 67ae6706f0SArnaldo Carvalho de Melo struct dccp_li_hist_entry *entry, *next; 68ae6706f0SArnaldo Carvalho de Melo 69ae6706f0SArnaldo Carvalho de Melo list_for_each_entry_safe(entry, next, list, dccplih_node) { 70ae6706f0SArnaldo Carvalho de Melo list_del_init(&entry->dccplih_node); 71ae6706f0SArnaldo Carvalho de Melo kmem_cache_free(hist->dccplih_slab, entry); 72ae6706f0SArnaldo Carvalho de Melo } 73ae6706f0SArnaldo Carvalho de Melo } 74ae6706f0SArnaldo Carvalho de Melo 75ae6706f0SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_li_hist_purge); 76ae6706f0SArnaldo Carvalho de Melo 77ae6706f0SArnaldo Carvalho de Melo /* Weights used to calculate loss event rate */ 78ae6706f0SArnaldo Carvalho de Melo /* 79ae6706f0SArnaldo Carvalho de Melo * These are integers as per section 8 of RFC3448. We can then divide by 4 * 80ae6706f0SArnaldo Carvalho de Melo * when we use it. 81ae6706f0SArnaldo Carvalho de Melo */ 82ae6706f0SArnaldo Carvalho de Melo static const int dccp_li_hist_w[DCCP_LI_HIST_IVAL_F_LENGTH] = { 83ae6706f0SArnaldo Carvalho de Melo 4, 4, 4, 4, 3, 2, 1, 1, 84ae6706f0SArnaldo Carvalho de Melo }; 85ae6706f0SArnaldo Carvalho de Melo 86ae6706f0SArnaldo Carvalho de Melo u32 dccp_li_hist_calc_i_mean(struct list_head *list) 87ae6706f0SArnaldo Carvalho de Melo { 88ae6706f0SArnaldo Carvalho de Melo struct dccp_li_hist_entry *li_entry, *li_next; 89ae6706f0SArnaldo Carvalho de Melo int i = 0; 90ae6706f0SArnaldo Carvalho de Melo u32 i_tot; 91ae6706f0SArnaldo Carvalho de Melo u32 i_tot0 = 0; 92ae6706f0SArnaldo Carvalho de Melo u32 i_tot1 = 0; 93ae6706f0SArnaldo Carvalho de Melo u32 w_tot = 0; 94ae6706f0SArnaldo Carvalho de Melo 95ae6706f0SArnaldo Carvalho de Melo list_for_each_entry_safe(li_entry, li_next, list, dccplih_node) { 96551dc5f7SIan McDonald if (li_entry->dccplih_interval != ~0U) { 97ae6706f0SArnaldo Carvalho de Melo i_tot0 += li_entry->dccplih_interval * dccp_li_hist_w[i]; 98ae6706f0SArnaldo Carvalho de Melo w_tot += dccp_li_hist_w[i]; 99ae6706f0SArnaldo Carvalho de Melo if (i != 0) 100ae6706f0SArnaldo Carvalho de Melo i_tot1 += li_entry->dccplih_interval * dccp_li_hist_w[i - 1]; 10166a377c5SIan McDonald } 10266a377c5SIan McDonald 103ae6706f0SArnaldo Carvalho de Melo 104ae6706f0SArnaldo Carvalho de Melo if (++i > DCCP_LI_HIST_IVAL_F_LENGTH) 105ae6706f0SArnaldo Carvalho de Melo break; 106ae6706f0SArnaldo Carvalho de Melo } 107ae6706f0SArnaldo Carvalho de Melo 108ae6706f0SArnaldo Carvalho de Melo if (i != DCCP_LI_HIST_IVAL_F_LENGTH) 109ae6706f0SArnaldo Carvalho de Melo return 0; 110ae6706f0SArnaldo Carvalho de Melo 111ae6706f0SArnaldo Carvalho de Melo i_tot = max(i_tot0, i_tot1); 112ae6706f0SArnaldo Carvalho de Melo 11366a377c5SIan McDonald if (!w_tot) { 11459348b19SGerrit Renker DCCP_WARN("w_tot = 0\n"); 11566a377c5SIan McDonald return 1; 11666a377c5SIan McDonald } 117ae6706f0SArnaldo Carvalho de Melo 11866a377c5SIan McDonald return i_tot / w_tot; 119ae6706f0SArnaldo Carvalho de Melo } 120ae6706f0SArnaldo Carvalho de Melo 121ae6706f0SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_li_hist_calc_i_mean); 122ae6706f0SArnaldo Carvalho de Melo 123*8c281780SArnaldo Carvalho de Melo static int dccp_li_hist_interval_new(struct dccp_li_hist *hist, 124*8c281780SArnaldo Carvalho de Melo struct list_head *list, 125*8c281780SArnaldo Carvalho de Melo const u64 seq_loss, const u8 win_loss) 126ae6706f0SArnaldo Carvalho de Melo { 12766a377c5SIan McDonald struct dccp_li_hist_entry *entry; 128ae6706f0SArnaldo Carvalho de Melo int i; 129ae6706f0SArnaldo Carvalho de Melo 13066a377c5SIan McDonald for (i = 0; i < DCCP_LI_HIST_IVAL_F_LENGTH; i++) { 13154e6ecb2SChristoph Lameter entry = dccp_li_hist_entry_new(hist, GFP_ATOMIC); 132ae6706f0SArnaldo Carvalho de Melo if (entry == NULL) { 133ae6706f0SArnaldo Carvalho de Melo dccp_li_hist_purge(hist, list); 13459348b19SGerrit Renker DCCP_BUG("loss interval list entry is NULL"); 13566a377c5SIan McDonald return 0; 136ae6706f0SArnaldo Carvalho de Melo } 13766a377c5SIan McDonald entry->dccplih_interval = ~0; 138ae6706f0SArnaldo Carvalho de Melo list_add(&entry->dccplih_node, list); 139ae6706f0SArnaldo Carvalho de Melo } 140ae6706f0SArnaldo Carvalho de Melo 141ae6706f0SArnaldo Carvalho de Melo entry->dccplih_seqno = seq_loss; 142ae6706f0SArnaldo Carvalho de Melo entry->dccplih_win_count = win_loss; 14366a377c5SIan McDonald return 1; 144ae6706f0SArnaldo Carvalho de Melo } 145ae6706f0SArnaldo Carvalho de Melo 146cc0a910bSArnaldo Carvalho de Melo /* calculate first loss interval 147cc0a910bSArnaldo Carvalho de Melo * 148cc0a910bSArnaldo Carvalho de Melo * returns estimated loss interval in usecs */ 149cc0a910bSArnaldo Carvalho de Melo static u32 dccp_li_calc_first_li(struct sock *sk, 150cc0a910bSArnaldo Carvalho de Melo struct list_head *hist_list, 151cc0a910bSArnaldo Carvalho de Melo struct timeval *last_feedback, 152cc0a910bSArnaldo Carvalho de Melo u16 s, u32 bytes_recv, 153cc0a910bSArnaldo Carvalho de Melo u32 previous_x_recv) 154cc0a910bSArnaldo Carvalho de Melo { 155cc0a910bSArnaldo Carvalho de Melo struct dccp_rx_hist_entry *entry, *next, *tail = NULL; 156cc0a910bSArnaldo Carvalho de Melo u32 x_recv, p; 157cc0a910bSArnaldo Carvalho de Melo suseconds_t rtt, delta; 158cc0a910bSArnaldo Carvalho de Melo struct timeval tstamp = { 0, 0 }; 159cc0a910bSArnaldo Carvalho de Melo int interval = 0; 160cc0a910bSArnaldo Carvalho de Melo int win_count = 0; 161cc0a910bSArnaldo Carvalho de Melo int step = 0; 162cc0a910bSArnaldo Carvalho de Melo u64 fval; 163cc0a910bSArnaldo Carvalho de Melo 164cc0a910bSArnaldo Carvalho de Melo list_for_each_entry_safe(entry, next, hist_list, dccphrx_node) { 165cc0a910bSArnaldo Carvalho de Melo if (dccp_rx_hist_entry_data_packet(entry)) { 166cc0a910bSArnaldo Carvalho de Melo tail = entry; 167cc0a910bSArnaldo Carvalho de Melo 168cc0a910bSArnaldo Carvalho de Melo switch (step) { 169cc0a910bSArnaldo Carvalho de Melo case 0: 170cc0a910bSArnaldo Carvalho de Melo tstamp = entry->dccphrx_tstamp; 171cc0a910bSArnaldo Carvalho de Melo win_count = entry->dccphrx_ccval; 172cc0a910bSArnaldo Carvalho de Melo step = 1; 173cc0a910bSArnaldo Carvalho de Melo break; 174cc0a910bSArnaldo Carvalho de Melo case 1: 175cc0a910bSArnaldo Carvalho de Melo interval = win_count - entry->dccphrx_ccval; 176cc0a910bSArnaldo Carvalho de Melo if (interval < 0) 177cc0a910bSArnaldo Carvalho de Melo interval += TFRC_WIN_COUNT_LIMIT; 178cc0a910bSArnaldo Carvalho de Melo if (interval > 4) 179cc0a910bSArnaldo Carvalho de Melo goto found; 180cc0a910bSArnaldo Carvalho de Melo break; 181cc0a910bSArnaldo Carvalho de Melo } 182cc0a910bSArnaldo Carvalho de Melo } 183cc0a910bSArnaldo Carvalho de Melo } 184cc0a910bSArnaldo Carvalho de Melo 185cc0a910bSArnaldo Carvalho de Melo if (unlikely(step == 0)) { 186cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("%s(%p), packet history has no data packets!\n", 187cc0a910bSArnaldo Carvalho de Melo dccp_role(sk), sk); 188cc0a910bSArnaldo Carvalho de Melo return ~0; 189cc0a910bSArnaldo Carvalho de Melo } 190cc0a910bSArnaldo Carvalho de Melo 191cc0a910bSArnaldo Carvalho de Melo if (unlikely(interval == 0)) { 192cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("%s(%p), Could not find a win_count interval > 0." 193cc0a910bSArnaldo Carvalho de Melo "Defaulting to 1\n", dccp_role(sk), sk); 194cc0a910bSArnaldo Carvalho de Melo interval = 1; 195cc0a910bSArnaldo Carvalho de Melo } 196cc0a910bSArnaldo Carvalho de Melo found: 197cc0a910bSArnaldo Carvalho de Melo if (!tail) { 198cc0a910bSArnaldo Carvalho de Melo DCCP_CRIT("tail is null\n"); 199cc0a910bSArnaldo Carvalho de Melo return ~0; 200cc0a910bSArnaldo Carvalho de Melo } 201cc0a910bSArnaldo Carvalho de Melo 202cc0a910bSArnaldo Carvalho de Melo delta = timeval_delta(&tstamp, &tail->dccphrx_tstamp); 203cc0a910bSArnaldo Carvalho de Melo DCCP_BUG_ON(delta < 0); 204cc0a910bSArnaldo Carvalho de Melo 205cc0a910bSArnaldo Carvalho de Melo rtt = delta * 4 / interval; 206cc0a910bSArnaldo Carvalho de Melo dccp_pr_debug("%s(%p), approximated RTT to %dus\n", 207cc0a910bSArnaldo Carvalho de Melo dccp_role(sk), sk, (int)rtt); 208cc0a910bSArnaldo Carvalho de Melo 209cc0a910bSArnaldo Carvalho de Melo /* 210cc0a910bSArnaldo Carvalho de Melo * Determine the length of the first loss interval via inverse lookup. 211cc0a910bSArnaldo Carvalho de Melo * Assume that X_recv can be computed by the throughput equation 212cc0a910bSArnaldo Carvalho de Melo * s 213cc0a910bSArnaldo Carvalho de Melo * X_recv = -------- 214cc0a910bSArnaldo Carvalho de Melo * R * fval 215cc0a910bSArnaldo Carvalho de Melo * Find some p such that f(p) = fval; return 1/p [RFC 3448, 6.3.1]. 216cc0a910bSArnaldo Carvalho de Melo */ 217cc0a910bSArnaldo Carvalho de Melo if (rtt == 0) { /* would result in divide-by-zero */ 218cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("RTT==0\n"); 219cc0a910bSArnaldo Carvalho de Melo return ~0; 220cc0a910bSArnaldo Carvalho de Melo } 221cc0a910bSArnaldo Carvalho de Melo 222cc0a910bSArnaldo Carvalho de Melo dccp_timestamp(sk, &tstamp); 223cc0a910bSArnaldo Carvalho de Melo delta = timeval_delta(&tstamp, last_feedback); 224cc0a910bSArnaldo Carvalho de Melo DCCP_BUG_ON(delta <= 0); 225cc0a910bSArnaldo Carvalho de Melo 226cc0a910bSArnaldo Carvalho de Melo x_recv = scaled_div32(bytes_recv, delta); 227cc0a910bSArnaldo Carvalho de Melo if (x_recv == 0) { /* would also trigger divide-by-zero */ 228cc0a910bSArnaldo Carvalho de Melo DCCP_WARN("X_recv==0\n"); 229cc0a910bSArnaldo Carvalho de Melo if (previous_x_recv == 0) { 230cc0a910bSArnaldo Carvalho de Melo DCCP_BUG("stored value of X_recv is zero"); 231cc0a910bSArnaldo Carvalho de Melo return ~0; 232cc0a910bSArnaldo Carvalho de Melo } 233cc0a910bSArnaldo Carvalho de Melo x_recv = previous_x_recv; 234cc0a910bSArnaldo Carvalho de Melo } 235cc0a910bSArnaldo Carvalho de Melo 236cc0a910bSArnaldo Carvalho de Melo fval = scaled_div(s, rtt); 237cc0a910bSArnaldo Carvalho de Melo fval = scaled_div32(fval, x_recv); 238cc0a910bSArnaldo Carvalho de Melo p = tfrc_calc_x_reverse_lookup(fval); 239cc0a910bSArnaldo Carvalho de Melo 240cc0a910bSArnaldo Carvalho de Melo dccp_pr_debug("%s(%p), receive rate=%u bytes/s, implied " 241cc0a910bSArnaldo Carvalho de Melo "loss rate=%u\n", dccp_role(sk), sk, x_recv, p); 242cc0a910bSArnaldo Carvalho de Melo 243cc0a910bSArnaldo Carvalho de Melo if (p == 0) 244cc0a910bSArnaldo Carvalho de Melo return ~0; 245cc0a910bSArnaldo Carvalho de Melo else 246cc0a910bSArnaldo Carvalho de Melo return 1000000 / p; 247cc0a910bSArnaldo Carvalho de Melo } 248cc0a910bSArnaldo Carvalho de Melo 249cc0a910bSArnaldo Carvalho de Melo void dccp_li_update_li(struct sock *sk, struct dccp_li_hist *li_hist, 250cc0a910bSArnaldo Carvalho de Melo struct list_head *li_hist_list, 251cc0a910bSArnaldo Carvalho de Melo struct list_head *hist_list, 252cc0a910bSArnaldo Carvalho de Melo struct timeval *last_feedback, u16 s, u32 bytes_recv, 253cc0a910bSArnaldo Carvalho de Melo u32 previous_x_recv, u64 seq_loss, u8 win_loss) 254cc0a910bSArnaldo Carvalho de Melo { 255cc0a910bSArnaldo Carvalho de Melo struct dccp_li_hist_entry *head; 256cc0a910bSArnaldo Carvalho de Melo u64 seq_temp; 257cc0a910bSArnaldo Carvalho de Melo 258cc0a910bSArnaldo Carvalho de Melo if (list_empty(li_hist_list)) { 259cc0a910bSArnaldo Carvalho de Melo if (!dccp_li_hist_interval_new(li_hist, li_hist_list, 260cc0a910bSArnaldo Carvalho de Melo seq_loss, win_loss)) 261cc0a910bSArnaldo Carvalho de Melo return; 262cc0a910bSArnaldo Carvalho de Melo 263cc0a910bSArnaldo Carvalho de Melo head = list_entry(li_hist_list->next, struct dccp_li_hist_entry, 264cc0a910bSArnaldo Carvalho de Melo dccplih_node); 265cc0a910bSArnaldo Carvalho de Melo head->dccplih_interval = dccp_li_calc_first_li(sk, hist_list, 266cc0a910bSArnaldo Carvalho de Melo last_feedback, 267cc0a910bSArnaldo Carvalho de Melo s, bytes_recv, 268cc0a910bSArnaldo Carvalho de Melo previous_x_recv); 269cc0a910bSArnaldo Carvalho de Melo } else { 270cc0a910bSArnaldo Carvalho de Melo struct dccp_li_hist_entry *entry; 271cc0a910bSArnaldo Carvalho de Melo struct list_head *tail; 272cc0a910bSArnaldo Carvalho de Melo 273cc0a910bSArnaldo Carvalho de Melo head = list_entry(li_hist_list->next, struct dccp_li_hist_entry, 274cc0a910bSArnaldo Carvalho de Melo dccplih_node); 275cc0a910bSArnaldo Carvalho de Melo /* FIXME win count check removed as was wrong */ 276cc0a910bSArnaldo Carvalho de Melo /* should make this check with receive history */ 277cc0a910bSArnaldo Carvalho de Melo /* and compare there as per section 10.2 of RFC4342 */ 278cc0a910bSArnaldo Carvalho de Melo 279cc0a910bSArnaldo Carvalho de Melo /* new loss event detected */ 280cc0a910bSArnaldo Carvalho de Melo /* calculate last interval length */ 281cc0a910bSArnaldo Carvalho de Melo seq_temp = dccp_delta_seqno(head->dccplih_seqno, seq_loss); 282cc0a910bSArnaldo Carvalho de Melo entry = dccp_li_hist_entry_new(li_hist, GFP_ATOMIC); 283cc0a910bSArnaldo Carvalho de Melo 284cc0a910bSArnaldo Carvalho de Melo if (entry == NULL) { 285cc0a910bSArnaldo Carvalho de Melo DCCP_BUG("out of memory - can not allocate entry"); 286cc0a910bSArnaldo Carvalho de Melo return; 287cc0a910bSArnaldo Carvalho de Melo } 288cc0a910bSArnaldo Carvalho de Melo 289cc0a910bSArnaldo Carvalho de Melo list_add(&entry->dccplih_node, li_hist_list); 290cc0a910bSArnaldo Carvalho de Melo 291cc0a910bSArnaldo Carvalho de Melo tail = li_hist_list->prev; 292cc0a910bSArnaldo Carvalho de Melo list_del(tail); 293cc0a910bSArnaldo Carvalho de Melo kmem_cache_free(li_hist->dccplih_slab, tail); 294cc0a910bSArnaldo Carvalho de Melo 295cc0a910bSArnaldo Carvalho de Melo /* Create the newest interval */ 296cc0a910bSArnaldo Carvalho de Melo entry->dccplih_seqno = seq_loss; 297cc0a910bSArnaldo Carvalho de Melo entry->dccplih_interval = seq_temp; 298cc0a910bSArnaldo Carvalho de Melo entry->dccplih_win_count = win_loss; 299cc0a910bSArnaldo Carvalho de Melo } 300cc0a910bSArnaldo Carvalho de Melo } 301cc0a910bSArnaldo Carvalho de Melo 302cc0a910bSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_li_update_li); 303