Coverage Report

Created: 2026-10-17 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openvswitch/lib/dpif-offload.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2025 Red Hat, 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 <errno.h>
19
20
#include "dpif-offload.h"
21
#include "dpif-offload-provider.h"
22
#include "dpif-provider.h"
23
#include "netdev-provider.h"
24
#include "unixctl.h"
25
#include "util.h"
26
#include "vswitch-idl.h"
27
28
#include "openvswitch/dynamic-string.h"
29
#include "openvswitch/shash.h"
30
#include "openvswitch/vlog.h"
31
32
VLOG_DEFINE_THIS_MODULE(dpif_offload);
33
34
static struct vlog_rate_limit rl_dbg = VLOG_RATE_LIMIT_INIT(100, 100);
35
36
static struct ovs_mutex dpif_offload_mutex = OVS_MUTEX_INITIALIZER;
37
static struct shash dpif_offload_classes \
38
    OVS_GUARDED_BY(dpif_offload_mutex) = \
39
    SHASH_INITIALIZER(&dpif_offload_classes);
40
static struct shash dpif_offload_providers \
41
    OVS_GUARDED_BY(dpif_offload_mutex) = \
42
    SHASH_INITIALIZER(&dpif_offload_providers);
43
44
static const struct dpif_offload_class *base_dpif_offload_classes[] = {
45
#if defined(__linux__)
46
    &dpif_offload_tc_class,
47
#endif
48
#ifdef DPDK_NETDEV
49
    &dpif_offload_dpdk_class,
50
#endif
51
    /* While adding a new offload class to this structure make sure to also
52
     * update the dpif_offload_provider_priority_list below. */
53
    &dpif_offload_dummy_class,
54
    &dpif_offload_dummy_x_class,
55
};
56
57
0
#define TOTAL_PROVIDERS ARRAY_SIZE(base_dpif_offload_classes)
58
0
#define DEFAULT_PROVIDER_PRIORITY_LIST "tc,dpdk,dummy,dummy_x"
59
60
static char *dpif_offload_provider_priority_list = NULL;
61
static atomic_bool offload_global_enabled = false;
62
static atomic_bool offload_rebalance_policy = false;
63
static struct smap iface_order_cfg = SMAP_INITIALIZER(&iface_order_cfg);
64
65
static int
66
dpif_offload_register_provider__(const struct dpif_offload_class *class)
67
    OVS_REQUIRES(dpif_offload_mutex)
