Coverage Report

Created: 2025-07-01 06:50

/src/openvswitch/lib/odp-execute-private.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2022 Intel.
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 <errno.h>
19
#include <stdio.h>
20
#include <string.h>
21
22
#include "cpu.h"
23
#include "dpdk.h"
24
#include "dp-packet.h"
25
#include "odp-execute.h"
26
#include "odp-execute-private.h"
27
#include "odp-netlink.h"
28
#include "odp-util.h"
29
#include "openvswitch/vlog.h"
30
31
VLOG_DEFINE_THIS_MODULE(odp_execute_impl);
32
static int active_action_impl_index;
33
34
#if ACTION_IMPL_AVX512_CHECK
35
/* Probe functions to check ISA requirements. */
36
bool
37
action_avx512_isa_probe(void)
38
{
39
    static enum ovs_cpu_isa isa_required[] = {
40
        OVS_CPU_ISA_X86_AVX512F,
41
        OVS_CPU_ISA_X86_AVX512BW,
42
        OVS_CPU_ISA_X86_BMI2,
43
        OVS_CPU_ISA_X86_AVX512VL,
44
    };
45
    for (int i = 0; i < ARRAY_SIZE(isa_required); i++) {
46
        if (!cpu_has_isa(isa_required[i])) {
47
            return false;
48
        }
49
    }
50
    return true;
51
}
52
53
#else
54
55
bool
56
action_avx512_isa_probe(void)
57
0
{
58
0
   return false;
59
0
}
60
61
#endif
62
63
#if ACTION_IMPL_AVX512_CHECK && HAVE_AVX512VBMI
64
bool
65
action_avx512vbmi_isa_probe(void)
66
{
67
    return cpu_has_isa(OVS_CPU_ISA_X86_AVX512VBMI);
68
}
69
#else
70
bool
71
action_avx512vbmi_isa_probe(void)
72
0
{
73
0
    return false;
74
0
}
75
#endif
76
77
static struct odp_execute_action_impl action_impls[] = {
78
    [ACTION_IMPL_AUTOVALIDATOR] = {
79
        .available = false,
80
        .name = "autovalidator",
81
        .init_func = action_autoval_init,
82
    },
83
84
    [ACTION_IMPL_SCALAR] = {
85
        .available = false,
86
        .name = "scalar",
87
        .init_func = odp_action_scalar_init,
88
    },
89
90
#if ACTION_IMPL_AVX512_CHECK
91
    [ACTION_IMPL_AVX512] = {
92
        .available = false,
93
        .name = "avx512",
94
        .init_func = action_avx512_init,
95
    },
96
#endif
97
};
98
99
static void
100
action_impl_copy_funcs(struct odp_execute_action_impl *dest,
101
                       const struct odp_execute_action_impl *src)
