blob: 1397360647130b30348ed607a9dee3512eb58ad5 [file] [log] [blame]
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -03001/*
Ian McDonaldf3166c02006-08-26 19:01:03 -07002 * net/dccp/packet_history.c
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -03003 *
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -02004 * Copyright (c) 2007 The University of Aberdeen, Scotland, UK
5 * Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand.
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -03006 *
7 * An implementation of the DCCP protocol
8 *
9 * This code has been developed by the University of Waikato WAND
10 * research group. For further information please see http://www.wand.net.nz/
Ian McDonalde6bccd32006-08-26 19:01:30 -070011 * or e-mail Ian McDonald - ian.mcdonald@jandi.co.nz
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -030012 *
13 * This code also uses code from Lulea University, rereleased as GPL by its
14 * authors:
15 * Copyright (c) 2003 Nils-Erik Mattsson, Joacim Haggmark, Magnus Erixzon
16 *
17 * Changes to meet Linux coding standards, to make it meet latest ccid3 draft
18 * and to make it work as a loadable module in the DCCP stack written by
19 * Arnaldo Carvalho de Melo <acme@conectiva.com.br>.
20 *
21 * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
22 *
23 * This program is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation; either version 2 of the License, or
26 * (at your option) any later version.
27 *
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program; if not, write to the Free Software
35 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
36 */
37
Arnaldo Carvalho de Melo5cea0dd2005-08-27 23:50:46 -030038#include <linux/module.h>
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -030039#include <linux/string.h>
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -030040#include "packet_history.h"
41
Gerrit Renker7af5af32006-12-10 00:24:33 -020042/*
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020043 * Transmitter History Routines
Gerrit Renker7af5af32006-12-10 00:24:33 -020044 */
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020045static struct kmem_cache *tfrc_tx_hist;
46
47struct tfrc_tx_hist_entry *
48 tfrc_tx_hist_find_entry(struct tfrc_tx_hist_entry *head, u64 seqno)
Gerrit Renker7af5af32006-12-10 00:24:33 -020049{
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020050 while (head != NULL && head->seqno != seqno)
51 head = head->next;
Gerrit Renker7af5af32006-12-10 00:24:33 -020052
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020053 return head;
Gerrit Renker7af5af32006-12-10 00:24:33 -020054}
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020055EXPORT_SYMBOL_GPL(tfrc_tx_hist_find_entry);
Gerrit Renker7af5af32006-12-10 00:24:33 -020056
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020057int tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno)
Gerrit Renker7af5af32006-12-10 00:24:33 -020058{
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020059 struct tfrc_tx_hist_entry *entry = kmem_cache_alloc(tfrc_tx_hist, gfp_any());
Gerrit Renker7af5af32006-12-10 00:24:33 -020060
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020061 if (entry == NULL)
62 return -ENOBUFS;
63 entry->seqno = seqno;
64 entry->stamp = ktime_get_real();
65 entry->next = *headp;
66 *headp = entry;
67 return 0;
Gerrit Renker7af5af32006-12-10 00:24:33 -020068}
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020069EXPORT_SYMBOL_GPL(tfrc_tx_hist_add);
Gerrit Renker7af5af32006-12-10 00:24:33 -020070
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020071void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp)
Gerrit Renker7af5af32006-12-10 00:24:33 -020072{
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020073 struct tfrc_tx_hist_entry *head = *headp;
Gerrit Renker7af5af32006-12-10 00:24:33 -020074
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020075 while (head != NULL) {
76 struct tfrc_tx_hist_entry *next = head->next;
Gerrit Renker7af5af32006-12-10 00:24:33 -020077
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020078 kmem_cache_free(tfrc_tx_hist, head);
79 head = next;
Gerrit Renker7af5af32006-12-10 00:24:33 -020080 }
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020081
82 *headp = NULL;
Gerrit Renker7af5af32006-12-10 00:24:33 -020083}
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -020084EXPORT_SYMBOL_GPL(tfrc_tx_hist_purge);
Gerrit Renker7af5af32006-12-10 00:24:33 -020085
86/*
87 * Receiver History Routines
88 */
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -030089struct dccp_rx_hist *dccp_rx_hist_new(const char *name)
90{
91 struct dccp_rx_hist *hist = kmalloc(sizeof(*hist), GFP_ATOMIC);
92 static const char dccp_rx_hist_mask[] = "rx_hist_%s";
93 char *slab_name;
94
95 if (hist == NULL)
96 goto out;
97
98 slab_name = kmalloc(strlen(name) + sizeof(dccp_rx_hist_mask) - 1,
99 GFP_ATOMIC);
100 if (slab_name == NULL)
101 goto out_free_hist;
102
103 sprintf(slab_name, dccp_rx_hist_mask, name);
104 hist->dccprxh_slab = kmem_cache_create(slab_name,
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -0300105 sizeof(struct dccp_rx_hist_entry),
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -0200106 0, SLAB_HWCACHE_ALIGN, NULL);
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -0300107 if (hist->dccprxh_slab == NULL)
108 goto out_free_slab_name;
109out:
110 return hist;
111out_free_slab_name:
112 kfree(slab_name);
113out_free_hist:
114 kfree(hist);
115 hist = NULL;
116 goto out;
117}
118
119EXPORT_SYMBOL_GPL(dccp_rx_hist_new);
120
121void dccp_rx_hist_delete(struct dccp_rx_hist *hist)
122{
123 const char* name = kmem_cache_name(hist->dccprxh_slab);
124
125 kmem_cache_destroy(hist->dccprxh_slab);
126 kfree(name);
127 kfree(hist);
128}
129
130EXPORT_SYMBOL_GPL(dccp_rx_hist_delete);
131
Gerrit Renker7af5af32006-12-10 00:24:33 -0200132int dccp_rx_hist_find_entry(const struct list_head *list, const u64 seq,
133 u8 *ccval)
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -0300134{
Gerrit Renker7af5af32006-12-10 00:24:33 -0200135 struct dccp_rx_hist_entry *packet = NULL, *entry;
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -0300136
Gerrit Renker7af5af32006-12-10 00:24:33 -0200137 list_for_each_entry(entry, list, dccphrx_node)
138 if (entry->dccphrx_seqno == seq) {
139 packet = entry;
140 break;
141 }
142
143 if (packet)
144 *ccval = packet->dccphrx_ccval;
145
146 return packet != NULL;
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -0300147}
148
Gerrit Renker7af5af32006-12-10 00:24:33 -0200149EXPORT_SYMBOL_GPL(dccp_rx_hist_find_entry);
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -0300150struct dccp_rx_hist_entry *
151 dccp_rx_hist_find_data_packet(const struct list_head *list)
152{
153 struct dccp_rx_hist_entry *entry, *packet = NULL;
154
155 list_for_each_entry(entry, list, dccphrx_node)
156 if (entry->dccphrx_type == DCCP_PKT_DATA ||
157 entry->dccphrx_type == DCCP_PKT_DATAACK) {
158 packet = entry;
159 break;
160 }
161
162 return packet;
163}
164
165EXPORT_SYMBOL_GPL(dccp_rx_hist_find_data_packet);
166
Ian McDonald66a377c2006-08-26 23:40:50 -0700167void dccp_rx_hist_add_packet(struct dccp_rx_hist *hist,
Arnaldo Carvalho de Melo072ab6c2005-08-28 01:19:14 -0300168 struct list_head *rx_list,
169 struct list_head *li_list,
Ian McDonald66a377c2006-08-26 23:40:50 -0700170 struct dccp_rx_hist_entry *packet,
171 u64 nonloss_seqno)
Arnaldo Carvalho de Melo072ab6c2005-08-28 01:19:14 -0300172{
Ian McDonald66a377c2006-08-26 23:40:50 -0700173 struct dccp_rx_hist_entry *entry, *next;
Arnaldo Carvalho de Melo072ab6c2005-08-28 01:19:14 -0300174 u8 num_later = 0;
175
Ian McDonald66a377c2006-08-26 23:40:50 -0700176 list_add(&packet->dccphrx_node, rx_list);
Arnaldo Carvalho de Melo072ab6c2005-08-28 01:19:14 -0300177
Arnaldo Carvalho de Melo072ab6c2005-08-28 01:19:14 -0300178 num_later = TFRC_RECV_NUM_LATE_LOSS + 1;
179
180 if (!list_empty(li_list)) {
181 list_for_each_entry_safe(entry, next, rx_list, dccphrx_node) {
182 if (num_later == 0) {
Ian McDonald66a377c2006-08-26 23:40:50 -0700183 if (after48(nonloss_seqno,
184 entry->dccphrx_seqno)) {
185 list_del_init(&entry->dccphrx_node);
186 dccp_rx_hist_entry_delete(hist, entry);
187 }
Arnaldo Carvalho de Melo072ab6c2005-08-28 01:19:14 -0300188 } else if (dccp_rx_hist_entry_data_packet(entry))
189 --num_later;
190 }
191 } else {
192 int step = 0;
193 u8 win_count = 0; /* Not needed, but lets shut up gcc */
194 int tmp;
195 /*
196 * We have no loss interval history so we need at least one
197 * rtt:s of data packets to approximate rtt.
198 */
199 list_for_each_entry_safe(entry, next, rx_list, dccphrx_node) {
200 if (num_later == 0) {
201 switch (step) {
202 case 0:
203 step = 1;
204 /* OK, find next data packet */
205 num_later = 1;
206 break;
207 case 1:
208 step = 2;
209 /* OK, find next data packet */
210 num_later = 1;
211 win_count = entry->dccphrx_ccval;
212 break;
213 case 2:
214 tmp = win_count - entry->dccphrx_ccval;
215 if (tmp < 0)
216 tmp += TFRC_WIN_COUNT_LIMIT;
217 if (tmp > TFRC_WIN_COUNT_PER_RTT + 1) {
218 /*
219 * We have found a packet older
220 * than one rtt remove the rest
221 */
222 step = 3;
223 } else /* OK, find next data packet */
224 num_later = 1;
225 break;
226 case 3:
227 list_del_init(&entry->dccphrx_node);
228 dccp_rx_hist_entry_delete(hist, entry);
229 break;
230 }
231 } else if (dccp_rx_hist_entry_data_packet(entry))
232 --num_later;
233 }
234 }
Arnaldo Carvalho de Melo072ab6c2005-08-28 01:19:14 -0300235}
236
237EXPORT_SYMBOL_GPL(dccp_rx_hist_add_packet);
238
Gerrit Renker7af5af32006-12-10 00:24:33 -0200239void dccp_rx_hist_purge(struct dccp_rx_hist *hist, struct list_head *list)
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -0300240{
Gerrit Renker7af5af32006-12-10 00:24:33 -0200241 struct dccp_rx_hist_entry *entry, *next;
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -0300242
Gerrit Renker7af5af32006-12-10 00:24:33 -0200243 list_for_each_entry_safe(entry, next, list, dccphrx_node) {
244 list_del_init(&entry->dccphrx_node);
245 kmem_cache_free(hist->dccprxh_slab, entry);
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -0300246 }
247}
248
Gerrit Renker7af5af32006-12-10 00:24:33 -0200249EXPORT_SYMBOL_GPL(dccp_rx_hist_purge);
Arnaldo Carvalho de Melo8c60f3f2005-08-10 12:59:38 -0300250
Arnaldo Carvalho de Melo276f2ed2007-11-28 11:15:40 -0200251extern int __init dccp_li_init(void);
252extern void dccp_li_exit(void);
253
254static __init int packet_history_init(void)
255{
256 if (dccp_li_init() != 0)
257 goto out;
258
259 tfrc_tx_hist = kmem_cache_create("tfrc_tx_hist",
260 sizeof(struct tfrc_tx_hist_entry), 0,
261 SLAB_HWCACHE_ALIGN, NULL);
262 if (tfrc_tx_hist == NULL)
263 goto out_li_exit;
264
265 return 0;
266out_li_exit:
267 dccp_li_exit();
268out:
269 return -ENOBUFS;
270}
271module_init(packet_history_init);
272
273static __exit void packet_history_exit(void)
274{
275 if (tfrc_tx_hist != NULL) {
276 kmem_cache_destroy(tfrc_tx_hist);
277 tfrc_tx_hist = NULL;
278 }
279 dccp_li_exit();
280}
281module_exit(packet_history_exit);
Arnaldo Carvalho de Melo5cea0dd2005-08-27 23:50:46 -0300282
Ian McDonalde6bccd32006-08-26 19:01:30 -0700283MODULE_AUTHOR("Ian McDonald <ian.mcdonald@jandi.co.nz>, "
Arnaldo Carvalho de Melo5cea0dd2005-08-27 23:50:46 -0300284 "Arnaldo Carvalho de Melo <acme@ghostprotocols.net>");
285MODULE_DESCRIPTION("DCCP TFRC library");
286MODULE_LICENSE("GPL");