68
0
{
69
0
    int error;
70
71
0
    if (shash_find(&dpif_offload_classes, class->type)) {
72
0
        VLOG_WARN("attempted to register duplicate dpif offload class: %s",
73
0
                  class->type);
74
0
        return EEXIST;
75
0
    }
76
77
0
    if (!class->supported_dpif_types) {
78
0
        VLOG_WARN("attempted to register a dpif offload class without any "
79
0
                  "supported dpif types: %s", class->type);
80
0
        return EINVAL;
81
0
    }
82
83
0
    error = class->init ? class->init() : 0;
84
0
    if (error) {
85
0
        VLOG_WARN("failed to initialize %s dpif offload class: %s",
86
0
                  class->type, ovs_strerror(error));
87
0
        return error;
88
0
    }
89
90
0
    shash_add(&dpif_offload_classes, class->type, class);
91
0
    return 0;
92
0
}
93
94
static int
95
dpif_offload_register_provider(const struct dpif_offload_class *class)
96
0
{
97
0
    int error;
98
99
0
    ovs_mutex_lock(&dpif_offload_mutex);
100
0
    error = dpif_offload_register_provider__(class);
101
0
    ovs_mutex_unlock(&dpif_offload_mutex);
102
103
0
    return error;
104
0
}
105
106
static void
107
dpif_offload_show_classes(struct unixctl_conn *conn, int argc OVS_UNUSED,
108
                          const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
109
0
{
110
0
    const struct shash_node **list;
111
0
    struct ds ds;
112
113
0
    ds_init(&ds);
114
0
    ovs_mutex_lock(&dpif_offload_mutex);
115
116
0
    list = shash_sort(&dpif_offload_classes);
117
0
    for (size_t i = 0; i < shash_count(&dpif_offload_classes); i++) {
118
0
        const struct dpif_offload_class *class = list[i]->data;
119
120
0
        if (i == 0) {
121
0
            ds_put_cstr(&ds, "Offload Class     Supported dpif class(es)\n");
122
0
            ds_put_cstr(&ds, "----------------  ------------------------\n");
123
0
        }
124
125
0
        ds_put_format(&ds, "%-16s  ", list[i]->name);
126
127
0
        for (size_t j = 0; class->supported_dpif_types[j] != NULL; j++) {
128
0
            ds_put_format(&ds, "%*s%s\n", j == 0 ? 0 : 18, "",
129
0
                          class->supported_dpif_types[j]);
130
0
        }
131
0
    }
132
133
0
    ovs_mutex_unlock(&dpif_offload_mutex);
134
0
    free(list);
135
136
0
    unixctl_command_reply(conn, ds_cstr(&ds));
137
0
    ds_destroy(&ds);
138
0
}
139
140
void
141
dpif_offload_module_init(void)
142
0
{
143
0
    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
144
145
0
    if (!ovsthread_once_start(&once)) {
146
0
        return;
147
0
    }
148
149
0
    if (!dpif_offload_provider_priority_list) {
150
0
        dpif_offload_provider_priority_list =
151
0
            xstrdup(DEFAULT_PROVIDER_PRIORITY_LIST);
152
0
    }
153
154
0
    unixctl_command_register("dpif/offload/classes", NULL, 0, 0,
155
0
                             dpif_offload_show_classes, NULL);
156
157
0
    for (int i = 0; i < ARRAY_SIZE(base_dpif_offload_classes); i++) {
158
0
        ovs_assert(base_dpif_offload_classes[i]->open
159
0
                   && base_dpif_offload_classes[i]->close
160
0
                   && base_dpif_offload_classes[i]->can_offload
161
0
                   && base_dpif_offload_classes[i]->port_add
162
0
                   && base_dpif_offload_classes[i]->port_del
163
0
                   && base_dpif_offload_classes[i]->get_netdev);
164
165
0
        ovs_assert((base_dpif_offload_classes[i]->flow_dump_create &&
166
0
                    base_dpif_offload_classes[i]->flow_dump_next &&
167
0
                    base_dpif_offload_classes[i]->flow_dump_destroy &&
168
0
                    base_dpif_offload_classes[i]->flow_dump_thread_create &&
169
0
                    base_dpif_offload_classes[i]->flow_dump_thread_destroy) ||
170
0
                   (!base_dpif_offload_classes[i]->flow_dump_create &&
171
0
                    !base_dpif_offload_classes[i]->flow_dump_next &&
172
0
                    !base_dpif_offload_classes[i]->flow_dump_destroy &&
173
0
                    !base_dpif_offload_classes[i]->flow_dump_thread_create &&
174
0
                    !base_dpif_offload_classes[i]->flow_dump_thread_destroy));
175
176
0
        dpif_offload_register_provider(base_dpif_offload_classes[i]);
177
0
    }
178
179
0
    ovsthread_once_done(&once);
180
0
}
181
182
static struct dpif_offload_provider_collection*
183
dpif_get_offload_provider_collection(const struct dpif *dpif)
184
0
{
185
0
    return ovsrcu_get(struct dpif_offload_provider_collection *,
186
0
                      &dpif->offload_provider_collection);
187
0
}
188
189
static struct dpif_offload_provider_collection*
190
dpif_get_offload_provider_collection_with_ref(const struct dpif *dpif)
191
0
{
192
0
    struct dpif_offload_provider_collection *collection;
193
194
0
    ovs_mutex_lock(&dpif_offload_mutex);
195
196
0
    collection = ovsrcu_get(struct dpif_offload_provider_collection *,
197
0
                            &dpif->offload_provider_collection);
198
199
0
    if (!collection || !ovs_refcount_try_ref_rcu(&collection->ref_cnt)) {
200
0
        collection = NULL;
201
0
    }
202
203
0
    ovs_mutex_unlock(&dpif_offload_mutex);
204
205
0
    return collection;
206
0
}
207
208
static void
209
dpif_attach_offload_provider_collection(
210
    struct dpif *dpif, struct dpif_offload_provider_collection *collection)
211
    OVS_REQUIRES(dpif_offload_mutex)
212
0
{
213
    /* When called, 'collection' should still have a refcount > 0, which is
214
     * guaranteed by holding the lock from the shash lookup up to this point.
215
     * If, for any reason, the refcount is not > 0, ovs_refcount_ref() will
216
     * assert. */
217
0
    ovs_refcount_ref(&collection->ref_cnt);
218
0
    ovsrcu_set(&dpif->offload_provider_collection, collection);
219
0
}
220
221
static int
222
provider_collection_add(struct dpif_offload_provider_collection *collection,
223
                        struct dpif_offload *offload)
224
0
{
225
0
    struct ovs_list *providers_list = &collection->list;
226
0
    struct dpif_offload *offload_entry;
227
228
0
    ovs_assert(collection);
229
230
0
    LIST_FOR_EACH (offload_entry, dpif_list_node, providers_list) {
231
0
        if (offload_entry == offload || !strcmp(offload->name,
232
0
                                                offload_entry->name)) {
233
0
            return EEXIST;
234
0
        }
235
0
    }
236
237
0
    ovs_list_push_back(providers_list, &offload->dpif_list_node);
238
0
    return 0;
239
0
}
240
241
static void
242
move_provider_to_collection(
243
    struct dpif_offload_provider_collection *collection,
244
    struct dpif_offload *offload)
245
    OVS_REQUIRES(dpif_offload_mutex)
246
0
{
247
0
    int error;
248
249
0
    ovs_list_remove(&offload->dpif_list_node);
250
0
    error = provider_collection_add(collection, offload);
251
0
    if (error) {
252
0
        VLOG_WARN("failed to add dpif offload provider %s to %s: %s",
253
0
                  dpif_offload_type(offload), collection->dpif_name,
254
0
                  ovs_strerror(error));
255
256
0
        offload->class->close(offload);
257
0
    }
258
0
}
259
260
static int
261
dpif_attach_new_offload_provider_collection(struct dpif *dpif)
262
    OVS_REQUIRES(dpif_offload_mutex)
263
0
{
264
0
    struct ovs_list provider_list = OVS_LIST_INITIALIZER(&provider_list);
265
0
    const char *dpif_type_str = dpif_normalize_type(dpif_type(dpif));
266
0
    struct dpif_offload_provider_collection *collection;
267
0
    struct dpif_offload *offload;
268
0
    struct shash_node *node;
269
0
    char *tokens, *saveptr;
270
271
    /* Allocate and attach collection to dpif. */
272
0
    collection = xmalloc(sizeof *collection);
273
0
    collection->dpif_name = xstrdup(dpif_name(dpif));
274
0
    ovs_mutex_init_recursive(&collection->mutex);
275
0
    ovs_refcount_init(&collection->ref_cnt);
276
0
    ovs_list_init(&collection->list);
277
0
    shash_add(&dpif_offload_providers, collection->dpif_name, collection);
278
279
    /* Open all the providers supporting this dpif type. */
280
0
    SHASH_FOR_EACH (node, &dpif_offload_classes) {
281
0
        const struct dpif_offload_class *class = node->data;
282
283
0
        for (size_t i = 0; class->supported_dpif_types[i] != NULL; i++) {
284
0
            if (!strcmp(class->supported_dpif_types[i], dpif_type_str)) {
285
0
                int error = class->open(class, dpif, &offload);
286
287
0
                if (error) {
288
0
                    VLOG_WARN("failed to initialize dpif offload provider "
289
0
                              "%s for %s: %s",
290
0
                              class->type, dpif_name(dpif),
291
0
                              ovs_strerror(error));
292
0
                } else {
293
0
                    ovs_list_push_back(&provider_list,
294
0
                                       &offload->dpif_list_node);
295
0
                }
296
0
                break;
297
0
            }
298
0
        }
299
0
    }
300
301
    /* Attach all the providers based on the priority list. */
302
0
    tokens = xstrdup(dpif_offload_provider_priority_list);
303
304
0
    for (char *name = strtok_r(tokens, ",", &saveptr);
305
0
         name;
306
0
         name = strtok_r(NULL, ",", &saveptr)) {
307
308
0
        LIST_FOR_EACH_SAFE (offload, dpif_list_node, &provider_list) {
309
0
            if (strcmp(name, offload->class->type)) {
310
0
                continue;
311
0
            }
312
313
0
            move_provider_to_collection(collection, offload);
314
0
            break;
315
0
        }
316
0
    }
317
0
    free(tokens);
318
319
    /* Add remaining entries in order. */
320
0
    LIST_FOR_EACH_SAFE (offload, dpif_list_node, &provider_list) {
321
0
        move_provider_to_collection(collection, offload);
322
0
    }
323
324
    /* Attach the new collection to the dpif. */
325
0
    ovsrcu_set(&dpif->offload_provider_collection, collection);
326
327
0
    return 0;
328
0
}
329
330
/* This function returns 0 if a new provider set was attached to the dpif,
331
 * returns EEXIST if an existing set of providers was attached, and
332
 * returns a negative error code on error. */
333
int
334
dpif_attach_offload_providers(struct dpif *dpif)
335
0
{
336
0
    struct dpif_offload_provider_collection *collection;
337
0
    int rc = EEXIST;
338
339
0
    ovs_mutex_lock(&dpif_offload_mutex);
340
341
0
    collection = shash_find_data(&dpif_offload_providers, dpif_name(dpif));
342
0
    if (collection) {
343
0
        dpif_attach_offload_provider_collection(dpif, collection);
344
0
    } else {
345
0
        rc = dpif_attach_new_offload_provider_collection(dpif);
346
0
    }
347
348
0
    ovs_mutex_unlock(&dpif_offload_mutex);
349
0
    return rc;
350
0
}
351
352
static void
353
provider_collection_free_rcu(
354
    struct dpif_offload_provider_collection *collection)
355
0
{
356
0
    struct dpif_offload *offload_entry;
357
358
    /* We need to use the safe variant here as we removed the entry, and the
359
     * close API will free() it. */
360
0
    LIST_FOR_EACH_SAFE (offload_entry, dpif_list_node, &collection->list) {
361
0
        ovs_list_remove(&offload_entry->dpif_list_node);
362
0
        offload_entry->class->close(offload_entry);
363
0
    }
364
365
    /* Free remaining resources. */
366
0
    ovs_mutex_destroy(&collection->mutex);
367
0
    free(collection->dpif_name);
368
0
    free(collection);
369
0
}
370
371
static void
372
dpif_offload_unref_collection(
373
    struct dpif_offload_provider_collection *collection)
374
0
{
375
0
    if (!collection) {
376
0
        return;
377
0
    }
378
379
    /* Take dpif_offload_mutex so that, if collection->ref_cnt falls to
380
     * zero, we can't get a new reference to 'collection' through the
381
     * 'dpif_offload_providers' shash. */
382
0
    ovs_mutex_lock(&dpif_offload_mutex);
383
0
    if (ovs_refcount_unref_relaxed(&collection->ref_cnt) == 1) {
384
0
        shash_find_and_delete(&dpif_offload_providers,
385
0
                              collection->dpif_name);
386
0
        ovsrcu_postpone(provider_collection_free_rcu, collection);
387
0
    }
388
0
    ovs_mutex_unlock(&dpif_offload_mutex);
389
0
}
390
391
void
392
dpif_detach_offload_providers(struct dpif *dpif)
393
0
{
394
0
    struct dpif_offload_provider_collection *collection;
395
396
0
    collection = dpif_get_offload_provider_collection(dpif);
397
0
    if (collection) {
398
0
        dpif_offload_unref_collection(collection);
399
0
        ovsrcu_set(&dpif->offload_provider_collection, NULL);
400
0
    }
401
0
}
402
403
void
404
dpif_offload_set_config(struct dpif *dpif, const struct smap *other_cfg)
405
0
{
406
0
    struct dpif_offload_provider_collection *collection;
407
0
    struct dpif_offload *offload;
408
409
0
    collection = dpif_get_offload_provider_collection(dpif);
410
411
0
    if (!collection) {
412
0
        return;
413
0
    }
414
415
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
416
0
        if (offload->class->set_config) {
417
0
            offload->class->set_config(offload, other_cfg);
418
0
        }
419
0
    }
420
0
}
421
422

423
void
424
dpif_offload_init(struct dpif_offload *offload,
425
                  const struct dpif_offload_class *class,
426
                  struct dpif *dpif)
427
0
{
428
0
    ovs_assert(offload && class && dpif);
429
430
0
    offload->class = class;
431
0
    offload->name = xasprintf("%s[%s]", class->type, dpif_name(dpif));
432
0
    offload->ports = dpif_offload_port_mgr_init();
433
0
}
434
435
void
436
dpif_offload_destroy(struct dpif_offload *offload)
437
0
{
438
0
    ovs_assert(offload);
439
440
0
    dpif_offload_port_mgr_destroy(offload);
441
0
    free(offload->name);
442
0
}
443
444
const char *
445
dpif_offload_name(const struct dpif_offload *offload)
446
0
{
447
0
    return offload->name;
448
0
}
449
450
const char *
451
dpif_offload_type(const struct dpif_offload *offload)
452
0
{
453
0
    return offload->class->type;
454
0
}
455
456
bool
457
dpif_offload_get_debug(const struct dpif_offload *offload, struct ds *ds,
458
                       struct json *json)
