#include "mac-learning.h"
#include "meta-flow.h"
#include "multipath.h"
+#include "netdev-vport.h"
#include "netdev.h"
#include "netlink.h"
#include "nx-match.h"
static void xlate_actions_for_side_effects(struct action_xlate_ctx *,
const struct ofpact *ofpacts,
size_t ofpacts_len);
+static void xlate_table_action(struct action_xlate_ctx *, uint16_t in_port,
+ uint8_t table_id, bool may_packet_in);
static size_t put_userspace_action(const struct ofproto_dpif *,
struct ofpbuf *odp_actions,
COVERAGE_DEFINE(rev_flow_table);
COVERAGE_DEFINE(rev_inconsistency);
+/* Drop keys are odp flow keys which have drop flows installed in the kernel.
+ * These are datapath flows which have no associated ofproto, if they did we
+ * would use facets. */
+struct drop_key {
+ struct hmap_node hmap_node;
+ struct nlattr *key;
+ size_t key_len;
+};
+
/* All datapaths of a given type share a single dpif backer instance. */
struct dpif_backer {
char *type;
/* Facet revalidation flags applying to facets which use this backer. */
enum revalidate_reason need_revalidate; /* Revalidate every facet. */
struct tag_set revalidate_set; /* Revalidate only matching facets. */
+
+ struct hmap drop_keys; /* Set of dropped odp keys. */
};
/* All existing ofproto_backer instances, indexed by ofproto->up.type. */
static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
+static void drop_key_clear(struct dpif_backer *);
static struct ofport_dpif *
odp_port_to_ofport(const struct dpif_backer *, uint32_t odp_port);
struct hmap vlandev_map; /* vlandev -> (realdev,vid). */
/* Ports. */
- struct sset ports; /* Set of port names. */
+ struct sset ports; /* Set of standard port names. */
+ struct sset ghost_ports; /* Ports with no datapath port. */
struct sset port_poll_set; /* Queued names for port_poll() reply. */
int port_poll_errno; /* Last errno for port_poll() reply. */
};
case REV_INCONSISTENCY: COVERAGE_INC(rev_inconsistency); break;
}
+ if (backer->need_revalidate) {
+ /* Clear the drop_keys in case we should now be accepting some
+ * formerly dropped flows. */
+ drop_key_clear(backer);
+ }
+
HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
struct facet *facet;
}
}
}
-
}
if (timer_expired(&backer->next_expiration)) {
return;
}
+ drop_key_clear(backer);
+ hmap_destroy(&backer->drop_keys);
+
hmap_destroy(&backer->odp_to_ofport_map);
node = shash_find(&all_dpif_backers, backer->type);
free(backer->type);
backer->type = xstrdup(type);
backer->refcount = 1;
hmap_init(&backer->odp_to_ofport_map);
+ hmap_init(&backer->drop_keys);
timer_set_duration(&backer->next_expiration, 1000);
backer->need_revalidate = 0;
tag_set_init(&backer->revalidate_set);
hmap_init(&ofproto->realdev_vid_map);
sset_init(&ofproto->ports);
+ sset_init(&ofproto->ghost_ports);
sset_init(&ofproto->port_poll_set);
ofproto->port_poll_errno = 0;
hmap_destroy(&ofproto->realdev_vid_map);
sset_destroy(&ofproto->ports);
+ sset_destroy(&ofproto->ghost_ports);
sset_destroy(&ofproto->port_poll_set);
close_dpif_backer(ofproto->backer);
port->vlandev_vid = 0;
port->carrier_seq = netdev_get_carrier_resets(port->up.netdev);
+ if (netdev_vport_is_patch(port->up.netdev)) {
+ /* XXX By bailing out here, we don't do required sFlow work. */
+ port->odp_port = OVSP_NONE;
+ return 0;
+ }
+
error = dpif_port_query_by_name(ofproto->backer->dpif,
netdev_get_name(port->up.netdev),
&dpif_port);
dpif_port_del(ofproto->backer->dpif, port->odp_port);
}
+ if (port->odp_port != OVSP_NONE) {
+ hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
+ }
+
sset_find_and_delete(&ofproto->ports, devname);
- hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
+ sset_find_and_delete(&ofproto->ghost_ports, devname);
ofproto->backer->need_revalidate = REV_RECONFIGURE;
bundle_remove(port_);
set_cfm(port_, NULL);
ofproto_port->ofp_port = odp_port_to_ofp_port(ofproto, dpif_port->port_no);
}
+static struct ofport_dpif *
+ofport_get_peer(const struct ofport_dpif *ofport_dpif)
+{
+ const struct ofproto_dpif *ofproto;
+ const char *peer;
+
+ peer = netdev_vport_patch_peer(ofport_dpif->up.netdev);
+ if (!peer) {
+ return NULL;
+ }
+
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ struct ofport *ofport;
+
+ ofport = shash_find_data(&ofproto->up.port_by_name, peer);
+ if (ofport && ofport->ofproto->ofproto_class == &ofproto_dpif_class) {
+ return ofport_dpif_cast(ofport);
+ }
+ }
+ return NULL;
+}
+
static void
port_run_fast(struct ofport_dpif *ofport)
{
struct dpif_port dpif_port;
int error;
+ if (sset_contains(&ofproto->ghost_ports, devname)) {
+ const char *type = netdev_get_type_from_name(devname);
+
+ /* We may be called before ofproto->up.port_by_name is populated with
+ * the appropriate ofport. For this reason, we must get the name and
+ * type from the netdev layer directly. */
+ if (type) {
+ const struct ofport *ofport;
+
+ ofport = shash_find_data(&ofproto->up.port_by_name, devname);
+ ofproto_port->ofp_port = ofport ? ofport->ofp_port : OFPP_NONE;
+ ofproto_port->name = xstrdup(devname);
+ ofproto_port->type = xstrdup(type);
+ return 0;
+ }
+ return ENODEV;
+ }
+
if (!sset_contains(&ofproto->ports, devname)) {
return ENODEV;
}
uint32_t odp_port = UINT32_MAX;
int error;
+ if (netdev_vport_is_patch(netdev)) {
+ sset_add(&ofproto->ghost_ports, netdev_get_name(netdev));
+ return 0;
+ }
+
error = dpif_port_add(ofproto->backer->dpif, netdev, &odp_port);
if (!error) {
sset_add(&ofproto->ports, netdev_get_name(netdev));
struct port_dump_state {
uint32_t bucket;
uint32_t offset;
+ bool ghost;
};
static int
port_dump_start(const struct ofproto *ofproto_ OVS_UNUSED, void **statep)
{
- struct port_dump_state *state;
-
- *statep = state = xmalloc(sizeof *state);
- state->bucket = 0;
- state->offset = 0;
+ *statep = xzalloc(sizeof(struct port_dump_state));
return 0;
}
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
struct port_dump_state *state = state_;
+ const struct sset *sset;
struct sset_node *node;
- while ((node = sset_at_position(&ofproto->ports, &state->bucket,
- &state->offset))) {
+ sset = state->ghost ? &ofproto->ghost_ports : &ofproto->ports;
+ while ((node = sset_at_position(sset, &state->bucket, &state->offset))) {
int error;
error = port_query_by_name(ofproto_, node->name, port);
}
}
+ if (!state->ghost) {
+ state->ghost = true;
+ state->bucket = 0;
+ state->offset = 0;
+ return port_dump_next(ofproto_, state_, port);
+ }
+
return EOF;
}
handle_flow_miss_with_facet(miss, facet, now, ops, n_ops);
}
+static struct drop_key *
+drop_key_lookup(const struct dpif_backer *backer, const struct nlattr *key,
+ size_t key_len)
+{
+ struct drop_key *drop_key;
+
+ HMAP_FOR_EACH_WITH_HASH (drop_key, hmap_node, hash_bytes(key, key_len, 0),
+ &backer->drop_keys) {
+ if (drop_key->key_len == key_len
+ && !memcmp(drop_key->key, key, key_len)) {
+ return drop_key;
+ }
+ }
+ return NULL;
+}
+
+static void
+drop_key_clear(struct dpif_backer *backer)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 15);
+ struct drop_key *drop_key, *next;
+
+ HMAP_FOR_EACH_SAFE (drop_key, next, hmap_node, &backer->drop_keys) {
+ int error;
+
+ error = dpif_flow_del(backer->dpif, drop_key->key, drop_key->key_len,
+ NULL);
+ if (error && !VLOG_DROP_WARN(&rl)) {
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ odp_flow_key_format(drop_key->key, drop_key->key_len, &ds);
+ VLOG_WARN("Failed to delete drop key (%s) (%s)", strerror(error),
+ ds_cstr(&ds));
+ ds_destroy(&ds);
+ }
+
+ hmap_remove(&backer->drop_keys, &drop_key->hmap_node);
+ free(drop_key->key);
+ free(drop_key);
+ }
+}
+
/* Given a datpath, packet, and flow metadata ('backer', 'packet', and 'key'
* respectively), populates 'flow' with the result of odp_flow_key_to_flow().
* Optionally, if nonnull, populates 'fitnessp' with the fitness of 'flow' as
port = odp_port_to_ofport(backer, flow->in_port);
if (!port) {
flow->in_port = OFPP_NONE;
- error = ofproto ? ENODEV : 0;
+ error = ENODEV;
goto exit;
}
upcall->key_len, &flow, &miss->key_fitness,
&ofproto, &odp_in_port, &miss->initial_tci);
if (error == ENODEV) {
+ struct drop_key *drop_key;
+
/* Received packet on port for which we couldn't associate
* an ofproto. This can happen if a port is removed while
* traffic is being received. Print a rate-limited message
- * in case it happens frequently. */
+ * in case it happens frequently. Install a drop flow so
+ * that future packets of the flow are inexpensively dropped
+ * in the kernel. */
VLOG_INFO_RL(&rl, "received packet on unassociated port %"PRIu32,
flow.in_port);
+
+ drop_key = drop_key_lookup(backer, upcall->key, upcall->key_len);
+ if (!drop_key) {
+ drop_key = xmalloc(sizeof *drop_key);
+ drop_key->key = xmemdup(upcall->key, upcall->key_len);
+ drop_key->key_len = upcall->key_len;
+
+ hmap_insert(&backer->drop_keys, &drop_key->hmap_node,
+ hash_bytes(drop_key->key, drop_key->key_len, 0));
+ dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
+ drop_key->key, drop_key->key_len, NULL, 0, NULL);
+ }
+ continue;
}
if (error) {
continue;
struct ofproto_dpif *ofproto;
int max_idle = INT32_MAX;
+ /* Periodically clear out the drop keys in an effort to keep them
+ * relatively few. */
+ drop_key_clear(backer);
+
/* Update stats for each flow in the backer. */
update_stats(backer);
HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
- struct rule_dpif *rule, *next_rule;
- struct oftable *table;
+ struct rule *rule, *next_rule;
int dp_max_idle;
if (ofproto->backer != backer) {
/* Expire OpenFlow flows whose idle_timeout or hard_timeout
* has passed. */
- OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
- struct cls_cursor cursor;
-
- cls_cursor_init(&cursor, &table->cls, NULL);
- CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
- rule_expire(rule);
- }
+ LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
+ &ofproto->up.expirable) {
+ rule_expire(rule_dpif_cast(rule));
}
/* All outstanding data in existing flows has been accounted, so it's a
while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
struct flow flow;
struct subfacet *subfacet;
- enum odp_key_fitness fitness;
struct ofproto_dpif *ofproto;
- struct ofport_dpif *port;
uint32_t key_hash;
- fitness = odp_flow_key_to_flow(key, key_len, &flow);
- if (fitness == ODP_FIT_ERROR) {
+ if (ofproto_receive(backer, NULL, key, key_len, &flow, NULL, &ofproto,
+ NULL, NULL)) {
continue;
}
- port = odp_port_to_ofport(backer, flow.in_port);
- if (!port) {
- /* This flow is for a port for which we couldn't associate an
- * ofproto. This can happen if a port is removed while
- * traffic is being received. Ignore this flow, since it
- * will get timed out. */
- continue;
- }
-
- ofproto = ofproto_dpif_cast(port->up.ofproto);
- flow.in_port = port->up.ofp_port;
key_hash = odp_flow_key_hash(key, key_len);
-
subfacet = subfacet_find(ofproto, key, key_len, key_hash, &flow);
switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
case SF_FAST_PATH:
}
/* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
- * 'packet', which arrived on 'in_port'.
- *
- * Takes ownership of 'packet'. */
+ * 'packet', which arrived on 'in_port'. */
static bool
execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
const struct nlattr *odp_actions, size_t actions_len,
error = dpif_execute(ofproto->backer->dpif, key.data, key.size,
odp_actions, actions_len, packet);
-
- ofpbuf_delete(packet);
return !error;
}
}
}
-static enum ofperr
-rule_execute(struct rule *rule_, const struct flow *flow,
- struct ofpbuf *packet)
+static void
+rule_dpif_execute(struct rule_dpif *rule, const struct flow *flow,
+ struct ofpbuf *packet)
{
- struct rule_dpif *rule = rule_dpif_cast(rule_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
struct dpif_flow_stats stats;
odp_actions.size, packet);
ofpbuf_uninit(&odp_actions);
+}
+static enum ofperr
+rule_execute(struct rule *rule, const struct flow *flow,
+ struct ofpbuf *packet)
+{
+ rule_dpif_execute(rule_dpif_cast(rule), flow, packet);
+ ofpbuf_delete(packet);
return 0;
}
int error;
flow_extract(packet, 0, 0, NULL, OFPP_LOCAL, &flow);
+ if (netdev_vport_is_patch(ofport->up.netdev)) {
+ struct ofproto_dpif *peer_ofproto;
+ struct dpif_flow_stats stats;
+ struct ofport_dpif *peer;
+ struct rule_dpif *rule;
+
+ peer = ofport_get_peer(ofport);
+ if (!peer) {
+ return ENODEV;
+ }
+
+ dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
+ netdev_vport_patch_inc_tx(ofport->up.netdev, &stats);
+ netdev_vport_patch_inc_rx(peer->up.netdev, &stats);
+
+ flow.in_port = peer->up.ofp_port;
+ peer_ofproto = ofproto_dpif_cast(peer->up.ofproto);
+ rule = rule_dpif_lookup(peer_ofproto, &flow);
+ rule_dpif_execute(rule, &flow, packet);
+
+ return 0;
+ }
+
odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
flow.vlan_tci);
if (odp_port != ofport->odp_port) {
bool check_stp)
{
const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
- uint32_t odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
uint8_t flow_nw_tos = ctx->flow.nw_tos;
struct priority_to_dscp *pdscp;
- uint32_t out_port;
+ uint32_t out_port, odp_port;
+
+ /* If 'struct flow' gets additional metadata, we'll need to zero it out
+ * before traversing a patch port. */
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
if (!ofport) {
xlate_report(ctx, "Nonexistent output port");
return;
}
+ if (netdev_vport_is_patch(ofport->up.netdev)) {
+ struct ofport_dpif *peer = ofport_get_peer(ofport);
+ struct flow old_flow = ctx->flow;
+ const struct ofproto_dpif *peer_ofproto;
+
+ if (!peer) {
+ xlate_report(ctx, "Nonexistent patch port peer");
+ return;
+ }
+
+ peer_ofproto = ofproto_dpif_cast(peer->up.ofproto);
+ if (peer_ofproto->backer != ctx->ofproto->backer) {
+ xlate_report(ctx, "Patch port peer on a different datapath");
+ return;
+ }
+
+ ctx->ofproto = ofproto_dpif_cast(peer->up.ofproto);
+ ctx->flow.in_port = peer->up.ofp_port;
+ ctx->flow.metadata = htonll(0);
+ memset(&ctx->flow.tunnel, 0, sizeof ctx->flow.tunnel);
+ memset(ctx->flow.regs, 0, sizeof ctx->flow.regs);
+ xlate_table_action(ctx, ctx->flow.in_port, 0, true);
+ ctx->flow = old_flow;
+ ctx->ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+
+ if (ctx->resubmit_stats) {
+ netdev_vport_patch_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
+ netdev_vport_patch_inc_rx(peer->up.netdev, ctx->resubmit_stats);
+ }
+
+ return;
+ }
+
pdscp = get_priority(ofport, ctx->flow.skb_priority);
if (pdscp) {
ctx->flow.nw_tos &= ~IP_DSCP_MASK;
ctx->flow.nw_tos |= pdscp->dscp;
}
+ odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
ctx->flow.vlan_tci);
if (out_port != odp_port) {
struct ofport *ofport = node->data;
const char *name = netdev_get_name(ofport->netdev);
const char *type = netdev_get_type(ofport->netdev);
+ uint32_t odp_port;
+
+ ds_put_format(ds, "\t%s %u/", name, ofport->ofp_port);
+
+ odp_port = ofp_port_to_odp_port(ofproto, ofport->ofp_port);
+ if (odp_port != OVSP_NONE) {
+ ds_put_format(ds, "%"PRIu32":", odp_port);
+ } else {
+ ds_put_cstr(ds, "none:");
+ }
- ds_put_format(ds, "\t%s %u/%u:", name, ofport->ofp_port,
- ofp_port_to_odp_port(ofproto, ofport->ofp_port));
if (strcmp(type, "system")) {
struct netdev *netdev;
int error;
return;
}
+ update_stats(ofproto->backer);
+
HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) {
struct odputil_keybuf keybuf;
struct ofpbuf key;