Blame view

net/sctp/tsnmap.c 9.46 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
   * 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
4b2f13a25   Jeff Kirsher   sctp: Fix FSF add...
24
25
   * along with GNU CC; see the file COPYING.  If not, see
   * <http://www.gnu.org/licenses/>.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
28
   *
   * Please send any bug reports or fixes you make to the
   * email address(es):
91705c61b   Daniel Borkmann   net: sctp: trivia...
29
   *    lksctp developers <linux-sctp@vger.kernel.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
34
35
   * 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>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
   */
5a0e3ad6a   Tejun Heo   include cleanup: ...
37
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
  #include <linux/types.h>
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
39
  #include <linux/bitmap.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
43
  #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 ...
44
45
  static void sctp_tsnmap_find_gap_ack(unsigned long *map, __u16 off,
  				     __u16 len, __u16 *start, __u16 *end);
70fc69bc5   Lee A. Roberts   sctp: fix associa...
46
  static int sctp_tsnmap_grow(struct sctp_tsnmap *map, u16 size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
  
  /* 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 ...
50
  				     __u32 initial_tsn, gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
  {
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
52
53
54
55
56
57
58
59
60
  	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
61
62
63
  
  	/* Keep track of TSNs represented by tsn_map.  */
  	map->base_tsn = initial_tsn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
  	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
66
67
68
69
  	map->num_dup_tsns = 0;
  
  	return map;
  }
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
70
71
72
73
74
  void sctp_tsnmap_free(struct sctp_tsnmap *map)
  {
  	map->len = 0;
  	kfree(map->tsn_map);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
79
80
81
82
  /* 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 ...
83
84
85
86
87
88
89
90
91
92
93
  	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
94
95
96
  
  	/* Calculate the index into the mapping arrays.  */
  	gap = tsn - map->base_tsn;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
97
98
99
  	/* 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
100
  	else
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
101
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
105
  }
  
  
  /* Mark this TSN as seen.  */
4244854d2   Neil Horman   sctp: be more res...
106
107
  int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn,
  		     struct sctp_transport *trans)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
  {
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
109
  	u16 gap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  	gap = tsn - map->base_tsn;
70fc69bc5   Lee A. Roberts   sctp: fix associa...
115
  	if (gap >= map->len && !sctp_tsnmap_grow(map, gap + 1))
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
116
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117

8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
118
119
120
121
122
123
124
  	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++;
4244854d2   Neil Horman   sctp: be more res...
125
126
127
  		if (trans)
  			trans->sack_generation =
  				trans->asoc->peer.sack_generation;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  		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
148
149
150
151
  }
  
  
  /* Initialize a Gap Ack Block iterator from memory being provided.  */
dda919285   Daniel Borkmann   net: sctp: remove...
152
153
  static void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map,
  				  struct sctp_tsnmap_iter *iter)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
155
156
157
158
159
160
161
  {
  	/* 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.
   */
dda919285   Daniel Borkmann   net: sctp: remove...
162
163
164
  static int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map,
  				    struct sctp_tsnmap_iter *iter,
  				    __u16 *start, __u16 *end)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  {
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
166
167
  	int ended = 0;
  	__u16 start_ = 0, end_ = 0, offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
171
  
  	/* 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 ...
172
173
174
  	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
175

8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
176
177
178
  	/* 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
179
180
181
182
  
  	/* If we found a Gap Ack Block, return the start and end and
  	 * bump the iterator forward.
  	 */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
183
  	if (end_) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
  		/* Fix up the start and end based on the
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
185
  		 * Cumulative TSN Ack which is always 1 behind base.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
  		 */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
187
188
  		*start = start_ + 1;
  		*end = end_ + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
  
  		/* Move the iterator forward.  */
  		iter->start = map->cumulative_tsn_ack_point + *end + 1;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
192
  		ended = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
196
197
198
199
200
  	}
  
  	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 ...
201
  	u32 gap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
  	if (TSN_lt(tsn, map->base_tsn))
  		return;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
205
  	if (!TSN_lt(tsn, map->base_tsn + SCTP_TSN_MAP_SIZE))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
209
210
  		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
211
  	gap = tsn - map->base_tsn + 1;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
212
213
214
215
216
217
218
219
  	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...