102
0
{
103
0
    for (int i = 0; i < __OVS_ACTION_ATTR_MAX; i++) {
104
0
        atomic_store_relaxed(&dest->funcs[i], src->funcs[i]);
105
0
    }
106
0
}
107
108
struct odp_execute_action_impl *
109
odp_execute_action_set(const char *name)
110
0
{
111
0
    for (int i = 0; i < ACTION_IMPL_MAX; i++) {
112
        /* String compare, and set ptrs atomically. */
113
0
        if (!strcmp(action_impls[i].name, name)) {
114
0
            if (i != active_action_impl_index) {
115
0
                active_action_impl_index = i;
116
0
                VLOG_INFO("Action implementation set to %s", name);
117
0
            }
118
0
            return &action_impls[i];
119
0
        }
120
0
    }
121
0
    return NULL;
122
0
}
123
124
void
125
odp_execute_action_get_info(struct ds *string)
126
0
{
127
0
    ds_put_cstr(string, "Available Actions implementations:\n");
128
0
    for (int i = 0; i < ACTION_IMPL_MAX; i++) {
129
0
        ds_put_format(string, "  %s (available: %s, active: %s)\n",
130
0
                      action_impls[i].name,
131
0
                      action_impls[i].available ? "Yes" : "No",
132
0
                      i == active_action_impl_index ? "Yes" : "No");
133
0
    }
134
0
}
135
136
void
137
odp_execute_action_init(void)
138
0
{
139
    /* Each impl's function array is initialized to reflect the scalar
140
     * implementation. This simplifies adding optimized implementations,
141
     * as the autovalidator can always compare all actions.
142
     *
143
     * Below will check if impl is available and copies the scalar functions
144
     * to all other implementations. */
145
0
    for (int i = 0; i < ACTION_IMPL_MAX; i++) {
146
0
        bool avail = true;
147
148
0
        if (i != ACTION_IMPL_SCALAR) {
149
0
            action_impl_copy_funcs(&action_impls[i],
150
0
                                   &action_impls[ACTION_IMPL_SCALAR]);
151
0
        }
152
153
0
        if (action_impls[i].init_func) {
154
            /* Return zero is success, non-zero means error. */
155
0
            avail = (action_impls[i].init_func(&action_impls[i]) == 0);
156
0
        }
157
158
0
        action_impls[i].available = avail;
159
160
0
        VLOG_DBG("Actions implementation '%s' %s available.",
161
0
                 action_impls[i].name, avail ? "is" : "is not");
162
163
        /* The following is a run-time check to make sure a scalar
164
         * implementation exists for the given ISA implementation. This is to
165
         * make sure the autovalidator works as expected. */
166
0
        if (avail && i != ACTION_IMPL_SCALAR) {
167
0
            for (int j = 0; j < __OVS_ACTION_ATTR_MAX; j++) {
168
                /* No ovs_assert(), as it can be compiled out. */
169
0
                if (action_impls[ACTION_IMPL_SCALAR].funcs[j] == NULL
170
0
                    && action_impls[i].funcs[j] != NULL) {
171
0
                    ovs_assert_failure(OVS_SOURCE_LOCATOR, __func__,
172
0
                                       "Missing scalar action function!");
173
0
                }
174
0
            }
175
0
        }
176
0
    }
177
0
}
178
179
/* Init sequence required to be scalar first to pick up the default scalar
180
 * implementations, allowing over-riding of the optimized functions later. */