459
0
{
460
0
    if (!offload->class->get_debug) {
461
0
        return false;
462
0
    }
463
464
0
    offload->class->get_debug(offload, ds, json);
465
0
    return true;
466
0
}
467
468
int
469
dpif_offload_stats_get(struct dpif *dpif, struct netdev_custom_stats **stats_,
470
                       size_t *n_stats)
471
0
{
472
0
    struct dpif_offload_provider_collection *collection;
473
0
    struct netdev_custom_stats *stats;
474
0
    struct dpif_offload *offload;
475
0
    size_t n_providers;
476
0
    int error = 0;
477
478
0
    *n_stats = 0;
479
0
    collection = dpif_get_offload_provider_collection(dpif);
480
0
    if (!collection || !dpif_offload_enabled()) {
481
0
        *stats_ = NULL;
482
0
        return 0;
483
0
    }
484
485
0
    n_providers = ovs_list_size(&collection->list);
486
0
    stats = xcalloc(n_providers, sizeof(struct netdev_custom_stats));
487
488
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
489
0
        if (!offload->class->get_global_stats) {
490
0
            continue;
491
0
        }
492
493
0
        error = offload->class->get_global_stats(offload, &stats[(*n_stats)]);
494
0
        if (error) {
495
0
            for (int i = 0; i < *n_stats; i++) {
496
0
                netdev_free_custom_stats_counters(&stats[i]);
497
0
            }
498
0
            *n_stats = 0;
499
0
            free(stats);
500
0
            stats = NULL;
501
0
            break;
502
0
        }
503
504
0
        (*n_stats)++;
505
0
    }
506
507
0
    *stats_ = stats;
508
0
    return error;
509
0
}
510
511
bool
512
dpif_offload_enabled(void)
513
0
{
514
0
    bool enabled;
515
516
0
    atomic_read_relaxed(&offload_global_enabled, &enabled);
517
0
    return enabled;
518
0
}
519
520
bool
521
dpif_offload_rebalance_policy_enabled(void)
522
0
{
523
0
    bool enabled;
524
525
0
    atomic_read_relaxed(&offload_rebalance_policy, &enabled);
526
0
    return enabled;
527
0
}
528
529
void
530
dpif_offload_set_netdev_offload(struct netdev *netdev,
531
                                struct dpif_offload *offload)
532
0
{
533
0
    ovsrcu_set(&netdev->dpif_offload, offload);
534
0
}
535
536
static bool
537
dpif_offload_try_port_add(struct dpif_offload *offload, struct netdev *netdev,
538
                          odp_port_t port_no)
539
0
{
540
0
    if (offload->class->can_offload(offload, netdev)) {
541
0
        int err = offload->class->port_add(offload, netdev, port_no);
542
543
0
        if (!err) {
544
0
            VLOG_DBG("netdev %s added to dpif-offload provider %s",
545
0
                     netdev_get_name(netdev), dpif_offload_name(offload));
546
0
            return true;
547
0
        } else {
548
0
            VLOG_ERR("Failed adding netdev %s to dpif-offload provider "
549
0
                     "%s, error %s",
550
0
                     netdev_get_name(netdev), dpif_offload_name(offload),
551
0
                     ovs_strerror(err));
552
0
        }
553
0
    } else {
554
0
        VLOG_DBG("netdev %s failed can_offload for dpif-offload provider %s",
555
0
                 netdev_get_name(netdev), dpif_offload_name(offload));
556
0
    }
557
0
    return false;
558
0
}
559
560
void
561
dpif_offload_port_add(struct dpif *dpif, struct netdev *netdev,
562
                      odp_port_t port_no)
563
0
{
564
0
    struct dpif_offload_provider_collection *collection;
565
0
    const char *port_priority = smap_get(&iface_order_cfg,
566
0
                                         netdev_get_name(netdev));
567
0
    struct dpif_offload *offload;
568
569
0
    collection = dpif_get_offload_provider_collection(dpif);
570
0
    if (!collection) {
571
0
        return;
572
0
    }
573
574
0
    if (port_priority) {
575
0
        char *tokens = xstrdup(port_priority);
576
0
        char *saveptr;
577
578
0
        VLOG_DBG("for netdev %s using port priority %s",
579
0
                 netdev_get_name(netdev), port_priority);
580
581
0
        for (char *name = strtok_r(tokens, ",", &saveptr);
582
0
             name;
583
0
             name = strtok_r(NULL, ",", &saveptr)) {
584
0
            bool provider_added = false;
585
586
0
            if (!strcmp("none", name)) {
587
0
                break;
588
0
            }
589
590
0
            LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
591
0
                if (!strcmp(name, offload->class->type)) {
592
0
                    provider_added = dpif_offload_try_port_add(offload, netdev,
593
0
                                                               port_no);
594
0
                    break;
595
0
                }
596
0
            }
597
598
0
            if (provider_added) {
599
0
                break;
600
0
            }
601
0
        }
602
0
        free(tokens);
603
0
    } else {
604
0
        LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
605
0
            if (dpif_offload_try_port_add(offload, netdev, port_no)) {
606
0
                break;
607
0
            }
608
0
        }
609
0
    }
610
0
}
611
612
void
613
dpif_offload_port_del(struct dpif *dpif, odp_port_t port_no)
614
0
{
615
0
    struct dpif_offload_provider_collection *collection;
616
0
    struct dpif_offload *offload;
617
618
0
    collection = dpif_get_offload_provider_collection(dpif);
619
0
    if (!collection) {
620
0
        return;
621
0
    }
622
623
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
624
0
        int err = offload->class->port_del(offload, port_no);
625
626
0
        if (err) {
627
0
            VLOG_ERR("Failed deleting port_no %d from dpif-offload provider "
628
0
                     "%s, error %s", port_no, dpif_offload_name(offload),
629
0
                     ovs_strerror(err));
630
0
        }
631
0
    }
632
0
}
633
634
void
635
dpif_offload_port_set_config(struct dpif *dpif, odp_port_t port_no,
636
                             const struct smap *cfg)
637
0
{
638
0
    struct dpif_offload_provider_collection *collection;
639
0
    struct dpif_offload *offload;
640
641
0
    collection = dpif_get_offload_provider_collection(dpif);
642
0
    if (!collection) {
643
0
        return;
644
0
    }
645
646
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
647
0
        if (offload->class->port_set_config) {
648
0
            offload->class->port_set_config(offload, port_no, cfg);
649
0
        }
650
0
    }
651
0
}
652
653
struct dpif_offload *
654
dpif_offload_port_offloaded_by(const struct dpif *dpif, odp_port_t port_no)
655
0
{
656
0
    struct dpif_offload_provider_collection *collection;
657
0
    struct dpif_offload *offload, *offload_return = NULL;
658
659
0
    collection = dpif_get_offload_provider_collection(dpif);
660
0
    if (!collection || !dpif_offload_enabled()) {
661
0
        return NULL;
662
0
    }
663
664
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
665
0
        if (offload->class->get_netdev(offload, port_no)) {
666
0
            offload_return = offload;
667
0
            break;
668
0
        }
669
0
    }
670
671
0
    return offload_return;
