Coverage Report

Created: 2025-07-01 06:51

/src/openvswitch/lib/pvector.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2014, 2016 Nicira, Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at:
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include <config.h>
18
#include "pvector.h"
19
20
/* Writers will preallocate space for some entries at the end to avoid future
21
 * reallocations. */
22
enum { PVECTOR_EXTRA_ALLOC = 4 };
23
24
static struct pvector_impl *
25
pvector_impl_get(const struct pvector *pvec)
26
0
{
27
0
    return ovsrcu_get(struct pvector_impl *, &pvec->impl);
28
0
}
29
30
static struct pvector_impl *
31
pvector_impl_alloc(size_t size)
32
0
{
33
0
    struct pvector_impl *impl;
34
35
0
    impl = xmalloc(sizeof *impl + size * sizeof impl->vector[0]);
36
0
    atomic_init(&impl->size, 0);
37
0
    impl->allocated = size;
38
39
0
    return impl;
40
0
}
41
42
static struct pvector_impl *
43
pvector_impl_dup(struct pvector_impl *old)
44
0
{
45
0
    struct pvector_impl *impl;
46
0
    size_t alloc = old->size + PVECTOR_EXTRA_ALLOC;
47
48
0
    impl = xmalloc(sizeof *impl + alloc * sizeof impl->vector[0]);
49
0
    impl->allocated = alloc;
50
0
    impl->size = old->size;
51
0
    memcpy(impl->vector, old->vector, old->size * sizeof old->vector[0]);
52
0
    return impl;
53
0
}
54
55
/* Initializes 'pvec' as an empty concurrent priority vector. */
56
void
57
pvector_init(struct pvector *pvec)
58
0
{
59
0
    ovsrcu_set(&pvec->impl, pvector_impl_alloc(PVECTOR_EXTRA_ALLOC));
60
0
    pvec->temp = NULL;
61
0
}
62
63
/* Destroys 'pvec'.
64
 *
65
 * The client is responsible for destroying any data previously held in
66
 * 'pvec'. */
67
void
68
pvector_destroy(struct pvector *pvec)
69
0
{
70
0
    free(pvec->temp);
71
0
    pvec->temp = NULL;
72
0
    ovsrcu_postpone(free, pvector_impl_get(pvec));
73
0
    ovsrcu_set(&pvec->impl, NULL); /* Poison. */
74
0
}
75
76
/* Iterators for callers that need the 'index' afterward. */
77
#define PVECTOR_IMPL_FOR_EACH(ENTRY, INDEX, IMPL)          \
78
0
    for ((INDEX) = 0;                                      \
79
0
         (INDEX) < (IMPL)->size                            \
80
0
             && ((ENTRY) = &(IMPL)->vector[INDEX], true);  \
81
0
         (INDEX)++)