181
BUILD_ASSERT_DECL(ACTION_IMPL_SCALAR == 0);
182
BUILD_ASSERT_DECL(ACTION_IMPL_AUTOVALIDATOR == 1);
183
184
/* Loop over packets, and validate each one for the given action. */
185
static void
186
action_autoval_generic(struct dp_packet_batch *batch, const struct nlattr *a)
187
0
{
188
0
    struct odp_execute_action_impl *scalar = &action_impls[ACTION_IMPL_SCALAR];
189
0
    enum ovs_action_attr attr_type = nl_attr_type(a);
190
0
    struct dp_packet_batch original_batch;
191
0
    bool failed = false;
192
193
0
    dp_packet_batch_clone(&original_batch, batch);
194
195
0
    scalar->funcs[attr_type](batch, a);
196
197
0
    for (int impl = ACTION_IMPL_BEGIN; impl < ACTION_IMPL_MAX; impl++) {
198
        /* Clone original batch and execute implementation under test. */
199
0
        struct dp_packet_batch test_batch;
200
201
0
        dp_packet_batch_clone(&test_batch, &original_batch);
202
0
        action_impls[impl].funcs[attr_type](&test_batch, a);
203
204
        /* Loop over implementations, checking each one. */
205
0
        for (int pidx = 0; pidx < original_batch.count; pidx++) {
206
0
            struct dp_packet *good_pkt = batch->packets[pidx];
207
0
            struct dp_packet *test_pkt = test_batch.packets[pidx];
208
209
0
            struct ds log_msg = DS_EMPTY_INITIALIZER;
210
211
            /* Compare packet length and payload contents. */
212
0
            bool eq = dp_packet_equal(good_pkt, test_pkt);
213
214
0
            if (!eq) {
215
0
                ds_put_format(&log_msg, "Packet: %d\nAction : ", pidx);
216
0
                format_odp_actions(&log_msg, a, a->nla_len, NULL);
217
0
                ds_put_format(&log_msg, "\nGood hex:\n");
218
0
                ds_put_hex_dump(&log_msg, dp_packet_data(good_pkt),
219
0
                                dp_packet_size(good_pkt), 0, false);
220
0
                ds_put_format(&log_msg, "Test hex:\n");
221
0
                ds_put_hex_dump(&log_msg, dp_packet_data(test_pkt),
222
0
                                dp_packet_size(test_pkt), 0, false);
223
224
0
                failed = true;
225
0
            }
226
227
            /* Compare offsets and RSS */
228
0
            if (!dp_packet_compare_offsets(good_pkt, test_pkt, &log_msg)) {
229
0
                failed = true;
230
0
            }
231
232
0
            if (dp_packet_rss_valid(good_pkt)) {
233
0
                uint32_t good_hash = dp_packet_get_rss_hash(good_pkt);
234
0
                uint32_t test_hash = dp_packet_get_rss_hash(test_pkt);
235
236
0
                if (good_hash != test_hash) {
237
0
                    ds_put_format(&log_msg,
238
0
                                  "Autovalidation rss hash failed\n");
239
0
                    ds_put_format(&log_msg, "Good RSS hash : %u\n", good_hash);
240
0
                    ds_put_format(&log_msg, "Test RSS hash : %u\n", test_hash);
241
242
0
                    failed = true;
243
0
                }
244
0
            }
245
246
            /* Compare packet metadata. */
247
0
            if (memcmp(&good_pkt->md, &test_pkt->md, sizeof good_pkt->md)) {
248
0
                ds_put_format(&log_msg, "Autovalidation metadata failed\n");
249
0
                ds_put_format(&log_msg, "Good packet metadata:\n");
250
0
                ds_put_sparse_hex_dump(&log_msg, &good_pkt->md,
251
0
                                       sizeof good_pkt->md, 0, false);
252
0
                ds_put_format(&log_msg, "Test packet metadata:\n");
253
0
                ds_put_sparse_hex_dump(&log_msg, &test_pkt->md,
254
0
                                       sizeof test_pkt->md, 0, false);
255
0
                failed = true;
256
0
            }
257
258
0
            if (failed) {
259
0
                VLOG_ERR("Autovalidation of %s failed. Details:\n%s",
260
0
                         action_impls[impl].name, ds_cstr(&log_msg));
261
0
                ds_destroy(&log_msg);
262
0
                failed = false;
263
0
            }
264
0
        }
265
0
        dp_packet_delete_batch(&test_batch, true);
266
0
    }
267
0
    dp_packet_delete_batch(&original_batch, true);
268
0
}
269
270
void
271
odp_execute_scalar_action(struct dp_packet_batch *batch,
272
                          const struct nlattr *action)
273
0
{
274
0
    enum ovs_action_attr type = nl_attr_type(action);
275
276
0
    if (type <= OVS_ACTION_ATTR_MAX &&
277
0
        action_impls[ACTION_IMPL_SCALAR].funcs[type]) {
278
279
0
        action_impls[ACTION_IMPL_SCALAR].funcs[type](batch, action);
280
0
    }
281
0
}
282
283
int
284
action_autoval_init(struct odp_execute_action_impl *self)
285
0
{
286
    /* Set function pointers for actions that can be applied directly, these
287
     * are identified by OVS_ACTION_ATTR_*. */
288
0
    for (int i = 0; i < __OVS_ACTION_ATTR_MAX; i++) {
289
0
        if (action_impls[ACTION_IMPL_SCALAR].funcs[i]) {
290
0
            self->funcs[i] = action_autoval_generic;
291
0
        }
292
0
    }
293
0
    return 0;
294
0
}