Coverage Report

Created: 2026-01-31 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openvswitch/lib/dpif-offload-tc.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-offload-tc-private.h"
23
#include "netdev-provider.h"
24
#include "netdev-vport.h"
25
#include "odp-util.h"
26
#include "tc.h"
27
#include "util.h"
28
29
#include "openvswitch/json.h"
30
#include "openvswitch/match.h"
31
#include "openvswitch/vlog.h"
32
33
VLOG_DEFINE_THIS_MODULE(dpif_offload_tc);
34
35
/* dpif offload interface for the tc implementation. */
36
struct tc_offload {
37
    struct dpif_offload offload;
38
    struct dpif_offload_port_mgr *port_mgr;
39
40
    /* Configuration specific variables. */
41
    struct ovsthread_once once_enable; /* Track first-time enablement. */
42
    bool recirc_id_shared;
43
};
44
45
/* tc's flow dump specific data structures. */
46
struct tc_flow_dump {
47
    struct dpif_offload_flow_dump dump;
48
    struct ovs_mutex netdev_dump_mutex;
49
    size_t netdev_dump_index;
50
    size_t netdev_dump_count;
51
    struct netdev_tc_flow_dump *netdev_dumps[];
52
};
53
54
#define FLOW_DUMP_MAX_BATCH 50
55
56
struct tc_flow_dump_thread {
57
    struct dpif_offload_flow_dump_thread thread;
58
    struct tc_flow_dump *dump;
59
    bool netdev_dump_done;
60
    size_t netdev_dump_index;
61
62
    /* (Flows/Key/Mask/Actions) Buffers for netdev dumping. */
63
    struct ofpbuf nl_flows;
64
    struct odputil_keybuf keybuf[FLOW_DUMP_MAX_BATCH];
65
    struct odputil_keybuf maskbuf[FLOW_DUMP_MAX_BATCH];
66
    struct odputil_keybuf actbuf[FLOW_DUMP_MAX_BATCH];
67
};
68
69
static struct tc_offload *
70
tc_offload_cast(const struct dpif_offload *offload)
71
0
{
72
0
    dpif_offload_assert_class(offload, &dpif_offload_tc_class);
73
0
    return CONTAINER_OF(offload, struct tc_offload, offload);
74
0
}
75
76
static int
77
tc_offload_enable(struct dpif_offload *dpif_offload,
78
                  struct dpif_offload_port_mgr_port *port)
79
0
{
80
0
    int ret = tc_netdev_init(port->netdev);
81
82
0
    if (ret) {
83
0
        VLOG_WARN("%s: Failed assigning flow API 'tc', error %d",
84
0
                  netdev_get_name(port->netdev), ret);
85
0
        return ret;
86
0
    }
87
0
    dpif_offload_set_netdev_offload(port->netdev, dpif_offload);
88
0
    VLOG_INFO("%s: Assigned flow API 'tc'", netdev_get_name(port->netdev));
89
0
    return 0;
90
0
}
91
92
static int
93
tc_offload_cleanup(struct dpif_offload *dpif_offload OVS_UNUSED,
94
                   struct dpif_offload_port_mgr_port *port)
95
0
{
96
0
    dpif_offload_set_netdev_offload(port->netdev, NULL);
97
0
    return 0;
98
0
}
99
100
static int
101
tc_port_add(struct dpif_offload *dpif_offload, struct netdev *netdev,
102
            odp_port_t port_no)