82
83
static int
84
pvector_entry_cmp(const void *a_, const void *b_)
85
0
{
86
0
    const struct pvector_entry *ap = a_;
87
0
    const struct pvector_entry *bp = b_;
88
0
    int a = ap->priority;
89
0
    int b = bp->priority;
90
91
0
    return a > b ? -1 : a < b;
92
0
}
93
94
static void
95
pvector_impl_sort(struct pvector_impl *impl)
96
0
{
97
0
    qsort(impl->vector, impl->size, sizeof *impl->vector, pvector_entry_cmp);
98
0
}
99
100
/* Returns the index of the 'ptr' in the vector, or -1 if none is found. */
101
static int
102
pvector_impl_find(struct pvector_impl *impl, void *target)
103
0
{
104
0
    const struct pvector_entry *entry;
105
0
    int index;
106
107
0
    PVECTOR_IMPL_FOR_EACH (entry, index, impl) {
108
0
        if (entry->ptr == target) {
109
0
            return index;
110
0
        }
111
0
    }
112
0
    return -1;
113
0
}
114
115
void
116
pvector_insert(struct pvector *pvec, void *ptr, int priority)
117
0
{
118
0
    struct pvector_impl *temp = pvec->temp;
119
0
    struct pvector_impl *old = pvector_impl_get(pvec);
120
0
    size_t size;
121
122
0
    ovs_assert(ptr != NULL);
123
124
    /* There is no possible concurrent writer. Insertions must be protected
125
     * by mutex or be always excuted from the same thread. */
126
0
    atomic_read_relaxed(&old->size, &size);
127
128
    /* Check if can add to the end without reallocation. */
129
0
    if (!temp && old->allocated > size &&
130
0
        (!size || priority <= old->vector[size - 1].priority)) {
131
0
        old->vector[size].ptr = ptr;
132
0
        old->vector[size].priority = priority;
133
        /* Size increment must not be visible to the readers before the new
134
         * entry is stored. */
135
0
        atomic_store_explicit(&old->size, size + 1, memory_order_release);
136
0
    } else {
137
0
        if (!temp) {
138
0
            temp = pvector_impl_dup(old);
139
0
            pvec->temp = temp;
140
0
        } else if (temp->size == temp->allocated) {
141
0
            temp = pvector_impl_dup(temp);
142
0
            free(pvec->temp);
143
0
            pvec->temp = temp;
144
0
        }
145
        /* Insert at the end, publish will sort. */
146
0
        temp->vector[temp->size].ptr = ptr;
147
0
        temp->vector[temp->size].priority = priority;
148
0
        temp->size += 1;
149
0
    }
150
0
}
151
152
void
153
pvector_remove(struct pvector *pvec, void *ptr)
154
0
{
155
0
    struct pvector_impl *temp = pvec->temp;
156
0
    int index;
157
158
0
    if (!temp) {
159
0
        temp = pvector_impl_dup(pvector_impl_get(pvec));
160
0
        pvec->temp = temp;
161
0
    }
162
0
    ovs_assert(temp->size > 0);
163
0
    index = pvector_impl_find(temp, ptr);
164
0
    ovs_assert(index >= 0);
165
    /* Now at the index of the entry to be deleted.
166
     * Swap another entry in if needed, publish will sort anyway. */
167
0
    temp->size--;
168
0
    if (index != temp->size) {
169
0
        temp->vector[index] = temp->vector[temp->size];
170
0
    }
171
0
}
172
173
/* Change entry's 'priority' and keep the vector ordered. */
174
void
175
pvector_change_priority(struct pvector *pvec, void *ptr, int priority)
176
0
{
177
0
    struct pvector_impl *old = pvec->temp;
178
0
    int index;
179
180
0
    if (!old) {
181
0
        old = pvector_impl_get(pvec);
182
0
    }
183
184
0
    index = pvector_impl_find(old, ptr);
185
186
0
    ovs_assert(index >= 0);
187
    /* Now at the index of the entry to be updated. */
188
189
    /* Check if can not update in place. */
190
0
    if ((priority > old->vector[index].priority && index > 0
191
0
         && priority > old->vector[index - 1].priority)
192
0
        || (priority < old->vector[index].priority && index < old->size - 1
193
0
            && priority < old->vector[index + 1].priority)) {
194
        /* Have to use a temp. */
195
0
        if (!pvec->temp) {
196
            /* Have to reallocate to reorder. */
197
0
            pvec->temp = pvector_impl_dup(old);
198
0
            old = pvec->temp;
199
            /* Publish will sort. */
200
0
        }
201
0
    }
202
0
    old->vector[index].priority = priority;
203
0
}
204
205
/* Make the modified pvector available for iteration. */
206
void pvector_publish__(struct pvector *pvec)
207
0
{
208
0
    struct pvector_impl *temp = pvec->temp;
209
210
0
    pvec->temp = NULL;
211
0
    pvector_impl_sort(temp); /* Also removes gaps. */
212
0
    ovsrcu_postpone(free, ovsrcu_get_protected(struct pvector_impl *,
213
0
                                               &pvec->impl));
214
0
    ovsrcu_set(&pvec->impl, temp);
215
0
}