lsquic_rechist.c revision b62ec17f
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * lsquic_rechist.c -- History of received packets. 4 */ 5 6#include <assert.h> 7#include <limits.h> 8#include <stddef.h> 9#include <stdint.h> 10#include <stdlib.h> 11#include <string.h> 12 13#include "lsquic_int_types.h" 14#include "lsquic_rechist.h" 15 16 17#define BITS_PER_MASK (sizeof(uintptr_t) * 8) 18 19#if UINTPTR_MAX == 18446744073709551615UL 20#define LOG2_BITS 6 21#else 22#define LOG2_BITS 5 23#endif 24 25 26void 27lsquic_rechist_init (struct lsquic_rechist *rechist, int ietf) 28{ 29 memset(rechist, 0, sizeof(*rechist)); 30 rechist->rh_cutoff = ietf ? 0 : 1; 31} 32 33 34void 35lsquic_rechist_cleanup (lsquic_rechist_t *rechist) 36{ 37 free(rechist->rh_elems); 38 memset(rechist, 0, sizeof(*rechist)); 39} 40 41 42static void 43rechist_free_elem (struct lsquic_rechist *rechist, unsigned idx) 44{ 45 rechist->rh_masks[idx >> LOG2_BITS] &= 46 ~(1ull << (idx & ((1u << LOG2_BITS) - 1))); 47 --rechist->rh_n_used; 48} 49 50 51#define RE_HIGH(el_) ((el_)->re_low + (el_)->re_count - 1) 52 53 54static unsigned 55find_free_slot (uintptr_t slots) 56{ 57#if __GNUC__ 58 if (slots) 59#if UINTPTR_MAX == 18446744073709551615UL 60 return __builtin_ctzll(~slots); 61#else 62 return __builtin_ctzl(~slots); 63#endif 64 else 65 return 0; 66#else 67 unsigned n; 68 69 slots =~ slots; 70 n = 0; 71 72#if UINTPTR_MAX == 18446744073709551615UL 73 if (0 == (slots & ((1ULL << 32) - 1))) { n += 32; slots >>= 32; } 74#endif 75 if (0 == (slots & ((1ULL << 16) - 1))) { n += 16; slots >>= 16; } 76 if (0 == (slots & ((1ULL << 8) - 1))) { n += 8; slots >>= 8; } 77 if (0 == (slots & ((1ULL << 4) - 1))) { n += 4; slots >>= 4; } 78 if (0 == (slots & ((1ULL << 2) - 1))) { n += 2; slots >>= 2; } 79 if (0 == (slots & ((1ULL << 1) - 1))) { n += 1; slots >>= 1; } 80 return n; 81#endif 82} 83 84 85static int 86rechist_grow (struct lsquic_rechist *rechist) 87{ 88 unsigned n_masks, nelems; 89 size_t size; 90 ptrdiff_t moff; 91 char *mem; 92 93 moff = (char *) rechist->rh_masks - (char *) rechist->rh_elems; 94 if (rechist->rh_n_alloced) 95 nelems = rechist->rh_n_alloced * 2; 96 else 97 nelems = 4; 98 n_masks = (nelems + (-nelems & (BITS_PER_MASK - 1))) / BITS_PER_MASK; 99 size = sizeof(struct rechist_elem) * nelems + sizeof(uintptr_t) * n_masks; 100 mem = realloc(rechist->rh_elems, size); 101 if (!mem) 102 return -1; 103 if (moff) 104 memcpy(mem + size - n_masks * sizeof(rechist->rh_masks[0]), 105 (char *) mem + moff, 106 rechist->rh_n_masks * sizeof(rechist->rh_masks[0])); 107 if (rechist->rh_n_masks < n_masks) 108 memset(mem + nelems * sizeof(rechist->rh_elems[0]) 109 + rechist->rh_n_masks * sizeof(rechist->rh_masks[0]), 110 0, (n_masks - rechist->rh_n_masks) * sizeof(rechist->rh_masks[0])); 111 rechist->rh_n_alloced = nelems; 112 rechist->rh_n_masks = n_masks; 113 rechist->rh_elems = (void *) mem; 114 rechist->rh_masks = (void *) (mem + size 115 - n_masks * sizeof(rechist->rh_masks[0])); 116 return 0; 117} 118 119 120static int 121rechist_alloc_elem (struct lsquic_rechist *rechist) 122{ 123 unsigned i, idx; 124 uintptr_t *mask; 125 126 if (rechist->rh_n_used == rechist->rh_n_alloced 127 && 0 != rechist_grow(rechist)) 128 return -1; 129 130 for (mask = rechist->rh_masks; *mask == UINTPTR_MAX; ++mask) 131 ; 132 133 i = mask - rechist->rh_masks; 134 assert(i < rechist->rh_n_masks); 135 136 idx = find_free_slot(*mask); 137 *mask |= 1ull << idx; 138 ++rechist->rh_n_used; 139 return idx + i * BITS_PER_MASK; 140} 141 142 143#if LSQUIC_TEST 144/* When compiled as unit test, run sanity check every 127 operations 145 * (127 is better than 128, as the latter aligns too well with the 146 * regular rechist data structure sizes). 147 */ 148static void 149rechist_test_sanity (const struct lsquic_rechist *rechist) 150{ 151 const struct rechist_elem *el; 152 ptrdiff_t idx; 153 uint64_t *masks; 154 unsigned n_elems; 155 156 masks = calloc(rechist->rh_n_masks, sizeof(masks[0])); 157 158 n_elems = 0; 159 if (rechist->rh_n_used) 160 { 161 el = &rechist->rh_elems[rechist->rh_head]; 162 while (1) 163 { 164 ++n_elems; 165 idx = el - rechist->rh_elems; 166 masks[idx >> LOG2_BITS] |= 1ull << (idx & ((1u << LOG2_BITS) - 1)); 167 if (el->re_next != UINT_MAX) 168 el = &rechist->rh_elems[el->re_next]; 169 else 170 break; 171 } 172 } 173 174 assert(rechist->rh_n_used == n_elems); 175 assert(0 == memcmp(masks, rechist->rh_masks, 176 sizeof(masks[0]) * rechist->rh_n_masks)); 177 free(masks); 178} 179#define rechist_sanity_check(rechist_) do { \ 180 if (0 == ++(rechist_)->rh_n_ops % 127) \ 181 rechist_test_sanity(rechist_); \ 182} while (0) 183#else 184#define rechist_sanity_check(rechist) 185#endif 186 187 188enum received_st 189lsquic_rechist_received (lsquic_rechist_t *rechist, lsquic_packno_t packno, 190 lsquic_time_t now) 191{ 192 struct rechist_elem *el, *prev; 193 ptrdiff_t next_idx, prev_idx; 194 int idx; 195 196 if (rechist->rh_n_alloced == 0) 197 goto first_elem; 198 199 if (packno < rechist->rh_cutoff) 200 return REC_ST_DUP; 201 202 el = &rechist->rh_elems[rechist->rh_head]; 203 prev = NULL; 204 205 if (packno > RE_HIGH(el)) 206 rechist->rh_largest_acked_received = now; 207 208 while (1) 209 { 210 if (packno > RE_HIGH(el) + 1) 211 goto insert_before; 212 if (packno == el->re_low - 1) 213 { 214 --el->re_low; 215 ++el->re_count; 216 if (el->re_next != UINT_MAX 217 && el->re_low == RE_HIGH(&rechist->rh_elems[el->re_next]) + 1) 218 { 219 rechist_free_elem(rechist, el->re_next); 220 el->re_count += rechist->rh_elems[el->re_next].re_count; 221 el->re_low = rechist->rh_elems[el->re_next].re_low; 222 el->re_next = rechist->rh_elems[el->re_next].re_next; 223 } 224 rechist_sanity_check(rechist); 225 return REC_ST_OK; 226 } 227 if (packno == RE_HIGH(el) + 1) 228 { 229 ++el->re_count; 230 rechist_sanity_check(rechist); 231 return REC_ST_OK; 232 } 233 if (packno >= el->re_low && packno <= RE_HIGH(el)) 234 return REC_ST_DUP; 235 if (el->re_next == UINT_MAX) 236 break; /* insert tail */ 237 prev = el; 238 el = &rechist->rh_elems[el->re_next]; 239 } 240 241 prev_idx = el - rechist->rh_elems; 242 idx = rechist_alloc_elem(rechist); 243 if (idx < 0) 244 return REC_ST_ERR; 245 246 rechist->rh_elems[idx].re_low = packno; 247 rechist->rh_elems[idx].re_count = 1; 248 rechist->rh_elems[idx].re_next = UINT_MAX; 249 rechist->rh_elems[prev_idx].re_next = idx; 250 rechist_sanity_check(rechist); 251 return REC_ST_OK; 252 253 first_elem: 254 if (packno < rechist->rh_cutoff) 255 return REC_ST_ERR; 256 idx = rechist_alloc_elem(rechist); 257 if (idx < 0) 258 return REC_ST_ERR; 259 260 rechist->rh_elems[idx].re_low = packno; 261 rechist->rh_elems[idx].re_count = 1; 262 rechist->rh_elems[idx].re_next = UINT_MAX; 263 rechist->rh_head = idx; 264 rechist->rh_largest_acked_received = now; 265 rechist_sanity_check(rechist); 266 return REC_ST_OK; 267 268 insert_before: 269 prev_idx = prev - rechist->rh_elems; 270 next_idx = el - rechist->rh_elems; 271 idx = rechist_alloc_elem(rechist); 272 if (idx < 0) 273 return REC_ST_ERR; 274 275 rechist->rh_elems[idx].re_low = packno; 276 rechist->rh_elems[idx].re_count = 1; 277 rechist->rh_elems[idx].re_next = next_idx; 278 if (next_idx == rechist->rh_head) 279 rechist->rh_head = idx; 280 else 281 rechist->rh_elems[prev_idx].re_next = idx; 282 283 rechist_sanity_check(rechist); 284 return REC_ST_OK; 285} 286 287 288void 289lsquic_rechist_stop_wait (lsquic_rechist_t *rechist, lsquic_packno_t cutoff) 290{ 291 struct rechist_elem *el, *prev; 292 293 if (rechist->rh_flags & RH_CUTOFF_SET) 294 { 295 assert(cutoff >= rechist->rh_cutoff); /* Check performed in full_conn */ 296 if (cutoff <= rechist->rh_cutoff) 297 return; 298 } 299 300 rechist->rh_cutoff = cutoff; 301 rechist->rh_flags |= RH_CUTOFF_SET; 302 303 if (rechist->rh_n_used == 0) 304 return; 305 306 el = &rechist->rh_elems[rechist->rh_head]; 307 prev = NULL; 308 while (1) 309 { 310 if (cutoff > RE_HIGH(el)) 311 { 312 if (prev) 313 prev->re_next = UINT_MAX; 314 break; 315 } 316 else if (cutoff > el->re_low) 317 { 318 el->re_count = RE_HIGH(el) - cutoff + 1; 319 el->re_low = cutoff; 320 if (el->re_next != UINT_MAX) 321 { 322 prev = el; 323 el = &rechist->rh_elems[el->re_next]; 324 prev->re_next = UINT_MAX; 325 break; 326 } 327 else 328 goto end; 329 } 330 else if (el->re_next == UINT_MAX) 331 goto end; 332 prev = el; 333 el = &rechist->rh_elems[el->re_next]; 334 } 335 336 assert(el); 337 while (1) 338 { 339 rechist_free_elem(rechist, el - rechist->rh_elems); 340 if (el->re_next != UINT_MAX) 341 el = &rechist->rh_elems[el->re_next]; 342 else 343 break; 344 } 345 346 end: 347 rechist_sanity_check(rechist); 348} 349 350 351lsquic_packno_t 352lsquic_rechist_largest_packno (const lsquic_rechist_t *rechist) 353{ 354 if (rechist->rh_n_used) 355 return RE_HIGH(&rechist->rh_elems[rechist->rh_head]); 356 else 357 return 0; /* Don't call this function if history is empty */ 358} 359 360 361lsquic_packno_t 362lsquic_rechist_cutoff (const lsquic_rechist_t *rechist) 363{ 364 if (rechist->rh_flags & RH_CUTOFF_SET) 365 return rechist->rh_cutoff; 366 else 367 return 0; 368} 369 370 371lsquic_time_t 372lsquic_rechist_largest_recv (const lsquic_rechist_t *rechist) 373{ 374 return rechist->rh_largest_acked_received; 375} 376 377 378const struct lsquic_packno_range * 379lsquic_rechist_first (lsquic_rechist_t *rechist) 380{ 381 unsigned idx; 382 383 if (rechist->rh_n_used) 384 { 385 idx = rechist->rh_head; 386 rechist->rh_iter.range.low = rechist->rh_elems[idx].re_low; 387 rechist->rh_iter.range.high = RE_HIGH(&rechist->rh_elems[idx]); 388 rechist->rh_iter.next = rechist->rh_elems[idx].re_next; 389 return &rechist->rh_iter.range; 390 } 391 else 392 return NULL; 393} 394 395 396const struct lsquic_packno_range * 397lsquic_rechist_next (lsquic_rechist_t *rechist) 398{ 399 unsigned idx; 400 401 idx = rechist->rh_iter.next; 402 if (idx != UINT_MAX) 403 { 404 rechist->rh_iter.range.low = rechist->rh_elems[idx].re_low; 405 rechist->rh_iter.range.high = RE_HIGH(&rechist->rh_elems[idx]); 406 rechist->rh_iter.next = rechist->rh_elems[idx].re_next; 407 return &rechist->rh_iter.range; 408 } 409 else 410 return NULL; 411} 412 413 414size_t 415lsquic_rechist_mem_used (const struct lsquic_rechist *rechist) 416{ 417 return sizeof(*rechist) 418 + rechist->rh_n_alloced * sizeof(rechist->rh_elems[0]) 419 + rechist->rh_n_masks * sizeof(rechist->rh_masks[0]); 420} 421 422 423const struct lsquic_packno_range * 424lsquic_rechist_peek (struct lsquic_rechist *rechist) 425{ 426 if (rechist->rh_n_used) 427 { 428 rechist->rh_iter.range.low 429 = rechist->rh_elems[rechist->rh_head].re_low; 430 rechist->rh_iter.range.high 431 = RE_HIGH(&rechist->rh_elems[rechist->rh_head]); 432 return &rechist->rh_iter.range; 433 } 434 else 435 return NULL; 436} 437