103
0
{
104
0
    struct dpif_offload_port_mgr_port *port = xmalloc(sizeof *port);
105
0
    struct tc_offload *offload = tc_offload_cast(dpif_offload);
106
107
0
    if (dpif_offload_port_mgr_add(offload->port_mgr, port, netdev, port_no,
108
0
                                  true)) {
109
0
        if (dpif_offload_enabled()) {
110
0
            return tc_offload_enable(dpif_offload, port);
111
0
        }
112
0
        return 0;
113
0
    }
114
115
0
    free(port);
116
0
    return EEXIST;
117
0
}
118
119
static void
120
tc_free_port(struct dpif_offload_port_mgr_port *port)
121
0
{
122
0
    netdev_close(port->netdev);
123
0
    free(port);
124
0
}
125
126
static int
127
tc_port_del(struct dpif_offload *dpif_offload, odp_port_t port_no)
128
0
{
129
0
    struct tc_offload *offload = tc_offload_cast(dpif_offload);
130
0
    struct dpif_offload_port_mgr_port *port;
131
0
    int ret = 0;
132
133
0
    port = dpif_offload_port_mgr_remove(offload->port_mgr, port_no);
134
0
    if (port) {
135
0
        if (dpif_offload_enabled()) {
136
0
            ret = tc_offload_cleanup(dpif_offload, port);
137
0
        }
138
0
        ovsrcu_postpone(tc_free_port, port);
139
0
    }
140
0
    return ret;
141
0
}
142
143
static int
144
tc_port_dump_start(const struct dpif_offload *offload_, void **statep)
145
0
{
146
0
    struct tc_offload *offload = tc_offload_cast(offload_);
147
148
0
    return dpif_offload_port_mgr_port_dump_start(offload->port_mgr, statep);
149
0
}
150
151
static int
152
tc_port_dump_next(const struct dpif_offload *offload_, void *state,
153
                  struct dpif_offload_port *port)
154
0
{
155
0
    struct tc_offload *offload = tc_offload_cast(offload_);
156
157
0
    return dpif_offload_port_mgr_port_dump_next(offload->port_mgr, state,
158
0
                                                port);
159
0
}
160
161
static int
162
tc_port_dump_done(const struct dpif_offload *offload_, void *state)
163
0
{
164
0
    struct tc_offload *offload = tc_offload_cast(offload_);
165
166
0
    return dpif_offload_port_mgr_port_dump_done(offload->port_mgr, state);
167
0
}
168
169
static struct netdev *
170
tc_get_netdev(struct dpif_offload *dpif_offload, odp_port_t port_no)
171
0
{
172
0
    struct tc_offload *offload = tc_offload_cast(dpif_offload);
173
0
    struct dpif_offload_port_mgr_port *port;
174
175
0
    port = dpif_offload_port_mgr_find_by_odp_port(offload->port_mgr, port_no);
176
0
    if (!port) {
177
0
        return NULL;
178
0
    }
179
180
0
    return port->netdev;
181
0
}
182
183
static int
184
tc_offload_open(const struct dpif_offload_class *offload_class,
185
                struct dpif *dpif, struct dpif_offload **dpif_offload)
186
0
{
187
0
    struct tc_offload *offload = xmalloc(sizeof *offload);
188
189
0
    dpif_offload_init(&offload->offload, offload_class, dpif);
190
0
    offload->port_mgr = dpif_offload_port_mgr_init();
191
0
    offload->once_enable = (struct ovsthread_once) OVSTHREAD_ONCE_INITIALIZER;
192
0
    offload->recirc_id_shared = !!(dpif_get_features(dpif)
193
0
                                   & OVS_DP_F_TC_RECIRC_SHARING);
194
195
0
    VLOG_DBG("Datapath %s recirculation id sharing ",
196
0
             offload->recirc_id_shared ? "supports" : "does not support");
197
198
0
    tc_meter_init();
199
200
0
    *dpif_offload = &offload->offload;
201
0
    return 0;
202
0
}
203
204
static void
205
tc_offload_close(struct dpif_offload *dpif_offload)
206
0
{
207
0
    struct tc_offload *offload = tc_offload_cast(dpif_offload);
208
0
    struct dpif_offload_port_mgr_port *port;
209
210
0
    DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload->port_mgr) {
211
0
        tc_port_del(dpif_offload, port->port_no);
212
0
    }
213
214
0
    dpif_offload_port_mgr_uninit(offload->port_mgr);
215
0
    ovsthread_once_destroy(&offload->once_enable);
216
0
    free(offload);
217
0
}
218
219
static void
220
tc_offload_set_config(struct dpif_offload *offload_,
221
                      const struct smap *other_cfg)
