#include "ofproto-dpif-sflow.h"
#include "ofproto-dpif-xlate.h"
#include "ofproto-dpif-xlate-cache.h"
+#include "ofproto-dpif-trace.h"
#include "ovs-rcu.h"
#include "packets.h"
#include "openvswitch/poll-loop.h"
#include "seq.h"
+#include "tunnel.h"
#include "unixctl.h"
#include "openvswitch/vlog.h"
SLOW_PATH_UPCALL, /* Slow path upcall. */
SFLOW_UPCALL, /* sFlow sample. */
FLOW_SAMPLE_UPCALL, /* Per-flow sampling. */
- IPFIX_UPCALL /* Per-bridge sampling. */
+ IPFIX_UPCALL, /* Per-bridge sampling. */
+ CONTROLLER_UPCALL /* Destined for the controller. */
};
enum reval_result {
* dpif-netdev. If a modification is absolutely necessary, a const cast
* may be used with other datapaths. */
const struct flow *flow; /* Parsed representation of the packet. */
+ enum odp_key_fitness fitness; /* Fitness of 'flow' relative to ODP key. */
const ovs_u128 *ufid; /* Unique identifier for 'flow'. */
unsigned pmd_id; /* Datapath poll mode driver id. */
const struct dp_packet *packet; /* Packet associated with this upcall. */
- ofp_port_t in_port; /* OpenFlow in port, or OFPP_NONE. */
+ ofp_port_t ofp_in_port; /* OpenFlow in port, or OFPP_NONE. */
uint16_t mru; /* If !0, Maximum receive unit of
fragmented IP packet */
bool ukey_persists; /* Set true to keep 'ukey' beyond the
lifetime of this upcall. */
- uint64_t dump_seq; /* udpif->dump_seq at translation time. */
uint64_t reval_seq; /* udpif->reval_seq at translation time. */
/* Not used by the upcall callback interface. */
/* Starts the handler and revalidator threads, must be enclosed in
* ovsrcu quiescent state. */
static void
-udpif_start_threads(struct udpif *udpif, size_t n_handlers,
- size_t n_revalidators)
+udpif_start_threads(struct udpif *udpif, size_t n_handlers_,
+ size_t n_revalidators_)
{
- if (udpif && n_handlers && n_revalidators) {
- size_t i;
- bool enable_ufid;
-
- udpif->n_handlers = n_handlers;
- udpif->n_revalidators = n_revalidators;
+ if (udpif && n_handlers_ && n_revalidators_) {
+ udpif->n_handlers = n_handlers_;
+ udpif->n_revalidators = n_revalidators_;
udpif->handlers = xzalloc(udpif->n_handlers * sizeof *udpif->handlers);
- for (i = 0; i < udpif->n_handlers; i++) {
+ for (size_t i = 0; i < udpif->n_handlers; i++) {
struct handler *handler = &udpif->handlers[i];
handler->udpif = udpif;
"handler", udpif_upcall_handler, handler);
}
- enable_ufid = udpif->backer->rt_support.ufid;
- atomic_init(&udpif->enable_ufid, enable_ufid);
+ atomic_init(&udpif->enable_ufid, udpif->backer->rt_support.ufid);
dpif_enable_upcall(udpif->dpif);
ovs_barrier_init(&udpif->reval_barrier, udpif->n_revalidators);
udpif->pause = false;
udpif->revalidators = xzalloc(udpif->n_revalidators
* sizeof *udpif->revalidators);
- for (i = 0; i < udpif->n_revalidators; i++) {
+ for (size_t i = 0; i < udpif->n_revalidators; i++) {
struct revalidator *revalidator = &udpif->revalidators[i];
revalidator->udpif = udpif;
}
/* Tells 'udpif' how many threads it should use to handle upcalls.
- * 'n_handlers' and 'n_revalidators' can never be zero. 'udpif''s
+ * 'n_handlers_' and 'n_revalidators_' can never be zero. 'udpif''s
* datapath handle must have packet reception enabled before starting
* threads. */
void
-udpif_set_threads(struct udpif *udpif, size_t n_handlers,
- size_t n_revalidators)
+udpif_set_threads(struct udpif *udpif, size_t n_handlers_,
+ size_t n_revalidators_)
{
ovs_assert(udpif);
- ovs_assert(n_handlers && n_revalidators);
+ ovs_assert(n_handlers_ && n_revalidators_);
ovsrcu_quiesce_start();
- if (udpif->n_handlers != n_handlers
- || udpif->n_revalidators != n_revalidators) {
+ if (udpif->n_handlers != n_handlers_
+ || udpif->n_revalidators != n_revalidators_) {
udpif_stop_threads(udpif);
}
if (!udpif->handlers && !udpif->revalidators) {
int error;
- error = dpif_handlers_set(udpif->dpif, n_handlers);
+ error = dpif_handlers_set(udpif->dpif, n_handlers_);
if (error) {
VLOG_ERR("failed to configure handlers in dpif %s: %s",
dpif_name(udpif->dpif), ovs_strerror(error));
return;
}
- udpif_start_threads(udpif, n_handlers, n_revalidators);
+ udpif_start_threads(udpif, n_handlers_, n_revalidators_);
}
ovsrcu_quiesce_end();
}
/* This is stronger than necessary. It would be sufficient to ensure
* (somehow) that each handler and revalidator thread had passed through
* its main loop once. */
- size_t n_handlers = udpif->n_handlers;
- size_t n_revalidators = udpif->n_revalidators;
+ size_t n_handlers_ = udpif->n_handlers;
+ size_t n_revalidators_ = udpif->n_revalidators;
ovsrcu_quiesce_start();
udpif_stop_threads(udpif);
- udpif_start_threads(udpif, n_handlers, n_revalidators);
+ udpif_start_threads(udpif, n_handlers_, n_revalidators_);
ovsrcu_quiesce_end();
}
void
udpif_flush(struct udpif *udpif)
{
- size_t n_handlers, n_revalidators;
-
- n_handlers = udpif->n_handlers;
- n_revalidators = udpif->n_revalidators;
+ size_t n_handlers_ = udpif->n_handlers;
+ size_t n_revalidators_ = udpif->n_revalidators;
ovsrcu_quiesce_start();
udpif_stop_threads(udpif);
dpif_flow_flush(udpif->dpif);
- udpif_start_threads(udpif, n_handlers, n_revalidators);
+ udpif_start_threads(udpif, n_handlers_, n_revalidators_);
ovsrcu_quiesce_end();
}
break;
}
- if (odp_flow_key_to_flow(dupcall->key, dupcall->key_len, flow)
- == ODP_FIT_ERROR) {
+ upcall->fitness = odp_flow_key_to_flow(dupcall->key, dupcall->key_len,
+ flow);
+ if (upcall->fitness == ODP_FIT_ERROR) {
goto free_dupcall;
}
return FLOW_SAMPLE_UPCALL;
} else if (cookie->type == USER_ACTION_COOKIE_IPFIX) {
return IPFIX_UPCALL;
+ } else if (cookie->type == USER_ACTION_COOKIE_CONTROLLER) {
+ return CONTROLLER_UPCALL;
} else {
VLOG_WARN_RL(&rl, "invalid user cookie of type %"PRIu16
" and size %"PRIuSIZE, cookie->type, userdata_len);
* initialized with at least 128 bytes of space. */
static void
compose_slow_path(struct udpif *udpif, struct xlate_out *xout,
- const struct flow *flow, odp_port_t odp_in_port,
- struct ofpbuf *buf, uint32_t slowpath_meter_id,
- uint32_t controller_meter_id)
+ const struct flow *flow,
+ odp_port_t odp_in_port, ofp_port_t ofp_in_port,
+ struct ofpbuf *buf, uint32_t meter_id,
+ struct uuid *ofproto_uuid)
{
struct user_action_cookie cookie;
odp_port_t port;
uint32_t pid;
cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
- cookie.slow_path.unused = 0;
+ cookie.ofp_in_port = ofp_in_port;
+ cookie.ofproto_uuid = *ofproto_uuid;
cookie.slow_path.reason = xout->slow;
port = xout->slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP)
size_t offset;
size_t ac_offset;
- uint32_t meter_id = xout->slow & SLOW_CONTROLLER ? controller_meter_id
- : slowpath_meter_id;
-
if (meter_id != UINT32_MAX) {
/* If slowpath meter is configured, generate clone(meter, userspace)
* action. */
upcall->type = classify_upcall(type, userdata, &upcall->cookie);
if (upcall->type == BAD_UPCALL) {
return EAGAIN;
- }
-
- error = xlate_lookup(backer, flow, &upcall->ofproto, &upcall->ipfix,
- &upcall->sflow, NULL, &upcall->in_port);
- if (error) {
- return error;
+ } else if (upcall->type == MISS_UPCALL) {
+ error = xlate_lookup(backer, flow, &upcall->ofproto, &upcall->ipfix,
+ &upcall->sflow, NULL, &upcall->ofp_in_port);
+ if (error) {
+ return error;
+ }
+ } else {
+ struct ofproto_dpif *ofproto
+ = ofproto_dpif_lookup_by_uuid(&upcall->cookie.ofproto_uuid);
+ if (!ofproto) {
+ VLOG_INFO_RL(&rl, "upcall could not find ofproto");
+ return ENODEV;
+ }
+ upcall->ofproto = ofproto;
+ upcall->ipfix = ofproto->ipfix;
+ upcall->sflow = ofproto->sflow;
+ upcall->ofp_in_port = upcall->cookie.ofp_in_port;
}
upcall->recirc = NULL;
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
{
struct dpif_flow_stats stats;
+ enum xlate_error xerr;
struct xlate_in xin;
+ struct ds output;
stats.n_packets = 1;
stats.n_bytes = dp_packet_size(upcall->packet);
xlate_in_init(&xin, upcall->ofproto,
ofproto_dpif_get_tables_version(upcall->ofproto),
- upcall->flow, upcall->in_port, NULL,
+ upcall->flow, upcall->ofp_in_port, NULL,
stats.tcp_flags, upcall->packet, wc, odp_actions);
if (upcall->type == MISS_UPCALL) {
* with pushing its stats eventually. */
}
- upcall->dump_seq = seq_read(udpif->dump_seq);
upcall->reval_seq = seq_read(udpif->reval_seq);
- xlate_actions(&xin, &upcall->xout);
+ xerr = xlate_actions(&xin, &upcall->xout);
+
+ /* Translate again and log the ofproto trace for
+ * these two error types. */
+ if (xerr == XLATE_RECURSION_TOO_DEEP ||
+ xerr == XLATE_TOO_MANY_RESUBMITS) {
+ static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(1, 1);
+
+ /* This is a huge log, so be conservative. */
+ if (!VLOG_DROP_WARN(&rll)) {
+ ds_init(&output);
+ ofproto_trace(upcall->ofproto, upcall->flow,
+ upcall->packet, NULL, 0, NULL, &output);
+ VLOG_WARN("%s", ds_cstr(&output));
+ ds_destroy(&output);
+ }
+ }
+
if (wc) {
/* Convert the input port wildcard from OFP to ODP format. There's no
* real way to do this for arbitrary bitmasks since the numbering spaces
upcall->xout_initialized = true;
+ if (upcall->fitness == ODP_FIT_TOO_LITTLE) {
+ upcall->xout.slow |= SLOW_MATCH;
+ }
if (!upcall->xout.slow) {
ofpbuf_use_const(&upcall->put_actions,
odp_actions->data, odp_actions->size);
} else {
- uint32_t smid = upcall->ofproto->up.slowpath_meter_id;
- uint32_t cmid = upcall->ofproto->up.controller_meter_id;
/* upcall->put_actions already initialized by upcall_receive(). */
compose_slow_path(udpif, &upcall->xout, upcall->flow,
- upcall->flow->in_port.odp_port,
- &upcall->put_actions, smid, cmid);
+ upcall->flow->in_port.odp_port, upcall->ofp_in_port,
+ &upcall->put_actions,
+ upcall->ofproto->up.slowpath_meter_id,
+ &upcall->ofproto->uuid);
}
/* This function is also called for slow-pathed flows. As we are only
const struct nlattr *userdata, struct ofpbuf *actions,
struct flow_wildcards *wc, struct ofpbuf *put_actions, void *aux)
{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
struct udpif *udpif = aux;
struct upcall upcall;
bool megaflow;
return error;
}
+ upcall.fitness = ODP_FIT_PERFECT;
error = process_upcall(udpif, &upcall, actions, wc);
if (error) {
goto out;
}
if (upcall.ukey && !ukey_install(udpif, upcall.ukey)) {
- VLOG_WARN_RL(&rl, "upcall_cb failure: ukey installation fails");
+ static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(1, 1);
+ VLOG_WARN_RL(&rll, "upcall_cb failure: ukey installation fails");
error = ENOSPC;
}
out:
switch (type) {
case SFLOW_UPCALL:
- dpif_sflow_read_actions(flow, actions, actions_len, upcall_data);
+ dpif_sflow_read_actions(flow, actions, actions_len, upcall_data, true);
break;
case FLOW_SAMPLE_UPCALL:
case IPFIX_UPCALL:
case BAD_UPCALL:
case MISS_UPCALL:
case SLOW_PATH_UPCALL:
+ case CONTROLLER_UPCALL:
default:
break;
}
break;
case IPFIX_UPCALL:
+ case FLOW_SAMPLE_UPCALL:
if (upcall->ipfix) {
struct flow_tnl output_tunnel_key;
struct dpif_ipfix_actions ipfix_actions;
actions_len = dpif_read_actions(udpif, upcall, flow,
upcall->type, &ipfix_actions);
- dpif_ipfix_bridge_sample(upcall->ipfix, packet, flow,
- flow->in_port.odp_port,
- upcall->cookie.ipfix.output_odp_port,
- upcall->out_tun_key ?
- &output_tunnel_key : NULL,
- actions_len > 0 ? &ipfix_actions: NULL);
+ if (upcall->type == IPFIX_UPCALL) {
+ dpif_ipfix_bridge_sample(upcall->ipfix, packet, flow,
+ flow->in_port.odp_port,
+ upcall->cookie.ipfix.output_odp_port,
+ upcall->out_tun_key ?
+ &output_tunnel_key : NULL,
+ actions_len > 0 ?
+ &ipfix_actions: NULL);
+ } else {
+ /* The flow reflects exactly the contents of the packet.
+ * Sample the packet using it. */
+ dpif_ipfix_flow_sample(upcall->ipfix, packet, flow,
+ &upcall->cookie, flow->in_port.odp_port,
+ upcall->out_tun_key ?
+ &output_tunnel_key : NULL,
+ actions_len > 0 ? &ipfix_actions: NULL);
+ }
}
break;
- case FLOW_SAMPLE_UPCALL:
- if (upcall->ipfix) {
- struct flow_tnl output_tunnel_key;
- struct dpif_ipfix_actions ipfix_actions;
+ case CONTROLLER_UPCALL:
+ {
+ struct user_action_cookie *cookie = &upcall->cookie;
- memset(&ipfix_actions, 0, sizeof ipfix_actions);
+ if (cookie->controller.dont_send) {
+ return 0;
+ }
- if (upcall->out_tun_key) {
- odp_tun_key_from_attr(upcall->out_tun_key, &output_tunnel_key);
+ uint32_t recirc_id = cookie->controller.recirc_id;
+ if (!recirc_id) {
+ break;
}
- actions_len = dpif_read_actions(udpif, upcall, flow,
- upcall->type, &ipfix_actions);
- /* The flow reflects exactly the contents of the packet.
- * Sample the packet using it. */
- dpif_ipfix_flow_sample(upcall->ipfix, packet, flow,
- &upcall->cookie, flow->in_port.odp_port,
- upcall->out_tun_key ?
- &output_tunnel_key : NULL,
- actions_len > 0 ? &ipfix_actions: NULL);
+ const struct recirc_id_node *recirc_node
+ = recirc_id_node_find(recirc_id);
+ if (!recirc_node) {
+ break;
+ }
+
+ const struct frozen_state *state = &recirc_node->state;
+
+ struct ofproto_async_msg *am = xmalloc(sizeof *am);
+ *am = (struct ofproto_async_msg) {
+ .controller_id = cookie->controller.controller_id,
+ .oam = OAM_PACKET_IN,
+ .pin = {
+ .up = {
+ .base = {
+ .packet = xmemdup(dp_packet_data(packet),
+ dp_packet_size(packet)),
+ .packet_len = dp_packet_size(packet),
+ .reason = cookie->controller.reason,
+ .table_id = state->table_id,
+ .cookie = get_32aligned_be64(
+ &cookie->controller.rule_cookie),
+ .userdata = (recirc_node->state.userdata_len
+ ? xmemdup(recirc_node->state.userdata,
+ recirc_node->state.userdata_len)
+ : NULL),
+ .userdata_len = recirc_node->state.userdata_len,
+ },
+ },
+ .max_len = cookie->controller.max_len,
+ },
+ };
+
+ if (cookie->controller.continuation) {
+ am->pin.up.stack = (state->stack_size
+ ? xmemdup(state->stack, state->stack_size)
+ : NULL),
+ am->pin.up.stack_size = state->stack_size,
+ am->pin.up.mirrors = state->mirrors,
+ am->pin.up.conntracked = state->conntracked,
+ am->pin.up.actions = (state->ofpacts_len
+ ? xmemdup(state->ofpacts,
+ state->ofpacts_len) : NULL),
+ am->pin.up.actions_len = state->ofpacts_len,
+ am->pin.up.action_set = (state->action_set_len
+ ? xmemdup(state->action_set,
+ state->action_set_len)
+ : NULL),
+ am->pin.up.action_set_len = state->action_set_len,
+ am->pin.up.bridge = upcall->ofproto->uuid;
+ }
+
+ /* We don't want to use the upcall 'flow', since it may be
+ * more specific than the point at which the "controller"
+ * action was specified. */
+ struct flow frozen_flow;
+
+ frozen_flow = *flow;
+ if (!state->conntracked) {
+ flow_clear_conntrack(&frozen_flow);
+ }
+
+ frozen_metadata_to_flow(&state->metadata, &frozen_flow);
+ flow_get_metadata(&frozen_flow, &am->pin.up.base.flow_metadata);
+
+ ofproto_dpif_send_async_msg(upcall->ofproto, am);
}
break;
* translation is what processes received packets for these
* protocols.
*
- * - For SLOW_CONTROLLER, translation sends the packet to the OpenFlow
- * controller.
- *
* - For SLOW_ACTION, translation executes the actions directly.
*
* The loop fills 'ops' with an array of operations to execute in the
static void
ukey_set_actions(struct udpif_key *ukey, const struct ofpbuf *actions)
{
- ovsrcu_postpone(ofpbuf_delete,
- ovsrcu_get_protected(struct ofpbuf *, &ukey->actions));
+ struct ofpbuf *old_actions = ovsrcu_get_protected(struct ofpbuf *,
+ &ukey->actions);
+
+ if (old_actions) {
+ ovsrcu_postpone(ofpbuf_delete, old_actions);
+ }
+
ovsrcu_set(&ukey->actions, ofpbuf_clone(actions));
}
const struct nlattr *mask, size_t mask_len,
bool ufid_present, const ovs_u128 *ufid,
const unsigned pmd_id, const struct ofpbuf *actions,
- uint64_t dump_seq, uint64_t reval_seq, long long int used,
+ uint64_t reval_seq, long long int used,
uint32_t key_recirc_id, struct xlate_out *xout)
OVS_NO_THREAD_SAFETY_ANALYSIS
{
ukey_set_actions(ukey, actions);
ovs_mutex_init(&ukey->mutex);
- ukey->dump_seq = dump_seq;
+ ukey->dump_seq = 0; /* Not yet dumped */
ukey->reval_seq = reval_seq;
ukey->state = UKEY_CREATED;
ukey->state_thread = ovsthread_id_self();
return ukey_create__(keybuf.data, keybuf.size, maskbuf.data, maskbuf.size,
true, upcall->ufid, upcall->pmd_id,
- &upcall->put_actions, upcall->dump_seq,
- upcall->reval_seq, 0,
+ &upcall->put_actions, upcall->reval_seq, 0,
upcall->have_recirc_ref ? upcall->recirc->id : 0,
&upcall->xout);
}
{
struct dpif_flow full_flow;
struct ofpbuf actions;
- uint64_t dump_seq, reval_seq;
+ uint64_t reval_seq;
uint64_t stub[DPIF_FLOW_BUFSIZE / 8];
const struct nlattr *a;
unsigned int left;
}
}
- dump_seq = seq_read(udpif->dump_seq);
reval_seq = seq_read(udpif->reval_seq) - 1; /* Ensure revalidation. */
ofpbuf_use_const(&actions, &flow->actions, flow->actions_len);
*ukey = ukey_create__(flow->key, flow->key_len,
flow->mask, flow->mask_len, flow->ufid_present,
- &flow->ufid, flow->pmd_id, &actions, dump_seq,
+ &flow->ufid, flow->pmd_id, &actions,
reval_seq, flow->stats.used, 0, NULL);
return 0;
{
struct ofproto_dpif *ofproto;
ofp_port_t ofp_in_port;
+ enum odp_key_fitness fitness;
struct xlate_in xin;
int error;
- if (odp_flow_key_to_flow(key, len, &ctx->flow) == ODP_FIT_ERROR) {
+ fitness = odp_flow_key_to_flow(key, len, &ctx->flow);
+ if (fitness == ODP_FIT_ERROR) {
return EINVAL;
}
}
xin.xcache = ctx->xcache;
xlate_actions(&xin, &ctx->xout);
+ if (fitness == ODP_FIT_TOO_LITTLE) {
+ ctx->xout.slow |= SLOW_MATCH;
+ }
return 0;
}
if (xoutp->slow) {
struct ofproto_dpif *ofproto;
- ofproto = xlate_lookup_ofproto(udpif->backer, &ctx.flow, NULL);
- uint32_t smid = ofproto ? ofproto->up.slowpath_meter_id : UINT32_MAX;
- uint32_t cmid = ofproto ? ofproto->up.controller_meter_id : UINT32_MAX;
+ ofp_port_t ofp_in_port;
+
+ ofproto = xlate_lookup_ofproto(udpif->backer, &ctx.flow, &ofp_in_port);
ofpbuf_clear(odp_actions);
+
+ if (!ofproto) {
+ goto exit;
+ }
+
compose_slow_path(udpif, xoutp, &ctx.flow, ctx.flow.in_port.odp_port,
- odp_actions, smid, cmid);
+ ofp_in_port, odp_actions,
+ ofproto->up.slowpath_meter_id, &ofproto->uuid);
}
if (odp_flow_key_to_mask(ukey->mask, ukey->mask_len, &dp_mask, &ctx.flow)
* but not the newly revalidated wildcard mask (wc), i.e., if revalidation
* tells that the datapath flow is now too generic and must be narrowed
* down. Note that we do not know if the datapath has ignored any of the
- * wildcarded bits, so we may be overtly conservative here. */
+ * wildcarded bits, so we may be overly conservative here. */
if (flow_wildcards_has_extra(&dp_mask, ctx.wc)) {
goto exit;
}
error = xlate_key(udpif, key, key_len, push, &ctx);
if (error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- VLOG_WARN_RL(&rl, "xlate_key failed (%s)!",
+ static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rll, "xlate_key failed (%s)!",
ovs_strerror(error));
} else {
xlate_out_uninit(&ctx.xout);
static void
log_unexpected_flow(const struct dpif_flow *flow, int error)
{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 60);
struct ds ds = DS_EMPTY_INITIALIZER;
ds_put_format(&ds, "Failed to acquire udpif_key corresponding to "
"unexpected flow (%s): ", ovs_strerror(error));
odp_format_ufid(&flow->ufid, &ds);
- VLOG_WARN_RL(&rl, "%s", ds_cstr(&ds));
+
+ static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(10, 60);
+ VLOG_WARN_RL(&rll, "%s", ds_cstr(&ds));
+
ds_destroy(&ds);
}