/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include /* kmem cache for notsack_blk_t */ kmem_cache_t *tcp_notsack_blk_cache; /* * To insert a new blk to the array of SACK blk in receiver. * * Parameters: * sack_blk_t *head: pointer to the array of SACK blks. * tcp_seq begin: starting seq num of the new blk. * tcp_seq end: ending seq num of the new blk. * int32_t *num: (referenced) total num of SACK blks on the list. */ void tcp_sack_insert(sack_blk_t *head, tcp_seq begin, tcp_seq end, int32_t *num) { int32_t i, j, old_num, new_num; sack_blk_t tmp[MAX_SACK_BLK - 1]; /* The array is empty, just add the new one. */ if (*num == 0) { head[0].begin = begin; head[0].end = end; *num = 1; return; } /* * Check for overlap. There are five cases. * * 1. there is no overlap with any other SACK blks. * 2. new SACK blk is completely contained in another blk. * 3. tail part of new SACK blk overlaps with another blk. * 4. head part of new SACK blk overlaps with another blk. * 5. new SACK blk completely contains another blk. * * Use tmp to hold old SACK blks. After the loop, copy them back * to head. */ old_num = *num; if (old_num > MAX_SACK_BLK - 1) { old_num = MAX_SACK_BLK - 1; } new_num = old_num; j = 0; for (i = 0; i < old_num; i++) { if (SEQ_LT(end, head[i].begin) || SEQ_GT(begin, head[i].end)) { /* Case 1: continue to check. */ tmp[j].begin = head[i].begin; tmp[j].end = head[i].end; j++; continue; } else if (SEQ_GEQ(begin, head[i].begin) && SEQ_LEQ(end, head[i].end)) { /* Case 2: re-insert the old blk to the head. */ begin = head[i].begin; end = head[i].end; } else if (SEQ_LEQ(end, head[i].end) && SEQ_GEQ(end, head[i].begin)) { /* * Case 3: Extend the new blk, remove the old one * and continue to check. */ end = head[i].end; } else if (SEQ_GEQ(begin, head[i].begin) && SEQ_LEQ(begin, head[i].end)) { /* Case 4 */ begin = head[i].begin; } /* * Common code for all cases except the first one, which * copies the original SACK blk into the tmp storage. Other * cases remove the original SACK blk by not copying into * tmp storage. */ new_num--; } head[0].begin = begin; head[0].end = end; for (i = 0; i < new_num; i++) { head[i+1].begin = tmp[i].begin; head[i+1].end = tmp[i].end; } *num = new_num + 1; } /* * To remove a SACK block. * * Parameters: * sack_blk_t *head: pointer to the array of SACK blks. * tcp_seq end: to remove all sack blk with seq num less than end. * int32_t *num: (referenced) total num of SACK blks in the array. */ void tcp_sack_remove(sack_blk_t *head, tcp_seq end, int32_t *num) { sack_blk_t tmp[MAX_SACK_BLK]; int32_t i, j, old_num, new_num; if (*num == 0) return; old_num = *num; new_num = old_num; j = 0; /* Walk thru the whole list and copy the new list to tmp[]. */ for (i = 0; i < old_num; i++) { if (SEQ_GT(end, head[i].begin)) { /* * Check to see if the old SACK blk needs to be * removed or updated. If the old blk is just * partially covered, update begin and continue. * If the old blk is completely covered, remove it * and continue to check. */ if (SEQ_GEQ(end, head[i].end)) { new_num--; continue; } else { tmp[j].begin = end; tmp[j].end = head[i].end; } } else { tmp[j].begin = head[i].begin; tmp[j].end = head[i].end; } j++; } /* Copy tmp[] back to the original list. */ for (i = 0; i < new_num; i++) { head[i].begin = tmp[i].begin; head[i].end = tmp[i].end; } *num = new_num; } /* * Use the SACK info to insert a "notsack'ed" blk. The notsack'ed blk list * contains the list of blks which have not been selectively acknowledged * by the receiver. The SACK info is a blk which is being selectively * acknowledged by the receiver. * * Parameters: * notsack_blk_t **head: address of the pointer to the list of notsack'ed * blks. * tcp_seq begin: starting seq num of the SACK info. * tcp_seq end: ending seq num of the SACK info. * int32_t *num: (referenced) total num of notsack'ed blk on the list. * uint32_t *sum: (referenced) total num of bytes of all the notsack'ed * blks. */ void tcp_notsack_insert(notsack_blk_t **head, tcp_seq begin, tcp_seq end, int32_t *num, uint32_t *sum) { notsack_blk_t *prev, *tmp, *new; uint32_t tmp_sum, tmp_num; if (*head == NULL) { return; } tmp = *head; prev = NULL; /* Find the right place of updating the list. */ while ((tmp != NULL) && SEQ_LEQ(tmp->end, begin)) { prev = tmp; (tmp->sack_cnt)++; tmp = tmp->next; } /* * This can happen only when TCP sends new data but the notsack list * is not updated. */ if (tmp == NULL) { return; } /* * This means the new SACK info covers something that is not on * the list anymore. */ if (SEQ_LEQ(end, tmp->begin)) { return; } /* The SACK info covers up to this blk. So just check for this blk. */ if (SEQ_LEQ(end, tmp->end)) { /* * Only this notsack'ed blk is completely covered. Delete * it and return. */ if (end == tmp->end && SEQ_LEQ(begin, tmp->begin)) { if (prev != NULL) { prev->next = tmp->next; } else { *head = tmp->next; } (*num)--; *sum -= tmp->end - tmp->begin; kmem_cache_free(tcp_notsack_blk_cache, tmp); return; } /* This blk is partially covered. */ if (SEQ_GEQ(begin, tmp->begin)) { /* Check what needs to be updated. */ if (begin == tmp->begin) { *sum -= end - tmp->begin; tmp->begin = end; } else if (end == tmp->end) { *sum -= tmp->end - begin; tmp->end = begin; (tmp->sack_cnt)++; } else { /* Split the notsack blk. */ if ((new = kmem_cache_alloc( tcp_notsack_blk_cache, KM_NOSLEEP)) == NULL) { return; } new->end = tmp->end; new->begin = end; new->next = tmp->next; new->sack_cnt = 0; tmp->end = begin; tmp->next = new; (tmp->sack_cnt)++; (*num)++; *sum -= end - begin; } } else { *sum -= end - tmp->begin; tmp->begin = end; } return; } /* Need to check for coverage of this blk and later blks. */ tmp_sum = *sum; tmp_num = *num; if (SEQ_LT(tmp->begin, begin)) { tmp_sum -= tmp->end - begin; tmp->end = begin; (tmp->sack_cnt)++; prev = tmp; tmp = tmp->next; } while (tmp != NULL) { /* The coverage stops here. */ if (SEQ_GT(tmp->begin, end)) { break; } else { /* Is the blk completely or partially covered? */ if (SEQ_LEQ(tmp->end, end)) { tmp_num--; tmp_sum -= tmp->end - tmp->begin; if (prev != NULL) { prev->next = tmp->next; kmem_cache_free(tcp_notsack_blk_cache, tmp); tmp = prev->next; } else { *head = tmp->next; kmem_cache_free(tcp_notsack_blk_cache, tmp); tmp = *head; } } else { /* * This blk is partially covered. It also * means it should be the end of coverage. */ tmp_sum -= end - tmp->begin; tmp->begin = end; break; } } } *num = tmp_num; *sum = tmp_sum; } /* * To remove notsack'ed blks. * * Parameters: * notsack_blk_t **head: address of the pointer to the list of notsack'ed * blks. * tcp_seq end: to remove all notsack'ed blk with seq num less than end. * int32_t *num: (referenced) total num of notsack'ed blks. * uint32_t *sum: (referenced) total num of bytes of all the notsack'ed * blks. */ void tcp_notsack_remove(notsack_blk_t **head, tcp_seq end, int32_t *num, uint32_t *sum) { notsack_blk_t *prev, *tmp; uint32_t tmp_sum = *sum; if (*head == NULL) return; prev = NULL; tmp = *head; while (tmp != NULL) { /* There is nothing to discard. */ if (SEQ_GT(tmp->begin, end)) { break; } /* Is the blk completely or partially covered? */ if (SEQ_GEQ(end, tmp->end)) { (*num)--; tmp_sum -= tmp->end - tmp->begin; if (prev == NULL) { *head = tmp->next; kmem_cache_free(tcp_notsack_blk_cache, tmp); tmp = *head; } else { prev->next = tmp->next; kmem_cache_free(tcp_notsack_blk_cache, tmp); tmp = prev->next; } } else { tmp_sum -= end - tmp->begin; tmp->begin = end; break; } } *sum = tmp_sum; } /* * To update the notsack'ed list when new data is sent. * * Assumption: this should only be called when new notsack blk is to be added. * * Parameters: * notsack_blk_t **head: address of the pointer to the list of notsack'ed * blks. * tcp_seq begin: beginning seq num of new data. * tcp_seq end: ending seq num of new data. * int32_t *num: (referenced) total num of notsack'ed blks. * uint32_t *sum: (referenced) total num of bytes of all the notsack'ed * blks. */ void tcp_notsack_update(notsack_blk_t **head, tcp_seq begin, tcp_seq end, int32_t *num, uint32_t *sum) { notsack_blk_t *tmp; tmp = *head; /* If the list is empty, create a new one. */ if (tmp == NULL) { if ((tmp = kmem_cache_alloc(tcp_notsack_blk_cache, KM_NOSLEEP)) == NULL) { return; } tmp->begin = begin; tmp->end = end; tmp->next = NULL; tmp->sack_cnt = 0; *head = tmp; *num = 1; *sum = end - begin; return; } /* * Find the place to add the new blk. This assumes that new data * is being sent, so the place to insert the new notsack blk is at * the end of the list. */ while (tmp->next != NULL) { tmp = tmp->next; } /* Does the new blk overlap with old one? */ if (SEQ_GEQ(tmp->end, begin)) { *sum += end - tmp->end; tmp->end = end; } else { /* No. Need to create a new notsack blk. */ tmp->next = kmem_cache_alloc(tcp_notsack_blk_cache, KM_NOSLEEP); if (tmp->next != NULL) { tmp = tmp->next; tmp->begin = begin; tmp->end = end; tmp->next = NULL; tmp->sack_cnt = 0; (*num)++; *sum += end - begin; } } }