lsquic_spi.c revision 461e84d8
1/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * lsquic_spi.c - implementation of Stream Priority Iterator. 4 */ 5 6#include <assert.h> 7#include <inttypes.h> 8#include <stdint.h> 9#include <stdlib.h> 10#include <string.h> 11#include <sys/queue.h> 12#include <sys/types.h> 13#ifdef WIN32 14#include <vc_compat.h> 15#endif 16 17#include "lsquic_types.h" 18#include "lsquic_int_types.h" 19#include "lsquic_sfcw.h" 20#include "lsquic_stream.h" 21#include "lsquic_spi.h" 22 23#define LSQUIC_LOGGER_MODULE LSQLM_SPI 24#define LSQUIC_LOG_CONN_ID iter->spi_cid 25#include "lsquic_logger.h" 26 27#define SPI_DEBUG(fmt, ...) LSQ_DEBUG("%s: " fmt, iter->spi_name, __VA_ARGS__) 28 29#define NEXT_STREAM(stream, off) \ 30 (* (struct lsquic_stream **) ((unsigned char *) (stream) + (off))) 31 32 33static void 34add_stream_to_spi (struct stream_prio_iter *iter, lsquic_stream_t *stream) 35{ 36 unsigned set, bit; 37 set = stream->sm_priority >> 6; 38 bit = stream->sm_priority & 0x3F; 39 if (!(iter->spi_set[set] & (1ULL << bit))) 40 { 41 iter->spi_set[set] |= 1ULL << bit; 42 TAILQ_INIT(&iter->spi_streams[ stream->sm_priority ]); 43 } 44 TAILQ_INSERT_TAIL(&iter->spi_streams[ stream->sm_priority ], 45 stream, next_prio_stream); 46} 47 48 49void 50lsquic_spi_init (struct stream_prio_iter *iter, struct lsquic_stream *first, 51 struct lsquic_stream *last, uintptr_t next_ptr_offset, 52 enum stream_flags onlist_mask, lsquic_cid_t cid, const char *name) 53{ 54 struct lsquic_stream *stream; 55 unsigned count; 56 57 iter->spi_cid = cid; 58 iter->spi_name = name ? name : "UNSET"; 59 iter->spi_set[0] = 0; 60 iter->spi_set[1] = 0; 61 iter->spi_set[2] = 0; 62 iter->spi_set[3] = 0; 63 iter->spi_onlist_mask = onlist_mask; 64 iter->spi_cur_prio = 0; 65 iter->spi_prev_stream = NULL; 66 iter->spi_next_stream = NULL; 67 68 stream = first; 69 count = 0; 70 71 while (1) 72 { 73 add_stream_to_spi(iter, stream); 74 ++count; 75 if (stream == last) 76 break; 77 stream = NEXT_STREAM(stream, next_ptr_offset); 78 } 79 80 if (count > 2) 81 SPI_DEBUG("initialized; # elems: %u; sets: [ %016"PRIX64", %016"PRIX64 82 ", %016"PRIX64", %016"PRIX64" ]", count, iter->spi_set[0], 83 iter->spi_set[1], iter->spi_set[2], iter->spi_set[3]); 84} 85 86 87static int 88find_and_set_lowest_priority (struct stream_prio_iter *iter) 89{ 90 unsigned set, prio; 91 uint64_t mask; 92 93 for (set = 0, prio = 0; set < 4; ++set, prio += 64) 94 if (iter->spi_set[ set ]) 95 break; 96 97 if (set == 4) 98 { 99 //SPI_DEBUG("%s: cannot find any", __func__); 100 return -1; 101 } 102 103 mask = iter->spi_set[set]; 104 if (!(mask & ((1ULL << 32) - 1))) { prio += 32; mask >>= 32; } 105 if (!(mask & ((1ULL << 16) - 1))) { prio += 16; mask >>= 16; } 106 if (!(mask & ((1ULL << 8) - 1))) { prio += 8; mask >>= 8; } 107 if (!(mask & ((1ULL << 4) - 1))) { prio += 4; mask >>= 4; } 108 if (!(mask & ((1ULL << 2) - 1))) { prio += 2; mask >>= 2; } 109 if (!(mask & ((1ULL << 1) - 1))) { prio += 1; } 110 111#ifndef NDEBUG 112 unsigned bit; 113 set = prio >> 6; 114 bit = prio & 0x3F; 115 assert(iter->spi_set[ set ] & (1ULL << bit)); 116#endif 117 118 SPI_DEBUG("%s: prio %u -> %u", __func__, iter->spi_cur_prio, prio); 119 iter->spi_cur_prio = prio; 120 return 0; 121} 122 123 124static int 125find_and_set_next_priority (struct stream_prio_iter *iter) 126{ 127 unsigned set, bit, prio; 128 uint64_t mask; 129 130 /* Examine values in the same set first */ 131 set = iter->spi_cur_prio >> 6; 132 bit = iter->spi_cur_prio & 0x3F; 133 prio = 64 * set; 134 135 if (bit < 63) 136 { 137 mask = iter->spi_set[set]; 138 mask &= ~((1ULL << (bit + 1)) - 1); 139 if (mask) 140 goto calc_priority; 141 } 142 143 ++set; 144 prio += 64; 145 for (; set < 4; ++set, prio += 64) 146 if (iter->spi_set[ set ]) 147 break; 148 149 if (set == 4) 150 { 151 //SPI_DEBUG("%s: cannot find any", __func__); 152 return -1; 153 } 154 155 mask = iter->spi_set[set]; 156 157 calc_priority: 158 if (!(mask & ((1ULL << 32) - 1))) { prio += 32; mask >>= 32; } 159 if (!(mask & ((1ULL << 16) - 1))) { prio += 16; mask >>= 16; } 160 if (!(mask & ((1ULL << 8) - 1))) { prio += 8; mask >>= 8; } 161 if (!(mask & ((1ULL << 4) - 1))) { prio += 4; mask >>= 4; } 162 if (!(mask & ((1ULL << 2) - 1))) { prio += 2; mask >>= 2; } 163 if (!(mask & ((1ULL << 1) - 1))) { prio += 1; } 164 165#ifndef NDEBUG 166 set = prio >> 6; 167 bit = prio & 0x3F; 168 assert(iter->spi_set[ set ] & (1ULL << bit)); 169#endif 170 171 SPI_DEBUG("%s: prio %u -> %u", __func__, iter->spi_cur_prio, prio); 172 iter->spi_cur_prio = prio; 173 return 0; 174} 175 176 177/* Each stream returned by the iterator is processed in some fashion. If, 178 * as a result of this, the stream gets taken off the original list, we 179 * have to follow suit and remove it from the iterator's set of streams. 180 */ 181static void 182maybe_evict_prev (struct stream_prio_iter *iter) 183{ 184 unsigned set, bit; 185 186 if (0 == (iter->spi_prev_stream->stream_flags & iter->spi_onlist_mask)) 187 { 188 SPI_DEBUG("evict stream %u", iter->spi_prev_stream->id); 189 TAILQ_REMOVE(&iter->spi_streams[ iter->spi_prev_prio ], 190 iter->spi_prev_stream, next_prio_stream); 191 if (TAILQ_EMPTY(&iter->spi_streams[ iter->spi_prev_prio ])) 192 { 193 set = iter->spi_prev_prio >> 6; 194 bit = iter->spi_prev_prio & 0x3F; 195 iter->spi_set[ set ] &= ~(1ULL << bit); 196 SPI_DEBUG("priority %u now has no elements", iter->spi_prev_prio); 197 } 198 iter->spi_prev_stream = NULL; 199 } 200} 201 202 203lsquic_stream_t * 204lsquic_spi_first (struct stream_prio_iter *iter) 205{ 206 lsquic_stream_t *stream; 207 unsigned set, bit; 208 209 if (iter->spi_prev_stream) 210 maybe_evict_prev(iter); 211 212 iter->spi_cur_prio = 0; 213 set = iter->spi_cur_prio >> 6; 214 bit = iter->spi_cur_prio & 0x3F; 215 216 if (!(iter->spi_set[set] & (1ULL << bit))) 217 { 218 if (0 != find_and_set_lowest_priority(iter)) 219 { 220 SPI_DEBUG("%s: return NULL", __func__); 221 return NULL; 222 } 223 } 224 225 stream = TAILQ_FIRST(&iter->spi_streams[ iter->spi_cur_prio ]); 226 iter->spi_prev_prio = iter->spi_cur_prio; 227 iter->spi_prev_stream = stream; 228 iter->spi_next_stream = TAILQ_NEXT(stream, next_prio_stream); 229 if (stream->id != 1 && stream->id != 3) 230 SPI_DEBUG("%s: return stream %u, priority %u", __func__, stream->id, 231 iter->spi_cur_prio); 232 return stream; 233} 234 235 236lsquic_stream_t * 237lsquic_spi_next (struct stream_prio_iter *iter) 238{ 239 lsquic_stream_t *stream; 240 241 if (iter->spi_prev_stream) 242 maybe_evict_prev(iter); 243 244 stream = iter->spi_next_stream; 245 if (stream) 246 { 247 assert(iter->spi_prev_prio == iter->spi_cur_prio); 248 iter->spi_prev_stream = stream; 249 iter->spi_next_stream = TAILQ_NEXT(stream, next_prio_stream); 250 if (stream->id != 1 && stream->id != 3) 251 SPI_DEBUG("%s: return stream %u, priority %u", __func__, stream->id, 252 iter->spi_cur_prio); 253 return stream; 254 } 255 256 if (0 != find_and_set_next_priority(iter)) 257 { 258 //SPI_DEBUG("%s: return NULL", __func__); 259 return NULL; 260 } 261 262 stream = TAILQ_FIRST(&iter->spi_streams[ iter->spi_cur_prio ]); 263 iter->spi_prev_prio = iter->spi_cur_prio; 264 iter->spi_prev_stream = stream; 265 iter->spi_next_stream = TAILQ_NEXT(stream, next_prio_stream); 266 267 if (!lsquic_stream_is_critical(stream)) 268 SPI_DEBUG("%s: return stream %u, priority %u", __func__, stream->id, 269 iter->spi_cur_prio); 270 return stream; 271} 272 273 274static int 275have_non_critical_streams (const struct stream_prio_iter *iter) 276{ 277 const struct lsquic_stream *stream; 278 TAILQ_FOREACH(stream, &iter->spi_streams[ iter->spi_cur_prio ], 279 next_prio_stream) 280 if (!lsquic_stream_is_critical(stream)) 281 return 1; 282 return 0; 283} 284 285 286static void 287spi_drop_high_or_non_high (struct stream_prio_iter *iter, int drop_high) 288{ 289 uint64_t new_set[ sizeof(iter->spi_set) / sizeof(iter->spi_set[0]) ]; 290 unsigned bit, set, n; 291 292 memset(new_set, 0, sizeof(new_set)); 293 294 find_and_set_lowest_priority(iter); 295 set = iter->spi_cur_prio >> 6; 296 bit = iter->spi_cur_prio & 0x3F; 297 new_set[set] |= 1ULL << bit; 298 299 if (!have_non_critical_streams(iter)) 300 { 301 ++iter->spi_cur_prio; 302 find_and_set_lowest_priority(iter); 303 set = iter->spi_cur_prio >> 6; 304 bit = iter->spi_cur_prio & 0x3F; 305 new_set[set] |= 1ULL << bit; 306 } 307 308 for (n = 0; n < sizeof(new_set) / sizeof(new_set[0]); ++n) 309 if (drop_high) 310 iter->spi_set[n] &= ~new_set[n]; 311 else 312 iter->spi_set[n] = new_set[n]; 313} 314 315 316void 317lsquic_spi_drop_high (struct stream_prio_iter *iter) 318{ 319 spi_drop_high_or_non_high(iter, 1); 320} 321 322 323void 324lsquic_spi_drop_non_high (struct stream_prio_iter *iter) 325{ 326 spi_drop_high_or_non_high(iter, 0); 327} 328