222
0
{
223
0
    struct tc_offload *offload = tc_offload_cast(offload_);
224
225
0
    if (smap_get_bool(other_cfg, "hw-offload", false)) {
226
0
        if (ovsthread_once_start(&offload->once_enable)) {
227
0
            struct dpif_offload_port_mgr_port *port;
228
229
0
            tc_set_policy(smap_get_def(other_cfg, "tc-policy",
230
0
                                       TC_POLICY_DEFAULT));
231
232
0
            DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload->port_mgr) {
233
0
                tc_offload_enable(offload_, port);
234
0
            }
235
236
0
            ovsthread_once_done(&offload->once_enable);
237
0
        }
238
0
    }
239
0
}
240
241
static void
242
tc_offload_get_debug(const struct dpif_offload *offload_, struct ds *ds,
243
                     struct json *json)
244
0
{
245
0
    struct tc_offload *offload = tc_offload_cast(offload_);
246
247
0
    if (json) {
248
0
        struct json *json_ports = json_object_create();
249
0
        struct dpif_offload_port_mgr_port *port;
250
251
0
        DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload->port_mgr) {
252
0
            struct json *json_port = json_object_create();
253
254
0
            json_object_put(json_port, "port_no",
255
0
                            json_integer_create(odp_to_u32(port->port_no)));
256
0
            json_object_put(json_port, "ifindex",
257
0
                            json_integer_create(port->ifindex));
258
259
0
            json_object_put(json_ports, netdev_get_name(port->netdev),
260
0
                            json_port);
261
0
        }
262
263
0
        if (!json_object_is_empty(json_ports)) {
264
0
            json_object_put(json, "ports", json_ports);
265
0
        } else {
266
0
            json_destroy(json_ports);
267
0
        }
268
0
    } else if (ds) {
269
0
        struct dpif_offload_port_mgr_port *port;
270
271
0
        DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload->port_mgr) {
272
0
            ds_put_format(ds, "  - %s: port_no: %u, ifindex: %d\n",
273
0
                          netdev_get_name(port->netdev),
274
0
                          port->port_no, port->ifindex);
275
0
        }
276
0
    }
277
0
}
278
279
static bool
280
tc_can_offload(struct dpif_offload *dpif_offload OVS_UNUSED,
281
               struct netdev *netdev)
282
0
{
283
0
    if (netdev_vport_is_vport_class(netdev->netdev_class) &&
284
0
        strcmp(netdev_get_dpif_type(netdev), "system")) {
285
0
        VLOG_DBG("%s: vport doesn't belong to the system datapath, skipping",
286
0
                 netdev_get_name(netdev));
287
0
        return false;
288
0
    }
289
0
    return true;
290
0
}
291
292
static int
293
tc_flow_flush(const struct dpif_offload *offload_)
294
0
{
295
0
    struct tc_offload *offload = tc_offload_cast(offload_);
296
0
    struct dpif_offload_port_mgr_port *port;
297
0
    int error = 0;
298
299
0
    DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload->port_mgr) {
300
0
        int rc = tc_netdev_flow_flush(port->netdev);
301
302
0
        if (rc && !error) {
303
0
            error = rc;
304
0
        }
305
0
    }
306
0
    return error;
307
0
}
308
309
static struct tc_flow_dump *
310
tc_flow_dump_cast(struct dpif_offload_flow_dump *dump)
311
0
{
312
0
    return CONTAINER_OF(dump, struct tc_flow_dump, dump);
313
0
}
314
315
static struct tc_flow_dump_thread *
316
tc_flow_dump_thread_cast(
317
    struct dpif_offload_flow_dump_thread *thread)
