2 * net/dccp/ccids/lib/loss_interval.c
4 * Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand.
5 * Copyright (c) 2005-7 Ian McDonald <ian.mcdonald@jandi.co.nz>
6 * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
14 #include <linux/module.h>
16 #include "../../dccp.h"
17 #include "loss_interval.h"
18 #include "packet_history.h"
21 #define DCCP_LI_HIST_IVAL_F_LENGTH 8
23 struct dccp_li_hist_entry
{
24 struct list_head dccplih_node
;
30 static struct kmem_cache
*dccp_li_cachep __read_mostly
;
32 static inline struct dccp_li_hist_entry
*dccp_li_hist_entry_new(const gfp_t prio
)
34 return kmem_cache_alloc(dccp_li_cachep
, prio
);
37 static inline void dccp_li_hist_entry_delete(struct dccp_li_hist_entry
*entry
)
40 kmem_cache_free(dccp_li_cachep
, entry
);
43 void dccp_li_hist_purge(struct list_head
*list
)
45 struct dccp_li_hist_entry
*entry
, *next
;
47 list_for_each_entry_safe(entry
, next
, list
, dccplih_node
) {
48 list_del_init(&entry
->dccplih_node
);
49 kmem_cache_free(dccp_li_cachep
, entry
);
53 EXPORT_SYMBOL_GPL(dccp_li_hist_purge
);
55 /* Weights used to calculate loss event rate */
57 * These are integers as per section 8 of RFC3448. We can then divide by 4 *
60 static const int dccp_li_hist_w
[DCCP_LI_HIST_IVAL_F_LENGTH
] = {
61 4, 4, 4, 4, 3, 2, 1, 1,
64 u32
dccp_li_hist_calc_i_mean(struct list_head
*list
)
66 struct dccp_li_hist_entry
*li_entry
, *li_next
;
73 list_for_each_entry_safe(li_entry
, li_next
, list
, dccplih_node
) {
74 if (li_entry
->dccplih_interval
!= ~0U) {
75 i_tot0
+= li_entry
->dccplih_interval
* dccp_li_hist_w
[i
];
76 w_tot
+= dccp_li_hist_w
[i
];
78 i_tot1
+= li_entry
->dccplih_interval
* dccp_li_hist_w
[i
- 1];
82 if (++i
> DCCP_LI_HIST_IVAL_F_LENGTH
)
86 if (i
!= DCCP_LI_HIST_IVAL_F_LENGTH
)
89 i_tot
= max(i_tot0
, i_tot1
);
92 DCCP_WARN("w_tot = 0\n");
99 EXPORT_SYMBOL_GPL(dccp_li_hist_calc_i_mean
);
101 static int dccp_li_hist_interval_new(struct list_head
*list
,
102 const u64 seq_loss
, const u8 win_loss
)
104 struct dccp_li_hist_entry
*entry
;
107 for (i
= 0; i
< DCCP_LI_HIST_IVAL_F_LENGTH
; i
++) {
108 entry
= dccp_li_hist_entry_new(GFP_ATOMIC
);
110 dccp_li_hist_purge(list
);
111 DCCP_BUG("loss interval list entry is NULL");
114 entry
->dccplih_interval
= ~0;
115 list_add(&entry
->dccplih_node
, list
);
118 entry
->dccplih_seqno
= seq_loss
;
119 entry
->dccplih_win_count
= win_loss
;
123 /* calculate first loss interval
125 * returns estimated loss interval in usecs */
126 static u32
dccp_li_calc_first_li(struct sock
*sk
,
127 struct list_head
*hist_list
,
128 ktime_t last_feedback
,
129 u16 s
, u32 bytes_recv
,
132 struct dccp_rx_hist_entry
*entry
, *next
, *tail
= NULL
;
134 suseconds_t rtt
, delta
;
135 ktime_t tstamp
= ktime_set(0, 0);
141 list_for_each_entry_safe(entry
, next
, hist_list
, dccphrx_node
) {
142 if (dccp_rx_hist_entry_data_packet(entry
)) {
147 tstamp
= entry
->dccphrx_tstamp
;
148 win_count
= entry
->dccphrx_ccval
;
152 interval
= win_count
- entry
->dccphrx_ccval
;
154 interval
+= TFRC_WIN_COUNT_LIMIT
;
162 if (unlikely(step
== 0)) {
163 DCCP_WARN("%s(%p), packet history has no data packets!\n",
168 if (unlikely(interval
== 0)) {
169 DCCP_WARN("%s(%p), Could not find a win_count interval > 0. "
170 "Defaulting to 1\n", dccp_role(sk
), sk
);
175 DCCP_CRIT("tail is null\n");
179 delta
= ktime_us_delta(tstamp
, tail
->dccphrx_tstamp
);
180 DCCP_BUG_ON(delta
< 0);
182 rtt
= delta
* 4 / interval
;
183 dccp_pr_debug("%s(%p), approximated RTT to %dus\n",
184 dccp_role(sk
), sk
, (int)rtt
);
187 * Determine the length of the first loss interval via inverse lookup.
188 * Assume that X_recv can be computed by the throughput equation
192 * Find some p such that f(p) = fval; return 1/p [RFC 3448, 6.3.1].
194 if (rtt
== 0) { /* would result in divide-by-zero */
195 DCCP_WARN("RTT==0\n");
199 delta
= ktime_us_delta(ktime_get_real(), last_feedback
);
200 DCCP_BUG_ON(delta
<= 0);
202 x_recv
= scaled_div32(bytes_recv
, delta
);
203 if (x_recv
== 0) { /* would also trigger divide-by-zero */
204 DCCP_WARN("X_recv==0\n");
205 if (previous_x_recv
== 0) {
206 DCCP_BUG("stored value of X_recv is zero");
209 x_recv
= previous_x_recv
;
212 fval
= scaled_div(s
, rtt
);
213 fval
= scaled_div32(fval
, x_recv
);
214 p
= tfrc_calc_x_reverse_lookup(fval
);
216 dccp_pr_debug("%s(%p), receive rate=%u bytes/s, implied "
217 "loss rate=%u\n", dccp_role(sk
), sk
, x_recv
, p
);
225 void dccp_li_update_li(struct sock
*sk
,
226 struct list_head
*li_hist_list
,
227 struct list_head
*hist_list
,
228 ktime_t last_feedback
, u16 s
, u32 bytes_recv
,
229 u32 previous_x_recv
, u64 seq_loss
, u8 win_loss
)
231 struct dccp_li_hist_entry
*head
;
234 if (list_empty(li_hist_list
)) {
235 if (!dccp_li_hist_interval_new(li_hist_list
, seq_loss
,
239 head
= list_entry(li_hist_list
->next
, struct dccp_li_hist_entry
,
241 head
->dccplih_interval
= dccp_li_calc_first_li(sk
, hist_list
,
246 struct dccp_li_hist_entry
*entry
;
247 struct list_head
*tail
;
249 head
= list_entry(li_hist_list
->next
, struct dccp_li_hist_entry
,
251 /* FIXME win count check removed as was wrong */
252 /* should make this check with receive history */
253 /* and compare there as per section 10.2 of RFC4342 */
255 /* new loss event detected */
256 /* calculate last interval length */
257 seq_temp
= dccp_delta_seqno(head
->dccplih_seqno
, seq_loss
);
258 entry
= dccp_li_hist_entry_new(GFP_ATOMIC
);
261 DCCP_BUG("out of memory - can not allocate entry");
265 list_add(&entry
->dccplih_node
, li_hist_list
);
267 tail
= li_hist_list
->prev
;
269 kmem_cache_free(dccp_li_cachep
, tail
);
271 /* Create the newest interval */
272 entry
->dccplih_seqno
= seq_loss
;
273 entry
->dccplih_interval
= seq_temp
;
274 entry
->dccplih_win_count
= win_loss
;
278 EXPORT_SYMBOL_GPL(dccp_li_update_li
);
280 static __init
int dccp_li_init(void)
282 dccp_li_cachep
= kmem_cache_create("dccp_li_hist",
283 sizeof(struct dccp_li_hist_entry
),
284 0, SLAB_HWCACHE_ALIGN
, NULL
);
285 return dccp_li_cachep
== NULL
? -ENOBUFS
: 0;
288 static __exit
void dccp_li_exit(void)
290 kmem_cache_destroy(dccp_li_cachep
);
293 module_init(dccp_li_init
);
294 module_exit(dccp_li_exit
);