summaryrefslogtreecommitdiff
path: root/ofproto/ofproto-dpif-rid.h
blob: 4df630c62bd3d5d6133e8f4cdc67f8af61f60c65 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
/*
 * Copyright (c) 2014, 2015, 2016, 2017 Nicira, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef OFPROTO_DPIF_RID_H
#define OFPROTO_DPIF_RID_H

#include <stddef.h>
#include <stdint.h>

#include "cmap.h"
#include "ofproto-dpif-mirror.h"
#include "ofproto/ofproto-provider.h"
#include "openvswitch/list.h"
#include "openvswitch/ofp-actions.h"
#include "ovs-thread.h"
#include "uuid.h"

struct ofproto_dpif;
struct rule;

/*
 * Freezing and recirculation
 * ==========================
 *
 * Freezing is a technique for halting and checkpointing packet translation in
 * a way that it can be restarted again later.  This file has a couple of data
 * structures related to freezing in general; their names begin with "frozen".
 *
 * Recirculation is the use of freezing to allow a frame to re-enter the
 * datapath packet processing path to achieve more flexible packet processing,
 * such as modifying header fields after MPLS POP action and selecting a
 * member interface for bond ports.
 *
 *
 * Data path and user space interface
 * -----------------------------------
 *
 * Recirculation uses two uint32_t fields, recirc_id and dp_hash, and a RECIRC
 * action.  recirc_id is used to select the next packet processing steps among
 * multiple instances of recirculation.  When a packet initially enters the
 * datapath it is assigned with recirc_id 0, which indicates no recirculation.
 * Recirc_ids are managed by the user space, opaque to the datapath.
 *
 * On the other hand, dp_hash can only be computed by the datapath, opaque to
 * the user space, as the datapath is free to choose the hashing algorithm
 * without informing user space about it.  The dp_hash value should be
 * wildcarded for newly received packets.  HASH action specifies whether the
 * hash is computed, and if computed, how many fields are to be included in the
 * hash computation.  The computed hash value is stored into the dp_hash field
 * prior to recirculation.
 *
 * The RECIRC action sets the recirc_id field and then reprocesses the packet
 * as if it was received again on the same input port.  RECIRC action works
 * like a function call; actions listed after the RECIRC action will be
 * executed after recirculation.  RECIRC action can be nested, but datapath
 * implementation limits the number of nested recirculations to prevent
 * unreasonable nesting depth or infinite loop.
 *
 * User space recirculation context
 * ---------------------------------
 *
 * Recirculation is usually hidden from the OpenFlow controllers.  Action
 * translation code deduces when recirculation is necessary and issues a
 * datapath recirculation action.  All OpenFlow actions to be performed after
 * recirculation are derived from the OpenFlow pipeline and are stored with the
 * recirculation ID.  When the OpenFlow tables are changed in a way affecting
 * the recirculation flows, new recirculation ID with new metadata and actions
 * is allocated and the old one is timed out.
 *
 * Recirculation ID pool
 * ----------------------
 *
 * Recirculation ID needs to be unique for all datapaths.  Recirculation ID
 * pool keeps track of recirculation ids and stores OpenFlow pipeline
 * translation context so that flow processing may continue after
 * recirculation.
 *
 * A Recirculation ID can be any uint32_t value, except for that the value 0 is
 * reserved for 'no recirculation' case.
 *
 * Thread-safety
 * --------------
 *
 * All APIs are thread safe.
 */

/* Metadata for restoring pipeline context after recirculation.  Helpers
 * are inlined below to keep them together with the definition for easier
 * updates. */
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);

struct frozen_metadata {
    /* Metadata in struct flow. */
    struct flow_tnl tunnel;       /* Encapsulating tunnel parameters. */
    ovs_be64 metadata;            /* OpenFlow Metadata. */
    uint64_t regs[FLOW_N_XREGS];  /* Registers. */
    ofp_port_t in_port;           /* Incoming port. */
};

static inline void
frozen_metadata_from_flow(struct frozen_metadata *md,
                          const struct flow *flow)
{
    memset(md, 0, sizeof *md);
    md->tunnel = flow->tunnel;
    /* It is unsafe for frozen_state to reference tun_table because
     * tun_table is protected by RCU while the lifecycle of frozen_state
     * can span several RCU quiesce states.
     *
     * The latest valid tun_table can be found by ofproto_get_tun_tab()
     * efficiently. */
    md->tunnel.metadata.tab = NULL;
    md->metadata = flow->metadata;
    memcpy(md->regs, flow->regs, sizeof md->regs);
    md->in_port = flow->in_port.ofp_port;
}

static inline void
frozen_metadata_to_flow(struct ofproto *ofproto,
                        const struct frozen_metadata *md,
                        struct flow *flow)
{
    flow->tunnel = md->tunnel;
    flow->tunnel.metadata.tab = ofproto_get_tun_tab(ofproto);
    flow->metadata = md->metadata;
    memcpy(flow->regs, md->regs, sizeof flow->regs);
    flow->in_port.ofp_port = md->in_port;
}