318
0
{
319
0
    return CONTAINER_OF(thread, struct tc_flow_dump_thread, thread);
320
0
}
321
322
static struct dpif_offload_flow_dump *
323
tc_flow_dump_create(const struct dpif_offload *offload_, bool terse)
324
0
{
325
0
    struct tc_offload *offload = tc_offload_cast(offload_);
326
0
    struct dpif_offload_port_mgr_port *port;
327
0
    size_t added_port_count = 0;
328
0
    struct tc_flow_dump *dump;
329
0
    size_t port_count;
330
331
0
    port_count = dpif_offload_port_mgr_port_count(offload->port_mgr);
332
333
0
    dump = xmalloc(sizeof *dump +
334
0
                   (port_count * sizeof(struct netdev_tc_flow_dump)));
335
336
0
    dpif_offload_flow_dump_init(&dump->dump, offload_, terse);
337
338
0
    DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload->port_mgr) {
339
0
        if (added_port_count >= port_count) {
340
0
            break;
341
0
        }
342
0
        if (tc_netdev_flow_dump_create(
343
0
            port->netdev, &dump->netdev_dumps[added_port_count], terse)) {
344
0
            continue;
345
0
        }
346
0
        dump->netdev_dumps[added_port_count]->port = port->port_no;
347
0
        added_port_count++;
348
0
    }
349
0
    dump->netdev_dump_count = added_port_count;
350
0
    dump->netdev_dump_index = 0;
351
0
    ovs_mutex_init(&dump->netdev_dump_mutex);
352
0
    return &dump->dump;
353
0
}
354
355
static int
356
tc_match_to_dpif_flow(struct match *match, struct ofpbuf *key_buf,
357
                      struct ofpbuf *mask_buf, struct nlattr *actions,
358
                      struct dpif_flow_stats *stats,
359
                      struct dpif_flow_attrs *attrs, ovs_u128 *ufid,
360
                      struct dpif_flow *flow, bool terse)
361
0
{
362
0
    memset(flow, 0, sizeof *flow);
363
364
0
    if (!terse) {
365
0
        struct odp_flow_key_parms odp_parms = {
366
0
            .flow = &match->flow,
367
0
            .mask = &match->wc.masks,
368
0
            .support = {
369
0
                .max_vlan_headers = 2,
370
0
                .recirc = true,
371
0
                .ct_state = true,
372
0
                .ct_zone = true,
373
0
                .ct_mark = true,
374
0
                .ct_label = true,
375
0
            },
376
0
        };
377
0
        size_t offset;
378
379
        /* Key */
380
0
        offset = key_buf->size;
381
0
        flow->key = ofpbuf_tail(key_buf);
382
0
        odp_flow_key_from_flow(&odp_parms, key_buf);
383
0
        flow->key_len = key_buf->size - offset;
384
385
        /* Mask */
386
0
        offset = mask_buf->size;
387
0
        flow->mask = ofpbuf_tail(mask_buf);
388
0
        odp_parms.key_buf = key_buf;
389
0
        odp_flow_key_from_mask(&odp_parms, mask_buf);
390
0
        flow->mask_len = mask_buf->size - offset;
391
392
        /* Actions */
393
0
        flow->actions = nl_attr_get(actions);
394
0
        flow->actions_len = nl_attr_get_size(actions);
395
0
    }
396
397
    /* Stats */
398
0
    memcpy(&flow->stats, stats, sizeof *stats);
399
400
    /* UFID */
401
0
    flow->ufid_present = true;
402
0
    flow->ufid = *ufid;
403
404
0
    flow->pmd_id = PMD_ID_NULL;
405
406
0
    memcpy(&flow->attrs, attrs, sizeof *attrs);
407
408
0
    return 0;
409
0
}
410
411
static void
412
tc_advance_provider_dump(struct tc_flow_dump_thread *thread)
413
0
{
414
0
    struct tc_flow_dump *dump = thread->dump;
415
416
0
    ovs_mutex_lock(&dump->netdev_dump_mutex);
417
418
    /* If we haven't finished (dumped all providers). */
419
0
    if (dump->netdev_dump_index < dump->netdev_dump_count) {
420
        /* If we are the first to find that current dump is finished
421
         * advance it. */
422
0
        if (thread->netdev_dump_index == dump->netdev_dump_index) {
423
0
            thread->netdev_dump_index = ++dump->netdev_dump_index;
424
            /* Did we just finish the last dump? If so we are done. */
425
0
            if (dump->netdev_dump_index == dump->netdev_dump_count) {
426
0
                thread->netdev_dump_done = true;
427
0
            }
428
0
        } else {
429
            /* Otherwise, we are behind, catch up. */
430
0
            thread->netdev_dump_index = dump->netdev_dump_index;
431
0
        }
432
0
    } else {
433
        /* Some other thread finished. */
434
0
        thread->netdev_dump_done = true;
435
0
    }
436
437
0
    ovs_mutex_unlock(&dump->netdev_dump_mutex);
438
0
}
439
440
static int
441
tc_flow_dump_next(struct dpif_offload_flow_dump_thread *thread_,
442
                  struct dpif_flow *flows, int max_flows)
