/*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 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 "connmgr.h"
#include "coverage.h"
#include "cfm.h"
-#include "dpif.h"
+#include "ct-dpif.h"
#include "fail-open.h"
#include "guarded-list.h"
#include "hmapx.h"
#include "lacp.h"
#include "learn.h"
#include "mac-learning.h"
+#include "math.h"
#include "mcast-snooping.h"
#include "multipath.h"
#include "netdev-vport.h"
#include "ofproto-dpif-monitor.h"
#include "ofproto-dpif-rid.h"
#include "ofproto-dpif-sflow.h"
+#include "ofproto-dpif-trace.h"
#include "ofproto-dpif-upcall.h"
#include "ofproto-dpif-xlate.h"
#include "ofproto-dpif-xlate-cache.h"
#include "openvswitch/ofp-actions.h"
#include "openvswitch/dynamic-string.h"
#include "openvswitch/meta-flow.h"
-#include "openvswitch/ofp-parse.h"
#include "openvswitch/ofp-print.h"
-#include "openvswitch/ofp-util.h"
#include "openvswitch/ofpbuf.h"
+#include "openvswitch/uuid.h"
#include "openvswitch/vlog.h"
#include "ovs-lldp.h"
#include "ovs-rcu.h"
#include "ovs-router.h"
-#include "poll-loop.h"
+#include "openvswitch/poll-loop.h"
#include "seq.h"
#include "simap.h"
#include "smap.h"
#include "unaligned.h"
#include "unixctl.h"
#include "util.h"
+#include "uuid.h"
#include "vlan-bitmap.h"
VLOG_DEFINE_THIS_MODULE(ofproto_dpif);
struct flow_miss;
-struct rule_dpif {
- struct rule up;
-
- /* These statistics:
- *
- * - Do include packets and bytes from datapath flows which have not
- * recently been processed by a revalidator. */
- struct ovs_mutex stats_mutex;
- struct dpif_flow_stats stats OVS_GUARDED;
-
- /* In non-NULL, will point to a new rule (for which a reference is held) to
- * which all the stats updates should be forwarded. This exists only
- * transitionally when flows are replaced.
- *
- * Protected by stats_mutex. If both 'rule->stats_mutex' and
- * 'rule->new_rule->stats_mutex' must be held together, acquire them in that
- * order, */
- struct rule_dpif *new_rule OVS_GUARDED;
- bool forward_counts OVS_GUARDED; /* Forward counts? 'used' time will be
- * forwarded in all cases. */
-
- /* If non-zero then the recirculation id that has
- * been allocated for use with this rule.
- * The recirculation id and associated internal flow should
- * be freed when the rule is freed */
- uint32_t recirc_id;
-};
-
-/* RULE_CAST() depends on this. */
-BUILD_ASSERT_DECL(offsetof(struct rule_dpif, up) == 0);
-
static void rule_get_stats(struct rule *, uint64_t *packets, uint64_t *bytes,
long long int *used);
static struct rule_dpif *rule_dpif_cast(const struct rule *);
static void rule_expire(struct rule_dpif *, long long now);
-struct group_dpif {
- struct ofgroup up;
-
- /* These statistics:
- *
- * - Do include packets and bytes from datapath flows which have not
- * recently been processed by a revalidator. */
- struct ovs_mutex stats_mutex;
- uint64_t packet_count OVS_GUARDED; /* Number of packets received. */
- uint64_t byte_count OVS_GUARDED; /* Number of bytes received. */
-};
-
struct ofbundle {
struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
struct ofproto_dpif *ofproto; /* Owning ofproto. */
char *name; /* Identifier for log messages. */
/* Configuration. */
- struct ovs_list ports; /* Contains "struct ofport"s. */
+ struct ovs_list ports; /* Contains "struct ofport_dpif"s. */
enum port_vlan_mode vlan_mode; /* VLAN mode */
+ uint16_t qinq_ethtype;
int vlan; /* -1=trunk port, else a 12-bit VLAN ID. */
unsigned long *trunks; /* Bitmap of trunked VLANs, if 'vlan' == -1.
* NULL if all VLANs are trunked. */
+ unsigned long *cvlans;
struct lacp *lacp; /* LACP if LACP is enabled, otherwise NULL. */
struct bond *bond; /* Nonnull iff more than one port. */
bool use_priority_tags; /* Use 802.1p tag for frames in VLAN 0? */
+ bool protected; /* Protected port mode */
+
/* Status. */
bool floodable; /* True if no port has OFPUTIL_PC_NO_FLOOD set. */
};
struct cfm *cfm; /* Connectivity Fault Management, if any. */
struct bfd *bfd; /* BFD, if any. */
struct lldp *lldp; /* lldp, if any. */
- bool may_enable; /* May be enabled in bonds. */
bool is_tunnel; /* This port is a tunnel. */
- bool is_layer3; /* This is a layer 3 port. */
long long int carrier_seq; /* Carrier status changes. */
struct ofport_dpif *peer; /* Peer if patch port. */
static int set_lldp(struct ofport *ofport_, const struct smap *cfg);
static void ofport_update_peer(struct ofport_dpif *);
-/* Reasons that we might need to revalidate every datapath flow, and
- * corresponding coverage counters.
- *
- * A value of 0 means that there is no need to revalidate.
- *
- * It would be nice to have some cleaner way to integrate with coverage
- * counters, but with only a few reasons I guess this is good enough for
- * now. */
-enum revalidate_reason {
- REV_RECONFIGURE = 1, /* Switch configuration changed. */
- REV_STP, /* Spanning tree protocol port status change. */
- REV_RSTP, /* RSTP port status change. */
- REV_BOND, /* Bonding changed. */
- REV_PORT_TOGGLED, /* Port enabled or disabled by CFM, LACP, ...*/
- REV_FLOW_TABLE, /* Flow table changed. */
- REV_MAC_LEARNING, /* Mac learning changed. */
- REV_MCAST_SNOOPING, /* Multicast snooping changed. */
-};
COVERAGE_DEFINE(rev_reconfigure);
COVERAGE_DEFINE(rev_stp);
COVERAGE_DEFINE(rev_rstp);
COVERAGE_DEFINE(rev_mac_learning);
COVERAGE_DEFINE(rev_mcast_snooping);
-/* All datapaths of a given type share a single dpif backer instance. */
-struct dpif_backer {
- char *type;
- int refcount;
- struct dpif *dpif;
- struct udpif *udpif;
-
- struct ovs_rwlock odp_to_ofport_lock;
- struct hmap odp_to_ofport_map OVS_GUARDED; /* Contains "struct ofport"s. */
-
- struct simap tnl_backers; /* Set of dpif ports backing tunnels. */
-
- enum revalidate_reason need_revalidate; /* Revalidate all flows. */
-
- bool recv_set_enable; /* Enables or disables receiving packets. */
-
- /* Version string of the datapath stored in OVSDB. */
- char *dp_version_string;
-
- /* Datapath feature support. */
- struct dpif_backer_support support;
- struct atomic_count tnl_count;
-};
-
/* All existing ofproto_backer instances, indexed by ofproto->up.type. */
-static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
-
-struct ofproto_dpif {
- struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
- struct ofproto up;
- struct dpif_backer *backer;
-
- /* Unique identifier for this instantiation of this bridge in this running
- * process. */
- struct uuid uuid;
-
- ATOMIC(ovs_version_t) tables_version; /* For classifier lookups. */
-
- uint64_t dump_seq; /* Last read of udpif_dump_seq(). */
-
- /* Special OpenFlow rules. */
- struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */
- struct rule_dpif *no_packet_in_rule; /* Drops flow table misses. */
- struct rule_dpif *drop_frags_rule; /* Used in OFPUTIL_FRAG_DROP mode. */
-
- /* Bridging. */
- struct netflow *netflow;
- struct dpif_sflow *sflow;
- struct dpif_ipfix *ipfix;
- struct hmap bundles; /* Contains "struct ofbundle"s. */
- struct mac_learning *ml;
- struct mcast_snooping *ms;
- bool has_bonded_bundles;
- bool lacp_enabled;
- struct mbridge *mbridge;
-
- struct ovs_mutex stats_mutex;
- struct netdev_stats stats OVS_GUARDED; /* To account packets generated and
- * consumed in userspace. */
-
- /* Spanning tree. */
- struct stp *stp;
- long long int stp_last_tick;
-
- /* Rapid Spanning Tree. */
- struct rstp *rstp;
- long long int rstp_last_tick;
-
- /* Ports. */
- 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. */
- uint64_t change_seq; /* Connectivity status changes. */
-
- /* Work queues. */
- struct guarded_list ams; /* Contains "struct ofproto_async_msgs"s. */
- struct seq *ams_seq; /* For notifying 'ams' reception. */
- uint64_t ams_seqno;
-};
+struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
/* All existing ofproto_dpif instances, indexed by ->up.name. */
-static struct hmap all_ofproto_dpifs = HMAP_INITIALIZER(&all_ofproto_dpifs);
+static struct hmap all_ofproto_dpifs_by_name =
+ HMAP_INITIALIZER(&all_ofproto_dpifs_by_name);
+
+/* All existing ofproto_dpif instances, indexed by ->uuid. */
+static struct hmap all_ofproto_dpifs_by_uuid =
+ HMAP_INITIALIZER(&all_ofproto_dpifs_by_uuid);
static bool ofproto_use_tnl_push_pop = true;
static void ofproto_unixctl_init(void);
return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
}
-bool
-ofproto_dpif_get_enable_ufid(const struct dpif_backer *backer)
-{
- return backer->support.ufid;
-}
-
-struct dpif_backer_support *
-ofproto_dpif_get_support(const struct ofproto_dpif *ofproto)
-{
- return &ofproto->backer->support;
-}
-
-static void ofproto_trace(struct ofproto_dpif *, struct flow *,
- const struct dp_packet *packet,
- const struct ofpact[], size_t ofpacts_len,
- struct ds *);
-
/* Global variables. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
}
ofproto_unixctl_init();
+ ofproto_dpif_trace_init();
udpif_init();
}
struct ofproto_dpif *ofproto;
sset_clear(names);
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
if (strcmp(type, ofproto->up.type)) {
continue;
}
{
struct ofproto_dpif *ofproto;
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
if (sset_contains(&ofproto->ports, name)) {
return ofproto;
}
return NULL;
}
-bool
-ofproto_dpif_backer_enabled(struct dpif_backer* backer)
-{
- return backer->recv_set_enable;
-}
-
static int
type_run(const char *type)
{
return 0;
}
- /* This must be called before dpif_run() */
- dpif_poll_threads_set(backer->dpif, pmd_cpu_mask);
-
if (dpif_run(backer->dpif)) {
backer->need_revalidate = REV_RECONFIGURE;
}
simap_init(&tmp_backers);
simap_swap(&backer->tnl_backers, &tmp_backers);
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
struct ofport_dpif *iter;
if (backer != ofproto->backer) {
HMAP_FOR_EACH (iter, up.hmap_node, &ofproto->up.ports) {
char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
const char *dp_port;
+ odp_port_t old_odp_port;
if (!iter->is_tunnel) {
continue;
dp_port = netdev_vport_get_dpif_port(iter->up.netdev,
namebuf, sizeof namebuf);
+ old_odp_port = iter->odp_port;
node = simap_find(&tmp_backers, dp_port);
if (node) {
simap_put(&backer->tnl_backers, dp_port, node->data);
iter->odp_port = node ? u32_to_odp(node->data) : ODPP_NONE;
if (tnl_port_reconfigure(iter, iter->up.netdev,
- iter->odp_port,
+ iter->odp_port, old_odp_port,
ovs_native_tunneling_is_on(ofproto), dp_port)) {
backer->need_revalidate = REV_RECONFIGURE;
}
}
SIMAP_FOR_EACH (node, &tmp_backers) {
- dpif_port_del(backer->dpif, u32_to_odp(node->data));
+ dpif_port_del(backer->dpif, u32_to_odp(node->data), false);
}
simap_destroy(&tmp_backers);
}
backer->need_revalidate = 0;
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ xlate_txn_start();
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
struct ofport_dpif *ofport;
struct ofbundle *bundle;
continue;
}
- xlate_txn_start();
xlate_ofproto_set(ofproto, ofproto->up.name,
ofproto->backer->dpif, ofproto->ml,
ofproto->stp, ofproto->rstp, ofproto->ms,
ofproto->netflow,
ofproto->up.forward_bpdu,
connmgr_has_in_band(ofproto->up.connmgr),
- &ofproto->backer->support);
+ &ofproto->backer->rt_support);
HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
xlate_bundle_set(ofproto, bundle, bundle->name,
- bundle->vlan_mode, bundle->vlan,
- bundle->trunks, bundle->use_priority_tags,
+ bundle->vlan_mode, bundle->qinq_ethtype,
+ bundle->vlan, bundle->trunks, bundle->cvlans,
+ bundle->use_priority_tags,
bundle->bond, bundle->lacp,
- bundle->floodable);
+ bundle->floodable, bundle->protected);
}
HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
ofport->rstp_port, ofport->qdscp,
ofport->n_qdscp, ofport->up.pp.config,
ofport->up.pp.state, ofport->is_tunnel,
- ofport->may_enable);
+ ofport->up.may_enable);
}
- xlate_txn_commit();
}
+ xlate_txn_commit();
udpif_revalidate(backer->udpif);
}
const char *devname;
sset_init(&devnames);
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
if (ofproto->backer == backer) {
struct ofport *ofport;
return;
}
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node,
- &all_ofproto_dpifs) {
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
if (simap_contains(&ofproto->backer->tnl_backers, devname)) {
return;
}
} else if (!ofproto) {
/* The port was added, but we don't know with which
* ofproto we should associate it. Delete it. */
- dpif_port_del(backer->dpif, port.port_no);
+ dpif_port_del(backer->dpif, port.port_no, false);
} else {
struct ofport_dpif *ofport;
{
struct ofproto_dpif *ofproto;
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
if (ofproto->backer == backer) {
sset_clear(&ofproto->port_poll_set);
ofproto->port_poll_errno = error;
}
static void
-close_dpif_backer(struct dpif_backer *backer)
+close_dpif_backer(struct dpif_backer *backer, bool del)
{
+ struct simap_node *node;
+
ovs_assert(backer->refcount > 0);
if (--backer->refcount) {
udpif_destroy(backer->udpif);
+ SIMAP_FOR_EACH (node, &backer->tnl_backers) {
+ dpif_port_del(backer->dpif, u32_to_odp(node->data), false);
+ }
simap_destroy(&backer->tnl_backers);
ovs_rwlock_destroy(&backer->odp_to_ofport_lock);
hmap_destroy(&backer->odp_to_ofport_map);
shash_find_and_delete(&all_dpif_backers, backer->type);
free(backer->type);
free(backer->dp_version_string);
+ if (del) {
+ dpif_delete(backer->dpif);
+ }
dpif_close(backer->dpif);
+ id_pool_destroy(backer->meter_ids);
free(backer);
}
odp_port_t odp_port;
};
-static bool check_variable_length_userdata(struct dpif_backer *backer);
static void check_support(struct dpif_backer *backer);
static int
dpif_port_dump_done(&port_dump);
LIST_FOR_EACH_POP (garbage, list_node, &garbage_list) {
- dpif_port_del(backer->dpif, garbage->odp_port);
+ dpif_port_del(backer->dpif, garbage->odp_port, false);
free(garbage);
}
if (error) {
VLOG_ERR("failed to listen on datapath of type %s: %s",
type, ovs_strerror(error));
- close_dpif_backer(backer);
+ close_dpif_backer(backer, false);
return error;
}
udpif_set_threads(backer->udpif, n_handlers, n_revalidators);
}
- /* This check fails if performed before udpif threads have been set,
- * as the kernel module checks that the 'pid' in userspace action
- * is non-zero. */
- backer->support.variable_length_userdata
- = check_variable_length_userdata(backer);
backer->dp_version_string = dpif_get_dp_version(backer->dpif);
+ /* Manage Datapath meter IDs if supported. */
+ struct ofputil_meter_features features;
+ dpif_meter_get_features(backer->dpif, &features);
+ if (features.max_meters) {
+ backer->meter_ids = id_pool_create(0, features.max_meters);
+ } else {
+ backer->meter_ids = NULL;
+ }
+
+ /* Make a pristine snapshot of 'support' into 'boottime_support'.
+ * 'boottime_support' can be checked to prevent 'support' to be changed
+ * beyond the datapath capabilities. In case 'support' is changed by
+ * the user, 'boottime_support' can be used to restore it. */
+ backer->bt_support = backer->rt_support;
+
return error;
}
bool
ovs_native_tunneling_is_on(struct ofproto_dpif *ofproto)
{
- return ofproto_use_tnl_push_pop && ofproto->backer->support.tnl_push_pop &&
- atomic_count_get(&ofproto->backer->tnl_count);
+ return ofproto_use_tnl_push_pop
+ && ofproto->backer->rt_support.tnl_push_pop
+ && atomic_count_get(&ofproto->backer->tnl_count);
}
/* Tests whether 'backer''s datapath supports recirculation. Only newer
ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
odp_flow_key_from_flow(&odp_parms, &key);
enable_recirc = dpif_probe_feature(backer->dpif, "recirculation", &key,
- NULL);
+ NULL, NULL);
if (enable_recirc) {
VLOG_INFO("%s: Datapath supports recirculation",
odp_flow_key_from_flow(&odp_parms, &key);
dpif_flow_hash(backer->dpif, key.data, key.size, &ufid);
- enable_ufid = dpif_probe_feature(backer->dpif, "UFID", &key, &ufid);
+ enable_ufid = dpif_probe_feature(backer->dpif, "UFID", &key, NULL, &ufid);
if (enable_ufid) {
VLOG_INFO("%s: Datapath supports unique flow ids",
return enable_ufid;
}
-/* Tests whether 'backer''s datapath supports variable-length
- * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions. We need
- * to disable some features on older datapaths that don't support this
- * feature.
+/* Tests number of 802.1q VLAN headers supported by 'backer''s datapath.
*
- * Returns false if 'backer' definitely does not support variable-length
- * userdata, true if it seems to support them or if at least the error we get
- * is ambiguous. */
-static bool
-check_variable_length_userdata(struct dpif_backer *backer)
+ * Returns the number of elements in a struct flow's vlan
+ * if the datapath supports at least that many VLAN headers. */
+static size_t
+check_max_vlan_headers(struct dpif_backer *backer)
{
- struct eth_header *eth;
- struct ofpbuf actions;
- struct dpif_execute execute;
- struct dp_packet packet;
struct flow flow;
- size_t start;
- int error;
-
- /* Compose a userspace action that will cause an ERANGE error on older
- * datapaths that don't support variable-length userdata.
- *
- * We really test for using userdata longer than 8 bytes, but older
- * datapaths accepted these, silently truncating the userdata to 8 bytes.
- * The same older datapaths rejected userdata shorter than 8 bytes, so we
- * test for that instead as a proxy for longer userdata support. */
- ofpbuf_init(&actions, 64);
- start = nl_msg_start_nested(&actions, OVS_ACTION_ATTR_USERSPACE);
- nl_msg_put_u32(&actions, OVS_USERSPACE_ATTR_PID,
- dpif_port_get_pid(backer->dpif, ODPP_NONE, 0));
- nl_msg_put_unspec_zero(&actions, OVS_USERSPACE_ATTR_USERDATA, 4);
- nl_msg_end_nested(&actions, start);
-
- /* Compose a dummy ethernet packet. */
- dp_packet_init(&packet, ETH_HEADER_LEN);
- eth = dp_packet_put_zeros(&packet, ETH_HEADER_LEN);
- eth->eth_type = htons(0x1234);
-
- flow_extract(&packet, &flow);
-
- /* Execute the actions. On older datapaths this fails with ERANGE, on
- * newer datapaths it succeeds. */
- execute.actions = actions.data;
- execute.actions_len = actions.size;
- execute.packet = &packet;
- execute.flow = &flow;
- execute.needs_help = false;
- execute.probe = true;
- execute.mtu = 0;
-
- error = dpif_execute(backer->dpif, &execute);
-
- dp_packet_uninit(&packet);
- ofpbuf_uninit(&actions);
+ struct odp_flow_key_parms odp_parms = {
+ .flow = &flow,
+ .probe = true,
+ };
+ int n;
- switch (error) {
- case 0:
- return true;
+ memset(&flow, 0, sizeof flow);
+ flow.dl_type = htons(ETH_TYPE_IP);
+ for (n = 0; n < FLOW_MAX_VLAN_HEADERS; n++) {
+ struct odputil_keybuf keybuf;
+ struct ofpbuf key;
- case ERANGE:
- /* Variable-length userdata is not supported. */
- VLOG_WARN("%s: datapath does not support variable-length userdata "
- "feature (needs Linux 3.10+ or kernel module from OVS "
- "1..11+). The NXAST_SAMPLE action will be ignored.",
- dpif_name(backer->dpif));
- return false;
+ flow_push_vlan_uninit(&flow, NULL);
+ flow.vlans[0].tpid = htons(ETH_TYPE_VLAN);
+ flow.vlans[0].tci = htons(1) | htons(VLAN_CFI);
- default:
- /* Something odd happened. We're not sure whether variable-length
- * userdata is supported. Default to "yes". */
- VLOG_WARN("%s: variable-length userdata feature probe failed (%s)",
- dpif_name(backer->dpif), ovs_strerror(error));
- return true;
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+ odp_flow_key_from_flow(&odp_parms, &key);
+ if (!dpif_probe_feature(backer->dpif, "VLAN", &key, NULL, NULL)) {
+ break;
+ }
}
-}
+ VLOG_INFO("%s: VLAN header stack length probed as %d",
+ dpif_name(backer->dpif), n);
+ return n;
+}
/* Tests the MPLS label stack depth supported by 'backer''s datapath.
*
* Returns the number of elements in a struct flow's mpls_lse field
ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
odp_flow_key_from_flow(&odp_parms, &key);
- if (!dpif_probe_feature(backer->dpif, "MPLS", &key, NULL)) {
+ if (!dpif_probe_feature(backer->dpif, "MPLS", &key, NULL, NULL)) {
break;
}
}
return n;
}
+static void
+add_sample_actions(struct ofpbuf *actions, int nesting)
+{
+ if (nesting == 0) {
+ nl_msg_put_odp_port(actions, OVS_ACTION_ATTR_OUTPUT, u32_to_odp(1));
+ return;
+ }
+
+ size_t start, actions_start;
+
+ start = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SAMPLE);
+ actions_start = nl_msg_start_nested(actions, OVS_SAMPLE_ATTR_ACTIONS);
+ add_sample_actions(actions, nesting - 1);
+ nl_msg_end_nested(actions, actions_start);
+ nl_msg_put_u32(actions, OVS_SAMPLE_ATTR_PROBABILITY, UINT32_MAX);
+ nl_msg_end_nested(actions, start);
+}
+
+/* Tests the nested sample actions levels supported by 'backer''s datapath.
+ *
+ * Returns the number of nested sample actions accepted by the datapath. */
+static size_t
+check_max_sample_nesting(struct dpif_backer *backer)
+{
+ struct odputil_keybuf keybuf;
+ struct ofpbuf key;
+ struct flow flow;
+ int n;
+
+ struct odp_flow_key_parms odp_parms = {
+ .flow = &flow,
+ };
+
+ memset(&flow, 0, sizeof flow);
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+ odp_flow_key_from_flow(&odp_parms, &key);
+
+ /* OVS datapath has always supported at least 3 nested levels. */
+ for (n = 3; n < FLOW_MAX_SAMPLE_NESTING; n++) {
+ struct ofpbuf actions;
+ bool ok;
+
+ ofpbuf_init(&actions, 300);
+ add_sample_actions(&actions, n);
+ ok = dpif_probe_feature(backer->dpif, "Sample action nesting", &key,
+ &actions, NULL);
+ ofpbuf_uninit(&actions);
+ if (!ok) {
+ break;
+ }
+ }
+
+ VLOG_INFO("%s: Max sample nesting level probed as %d",
+ dpif_name(backer->dpif), n);
+ return n;
+}
+
/* Tests whether 'backer''s datapath supports masked data in
* OVS_ACTION_ATTR_SET actions. We need to disable some features on older
* datapaths that don't support this feature. */
return !error;
}
-#define CHECK_FEATURE__(NAME, SUPPORT, FIELD, VALUE) \
+/* Tests whether 'backer''s datapath supports the clone action
+ * OVS_ACTION_ATTR_CLONE. */
+static bool
+check_clone(struct dpif_backer *backer)
+{
+ struct dpif_execute execute;
+ struct eth_header *eth;
+ struct flow flow;
+ struct dp_packet packet;
+ struct ofpbuf actions;
+ size_t clone_start;
+ int error;
+
+ /* Compose clone with an empty action list.
+ * and check if datapath can decode the message. */
+ ofpbuf_init(&actions, 64);
+ clone_start = nl_msg_start_nested(&actions, OVS_ACTION_ATTR_CLONE);
+ nl_msg_end_nested(&actions, clone_start);
+
+ /* Compose a dummy Ethernet packet. */
+ dp_packet_init(&packet, ETH_HEADER_LEN);
+ eth = dp_packet_put_zeros(&packet, ETH_HEADER_LEN);
+ eth->eth_type = htons(0x1234);
+
+ flow_extract(&packet, &flow);
+
+ /* Execute the actions. On older datapaths this fails with EINVAL, on
+ * newer datapaths it succeeds. */
+ execute.actions = actions.data;
+ execute.actions_len = actions.size;
+ execute.packet = &packet;
+ execute.flow = &flow;
+ execute.needs_help = false;
+ execute.probe = true;
+ execute.mtu = 0;
+
+ error = dpif_execute(backer->dpif, &execute);
+
+ dp_packet_uninit(&packet);
+ ofpbuf_uninit(&actions);
+
+ if (error) {
+ VLOG_INFO("%s: Datapath does not support clone action",
+ dpif_name(backer->dpif));
+ } else {
+ VLOG_INFO("%s: Datapath supports clone action",
+ dpif_name(backer->dpif));
+ }
+
+ return !error;
+}
+
+/* Tests whether 'backer''s datapath supports the OVS_CT_ATTR_EVENTMASK
+ * attribute in OVS_ACTION_ATTR_CT. */
+static bool
+check_ct_eventmask(struct dpif_backer *backer)
+{
+ struct dpif_execute execute;
+ struct dp_packet packet;
+ struct ofpbuf actions;
+ struct flow flow = {
+ .dl_type = CONSTANT_HTONS(ETH_TYPE_IP),
+ .nw_proto = IPPROTO_UDP,
+ .nw_ttl = 64,
+ /* Use the broadcast address on the loopback address range 127/8 to
+ * avoid hitting any real conntrack entries. We leave the UDP ports to
+ * zeroes for the same purpose. */
+ .nw_src = CONSTANT_HTONL(0x7fffffff),
+ .nw_dst = CONSTANT_HTONL(0x7fffffff),
+ };
+ size_t ct_start;
+ int error;
+
+ /* Compose CT action with eventmask attribute and check if datapath can
+ * decode the message. */
+ ofpbuf_init(&actions, 64);
+ ct_start = nl_msg_start_nested(&actions, OVS_ACTION_ATTR_CT);
+ /* Eventmask has no effect without the commit flag, but currently the
+ * datapath will accept an eventmask even without commit. This is useful
+ * as we do not want to persist the probe connection in the conntrack
+ * table. */
+ nl_msg_put_u32(&actions, OVS_CT_ATTR_EVENTMASK, ~0);
+ nl_msg_end_nested(&actions, ct_start);
+
+ /* Compose a dummy UDP packet. */
+ dp_packet_init(&packet, 0);
+ flow_compose(&packet, &flow, NULL, 64);
+
+ /* Execute the actions. On older datapaths this fails with EINVAL, on
+ * newer datapaths it succeeds. */
+ execute.actions = actions.data;
+ execute.actions_len = actions.size;
+ execute.packet = &packet;
+ execute.flow = &flow;
+ execute.needs_help = false;
+ execute.probe = true;
+ execute.mtu = 0;
+
+ error = dpif_execute(backer->dpif, &execute);
+
+ dp_packet_uninit(&packet);
+ ofpbuf_uninit(&actions);
+
+ if (error) {
+ VLOG_INFO("%s: Datapath does not support eventmask in conntrack action",
+ dpif_name(backer->dpif));
+ } else {
+ VLOG_INFO("%s: Datapath supports eventmask in conntrack action",
+ dpif_name(backer->dpif));
+ }
+
+ return !error;
+}
+
+/* Tests whether 'backer''s datapath supports the OVS_ACTION_ATTR_CT_CLEAR
+ * action. */
+static bool
+check_ct_clear(struct dpif_backer *backer)
+{
+ struct odputil_keybuf keybuf;
+ uint8_t actbuf[NL_A_FLAG_SIZE];
+ struct ofpbuf actions;
+ struct ofpbuf key;
+ struct flow flow;
+ bool supported;
+
+ struct odp_flow_key_parms odp_parms = {
+ .flow = &flow,
+ .probe = true,
+ };
+
+ memset(&flow, 0, sizeof flow);
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+ odp_flow_key_from_flow(&odp_parms, &key);
+
+ ofpbuf_use_stack(&actions, &actbuf, sizeof actbuf);
+ nl_msg_put_flag(&actions, OVS_ACTION_ATTR_CT_CLEAR);
+
+ supported = dpif_probe_feature(backer->dpif, "ct_clear", &key,
+ &actions, NULL);
+
+ VLOG_INFO("%s: Datapath %s ct_clear action",
+ dpif_name(backer->dpif), (supported) ? "supports"
+ : "does not support");
+ return supported;
+}
+
+/* Probe the highest dp_hash algorithm supported by the datapath. */
+static size_t
+check_max_dp_hash_alg(struct dpif_backer *backer)
+{
+ struct odputil_keybuf keybuf;
+ struct ofpbuf key;
+ struct flow flow;
+ struct ovs_action_hash *hash;
+ int max_alg = 0;
+
+ struct odp_flow_key_parms odp_parms = {
+ .flow = &flow,
+ .probe = true,
+ };
+
+ memset(&flow, 0, sizeof flow);
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+ odp_flow_key_from_flow(&odp_parms, &key);
+
+ /* All datapaths support algortithm 0 (OVS_HASH_ALG_L4). */
+ for (int alg = 1; alg < __OVS_HASH_MAX; alg++) {
+ struct ofpbuf actions;
+ bool ok;
+
+ ofpbuf_init(&actions, 300);
+ hash = nl_msg_put_unspec_uninit(&actions,
+ OVS_ACTION_ATTR_HASH, sizeof *hash);
+ hash->hash_basis = 0;
+ hash->hash_alg = alg;
+ ok = dpif_probe_feature(backer->dpif, "Max dp_hash algorithm", &key,
+ &actions, NULL);
+ ofpbuf_uninit(&actions);
+ if (ok) {
+ max_alg = alg;
+ } else {
+ break;
+ }
+ }
+
+ VLOG_INFO("%s: Max dp_hash algorithm probed to be %d",
+ dpif_name(backer->dpif), max_alg);
+ return max_alg;
+}
+
+#define CHECK_FEATURE__(NAME, SUPPORT, FIELD, VALUE, ETHTYPE) \
static bool \
check_##NAME(struct dpif_backer *backer) \
{ \
\
memset(&flow, 0, sizeof flow); \
flow.FIELD = VALUE; \
+ flow.dl_type = htons(ETHTYPE); \
\
ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); \
odp_flow_key_from_flow(&odp_parms, &key); \
- enable = dpif_probe_feature(backer->dpif, #NAME, &key, NULL); \
+ enable = dpif_probe_feature(backer->dpif, #NAME, &key, NULL, NULL); \
\
if (enable) { \
VLOG_INFO("%s: Datapath supports "#NAME, dpif_name(backer->dpif)); \
\
return enable; \
}
-#define CHECK_FEATURE(FIELD) CHECK_FEATURE__(FIELD, FIELD, FIELD, 1)
+#define CHECK_FEATURE(FIELD) CHECK_FEATURE__(FIELD, FIELD, FIELD, 1, \
+ ETH_TYPE_IP)
CHECK_FEATURE(ct_state)
CHECK_FEATURE(ct_zone)
CHECK_FEATURE(ct_mark)
-CHECK_FEATURE__(ct_label, ct_label, ct_label.u64.lo, 1)
-CHECK_FEATURE__(ct_state_nat, ct_state, ct_state, CS_TRACKED|CS_SRC_NAT)
+CHECK_FEATURE__(ct_label, ct_label, ct_label.u64.lo, 1, ETH_TYPE_IP)
+CHECK_FEATURE__(ct_state_nat, ct_state, ct_state, \
+ CS_TRACKED|CS_SRC_NAT, ETH_TYPE_IP)
+CHECK_FEATURE__(ct_orig_tuple, ct_orig_tuple, ct_nw_proto, 1, ETH_TYPE_IP)
+CHECK_FEATURE__(ct_orig_tuple6, ct_orig_tuple6, ct_nw_proto, 1, ETH_TYPE_IPV6)
#undef CHECK_FEATURE
#undef CHECK_FEATURE__
static void
check_support(struct dpif_backer *backer)
{
- /* This feature needs to be tested after udpif threads are set. */
- backer->support.variable_length_userdata = false;
-
- backer->support.odp.recirc = check_recirc(backer);
- backer->support.odp.max_mpls_depth = check_max_mpls_depth(backer);
- backer->support.masked_set_action = check_masked_set_action(backer);
- backer->support.trunc = check_trunc_action(backer);
- backer->support.ufid = check_ufid(backer);
- backer->support.tnl_push_pop = dpif_supports_tnl_push_pop(backer->dpif);
-
- backer->support.odp.ct_state = check_ct_state(backer);
- backer->support.odp.ct_zone = check_ct_zone(backer);
- backer->support.odp.ct_mark = check_ct_mark(backer);
- backer->support.odp.ct_label = check_ct_label(backer);
-
- backer->support.odp.ct_state_nat = check_ct_state_nat(backer);
+ /* Actions. */
+ backer->rt_support.odp.recirc = check_recirc(backer);
+ backer->rt_support.odp.max_vlan_headers = check_max_vlan_headers(backer);
+ backer->rt_support.odp.max_mpls_depth = check_max_mpls_depth(backer);
+ backer->rt_support.masked_set_action = check_masked_set_action(backer);
+ backer->rt_support.trunc = check_trunc_action(backer);
+ backer->rt_support.ufid = check_ufid(backer);
+ backer->rt_support.tnl_push_pop = dpif_supports_tnl_push_pop(backer->dpif);
+ backer->rt_support.clone = check_clone(backer);
+ backer->rt_support.sample_nesting = check_max_sample_nesting(backer);
+ backer->rt_support.ct_eventmask = check_ct_eventmask(backer);
+ backer->rt_support.ct_clear = check_ct_clear(backer);
+ backer->rt_support.max_hash_alg = check_max_dp_hash_alg(backer);
+
+ /* Flow fields. */
+ backer->rt_support.odp.ct_state = check_ct_state(backer);
+ backer->rt_support.odp.ct_zone = check_ct_zone(backer);
+ backer->rt_support.odp.ct_mark = check_ct_mark(backer);
+ backer->rt_support.odp.ct_label = check_ct_label(backer);
+
+ backer->rt_support.odp.ct_state_nat = check_ct_state_nat(backer);
+ backer->rt_support.odp.ct_orig_tuple = check_ct_orig_tuple(backer);
+ backer->rt_support.odp.ct_orig_tuple6 = check_ct_orig_tuple6(backer);
}
static int
}
}
- hmap_insert(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node,
+ hmap_insert(&all_ofproto_dpifs_by_name,
+ &ofproto->all_ofproto_dpifs_by_name_node,
hash_string(ofproto->up.name, 0));
+ hmap_insert(&all_ofproto_dpifs_by_uuid,
+ &ofproto->all_ofproto_dpifs_by_uuid_node,
+ uuid_hash(&ofproto->uuid));
memset(&ofproto->stats, 0, sizeof ofproto->stats);
ofproto_init_tables(ofproto_, N_TABLES);
controller->max_len = UINT16_MAX;
controller->controller_id = 0;
controller->reason = OFPR_IMPLICIT_MISS;
+ controller->meter_id = NX_CTLR_NO_METER;
ofpact_finish_CONTROLLER(&ofpacts, &controller);
error = add_internal_miss_flow(ofproto, id++, &ofpacts,
}
static void
-destruct(struct ofproto *ofproto_)
+destruct(struct ofproto *ofproto_, bool del)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
struct ofproto_async_msg *am;
* to the ofproto or anything in it. */
udpif_synchronize(ofproto->backer->udpif);
- hmap_remove(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node);
+ hmap_remove(&all_ofproto_dpifs_by_name,
+ &ofproto->all_ofproto_dpifs_by_name_node);
+ hmap_remove(&all_ofproto_dpifs_by_uuid,
+ &ofproto->all_ofproto_dpifs_by_uuid_node);
OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
CLS_FOR_EACH (rule, up.cr, &table->cls) {
hmap_destroy(&ofproto->bundles);
mac_learning_unref(ofproto->ml);
mcast_snooping_unref(ofproto->ms);
+ stp_unref(ofproto->stp);
+ rstp_unref(ofproto->rstp);
sset_destroy(&ofproto->ports);
sset_destroy(&ofproto->ghost_ports);
seq_destroy(ofproto->ams_seq);
- close_dpif_backer(ofproto->backer);
+ close_dpif_backer(ofproto->backer, del);
}
static int
static void
query_tables(struct ofproto *ofproto,
- struct ofputil_table_features *features,
+ struct ofputil_table_features *features OVS_UNUSED,
struct ofputil_table_stats *stats)
{
- strcpy(features->name, "classifier");
-
if (stats) {
int i;
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- atomic_store_relaxed(&ofproto->tables_version, version);
+ /* Use memory_order_release to signify that any prior memory accesses can
+ * not be reordered to happen after this atomic store. This makes sure the
+ * new version is properly set up when the readers can read this 'version'
+ * value. */
+ atomic_store_explicit(&ofproto->tables_version, version,
+ memory_order_release);
+ /* 'need_revalidate' can be reordered to happen before the atomic_store
+ * above, but it does not matter as this variable is not accessed by other
+ * threads. */
ofproto->backer->need_revalidate = REV_FLOW_TABLE;
}
port->cfm = NULL;
port->bfd = NULL;
port->lldp = NULL;
- port->may_enable = false;
port->stp_port = NULL;
port->stp_state = STP_DISABLED;
port->rstp_port = NULL;
port->qdscp = NULL;
port->n_qdscp = 0;
port->carrier_seq = netdev_get_carrier_resets(netdev);
- port->is_layer3 = netdev_vport_is_layer3(netdev);
if (netdev_vport_is_patch(netdev)) {
/* By bailing out here, we don't submit the port to the sFlow module
}
port->is_tunnel = true;
- if (ofproto->ipfix) {
- dpif_ipfix_add_tunnel_port(ofproto->ipfix, port_, port->odp_port);
- }
} else {
/* Sanity-check that a mapping doesn't already exist. This
* shouldn't happen for non-tunnel ports. */
if (ofproto->sflow) {
dpif_sflow_add_port(ofproto->sflow, port_, port->odp_port);
}
+ if (ofproto->ipfix) {
+ dpif_ipfix_add_port(ofproto->ipfix, port_, port->odp_port);
+ }
return 0;
}
* assumes that removal of attached ports will happen as part of
* destruction. */
if (!port->is_tunnel) {
- dpif_port_del(ofproto->backer->dpif, port->odp_port);
+ dpif_port_del(ofproto->backer->dpif, port->odp_port, false);
+ }
+ } else if (del) {
+ /* The underlying device is already deleted (e.g. tunctl -d).
+ * Calling dpif_port_remove to do local cleanup for the netdev */
+ if (!port->is_tunnel) {
+ dpif_port_del(ofproto->backer->dpif, port->odp_port, true);
}
}
atomic_count_dec(&ofproto->backer->tnl_count);
}
- if (port->is_tunnel && ofproto->ipfix) {
- dpif_ipfix_del_tunnel_port(ofproto->ipfix, port->odp_port);
- }
-
- tnl_port_del(port);
+ tnl_port_del(port, port->odp_port);
sset_find_and_delete(&ofproto->ports, devname);
sset_find_and_delete(&ofproto->ghost_ports, devname);
bundle_remove(port_);
if (ofproto->sflow) {
dpif_sflow_del_port(ofproto->sflow, port->odp_port);
}
+ if (ofproto->ipfix) {
+ dpif_ipfix_del_port(ofproto->ipfix, port->odp_port);
+ }
free(port->qdscp);
}
if (port->is_tunnel) {
struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
- if (tnl_port_reconfigure(port, netdev, port->odp_port,
+ if (tnl_port_reconfigure(port, netdev, port->odp_port, port->odp_port,
ovs_native_tunneling_is_on(ofproto),
dp_port_name)) {
ofproto->backer->need_revalidate = REV_RECONFIGURE;
bundle_update(port->bundle);
}
}
+ port_run(port);
}
static int
di, bridge_exporter_options, flow_exporters_options,
n_flow_exporters_options);
- /* Add tunnel ports only when a new ipfix created */
+ /* Add ports only when a new ipfix created */
if (new_di == true) {
struct ofport_dpif *ofport;
HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
- if (ofport->is_tunnel == true) {
- dpif_ipfix_add_tunnel_port(di, &ofport->up, ofport->odp_port);
- }
+ dpif_ipfix_add_port(di, &ofport->up, ofport->odp_port);
}
}
{
struct ofproto_dpif *ofproto = ofproto_;
struct ofport_dpif *ofport = ofport_;
- struct eth_header *eth = dp_packet_l2(pkt);
+ struct eth_header *eth = dp_packet_eth(pkt);
netdev_get_etheraddr(ofport->up.netdev, ð->eth_src);
if (eth_addr_is_zero(eth->eth_src)) {
VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d",
ofproto->up.name, port_num);
} else {
- struct eth_header *eth = dp_packet_l2(pkt);
+ struct eth_header *eth = dp_packet_eth(pkt);
netdev_get_etheraddr(ofport->up.netdev, ð->eth_src);
if (eth_addr_is_zero(eth->eth_src)) {
}
}
+static void
+stp_check_and_update_link_state(struct ofproto_dpif *ofproto)
+{
+ struct ofport_dpif *ofport;
+
+ HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
+ bool up = netdev_get_carrier(ofport->up.netdev);
+
+ if (ofport->stp_port &&
+ up != (stp_port_get_state(ofport->stp_port) != STP_DISABLED)) {
+
+ VLOG_DBG("bridge %s, port %s is %s, %s it.",
+ ofproto->up.name, netdev_get_name(ofport->up.netdev),
+ up ? "up" : "down",
+ up ? "enabling" : "disabling");
+
+ if (up) {
+ stp_port_enable(ofport->stp_port);
+ stp_port_set_aux(ofport->stp_port, ofport);
+ } else {
+ stp_port_disable(ofport->stp_port);
+ }
+
+ update_stp_port_state(ofport);
+ }
+ }
+}
+
/* Configures STP on 'ofport_' using the settings defined in 's'. The
* caller is responsible for assigning STP port numbers and ensuring
* there are no duplicates. */
/* Set name before enabling the port so that debugging messages can print
* the name. */
stp_port_set_name(sp, netdev_get_name(ofport->up.netdev));
- stp_port_enable(sp);
+
+ if (netdev_get_carrier(ofport_->netdev)) {
+ stp_port_enable(sp);
+ } else {
+ stp_port_disable(sp);
+ }
stp_port_set_aux(sp, ofport);
stp_port_set_priority(sp, s->priority);
}
s->enabled = true;
- s->port_id = stp_port_get_id(sp);
- s->state = stp_port_get_state(sp);
+ stp_port_get_status(sp, &s->port_id, &s->state, &s->role);
s->sec_in_state = (time_msec() - ofport->stp_state_entered) / 1000;
- s->role = stp_port_get_role(sp);
return 0;
}
stp_tick(ofproto->stp, MIN(INT_MAX, elapsed));
ofproto->stp_last_tick = now;
}
+
+ stp_check_and_update_link_state(ofproto);
+
while (stp_get_changed_port(ofproto->stp, &sp)) {
struct ofport_dpif *ofport = stp_port_get_aux(sp);
rstp_port_set(rp, s->port_num, s->priority, s->path_cost,
s->admin_edge_port, s->auto_edge,
s->admin_p2p_mac_state, s->admin_port_state, s->mcheck,
- ofport);
+ ofport, netdev_get_name(ofport->up.netdev));
update_rstp_port_state(ofport);
/* Synchronize operational status. */
- rstp_port_set_mac_operational(rp, ofport->may_enable);
+ rstp_port_set_mac_operational(rp, ofport->up.may_enable);
}
static void
if (all_ofprotos) {
struct ofproto_dpif *o;
- HMAP_FOR_EACH (o, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ HMAP_FOR_EACH (o, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
if (o != ofproto) {
struct mac_entry *e;
bundle->floodable = true;
LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD
- || port->is_layer3
+ || netdev_get_pt_mode(port->up.netdev) == NETDEV_PT_LEGACY_L3
|| (bundle->ofproto->stp && !stp_forward_in_state(port->stp_state))
|| (bundle->ofproto->rstp && !rstp_forward_in_state(port->rstp_state))) {
bundle->floodable = false;
port->bundle = bundle;
ovs_list_push_back(&bundle->ports, &port->bundle_node);
if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD
- || port->is_layer3
+ || netdev_get_pt_mode(port->up.netdev) == NETDEV_PT_LEGACY_L3
|| (bundle->ofproto->stp && !stp_forward_in_state(port->stp_state))
|| (bundle->ofproto->rstp && !rstp_forward_in_state(port->rstp_state))) {
bundle->floodable = false;
}
bundle_flush_macs(bundle, true);
+ mcast_snooping_flush_bundle(ofproto->ms, bundle);
hmap_remove(&ofproto->bundles, &bundle->hmap_node);
free(bundle->name);
free(bundle->trunks);
+ free(bundle->cvlans);
lacp_unref(bundle->lacp);
bond_unref(bundle->bond);
free(bundle);
bool need_flush = false;
struct ofport_dpif *port;
struct ofbundle *bundle;
- unsigned long *trunks;
+ unsigned long *trunks = NULL;
+ unsigned long *cvlans = NULL;
int vlan;
size_t i;
bool ok;
+ bundle = bundle_lookup(ofproto, aux);
+
if (!s) {
- bundle_destroy(bundle_lookup(ofproto, aux));
+ bundle_destroy(bundle);
return 0;
}
ovs_assert(s->n_slaves == 1 || s->bond != NULL);
ovs_assert((s->lacp != NULL) == (s->lacp_slaves != NULL));
- bundle = bundle_lookup(ofproto, aux);
if (!bundle) {
bundle = xmalloc(sizeof *bundle);
ovs_list_init(&bundle->ports);
bundle->vlan_mode = PORT_VLAN_TRUNK;
+ bundle->qinq_ethtype = ETH_TYPE_VLAN_8021AD;
bundle->vlan = -1;
bundle->trunks = NULL;
+ bundle->cvlans = NULL;
bundle->use_priority_tags = s->use_priority_tags;
bundle->lacp = NULL;
bundle->bond = NULL;
bundle->floodable = true;
+ bundle->protected = false;
mbridge_register_bundle(ofproto->mbridge, bundle);
}
need_flush = true;
}
+ if (s->qinq_ethtype != bundle->qinq_ethtype) {
+ bundle->qinq_ethtype = s->qinq_ethtype;
+ need_flush = true;
+ }
+
/* Set VLAN tag. */
vlan = (s->vlan_mode == PORT_VLAN_TRUNK ? -1
: s->vlan >= 0 && s->vlan <= 4095 ? s->vlan
}
break;
+ case PORT_VLAN_DOT1Q_TUNNEL:
+ cvlans = CONST_CAST(unsigned long *, s->cvlans);
+ break;
+
default:
OVS_NOT_REACHED();
}
free(trunks);
}
+ if (!vlan_bitmap_equal(cvlans, bundle->cvlans)) {
+ free(bundle->cvlans);
+ if (cvlans == s->cvlans) {
+ bundle->cvlans = vlan_bitmap_clone(cvlans);
+ } else {
+ bundle->cvlans = cvlans;
+ cvlans = NULL;
+ }
+ need_flush = true;
+ }
+ if (cvlans != s->cvlans) {
+ free(cvlans);
+ }
+
/* Bonding. */
if (!ovs_list_is_short(&bundle->ports)) {
bundle->ofproto->has_bonded_bundles = true;
bundle->bond = NULL;
}
+ /* Set proteced port mode */
+ if (s->protected != bundle->protected) {
+ bundle->protected = s->protected;
+ need_flush = true;
+ }
+
/* If we changed something that would affect MAC learning, un-learn
* everything on this port and force flow revalidation. */
if (need_flush) {
bundle_flush_macs(bundle, false);
+ mcast_snooping_flush_bundle(ofproto->ms, bundle);
}
return 0;
static void
send_pdu_cb(void *port_, const void *pdu, size_t pdu_size)
{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
struct ofport_dpif *port = port_;
struct eth_addr ea;
int error;
ofproto_dpif_send_packet(port, false, &packet);
dp_packet_uninit(&packet);
} else {
- VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface "
+ static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(1, 10);
+ VLOG_ERR_RL(&rll, "port %s: cannot obtain Ethernet address of iface "
"%s (%s)", port->bundle->name,
netdev_get_name(port->up.netdev), ovs_strerror(error));
}
}
if (n_errors) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "bond %s: %d errors sending %d gratuitous learning "
+ static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rll, "bond %s: %d errors sending %d gratuitous learning "
"packets, last error was: %s",
bundle->name, n_errors, n_packets, ovs_strerror(error));
} else {
struct ofport_dpif *port;
LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
- bond_slave_set_may_enable(bundle->bond, port, port->may_enable);
+ bond_slave_set_may_enable(bundle->bond, port, port->up.may_enable);
}
if (bond_run(bundle->bond, lacp_status(bundle->lacp))) {
return;
}
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
struct ofport *peer_ofport;
struct ofport_dpif *peer;
char *peer_peer;
free(peer_name);
}
-static void
-port_run(struct ofport_dpif *ofport)
+static bool
+may_enable_port(struct ofport_dpif *ofport)
{
- long long int carrier_seq = netdev_get_carrier_resets(ofport->up.netdev);
- bool carrier_changed = carrier_seq != ofport->carrier_seq;
- bool enable = netdev_get_carrier(ofport->up.netdev);
- bool cfm_enable = false;
- bool bfd_enable = false;
-
- ofport->carrier_seq = carrier_seq;
-
- if (ofport->cfm) {
- int cfm_opup = cfm_get_opup(ofport->cfm);
-
- cfm_enable = !cfm_get_fault(ofport->cfm);
-
- if (cfm_opup >= 0) {
- cfm_enable = cfm_enable && cfm_opup;
- }
+ /* Carrier must be up. */
+ if (!netdev_get_carrier(ofport->up.netdev)) {
+ return false;
}
- if (ofport->bfd) {
- bfd_enable = bfd_forwarding(ofport->bfd);
+ /* If CFM or BFD is enabled, then at least one of them must report that the
+ * port is up. */
+ if ((ofport->bfd || ofport->cfm)
+ && !(ofport->cfm
+ && !cfm_get_fault(ofport->cfm)
+ && cfm_get_opup(ofport->cfm) != 0)
+ && !(ofport->bfd
+ && bfd_forwarding(ofport->bfd))) {
+ return false;
}
- if (ofport->bfd || ofport->cfm) {
- enable = enable && (cfm_enable || bfd_enable);
+ /* If LACP is enabled, it must report that the link is enabled. */
+ if (ofport->bundle
+ && !lacp_slave_may_enable(ofport->bundle->lacp, ofport)) {
+ return false;
}
- if (ofport->bundle) {
- enable = enable && lacp_slave_may_enable(ofport->bundle->lacp, ofport);
- if (carrier_changed) {
- lacp_slave_carrier_changed(ofport->bundle->lacp, ofport);
- }
+ return true;
+}
+
+static void
+port_run(struct ofport_dpif *ofport)
+{
+ long long int carrier_seq = netdev_get_carrier_resets(ofport->up.netdev);
+ bool carrier_changed = carrier_seq != ofport->carrier_seq;
+ ofport->carrier_seq = carrier_seq;
+ if (carrier_changed && ofport->bundle) {
+ lacp_slave_carrier_changed(ofport->bundle->lacp, ofport);
}
- if (ofport->may_enable != enable) {
- struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ bool enable = may_enable_port(ofport);
+ if (ofport->up.may_enable != enable) {
+ ofproto_port_set_enable(&ofport->up, enable);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
ofproto->backer->need_revalidate = REV_PORT_TOGGLED;
if (ofport->rstp_port) {
rstp_port_set_mac_operational(ofport->rstp_port, enable);
}
}
-
- ofport->may_enable = enable;
}
static int
}
dp_port_name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
- if (!dpif_port_exists(ofproto->backer->dpif, dp_port_name)) {
- odp_port_t port_no = ODPP_NONE;
- int error;
- error = dpif_port_add(ofproto->backer->dpif, netdev, &port_no);
+ odp_port_t port_no = ODPP_NONE;
+ int error = dpif_port_add(ofproto->backer->dpif, netdev, &port_no);
+ if (error != EEXIST && error != EBUSY) {
if (error) {
return error;
}
netdev_get_name(ofport->up.netdev));
ofproto->backer->need_revalidate = REV_RECONFIGURE;
if (!ofport->is_tunnel && !netdev_vport_is_patch(ofport->up.netdev)) {
- error = dpif_port_del(ofproto->backer->dpif, ofport->odp_port);
+ error = dpif_port_del(ofproto->backer->dpif, ofport->odp_port, false);
if (!error) {
/* The caller is going to close ofport->up.netdev. If this is a
* bonded port, then the bond is using that netdev, so remove it
ovs_version_t version, const struct flow *flow,
struct rule_dpif *rule,
const struct ofpact *ofpacts, size_t ofpacts_len,
- int indentation, int depth, int resubmits,
+ int depth, int resubmits,
struct dp_packet *packet)
{
struct dpif_flow_stats stats;
xin.ofpacts = ofpacts;
xin.ofpacts_len = ofpacts_len;
xin.resubmit_stats = &stats;
- xin.indentation = indentation;
xin.depth = depth;
xin.resubmits = resubmits;
if (xlate_actions(&xin, &xout) != XLATE_OK) {
struct dp_packet *packet)
{
return ofproto_dpif_execute_actions__(ofproto, version, flow, rule,
- ofpacts, ofpacts_len, 0, 0, 0,
- packet);
+ ofpacts, ofpacts_len, 0, 0, packet);
}
static void
ovs_mutex_unlock(&rule->stats_mutex);
}
-ovs_be64
-rule_dpif_get_flow_cookie(const struct rule_dpif *rule)
- OVS_REQUIRES(rule->up.mutex)
-{
- return rule->up.flow_cookie;
-}
-
-void
-rule_dpif_reduce_timeouts(struct rule_dpif *rule, uint16_t idle_timeout,
- uint16_t hard_timeout)
-{
- ofproto_rule_reduce_timeouts(&rule->up, idle_timeout, hard_timeout);
-}
-
-/* Returns 'rule''s actions. The returned actions are RCU-protected, and can
- * be read until the calling thread quiesces. */
-const struct rule_actions *
-rule_dpif_get_actions(const struct rule_dpif *rule)
-{
- return rule_get_actions(&rule->up);
-}
-
/* Sets 'rule''s recirculation id. */
static void
rule_dpif_set_recirc_id(struct rule_dpif *rule, uint32_t id)
{
ovs_version_t version;
- atomic_read_relaxed(&ofproto->tables_version, &version);
-
+ /* Use memory_order_acquire to signify that any following memory accesses
+ * can not be reordered to happen before this atomic read. This makes sure
+ * all following reads relate to this or a newer version, but never to an
+ * older version. */
+ atomic_read_explicit(&ofproto->tables_version, &version,
+ memory_order_acquire);
return version;
}
port = ofp_port_to_ofport(ofproto, old_in_port);
if (!port) {
- VLOG_WARN_RL(&rl, "packet-in on unknown OpenFlow port %"PRIu16,
+ VLOG_WARN_RL(&rl, "packet-in on unknown OpenFlow port %"PRIu32,
old_in_port);
} else if (!(port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN)) {
rule = ofproto->miss_rule;
ovs_u128 ct_label;
uint32_t ct_mark;
- support = &ofproto_dpif_get_support(ofproto)->odp;
- ct_state = MINIFLOW_GET_U16(flow, ct_state);
+ support = &ofproto->backer->rt_support.odp;
+ ct_state = MINIFLOW_GET_U8(flow, ct_state);
+
+ if (ct_state & CS_UNSUPPORTED_MASK) {
+ return OFPERR_OFPBMC_BAD_MASK;
+ }
+
+ /* Do not bother dissecting the flow further if the datapath supports all
+ * the features we know of. */
if (support->ct_state && support->ct_zone && support->ct_mark
- && support->ct_label && support->ct_state_nat) {
- return ct_state & CS_UNSUPPORTED_MASK ? OFPERR_OFPBMC_BAD_MASK : 0;
+ && support->ct_label && support->ct_state_nat
+ && support->ct_orig_tuple && support->ct_orig_tuple6) {
+ return 0;
}
ct_zone = MINIFLOW_GET_U16(flow, ct_zone);
ct_label = MINIFLOW_GET_U128(flow, ct_label);
if ((ct_state && !support->ct_state)
- || (ct_state & CS_UNSUPPORTED_MASK)
|| ((ct_state & (CS_SRC_NAT | CS_DST_NAT)) && !support->ct_state_nat)
|| (ct_zone && !support->ct_zone)
|| (ct_mark && !support->ct_mark)
|| (!ovs_u128_is_zero(ct_label) && !support->ct_label)) {
- return OFPERR_OFPBMC_BAD_MASK;
+ return OFPERR_NXBMC_CT_DATAPATH_SUPPORT;
+ }
+
+ if (!support->ct_orig_tuple && !support->ct_orig_tuple6
+ && (MINIFLOW_GET_U8(flow, ct_nw_proto)
+ || MINIFLOW_GET_U16(flow, ct_tp_src)
+ || MINIFLOW_GET_U16(flow, ct_tp_dst))) {
+ return OFPERR_NXBMC_CT_DATAPATH_SUPPORT;
+ }
+
+ if (!support->ct_orig_tuple
+ && (MINIFLOW_GET_U32(flow, ct_nw_src)
+ || MINIFLOW_GET_U32(flow, ct_nw_dst))) {
+ return OFPERR_NXBMC_CT_DATAPATH_SUPPORT;
+ }
+
+ if (!support->ct_orig_tuple6
+ && (!ovs_u128_is_zero(MINIFLOW_GET_U128(flow, ct_ipv6_src))
+ || !ovs_u128_is_zero(MINIFLOW_GET_U128(flow, ct_ipv6_dst)))) {
+ return OFPERR_NXBMC_CT_DATAPATH_SUPPORT;
}
return 0;
}
+static void
+report_unsupported_act(const char *action, const char *detail)
+{
+ static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rll, "Rejecting %s action because datapath does not support"
+ "%s%s (your kernel module may be out of date)",
+ action, detail ? " " : "", detail ? detail : "");
+}
+
static enum ofperr
check_actions(const struct ofproto_dpif *ofproto,
const struct rule_actions *const actions)
{
const struct ofpact *ofpact;
+ const struct odp_support *support = &ofproto->backer->rt_support.odp;
OFPACT_FOR_EACH (ofpact, actions->ofpacts, actions->ofpacts_len) {
- const struct odp_support *support;
- const struct ofpact_conntrack *ct;
- const struct ofpact *a;
-
- if (ofpact->type != OFPACT_CT) {
- continue;
- }
+ if (ofpact->type == OFPACT_CT) {
+ const struct ofpact_conntrack *ct;
+ const struct ofpact *a;
- ct = CONTAINER_OF(ofpact, struct ofpact_conntrack, ofpact);
- support = &ofproto_dpif_get_support(ofproto)->odp;
+ ct = CONTAINER_OF(ofpact, struct ofpact_conntrack, ofpact);
- if (!support->ct_state) {
- return OFPERR_OFPBAC_BAD_TYPE;
- }
- if ((ct->zone_imm || ct->zone_src.field) && !support->ct_zone) {
- return OFPERR_OFPBAC_BAD_ARGUMENT;
- }
+ if (!support->ct_state) {
+ report_unsupported_act("ct", "ct action");
+ return OFPERR_NXBAC_CT_DATAPATH_SUPPORT;
+ }
+ if ((ct->zone_imm || ct->zone_src.field) && !support->ct_zone) {
+ report_unsupported_act("ct", "ct zones");
+ return OFPERR_NXBAC_CT_DATAPATH_SUPPORT;
+ }
+ /* So far the force commit feature is implemented together with the
+ * original direction tuple feature by all datapaths, so we use the
+ * support flag for the 'ct_orig_tuple' to indicate support for the
+ * force commit feature as well. */
+ if ((ct->flags & NX_CT_F_FORCE) && !support->ct_orig_tuple) {
+ report_unsupported_act("ct", "force commit");
+ return OFPERR_NXBAC_CT_DATAPATH_SUPPORT;
+ }
- OFPACT_FOR_EACH(a, ct->actions, ofpact_ct_get_action_len(ct)) {
- const struct mf_field *dst = ofpact_get_mf_dst(a);
+ OFPACT_FOR_EACH(a, ct->actions, ofpact_ct_get_action_len(ct)) {
+ const struct mf_field *dst = ofpact_get_mf_dst(a);
- if (a->type == OFPACT_NAT && !support->ct_state_nat) {
- /* The backer doesn't seem to support the NAT bits in
- * 'ct_state': assume that it doesn't support the NAT
- * action. */
- return OFPERR_OFPBAC_BAD_TYPE;
+ if (a->type == OFPACT_NAT && !support->ct_state_nat) {
+ /* The backer doesn't seem to support the NAT bits in
+ * 'ct_state': assume that it doesn't support the NAT
+ * action. */
+ report_unsupported_act("ct", "nat");
+ return OFPERR_NXBAC_CT_DATAPATH_SUPPORT;
+ }
+ if (dst && ((dst->id == MFF_CT_MARK && !support->ct_mark) ||
+ (dst->id == MFF_CT_LABEL && !support->ct_label))) {
+ report_unsupported_act("ct", "setting mark and/or label");
+ return OFPERR_NXBAC_CT_DATAPATH_SUPPORT;
+ }
}
- if (dst && ((dst->id == MFF_CT_MARK && !support->ct_mark)
- || (dst->id == MFF_CT_LABEL && !support->ct_label))) {
- return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
+ } else if (ofpact->type == OFPACT_RESUBMIT) {
+ struct ofpact_resubmit *resubmit = ofpact_get_RESUBMIT(ofpact);
+
+ if (resubmit->with_ct_orig && !support->ct_orig_tuple) {
+ report_unsupported_act("resubmit",
+ "ct original direction tuple");
+ return OFPERR_NXBAC_CT_DATAPATH_SUPPORT;
}
}
}
/* Take a reference to the new rule, and refer all stats updates from
* the old rule to the new rule. */
- rule_dpif_ref(rule);
+ ofproto_rule_ref(&rule->up);
ovs_mutex_lock(&old_rule->stats_mutex);
ovs_mutex_lock(&rule->stats_mutex);
ovs_mutex_destroy(&rule->stats_mutex);
/* Release reference to the new rule, if any. */
if (rule->new_rule) {
- rule_dpif_unref(rule->new_rule);
+ ofproto_rule_unref(&rule->new_rule->up);
}
if (rule->recirc_id) {
recirc_free_id(rule->recirc_id);
xin.allow_side_effects = false;
xin.resubmit_stats = NULL;
xin.xcache = &aux->xcache;
+ xin.in_packet_out = true;
if (xlate_actions(&xin, &xout) != XLATE_OK) {
error = OFPERR_OFPFMFC_UNKNOWN; /* Error processing actions. */
static void
ofproto_dpif_xcache_execute(struct ofproto_dpif *ofproto,
struct xlate_cache *xcache,
- const struct dpif_flow_stats *stats)
+ struct dpif_flow_stats *stats)
OVS_REQUIRES(ofproto_mutex)
{
struct xc_entry *entry;
case XC_NORMAL:
case XC_GROUP:
case XC_TNL_NEIGH:
- case XC_CONTROLLER:
+ case XC_TUNNEL_HEADER:
xlate_push_stats_entry(entry, stats);
break;
default:
group_construct_stats(struct group_dpif *group)
OVS_REQUIRES(group->stats_mutex)
{
- struct ofputil_bucket *bucket;
- const struct ovs_list *buckets;
-
group->packet_count = 0;
group->byte_count = 0;
- buckets = group_dpif_get_buckets(group, NULL);
- LIST_FOR_EACH (bucket, list_node, buckets) {
+ struct ofputil_bucket *bucket;
+ LIST_FOR_EACH (bucket, list_node, &group->up.buckets) {
bucket->stats.packet_count = 0;
bucket->stats.byte_count = 0;
}
bucket->stats.packet_count += stats->n_packets;
bucket->stats.byte_count += stats->n_bytes;
} else { /* Credit to all buckets */
- const struct ovs_list *buckets;
-
- buckets = group_dpif_get_buckets(group, NULL);
- LIST_FOR_EACH (bucket, list_node, buckets) {
+ LIST_FOR_EACH (bucket, list_node, &group->up.buckets) {
bucket->stats.packet_count += stats->n_packets;
bucket->stats.byte_count += stats->n_bytes;
}
ovs_mutex_unlock(&group->stats_mutex);
}
+/* Calculate the dp_hash mask needed to provide the least weighted bucket
+ * with at least one hash value and construct a mapping table from masked
+ * dp_hash value to group bucket using the Webster method.
+ * If the caller specifies a non-zero max_hash value, abort and return false
+ * if more hash values would be required. The absolute maximum number of
+ * hash values supported is 256. */
+
+#define MAX_SELECT_GROUP_HASH_VALUES 256
+
+static bool
+group_setup_dp_hash_table(struct group_dpif *group, size_t max_hash)
+{
+ struct ofputil_bucket *bucket;
+ uint32_t n_buckets = group->up.n_buckets;
+ uint64_t total_weight = 0;
+ uint16_t min_weight = UINT16_MAX;
+ struct webster {
+ struct ofputil_bucket *bucket;
+ uint32_t divisor;
+ double value;
+ int hits;
+ } *webster;
+
+ if (n_buckets == 0) {
+ VLOG_DBG(" Don't apply dp_hash method without buckets.");
+ return false;
+ }
+
+ webster = xcalloc(n_buckets, sizeof(struct webster));
+ int i = 0;
+ LIST_FOR_EACH (bucket, list_node, &group->up.buckets) {
+ if (bucket->weight > 0 && bucket->weight < min_weight) {
+ min_weight = bucket->weight;
+ }
+ total_weight += bucket->weight;
+ webster[i].bucket = bucket;
+ webster[i].divisor = 1;
+ webster[i].value = bucket->weight;
+ webster[i].hits = 0;
+ i++;
+ }
+
+ if (total_weight == 0) {
+ VLOG_DBG(" Total weight is zero. No active buckets.");
+ free(webster);
+ return false;
+ }
+ VLOG_DBG(" Minimum weight: %d, total weight: %"PRIu64,
+ min_weight, total_weight);
+
+ uint64_t min_slots = DIV_ROUND_UP(total_weight, min_weight);
+ uint64_t min_slots2 = ROUND_UP_POW2(min_slots);
+ uint64_t n_hash = MAX(16, min_slots2);
+ if (n_hash > MAX_SELECT_GROUP_HASH_VALUES ||
+ (max_hash != 0 && n_hash > max_hash)) {
+ VLOG_DBG(" Too many hash values required: %"PRIu64, n_hash);
+ return false;
+ }
+
+ VLOG_DBG(" Using %"PRIu64" hash values:", n_hash);
+ group->hash_mask = n_hash - 1;
+ if (group->hash_map) {
+ free(group->hash_map);
+ }
+ group->hash_map = xcalloc(n_hash, sizeof(struct ofputil_bucket *));
+
+ /* Use Webster method to distribute hash values over buckets. */
+ for (int hash = 0; hash < n_hash; hash++) {
+ struct webster *winner = &webster[0];
+ for (i = 1; i < n_buckets; i++) {
+ if (webster[i].value > winner->value) {
+ winner = &webster[i];
+ }
+ }
+ winner->hits++;
+ winner->divisor += 2;
+ winner->value = (double) winner->bucket->weight / winner->divisor;
+ group->hash_map[hash] = winner->bucket;
+ }
+
+ i = 0;
+ LIST_FOR_EACH (bucket, list_node, &group->up.buckets) {
+ double target = (n_hash * bucket->weight) / (double) total_weight;
+ VLOG_DBG(" Bucket %d: weight=%d, target=%.2f hits=%d",
+ bucket->bucket_id, bucket->weight,
+ target, webster[i].hits);
+ i++;
+ }
+
+ free(webster);
+ return true;
+}
+
+static void
+group_set_selection_method(struct group_dpif *group)
+{
+ const struct ofputil_group_props *props = &group->up.props;
+ const char *selection_method = props->selection_method;
+
+ VLOG_DBG("Constructing select group %"PRIu32, group->up.group_id);
+ if (selection_method[0] == '\0') {
+ VLOG_DBG("No selection method specified. Trying dp_hash.");
+ /* If the controller has not specified a selection method, check if
+ * the dp_hash selection method with max 64 hash values is appropriate
+ * for the given bucket configuration. */
+ if (group_setup_dp_hash_table(group, 64)) {
+ /* Use dp_hash selection method with symmetric L4 hash. */
+ group->selection_method = SEL_METHOD_DP_HASH;
+ group->hash_alg = OVS_HASH_ALG_SYM_L4;
+ group->hash_basis = 0;
+ VLOG_DBG("Use dp_hash with %d hash values using algorithm %d.",
+ group->hash_mask + 1, group->hash_alg);
+ } else {
+ /* Fall back to original default hashing in slow path. */
+ VLOG_DBG("Falling back to default hash method.");
+ group->selection_method = SEL_METHOD_DEFAULT;
+ }
+ } else if (!strcmp(selection_method, "dp_hash")) {
+ VLOG_DBG("Selection method specified: dp_hash.");
+ /* Try to use dp_hash if possible at all. */
+ if (group_setup_dp_hash_table(group, 0)) {
+ group->selection_method = SEL_METHOD_DP_HASH;
+ group->hash_alg = props->selection_method_param >> 32;
+ if (group->hash_alg >= __OVS_HASH_MAX) {
+ VLOG_DBG("Invalid dp_hash algorithm %d. "
+ "Defaulting to OVS_HASH_ALG_L4", group->hash_alg);
+ group->hash_alg = OVS_HASH_ALG_L4;
+ }
+ group->hash_basis = (uint32_t) props->selection_method_param;
+ VLOG_DBG("Use dp_hash with %d hash values using algorithm %d.",
+ group->hash_mask + 1, group->hash_alg);
+ } else {
+ /* Fall back to original default hashing in slow path. */
+ VLOG_DBG("Falling back to default hash method.");
+ group->selection_method = SEL_METHOD_DEFAULT;
+ }
+ } else if (!strcmp(selection_method, "hash")) {
+ VLOG_DBG("Selection method specified: hash.");
+ if (props->fields.values_size > 0) {
+ /* Controller has specified hash fields. */
+ struct ds s = DS_EMPTY_INITIALIZER;
+ oxm_format_field_array(&s, &props->fields);
+ VLOG_DBG("Hash fields: %s", ds_cstr(&s));
+ ds_destroy(&s);
+ group->selection_method = SEL_METHOD_HASH;
+ } else {
+ /* No hash fields. Fall back to original default hashing. */
+ VLOG_DBG("No hash fields. Falling back to default hash method.");
+ group->selection_method = SEL_METHOD_DEFAULT;
+ }
+ } else {
+ /* Parsing of groups should ensure this never happens */
+ OVS_NOT_REACHED();
+ }
+}
+
static enum ofperr
group_construct(struct ofgroup *group_)
{
ovs_mutex_init_adaptive(&group->stats_mutex);
ovs_mutex_lock(&group->stats_mutex);
group_construct_stats(group);
+ group->hash_map = NULL;
+ if (group->up.type == OFPGT11_SELECT) {
+ group_set_selection_method(group);
+ }
ovs_mutex_unlock(&group->stats_mutex);
return 0;
}
{
struct group_dpif *group = group_dpif_cast(group_);
ovs_mutex_destroy(&group->stats_mutex);
-}
-
-static void
-group_modify(struct ofgroup *group_)
-{
- struct ofproto_dpif *ofproto = ofproto_dpif_cast(group_->ofproto);
-
- ofproto->backer->need_revalidate = REV_FLOW_TABLE;
+ if (group->hash_map) {
+ free(group->hash_map);
+ group->hash_map = NULL;
+ }
}
static enum ofperr
group_get_stats(const struct ofgroup *group_, struct ofputil_group_stats *ogs)
{
struct group_dpif *group = group_dpif_cast(group_);
- struct ofputil_bucket *bucket;
- const struct ovs_list *buckets;
- struct bucket_counter *bucket_stats;
ovs_mutex_lock(&group->stats_mutex);
ogs->packet_count = group->packet_count;
ogs->byte_count = group->byte_count;
- buckets = group_dpif_get_buckets(group, NULL);
- bucket_stats = ogs->bucket_stats;
- LIST_FOR_EACH (bucket, list_node, buckets) {
+ struct bucket_counter *bucket_stats = ogs->bucket_stats;
+ struct ofputil_bucket *bucket;
+ LIST_FOR_EACH (bucket, list_node, &group->up.buckets) {
bucket_stats->packet_count = bucket->stats.packet_count;
bucket_stats->byte_count = bucket->stats.byte_count;
bucket_stats++;
/* If the group exists, this function increments the groups's reference count.
*
- * Make sure to call group_dpif_unref() after no longer needing to maintain
+ * Make sure to call ofproto_group_unref() after no longer needing to maintain
* a reference to the group. */
struct group_dpif *
group_dpif_lookup(struct ofproto_dpif *ofproto, uint32_t group_id,
version, take_ref);
return ofgroup ? group_dpif_cast(ofgroup) : NULL;
}
-
-const struct ovs_list *
-group_dpif_get_buckets(const struct group_dpif *group, uint32_t *n_buckets)
-{
- if (n_buckets) {
- *n_buckets = group->up.n_buckets;
- }
- return &group->up.buckets;
-}
-
-enum ofp11_group_type
-group_dpif_get_type(const struct group_dpif *group)
-{
- return group->up.type;
-}
-
-const char *
-group_dpif_get_selection_method(const struct group_dpif *group)
-{
- return group->up.props.selection_method;
-}
\f
/* Sends 'packet' out 'ofport'. If 'port' is a tunnel and that tunnel type
* supports a notion of an OAM flag, sets it if 'oam' is true.
ovs_mutex_unlock(&ofproto->stats_mutex);
return error;
}
-
-uint64_t
-group_dpif_get_selection_method_param(const struct group_dpif *group)
-{
- return group->up.props.selection_method_param;
-}
-
-const struct field_array *
-group_dpif_get_fields(const struct group_dpif *group)
-{
- return &group->up.props.fields;
-}
\f
/* Return the version string of the datapath that backs up
* this 'ofproto'.
return ofproto->backer->dp_version_string;
}
+static void
+type_set_config(const char *type, const struct smap *other_config)
+{
+ struct dpif_backer *backer;
+
+ backer = shash_find_data(&all_dpif_backers, type);
+ if (!backer) {
+ /* This is not necessarily a problem, since backers are only
+ * created on demand. */
+ return;
+ }
+
+ dpif_set_config(backer->dpif, other_config);
+}
+
+static void
+ct_flush(const struct ofproto *ofproto_, const uint16_t *zone)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ ct_dpif_flush(ofproto->backer->dpif, zone, NULL);
+}
+
static bool
set_frag_handling(struct ofproto *ofproto_,
enum ofputil_frag_handling frag_handling)
const struct ofputil_packet_in_private *pin)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct dpif_flow_stats stats;
+ struct xlate_cache xcache;
+ struct flow flow;
+ xlate_cache_init(&xcache);
/* Translate pin into datapath actions. */
uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf odp_actions = OFPBUF_STUB_INITIALIZER(odp_actions_stub);
enum slow_path_reason slow;
- enum ofperr error = xlate_resume(ofproto, pin, &odp_actions, &slow);
+ enum ofperr error = xlate_resume(ofproto, pin, &odp_actions, &slow,
+ &flow, &xcache);
/* Steal 'pin->packet' and put it into a dp_packet. */
struct dp_packet packet;
- dp_packet_init(&packet, pin->public.packet_len);
- dp_packet_put(&packet, pin->public.packet, pin->public.packet_len);
+ dp_packet_init(&packet, pin->base.packet_len);
+ dp_packet_put(&packet, pin->base.packet, pin->base.packet_len);
+
+ /* Run the side effects from the xcache. */
+ dpif_flow_stats_extract(&flow, &packet, time_msec(), &stats);
+ ovs_mutex_lock(&ofproto_mutex);
+ ofproto_dpif_xcache_execute(ofproto, &xcache, &stats);
+ ovs_mutex_unlock(&ofproto_mutex);
- pkt_metadata_from_flow(&packet.md, &pin->public.flow_metadata.flow);
+ pkt_metadata_from_flow(&packet.md, &pin->base.flow_metadata.flow);
/* Fix up in_port. */
ofproto_dpif_set_packet_odp_port(ofproto,
- pin->public.flow_metadata.flow.in_port.ofp_port,
+ pin->base.flow_metadata.flow.in_port.ofp_port,
&packet);
struct flow headers;
dpif_get_netflow_ids(ofproto->backer->dpif, engine_type, engine_id);
}
\f
-static struct ofproto_dpif *
-ofproto_dpif_lookup(const char *name)
+struct ofproto_dpif *
+ofproto_dpif_lookup_by_name(const char *name)
{
struct ofproto_dpif *ofproto;
- HMAP_FOR_EACH_WITH_HASH (ofproto, all_ofproto_dpifs_node,
- hash_string(name, 0), &all_ofproto_dpifs) {
+ HMAP_FOR_EACH_WITH_HASH (ofproto, all_ofproto_dpifs_by_name_node,
+ hash_string(name, 0),
+ &all_ofproto_dpifs_by_name) {
if (!strcmp(ofproto->up.name, name)) {
return ofproto;
}
return NULL;
}
+struct ofproto_dpif *
+ofproto_dpif_lookup_by_uuid(const struct uuid *uuid)
+{
+ struct ofproto_dpif *ofproto;
+
+ HMAP_FOR_EACH_WITH_HASH (ofproto, all_ofproto_dpifs_by_uuid_node,
+ uuid_hash(uuid), &all_ofproto_dpifs_by_uuid) {
+ if (uuid_equals(&ofproto->uuid, uuid)) {
+ return ofproto;
+ }
+ }
+ return NULL;
+}
+
static void
ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, int argc,
const char *argv[], void *aux OVS_UNUSED)
struct ofproto_dpif *ofproto;
if (argc > 1) {
- ofproto = ofproto_dpif_lookup(argv[1]);
+ ofproto = ofproto_dpif_lookup_by_name(argv[1]);
if (!ofproto) {
unixctl_command_reply_error(conn, "no such bridge");
return;
mac_learning_flush(ofproto->ml);
ovs_rwlock_unlock(&ofproto->ml->rwlock);
} else {
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
ovs_rwlock_wrlock(&ofproto->ml->rwlock);
mac_learning_flush(ofproto->ml);
ovs_rwlock_unlock(&ofproto->ml->rwlock);
struct ofproto_dpif *ofproto;
if (argc > 1) {
- ofproto = ofproto_dpif_lookup(argv[1]);
+ ofproto = ofproto_dpif_lookup_by_name(argv[1]);
if (!ofproto) {
unixctl_command_reply_error(conn, "no such bridge");
return;
}
mcast_snooping_mdb_flush(ofproto->ms);
} else {
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
if (!mcast_snooping_enabled(ofproto->ms)) {
continue;
}
const struct ofproto_dpif *ofproto;
const struct mac_entry *e;
- ofproto = ofproto_dpif_lookup(argv[1]);
+ ofproto = ofproto_dpif_lookup_by_name(argv[1]);
if (!ofproto) {
unixctl_command_reply_error(conn, "no such bridge");
return;
ovs_rwlock_rdlock(&ofproto->ml->rwlock);
LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
struct ofbundle *bundle = mac_entry_get_port(ofproto->ml, e);
- char name[OFP_MAX_PORT_NAME_LEN];
+ char name[OFP10_MAX_PORT_NAME_LEN];
ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
- name, sizeof name);
+ NULL, name, sizeof name);
ds_put_format(&ds, "%5s %4d "ETH_ADDR_FMT" %3d\n",
name, e->vlan, ETH_ADDR_ARGS(e->mac),
mac_entry_age(ofproto->ml, e));
}
static void
-ofproto_unixctl_mcast_snooping_show(struct unixctl_conn *conn,
- int argc OVS_UNUSED,
- const char *argv[],
- void *aux OVS_UNUSED)
+ofproto_unixctl_fdb_stats_clear(struct unixctl_conn *conn, int argc,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ struct ofproto_dpif *ofproto;
+
+ if (argc > 1) {
+ ofproto = ofproto_dpif_lookup_by_name(argv[1]);
+ if (!ofproto) {
+ unixctl_command_reply_error(conn, "no such bridge");
+ return;
+ }
+ ovs_rwlock_wrlock(&ofproto->ml->rwlock);
+ mac_learning_clear_statistics(ofproto->ml);
+ ovs_rwlock_unlock(&ofproto->ml->rwlock);
+ } else {
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
+ ovs_rwlock_wrlock(&ofproto->ml->rwlock);
+ mac_learning_clear_statistics(ofproto->ml);
+ ovs_rwlock_unlock(&ofproto->ml->rwlock);
+ }
+ }
+
+ unixctl_command_reply(conn, "statistics successfully cleared");
+}
+
+static void
+ofproto_unixctl_fdb_stats_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[], void *aux OVS_UNUSED)
{
struct ds ds = DS_EMPTY_INITIALIZER;
const struct ofproto_dpif *ofproto;
- const struct ofbundle *bundle;
- const struct mcast_group *grp;
- struct mcast_group_bundle *b;
- struct mcast_mrouter_bundle *mrouter;
-
- ofproto = ofproto_dpif_lookup(argv[1]);
+ ofproto = ofproto_dpif_lookup_by_name(argv[1]);
if (!ofproto) {
unixctl_command_reply_error(conn, "no such bridge");
return;
}
- if (!mcast_snooping_enabled(ofproto->ms)) {
- unixctl_command_reply_error(conn, "multicast snooping is disabled");
- return;
+ ds_put_format(&ds, "Statistics for bridge \"%s\":\n", argv[1]);
+ ovs_rwlock_rdlock(&ofproto->ml->rwlock);
+
+ ds_put_format(&ds, " Current/maximum MAC entries in the table: %"
+ PRIuSIZE"/%"PRIuSIZE"\n",
+ hmap_count(&ofproto->ml->table), ofproto->ml->max_entries);
+ ds_put_format(&ds,
+ " Total number of learned MAC entries : %"PRIu64"\n",
+ ofproto->ml->total_learned);
+ ds_put_format(&ds,
+ " Total number of expired MAC entries : %"PRIu64"\n",
+ ofproto->ml->total_expired);
+ ds_put_format(&ds,
+ " Total number of evicted MAC entries : %"PRIu64"\n",
+ ofproto->ml->total_evicted);
+ ds_put_format(&ds,
+ " Total number of port moved MAC entries : %"PRIu64"\n",
+ ofproto->ml->total_moved);
+
+ ovs_rwlock_unlock(&ofproto->ml->rwlock);
+ unixctl_command_reply(conn, ds_cstr(&ds));
+ ds_destroy(&ds);
+}
+
+static void
+ofproto_unixctl_mcast_snooping_show(struct unixctl_conn *conn,
+ int argc OVS_UNUSED,
+ const char *argv[],
+ void *aux OVS_UNUSED)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ const struct ofproto_dpif *ofproto;
+ const struct ofbundle *bundle;
+ const struct mcast_group *grp;
+ struct mcast_group_bundle *b;
+ struct mcast_mrouter_bundle *mrouter;
+
+ ofproto = ofproto_dpif_lookup_by_name(argv[1]);
+ if (!ofproto) {
+ unixctl_command_reply_error(conn, "no such bridge");
+ return;
+ }
+
+ if (!mcast_snooping_enabled(ofproto->ms)) {
+ unixctl_command_reply_error(conn, "multicast snooping is disabled");
+ return;
}
ds_put_cstr(&ds, " port VLAN GROUP Age\n");
ovs_rwlock_rdlock(&ofproto->ms->rwlock);
LIST_FOR_EACH (grp, group_node, &ofproto->ms->group_lru) {
LIST_FOR_EACH(b, bundle_node, &grp->bundle_lru) {
- char name[OFP_MAX_PORT_NAME_LEN];
+ char name[OFP10_MAX_PORT_NAME_LEN];
bundle = b->port;
ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
- name, sizeof name);
+ NULL, name, sizeof name);
ds_put_format(&ds, "%5s %4d ", name, grp->vlan);
ipv6_format_mapped(&grp->addr, &ds);
ds_put_format(&ds, " %3d\n",
/* ports connected to multicast routers */
LIST_FOR_EACH(mrouter, mrouter_node, &ofproto->ms->mrouter_lru) {
- char name[OFP_MAX_PORT_NAME_LEN];
+ char name[OFP10_MAX_PORT_NAME_LEN];
bundle = mrouter->port;
ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
- name, sizeof name);
+ NULL, name, sizeof name);
ds_put_format(&ds, "%5s %4d querier %3d\n",
name, mrouter->vlan,
mcast_mrouter_age(ofproto->ms, mrouter));
ds_destroy(&ds);
}
-struct trace_ctx {
- struct xlate_out xout;
- struct xlate_in xin;
- const struct flow *key;
- struct flow flow;
- struct ds *result;
- struct flow_wildcards wc;
- struct ofpbuf odp_actions;
-};
-
-static void
-trace_format_rule(struct ds *result, int level, const struct rule_dpif *rule)
+/* Store the current ofprotos in 'ofproto_shash'. Returns a sorted list
+ * of the 'ofproto_shash' nodes. It is the responsibility of the caller
+ * to destroy 'ofproto_shash' and free the returned value. */
+static const struct shash_node **
+get_ofprotos(struct shash *ofproto_shash)
{
- const struct rule_actions *actions;
- ovs_be64 cookie;
+ const struct ofproto_dpif *ofproto;
- ds_put_char_multiple(result, '\t', level);
- if (!rule) {
- ds_put_cstr(result, "No match\n");
- return;
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+ &all_ofproto_dpifs_by_name) {
+ char *name = xasprintf("%s@%s", ofproto->up.type, ofproto->up.name);
+ shash_add_nocopy(ofproto_shash, name, ofproto);
}
- ovs_mutex_lock(&rule->up.mutex);
- cookie = rule->up.flow_cookie;
- ovs_mutex_unlock(&rule->up.mutex);
-
- ds_put_format(result, "Rule: table=%"PRIu8" cookie=%#"PRIx64" ",
- rule ? rule->up.table_id : 0, ntohll(cookie));
- cls_rule_format(&rule->up.cr, result);
- ds_put_char(result, '\n');
-
- actions = rule_dpif_get_actions(rule);
-
- ds_put_char_multiple(result, '\t', level);
- ds_put_cstr(result, "OpenFlow actions=");
- ofpacts_format(actions->ofpacts, actions->ofpacts_len, result);
- ds_put_char(result, '\n');
-}
-
-static void
-trace_format_flow(struct ds *result, int level, const char *title,
- struct trace_ctx *trace)
-{
- ds_put_char_multiple(result, '\t', level);
- ds_put_format(result, "%s: ", title);
- /* Do not report unchanged flows for resubmits. */
- if ((level > 0 && flow_equal(&trace->xin.flow, &trace->flow))
- || (level == 0 && flow_equal(&trace->xin.flow, trace->key))) {
- ds_put_cstr(result, "unchanged");
- } else {
- flow_format(result, &trace->xin.flow);
- trace->flow = trace->xin.flow;
- }
- ds_put_char(result, '\n');
+ return shash_sort(ofproto_shash);
}
static void
-trace_format_regs(struct ds *result, int level, const char *title,
- struct trace_ctx *trace)
+ofproto_unixctl_dpif_dump_dps(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED,
+ void *aux OVS_UNUSED)
{
- size_t i;
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ struct shash ofproto_shash;
+ const struct shash_node **sorted_ofprotos;
+ int i;
- ds_put_char_multiple(result, '\t', level);
- ds_put_format(result, "%s:", title);
- for (i = 0; i < FLOW_N_REGS; i++) {
- ds_put_format(result, " reg%"PRIuSIZE"=0x%"PRIx32, i, trace->flow.regs[i]);
+ shash_init(&ofproto_shash);
+ sorted_ofprotos = get_ofprotos(&ofproto_shash);
+ for (i = 0; i < shash_count(&ofproto_shash); i++) {
+ const struct shash_node *node = sorted_ofprotos[i];
+ ds_put_format(&ds, "%s\n", node->name);
}
- ds_put_char(result, '\n');
-}
-
-static void
-trace_format_odp(struct ds *result, int level, const char *title,
- struct trace_ctx *trace)
-{
- struct ofpbuf *odp_actions = &trace->odp_actions;
- ds_put_char_multiple(result, '\t', level);
- ds_put_format(result, "%s: ", title);
- format_odp_actions(result, odp_actions->data, odp_actions->size);
- ds_put_char(result, '\n');
-}
-
-static void
-trace_format_megaflow(struct ds *result, int level, const char *title,
- struct trace_ctx *trace)
-{
- struct match match;
-
- ds_put_char_multiple(result, '\t', level);
- ds_put_format(result, "%s: ", title);
- match_init(&match, trace->key, &trace->wc);
- match_format(&match, result, OFP_DEFAULT_PRIORITY);
- ds_put_char(result, '\n');
-}
-
-static void trace_report(struct xlate_in *, int indentation,
- const char *format, ...)
- OVS_PRINTF_FORMAT(3, 4);
-static void trace_report_valist(struct xlate_in *, int indentation,
- const char *format, va_list args)
- OVS_PRINTF_FORMAT(3, 0);
-
-static void
-trace_resubmit(struct xlate_in *xin, struct rule_dpif *rule, int indentation)
-{
- struct trace_ctx *trace = CONTAINER_OF(xin, struct trace_ctx, xin);
- struct ds *result = trace->result;
-
- if (!indentation) {
- if (rule == xin->ofproto->miss_rule) {
- trace_report(xin, indentation,
- "No match, flow generates \"packet in\"s.");
- } else if (rule == xin->ofproto->no_packet_in_rule) {
- trace_report(xin, indentation, "No match, packets dropped because "
- "OFPPC_NO_PACKET_IN is set on in_port.");
- } else if (rule == xin->ofproto->drop_frags_rule) {
- trace_report(xin, indentation,
- "Packets dropped because they are IP fragments and "
- "the fragment handling mode is \"drop\".");
- }
- }
+ shash_destroy(&ofproto_shash);
+ free(sorted_ofprotos);
- ds_put_char(result, '\n');
- if (indentation) {
- trace_format_flow(result, indentation, "Resubmitted flow", trace);
- trace_format_regs(result, indentation, "Resubmitted regs", trace);
- trace_format_odp(result, indentation, "Resubmitted odp", trace);
- trace_format_megaflow(result, indentation, "Resubmitted megaflow",
- trace);
- }
- trace_format_rule(result, indentation, rule);
+ unixctl_command_reply(conn, ds_cstr(&ds));
+ ds_destroy(&ds);
}
static void
-trace_report_valist(struct xlate_in *xin, int indentation,
- const char *format, va_list args)
+show_dp_feature_bool(struct ds *ds, const char *feature, bool b)
{
- struct trace_ctx *trace = CONTAINER_OF(xin, struct trace_ctx, xin);
- struct ds *result = trace->result;
-
- ds_put_char_multiple(result, '\t', indentation);
- ds_put_format_valist(result, format, args);
- ds_put_char(result, '\n');
+ ds_put_format(ds, "%s: %s\n", feature, b ? "Yes" : "No");
}
static void
-trace_report(struct xlate_in *xin, int indentation, const char *format, ...)
+show_dp_feature_size_t(struct ds *ds, const char *feature, size_t s)
{
- va_list args;
-
- va_start(args, format);
- trace_report_valist(xin, indentation, format, args);
- va_end(args);
+ ds_put_format(ds, "%s: %"PRIuSIZE"\n", feature, s);
}
-/* Parses the 'argc' elements of 'argv', ignoring argv[0]. The following
- * forms are supported:
- *
- * - [dpname] odp_flow [-generate | packet]
- * - bridge br_flow [-generate | packet]
- *
- * On success, initializes '*ofprotop' and 'flow' and returns NULL. On failure
- * returns a nonnull malloced error message. */
-static char * OVS_WARN_UNUSED_RESULT
-parse_flow_and_packet(int argc, const char *argv[],
- struct ofproto_dpif **ofprotop, struct flow *flow,
- struct dp_packet **packetp)
-{
- const struct dpif_backer *backer = NULL;
- const char *error = NULL;
- char *m_err = NULL;
- struct simap port_names = SIMAP_INITIALIZER(&port_names);
- struct dp_packet *packet;
- struct ofpbuf odp_key;
- struct ofpbuf odp_mask;
-
- ofpbuf_init(&odp_key, 0);
- ofpbuf_init(&odp_mask, 0);
-
- /* Handle "-generate" or a hex string as the last argument. */
- if (!strcmp(argv[argc - 1], "-generate")) {
- packet = dp_packet_new(0);
- argc--;
- } else {
- error = eth_from_hex(argv[argc - 1], &packet);
- if (!error) {
- argc--;
- } else if (argc == 4) {
- /* The 3-argument form must end in "-generate' or a hex string. */
- goto exit;
- }
- error = NULL;
- }
-
- /* odp_flow can have its in_port specified as a name instead of port no.
- * We do not yet know whether a given flow is a odp_flow or a br_flow.
- * But, to know whether a flow is odp_flow through odp_flow_from_string(),
- * we need to create a simap of name to port no. */
- if (argc == 3) {
- const char *dp_type;
- if (!strncmp(argv[1], "ovs-", 4)) {
- dp_type = argv[1] + 4;
- } else {
- dp_type = argv[1];
- }
- backer = shash_find_data(&all_dpif_backers, dp_type);
- } else if (argc == 2) {
- struct shash_node *node;
- if (shash_count(&all_dpif_backers) == 1) {
- node = shash_first(&all_dpif_backers);
- backer = node->data;
- }
- } else {
- error = "Syntax error";
- goto exit;
- }
- if (backer && backer->dpif) {
- struct dpif_port dpif_port;
- struct dpif_port_dump port_dump;
- DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, backer->dpif) {
- simap_put(&port_names, dpif_port.name,
- odp_to_u32(dpif_port.port_no));
- }
- }
-
- /* Parse the flow and determine whether a datapath or
- * bridge is specified. If function odp_flow_key_from_string()
- * returns 0, the flow is a odp_flow. If function
- * parse_ofp_exact_flow() returns NULL, the flow is a br_flow. */
- if (!odp_flow_from_string(argv[argc - 1], &port_names,
- &odp_key, &odp_mask)) {
- if (!backer) {
- error = "Cannot find the datapath";
- goto exit;
- }
-
- if (odp_flow_key_to_flow(odp_key.data, odp_key.size, flow) == ODP_FIT_ERROR) {
- error = "Failed to parse datapath flow key";
- goto exit;
- }
-
- *ofprotop = xlate_lookup_ofproto(backer, flow,
- &flow->in_port.ofp_port);
- if (*ofprotop == NULL) {
- error = "Invalid datapath flow";
- goto exit;
- }
- } else {
- char *err = parse_ofp_exact_flow(flow, NULL, argv[argc - 1], NULL);
-
- if (err) {
- m_err = xasprintf("Bad openflow flow syntax: %s", err);
- free(err);
- goto exit;
- } else {
- if (argc != 3) {
- error = "Must specify bridge name";
- goto exit;
- }
-
- *ofprotop = ofproto_dpif_lookup(argv[1]);
- if (!*ofprotop) {
- error = "Unknown bridge name";
- goto exit;
- }
- }
- }
+enum dpif_support_field_type {
+ DPIF_SUPPORT_FIELD_bool,
+ DPIF_SUPPORT_FIELD_size_t,
+};
- /* Generate a packet, if requested. */
- if (packet) {
- if (!dp_packet_size(packet)) {
- flow_compose(packet, flow);
- } else {
- /* Use the metadata from the flow and the packet argument
- * to reconstruct the flow. */
- pkt_metadata_from_flow(&packet->md, flow);
- flow_extract(packet, flow);
- }
- }
+struct dpif_support_field {
+ void *rt_ptr; /* Points to the 'rt_support' field. */
+ const void *bt_ptr; /* Points to the 'bt_support' field. */
+ const char *title;
+ enum dpif_support_field_type type;
+};
-exit:
- if (error && !m_err) {
- m_err = xstrdup(error);
- }
- if (m_err) {
- dp_packet_delete(packet);
- packet = NULL;
- }
- *packetp = packet;
- ofpbuf_uninit(&odp_key);
- ofpbuf_uninit(&odp_mask);
- simap_destroy(&port_names);
- return m_err;
-}
+#define DPIF_SUPPORT_FIELD_INTIALIZER(RT_PTR, BT_PTR, TITLE, TYPE) \
+ (struct dpif_support_field) {RT_PTR, BT_PTR, TITLE, TYPE}
static void
-ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
- void *aux OVS_UNUSED)
+dpif_show_support(const struct dpif_backer_support *support, struct ds *ds)
{
- struct ofproto_dpif *ofproto;
- struct dp_packet *packet;
- char *error;
- struct flow flow;
-
- error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
- if (!error) {
- struct ds result;
+#define DPIF_SUPPORT_FIELD(TYPE, NAME, TITLE) \
+ show_dp_feature_##TYPE (ds, TITLE, support->NAME);
+ DPIF_SUPPORT_FIELDS
+#undef DPIF_SUPPORT_FIELD
- ds_init(&result);
- ofproto_trace(ofproto, &flow, packet, NULL, 0, &result);
- unixctl_command_reply(conn, ds_cstr(&result));
- ds_destroy(&result);
- dp_packet_delete(packet);
- } else {
- unixctl_command_reply_error(conn, error);
- free(error);
- }
+#define ODP_SUPPORT_FIELD(TYPE, NAME, TITLE) \
+ show_dp_feature_##TYPE (ds, TITLE, support->odp.NAME );
+ ODP_SUPPORT_FIELDS
+#undef ODP_SUPPORT_FIELD
}
static void
-ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
- const char *argv[], void *aux OVS_UNUSED)
-{
- enum ofputil_protocol usable_protocols;
- struct ofproto_dpif *ofproto;
- bool enforce_consistency;
- struct ofpbuf ofpacts;
- struct dp_packet *packet;
- struct ds result;
- struct flow flow;
- uint16_t in_port;
-
- /* Three kinds of error return values! */
- enum ofperr retval;
- char *error;
-
- packet = NULL;
- ds_init(&result);
- ofpbuf_init(&ofpacts, 0);
-
- /* Parse actions. */
- error = ofpacts_parse_actions(argv[--argc], &ofpacts, &usable_protocols);
- if (error) {
- unixctl_command_reply_error(conn, error);
- free(error);
- goto exit;
- }
-
- /* OpenFlow 1.1 and later suggest that the switch enforces certain forms of
- * consistency between the flow and the actions. With -consistent, we
- * enforce consistency even for a flow supported in OpenFlow 1.0. */
- if (!strcmp(argv[1], "-consistent")) {
- enforce_consistency = true;
- argv++;
- argc--;
- } else {
- enforce_consistency = false;
- }
-
- error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
- if (error) {
- unixctl_command_reply_error(conn, error);
- free(error);
- goto exit;
- }
-
- /* Do the same checks as handle_packet_out() in ofproto.c.
- *
- * We pass a 'table_id' of 0 to ofpacts_check(), which isn't
- * strictly correct because these actions aren't in any table, but it's OK
- * because it 'table_id' is used only to check goto_table instructions, but
- * packet-outs take a list of actions and therefore it can't include
- * instructions.
- *
- * We skip the "meter" check here because meter is an instruction, not an
- * action, and thus cannot appear in ofpacts. */
- in_port = ofp_to_u16(flow.in_port.ofp_port);
- if (in_port >= ofproto->up.max_ports && in_port < ofp_to_u16(OFPP_MAX)) {
- unixctl_command_reply_error(conn, "invalid in_port");
- goto exit;
- }
- if (enforce_consistency) {
- retval = ofpacts_check_consistency(ofpacts.data, ofpacts.size, &flow,
- u16_to_ofp(ofproto->up.max_ports),
- 0, ofproto->up.n_tables,
- usable_protocols);
- } else {
- retval = ofpacts_check(ofpacts.data, ofpacts.size, &flow,
- u16_to_ofp(ofproto->up.max_ports), 0,
- ofproto->up.n_tables, &usable_protocols);
- }
- if (!retval) {
- retval = ofproto_check_ofpacts(&ofproto->up, ofpacts.data,
- ofpacts.size);
+display_support_field(const char *name,
+ const struct dpif_support_field *field,
+ struct ds *ds)
+{
+ switch (field->type) {
+ case DPIF_SUPPORT_FIELD_bool: {
+ bool v = *(bool *)field->rt_ptr;
+ bool b = *(bool *)field->bt_ptr;
+ ds_put_format(ds, "%s (%s) : [run time]:%s, [boot time]:%s\n", name,
+ field->title, v ? "true" : "false",
+ b ? "true" : "false");
+ break;
}
-
- if (retval) {
- ds_clear(&result);
- ds_put_format(&result, "Bad actions: %s", ofperr_to_string(retval));
- unixctl_command_reply_error(conn, ds_cstr(&result));
- goto exit;
+ case DPIF_SUPPORT_FIELD_size_t:
+ ds_put_format(ds, "%s (%s) : [run time]:%"PRIuSIZE
+ ", [boot time]:%"PRIuSIZE"\n", name,
+ field->title, *(size_t *)field->rt_ptr,
+ *(size_t *)field->bt_ptr);
+ break;
+ default:
+ OVS_NOT_REACHED();
}
-
- ofproto_trace(ofproto, &flow, packet,
- ofpacts.data, ofpacts.size, &result);
- unixctl_command_reply(conn, ds_cstr(&result));
-
-exit:
- ds_destroy(&result);
- dp_packet_delete(packet);
- ofpbuf_uninit(&ofpacts);
}
-/* Implements a "trace" through 'ofproto''s flow table, appending a textual
- * description of the results to 'ds'.
- *
- * The trace follows a packet with the specified 'flow' through the flow
- * table. 'packet' may be nonnull to trace an actual packet, with consequent
- * side effects (if it is nonnull then its flow must be 'flow').
+/* Set a field of 'rt_support' to a new value.
*
- * If 'ofpacts' is nonnull then its 'ofpacts_len' bytes specify the actions to
- * trace, otherwise the actions are determined by a flow table lookup. */
-static void
-ofproto_trace(struct ofproto_dpif *ofproto, struct flow *flow,
- const struct dp_packet *packet,
- const struct ofpact ofpacts[], size_t ofpacts_len,
- struct ds *ds)
+ * Returns 'true' if the value is actually set. */
+static bool
+dpif_set_support(struct dpif_backer_support *rt_support,
+ struct dpif_backer_support *bt_support,
+ const char *name, const char *value, struct ds *ds)
{
- struct trace_ctx trace;
- enum xlate_error error;
-
- ds_put_format(ds, "Bridge: %s\n", ofproto->up.name);
- ds_put_cstr(ds, "Flow: ");
- flow_format(ds, flow);
- ds_put_char(ds, '\n');
-
- ofpbuf_init(&trace.odp_actions, 0);
-
- trace.result = ds;
- trace.key = flow; /* Original flow key, used for megaflow. */
- trace.flow = *flow; /* May be modified by actions. */
- xlate_in_init(&trace.xin, ofproto,
- ofproto_dpif_get_tables_version(ofproto), flow,
- flow->in_port.ofp_port, NULL, ntohs(flow->tcp_flags),
- packet, &trace.wc, &trace.odp_actions);
- trace.xin.ofpacts = ofpacts;
- trace.xin.ofpacts_len = ofpacts_len;
- trace.xin.resubmit_hook = trace_resubmit;
- trace.xin.report_hook = trace_report_valist;
-
- error = xlate_actions(&trace.xin, &trace.xout);
- ds_put_char(ds, '\n');
- trace.xin.flow.actset_output = 0;
- trace_format_flow(ds, 0, "Final flow", &trace);
- trace_format_megaflow(ds, 0, "Megaflow", &trace);
-
- ds_put_cstr(ds, "Datapath actions: ");
- format_odp_actions(ds, trace.odp_actions.data, trace.odp_actions.size);
-
- if (error != XLATE_OK) {
- ds_put_format(ds, "\nTranslation failed (%s), packet is dropped.\n",
- xlate_strerror(error));
- } else if (trace.xout.slow) {
- enum slow_path_reason slow;
-
- ds_put_cstr(ds, "\nThis flow is handled by the userspace "
- "slow path because it:");
+ struct shash all_fields = SHASH_INITIALIZER(&all_fields);
+ struct dpif_support_field *field;
+ struct shash_node *node;
+ bool changed = false;
- slow = trace.xout.slow;
- while (slow) {
- enum slow_path_reason bit = rightmost_1bit(slow);
+#define DPIF_SUPPORT_FIELD(TYPE, NAME, TITLE) \
+ {\
+ struct dpif_support_field *f = xmalloc(sizeof *f); \
+ *f = DPIF_SUPPORT_FIELD_INTIALIZER(&rt_support->NAME, \
+ &bt_support->NAME, \
+ TITLE, \
+ DPIF_SUPPORT_FIELD_##TYPE);\
+ shash_add_once(&all_fields, #NAME, f); \
+ }
+ DPIF_SUPPORT_FIELDS;
+#undef DPIF_SUPPORT_FIELD
- ds_put_format(ds, "\n\t- %s.",
- slow_path_reason_to_explanation(bit));
+#define ODP_SUPPORT_FIELD(TYPE, NAME, TITLE) \
+ {\
+ struct dpif_support_field *f = xmalloc(sizeof *f); \
+ *f = DPIF_SUPPORT_FIELD_INTIALIZER(&rt_support->odp.NAME, \
+ &bt_support->odp.NAME, \
+ TITLE, \
+ DPIF_SUPPORT_FIELD_##TYPE);\
+ shash_add_once(&all_fields, #NAME, f); \
+ }
+ ODP_SUPPORT_FIELDS;
+#undef ODP_SUPPORT_FIELD
- slow &= ~bit;
+ if (!name) {
+ SHASH_FOR_EACH (node, &all_fields) {
+ display_support_field(node->name, node->data, ds);
}
+ goto done;
}
- xlate_out_uninit(&trace.xout);
- ofpbuf_uninit(&trace.odp_actions);
-}
-
-/* Store the current ofprotos in 'ofproto_shash'. Returns a sorted list
- * of the 'ofproto_shash' nodes. It is the responsibility of the caller
- * to destroy 'ofproto_shash' and free the returned value. */
-static const struct shash_node **
-get_ofprotos(struct shash *ofproto_shash)
-{
- const struct ofproto_dpif *ofproto;
-
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
- char *name = xasprintf("%s@%s", ofproto->up.type, ofproto->up.name);
- shash_add_nocopy(ofproto_shash, name, ofproto);
+ node = shash_find(&all_fields, name);
+ if (!node) {
+ ds_put_cstr(ds, "Unexpected support field");
+ goto done;
}
+ field = node->data;
- return shash_sort(ofproto_shash);
-}
-
-static void
-ofproto_unixctl_dpif_dump_dps(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED,
- void *aux OVS_UNUSED)
-{
- struct ds ds = DS_EMPTY_INITIALIZER;
- struct shash ofproto_shash;
- const struct shash_node **sorted_ofprotos;
- int i;
-
- shash_init(&ofproto_shash);
- sorted_ofprotos = get_ofprotos(&ofproto_shash);
- for (i = 0; i < shash_count(&ofproto_shash); i++) {
- const struct shash_node *node = sorted_ofprotos[i];
- ds_put_format(&ds, "%s\n", node->name);
+ if (!value) {
+ display_support_field(node->name, field, ds);
+ goto done;
}
- shash_destroy(&ofproto_shash);
- free(sorted_ofprotos);
+ if (field->type == DPIF_SUPPORT_FIELD_bool) {
+ if (!strcasecmp(value, "true")) {
+ if (*(bool *)field->bt_ptr) {
+ *(bool *)field->rt_ptr = true;
+ changed = true;
+ } else {
+ ds_put_cstr(ds, "Can not enable features not supported by the datapth");
+ }
+ } else if (!strcasecmp(value, "false")) {
+ *(bool *)field->rt_ptr = false;
+ changed = true;
+ } else {
+ ds_put_cstr(ds, "Boolean value expected");
+ }
+ } else if (field->type == DPIF_SUPPORT_FIELD_size_t) {
+ int v;
+ if (str_to_int(value, 10, &v)) {
+ if (v >= 0) {
+ if (v <= *(size_t *)field->bt_ptr) {
+ *(size_t *)field->rt_ptr = v;
+ changed = true;
+ } else {
+ ds_put_cstr(ds, "Can not set value beyond the datapath capability");
+ }
+ } else {
+ ds_put_format(ds, "Negative number not expected");
+ }
+ } else {
+ ds_put_cstr(ds, "Integer number expected");
+ }
+ }
- unixctl_command_reply(conn, ds_cstr(&ds));
- ds_destroy(&ds);
+done:
+ shash_destroy_free_data(&all_fields);
+ return changed;
}
static void
size_t i;
dpif_get_dp_stats(backer->dpif, &dp_stats);
-
ds_put_format(ds, "%s: hit:%"PRIu64" missed:%"PRIu64"\n",
dpif_name(backer->dpif), dp_stats.n_hit, dp_stats.n_missed);
continue;
}
- ds_put_format(ds, "\t%s:\n", ofproto->up.name);
+ ds_put_format(ds, " %s:\n", ofproto->up.name);
ports = shash_sort(&ofproto->up.port_by_name);
for (j = 0; j < shash_count(&ofproto->up.port_by_name); j++) {
struct smap config;
odp_port_t odp_port;
- ds_put_format(ds, "\t\t%s %u/", netdev_get_name(ofport->netdev),
+ ds_put_format(ds, " %s %u/", netdev_get_name(ofport->netdev),
ofport->ofp_port);
odp_port = ofp_port_to_odp_port(ofproto, ofport->ofp_port);
smap_init(&config);
if (!netdev_get_config(ofport->netdev, &config)) {
- const struct smap_node **nodes;
- size_t i;
-
- nodes = smap_sort(&config);
- for (i = 0; i < smap_count(&config); i++) {
- const struct smap_node *node = nodes[i];
- ds_put_format(ds, "%c %s=%s", i ? ',' : ':',
- node->key, node->value);
+ const struct smap_node **nodes = smap_sort(&config);
+ for (size_t k = 0; k < smap_count(&config); k++) {
+ ds_put_format(ds, "%c %s=%s", k ? ',' : ':',
+ nodes[k]->key, nodes[k]->value);
}
free(nodes);
}
const struct ofproto_dpif *ofproto;
struct ds ds = DS_EMPTY_INITIALIZER;
- bool verbosity = false;
-
- struct dpif_port dpif_port;
- struct dpif_port_dump port_dump;
- struct hmap portno_names;
struct dpif_flow_dump *flow_dump;
struct dpif_flow_dump_thread *flow_dump_thread;
struct dpif_flow f;
int error;
- ofproto = ofproto_dpif_lookup(argv[argc - 1]);
+ ofproto = ofproto_dpif_lookup_by_name(argv[argc - 1]);
if (!ofproto) {
unixctl_command_reply_error(conn, "no such bridge");
return;
}
- if (argc > 2 && !strcmp(argv[1], "-m")) {
- verbosity = true;
+ bool verbosity = false;
+ bool names = false;
+ bool set_names = false;
+ for (int i = 1; i < argc - 1; i++) {
+ if (!strcmp(argv[i], "-m")) {
+ verbosity = true;
+ } else if (!strcmp(argv[i], "--names")) {
+ names = true;
+ set_names = true;
+ } else if (!strcmp(argv[i], "--no-names")) {
+ names = false;
+ set_names = true;
+ }
}
+ if (!set_names) {
+ names = verbosity;
+ }
+
+ struct hmap *portno_names = NULL;
+ if (names) {
+ portno_names = xmalloc(sizeof *portno_names);
+ hmap_init(portno_names);
- hmap_init(&portno_names);
- DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, ofproto->backer->dpif) {
- odp_portno_names_set(&portno_names, dpif_port.port_no, dpif_port.name);
+ struct dpif_port dpif_port;
+ struct dpif_port_dump port_dump;
+ DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, ofproto->backer->dpif) {
+ odp_portno_names_set(portno_names, dpif_port.port_no,
+ dpif_port.name);
+ }
}
ds_init(&ds);
- flow_dump = dpif_flow_dump_create(ofproto->backer->dpif, false);
+ flow_dump = dpif_flow_dump_create(ofproto->backer->dpif, false, NULL);
flow_dump_thread = dpif_flow_dump_thread_create(flow_dump);
while (dpif_flow_dump_next(flow_dump_thread, &f, 1)) {
struct flow flow;
ds_put_cstr(&ds, " ");
}
odp_flow_format(f.key, f.key_len, f.mask, f.mask_len,
- &portno_names, &ds, verbosity);
+ portno_names, &ds, verbosity);
ds_put_cstr(&ds, ", ");
dpif_flow_stats_format(&f.stats, &ds);
ds_put_cstr(&ds, ", actions:");
- format_odp_actions(&ds, f.actions, f.actions_len);
+ format_odp_actions(&ds, f.actions, f.actions_len, portno_names);
ds_put_char(&ds, '\n');
}
dpif_flow_dump_thread_destroy(flow_dump_thread);
} else {
unixctl_command_reply(conn, ds_cstr(&ds));
}
- odp_portno_names_destroy(&portno_names);
- hmap_destroy(&portno_names);
+ if (portno_names) {
+ odp_portno_names_destroy(portno_names);
+ hmap_destroy(portno_names);
+ free(portno_names);
+ }
ds_destroy(&ds);
}
static void
-ofproto_revalidate_all_backers(void)
+ofproto_unixctl_dpif_show_dp_features(struct unixctl_conn *conn,
+ int argc, const char *argv[],
+ void *aux OVS_UNUSED)
{
- const struct shash_node **backers;
- int i;
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ const char *br = argv[argc -1];
+ struct ofproto_dpif *ofproto = ofproto_dpif_lookup_by_name(br);
- backers = shash_sort(&all_dpif_backers);
- for (i = 0; i < shash_count(&all_dpif_backers); i++) {
- struct dpif_backer *backer = backers[i]->data;
- backer->need_revalidate = REV_RECONFIGURE;
+ if (!ofproto) {
+ unixctl_command_reply_error(conn, "no such bridge");
+ return;
}
- free(backers);
-}
-static void
-disable_tnl_push_pop(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED,
- const char *argv[], void *aux OVS_UNUSED)
-{
- if (!strcasecmp(argv[1], "off")) {
- ofproto_use_tnl_push_pop = false;
- unixctl_command_reply(conn, "Tunnel push-pop off");
- ofproto_revalidate_all_backers();
- } else if (!strcasecmp(argv[1], "on")) {
- ofproto_use_tnl_push_pop = true;
- unixctl_command_reply(conn, "Tunnel push-pop on");
- ofproto_revalidate_all_backers();
- } else {
- unixctl_command_reply_error(conn, "Invalid argument");
- }
+ dpif_show_support(&ofproto->backer->bt_support, &ds);
+ unixctl_command_reply(conn, ds_cstr(&ds));
}
static void
-disable_datapath_truncate(struct unixctl_conn *conn OVS_UNUSED,
- int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED,
- void *aux OVS_UNUSED)
+ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
+ int argc, const char *argv[],
+ void *aux OVS_UNUSED)
{
- const struct shash_node **backers;
- int i;
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ const char *br = argv[1];
+ const char *name, *value;
+ struct ofproto_dpif *ofproto = ofproto_dpif_lookup_by_name(br);
+ bool changed;
- backers = shash_sort(&all_dpif_backers);
- for (i = 0; i < shash_count(&all_dpif_backers); i++) {
- struct dpif_backer *backer = backers[i]->data;
- backer->support.trunc = false;
+ if (!ofproto) {
+ unixctl_command_reply_error(conn, "no such bridge");
+ return;
}
- free(backers);
- unixctl_command_reply(conn, "Datapath truncate action diabled");
+
+ name = argc > 2 ? argv[2] : NULL;
+ value = argc > 3 ? argv[3] : NULL;
+ changed = dpif_set_support(&ofproto->backer->rt_support,
+ &ofproto->backer->bt_support,
+ name, value, &ds);
+ if (changed) {
+ xlate_set_support(ofproto, &ofproto->backer->rt_support);
+ udpif_flush(ofproto->backer->udpif);
+ }
+ unixctl_command_reply(conn, ds_cstr(&ds));
+ ds_destroy(&ds);
}
static void
}
registered = true;
- unixctl_command_register(
- "ofproto/trace",
- "{[dp_name] odp_flow | bridge br_flow} [-generate|packet]",
- 1, 3, ofproto_unixctl_trace, NULL);
- unixctl_command_register(
- "ofproto/trace-packet-out",
- "[-consistent] {[dp_name] odp_flow | bridge br_flow} [-generate|packet] actions",
- 2, 6, ofproto_unixctl_trace_actions, NULL);
unixctl_command_register("fdb/flush", "[bridge]", 0, 1,
ofproto_unixctl_fdb_flush, NULL);
unixctl_command_register("fdb/show", "bridge", 1, 1,
ofproto_unixctl_fdb_show, NULL);
+ unixctl_command_register("fdb/stats-clear", "[bridge]", 0, 1,
+ ofproto_unixctl_fdb_stats_clear, NULL);
+ unixctl_command_register("fdb/stats-show", "bridge", 1, 1,
+ ofproto_unixctl_fdb_stats_show, NULL);
unixctl_command_register("mdb/flush", "[bridge]", 0, 1,
ofproto_unixctl_mcast_snooping_flush, NULL);
unixctl_command_register("mdb/show", "bridge", 1, 1,
ofproto_unixctl_dpif_dump_dps, NULL);
unixctl_command_register("dpif/show", "", 0, 0, ofproto_unixctl_dpif_show,
NULL);
- unixctl_command_register("dpif/dump-flows", "[-m] bridge", 1, 2,
+ unixctl_command_register("dpif/show-dp-features", "bridge", 1, 1,
+ ofproto_unixctl_dpif_show_dp_features, NULL);
+ unixctl_command_register("dpif/dump-flows",
+ "[-m] [--names | --no-names] bridge", 1, INT_MAX,
ofproto_unixctl_dpif_dump_flows, NULL);
-
- unixctl_command_register("ofproto/tnl-push-pop", "[on]|[off]", 1, 1,
- disable_tnl_push_pop, NULL);
-
- unixctl_command_register("dpif/disable-truncate", "", 0, 0,
- disable_datapath_truncate, NULL);
-}
-
-/* Returns true if 'table' is the table used for internal rules,
- * false otherwise. */
-bool
-table_is_internal(uint8_t table_id)
-{
- return table_id == TBL_INTERNAL;
+ unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
+ ofproto_unixctl_dpif_set_dp_features, NULL);
}
\f
-
static odp_port_t
ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
{
}
}
+/* 'match' is non-const to allow for temporary modifications. Any changes are
+ * restored before returning. */
int
ofproto_dpif_add_internal_flow(struct ofproto_dpif *ofproto,
- const struct match *match, int priority,
+ struct match *match, int priority,
uint16_t idle_timeout,
const struct ofpbuf *ofpacts,
struct rule **rulep)
fm = (struct ofputil_flow_mod) {
.buffer_id = UINT32_MAX,
- .match = *match,
.priority = priority,
.table_id = TBL_INTERNAL,
.command = OFPFC_ADD,
.ofpacts = ofpacts->data,
.ofpacts_len = ofpacts->size,
};
-
+ minimatch_init(&fm.match, match);
error = ofproto_flow_mod(&ofproto->up, &fm);
+ minimatch_destroy(&fm.match);
+
if (error) {
VLOG_ERR_RL(&rl, "failed to add internal flow (%s)",
ofperr_to_string(error));
rule = rule_dpif_lookup_in_table(ofproto,
ofproto_dpif_get_tables_version(ofproto),
- TBL_INTERNAL, &fm.match.flow,
- &fm.match.wc);
+ TBL_INTERNAL, &match->flow, &match->wc);
if (rule) {
*rulep = &rule->up;
} else {
fm = (struct ofputil_flow_mod) {
.buffer_id = UINT32_MAX,
- .match = *match,
.priority = priority,
.table_id = TBL_INTERNAL,
+ .out_port = OFPP_ANY,
+ .out_group = OFPG_ANY,
.flags = OFPUTIL_FF_HIDDEN_FIELDS | OFPUTIL_FF_NO_READONLY,
.command = OFPFC_DELETE_STRICT,
};
-
+ minimatch_init(&fm.match, match);
error = ofproto_flow_mod(&ofproto->up, &fm);
+ minimatch_destroy(&fm.match);
+
if (error) {
VLOG_ERR_RL(&rl, "failed to delete internal flow (%s)",
ofperr_to_string(error));
return 0;
}
-const struct uuid *
-ofproto_dpif_get_uuid(const struct ofproto_dpif *ofproto)
+static void
+meter_get_features(const struct ofproto *ofproto_,
+ struct ofputil_meter_features *features)
+{
+ const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ dpif_meter_get_features(ofproto->backer->dpif, features);
+}
+
+static enum ofperr
+meter_set(struct ofproto *ofproto_, ofproto_meter_id *meter_id,
+ struct ofputil_meter_config *config)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ /* Provider ID unknown. Use backer to allocate a new DP meter */
+ if (meter_id->uint32 == UINT32_MAX) {
+ if (!ofproto->backer->meter_ids) {
+ return EFBIG; /* Datapath does not support meter. */
+ }
+
+ if(!id_pool_alloc_id(ofproto->backer->meter_ids, &meter_id->uint32)) {
+ return ENOMEM; /* Can't allocate a DP meter. */
+ }
+ }
+
+ switch (dpif_meter_set(ofproto->backer->dpif, *meter_id, config)) {
+ case 0:
+ return 0;
+ case EFBIG: /* meter_id out of range */
+ case ENOMEM: /* Cannot allocate meter */
+ return OFPERR_OFPMMFC_OUT_OF_METERS;
+ case EBADF: /* Unsupported flags */
+ return OFPERR_OFPMMFC_BAD_FLAGS;
+ case EINVAL: /* Too many bands */
+ return OFPERR_OFPMMFC_OUT_OF_BANDS;
+ case ENODEV: /* Unsupported band type */
+ return OFPERR_OFPMMFC_BAD_BAND;
+ case EDOM: /* Rate must be non-zero */
+ return OFPERR_OFPMMFC_BAD_RATE;
+ default:
+ return OFPERR_OFPMMFC_UNKNOWN;
+ }
+}
+
+static enum ofperr
+meter_get(const struct ofproto *ofproto_, ofproto_meter_id meter_id,
+ struct ofputil_meter_stats *stats, uint16_t n_bands)
{
- return &ofproto->uuid;
+ const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ if (!dpif_meter_get(ofproto->backer->dpif, meter_id, stats, n_bands)) {
+ return 0;
+ }
+ return OFPERR_OFPMMFC_UNKNOWN_METER;
+}
+
+struct free_meter_id_args {
+ struct ofproto_dpif *ofproto;
+ ofproto_meter_id meter_id;
+};
+
+static void
+free_meter_id(struct free_meter_id_args *args)
+{
+ struct ofproto_dpif *ofproto = args->ofproto;
+
+ dpif_meter_del(ofproto->backer->dpif, args->meter_id, NULL, 0);
+ id_pool_free_id(ofproto->backer->meter_ids, args->meter_id.uint32);
+ free(args);
+}
+
+static void
+meter_del(struct ofproto *ofproto_, ofproto_meter_id meter_id)
+{
+ struct free_meter_id_args *arg = xmalloc(sizeof *arg);
+
+ /* Before a meter can be deleted, Openflow spec requires all rules
+ * referring to the meter to be (automatically) removed before the
+ * meter is deleted. However, since vswitchd is multi-threaded,
+ * those rules and their actions remain accessible by other threads,
+ * especially by the handler and revalidator threads.
+ * Postpone meter deletion after RCU grace period, so that ongoing
+ * upcall translation or flow revalidation can complete. */
+ arg->ofproto = ofproto_dpif_cast(ofproto_);
+ arg->meter_id = meter_id;
+ ovsrcu_postpone(free_meter_id, arg);
}
const struct ofproto_class ofproto_dpif_class = {
set_mac_table_config,
set_mcast_snooping,
set_mcast_snooping_port,
- NULL, /* meter_get_features */
- NULL, /* meter_set */
- NULL, /* meter_get */
- NULL, /* meter_del */
+ meter_get_features,
+ meter_set,
+ meter_get,
+ meter_del,
group_alloc, /* group_alloc */
group_construct, /* group_construct */
group_destruct, /* group_destruct */
group_dealloc, /* group_dealloc */
- group_modify, /* group_modify */
+ NULL, /* group_modify */
group_get_stats, /* group_get_stats */
get_datapath_version, /* get_datapath_version */
+ type_set_config,
+ ct_flush, /* ct_flush */
};