Blame view

net/sctp/tsnmap.c 9.66 KB
60c778b25   Vlad Yasevich   [SCTP]: Stop clai...
1
  /* SCTP kernel implementation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
   * (C) Copyright IBM Corp. 2001, 2004
   * Copyright (c) 1999-2000 Cisco, Inc.
   * Copyright (c) 1999-2001 Motorola, Inc.
   * Copyright (c) 2001 Intel Corp.
   *
60c778b25   Vlad Yasevich   [SCTP]: Stop clai...
7
   * This file is part of the SCTP kernel implementation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
   *
   * These functions manipulate sctp tsn mapping array.
   *
60c778b25   Vlad Yasevich   [SCTP]: Stop clai...
11
   * This SCTP implementation is free software;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
   * 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, or (at your option)
   * any later version.
   *
60c778b25   Vlad Yasevich   [SCTP]: Stop clai...
17
   * This SCTP implementation is distributed in the hope that it
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
   * will be useful, but WITHOUT ANY WARRANTY; without even the implied
   *                 ************************
   * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   * See the GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with GNU CC; see the file COPYING.  If not, write to
   * the Free Software Foundation, 59 Temple Place - Suite 330,
   * Boston, MA 02111-1307, USA.
   *
   * Please send any bug reports or fixes you make to the
   * email address(es):
   *    lksctp developers <lksctp-developers@lists.sourceforge.net>
   *
   * Or submit a bug report through the following website:
   *    http://www.sf.net/projects/lksctp
   *
   * Written or modified by:
   *    La Monte H.P. Yarroll <piggy@acm.org>
   *    Jon Grimm             <jgrimm@us.ibm.com>
   *    Karl Knutson          <karl@athena.chicago.il.us>
   *    Sridhar Samudrala     <sri@us.ibm.com>
   *
   * Any bugs reported given to us we will try to fix... any fixes shared will
   * be incorporated into the next SCTP release.
   */
5a0e3ad6a   Tejun Heo   include cleanup: ...
44
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
  #include <linux/types.h>
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
46
  #include <linux/bitmap.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
  #include <net/sctp/sctp.h>
  #include <net/sctp/sm.h>
  
  static void sctp_tsnmap_update(struct sctp_tsnmap *map);
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
51
52
53
  static void sctp_tsnmap_find_gap_ack(unsigned long *map, __u16 off,
  				     __u16 len, __u16 *start, __u16 *end);
  static int sctp_tsnmap_grow(struct sctp_tsnmap *map, u16 gap);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