443
0
{
444
0
    struct tc_flow_dump_thread *thread = tc_flow_dump_thread_cast(thread_);
445
0
    int n_flows = 0;
446
447
0
    max_flows = MIN(max_flows, FLOW_DUMP_MAX_BATCH);
448
449
0
    while (!thread->netdev_dump_done && n_flows < max_flows) {
450
0
        struct odputil_keybuf *maskbuf = &thread->maskbuf[n_flows];
451
0
        struct odputil_keybuf *keybuf = &thread->keybuf[n_flows];
452
0
        struct odputil_keybuf *actbuf = &thread->actbuf[n_flows];
453
0
        struct netdev_tc_flow_dump *netdev_dump;
454
0
        struct dpif_flow *f = &flows[n_flows];
455
0
        int cur = thread->netdev_dump_index;
456
0
        struct ofpbuf key, mask, act;
457
0
        struct dpif_flow_stats stats;
458
0
        struct dpif_flow_attrs attrs;
459
0
        struct nlattr *actions;
460
0
        struct match match;
461
0
        ovs_u128 ufid;
462
0
        bool has_next;
463
464
0
        netdev_dump = thread->dump->netdev_dumps[cur];
465
0
        ofpbuf_use_stack(&key, keybuf, sizeof *keybuf);
466
0
        ofpbuf_use_stack(&act, actbuf, sizeof *actbuf);
467
0
        ofpbuf_use_stack(&mask, maskbuf, sizeof *maskbuf);
468
0
        has_next = tc_netdev_flow_dump_next(netdev_dump, &match, &actions,
469
0
                                            &stats, &attrs, &ufid,
470
0
                                            &thread->nl_flows, &act);
471
0
        if (has_next) {
472
0
            tc_match_to_dpif_flow(&match, &key, &mask, actions, &stats,
473
0
                                  &attrs, &ufid, f, thread->dump->dump.terse);
474
0
            n_flows++;
475
0
        } else {
476
0
            tc_advance_provider_dump(thread);
477
0
        }
478
0
    }
479
0
    return n_flows;
480
0
}
481
482
static int
483
tc_flow_dump_destroy(struct dpif_offload_flow_dump *dump_)
484
0
{
485
0
    struct tc_flow_dump *dump = tc_flow_dump_cast(dump_);
486
0
    int error = 0;
487
488
0
    for (int i = 0; i < dump->netdev_dump_count; i++) {
489
0
        struct netdev_tc_flow_dump *dump_netdev = dump->netdev_dumps[i];
490
0
        int rc = tc_netdev_flow_dump_destroy(dump_netdev);
491
492
0
        if (rc && !error) {
493
0
            error = rc;
494
0
        }
495
0
    }
496
0
    ovs_mutex_destroy(&dump->netdev_dump_mutex);
497
0
    free(dump);
498
0
    return error;
499
0
}
500
501
static struct dpif_offload_flow_dump_thread *
502
tc_flow_dump_thread_create(struct dpif_offload_flow_dump *dump)
503
0
{
504
0
    struct tc_flow_dump_thread *thread;
505
506
0
    thread = xmalloc(sizeof *thread);
507
0
    dpif_offload_flow_dump_thread_init(&thread->thread, dump);
508
0
    thread->dump = tc_flow_dump_cast(dump);
509
0
    thread->netdev_dump_index = 0;
510
0
    thread->netdev_dump_done = !thread->dump->netdev_dump_count;
511
0
    ofpbuf_init(&thread->nl_flows, NL_DUMP_BUFSIZE);
512
0
    return &thread->thread;
513
0
}
514
515
static void
516
tc_flow_dump_thread_destroy(struct dpif_offload_flow_dump_thread *thread_)
517
0
{
518
0
    struct tc_flow_dump_thread *thread = tc_flow_dump_thread_cast(thread_);
519
520
0
    ofpbuf_uninit(&thread->nl_flows);
521
0
    free(thread);
522
0
}
523
524
static int
525
tc_parse_flow_put(struct tc_offload *offload_tc, struct dpif *dpif,
526
                  struct dpif_flow_put *put)
