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