/* State that flow translation can save, to restore when translation
 * resumes.  */
struct frozen_state {
    /* Initial table for processing when thawing. */
    uint8_t table_id;

    /* Pipeline context for processing when thawing. */
    struct uuid ofproto_uuid;     /* Bridge to resume from. */
    struct frozen_metadata metadata; /* Flow metadata. */
    uint8_t *stack;               /* Stack if any. */
    size_t stack_size;
    mirror_mask_t mirrors;        /* Mirrors already output. */
    bool conntracked;             /* Conntrack occurred prior to freeze. */
    bool was_mpls;                /* MPLS packet */
    struct uuid xport_uuid;       /* UUID of 1st port packet received on. */

    /* Actions to be translated when thawing. */
    struct ofpact *ofpacts;
    size_t ofpacts_len;           /* Size of 'ofpacts', in bytes. */
    struct ofpact *action_set;
    size_t action_set_len;        /* Size of 'action_set', in bytes. */

    /* User data for controller userspace cookie. */
    uint8_t *userdata;
    size_t userdata_len;
};

/* This maps a recirculation ID to saved state that flow translation can
 * restore when recirculation occurs. */
struct recirc_id_node {
    /* Index data. */
    struct ovs_list exp_node OVS_GUARDED;
    struct cmap_node id_node;
    struct cmap_node metadata_node;
    uint32_t id;
    uint32_t hash;
    struct ovs_refcount refcount;

    /* Saved state.
     *
     * This state should not be modified after inserting a node in the pool,
     * hence the 'const' to emphasize that. */
    const struct frozen_state state;
};

/* This is only used for bonds and will go away when bonds implementation is
 * updated to use this mechanism instead of internal rules. */
uint32_t recirc_alloc_id(struct ofproto_dpif *);

uint32_t recirc_alloc_id_ctx(const struct frozen_state *);
uint32_t recirc_find_id(const struct frozen_state *);
void recirc_free_id(uint32_t recirc_id);
void recirc_free_ofproto(struct ofproto_dpif *, const char *ofproto_name);

const struct recirc_id_node *recirc_id_node_find(uint32_t recirc_id);
bool recirc_id_node_find_and_ref(uint32_t id);

static inline struct recirc_id_node *
recirc_id_node_from_state(const struct frozen_state *state)
{
    return CONTAINER_OF(state, struct recirc_id_node, state);
}

static inline bool recirc_id_node_try_ref_rcu(const struct recirc_id_node *n_)
{
    struct recirc_id_node *node = CONST_CAST(struct recirc_id_node *, n_);

    return node ? ovs_refcount_try_ref_rcu(&node->refcount) : false;
}

void recirc_id_node_unref(const struct recirc_id_node *);

void recirc_run(void);

/* Recirculation IDs on which references are held. */
struct recirc_refs {
    unsigned n_recircs;
    union {
        uint32_t recirc[2];   /* When n_recircs == 1 or 2 */
        uint32_t *recircs;    /* When 'n_recircs' > 2 */
    };
};

#define RECIRC_REFS_EMPTY_INITIALIZER ((struct recirc_refs) \
                                       { 0, { { 0, 0 } } })
/* Helpers to abstract the recirculation union away. */
static inline void
recirc_refs_init(struct recirc_refs *rr)
{
    *rr = RECIRC_REFS_EMPTY_INITIALIZER;
}

static inline void
recirc_refs_add(struct recirc_refs *rr, uint32_t id)
{
    if (OVS_LIKELY(rr->n_recircs < ARRAY_SIZE(rr->recirc))) {
        rr->recirc[rr->n_recircs++] = id;
    } else {
        if (rr->n_recircs == ARRAY_SIZE(rr->recirc)) {
            uint32_t *recircs = xmalloc(sizeof rr->recirc + sizeof id);

            memcpy(recircs, rr->recirc, sizeof rr->recirc);
            rr->recircs = recircs;
        } else {
            rr->recircs = xrealloc(rr->recircs,
                                   (rr->n_recircs + 1) * sizeof id);
        }
        rr->recircs[rr->n_recircs++] = id;
    }
}

static inline void
recirc_refs_swap(struct recirc_refs *a, struct recirc_refs *b)
{
    struct recirc_refs tmp;

    tmp = *a;
    *a = *b;
    *b = tmp;
}

static inline void
recirc_refs_unref(struct recirc_refs *rr)
{
    if (OVS_LIKELY(rr->n_recircs <= ARRAY_SIZE(rr->recirc))) {
        for (int i = 0; i < rr->n_recircs; i++) {
            recirc_free_id(rr->recirc[i]);
        }
    } else {
        for (int i = 0; i < rr->n_recircs; i++) {
            recirc_free_id(rr->recircs[i]);
        }
        free(rr->recircs);
    }
    rr->n_recircs = 0;
}

#endif