527
0
{
528
0
    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
529
0
    struct dpif_offload_port_mgr_port *port;
530
0
    const struct nlattr *nla;
531
0
    struct tc_offload_info info;
532
0
    struct match match;
533
0
    odp_port_t in_port;
534
0
    size_t left;
535
0
    int err;
536
537
0
    info.tc_modify_flow_deleted = false;
538
0
    info.tc_modify_flow = false;
539
540
0
    if (put->flags & DPIF_FP_PROBE) {
541
0
        return EOPNOTSUPP;
542
0
    }
543
544
0
    err = parse_key_and_mask_to_match(put->key, put->key_len, put->mask,
545
0
                                      put->mask_len, &match);
546
0
    if (err) {
547
0
        return err;
548
0
    }
549
550
0
    in_port = match.flow.in_port.odp_port;
551
0
    port = dpif_offload_port_mgr_find_by_odp_port(offload_tc->port_mgr,
552
0
                                                  in_port);
553
0
    if (!port) {
554
0
        return EOPNOTSUPP;
555
0
    }
556
557
    /* Check the output port for a tunnel. */
558
0
    NL_ATTR_FOR_EACH (nla, left, put->actions, put->actions_len) {
559
0
        if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {
560
0
            struct dpif_offload_port_mgr_port *mgr_port;
561
0
            odp_port_t out_port;
562
563
0
            out_port = nl_attr_get_odp_port(nla);
564
0
            mgr_port = dpif_offload_port_mgr_find_by_odp_port(
565
0
                offload_tc->port_mgr, out_port);
566
567
0
            if (!mgr_port) {
568
0
                err = EOPNOTSUPP;
569
0
                goto out;
570
0
            }
571
0
        }
572
0
    }
573
574
0
    info.recirc_id_shared_with_tc = offload_tc->recirc_id_shared;
575
576
0
    err = tc_netdev_flow_put(dpif, port->netdev, &match,
577
0
                             CONST_CAST(struct nlattr *, put->actions),
578
0
                             put->actions_len,
579
0
                             CONST_CAST(ovs_u128 *, put->ufid),
580
0
                             &info, put->stats);
581
582
0
    if (!err) {
583
0
        if (put->flags & DPIF_FP_MODIFY && !info.tc_modify_flow) {
584
0
            struct dpif_op *opp;
585
0
            struct dpif_op op;
586
587
0
            op.type = DPIF_OP_FLOW_DEL;
588
0
            op.flow_del.key = put->key;
589
0
            op.flow_del.key_len = put->key_len;
590
0
            op.flow_del.ufid = put->ufid;
591
0
            op.flow_del.pmd_id = put->pmd_id;
592
0
            op.flow_del.stats = NULL;
593
0
            op.flow_del.terse = false;
594
595
0
            opp = &op;
596
0
            dpif_operate(dpif, &opp, 1, DPIF_OFFLOAD_NEVER);
597
0
        }
598
599
0
        VLOG_DBG("added flow");
600
0
    } else if (err != EEXIST) {
601
0
        struct netdev *oor_netdev = NULL;
602
0
        enum vlog_level level;
603
604
0
        if (err == ENOSPC && dpif_offload_rebalance_policy_enabled()) {
605
            /*
606
             * We need to set OOR on the input netdev (i.e, 'dev') for the
607
             * flow.  But if the flow has a tunnel attribute (i.e, decap
608
             * action, with a virtual device like a VxLAN interface as its
609
             * in-port), then lookup and set OOR on the underlying tunnel
610
             * (real) netdev. */
611
0
            oor_netdev = flow_get_tunnel_netdev(&match.flow.tunnel);
612
0
            if (!oor_netdev) {
613
                /* Not a 'tunnel' flow. */
614
0
                oor_netdev = port->netdev;
615
0
            }
616
0
            netdev_set_hw_info(oor_netdev, HW_INFO_TYPE_OOR, true);
617
0
        }
618
0
        level = (err == ENOSPC || err == EOPNOTSUPP) ? VLL_DBG : VLL_ERR;
619
0
        VLOG_RL(&rl, level, "failed to offload flow: %s: %s",
620
0
                ovs_strerror(err),
621
0
                (oor_netdev ? netdev_get_name(oor_netdev) :
622
0
                              netdev_get_name(port->netdev)));
623
0
    }
624
625
0
out:
626
0
    if (err && err != EEXIST && (put->flags & DPIF_FP_MODIFY)) {
627
        /* Modified rule can't be offloaded, try and delete from HW. */
628
0
        int del_err = 0;
629
630
0
        if (!info.tc_modify_flow_deleted) {
631
0
            del_err = tc_netdev_flow_del(put->ufid, put->stats);
632
0
        }
633
634
0
        if (!del_err) {
635
            /* Delete from hw success, so old flow was offloaded.
636
             * Change flags to create the flow at the dpif level. */
637
0
            put->flags &= ~DPIF_FP_MODIFY;
638
0
            put->flags |= DPIF_FP_CREATE;
639
0
        } else if (del_err != ENOENT) {
640
0
            VLOG_ERR_RL(&rl, "failed to delete offloaded flow: %s",
641
0
                        ovs_strerror(del_err));
642
            /* Stop processing the flow in kernel. */
643
0
            err = 0;
644
0
        }
645
0
    }
646
647
0
    return err;
648
0
}
649
650
static int
651
tc_parse_flow_get(struct tc_offload *offload_tc, struct dpif_flow_get *get)
652
0
{
653
0
    struct dpif_offload_port_mgr_port *port;
654
0
    struct dpif_flow *dpif_flow = get->flow;
655
0
    struct odputil_keybuf maskbuf;
656
0
    struct odputil_keybuf keybuf;
657
0
    struct odputil_keybuf actbuf;
658
0
    struct ofpbuf key, mask, act;
659
0
    struct dpif_flow_stats stats;
660
0
    struct dpif_flow_attrs attrs;
661
0
    uint64_t act_buf[1024 / 8];
662
0
    struct nlattr *actions;
663
0
    struct match match;
664
0
    struct ofpbuf buf;
665
0
    int err = ENOENT;
666
667
0
    ofpbuf_use_stack(&buf, &act_buf, sizeof act_buf);
668
669
0
    DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload_tc->port_mgr) {
670
0
        if (!tc_netdev_flow_get(port->netdev, &match, &actions, get->ufid,
671
0
                                &stats, &attrs, &buf)) {
672
0
            err = 0;
673
0
            break;
674
0
        }
675
0
    }
