Coverage Report

Created: 2026-02-26 06:41

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