From: Ethan Jackson Date: Wed, 15 May 2013 21:31:06 +0000 (-0700) Subject: cfm: Implement "demand mode". X-Git-Tag: v2.0~725 X-Git-Url: http://git.cascardo.eti.br/?p=cascardo%2Fovs.git;a=commitdiff_plain;h=90967e953f5019299b5b5e2d70f440d82c32e2f1 cfm: Implement "demand mode". 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 --- diff --git a/NEWS b/NEWS index 4cb4499a8..3cb724eaf 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,8 @@ v1.11.0 - xx xxx xxxx pass through. Any users that relied on this automatic firewall hole will have to manually configure it. The ovs-ctl(8) manpage documents the "enable-protocol" command that can be used as an alternative. + - New CFM demand mode which uses data traffic to indicate interface + liveness. v1.10.0 - 01 May 2013 --------------------- diff --git a/lib/cfm.c b/lib/cfm.c index d232b34f1..d16e2dd8f 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. */ + 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->netdev = netdev_ref(netdev); + cfm->name = netdev_get_name(cfm->netdev); hmap_init(&cfm->remote_mps); cfm_generate_maid(cfm); hmap_insert(&all_cfms, &cfm->hmap_node, hash_string(cfm->name, 0)); @@ -319,8 +337,8 @@ cfm_destroy(struct cfm *cfm) hmap_destroy(&cfm->remote_mps); hmap_remove(&all_cfms, &cfm->hmap_node); + netdev_close(cfm->netdev); free(cfm->rmps_array); - free(cfm->name); free(cfm); } @@ -332,6 +350,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 +392,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 +546,16 @@ 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); + } + } else { + cfm->demand = false; + } + 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 8bb677840..eec9704dd 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 1b6a17a22..280fd577c 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 e10036c9a..f067c9de4 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 4396779ba..47b13d219 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -2000,6 +2000,43 @@ compatibility with 802.1ag compliant implementations. Defaults to false. + + +

+ When true, and + is true, the CFM + module operates in demand mode. When in demand mode, traffic + received on the is used to indicate + liveness. CCMs are still transmitted and received, but if the + is receiving traffic, their absence does not + cause a connectivity fault. +

+ +

+ Demand mode has a couple of caveats: +

    +
  • + To ensure that ovs-vswitchd has enough time to pull statistics + from the datapath, the minimum + is 500ms. +
  • + +
  • + To avoid ambiguity, demand mode disables itself when there are + multiple remote maintenance points. +
  • + +
  • + If the is heavily congested, CCMs + containing the + 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. +
  • +
+

+
+ When down, the CFM module marks all CCMs it generates as