Coverage Report

Created: 2025-07-01 06:50

/src/openvswitch/lib/cooperative-multitasking.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2024 Canonical Ltd.
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
19
#include "backtrace.h"
20
#include "cooperative-multitasking-private.h"
21
#include "cooperative-multitasking.h"
22
#include "hash.h"
23
#include "openvswitch/hmap.h"
24
#include "openvswitch/vlog.h"
25
#include "timeval.h"
26
27
VLOG_DEFINE_THIS_MODULE(cooperative_multitasking);
28
29
struct hmap cooperative_multitasking_callbacks = HMAP_INITIALIZER(
30
    &cooperative_multitasking_callbacks);
31
32
/* Free any data allocated by calls to cooperative_multitasking_set(). */
33
void
34
cooperative_multitasking_destroy(void)
35
0
{
36
0
    struct cm_entry *cm_entry;
37
0
    HMAP_FOR_EACH_SAFE (cm_entry, node, &cooperative_multitasking_callbacks) {
38
0
        hmap_remove(&cooperative_multitasking_callbacks, &cm_entry->node);
39
0
        free(cm_entry);
40
0
    }
41
0
}
42
43
/* Set/update callback as identified by 'cb' and 'arg'.
44
 *
45
 * 'name' is used for logging events related to this callback.
46
 *
47
 * The value for 'last_run' must be updated each time the callback is run.
48
 *
49
 * Updating the value for 'threshold' may be necessary as a consequence of
50
 * change in runtime configuration or requirements of the part of the program
51
 * serviced by the callback.
52
 *
53
 * Providing a value of 0 for 'last_run' or 'threshold' will leave the stored
54
 * value untouched. */
55
void
56
cooperative_multitasking_set(void (*cb)(void *), void *arg,
57
                             long long int last_run, long long int threshold,
58
                             const char *name)
59
0
{
60
0
    struct cm_entry *cm_entry;
61
62
0
    HMAP_FOR_EACH_WITH_HASH (cm_entry, node, hash_pointer((void *) cb, 0),
63
0
                             &cooperative_multitasking_callbacks) {
64
0
        if (cm_entry->cb == cb && cm_entry->arg == arg) {
65
0
            if (last_run) {
66
0
                cm_entry->last_run = last_run;
67
0
            }
68
69
0
            if (threshold) {
70
0
                cm_entry->threshold = threshold;
71
0
            }
72
0
            return;
73
0
        }
74
0
    }
75
76
0
    cm_entry = xzalloc(sizeof *cm_entry);
77
0
    cm_entry->cb = cb;
78
0
    cm_entry->arg = arg;
79
0
    cm_entry->threshold = threshold;
80
0
    cm_entry->last_run = last_run ? last_run : time_msec();
81
0
    cm_entry->name = name;
82
83
0
    hmap_insert(&cooperative_multitasking_callbacks,
84
0
                &cm_entry->node, hash_pointer((void *) cm_entry->cb, 0));
85
0
}
86
87
/* Remove callback identified by 'cb' and 'arg'. */
88
void
89
cooperative_multitasking_remove(void (*cb)(void *), void *arg)
90
0
{
91
0
    struct cm_entry *cm_entry;
92
93
0
    HMAP_FOR_EACH_WITH_HASH (cm_entry, node, hash_pointer((void *) cb, 0),
94
0
                             &cooperative_multitasking_callbacks) {
95
0
        if (cm_entry->cb == cb && cm_entry->arg == arg) {
96
0
            hmap_remove(&cooperative_multitasking_callbacks, &cm_entry->node);
97
0
            free(cm_entry);
98
0
            return;
99
0
        }
100
0
    }
101
0
}
102
103
static void
104
cooperative_multitasking_yield_at__(const char *source_location)
105
0
{
106
0
    long long int start = time_msec();
107
0
    struct cm_entry *cm_entry;
108
0
    long long int elapsed;
109
0
    bool warn;
110
111
0
    HMAP_FOR_EACH (cm_entry, node, &cooperative_multitasking_callbacks) {
112
0
        elapsed = time_msec() - cm_entry->last_run;
113
114
0
        if (elapsed >= cm_entry->threshold) {
115
0
            warn = elapsed - cm_entry->threshold > cm_entry->threshold / 8;
116
117
0
            VLOG(warn ? VLL_WARN : VLL_DBG, "%s: yield for %s(%p): "
118
0
                 "elapsed(%lld) >= threshold(%lld), overrun: %lld",
119
0
                 source_location, cm_entry->name, cm_entry->arg, elapsed,
120
0
                 cm_entry->threshold, elapsed - cm_entry->threshold);
121
122
0
            if (warn && VLOG_IS_DBG_ENABLED()) {
123
0
                log_backtrace();
124
0
            }
125
126
0
            (*cm_entry->cb)(cm_entry->arg);
127
0
        }
128
0
    }
129
130
0
    elapsed = time_msec() - start;
131
0
    if (elapsed > 1000) {
132
0
        VLOG_WARN("Unreasonably long %lldms runtime for callbacks.", elapsed);
133
0
    }
134
0
}
135
136
/* Iterate over registered callbacks and execute callbacks as demanded by the
137
 * recorded time threshold. */
138
void
139
cooperative_multitasking_yield_at(const char *source_location)
140
0
{
141
0
    static bool yield_in_progress = false;
142
143
0
    if (yield_in_progress) {
144
0
        VLOG_ERR_ONCE("Nested yield avoided, this is a bug! "
145
0
                      "Enable debug logging for more details.");
146
0
        if (VLOG_IS_DBG_ENABLED()) {
147
0
            VLOG_DBG("%s: nested yield.", source_location);
148
0
            log_backtrace();
149
0
        }
150
0
        return;
151
0
    }
152
0
    yield_in_progress = true;
153
154
0
    cooperative_multitasking_yield_at__(source_location);
155
156
0
    yield_in_progress = false;
157
0
}