672
0
}
673
674
struct dpif_offload_dump_state {
675
    const struct dpif_offload_provider_collection *collection;
676
    struct dpif_offload *entry;
677
    int error;
678
};
679
680
void
681
dpif_offload_dump_start(const struct dpif *dpif, void **statep)
682
0
{
683
0
    struct dpif_offload_dump_state *state;
684
685
0
    state = xzalloc(sizeof *state);
686
0
    state->collection = dpif_get_offload_provider_collection_with_ref(dpif);
687
0
    if (!state->collection) {
688
0
        state->error = EIDRM;
689
0
    }
690
691
0
    *statep = state;
692
0
}
693
694
bool
695
dpif_offload_dump_next(void *state_, struct dpif_offload **offload)
696
0
{
697
0
    struct dpif_offload_dump_state *state = state_;
698
699
0
    if (!offload || !state || state->error || !state->collection) {
700
0
        return false;
701
0
    }
702
703
0
    if (state->entry) {
704
0
        struct dpif_offload *entry = state->entry;
705
706
0
        LIST_FOR_EACH_CONTINUE (entry, dpif_list_node,
707
0
                                &state->collection->list) {
708
0
            state->entry = entry;
709
0
            *offload = entry;
710
0
            return true;
711
0
        }
712
713
0
        state->error = EOF;
714
0
    } else {
715
        /* Get the first entry in the list. */
716
0
        struct dpif_offload *entry;
717
718
0
        LIST_FOR_EACH (entry, dpif_list_node, &state->collection->list) {
719
0
            break;
720
0
        }
721
722
0
        if (entry) {
723
0
            state->entry = entry;
724
0
            *offload = entry;
725
0
        } else {
726
0
            state->error = EOF;
727
0
        }
728
0
    }
729
730
0
    return !state->error;
731
0
}
732
733
int
734
dpif_offload_dump_done(void *state_)
735
0
{
736
0
    struct dpif_offload_dump_state *state = state_;
737
0
    int error;
738
739
0
    dpif_offload_unref_collection(
740
0
        CONST_CAST(struct dpif_offload_provider_collection *,
741
0
                   state->collection));
742
0
    error = state->error == EOF ? 0 : state->error;
743
0
    free(state);
744
745
0
    return error;
746
0
}
747
748
void
749
dpif_offload_set_global_cfg(const struct ovsrec_open_vswitch *cfg)
750
0
{
751
0
    static struct ovsthread_once init_once = OVSTHREAD_ONCE_INITIALIZER;
752
0
    const struct smap *other_cfg = &cfg->other_config;
753
0
    const char *priority;
754
755
    /* The 'hw-offload-priority' parameter can only be set at startup,
756
     * any successive change needs a restart. */
757
0
    priority = smap_get(other_cfg, "hw-offload-priority");
758
759
0
    if (ovsthread_once_start(&init_once)) {
760
        /* Initialize the dpif-offload layer in case it's not yet initialized
761
         * at the first invocation of setting the configuration. */
762
0
        dpif_offload_module_init();
763
764
        /* If priority is not set keep the default value. */
765
0
        if (priority) {
766
0
            char *tokens = xstrdup(priority);
767
0
            char *saveptr;
768
769
0
            free(dpif_offload_provider_priority_list);
770
0
            dpif_offload_provider_priority_list = xstrdup(priority);
771
772
            /* Log a warning for unknown offload providers. */
773
0
            for (char *name = strtok_r(tokens, ",", &saveptr);
774
0
                 name;
775
0
                 name = strtok_r(NULL, ",", &saveptr)) {
776
777
0
                if (!shash_find(&dpif_offload_classes, name)) {
778
0
                    VLOG_WARN("hw-offload-priority configuration has an "
779
0
                              "unknown type; %s", name);
780
0
                }
781
0
            }
782
0
            free(tokens);
783
0
        }
784
0
        ovsthread_once_done(&init_once);
785
0
    } else {
786
0
        if (priority && strcmp(priority,
787
0
                               dpif_offload_provider_priority_list)) {
788
0
            VLOG_INFO_ONCE("hw-offload-priority configuration changed; "
789
0
                           "restart required");
790
0
        }
791
0
    }
792
793
    /* Handle other global configuration settings. */
794
0
     if (smap_get_bool(other_cfg, "hw-offload", false)) {
795
0
        static struct ovsthread_once once_enable = OVSTHREAD_ONCE_INITIALIZER;
796
797
0
        if (ovsthread_once_start(&once_enable)) {
798
0
            atomic_store_relaxed(&offload_global_enabled, true);
799
0
            VLOG_INFO("Flow HW offload is enabled");
800
801
0
            if (smap_get_bool(other_cfg, "offload-rebalance", false)) {
802
0
                atomic_store_relaxed(&offload_rebalance_policy, true);
803
0
            }
804
805
0
            ovsthread_once_done(&once_enable);
806
0
        }
807
0
    }
808
809
    /* Filter out the 'hw-offload-priority' per interface setting.  We need it
810
     * before ports are added, so we can assign the correct offload-provider.
811
     * Note that we can safely rebuild the map here, as we only access this
812
     * from the same (main) thread. */
813
0
    smap_clear(&iface_order_cfg);
814
0
    for (int i = 0; i < cfg->n_bridges; i++) {
815
0
        const struct ovsrec_bridge *br_cfg = cfg->bridges[i];
816
817
0
        for (int j = 0; j < br_cfg->n_ports; j++) {
818
0
            const struct ovsrec_port *port_cfg = br_cfg->ports[j];
819
820
0
            for (int k = 0; k < port_cfg->n_interfaces; k++) {
821
0
                const struct ovsrec_interface *iface_cfg;
822
823
0
                iface_cfg = port_cfg->interfaces[k];
824
0
                priority = smap_get(&iface_cfg->other_config,
825
0
                                    "hw-offload-priority");
826
0
                if (priority) {
827
0
                    smap_add(&iface_order_cfg, iface_cfg->name, priority);
828
0
                }
829
0
            }
830
0
        }
831
0
    }
832
0
}
833
834
void
835
dpif_offload_flow_flush(struct dpif *dpif)
836
0
{
837
0
    struct dpif_offload_provider_collection *collection;
838
0
    struct dpif_offload *offload;
839
840
0
    collection = dpif_get_offload_provider_collection(dpif);
841
0
    if (!collection) {
842
0
        return;
843
0
    }
844
845
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
846
0
        if (offload->class->flow_flush) {
847
0
            int err = offload->class->flow_flush(offload);
848
849
0
            if (err) {
850
0
                VLOG_ERR(
851
0
                    "Failed flow flush on dpif-offload provider %s, error %s",
852
0
                    dpif_offload_name(offload), ovs_strerror(err));
853
0
            }
854
0
        }
855
0
    }
856
0
}
857
858
enum dpif_offload_impl_type
859
dpif_offload_get_impl_type(const struct dpif_offload *offload)
860
0
{
861
0
    return offload->class->impl_type;
862
0
}
863
864
enum dpif_offload_impl_type
865
dpif_offload_get_impl_type_by_class(const char *type)
866
0
{
867
0
    enum dpif_offload_impl_type impl_type = DPIF_OFFLOAD_IMPL_NONE;
868
0
    struct shash_node *node;
869
870
0
    ovs_mutex_lock(&dpif_offload_mutex);
871
0
    SHASH_FOR_EACH (node, &dpif_offload_classes) {
872
0
        const struct dpif_offload_class *class = node->data;
873
874
0
        if (!strcmp(type, class->type)) {
875
0
            impl_type = class->impl_type;
876
0
            break;
877
0
        }
878
0
    }
879
0
    ovs_mutex_unlock(&dpif_offload_mutex);
880
881
0
    return impl_type;
882
0
}
883
884
uint64_t
885
dpif_offload_flow_count(const struct dpif *dpif)
886
0
{
887
0
    struct dpif_offload_provider_collection *collection;
888
0
    const struct dpif_offload *offload;
889
0
    uint64_t flow_count = 0;
890
891
0
    collection = dpif_get_offload_provider_collection(dpif);
892
0
    if (!collection || !dpif_offload_enabled()) {
893
0
        return 0;
894
0
    }
895
896
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
897
0
        if (offload->class->flow_count) {
898
0
            flow_count += offload->class->flow_count(offload);
899
0
        }
900
0
    }
901
902
0
    return flow_count;
903
0
}
904
905
uint64_t
906
dpif_offload_flow_count_by_impl(const struct dpif *dpif,
907
                                enum dpif_offload_impl_type type)
908
0
{
909
0
     struct dpif_offload_provider_collection *collection;
910
0
    const struct dpif_offload *offload;
911
0
    uint64_t flow_count = 0;
912
913
0
    collection = dpif_get_offload_provider_collection(dpif);
914
0
    if (!collection || !dpif_offload_enabled()) {
915
0
        return 0;
916
0
    }
917
918
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
919
0
        if (offload->class->flow_count
920
0
            && type == dpif_offload_get_impl_type(offload)) {
921
0
            flow_count += offload->class->flow_count(offload);
922
0
        }
923
0
    }
924
925
0
    return flow_count;
926
0
}
927
928
void
929
dpif_offload_meter_set(const struct dpif *dpif, ofproto_meter_id meter_id,
930
                       struct ofputil_meter_config *config)
931
0
{
932
0
    struct dpif_offload_provider_collection *collection;
933
0
    struct dpif_offload *offload;
934
935
0
    collection = dpif_get_offload_provider_collection(dpif);
936
0
    if (!collection || !dpif_offload_enabled()) {
937
0
        return;
938
0
    }
939
940
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
941
0
        if (offload->class->meter_set) {
942
0
            int err = offload->class->meter_set(offload, meter_id, config);
943
944
0
            if (err) {
945
                /* Offload APIs could fail, for example, because the offload
946
                 * is not supported.  This is fine, as the offload API should
947
                 * take care of this. */
948
0
                VLOG_DBG_RL(&rl_dbg,
949
0
                            "Failed setting meter %u on dpif-offload provider"
950
0
                            " %s, error %s", meter_id.uint32,
951
0
                            dpif_offload_name(offload), ovs_strerror(err));
952
0
            }
953
0
        }
954
0
    }
955
0
}
956
957
void
958
dpif_offload_meter_get(const struct dpif *dpif, ofproto_meter_id meter_id,
959
                       struct ofputil_meter_stats *stats)