56
  
  /* Initialize a block of memory as a tsnmap.  */
  struct sctp_tsnmap *sctp_tsnmap_init(struct sctp_tsnmap *map, __u16 len,
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
57
  				     __u32 initial_tsn, gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
  {
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
59
60
61
62
63
64
65
66
67
  	if (!map->tsn_map) {
  		map->tsn_map = kzalloc(len>>3, gfp);
  		if (map->tsn_map == NULL)
  			return NULL;
  
  		map->len = len;
  	} else {
  		bitmap_zero(map->tsn_map, map->len);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
  
  	/* Keep track of TSNs represented by tsn_map.  */
  	map->base_tsn = initial_tsn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
  	map->cumulative_tsn_ack_point = initial_tsn - 1;
  	map->max_tsn_seen = map->cumulative_tsn_ack_point;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
  	map->num_dup_tsns = 0;
  
  	return map;
  }
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
77
78
79
80
81
  void sctp_tsnmap_free(struct sctp_tsnmap *map)
  {
  	map->len = 0;
  	kfree(map->tsn_map);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
85
86
87
88
89
  /* Test the tracking state of this TSN.
   * Returns:
   *   0 if the TSN has not yet been seen
   *  >0 if the TSN has been seen (duplicate)
   *  <0 if the TSN is invalid (too large to track)
   */
  int sctp_tsnmap_check(const struct sctp_tsnmap *map, __u32 tsn)
  {
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
90
91
92
93
94
95
96
97
98
99
100
  	u32 gap;
  
  	/* Check to see if this is an old TSN */
  	if (TSN_lte(tsn, map->cumulative_tsn_ack_point))
  		return 1;
  
  	/* Verify that we can hold this TSN and that it will not
  	 * overlfow our map
  	 */
  	if (!TSN_lt(tsn, map->base_tsn + SCTP_TSN_MAP_SIZE))
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
  
  	/* Calculate the index into the mapping arrays.  */
  	gap = tsn - map->base_tsn;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
104
105
106
  	/* Check to see if TSN has already been recorded.  */
  	if (gap < map->len && test_bit(gap, map->tsn_map))
  		return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
  	else
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
108
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
  }
  
  
  /* Mark this TSN as seen.  */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
113
  int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  {
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
115
  	u16 gap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
  	if (TSN_lt(tsn, map->base_tsn))
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
118
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
  	gap = tsn - map->base_tsn;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
121
122
  	if (gap >= map->len && !sctp_tsnmap_grow(map, gap))
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123

8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  	if (!sctp_tsnmap_has_gap(map) && gap == 0) {
  		/* In this case the map has no gaps and the tsn we are
  		 * recording is the next expected tsn.  We don't touch
  		 * the map but simply bump the values.
  		 */
  		map->max_tsn_seen++;
  		map->cumulative_tsn_ack_point++;
  		map->base_tsn++;
  	} else {
  		/* Either we already have a gap, or about to record a gap, so
  		 * have work to do.
  		 *
  		 * Bump the max.
  		 */
  		if (TSN_lt(map->max_tsn_seen, tsn))
  			map->max_tsn_seen = tsn;
  
  		/* Mark the TSN as received.  */
  		set_bit(gap, map->tsn_map);
  
  		/* Go fixup any internal TSN mapping variables including
  		 * cumulative_tsn_ack_point.
  		 */
  		sctp_tsnmap_update(map);
  	}
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
  }
  
  
  /* Initialize a Gap Ack Block iterator from memory being provided.  */
  SCTP_STATIC void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map,
  				       struct sctp_tsnmap_iter *iter)
  {
  	/* Only start looking one past the Cumulative TSN Ack Point.  */
  	iter->start = map->cumulative_tsn_ack_point + 1;
  }
  
  /* Get the next Gap Ack Blocks. Returns 0 if there was not another block
   * to get.
   */
  SCTP_STATIC int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map,
  					 struct sctp_tsnmap_iter *iter,
  					 __u16 *start, __u16 *end)
  {
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
169
170
  	int ended = 0;
  	__u16 start_ = 0, end_ = 0, offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
174
  
  	/* If there are no more gap acks possible, get out fast.  */
  	if (TSN_lte(map->max_tsn_seen, iter->start))
  		return 0;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
175
176
177
  	offset = iter->start - map->base_tsn;
  	sctp_tsnmap_find_gap_ack(map->tsn_map, offset, map->len,
  				 &start_, &end_);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178

8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
179
180
181
  	/* The Gap Ack Block happens to end at the end of the map. */
  	if (start_ && !end_)
  		end_ = map->len - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
184
185
  
  	/* If we found a Gap Ack Block, return the start and end and
  	 * bump the iterator forward.
  	 */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
186
  	if (end_) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
  		/* Fix up the start and end based on the
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
188
  		 * Cumulative TSN Ack which is always 1 behind base.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  		 */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
190
191
  		*start = start_ + 1;
  		*end = end_ + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
194
  
  		/* Move the iterator forward.  */
  		iter->start = map->cumulative_tsn_ack_point + *end + 1;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
195
  		ended = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
199
200
201
202
203
  	}
  
  	return ended;
  }
  
  /* Mark this and any lower TSN as seen.  */
  void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn)
  {
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
204
  	u32 gap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
  	if (TSN_lt(tsn, map->base_tsn))
  		return;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
208
  	if (!TSN_lt(tsn, map->base_tsn + SCTP_TSN_MAP_SIZE))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
213
  		return;
  
  	/* Bump the max.  */
  	if (TSN_lt(map->max_tsn_seen, tsn))
  		map->max_tsn_seen = tsn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  	gap = tsn - map->base_tsn + 1;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
215
216
217
218
219
220
221
222
  	map->base_tsn += gap;
  	map->cumulative_tsn_ack_point += gap;
  	if (gap >= map->len) {
  		/* If our gap is larger then the map size, just
  		 * zero out the map.
  		 */
  		bitmap_zero(map->tsn_map, map->len);
  	} else {
025dfdafe   Frederik Schwarzer   trivial: fix then...
223
  		/* If the gap is smaller than the map size,
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
224
225
226
227
  		 * shift the map by 'gap' bits and update further.
  		 */
  		bitmap_shift_right(map->tsn_map, map->tsn_map, gap, map->len);
  		sctp_tsnmap_update(map);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
233
234
235
236
237
238
239
  }
  
  /********************************************************************
   * 2nd Level Abstractions
   ********************************************************************/
  
  /* This private helper function updates the tsnmap buffers and
   * the Cumulative TSN Ack Point.
   */
  static void sctp_tsnmap_update(struct sctp_tsnmap *map)
  {
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
240
241
242
243
244
245
246
247
248
249
250
  	u16 len;
  	unsigned long zero_bit;
  
  
  	len = map->max_tsn_seen - map->cumulative_tsn_ack_point;
  	zero_bit = find_first_zero_bit(map->tsn_map, len);
  	if (!zero_bit)
  		return;		/* The first 0-bit is bit 0.  nothing to do */
  
  	map->base_tsn += zero_bit;
  	map->cumulative_tsn_ack_point += zero_bit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251

8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
252
  	bitmap_shift_right(map->tsn_map, map->tsn_map, zero_bit, map->len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
255
256
257
258
259
260
261
262
  }
  
  /* How many data chunks  are we missing from our peer?
   */
  __u16 sctp_tsnmap_pending(struct sctp_tsnmap *map)
  {
  	__u32 cum_tsn = map->cumulative_tsn_ack_point;
  	__u32 max_tsn = map->max_tsn_seen;
  	__u32 base_tsn = map->base_tsn;
  	__u16 pending_data;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
263
  	u32 gap, i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
  
  	pending_data = max_tsn - cum_tsn;
  	gap = max_tsn - base_tsn;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
267
  	if (gap == 0 || gap >= map->len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
  		goto out;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
269
270
  	for (i = 0; i < gap+1; i++) {
  		if (test_bit(i, map->tsn_map))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
  			pending_data--;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
277
278
279
280
281
282
  out:
  	return pending_data;
  }
  
  /* This is a private helper for finding Gap Ack Blocks.  It searches a
   * single array for the start and end of a Gap Ack Block.
   *
   * The flags "started" and "ended" tell is if we found the beginning
   * or (respectively) the end of a Gap Ack Block.
   */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
283
284
  static void sctp_tsnmap_find_gap_ack(unsigned long *map, __u16 off,
  				     __u16 len, __u16 *start, __u16 *end)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
288
289
290
291
292
293
294
  {
  	int i = off;
  
  	/* Look through the entire array, but break out
  	 * early if we have found the end of the Gap Ack Block.
  	 */
  
  	/* Also, stop looking past the maximum TSN seen. */
  
  	/* Look for the start. */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
295
296
297
  	i = find_next_bit(map, len, off);
  	if (i < len)
  		*start = i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
  
  	/* Look for the end.  */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
300
  	if (*start) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
302
303
  		/* We have found the start, let's find the
  		 * end.  If we find the end, break out.
  		 */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
304
305
306
  		i = find_next_zero_bit(map, len, i);
  		if (i < len)
  			*end = i - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
309
310
311
312
  	}
  }
  
  /* Renege that we have seen a TSN.  */
  void sctp_tsnmap_renege(struct sctp_tsnmap *map, __u32 tsn)
  {
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
313
  	u32 gap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
  
  	if (TSN_lt(tsn, map->base_tsn))
  		return;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
317
318
  	/* Assert: TSN is in range.  */
  	if (!TSN_lt(tsn, map->base_tsn + map->len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
  	gap = tsn - map->base_tsn;
  
  	/* Pretend we never saw the TSN.  */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
323
  	clear_bit(gap, map->tsn_map);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
326
  }
  
  /* How many gap ack blocks do we have recorded? */
02015180e   Vlad Yasevich   sctp: shrink sctp...
327
328
  __u16 sctp_tsnmap_num_gabs(struct sctp_tsnmap *map,
  			   struct sctp_gap_ack_block *gabs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
  {
  	struct sctp_tsnmap_iter iter;
02015180e   Vlad Yasevich   sctp: shrink sctp...
331
  	int ngaps = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
333
334
  
  	/* Refresh the gap ack information. */
  	if (sctp_tsnmap_has_gap(map)) {
59ed5aba9   Shan Wei   sctp: fix compile...
335
  		__u16 start = 0, end = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
  		sctp_tsnmap_iter_init(map, &iter);
  		while (sctp_tsnmap_next_gap_ack(map, &iter,
9f81bcd94   Al Viro   [SCTP]: More triv...
338
339
  						&start,
  						&end)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340

02015180e   Vlad Yasevich   sctp: shrink sctp...
341
342
343
344
  			gabs[ngaps].start = htons(start);
  			gabs[ngaps].end = htons(end);
  			ngaps++;
  			if (ngaps >= SCTP_MAX_GABS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
347
  				break;
  		}
  	}
02015180e   Vlad Yasevich   sctp: shrink sctp...
348
  	return ngaps;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
  }
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
  
  static int sctp_tsnmap_grow(struct sctp_tsnmap *map, u16 gap)
  {
  	unsigned long *new;
  	unsigned long inc;
  	u16  len;
  
  	if (gap >= SCTP_TSN_MAP_SIZE)
  		return 0;
  
  	inc = ALIGN((gap - map->len),BITS_PER_LONG) + SCTP_TSN_MAP_INCREMENT;
  	len = min_t(u16, map->len + inc, SCTP_TSN_MAP_SIZE);
  
  	new = kzalloc(len>>3, GFP_ATOMIC);
  	if (!new)
  		return 0;
  
  	bitmap_copy(new, map->tsn_map, map->max_tsn_seen - map->base_tsn);
  	kfree(map->tsn_map);
  	map->tsn_map = new;
  	map->len = len;
  
  	return 1;
  }