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:
parent
0bb0393a0b
commit
90967e953f
2
NEWS
2
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
|
||||
---------------------
|
||||
|
52
lib/cfm.c
52
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;
|
||||
|
@ -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 *);
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user