960
0
{
961
0
    struct dpif_offload_provider_collection *collection;
962
0
    struct dpif_offload *offload;
963
964
0
    collection = dpif_get_offload_provider_collection(dpif);
965
0
    if (!collection || !dpif_offload_enabled()) {
966
0
        return;
967
0
    }
968
969
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
970
0
        if (offload->class->meter_get) {
971
0
            int err = offload->class->meter_get(offload, meter_id, stats);
972
0
            if (err) {
973
0
                VLOG_DBG_RL(&rl_dbg,
974
0
                            "Failed getting meter %u on dpif-offload provider"
975
0
                            " %s, error %s", meter_id.uint32,
976
0
                            dpif_offload_name(offload), ovs_strerror(err));
977
0
            }
978
0
        }
979
0
    }
980
0
}
981
982
void
983
dpif_offload_meter_del(const struct dpif *dpif, ofproto_meter_id meter_id,
984
                       struct ofputil_meter_stats *stats)
985
0
{
986
0
    struct dpif_offload_provider_collection *collection;
987
0
    struct dpif_offload *offload;
988
989
0
    collection = dpif_get_offload_provider_collection(dpif);
990
0
    if (!collection || !dpif_offload_enabled()) {
991
0
        return;
992
0
    }
993
994
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
995
0
        if (offload->class->meter_del) {
996
0
            int err = offload->class->meter_del(offload, meter_id, stats);
997
0
            if (err) {
998
0
                VLOG_DBG_RL(&rl_dbg,
999
0
                            "Failed deleting meter %u on dpif-offload provider"
1000
0
                            " %s, error %s", meter_id.uint32,
1001
0
                            dpif_offload_name(offload), ovs_strerror(err));
1002
0
            }
1003
0
        }
1004
0
    }
1005
0
}
1006
1007
/*
1008
 * Further initializes a 'struct dpif_flow_dump' that was already initialized
1009
 * by dpif_flow_dump_create(), preparing it for use by
1010
 * dpif_offload_flow_dump_next().
1011
 *
1012
 * For more details, see the documentation of dpif_flow_dump_create(). */
1013
void
1014
dpif_offload_flow_dump_create(struct dpif_flow_dump *dump,
1015
                              const struct dpif *dpif, bool terse)
1016
0
{
1017
0
    struct dpif_offload_provider_collection *collection;
1018
0
    const struct dpif_offload *offload;
1019
0
    size_t n_providers = 0;
1020
0
    int i = 0;
1021
1022
0
    collection = dpif_get_offload_provider_collection(dpif);
1023
0
    if (!dump || !dpif_offload_enabled() || !collection) {
1024
0
        return;
1025
0
    }
1026
1027
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
1028
0
        if (offload->class->flow_dump_create) {
1029
0
            n_providers++;
1030
0
        }
1031
0
    }
1032
1033
0
    if (!n_providers) {
1034
0
        return;
1035
0
    }
1036
1037
0
    dump->offload_dumps = xmalloc(n_providers * sizeof(
1038
0
        struct dpif_offload_flow_dump *));
1039
1040
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
1041
0
        if (offload->class->flow_dump_create) {
1042
0
            dump->offload_dumps[i++] =
1043
0
                offload->class->flow_dump_create(offload, terse);
1044
0
        }
1045
0
    }
1046
0
    dump->n_offload_dumps = i;
1047
0
    dump->offload_dump_index = 0;
1048
0
}
1049
1050
/* Destroys the 'dump' data associated with the offload flow dump.
1051
 * This function is called as part of the general dump cleanup
1052
 * by dpif_flow_dump_destroy().
1053
 *
1054
 * Returns 0 if all individual dpif-offload dump operations complete
1055
 * without error.  If one or more providers return an error, the error
1056
 * code from the first failing provider is returned as a positive errno
1057
 * value. */
1058
int
1059
dpif_offload_flow_dump_destroy(struct dpif_flow_dump *dump)
1060
0
{
1061
0
    int error = 0;
1062
1063
0
    for (int i = 0; i < dump->n_offload_dumps; i++) {
1064
0
        struct dpif_offload_flow_dump *offload_dump = dump->offload_dumps[i];
1065
0
        const struct dpif_offload *offload = offload_dump->offload;
1066
0
        int rc = offload->class->flow_dump_destroy(offload_dump);
1067
1068
0
        if (rc && rc != EOF) {
1069
0
            VLOG_ERR("Failed flow dumping on dpif-offload provider "
1070
0
                     "%s, error %s", dpif_offload_name(offload),
1071
0
                     ovs_strerror(rc));
1072
0
            if (!error) {
1073
0
                error = rc;
1074
0
            }
1075
0
        }
1076
0
    }
1077
0
    ovs_mutex_destroy(&dump->offload_dump_mutex);
1078
0
    free(dump->offload_dumps);
1079
0
    return error;
1080
0
}
1081
1082
static void
1083
dpif_offload_advance_provider_dump(struct dpif_flow_dump_thread *thread)
1084
0
{
1085
0
    struct dpif_flow_dump *dump = thread->dump;
1086
1087
0
    ovs_mutex_lock(&dump->offload_dump_mutex);
1088
1089
    /* If we haven't finished (dumped all providers). */
1090
0
    if (dump->offload_dump_index < dump->n_offload_dumps) {
1091
        /* If we are the first to find that current dump is finished
1092
         * advance it. */
1093
0
        if (thread->offload_dump_index == dump->offload_dump_index) {
1094
0
            thread->offload_dump_index = ++dump->offload_dump_index;
1095
            /* Did we just finish the last dump? If so we are done. */
1096
0
            if (dump->offload_dump_index == dump->n_offload_dumps) {
1097
0
                thread->offload_dump_done = true;
1098
0
            }
1099
0
        } else {
1100
            /* otherwise, we are behind, catch up */
1101
0
            thread->offload_dump_index = dump->offload_dump_index;
1102
0
        }
1103
0
    } else {
1104
        /* Some other thread finished. */
1105
0
        thread->offload_dump_done = true;
1106
0
    }
1107
1108
0
    ovs_mutex_unlock(&dump->offload_dump_mutex);
1109
0
}
1110
1111
/* This function behaves exactly the same as dpif_flow_dump_next(),
1112
 * so see its documentation for details. */
1113
int
1114
dpif_offload_flow_dump_next(struct dpif_flow_dump_thread *thread,
1115
                            struct dpif_flow *flows, int max_flows)
1116
0
{
1117
0
    int n_flows = 0;
1118
1119
0
    ovs_assert(max_flows > 0);
1120
1121
    /* The logic here processes all registered offload providers and
1122
     * dumps all related flows.  If done (i.e., it returns 0), continue
1123
     * with the next offload provider. */
1124
0
    while (!thread->offload_dump_done) {
1125
0
        struct dpif_offload_flow_dump_thread *offload_thread;
1126
1127
0
        ovs_assert(thread->offload_dump_index < thread->n_offload_threads);
1128
0
        offload_thread = thread->offload_threads[thread->offload_dump_index];
1129
0
        n_flows = offload_thread->dump->offload->class->flow_dump_next(
1130
0
                        offload_thread, flows, max_flows);
1131
1132
0
        if (n_flows > 0) {
1133
            /* If we got some flows, we need to return due to the constraint
1134
             * on returned flows, as explained in dpif_flow_dump_next(). */
1135
0
            break;
1136
0
        }
1137
0
        dpif_offload_advance_provider_dump(thread);
1138
0
    }
1139
0
    return MAX(n_flows, 0);
1140
0
}
1141
1142
/* Further initializes a 'struct dpif_flow_dump_thread' that was already
1143
 * initialized by dpif_flow_dump_thread_create(), preparing it for use by
1144
 * dpif_offload_flow_dump_next(). */
1145
void
1146
dpif_offload_flow_dump_thread_create(struct dpif_flow_dump_thread *thread,
1147
                                     struct dpif_flow_dump *dump)
1148
0
{
1149
0
    if (!dpif_offload_enabled() || !dump || !dump->n_offload_dumps) {
1150
0
        return;
1151
0
    }
1152
1153
0
    thread->n_offload_threads = dump->n_offload_dumps;
1154
0
    thread->offload_dump_done = false;
1155
0
    thread->offload_dump_index = 0;
1156
0
    thread->offload_threads =
1157
0
        xmalloc(thread->n_offload_threads * sizeof *thread->offload_threads);
1158
1159
0
    for (int i = 0; i < dump->n_offload_dumps; i++) {
1160
0
        struct dpif_offload_flow_dump *offload_dump = dump->offload_dumps[i];
1161
0
        const struct dpif_offload *offload = offload_dump->offload;
1162
1163
0
        thread->offload_threads[i] =
1164
0
            offload->class->flow_dump_thread_create(offload_dump);
1165
0
    }
1166
0
}
1167
1168
/* Destroys the 'thread' data associated with the offload flow dump.
1169
 * This function is called as part of the general thread cleanup
1170
 * by dpif_flow_dump_thread_destroy(). */
