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