/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "ofp-util.h"
#include "ofpbuf.h"
#include "ofp-print.h"
+#include "ofproto-dpif-governor.h"
#include "ofproto-dpif-sflow.h"
#include "poll-loop.h"
#include "timer.h"
COVERAGE_DEFINE(facet_invalidated);
COVERAGE_DEFINE(facet_revalidate);
COVERAGE_DEFINE(facet_unexpected);
+COVERAGE_DEFINE(facet_suppress);
/* Maximum depth of flow table recursion (due to resubmit actions) in a
* flow translation. */
static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *,
const struct flow *, uint8_t table);
+static void rule_credit_stats(struct rule_dpif *,
+ const struct dpif_flow_stats *);
static void flow_push_stats(struct rule_dpif *, const struct flow *,
- uint64_t packets, uint64_t bytes,
- long long int used);
-
+ const struct dpif_flow_stats *);
static tag_type rule_calculate_tag(const struct flow *,
const struct flow_wildcards *,
uint32_t basis);
static void bundle_run(struct ofbundle *);
static void bundle_wait(struct ofbundle *);
static struct ofbundle *lookup_input_bundle(struct ofproto_dpif *,
- uint16_t in_port, bool warn);
+ uint16_t in_port, bool warn,
+ struct ofport_dpif **in_ofportp);
/* A controller may use OFPP_NONE as the ingress port to indicate that
* it did not arrive on a "real" port. 'ofpp_none_bundle' exists for
* timeouts.) */
uint8_t tcp_flags;
- /* If nonnull, called just before executing a resubmit action. In
- * addition, disables logging of traces when the recursion depth is
- * exceeded.
+ /* If nonnull, flow translation calls this function just before executing a
+ * resubmit or OFPP_TABLE action. In addition, disables logging of traces
+ * when the recursion depth is exceeded.
+ *
+ * 'rule' is the rule being submitted into. It will be null if the
+ * resubmit or OFPP_TABLE action didn't find a matching rule.
+ *
+ * This is normally null so the client has to set it manually after
+ * calling action_xlate_ctx_init(). */
+ void (*resubmit_hook)(struct action_xlate_ctx *, struct rule_dpif *rule);
+
+ /* If nonnull, flow translation credits the specified statistics to each
+ * rule reached through a resubmit or OFPP_TABLE action.
*
* This is normally null so the client has to set it manually after
* calling action_xlate_ctx_init(). */
- void (*resubmit_hook)(struct action_xlate_ctx *, struct rule_dpif *);
+ const struct dpif_flow_stats *resubmit_stats;
/* xlate_actions() initializes and uses these members. The client might want
* to look at them after it returns. */
uint16_t sflow_odp_port; /* Output port for composing sFlow action. */
uint16_t user_cookie_offset;/* Used for user_action_cookie fixup. */
bool exit; /* No further actions should be processed. */
+ struct flow orig_flow; /* Copy of original flow. */
};
static void action_xlate_ctx_init(struct action_xlate_ctx *,
static void subfacet_update_stats(struct subfacet *,
const struct dpif_flow_stats *);
static void subfacet_make_actions(struct subfacet *,
- const struct ofpbuf *packet);
+ const struct ofpbuf *packet,
+ struct ofpbuf *odp_actions);
static int subfacet_install(struct subfacet *,
const struct nlattr *actions, size_t actions_len,
struct dpif_flow_stats *);
struct hmap bundles; /* Contains "struct ofbundle"s. */
struct mac_learning *ml;
struct ofmirror *mirrors[MAX_MIRRORS];
+ bool has_mirrors;
bool has_bonded_bundles;
/* Expiration. */
/* Facets. */
struct hmap facets;
struct hmap subfacets;
+ struct governor *governor;
/* Revalidation. */
struct table_dpif tables[N_TABLES];
hmap_init(&ofproto->facets);
hmap_init(&ofproto->subfacets);
+ ofproto->governor = NULL;
for (i = 0; i < N_TABLES; i++) {
struct table_dpif *table = &ofproto->tables[i];
ofproto_dpif_unixctl_init();
+ ofproto->has_mirrors = false;
ofproto->has_bundle_action = false;
hmap_init(&ofproto->vlandev_map);
hmap_destroy(&ofproto->facets);
hmap_destroy(&ofproto->subfacets);
+ governor_destroy(ofproto->governor);
hmap_destroy(&ofproto->vlandev_map);
hmap_destroy(&ofproto->realdev_vid_map);
}
}
+ if (ofproto->governor) {
+ size_t n_subfacets;
+
+ governor_run(ofproto->governor);
+
+ /* If the governor has shrunk to its minimum size and the number of
+ * subfacets has dwindled, then drop the governor entirely.
+ *
+ * For hysteresis, the number of subfacets to drop the governor is
+ * smaller than the number needed to trigger its creation. */
+ n_subfacets = hmap_count(&ofproto->subfacets);
+ if (n_subfacets * 4 < ofproto->up.flow_eviction_threshold
+ && governor_is_idle(ofproto->governor)) {
+ governor_destroy(ofproto->governor);
+ ofproto->governor = NULL;
+ }
+ }
+
return 0;
}
} else {
timer_wait(&ofproto->next_expiration);
}
+ if (ofproto->governor) {
+ governor_wait(ofproto->governor);
+ }
}
static void
}
ofproto->need_revalidate = true;
+ ofproto->has_mirrors = true;
mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
mirror_update_dups(ofproto);
struct ofproto_dpif *ofproto;
mirror_mask_t mirror_bit;
struct ofbundle *bundle;
+ int i;
if (!mirror) {
return;
free(mirror);
mirror_update_dups(ofproto);
+
+ ofproto->has_mirrors = false;
+ for (i = 0; i < MAX_MIRRORS; i++) {
+ if (ofproto->mirrors[i]) {
+ ofproto->has_mirrors = true;
+ break;
+ }
+ }
}
static int
struct flow_miss_op {
struct dpif_op dpif_op;
- struct subfacet *subfacet;
+ struct subfacet *subfacet; /* Subfacet */
+ void *garbage; /* Pointer to pass to free(), NULL if none. */
+ uint64_t stub[1024 / 8]; /* Temporary buffer. */
};
/* Sends an OFPT_PACKET_IN message for 'packet' of type OFPR_NO_MATCH to each
return NULL;
}
+/* Partially Initializes 'op' as an "execute" operation for 'miss' and
+ * 'packet'. The caller must initialize op->actions and op->actions_len. If
+ * 'miss' is associated with a subfacet the caller must also initialize the
+ * returned op->subfacet, and if anything needs to be freed after processing
+ * the op, the caller must initialize op->garbage also. */
static void
-handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss,
- struct flow_miss_op *ops, size_t *n_ops)
+init_flow_miss_execute_op(struct flow_miss *miss, struct ofpbuf *packet,
+ struct flow_miss_op *op)
+{
+ if (miss->flow.vlan_tci != miss->initial_tci) {
+ /* This packet was received on a VLAN splinter port. We
+ * added a VLAN to the packet to make the packet resemble
+ * the flow, but the actions were composed assuming that
+ * the packet contained no VLAN. So, we must remove the
+ * VLAN header from the packet before trying to execute the
+ * actions. */
+ eth_pop_vlan(packet);
+ }
+
+ op->subfacet = NULL;
+ op->garbage = NULL;
+ op->dpif_op.type = DPIF_OP_EXECUTE;
+ op->dpif_op.u.execute.key = miss->key;
+ op->dpif_op.u.execute.key_len = miss->key_len;
+ op->dpif_op.u.execute.packet = packet;
+}
+
+/* Helper for handle_flow_miss_without_facet() and
+ * handle_flow_miss_with_facet(). */
+static void
+handle_flow_miss_common(struct rule_dpif *rule,
+ struct ofpbuf *packet, const struct flow *flow)
{
- const struct flow *flow = &miss->flow;
- struct subfacet *subfacet;
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+
+ ofproto->n_matches++;
+
+ if (rule->up.cr.priority == FAIL_OPEN_PRIORITY) {
+ /*
+ * Extra-special case for fail-open mode.
+ *
+ * We are in fail-open mode and the packet matched the fail-open
+ * rule, but we are connected to a controller too. We should send
+ * the packet up to the controller in the hope that it will try to
+ * set up a flow and thereby allow us to exit fail-open.
+ *
+ * See the top-level comment in fail-open.c for more information.
+ */
+ send_packet_in_miss(ofproto, packet, flow);
+ }
+}
+
+/* Figures out whether a flow that missed in 'ofproto', whose details are in
+ * 'miss', is likely to be worth tracking in detail in userspace and (usually)
+ * installing a datapath flow. The answer is usually "yes" (a return value of
+ * true). However, for short flows the cost of bookkeeping is much higher than
+ * the benefits, so when the datapath holds a large number of flows we impose
+ * some heuristics to decide which flows are likely to be worth tracking. */
+static bool
+flow_miss_should_make_facet(struct ofproto_dpif *ofproto,
+ struct flow_miss *miss, uint32_t hash)
+{
+ if (!ofproto->governor) {
+ size_t n_subfacets;
+
+ n_subfacets = hmap_count(&ofproto->subfacets);
+ if (n_subfacets * 2 <= ofproto->up.flow_eviction_threshold) {
+ return true;
+ }
+
+ ofproto->governor = governor_create(ofproto->up.name);
+ }
+
+ return governor_should_install_flow(ofproto->governor, hash,
+ list_size(&miss->packets));
+}
+
+/* Handles 'miss', which matches 'rule', without creating a facet or subfacet
+ * or creating any datapath flow. May add an "execute" operation to 'ops' and
+ * increment '*n_ops'. */
+static void
+handle_flow_miss_without_facet(struct flow_miss *miss,
+ struct rule_dpif *rule,
+ struct flow_miss_op *ops, size_t *n_ops)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct action_xlate_ctx ctx;
struct ofpbuf *packet;
- struct facet *facet;
- uint32_t hash;
- /* The caller must ensure that miss->hmap_node.hash contains
- * flow_hash(miss->flow, 0). */
- hash = miss->hmap_node.hash;
+ LIST_FOR_EACH (packet, list_node, &miss->packets) {
+ struct flow_miss_op *op = &ops[*n_ops];
+ struct dpif_flow_stats stats;
+ struct ofpbuf odp_actions;
- facet = facet_lookup_valid(ofproto, flow, hash);
- if (!facet) {
- struct rule_dpif *rule;
+ COVERAGE_INC(facet_suppress);
- rule = rule_dpif_lookup(ofproto, flow, 0);
- if (!rule) {
- /* Don't send a packet-in if OFPUTIL_PC_NO_PACKET_IN asserted. */
- struct ofport_dpif *port = get_ofp_port(ofproto, flow->in_port);
- if (port) {
- if (port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN) {
- COVERAGE_INC(ofproto_dpif_no_packet_in);
- /* XXX install 'drop' flow entry */
- return;
- }
- } else {
- VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16,
- flow->in_port);
- }
+ ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
- LIST_FOR_EACH (packet, list_node, &miss->packets) {
- send_packet_in_miss(ofproto, packet, flow);
- }
+ dpif_flow_stats_extract(&miss->flow, packet, &stats);
+ rule_credit_stats(rule, &stats);
- return;
- }
+ action_xlate_ctx_init(&ctx, ofproto, &miss->flow, miss->initial_tci,
+ rule, 0, packet);
+ ctx.resubmit_stats = &stats;
+ xlate_actions(&ctx, rule->up.actions, rule->up.n_actions,
+ &odp_actions);
+
+ if (odp_actions.size) {
+ struct dpif_execute *execute = &op->dpif_op.u.execute;
+
+ init_flow_miss_execute_op(miss, packet, op);
+ execute->actions = odp_actions.data;
+ execute->actions_len = odp_actions.size;
+ op->garbage = ofpbuf_get_uninit_pointer(&odp_actions);
- facet = facet_create(rule, flow, hash);
+ (*n_ops)++;
+ } else {
+ ofpbuf_uninit(&odp_actions);
+ }
}
+}
+
+/* Handles 'miss', which matches 'facet'. May add any required datapath
+ * operations to 'ops', incrementing '*n_ops' for each new op. */
+static void
+handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
+ struct flow_miss_op *ops, size_t *n_ops)
+{
+ struct subfacet *subfacet;
+ struct ofpbuf *packet;
subfacet = subfacet_create(facet,
miss->key_fitness, miss->key, miss->key_len,
miss->initial_tci);
LIST_FOR_EACH (packet, list_node, &miss->packets) {
+ struct flow_miss_op *op = &ops[*n_ops];
struct dpif_flow_stats stats;
- struct flow_miss_op *op;
- struct dpif_execute *execute;
-
- ofproto->n_matches++;
+ struct ofpbuf odp_actions;
- if (facet->rule->up.cr.priority == FAIL_OPEN_PRIORITY) {
- /*
- * Extra-special case for fail-open mode.
- *
- * We are in fail-open mode and the packet matched the fail-open
- * rule, but we are connected to a controller too. We should send
- * the packet up to the controller in the hope that it will try to
- * set up a flow and thereby allow us to exit fail-open.
- *
- * See the top-level comment in fail-open.c for more information.
- */
- send_packet_in_miss(ofproto, packet, flow);
- }
+ handle_flow_miss_common(facet->rule, packet, &miss->flow);
+ ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
if (!facet->may_install || !subfacet->actions) {
- subfacet_make_actions(subfacet, packet);
+ subfacet_make_actions(subfacet, packet, &odp_actions);
}
dpif_flow_stats_extract(&facet->flow, packet, &stats);
subfacet_update_stats(subfacet, &stats);
- if (!subfacet->actions_len) {
- /* No actions to execute, so skip talking to the dpif. */
- continue;
- }
+ if (subfacet->actions_len) {
+ struct dpif_execute *execute = &op->dpif_op.u.execute;
- if (flow->vlan_tci != subfacet->initial_tci) {
- /* This packet was received on a VLAN splinter port. We added
- * a VLAN to the packet to make the packet resemble the flow,
- * but the actions were composed assuming that the packet
- * contained no VLAN. So, we must remove the VLAN header from
- * the packet before trying to execute the actions. */
- eth_pop_vlan(packet);
- }
+ init_flow_miss_execute_op(miss, packet, op);
+ op->subfacet = subfacet;
+ if (facet->may_install) {
+ execute->actions = subfacet->actions;
+ execute->actions_len = subfacet->actions_len;
+ ofpbuf_uninit(&odp_actions);
+ } else {
+ execute->actions = odp_actions.data;
+ execute->actions_len = odp_actions.size;
+ op->garbage = ofpbuf_get_uninit_pointer(&odp_actions);
+ }
- op = &ops[(*n_ops)++];
- execute = &op->dpif_op.u.execute;
- op->subfacet = subfacet;
- op->dpif_op.type = DPIF_OP_EXECUTE;
- execute->key = miss->key;
- execute->key_len = miss->key_len;
- execute->actions = (facet->may_install
- ? subfacet->actions
- : xmemdup(subfacet->actions,
- subfacet->actions_len));
- execute->actions_len = subfacet->actions_len;
- execute->packet = packet;
+ (*n_ops)++;
+ } else {
+ ofpbuf_uninit(&odp_actions);
+ }
}
if (facet->may_install && subfacet->key_fitness != ODP_FIT_TOO_LITTLE) {
struct dpif_flow_put *put = &op->dpif_op.u.flow_put;
op->subfacet = subfacet;
+ op->garbage = NULL;
op->dpif_op.type = DPIF_OP_FLOW_PUT;
put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
put->key = miss->key;
}
}
+/* Handles flow miss 'miss' on 'ofproto'. The flow does not match any flow in
+ * the OpenFlow flow table. */
+static void
+handle_flow_miss_no_rule(struct ofproto_dpif *ofproto, struct flow_miss *miss)
+{
+ uint16_t in_port = miss->flow.in_port;
+ struct ofport_dpif *port = get_ofp_port(ofproto, in_port);
+
+ if (!port) {
+ VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, in_port);
+ }
+
+ if (port && port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN) {
+ /* XXX install 'drop' flow entry */
+ COVERAGE_INC(ofproto_dpif_no_packet_in);
+ } else {
+ const struct ofpbuf *packet;
+
+ LIST_FOR_EACH (packet, list_node, &miss->packets) {
+ send_packet_in_miss(ofproto, packet, &miss->flow);
+ }
+ }
+}
+
+/* Handles flow miss 'miss' on 'ofproto'. May add any required datapath
+ * operations to 'ops', incrementing '*n_ops' for each new op. */
+static void
+handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss,
+ struct flow_miss_op *ops, size_t *n_ops)
+{
+ struct facet *facet;
+ uint32_t hash;
+
+ /* The caller must ensure that miss->hmap_node.hash contains
+ * flow_hash(miss->flow, 0). */
+ hash = miss->hmap_node.hash;
+
+ facet = facet_lookup_valid(ofproto, &miss->flow, hash);
+ if (!facet) {
+ struct rule_dpif *rule = rule_dpif_lookup(ofproto, &miss->flow, 0);
+ if (!rule) {
+ handle_flow_miss_no_rule(ofproto, miss);
+ return;
+ } else if (!flow_miss_should_make_facet(ofproto, miss, hash)) {
+ handle_flow_miss_without_facet(miss, rule, ops, n_ops);
+ return;
+ }
+
+ facet = facet_create(rule, &miss->flow, hash);
+ }
+ handle_flow_miss_with_facet(miss, facet, ops, n_ops);
+}
+
/* Like odp_flow_key_to_flow(), this function converts the 'key_len' bytes of
* OVS_KEY_ATTR_* attributes in 'key' to a flow structure in 'flow' and returns
* an ODP_FIT_* value that indicates how well 'key' fits our expectations for
/* Free memory and update facets. */
for (i = 0; i < n_ops; i++) {
struct flow_miss_op *op = &flow_miss_ops[i];
- struct dpif_execute *execute;
switch (op->dpif_op.type) {
case DPIF_OP_EXECUTE:
- execute = &op->dpif_op.u.execute;
- if (op->subfacet->actions != execute->actions) {
- free((struct nlattr *) execute->actions);
- }
break;
case DPIF_OP_FLOW_PUT:
case DPIF_OP_FLOW_DEL:
NOT_REACHED();
}
+
+ free(op->garbage);
}
hmap_destroy(&todo);
}
static void
facet_push_stats(struct facet *facet)
{
- uint64_t new_packets, new_bytes;
+ struct dpif_flow_stats stats;
assert(facet->packet_count >= facet->prev_packet_count);
assert(facet->byte_count >= facet->prev_byte_count);
assert(facet->used >= facet->prev_used);
- new_packets = facet->packet_count - facet->prev_packet_count;
- new_bytes = facet->byte_count - facet->prev_byte_count;
+ stats.n_packets = facet->packet_count - facet->prev_packet_count;
+ stats.n_bytes = facet->byte_count - facet->prev_byte_count;
+ stats.used = facet->used;
+ stats.tcp_flags = 0;
- if (new_packets || new_bytes || facet->used > facet->prev_used) {
+ if (stats.n_packets || stats.n_bytes || facet->used > facet->prev_used) {
facet->prev_packet_count = facet->packet_count;
facet->prev_byte_count = facet->byte_count;
facet->prev_used = facet->used;
- flow_push_stats(facet->rule, &facet->flow,
- new_packets, new_bytes, facet->used);
+ flow_push_stats(facet->rule, &facet->flow, &stats);
update_mirror_stats(ofproto_dpif_cast(facet->rule->up.ofproto),
- facet->mirrors, new_packets, new_bytes);
+ facet->mirrors, stats.n_packets, stats.n_bytes);
}
}
-struct ofproto_push {
- struct action_xlate_ctx ctx;
- uint64_t packets;
- uint64_t bytes;
- long long int used;
-};
-
static void
-push_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
+rule_credit_stats(struct rule_dpif *rule, const struct dpif_flow_stats *stats)
{
- struct ofproto_push *push = CONTAINER_OF(ctx, struct ofproto_push, ctx);
-
- if (rule) {
- rule->packet_count += push->packets;
- rule->byte_count += push->bytes;
- ofproto_rule_update_used(&rule->up, push->used);
- }
+ rule->packet_count += stats->n_packets;
+ rule->byte_count += stats->n_bytes;
+ ofproto_rule_update_used(&rule->up, stats->used);
}
/* Pushes flow statistics to the rules which 'flow' resubmits into given
* 'rule''s actions and mirrors. */
static void
flow_push_stats(struct rule_dpif *rule,
- const struct flow *flow, uint64_t packets, uint64_t bytes,
- long long int used)
+ const struct flow *flow, const struct dpif_flow_stats *stats)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
- struct ofproto_push push;
-
- push.packets = packets;
- push.bytes = bytes;
- push.used = used;
+ struct action_xlate_ctx ctx;
- ofproto_rule_update_used(&rule->up, used);
+ ofproto_rule_update_used(&rule->up, stats->used);
- action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, rule,
+ action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, rule,
0, NULL);
- push.ctx.resubmit_hook = push_resubmit;
- xlate_actions_for_side_effects(&push.ctx,
- rule->up.actions, rule->up.n_actions);
+ ctx.resubmit_stats = stats;
+ xlate_actions_for_side_effects(&ctx, rule->up.actions, rule->up.n_actions);
}
\f
/* Subfacets. */
}
}
-/* Composes the datapath actions for 'subfacet' based on its rule's actions. */
+/* Composes the datapath actions for 'subfacet' based on its rule's actions.
+ * Translates the actions into 'odp_actions', which the caller must have
+ * initialized and is responsible for uninitializing. */
static void
-subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet)
+subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet,
+ struct ofpbuf *odp_actions)
{
struct facet *facet = subfacet->facet;
struct rule_dpif *rule = facet->rule;
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
struct action_xlate_ctx ctx;
- uint64_t odp_actions_stub[1024 / 8];
- struct ofpbuf odp_actions;
- ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci,
rule, 0, packet);
- xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, &odp_actions);
+ xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, odp_actions);
facet->tags = ctx.tags;
facet->may_install = ctx.may_set_up_flow;
facet->has_learn = ctx.has_learn;
facet->nf_flow.output_iface = ctx.nf_output_iface;
facet->mirrors = ctx.mirrors;
- if (subfacet->actions_len != odp_actions.size
- || memcmp(subfacet->actions, odp_actions.data, odp_actions.size)) {
+ if (subfacet->actions_len != odp_actions->size
+ || memcmp(subfacet->actions, odp_actions->data, odp_actions->size)) {
free(subfacet->actions);
- subfacet->actions_len = odp_actions.size;
- subfacet->actions = xmemdup(odp_actions.data, odp_actions.size);
+ subfacet->actions_len = odp_actions->size;
+ subfacet->actions = xmemdup(odp_actions->data, odp_actions->size);
}
-
- ofpbuf_uninit(&odp_actions);
}
/* Updates 'subfacet''s datapath flow, setting its actions to 'actions_len'
struct rule_dpif *rule = rule_dpif_cast(rule_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
- size_t size = packet->size;
+ struct dpif_flow_stats stats;
struct action_xlate_ctx ctx;
uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf odp_actions;
+ dpif_flow_stats_extract(flow, packet, &stats);
+ rule_credit_stats(rule, &stats);
+
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci,
- rule, packet_get_tcp_flags(packet, flow), packet);
+ rule, stats.tcp_flags, packet);
+ ctx.resubmit_stats = &stats;
xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, &odp_actions);
- if (execute_odp_actions(ofproto, flow, odp_actions.data,
- odp_actions.size, packet)) {
- rule->packet_count++;
- rule->byte_count += size;
- flow_push_stats(rule, flow, 1, size, time_msec());
- }
+
+ execute_odp_actions(ofproto, flow, odp_actions.data,
+ odp_actions.size, packet);
+
ofpbuf_uninit(&odp_actions);
return 0;
if (rule) {
struct rule_dpif *old_rule = ctx->rule;
+ if (ctx->resubmit_stats) {
+ rule_credit_stats(rule, ctx->resubmit_stats);
+ }
+
ctx->recurse++;
ctx->rule = rule;
do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx);
ctx->may_learn = packet != NULL;
ctx->tcp_flags = tcp_flags;
ctx->resubmit_hook = NULL;
+ ctx->resubmit_stats = NULL;
}
/* Translates the 'n_in' "union ofp_action"s in 'in' into datapath actions in
const union ofp_action *in, size_t n_in,
struct ofpbuf *odp_actions)
{
- struct flow orig_flow = ctx->flow;
+ /* Normally false. Set to true if we ever hit MAX_RESUBMIT_RECURSION, so
+ * that in the future we always keep a copy of the original flow for
+ * tracing purposes. */
+ static bool hit_resubmit_limit;
COVERAGE_INC(ofproto_dpif_xlate);
ctx->table_id = 0;
ctx->exit = false;
+ if (ctx->ofproto->has_mirrors || hit_resubmit_limit) {
+ /* Do this conditionally because the copy is expensive enough that it
+ * shows up in profiles.
+ *
+ * We keep orig_flow in 'ctx' only because I couldn't make GCC 4.4
+ * believe that I wasn't using it without initializing it if I kept it
+ * in a local variable. */
+ ctx->orig_flow = ctx->flow;
+ }
+
if (ctx->flow.nw_frag & FLOW_NW_FRAG_ANY) {
switch (ctx->ofproto->up.frag_handling) {
case OFPC_FRAG_NORMAL:
ctx->may_set_up_flow = false;
} else {
static struct vlog_rate_limit trace_rl = VLOG_RATE_LIMIT_INIT(1, 1);
- struct flow original_flow = ctx->flow;
ovs_be16 initial_tci = ctx->base_flow.vlan_tci;
add_sflow_action(ctx);
do_xlate_actions(in, n_in, ctx);
- if (ctx->max_resubmit_trigger && !ctx->resubmit_hook
- && !VLOG_DROP_ERR(&trace_rl)) {
- struct ds ds = DS_EMPTY_INITIALIZER;
-
- ofproto_trace(ctx->ofproto, &original_flow, ctx->packet,
- initial_tci, &ds);
- VLOG_ERR("Trace triggered by excessive resubmit recursion:\n%s",
- ds_cstr(&ds));
- ds_destroy(&ds);
+ if (ctx->max_resubmit_trigger && !ctx->resubmit_hook) {
+ if (!hit_resubmit_limit) {
+ /* We didn't record the original flow. Make sure we do from
+ * now on. */
+ hit_resubmit_limit = true;
+ } else if (!VLOG_DROP_ERR(&trace_rl)) {
+ struct ds ds = DS_EMPTY_INITIALIZER;
+
+ ofproto_trace(ctx->ofproto, &ctx->orig_flow, ctx->packet,
+ initial_tci, &ds);
+ VLOG_ERR("Trace triggered by excessive resubmit "
+ "recursion:\n%s", ds_cstr(&ds));
+ ds_destroy(&ds);
+ }
}
if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow,
compose_output_action(ctx, OFPP_LOCAL);
}
}
- add_mirror_actions(ctx, &orig_flow);
+ if (ctx->ofproto->has_mirrors) {
+ add_mirror_actions(ctx, &ctx->orig_flow);
+ }
fix_sflow_action(ctx);
}
}
size_t left;
in_bundle = lookup_input_bundle(ctx->ofproto, orig_flow->in_port,
- ctx->packet != NULL);
+ ctx->packet != NULL, NULL);
if (!in_bundle) {
return;
}
}
static struct ofbundle *
-lookup_input_bundle(struct ofproto_dpif *ofproto, uint16_t in_port, bool warn)
+lookup_input_bundle(struct ofproto_dpif *ofproto, uint16_t in_port, bool warn,
+ struct ofport_dpif **in_ofportp)
{
struct ofport_dpif *ofport;
- /* Special-case OFPP_NONE, which a controller may use as the ingress
- * port for traffic that it is sourcing. */
- if (in_port == OFPP_NONE) {
- return &ofpp_none_bundle;
- }
-
/* Find the port and bundle for the received packet. */
ofport = get_ofp_port(ofproto, in_port);
+ if (in_ofportp) {
+ *in_ofportp = ofport;
+ }
if (ofport && ofport->bundle) {
return ofport->bundle;
}
+ /* Special-case OFPP_NONE, which a controller may use as the ingress
+ * port for traffic that it is sourcing. */
+ if (in_port == OFPP_NONE) {
+ return &ofpp_none_bundle;
+ }
+
/* Odd. A few possible reasons here:
*
* - We deleted a port but there are still a few packets queued up
ctx->has_normal = true;
in_bundle = lookup_input_bundle(ctx->ofproto, ctx->flow.in_port,
- ctx->packet != NULL);
+ ctx->packet != NULL, &in_port);
if (!in_bundle) {
return;
}
- /* We know 'in_port' exists unless it is "ofpp_none_bundle",
- * since lookup_input_bundle() succeeded. */
- in_port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
-
/* Drop malformed frames. */
if (ctx->flow.dl_type == htons(ETH_TYPE_VLAN) &&
!(ctx->flow.vlan_tci & htons(VLAN_CFI))) {
ofproto->max_ports);
if (!error) {
struct odputil_keybuf keybuf;
+ struct dpif_flow_stats stats;
+
struct ofpbuf key;
+ struct action_xlate_ctx ctx;
uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf odp_actions;
- struct ofproto_push push;
ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
odp_flow_key_from_flow(&key, flow);
- action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, NULL,
- packet_get_tcp_flags(packet, flow), packet);
+ dpif_flow_stats_extract(flow, packet, &stats);
- /* Ensure that resubmits in 'ofp_actions' get accounted to their
- * matching rules. */
- push.packets = 1;
- push.bytes = packet->size;
- push.used = time_msec();
- push.ctx.resubmit_hook = push_resubmit;
+ action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, NULL,
+ packet_get_tcp_flags(packet, flow), packet);
+ ctx.resubmit_stats = &stats;
ofpbuf_use_stub(&odp_actions,
odp_actions_stub, sizeof odp_actions_stub);
- xlate_actions(&push.ctx, ofp_actions, n_ofp_actions, &odp_actions);
+ xlate_actions(&ctx, ofp_actions, n_ofp_actions, &odp_actions);
dpif_execute(ofproto->dpif, key.data, key.size,
odp_actions.data, odp_actions.size, packet);
ofpbuf_uninit(&odp_actions);