1171
void
1172
dpif_offload_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread)
1173
0
{
1174
0
    for (int i = 0; i < thread->n_offload_threads; i++) {
1175
0
        struct dpif_offload_flow_dump_thread *offload_thread;
1176
0
        const struct dpif_offload *offload;
1177
1178
0
        offload_thread = thread->offload_threads[i];
1179
0
        offload = offload_thread->dump->offload;
1180
0
        offload->class->flow_dump_thread_destroy(offload_thread);
1181
0
    }
1182
0
    free(thread->offload_threads);
1183
0
}
1184
1185
static struct netdev *
1186
dpif_offload_get_netdev_by_port_id__(
1187
    struct dpif_offload_provider_collection *collection,
1188
    struct dpif_offload **offload, odp_port_t port_no)
1189
0
{
1190
0
    struct dpif_offload *tmp_offload;
1191
0
    struct netdev *netdev = NULL;
1192
1193
0
    LIST_FOR_EACH (tmp_offload, dpif_list_node, &collection->list) {
1194
0
        netdev = tmp_offload->class->get_netdev(tmp_offload, port_no);
1195
0
        if (netdev) {
1196
0
            if (offload) {
1197
0
                *offload = tmp_offload;
1198
0
            }
1199
0
            break;
1200
0
        }
1201
0
    }
1202
0
    return netdev;
1203
0
}
1204
1205
struct netdev *
1206
dpif_offload_get_netdev_by_port_id(struct dpif *dpif,
1207
                                   struct dpif_offload **offload,
1208
                                   odp_port_t port_no)
1209
0
{
1210
0
    struct dpif_offload_provider_collection *collection;
1211
1212
0
    collection = dpif_get_offload_provider_collection(dpif);
1213
0
    if (!collection || !dpif_offload_enabled()) {
1214
0
        return NULL;
1215
0
    }
1216
1217
0
    return dpif_offload_get_netdev_by_port_id__(collection, offload, port_no);
1218
0
}
1219
1220
bool
1221
dpif_offload_netdevs_out_of_resources(struct dpif *dpif)
1222
0
{
1223
0
    struct dpif_offload_provider_collection *collection;
1224
0
    struct dpif_offload_port *port;
1225
0
    struct dpif_offload *offload;
1226
1227
0
    collection = dpif_get_offload_provider_collection(dpif);
1228
0
    if (!collection || !dpif_offload_enabled()) {
1229
0
        return false;
1230
0
    }
1231
1232
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
1233
0
        DPIF_OFFLOAD_PORT_FOR_EACH (port, offload) {
1234
0
            if (port->netdev->hw_info.oor) {
1235
0
                return true;
1236
0
            }
1237
0
        }
1238
0
    }
1239
1240
0
    return false;
1241
0
}
1242
1243
/* This function tries to offload the operations to the dpif-offload
1244
 * providers.  It will return the number of operations not handled, whose
1245
 * pointers are re-arranged and available in **ops. */
1246
size_t
1247
dpif_offload_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops,
1248
                     enum dpif_offload_type offload_type)
1249
0
{
1250
0
    struct dpif_offload_provider_collection *collection;
1251
0
    const struct dpif_offload *offload;
1252
0
    size_t n_ops_left = 0;
1253
1254
0
    collection = dpif_get_offload_provider_collection(dpif);
1255
0
    if (!collection || !dpif_offload_enabled()) {
1256
0
        return n_ops;
1257
0
    }
1258
1259
0
    for (size_t i = 0; i < n_ops; i++) {
1260
0
        ops[i]->error = -1;
1261
0
    }
1262
1263
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
1264
0
        if (offload->class->impl_type == DPIF_OFFLOAD_IMPL_FLOWS_PROVIDER_ONLY
1265
0
            && offload->class->operate) {
1266
1267
0
            offload->class->operate(dpif, offload, ops, n_ops);
1268
1269
0
            for (size_t i = 0; i < n_ops; i++) {
1270
0
                struct dpif_op *op = ops[i];
1271
1272
0
                if (op->error == EOPNOTSUPP) {
1273
                    /* Not supported by this offload provider, try next one. */
1274
0
                    op->error = -1;
1275
0
                } else {
1276
0
                    VLOG_DBG("Tried offloading %d to dpif-offload provider "
1277
0
                             "%s, error %d",
1278
0
                             op->type,
1279
0
                             dpif_offload_name(offload), op->error);
1280
1281
0
                    switch (op->type) {
1282
0
                    case DPIF_OP_FLOW_PUT: {
1283
0
                        int log_error = 0;
1284
1285
                        /* Keep ENOSPC and unhandled operations on the
1286
                         * existing debug-only path, but include full flow
1287
                         * details for real offload failures. */
1288
0
                        if (op->error > 0 && op->error != ENOSPC) {
1289
0
                            log_error = op->error;
1290
0
                        }
1291
0
                        log_flow_put_message(dpif, &this_module,
1292
0
                                             &op->flow_put, log_error);
1293
0
                        break;
1294
0
                    }
1295
0
                    case DPIF_OP_FLOW_DEL:
1296
0
                        log_flow_del_message(dpif, &this_module,
1297
0
                                             &op->flow_del, 0);
1298
0
                        break;
1299
0
                    case DPIF_OP_FLOW_GET:
1300
0
                        log_flow_get_message(dpif, &this_module,
1301
0
                                             &op->flow_get, 0);
1302
0
                        break;
1303
0
                    case DPIF_OP_EXECUTE:
1304
0
                        break;
1305
0
                    }
1306
0
                }
1307
0
            }
1308
0
        }
1309
0
    }
1310
1311
0
    for (size_t i = 0; i < n_ops; i++) {
1312
0
        struct dpif_op *op = ops[i];
1313
1314
0
        if (offload_type == DPIF_OFFLOAD_ALWAYS) {
1315
            /* For DPIF_OFFLOAD_ALWAYS, we should keep the error values,
1316
             * and mark the unprocessed ones as EOPNOTSUPP.  This way, they
1317
             * will not be processed by the dpif layer. */
1318
0
            if (op->error < 0) {
1319
0
                op->error = EOPNOTSUPP;
1320
0
            }
1321
0
            continue;
1322
0
        }
1323
1324
        /* For the other offload types, operations that were not handled or
1325
         * failed to offload should be processed by the dpif layer. */
1326
0
        if (op->error != 0 && op->error != EEXIST) {
1327
0
            op->error = 0;
1328
0
            ops[n_ops_left++] = op;
1329
0
        }
1330
0
    }
1331
1332
0
    return n_ops_left;
1333
0
}
1334
1335

1336
bool
1337
dpif_offload_netdev_same_offload(const struct netdev *a,
1338
                                 const struct netdev *b)
1339
0
{
1340
0
    const struct dpif_offload *offload_a, *offload_b;
1341
1342
0
    offload_a = ovsrcu_get(const struct dpif_offload *, &a->dpif_offload);
1343
0
    offload_b = ovsrcu_get(const struct dpif_offload *, &b->dpif_offload);
1344
1345
0
    return offload_a == offload_b;
1346
0
}
1347
1348
int
1349
dpif_offload_datapath_flow_put(const char *dpif_name,
1350
                               struct dpif_offload_flow_put *put,
1351
                               void **previous_flow_reference)
1352
0
{
1353
0
    struct dpif_offload_provider_collection *collection;
1354
0
    struct dpif_offload *offload;
1355
0
    struct netdev *netdev;
1356
1357
0
    ovs_mutex_lock(&dpif_offload_mutex);
1358
    /* XXX: Implement a faster solution than the current dpif_name lookup. */
1359
0
    collection = shash_find_data(&dpif_offload_providers, dpif_name);
1360
0
    ovs_mutex_unlock(&dpif_offload_mutex);
1361
1362
0
    if (OVS_UNLIKELY(!collection)) {
1363
0
        *previous_flow_reference = NULL;
1364
0
        return EOPNOTSUPP;
1365
0
    }
1366
1367
0
    netdev = dpif_offload_get_netdev_by_port_id__(collection, &offload,
1368
0
                                                  put->in_port);
1369
1370
0
    if (OVS_LIKELY(netdev && offload->class->netdev_flow_put)) {
1371
0
        return offload->class->netdev_flow_put(offload, netdev, put,
1372
0
                                               previous_flow_reference);
1373
0
    }
1374
1375
0
    *previous_flow_reference = NULL;
1376
0
    return EOPNOTSUPP;
1377
0
}
1378
1379
int
1380
dpif_offload_datapath_flow_del(const char *dpif_name,
1381
                               struct dpif_offload_flow_del *del)
