loss_interval.c 7.18 KB
Newer Older
1 2 3
/*
 *  net/dccp/ccids/lib/loss_interval.c
 *
Ian McDonald's avatar
Ian McDonald committed
4 5
 *  Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand.
 *  Copyright (c) 2005-7 Ian McDonald <ian.mcdonald@jandi.co.nz>
6 7 8 9 10 11 12 13 14
 *  Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 */

#include <linux/module.h>
Ian McDonald's avatar
Ian McDonald committed
15
#include <net/sock.h>
16
#include "../../dccp.h"
17
#include "loss_interval.h"
18 19
#include "packet_history.h"
#include "tfrc.h"
20

21 22 23 24 25 26 27 28 29
#define DCCP_LI_HIST_IVAL_F_LENGTH  8

struct dccp_li_hist_entry {
	struct list_head dccplih_node;
	u64		 dccplih_seqno:48,
			 dccplih_win_count:4;
	u32		 dccplih_interval;
};

30
static struct kmem_cache *dccp_li_cachep __read_mostly;
31

32
static inline struct dccp_li_hist_entry *dccp_li_hist_entry_new(const gfp_t prio)
33
{
34
	return kmem_cache_alloc(dccp_li_cachep, prio);
35 36
}

37
static inline void dccp_li_hist_entry_delete(struct dccp_li_hist_entry *entry)
38 39
{
	if (entry != NULL)
40
		kmem_cache_free(dccp_li_cachep, entry);
41 42
}

43
void dccp_li_hist_purge(struct list_head *list)
44 45 46 47 48
{
	struct dccp_li_hist_entry *entry, *next;

	list_for_each_entry_safe(entry, next, list, dccplih_node) {
		list_del_init(&entry->dccplih_node);
49
		kmem_cache_free(dccp_li_cachep, entry);
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
	}
}

EXPORT_SYMBOL_GPL(dccp_li_hist_purge);

/* Weights used to calculate loss event rate */
/*
 * These are integers as per section 8 of RFC3448. We can then divide by 4 *
 * when we use it.
 */
static const int dccp_li_hist_w[DCCP_LI_HIST_IVAL_F_LENGTH] = {
	4, 4, 4, 4, 3, 2, 1, 1,
};

u32 dccp_li_hist_calc_i_mean(struct list_head *list)
{
	struct dccp_li_hist_entry *li_entry, *li_next;
	int i = 0;
	u32 i_tot;
	u32 i_tot0 = 0;
	u32 i_tot1 = 0;
	u32 w_tot  = 0;

	list_for_each_entry_safe(li_entry, li_next, list, dccplih_node) {
74
		if (li_entry->dccplih_interval != ~0U) {
75 76
			i_tot0 += li_entry->dccplih_interval * dccp_li_hist_w[i];
			w_tot  += dccp_li_hist_w[i];
Ian McDonald's avatar
Ian McDonald committed
77 78
			if (i != 0)
				i_tot1 += li_entry->dccplih_interval * dccp_li_hist_w[i - 1];
79 80 81 82 83 84 85 86 87 88 89 90
		}


		if (++i > DCCP_LI_HIST_IVAL_F_LENGTH)
			break;
	}

	if (i != DCCP_LI_HIST_IVAL_F_LENGTH)
		return 0;

	i_tot = max(i_tot0, i_tot1);

Ian McDonald's avatar
Ian McDonald committed
91
	if (!w_tot) {
92
		DCCP_WARN("w_tot = 0\n");
Ian McDonald's avatar
Ian McDonald committed
93 94
		return 1;
	}
95

Ian McDonald's avatar
Ian McDonald committed
96
	return i_tot / w_tot;
97 98 99 100
}

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)
103
{
Ian McDonald's avatar
Ian McDonald committed
104
	struct dccp_li_hist_entry *entry;
105 106
	int i;

Ian McDonald's avatar
Ian McDonald committed
107
	for (i = 0; i < DCCP_LI_HIST_IVAL_F_LENGTH; i++) {
108
		entry = dccp_li_hist_entry_new(GFP_ATOMIC);
109
		if (entry == NULL) {
110
			dccp_li_hist_purge(list);
111
			DCCP_BUG("loss interval list entry is NULL");
Ian McDonald's avatar
Ian McDonald committed
112
			return 0;
113
		}
Ian McDonald's avatar
Ian McDonald committed
114
		entry->dccplih_interval = ~0;
115 116 117 118 119
		list_add(&entry->dccplih_node, list);
	}

	entry->dccplih_seqno     = seq_loss;
	entry->dccplih_win_count = win_loss;
Ian McDonald's avatar
Ian McDonald committed
120
	return 1;
121 122
}

123 124 125 126 127
/* calculate first loss interval
 *
 * returns estimated loss interval in usecs */
