xref: /illumos-gate/usr/src/uts/common/inet/tcp/tcp_sack.c (revision 71269a2275bf5a143dad6461eee2710a344e7261)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1997-2001, 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/kmem.h>
31 #include <sys/debug.h>
32 #include <netinet/tcp.h>
33 #include <sys/systm.h>
34 #include <sys/stropts.h>
35 #include <netinet/in.h>
36 #include <netinet/ip6.h>
37 #include <inet/common.h>
38 #include <inet/ip.h>
39 #include <inet/tcp.h>
40 
41 /*
42  * To insert a new blk to the array of SACK blk in receiver.
43  *
44  * Parameters:
45  *	sack_blk_t *head: pointer to the array of SACK blks.
46  *	tcp_seq begin: starting seq num of the new blk.
47  *	tcp_seq end: ending seq num of the new blk.
48  *	int32_t *num: (referenced) total num of SACK blks on the list.
49  */
50 void
51 tcp_sack_insert(sack_blk_t *head, tcp_seq begin, tcp_seq end, int32_t *num)
52 {
53 	int32_t	i, j, old_num, new_num;
54 	sack_blk_t tmp[MAX_SACK_BLK - 1];
55 
56 	/* The array is empty, just add the new one. */
57 	if (*num == 0) {
58 		head[0].begin = begin;
59 		head[0].end = end;
60 		*num = 1;
61 		return;
62 	}
63 
64 	/*
65 	 * Check for overlap.  There are five cases.
66 	 *
67 	 * 1. there is no overlap with any other SACK blks.
68 	 * 2. new SACK blk is completely contained in another blk.
69 	 * 3. tail part of new SACK blk overlaps with another blk.
70 	 * 4. head part of new SACK blk overlaps with another blk.
71 	 * 5. new SACK blk completely contains another blk.
72 	 *
73 	 * Use tmp to hold old SACK blks.  After the loop, copy them back
74 	 * to head.
75 	 */
76 	old_num = *num;
77 	if (old_num > MAX_SACK_BLK - 1) {
78 		old_num = MAX_SACK_BLK - 1;
79 	}
80 	new_num = old_num;
81 	j = 0;
82 	for (i = 0; i < old_num; i++) {
83 		if (SEQ_LT(end, head[i].begin) || SEQ_GT(begin, head[i].end)) {
84 			/* Case 1: continue to check. */
85 			tmp[j].begin = head[i].begin;
86 			tmp[j].end = head[i].end;
87 			j++;
88 			continue;
89 		} else if (SEQ_GEQ(begin, head[i].begin) &&
90 		    SEQ_LEQ(end, head[i].end)) {
91 			/* Case 2: re-insert the old blk to the head. */
92 			begin = head[i].begin;
93 			end = head[i].end;
94 		} else if (SEQ_LEQ(end, head[i].end) &&
95 		    SEQ_GEQ(end, head[i].begin)) {
96 			/*
97 			 * Case 3: Extend the new blk, remove the old one
98 			 * and continue to check.
99 			 */
100 			end = head[i].end;
101 		} else if (SEQ_GEQ(begin, head[i].begin) &&
102 		    SEQ_LEQ(begin, head[i].end)) {
103 			/* Case 4 */
104 			begin = head[i].begin;
105 		}
106 		/*
107 		 * Common code for all cases except the first one, which
108 		 * copies the original SACK blk into the tmp storage.  Other
109 		 * cases remove the original SACK blk by not copying into
110 		 * tmp storage.
111 		 */
112 		new_num--;
113 	}
114 
115 	head[0].begin = begin;
116 	head[0].end = end;
117 	for (i = 0; i < new_num; i++) {
118 		head[i+1].begin = tmp[i].begin;
119 		head[i+1].end = tmp[i].end;
120 	}
121 	*num = new_num + 1;
122 }
123 
124 
125 /*
126  * To remove a SACK block.
127  *
128  * Parameters:
129  *	sack_blk_t *head: pointer to the array of SACK blks.
130  *	tcp_seq end: to remove all sack blk with seq num less than end.
131  *	int32_t *num: (referenced) total num of SACK blks in the array.
132  */
133 void
134 tcp_sack_remove(sack_blk_t *head, tcp_seq end, int32_t *num)
135 {
136 	sack_blk_t tmp[MAX_SACK_BLK];
137 	int32_t i, j, old_num, new_num;
138 
139 	if (*num == 0)
140 		return;
141 
142 	old_num = *num;
143 	new_num = old_num;
144 	j = 0;
145 	/* Walk thru the whole list and copy the new list to tmp[]. */
146 	for (i = 0; i < old_num; i++) {
147 		if (SEQ_GT(end, head[i].begin)) {
148 			/*
149 			 * Check to see if the old SACK blk needs to be
150 			 * removed or updated.  If the old blk is just
151 			 * partially covered, update begin and continue.
152 			 * If the old blk is completely covered, remove it
153 			 * and continue to check.
154 			 */
155 			if (SEQ_GEQ(end, head[i].end)) {
156 				new_num--;
157 				continue;
158 			} else {
159 				tmp[j].begin = end;
160 				tmp[j].end = head[i].end;
161 			}
162 		} else {
163 			tmp[j].begin = head[i].begin;
164 			tmp[j].end = head[i].end;
165 		}
166 		j++;
167 	}
168 	/* Copy tmp[] back to the original list. */
169 	for (i = 0; i < new_num; i++) {
170 		head[i].begin = tmp[i].begin;
171 		head[i].end = tmp[i].end;
172 	}
173 	*num = new_num;
174 }
175 
176 
177 /*
178  * Use the SACK info to insert a "notsack'ed" blk.  The notsack'ed blk list
179  * contains the list of blks which have not been selectively acknowledged
180  * by the receiver.  The SACK info is a blk which is being selectively
181  * acknowledged by the receiver.
182  *
183  * Parameters:
184  *	notsack_blk_t **head: address of the pointer to the list of notsack'ed
185  *		blks.
186  *	tcp_seq begin: starting seq num of the SACK info.
187  *	tcp_seq end: ending seq num of the SACK info.
188  *	int32_t *num: (referenced) total num of notsack'ed blk on the list.
189  *	uint32_t *sum: (referenced) total num of bytes of all the notsack'ed
190  *		blks.
191  */
192 void
193 tcp_notsack_insert(notsack_blk_t **head, tcp_seq begin, tcp_seq end,
194     int32_t *num, uint32_t *sum)
195 {
196 	notsack_blk_t *prev, *tmp, *new;
197 	uint32_t tmp_sum, tmp_num;
198 
199 	if (*head == NULL) {
200 		return;
201 	}
202 
203 	tmp = *head;
204 	prev = NULL;
205 	/* Find the right place of updating the list. */
206 	while ((tmp != NULL) && SEQ_LEQ(tmp->end, begin)) {
207 		prev = tmp;
208 		(tmp->sack_cnt)++;
209 		tmp = tmp->next;
210 	}
211 
212 	/*
213 	 * This can happen only when TCP sends new data but the notsack list
214 	 * is not updated.
215 	 */
216 	if (tmp == NULL) {
217 		return;
218 	}
219 
220 	/*
221 	 * This means the new SACK info covers something that is not on
222 	 * the list anymore.
223 	 */
224 	if (SEQ_LEQ(end, tmp->begin)) {
225 		return;
226 	}
227 
228 	/* The SACK info covers up to this blk.  So just check for this blk. */
229 	if (SEQ_LEQ(end, tmp->end)) {
230 		/*
231 		 * Only this notsack'ed blk is completely covered.  Delete
232 		 * it and return.
233 		 */
234 		if (end == tmp->end && SEQ_LEQ(begin, tmp->begin)) {
235 			if (prev != NULL) {
236 				prev->next = tmp->next;
237 			} else {
238 				*head = tmp->next;
239 			}
240 			(*num)--;
241 			*sum -= tmp->end - tmp->begin;
242 			kmem_free(tmp, sizeof (notsack_blk_t));
243 			return;
244 		}
245 		/* This blk is partially covered. */
246 		if (SEQ_GEQ(begin, tmp->begin)) {
247 			/* Check what needs to be updated. */
248 			if (begin == tmp->begin) {
249 				*sum -= end - tmp->begin;
250 				tmp->begin = end;
251 			} else if (end == tmp->end) {
252 				*sum -= tmp->end - begin;
253 				tmp->end = begin;
254 				(tmp->sack_cnt)++;
255 			} else {
256 				/* Split the notsack blk. */
257 				if ((new = kmem_alloc(sizeof (notsack_blk_t),
258 				    KM_NOSLEEP)) == NULL) {
259 					return;
260 				}
261 				new->end = tmp->end;
262 				new->begin = end;
263 				new->next = tmp->next;
264 				new->sack_cnt = 0;
265 				tmp->end = begin;
266 				tmp->next = new;
267 				(tmp->sack_cnt)++;
268 				(*num)++;
269 				*sum -= end - begin;
270 			}
271 		} else {
272 			*sum -= end - tmp->begin;
273 			tmp->begin = end;
274 		}
275 		return;
276 	}
277 
278 	/* Need to check for coverage of this blk and later blks. */
279 	tmp_sum = *sum;
280 	tmp_num = *num;
281 	if (SEQ_LT(tmp->begin, begin)) {
282 		tmp_sum -= tmp->end - begin;
283 		tmp->end = begin;
284 		(tmp->sack_cnt)++;
285 		prev = tmp;
286 		tmp = tmp->next;
287 	}
288 
289 	while (tmp != NULL) {
290 		/* The coverage stops here. */
291 		if (SEQ_GT(tmp->begin, end)) {
292 			break;
293 		} else {
294 			/* Is the blk completely or partially covered? */
295 			if (SEQ_LEQ(tmp->end, end)) {
296 				tmp_num--;
297 				tmp_sum -= tmp->end - tmp->begin;
298 				if (prev != NULL) {
299 					prev->next = tmp->next;
300 					kmem_free(tmp, sizeof (notsack_blk_t));
301 					tmp = prev->next;
302 				} else {
303 					*head = tmp->next;
304 					kmem_free(tmp, sizeof (notsack_blk_t));
305 					tmp = *head;
306 				}
307 			} else {
308 				/*
309 				 * This blk is partially covered.  It also
310 				 * means it should be the end of coverage.
311 				 */
312 				tmp_sum -= end - tmp->begin;
313 				tmp->begin = end;
314 				break;
315 			}
316 		}
317 	}
318 	*num = tmp_num;
319 	*sum = tmp_sum;
320 }
321 
322 
323 /*
324  * To remove notsack'ed blks.
325  *
326  * Parameters:
327  *	notsack_blk_t **head: address of the pointer to the list of notsack'ed
328  *		blks.
329  *	tcp_seq end: to remove all notsack'ed blk with seq num less than end.
330  *	int32_t *num: (referenced) total num of notsack'ed blks.
331  *	uint32_t *sum: (referenced) total num of bytes of all the notsack'ed
332  *		blks.
333  */
334 void
335 tcp_notsack_remove(notsack_blk_t **head, tcp_seq end, int32_t *num,
336     uint32_t *sum)
337 {
338 	notsack_blk_t *prev, *tmp;
339 	uint32_t tmp_sum = *sum;
340 
341 	if (*head == NULL)
342 		return;
343 
344 	prev = NULL;
345 	tmp = *head;
346 	while (tmp != NULL) {
347 		/* There is nothing to discard. */
348 		if (SEQ_GT(tmp->begin, end)) {
349 			break;
350 		}
351 
352 		/* Is the blk completely or partially covered? */
353 		if (SEQ_GEQ(end, tmp->end)) {
354 			(*num)--;
355 			tmp_sum -= tmp->end - tmp->begin;
356 			if (prev == NULL) {
357 				*head = tmp->next;
358 				kmem_free(tmp, sizeof (notsack_blk_t));
359 				tmp = *head;
360 			} else {
361 				prev->next = tmp->next;
362 				kmem_free(tmp, sizeof (notsack_blk_t));
363 				tmp = prev->next;
364 			}
365 		} else {
366 			tmp_sum -= end - tmp->begin;
367 			tmp->begin = end;
368 			break;
369 		}
370 	}
371 	*sum = tmp_sum;
372 }
373 
374 
375 /*
376  * To update the notsack'ed list when new data is sent.
377  *
378  * Assumption: this should only be called when new notsack blk is to be added.
379  *
380  * Parameters:
381  *	notsack_blk_t **head: address of the pointer to the list of notsack'ed
382  *		blks.
383  *	tcp_seq begin: beginning seq num of new data.
384  *	tcp_seq end: ending seq num of new data.
385  *	int32_t *num: (referenced) total num of notsack'ed blks.
386  *	uint32_t *sum: (referenced) total num of bytes of all the notsack'ed
387  *		blks.
388  */
389 void tcp_notsack_update(notsack_blk_t **head, tcp_seq begin, tcp_seq end,
390     int32_t *num, uint32_t *sum)
391 {
392 	notsack_blk_t *tmp;
393 
394 	tmp = *head;
395 	/* If the list is empty, create a new one. */
396 	if (tmp == NULL) {
397 		if ((tmp = kmem_alloc(sizeof (notsack_blk_t), KM_NOSLEEP)) ==
398 		    NULL) {
399 			return;
400 		}
401 		tmp->begin = begin;
402 		tmp->end = end;
403 		tmp->next = NULL;
404 		tmp->sack_cnt = 0;
405 		*head = tmp;
406 		*num = 1;
407 		*sum = end - begin;
408 		return;
409 	}
410 
411 	/*
412 	 * Find the place to add the new blk.  This assumes that new data
413 	 * is being sent, so the place to insert the new notsack blk is at
414 	 * the end of the list.
415 	 */
416 	while (tmp->next != NULL) {
417 		tmp = tmp->next;
418 	}
419 
420 	/* Does the new blk overlap with old one? */
421 	if (SEQ_GEQ(tmp->end, begin)) {
422 		*sum += end - tmp->end;
423 		tmp->end = end;
424 	} else {
425 		/* No.  Need to create a new notsack blk. */
426 		tmp->next = kmem_alloc(sizeof (notsack_blk_t), KM_NOSLEEP);
427 		if (tmp->next != NULL) {
428 			tmp = tmp->next;
429 			tmp->begin = begin;
430 			tmp->end = end;
431 			tmp->next = NULL;
432 			tmp->sack_cnt = 0;
433 			(*num)++;
434 			*sum += end - begin;
435 		}
436 	}
437 }
438