1382
0
{
1383
0
    struct dpif_offload_provider_collection *collection;
1384
0
    struct dpif_offload *offload;
1385
0
    struct netdev *netdev;
1386
1387
0
    ovs_mutex_lock(&dpif_offload_mutex);
1388
    /* XXX: Implement a faster solution than the current dpif_name lookup. */
1389
0
    collection = shash_find_data(&dpif_offload_providers, dpif_name);
1390
0
    ovs_mutex_unlock(&dpif_offload_mutex);
1391
1392
0
    if (OVS_UNLIKELY(!collection)) {
1393
0
        return EOPNOTSUPP;
1394
0
    }
1395
1396
0
    netdev = dpif_offload_get_netdev_by_port_id__(collection, &offload,
1397
0
                                                  del->in_port);
1398
1399
0
    if (OVS_LIKELY(netdev && offload->class->netdev_flow_del)) {
1400
0
        return offload->class->netdev_flow_del(offload, netdev, del);
1401
0
    }
1402
1403
0
    return EOPNOTSUPP;
1404
0
}
1405
1406
bool
1407
dpif_offload_datapath_flow_stats(const char *dpif_name, odp_port_t in_port,
1408
                                 const ovs_u128 *ufid,
1409
                                 struct dpif_flow_stats *stats,
1410
                                 struct dpif_flow_attrs *attrs)
1411
0
{
1412
0
    struct dpif_offload_provider_collection *collection;
1413
0
    struct dpif_offload *offload;
1414
0
    struct netdev *netdev;
1415
1416
0
    if (!dpif_offload_enabled()) {
1417
0
        return false;
1418
0
    }
1419
1420
0
    ovs_mutex_lock(&dpif_offload_mutex);
1421
    /* XXX: Implement a faster solution than the current dpif_name lookup. */
1422
0
    collection = shash_find_data(&dpif_offload_providers, dpif_name);
1423
0
    ovs_mutex_unlock(&dpif_offload_mutex);
1424
1425
0
    if (OVS_UNLIKELY(!collection)) {
1426
0
        return false;
1427
0
    }
1428
1429
0
    netdev = dpif_offload_get_netdev_by_port_id__(collection, &offload,
1430
0
                                                  in_port);
1431
1432
0
    if (OVS_LIKELY(netdev && offload->class->netdev_flow_stats)) {
1433
0
        return offload->class->netdev_flow_stats(offload, netdev, ufid, stats,
1434
0
                                                 attrs);
1435
0
    }
1436
0
    return false;
1437
0
}
1438
1439
int
1440
dpif_offload_netdev_hw_post_process(struct netdev *netdev, unsigned pmd_id,
1441
                                    struct dp_packet *packet,
1442
                                    void **flow_reference)
1443
0
{
1444
0
    const struct dpif_offload *offload;
1445
0
    bool post_process_api_supported;
1446
0
    int rc;
1447
1448
0
    atomic_read_relaxed(&netdev->hw_info.post_process_api_supported,
1449
0
                        &post_process_api_supported);
1450
0
    if (!post_process_api_supported) {
1451
0
        return EOPNOTSUPP;
1452
0
    }
1453
1454
0
    offload = ovsrcu_get(const struct dpif_offload *, &netdev->dpif_offload);
1455
1456
0
    if (!offload || !offload->class->netdev_hw_post_process) {
1457
0
        if (offload) {
1458
            /* Offload is configured and API unsupported by the port;
1459
             * avoid subsequent calls. */
1460
0
            atomic_store_relaxed(&netdev->hw_info.post_process_api_supported,
1461
0
                                 false);
1462
0
        }
1463
0
        return EOPNOTSUPP;
1464
0
    }
1465
1466
0
    rc = offload->class->netdev_hw_post_process(offload, netdev, pmd_id,
1467
0
                                                packet, flow_reference);
1468
0
    if (rc == EOPNOTSUPP) {
1469
        /* API unsupported by the port; avoid subsequent calls. */
1470
0
        atomic_store_relaxed(&netdev->hw_info.post_process_api_supported,
1471
0
                             false);
1472
0
    }
1473
0
    return rc;
1474
0
}
1475
1476
bool
1477
dpif_offload_netdev_udp_tnl_get_src_port(const struct netdev *ingress_netdev,
1478
                                         struct dp_packet *packet,
1479
                                         ovs_be16 *src_port)
1480
0
{
1481
0
    const struct dpif_offload *offload;
1482
1483
0
    offload = ovsrcu_get(const struct dpif_offload *,
1484
0
                         &ingress_netdev->dpif_offload);
1485
1486
0
    if (OVS_UNLIKELY(!offload)
1487
0
        || !offload->class->netdev_udp_tnl_get_src_port) {
1488
0
        return false;
1489
0
    }
1490
1491
0
    return offload->class->netdev_udp_tnl_get_src_port(offload, ingress_netdev,
1492
0
                                                       packet, src_port);
1493
0
}
1494
1495
void
1496
dpif_offload_datapath_register_flow_unreference_cb(
1497
    struct dpif *dpif, dpif_offload_flow_unreference_cb *cb)
1498
0
{
1499
0
    struct dpif_offload_provider_collection *collection;
1500
0
    const struct dpif_offload *offload;
1501
1502
    /* In this case, we assert to make sure this initialization is done after
1503
     * the offload providers have been assigned to the dpif. */
1504
0
    collection = dpif_get_offload_provider_collection(dpif);
1505
0
    ovs_assert(collection);
1506
1507
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
1508
0
        if (offload->class->register_flow_unreference_cb) {
1509
0
            offload->class->register_flow_unreference_cb(offload, cb);
1510
0
        }
1511
0
    }
1512
0
}
1513
1514

1515
struct dpif_offload_port_mgr *
1516
dpif_offload_port_mgr_init(void)
1517
0
{
1518
0
    struct dpif_offload_port_mgr *mgr = xmalloc(sizeof *mgr);
1519
1520
0
    ovs_mutex_init(&mgr->cmap_mod_lock);
1521
1522
0
    cmap_init(&mgr->odp_port_to_port);
1523
0
    cmap_init(&mgr->netdev_to_port);
1524
0
    cmap_init(&mgr->ifindex_to_port);
1525
1526
0
    return mgr;
1527
0
}
1528
1529
void
1530
dpif_offload_port_mgr_destroy(struct dpif_offload *offload)
1531
0
{
1532
0
    struct dpif_offload_port_mgr *ports;
1533
1534
0
    if (!offload || !offload->ports) {
1535
0
        return;
1536
0
    }
1537
1538
0
    ports = offload->ports;
1539
0
    offload->ports = NULL;
1540
1541
0
    ovs_assert(cmap_count(&ports->odp_port_to_port) == 0);
1542
0
    ovs_assert(cmap_count(&ports->netdev_to_port) == 0);
1543
0
    ovs_assert(cmap_count(&ports->ifindex_to_port) == 0);
1544
1545
0
    cmap_destroy(&ports->odp_port_to_port);
1546
0
    cmap_destroy(&ports->netdev_to_port);
1547
0
    cmap_destroy(&ports->ifindex_to_port);
1548
0
    free(ports);
1549
0
}
1550
1551
struct dpif_offload_port *
1552
dpif_offload_port_mgr_find_by_ifindex(const struct dpif_offload *offload,
1553
                                      int ifindex)
1554
0
{
1555
0
    struct dpif_offload_port_mgr *ports = offload->ports;
1556
0
    struct dpif_offload_port *port;
1557
1558
0
    ovs_assert(ports);
1559
1560
0
    if (ifindex < 0) {
1561
0
        return NULL;
1562
0
    }
1563
1564
0
    CMAP_FOR_EACH_WITH_HASH (port, ifindex_node, hash_int(ifindex, 0),
1565
0
                             &ports->ifindex_to_port) {
1566
0
        if (port->ifindex == ifindex) {
1567
0
            return port;
1568
0
        }
1569
0
    }
1570
0
    return NULL;
1571
0
}
1572
1573
struct dpif_offload_port *
1574
dpif_offload_port_mgr_find_by_netdev(const struct dpif_offload *offload,
1575
                                     struct netdev *netdev)
1576
0
{
1577
0
    struct dpif_offload_port_mgr *ports = offload->ports;
1578
0
    struct dpif_offload_port *port;
1579
1580
0
    ovs_assert(ports);
1581
1582
0
    if (!netdev) {
1583
0
        return NULL;
1584
0
    }
1585
1586
0
    CMAP_FOR_EACH_WITH_HASH (port, netdev_node, hash_pointer(netdev, 0),
1587
0
                             &ports->netdev_to_port) {
1588
0
        if (port->netdev == netdev) {
1589
0
            return port;
1590
0
        }
1591
0
    }
1592
0
    return NULL;
1593
0
}
1594
1595
struct dpif_offload_port *
1596
dpif_offload_port_mgr_find_by_odp_port(const struct dpif_offload *offload,
1597
                                       odp_port_t port_no)