676
677
0
    if (err) {
678
0
        return err;
679
0
    }
680
681
0
    VLOG_DBG("found flow from netdev, translating to dpif flow");
682
683
0
    ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
684
0
    ofpbuf_use_stack(&act, &actbuf, sizeof actbuf);
685
0
    ofpbuf_use_stack(&mask, &maskbuf, sizeof maskbuf);
686
0
    tc_match_to_dpif_flow(&match, &key, &mask, actions, &stats, &attrs,
687
0
                          (ovs_u128 *) get->ufid, dpif_flow, false);
688
0
    ofpbuf_put(get->buffer, nl_attr_get(actions), nl_attr_get_size(actions));
689
0
    dpif_flow->actions = ofpbuf_at(get->buffer, 0, 0);
690
0
    dpif_flow->actions_len = nl_attr_get_size(actions);
691
692
0
    return 0;
693
0
}
694
695
static void
696
tc_operate(struct dpif *dpif, const struct dpif_offload *offload_,
697
           struct dpif_op **ops, size_t n_ops)
698
0
{
699
0
    struct tc_offload *offload = tc_offload_cast(offload_);
700
701
0
    for (size_t i = 0; i < n_ops; i++) {
702
0
        struct dpif_op *op = ops[i];
703
0
        int error = EOPNOTSUPP;
704
705
0
        if (op->error >= 0) {
706
0
            continue;
707
0
        }
708
709
0
        switch (op->type) {
710
0
        case DPIF_OP_FLOW_PUT: {
711
0
            struct dpif_flow_put *put = &op->flow_put;
712
713
0
            if (!put->ufid) {
714
0
                break;
715
0
            }
716
717
0
            error = tc_parse_flow_put(offload, dpif, put);
718
0
            break;
719
0
        }
720
0
        case DPIF_OP_FLOW_DEL: {
721
0
            struct dpif_flow_del *del = &op->flow_del;
722
723
0
            if (!del->ufid) {
724
0
                break;
725
0
            }
726
727
0
            error = tc_netdev_flow_del(del->ufid, del->stats);
728
0
            break;
729
0
        }
730
0
        case DPIF_OP_FLOW_GET: {
731
0
            struct dpif_flow_get *get = &op->flow_get;
732
733
0
            if (!get->ufid) {
734
0
                break;
735
0
            }
736
737
0
            error = tc_parse_flow_get(offload, get);
738
0
            break;
739
0
        }
740
0
        case DPIF_OP_EXECUTE:
741
0
            break;
742
0
        } /* End of 'switch (op->type)'. */
743
744
0
        if (error != EOPNOTSUPP && error != ENOENT) {
745
            /* If the operation is unsupported or the entry was not found,
746
             * we are skipping this flow operation.  Otherwise, it was
747
             * processed and we should report the result. */
748
0
            op->error = error;
749
0
        }
750
0
    }
751
0
}
752
753
odp_port_t
754
tc_get_port_id_by_ifindex(const struct dpif_offload *offload_, int ifindex)
755
0
{
756
0
    struct tc_offload *offload = tc_offload_cast(offload_);
757
0
    struct dpif_offload_port_mgr_port *port;
758
759
0
    port = dpif_offload_port_mgr_find_by_ifindex(offload->port_mgr, ifindex);
760
0
    if (port) {
761
0
        return port->port_no;
762
0
    }
763
0
    return ODPP_NONE;
764
0
}
765
766
struct dpif_offload_class dpif_offload_tc_class = {
767
    .type = "tc",
768
    .impl_type = DPIF_OFFLOAD_IMPL_FLOWS_PROVIDER_ONLY,
769
    .supported_dpif_types = (const char *const[]) {"system", NULL},
770
    .open = tc_offload_open,
771
    .close = tc_offload_close,
772
    .set_config = tc_offload_set_config,
773
    .get_debug = tc_offload_get_debug,
774
    .can_offload = tc_can_offload,
775
    .port_add = tc_port_add,
776
    .port_del = tc_port_del,
777
    .port_dump_start = tc_port_dump_start,
778
    .port_dump_next = tc_port_dump_next,
779
    .port_dump_done = tc_port_dump_done,
780
    .flow_flush = tc_flow_flush,
781
    .flow_dump_create = tc_flow_dump_create,
782
    .flow_dump_next = tc_flow_dump_next,
783
    .flow_dump_destroy = tc_flow_dump_destroy,
784
    .flow_dump_thread_create = tc_flow_dump_thread_create,
785
    .flow_dump_thread_destroy = tc_flow_dump_thread_destroy,
786
    .operate = tc_operate,
787
    .flow_count = tc_flow_count,
788
    .meter_set = tc_meter_set,
789
    .meter_get = tc_meter_get,
790
    .meter_del = tc_meter_del,
791
    .get_netdev = tc_get_netdev,
792
};