static u32 dccp_li_calc_first_li(struct sock *sk,
				 struct list_head *hist_list,
128
				 ktime_t last_feedback,
129 130 131 132 133 134
				 u16 s, u32 bytes_recv,
				 u32 previous_x_recv)
{
	struct dccp_rx_hist_entry *entry, *next, *tail = NULL;
	u32 x_recv, p;
	suseconds_t rtt, delta;
135
	ktime_t tstamp = ktime_set(0, 0);
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
	int interval = 0;
	int win_count = 0;
	int step = 0;
	u64 fval;

	list_for_each_entry_safe(entry, next, hist_list, dccphrx_node) {
		if (dccp_rx_hist_entry_data_packet(entry)) {
			tail = entry;

			switch (step) {
			case 0:
				tstamp	  = entry->dccphrx_tstamp;
				win_count = entry->dccphrx_ccval;
				step = 1;
				break;
			case 1:
				interval = win_count - entry->dccphrx_ccval;
				if (interval < 0)
					interval += TFRC_WIN_COUNT_LIMIT;
				if (interval > 4)
					goto found;
				break;
			}
		}
	}

	if (unlikely(step == 0)) {
		DCCP_WARN("%s(%p), packet history has no data packets!\n",
			  dccp_role(sk), sk);
		return ~0;
	}

	if (unlikely(interval == 0)) {
		DCCP_WARN("%s(%p), Could not find a win_count interval > 0."
			  "Defaulting to 1\n", dccp_role(sk), sk);
		interval = 1;
	}
found:
	if (!tail) {
		DCCP_CRIT("tail is null\n");
		return ~0;
	}

179
	delta = ktime_us_delta(tstamp, tail->dccphrx_tstamp);
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
	DCCP_BUG_ON(delta < 0);

	rtt = delta * 4 / interval;
	dccp_pr_debug("%s(%p), approximated RTT to %dus\n",
		      dccp_role(sk), sk, (int)rtt);

	/*
	 * Determine the length of the first loss interval via inverse lookup.
	 * Assume that X_recv can be computed by the throughput equation
	 *		    s
	 *	X_recv = --------
	 *		 R * fval
	 * Find some p such that f(p) = fval; return 1/p [RFC 3448, 6.3.1].
	 */
	if (rtt == 0) {			/* would result in divide-by-zero */
		DCCP_WARN("RTT==0\n");
		return ~0;
	}

199
	delta = ktime_us_delta(ktime_get_real(), last_feedback);
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
	DCCP_BUG_ON(delta <= 0);

	x_recv = scaled_div32(bytes_recv, delta);
	if (x_recv == 0) {		/* would also trigger divide-by-zero */
		DCCP_WARN("X_recv==0\n");
		if (previous_x_recv == 0) {
			DCCP_BUG("stored value of X_recv is zero");
			return ~0;
		}
		x_recv = previous_x_recv;
	}

	fval = scaled_div(s, rtt);
	fval = scaled_div32(fval, x_recv);
	p = tfrc_calc_x_reverse_lookup(fval);

	dccp_pr_debug("%s(%p), receive rate=%u bytes/s, implied "
		      "loss rate=%u\n", dccp_role(sk), sk, x_recv, p);

	if (p == 0)
		return ~0;
	else
		return 1000000 / p;
}

225
void dccp_li_update_li(struct sock *sk,
226 227
		       struct list_head *li_hist_list,
		       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)
230 231 232 233 234
{
	struct dccp_li_hist_entry *head;
	u64 seq_temp;

	if (list_empty(li_hist_list)) {
235 236
		if (!dccp_li_hist_interval_new(li_hist_list, seq_loss,
					       win_loss))
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
			return;

		head = list_entry(li_hist_list->next, struct dccp_li_hist_entry,
				  dccplih_node);
		head->dccplih_interval = dccp_li_calc_first_li(sk, hist_list,
							       last_feedback,
							       s, bytes_recv,
							       previous_x_recv);
	} else {
		struct dccp_li_hist_entry *entry;
		struct list_head *tail;

		head = list_entry(li_hist_list->next, struct dccp_li_hist_entry,
				  dccplih_node);
		/* FIXME win count check removed as was wrong */
		/* should make this check with receive history */
		/* and compare there as per section 10.2 of RFC4342 */

		/* new loss event detected */
		/* calculate last interval length */
		seq_temp = dccp_delta_seqno(head->dccplih_seqno, seq_loss);
258
		entry = dccp_li_hist_entry_new(GFP_ATOMIC);
259 260 261 262 263 264 265 266 267 268

		if (entry == NULL) {
			DCCP_BUG("out of memory - can not allocate entry");
			return;
		}

		list_add(&entry->dccplih_node, li_hist_list);

		tail = li_hist_list->prev;
		list_del(tail);
269
		kmem_cache_free(dccp_li_cachep, tail);
270 271 272 273 274 275 276 277 278

		/* Create the newest interval */
		entry->dccplih_seqno = seq_loss;
		entry->dccplih_interval = seq_temp;
		entry->dccplih_win_count = win_loss;
	}
}

EXPORT_SYMBOL_GPL(dccp_li_update_li);
279 280 281 282 283

static __init int dccp_li_init(void)
{
	dccp_li_cachep = kmem_cache_create("dccp_li_hist",
					   sizeof(struct dccp_li_hist_entry),
284
					   0, SLAB_HWCACHE_ALIGN, NULL);
285 286 287 288 289 290 291 292 293 294
	return dccp_li_cachep == NULL ? -ENOBUFS : 0;
}

static __exit void dccp_li_exit(void)
{
	kmem_cache_destroy(dccp_li_cachep);
}

module_init(dccp_li_init);
module_exit(dccp_li_exit);