1598
0
{
1599
0
    struct dpif_offload_port_mgr *ports = offload->ports;
1600
0
    struct dpif_offload_port *port;
1601
1602
0
    ovs_assert(ports);
1603
1604
0
    CMAP_FOR_EACH_WITH_HASH (port, odp_port_node,
1605
0
                             hash_int(odp_to_u32(port_no), 0),
1606
0
                             &ports->odp_port_to_port) {
1607
0
        if (port->port_no == port_no) {
1608
0
            return port;
1609
0
        }
1610
0
    }
1611
0
    return NULL;
1612
0
}
1613
1614
struct dpif_offload_port *
1615
dpif_offload_port_mgr_remove(struct dpif_offload *offload, odp_port_t port_no)
1616
0
{
1617
    /* Note that it is the caller's responsibility to release the netdev
1618
     * after port removal.  This should probably be done through the
1619
     * ovsrcu_postpone() API. */
1620
0
    struct dpif_offload_port_mgr *ports = offload->ports;
1621
0
    struct dpif_offload_port *port;
1622
1623
0
    ovs_assert(ports);
1624
1625
0
    ovs_mutex_lock(&ports->cmap_mod_lock);
1626
1627
0
    port = dpif_offload_port_mgr_find_by_odp_port(offload, port_no);
1628
1629
0
    if (port) {
1630
0
        cmap_remove(&ports->odp_port_to_port, &port->odp_port_node,
1631
0
                    hash_int(odp_to_u32(port_no), 0));
1632
0
        cmap_remove(&ports->netdev_to_port, &port->netdev_node,
1633
0
                    hash_pointer(port->netdev, 0));
1634
1635
0
        if (port->ifindex >= 0) {
1636
0
            cmap_remove(&ports->ifindex_to_port, &port->ifindex_node,
1637
0
                        hash_int(port->ifindex, 0));
1638
0
        }
1639
0
    }
1640
1641
0
    ovs_mutex_unlock(&ports->cmap_mod_lock);
1642
0
    return port;
1643
0
}
1644
1645
bool
1646
dpif_offload_port_mgr_add(struct dpif_offload *offload,
1647
                          struct dpif_offload_port *port,
1648
                          struct netdev *netdev, odp_port_t port_no,
1649
                          bool need_ifindex)
1650
0
{
1651
0
    struct dpif_offload_port_mgr *ports;
1652
1653
    /* Note that this function takes a reference to the passed-in netdev.
1654
     * However, on port removal it is the caller's responsibility to
1655
     * release this reference. */
1656
0
    ovs_assert(offload->ports && netdev);
1657
1658
0
    ports = offload->ports;
1659
0
    memset(port, 0, sizeof *port);
1660
0
    port->port_no = port_no;
1661
0
    port->ifindex = need_ifindex ? netdev_get_ifindex(netdev) : -1;
1662
1663
0
    ovs_mutex_lock(&ports->cmap_mod_lock);
1664
1665
0
    if (dpif_offload_port_mgr_find_by_odp_port(offload, port_no)
1666
0
        || dpif_offload_port_mgr_find_by_ifindex(offload, port->ifindex)
1667
0
        || dpif_offload_port_mgr_find_by_netdev(offload, netdev)) {
1668
1669
0
        ovs_mutex_unlock(&ports->cmap_mod_lock);
1670
0
        return false;
1671
0
    }
1672
1673
0
    port->netdev = netdev_ref(netdev);
1674
1675
0
    cmap_insert(&ports->odp_port_to_port, &port->odp_port_node,
1676
0
                hash_int(odp_to_u32(port_no), 0));
1677
1678
0
    cmap_insert(&ports->netdev_to_port, &port->netdev_node,
1679
0
                hash_pointer(netdev, 0));
1680
1681
0
    if (port->ifindex >= 0) {
1682
0
        cmap_insert(&ports->ifindex_to_port, &port->ifindex_node,
1683
0
                    hash_int(port->ifindex, 0));
1684
0
    }
1685
1686
0
    ovs_mutex_unlock(&ports->cmap_mod_lock);
1687
0
    return true;
1688
0
}
1689
1690
size_t
1691
dpif_offload_port_mgr_port_count(const struct dpif_offload *offload)
1692
0
{
1693
0
    ovs_assert(offload && offload->ports);
1694
1695
0
    return cmap_count(&offload->ports->odp_port_to_port);
1696
0
}
1697
1698
struct dpif_offload_pmd_ctx_node {
1699
    const struct dpif_offload *offload;
1700
    void *provider_ctx;
1701
};
1702
1703
struct dpif_offload_pmd_ctx {
1704
    unsigned core_id;
1705
    int numa_id;
1706
    size_t n_nodes;
1707
    struct dpif_offload_pmd_ctx_node nodes[TOTAL_PROVIDERS];
1708
};
1709
1710
void
1711
dpif_offload_pmd_thread_reload(const char *dpif_name, unsigned core_id,
1712
                               int numa_id, struct dpif_offload_pmd_ctx **ctx_)
1713
0
{
1714
0
    struct dpif_offload_pmd_ctx_node old_nodes[TOTAL_PROVIDERS];
1715
0
    struct dpif_offload_provider_collection *collection;
1716
0
    struct dpif_offload_pmd_ctx *ctx;
1717
0
    struct dpif_offload *offload;
1718
0
    size_t old_n_nodes = 0;
1719
1720
0
    if (!dpif_offload_enabled()) {
1721
0
        ovs_assert(!*ctx_);
1722
0
        return;
1723
0
    }
1724
1725
0
    ovs_mutex_lock(&dpif_offload_mutex);
1726
0
    collection = shash_find_data(&dpif_offload_providers, dpif_name);
1727
0
    ovs_mutex_unlock(&dpif_offload_mutex);
1728
1729
0
    if (OVS_UNLIKELY(!collection)) {
1730
0
        ovs_assert(!*ctx_);
1731
0
        return;
1732
0
    }
1733
1734
0
    if (!*ctx_) {
1735
        /* XXX: Would be nice if we have a numa specific xzalloc(). */
1736
0
        ctx = xzalloc(sizeof *ctx);
1737
0
        ctx->core_id = core_id;
1738
0
        ctx->numa_id = numa_id;
1739
0
        *ctx_ = ctx;
1740
0
    } else {
1741
0
        ctx = *ctx_;
1742
0
        old_n_nodes = ctx->n_nodes;
1743
1744
0
        if (old_n_nodes) {
1745
0
            memcpy(old_nodes, ctx->nodes, old_n_nodes * sizeof old_nodes[0]);
1746
0
        }
1747
1748
        /* Reset active nodes array. */
1749
0
        memset(ctx->nodes, 0, sizeof ctx->nodes);
1750
0
        ctx->n_nodes = 0;
1751
0
    }
1752
1753
0
    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
1754
1755
0
        ovs_assert(ctx->n_nodes < TOTAL_PROVIDERS);
1756
1757
0
        if (!offload->class->pmd_thread_lifecycle) {
1758
0
            continue;
1759
0
        }
1760
1761
        /* If this is a reload, try to find previous context. */
1762
0
        for (size_t i = 0; i < old_n_nodes; i++) {
1763
0
            struct dpif_offload_pmd_ctx_node *node = &old_nodes[i];
1764
1765
0
            if (offload == node->offload) {
1766
0
                ctx->nodes[ctx->n_nodes].provider_ctx = node->provider_ctx;
1767
0
                break;
1768
0
            }
1769
0
        }
1770
1771
0
        offload->class->pmd_thread_lifecycle(
1772
0
            offload, false, core_id, numa_id,
1773
0
            &ctx->nodes[ctx->n_nodes].provider_ctx);
1774
1775
0
        if (ctx->nodes[ctx->n_nodes].provider_ctx) {
1776
0
            ctx->nodes[ctx->n_nodes].offload = offload;
1777
0
            ctx->n_nodes++;
1778
0
        }
1779
0
    }
1780
0
}
1781
1782
void
1783
dpif_offload_pmd_thread_exit(struct dpif_offload_pmd_ctx *ctx)
1784
0
{
1785
0
    if (!ctx) {
1786
0
        return;
1787
0
    }
1788
1789
0
    for (size_t i = 0; i < ctx->n_nodes; i++) {
1790
0
        struct dpif_offload_pmd_ctx_node *node = &ctx->nodes[i];
1791
1792
0
        node->offload->class->pmd_thread_lifecycle(node->offload, true,
1793
0
                                                   ctx->core_id, ctx->numa_id,
1794
0
                                                   &node->provider_ctx);
1795
0
    }
1796
1797
0
    free(ctx);
1798
0
}