The new CFM "demand mode" (named after BFD's demand mode) uses data traffic to indicate interface liveness. It's helpful on heavily congested networks where CCMs may be dropped.
Signed-off-by: Ethan Jackson <[email protected]> --- lib/cfm.c | 49 +++++++++++++++++++++++++++++++++++++++++------- lib/cfm.h | 4 +++- ofproto/ofproto-dpif.c | 2 +- vswitchd/bridge.c | 1 + vswitchd/vswitch.xml | 37 ++++++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 9 deletions(-) diff --git a/lib/cfm.c b/lib/cfm.c index d232b34..9df404e 100644 --- a/lib/cfm.c +++ b/lib/cfm.c @@ -26,6 +26,7 @@ #include "flow.h" #include "hash.h" #include "hmap.h" +#include "netdev.h" #include "ofpbuf.h" #include "packets.h" #include "poll-loop.h" @@ -81,12 +82,16 @@ struct ccm { BUILD_ASSERT_DECL(CCM_LEN == sizeof(struct ccm)); struct cfm { - char *name; /* Name of this CFM object. */ + const char *name; /* Name of this CFM object. */ struct hmap_node hmap_node; /* Node in all_cfms list. */ + const struct netdev *netdev; + uint64_t rx_packets; /* Packets received by 'netdev'. */ + uint64_t mpid; bool check_tnl_key; /* Verify the tunnel key of inbound packets? */ bool extended; /* Extended mode. */ + bool demand; /* Demand mode. */ bool booted; /* A full fault interval has occured. */ enum cfm_fault_reason fault; /* Connectivity fault status. */ enum cfm_fault_reason recv_fault; /* Bit mask of faults occuring on @@ -143,6 +148,18 @@ static struct hmap all_cfms = HMAP_INITIALIZER(&all_cfms); static unixctl_cb_func cfm_unixctl_show; static unixctl_cb_func cfm_unixctl_set_fault; +static uint64_t +cfm_rx_packets(const struct cfm *cfm) +{ + struct netdev_stats stats; + + if (!netdev_get_stats(cfm->netdev, &stats)) { + return stats.rx_packets; + } else { + return 0; + } +} + static const uint8_t * cfm_ccm_addr(const struct cfm *cfm) { @@ -287,12 +304,13 @@ cfm_init(void) /* Allocates a 'cfm' object called 'name'. 'cfm' should be initialized by * cfm_configure() before use. */ struct cfm * -cfm_create(const char *name) +cfm_create(const struct netdev *netdev) { struct cfm *cfm; cfm = xzalloc(sizeof *cfm); - cfm->name = xstrdup(name); + cfm->name = netdev_get_name(netdev); + cfm->netdev = netdev; hmap_init(&cfm->remote_mps); cfm_generate_maid(cfm); hmap_insert(&all_cfms, &cfm->hmap_node, hash_string(cfm->name, 0)); @@ -320,7 +338,6 @@ cfm_destroy(struct cfm *cfm) hmap_destroy(&cfm->remote_mps); hmap_remove(&all_cfms, &cfm->hmap_node); free(cfm->rmps_array); - free(cfm->name); free(cfm); } @@ -332,6 +349,7 @@ cfm_run(struct cfm *cfm) long long int interval = cfm_fault_interval(cfm); struct remote_mp *rmp, *rmp_next; bool old_cfm_fault = cfm->fault; + bool demand_override; cfm->fault = cfm->recv_fault; cfm->recv_fault = 0; @@ -373,14 +391,23 @@ cfm_run(struct cfm *cfm) } cfm->health_interval++; - HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) { + demand_override = false; + if (cfm->demand) { + uint64_t rx_packets = cfm_rx_packets(cfm); + demand_override = hmap_count(&cfm->remote_mps) == 1 + && rx_packets > cfm->rx_packets; + cfm->rx_packets = rx_packets; + } + HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) { if (!rmp->recv) { VLOG_INFO("%s: Received no CCM from RMP %"PRIu64" in the last" " %lldms", cfm->name, rmp->mpid, time_msec() - rmp->last_rx); - hmap_remove(&cfm->remote_mps, &rmp->node); - free(rmp); + if (!demand_override) { + hmap_remove(&cfm->remote_mps, &rmp->node); + free(rmp); + } } else { rmp->recv = false; @@ -518,6 +545,14 @@ cfm_configure(struct cfm *cfm, const struct cfm_settings *s) interval_ms = MIN(s->interval, UINT16_MAX); } + if (cfm->extended && s->demand) { + interval_ms = MAX(interval_ms, 500); + if (!cfm->demand) { + cfm->demand = true; + cfm->rx_packets = cfm_rx_packets(cfm); + } + } + if (interval != cfm->ccm_interval || interval_ms != cfm->ccm_interval_ms) { cfm->ccm_interval = interval; cfm->ccm_interval_ms = interval_ms; diff --git a/lib/cfm.h b/lib/cfm.h index 8bb6778..eec9704 100644 --- a/lib/cfm.h +++ b/lib/cfm.h @@ -23,6 +23,7 @@ struct flow; struct ofpbuf; +struct netdev; #define CFM_RANDOM_VLAN UINT16_MAX @@ -53,6 +54,7 @@ struct cfm_settings { uint64_t mpid; /* The MPID of this CFM. */ int interval; /* The requested transmission interval. */ bool extended; /* Run in extended mode. */ + bool demand; /* Run in demand mode. */ bool opup; /* Operational State. */ uint16_t ccm_vlan; /* CCM Vlan tag. Zero if none. CFM_RANDOM_VLAN if random. */ @@ -62,7 +64,7 @@ struct cfm_settings { }; void cfm_init(void); -struct cfm *cfm_create(const char *name); +struct cfm *cfm_create(const struct netdev *); void cfm_destroy(struct cfm *); void cfm_run(struct cfm *); bool cfm_should_send_ccm(struct cfm *); diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 5cc56d1..b84e2c0 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -1973,7 +1973,7 @@ set_cfm(struct ofport *ofport_, const struct cfm_settings *s) ofproto = ofproto_dpif_cast(ofport->up.ofproto); ofproto->backer->need_revalidate = REV_RECONFIGURE; - ofport->cfm = cfm_create(netdev_get_name(ofport->up.netdev)); + ofport->cfm = cfm_create(ofport->up.netdev); } if (cfm_configure(ofport->cfm, s)) { diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index e10036c..f067c9d 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -3691,6 +3691,7 @@ iface_configure_cfm(struct iface *iface) s.extended = smap_get_bool(&iface->cfg->other_config, "cfm_extended", false); + s.demand = smap_get_bool(&iface->cfg->other_config, "cfm_demand", false); opstate_str = smap_get(&iface->cfg->other_config, "cfm_opstate"); s.opup = !opstate_str || !strcasecmp("up", opstate_str); diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 4396779..47b13d2 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -2000,6 +2000,43 @@ compatibility with 802.1ag compliant implementations. Defaults to <code>false</code>. </column> + + <column name="other_config" key="cfm_demand" type='{"type": "boolean"}'> + <p> + When <code>true</code>, and + <ref column="other_config" key="cfm_extended"/> is true, the CFM + module operates in demand mode. When in demand mode, traffic + received on the <ref table="Interface"/> is used to indicate + liveness. CCMs are still transmitted and received, but if the + <ref table="Interface"/> is receiving traffic, their absence does not + cause a connectivity fault. + </p> + + <p> + Demand mode has a couple of caveats: + <ul> + <li> + To ensure that ovs-vswitchd has enough time to pull statistics + from the datapath, the minimum + <ref column="other_config" key="cfm_interval"/> is 500ms. + </li> + + <li> + To avoid ambiguity, demand mode disables itself when there are + multiple remote maintenance points. + </li> + + <li> + If the <ref table="Interface"/> is heavily congested, CCMs + containing the <ref column="other_config" key="cfm_opstate"/> + status may be dropped causing changes in the operational state to + be delayed. Similarly, if CCMs containing the RDI bit are not + received, unidirectional link failures may not be detected. + </li> + </ul> + </p> + </column> + <column name="other_config" key="cfm_opstate" type='{"type": "string", "enum": ["set", ["down", "up"]]}'> When <code>down</code>, the CFM module marks all CCMs it generates as -- 1.7.9.5 _______________________________________________ dev mailing list [email protected] http://openvswitch.org/mailman/listinfo/dev