220
  		/* If the gap is smaller than the map size,
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
221
222
223
224
  		 * 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
225
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
229
230
231
232
233
234
235
236
  }
  
  /********************************************************************
   * 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 ...
237
238
239
240
241
242
243
244
245
246
247
  	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
248

8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
249
  	bitmap_shift_right(map->tsn_map, map->tsn_map, zero_bit, map->len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
253
254
255
256
257
258
259
  }
  
  /* 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;
fc184f089   Akinobu Mita   sctp: use bitmap_...
260
  	u32 gap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
  
  	pending_data = max_tsn - cum_tsn;
  	gap = max_tsn - base_tsn;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
264
  	if (gap == 0 || gap >= map->len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
  		goto out;
fc184f089   Akinobu Mita   sctp: use bitmap_...
266
  	pending_data -= bitmap_weight(map->tsn_map, gap + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
268
269
270
271
272
273
274
275
276
  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 ...
277
278
  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
279
280
281
282
283
284
285
286
287
288
  {
  	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 ...
289
290
291
  	i = find_next_bit(map, len, off);
  	if (i < len)
  		*start = i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
  
  	/* Look for the end.  */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
294
  	if (*start) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
  		/* We have found the start, let's find the
  		 * end.  If we find the end, break out.
  		 */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
298
299
300
  		i = find_next_zero_bit(map, len, i);
  		if (i < len)
  			*end = i - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
302
303
304
305
306
  	}
  }
  
  /* Renege that we have seen a TSN.  */
  void sctp_tsnmap_renege(struct sctp_tsnmap *map, __u32 tsn)
  {
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
307
  	u32 gap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
  
  	if (TSN_lt(tsn, map->base_tsn))
  		return;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
311
312
  	/* Assert: TSN is in range.  */
  	if (!TSN_lt(tsn, map->base_tsn + map->len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
  	gap = tsn - map->base_tsn;
  
  	/* Pretend we never saw the TSN.  */
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
317
  	clear_bit(gap, map->tsn_map);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
320
  }
  
  /* How many gap ack blocks do we have recorded? */
02015180e   Vlad Yasevich   sctp: shrink sctp...
321
322
  __u16 sctp_tsnmap_num_gabs(struct sctp_tsnmap *map,
  			   struct sctp_gap_ack_block *gabs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
  {
  	struct sctp_tsnmap_iter iter;
02015180e   Vlad Yasevich   sctp: shrink sctp...
325
  	int ngaps = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
328
  
  	/* Refresh the gap ack information. */
  	if (sctp_tsnmap_has_gap(map)) {
59ed5aba9   Shan Wei   sctp: fix compile...
329
  		__u16 start = 0, end = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
  		sctp_tsnmap_iter_init(map, &iter);
  		while (sctp_tsnmap_next_gap_ack(map, &iter,
9f81bcd94   Al Viro   [SCTP]: More triv...
332
333
  						&start,
  						&end)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334

02015180e   Vlad Yasevich   sctp: shrink sctp...
335
336
337
338
  			gabs[ngaps].start = htons(start);
  			gabs[ngaps].end = htons(end);
  			ngaps++;
  			if (ngaps >= SCTP_MAX_GABS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
340
341
  				break;
  		}
  	}
02015180e   Vlad Yasevich   sctp: shrink sctp...
342
  	return ngaps;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
  }
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
344

70fc69bc5   Lee A. Roberts   sctp: fix associa...
345
  static int sctp_tsnmap_grow(struct sctp_tsnmap *map, u16 size)
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
346
347
348
349
  {
  	unsigned long *new;
  	unsigned long inc;
  	u16  len;
70fc69bc5   Lee A. Roberts   sctp: fix associa...
350
  	if (size > SCTP_TSN_MAP_SIZE)
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
351
  		return 0;
70fc69bc5   Lee A. Roberts   sctp: fix associa...
352
  	inc = ALIGN((size - map->len), BITS_PER_LONG) + SCTP_TSN_MAP_INCREMENT;
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
353
354
355
356
357
  	len = min_t(u16, map->len + inc, SCTP_TSN_MAP_SIZE);
  
  	new = kzalloc(len>>3, GFP_ATOMIC);
  	if (!new)
  		return 0;
70fc69bc5   Lee A. Roberts   sctp: fix associa...
358
359
  	bitmap_copy(new, map->tsn_map,
  		map->max_tsn_seen - map->cumulative_tsn_ack_point);
8e1ee18c3   Vlad Yasevich   sctp: Rework the ...
360
361
362
363
364
365
  	kfree(map->tsn_map);
  	map->tsn_map = new;
  	map->len = len;
  
  	return 1;
  }