2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

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 <ethan@nicira.com>
This commit is contained in:
Ethan Jackson 2013-05-15 14:31:06 -07:00
parent 0bb0393a0b
commit 90967e953f
6 changed files with 89 additions and 9 deletions

2
NEWS
View File

@ -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
---------------------

View File

@ -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;

View File

@ -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 *);

View File

@ -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)) {

View File

@ -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);

View File

@ -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