#include <string.h>
#include "byte-order.h"
+#include "connectivity.h"
#include "dynamic-string.h"
#include "flow.h"
#include "hash.h"
#include "packets.h"
#include "poll-loop.h"
#include "random.h"
+#include "seq.h"
#include "timer.h"
#include "timeval.h"
#include "unixctl.h"
atomic_bool check_tnl_key; /* Verify the tunnel key of inbound packets? */
atomic_bool extended; /* Extended mode. */
- atomic_int ref_cnt;
+ struct ovs_refcount ref_cnt;
+
+ uint64_t flap_count; /* Count the flaps since boot. */
};
/* Remote MPs represent foreign network entities that are configured to have
ccm_interval_to_ms(uint8_t interval)
{
switch (interval) {
- case 0: NOT_REACHED(); /* Explicitly not supported by 802.1ag. */
+ case 0: OVS_NOT_REACHED(); /* Explicitly not supported by 802.1ag. */
case 1: return 3; /* Not recommended due to timer resolution. */
case 2: return 10; /* Not recommended due to timer resolution. */
case 3: return 100;
case 5: return 10000;
case 6: return 60000;
case 7: return 600000;
- default: NOT_REACHED(); /* Explicitly not supported by 802.1ag. */
+ default: OVS_NOT_REACHED(); /* Explicitly not supported by 802.1ag. */
}
- NOT_REACHED();
+ OVS_NOT_REACHED();
}
static long long int
* as a fault (likely due to a configuration error). Thus we can check all
* MPs at once making this quite a bit simpler.
*
- * According to the specification we should check when (ccm_interval_ms *
- * 3.5)ms have passed. */
- return (cfm->ccm_interval_ms * 7) / 2;
+ * When cfm is not in demand mode, we check when (ccm_interval_ms * 3.5) ms
+ * have passed. When cfm is in demand mode, we check when
+ * (MAX(ccm_interval_ms, 500) * 3.5) ms have passed. This ensures that
+ * ovs-vswitchd has enough time to pull statistics from the datapath. */
+
+ return (MAX(cfm->ccm_interval_ms, cfm->demand ? 500 : cfm->ccm_interval_ms)
+ * 7) / 2;
}
static uint8_t
cfm->fault_override = -1;
cfm->health = -1;
cfm->last_tx = 0;
+ cfm->flap_count = 0;
atomic_init(&cfm->extended, false);
atomic_init(&cfm->check_tnl_key, false);
- atomic_init(&cfm->ref_cnt, 1);
+ ovs_refcount_init(&cfm->ref_cnt);
ovs_mutex_lock(&mutex);
cfm_generate_maid(cfm);
cfm_unref(struct cfm *cfm) OVS_EXCLUDED(mutex)
{
struct remote_mp *rmp, *rmp_next;
- int orig;
if (!cfm) {
return;
}
- atomic_sub(&cfm->ref_cnt, 1, &orig);
- ovs_assert(orig > 0);
- if (orig != 1) {
+ if (ovs_refcount_unref(&cfm->ref_cnt) != 1) {
return;
}
hmap_destroy(&cfm->remote_mps);
netdev_close(cfm->netdev);
free(cfm->rmps_array);
+
+ atomic_destroy(&cfm->extended);
+ atomic_destroy(&cfm->check_tnl_key);
+ ovs_refcount_destroy(&cfm->ref_cnt);
+
free(cfm);
}
{
struct cfm *cfm = CONST_CAST(struct cfm *, cfm_);
if (cfm) {
- int orig;
- atomic_add(&cfm->ref_cnt, 1, &orig);
- ovs_assert(orig > 0);
+ ovs_refcount_ref(&cfm->ref_cnt);
}
return cfm;
}
long long int interval = cfm_fault_interval(cfm);
struct remote_mp *rmp, *rmp_next;
bool old_cfm_fault = cfm->fault;
+ bool old_rmp_opup = cfm->remote_opup;
bool demand_override;
bool rmp_set_opup = false;
bool rmp_set_opdown = false;
cfm->health = 0;
} else {
int exp_ccm_recvd;
+ int old_health = cfm->health;
rmp = CONTAINER_OF(hmap_first(&cfm->remote_mps),
struct remote_mp, node);
cfm->health = MIN(cfm->health, 100);
rmp->num_health_ccm = 0;
ovs_assert(cfm->health >= 0 && cfm->health <= 100);
+
+ if (cfm->health != old_health) {
+ seq_change(connectivity_seq_get());
+ }
}
cfm->health_interval = 0;
}
cfm->remote_opup = true;
}
+ if (old_rmp_opup != cfm->remote_opup) {
+ seq_change(connectivity_seq_get());
+ }
+
if (hmap_is_empty(&cfm->remote_mps)) {
cfm->fault |= CFM_FAULT_RECV;
}
- if (old_cfm_fault != cfm->fault && !VLOG_DROP_INFO(&rl)) {
- struct ds ds = DS_EMPTY_INITIALIZER;
+ if (old_cfm_fault != cfm->fault) {
+ if (!VLOG_DROP_INFO(&rl)) {
+ struct ds ds = DS_EMPTY_INITIALIZER;
+
+ ds_put_cstr(&ds, "from [");
+ ds_put_cfm_fault(&ds, old_cfm_fault);
+ ds_put_cstr(&ds, "] to [");
+ ds_put_cfm_fault(&ds, cfm->fault);
+ ds_put_char(&ds, ']');
+ VLOG_INFO("%s: CFM faults changed %s.", cfm->name, ds_cstr(&ds));
+ ds_destroy(&ds);
+ }
+
+ /* If there is a flap, increments the counter. */
+ if (old_cfm_fault == false || cfm->fault == false) {
+ cfm->flap_count++;
+ }
- ds_put_cstr(&ds, "from [");
- ds_put_cfm_fault(&ds, old_cfm_fault);
- ds_put_cstr(&ds, "] to [");
- ds_put_cfm_fault(&ds, cfm->fault);
- ds_put_char(&ds, ']');
- VLOG_INFO("%s: CFM faults changed %s.", cfm->name, ds_cstr(&ds));
- ds_destroy(&ds);
+ seq_change(connectivity_seq_get());
}
cfm->booted = true;
if (ccm_vlan || cfm->ccm_pcp) {
uint16_t tci = ccm_vlan | (cfm->ccm_pcp << VLAN_PCP_SHIFT);
- eth_push_vlan(packet, htons(tci));
+ eth_push_vlan(packet, htons(ETH_TYPE_VLAN), htons(tci));
}
ccm = packet->l3;
void
cfm_wait(struct cfm *cfm) OVS_EXCLUDED(mutex)
{
+ poll_timer_wait_until(cfm_wake_time(cfm));
+}
+
+
+/* Returns the next cfm wakeup time. */
+long long int
+cfm_wake_time(struct cfm *cfm) OVS_EXCLUDED(mutex)
+{
+ long long int retval;
+
+ if (!cfm) {
+ return LLONG_MAX;
+ }
+
ovs_mutex_lock(&mutex);
- timer_wait(&cfm->tx_timer);
- timer_wait(&cfm->fault_timer);
+ retval = MIN(cfm->tx_timer.t, cfm->fault_timer.t);
ovs_mutex_unlock(&mutex);
+ return retval;
}
+
/* Configures 'cfm' with settings from 's'. */
bool
cfm_configure(struct cfm *cfm, const struct cfm_settings *s)
}
if (s->extended && s->demand) {
- interval_ms = MAX(interval_ms, 500);
if (!cfm->demand) {
cfm->demand = true;
cfm->rx_packets = cfm_rx_packets(cfm);
ccm_seq = ntohl(ccm->seq);
if (ccm_interval != cfm->ccm_interval) {
- cfm_fault |= CFM_FAULT_INTERVAL;
VLOG_WARN_RL(&rl, "%s: received a CCM with an unexpected interval"
" (%"PRIu8") from RMP %"PRIu64, cfm->name,
ccm_interval, ccm_mpid);
if (extended && ccm_interval == 0
&& ccm_interval_ms_x != cfm->ccm_interval_ms) {
- cfm_fault |= CFM_FAULT_INTERVAL;
VLOG_WARN_RL(&rl, "%s: received a CCM with an unexpected extended"
" interval (%"PRIu16"ms) from RMP %"PRIu64, cfm->name,
ccm_interval_ms_x, ccm_mpid);
return fault;
}
+/* Gets the number of cfm fault flapping since start. */
+uint64_t
+cfm_get_flap_count(const struct cfm *cfm) OVS_EXCLUDED(mutex)
+{
+ uint64_t flap_count;
+ ovs_mutex_lock(&mutex);
+ flap_count = cfm->flap_count;
+ ovs_mutex_unlock(&mutex);
+ return flap_count;
+}
+
/* Gets the health of 'cfm'. Returns an integer between 0 and 100 indicating
* the health of the link as a percentage of ccm frames received in
* CFM_HEALTH_INTERVAL * 'fault_interval' if there is only 1 remote_mpid,
OVS_EXCLUDED(mutex)
{
ovs_mutex_lock(&mutex);
- *rmps = xmemdup(cfm->rmps_array, cfm->rmps_array_len);
+ *rmps = xmemdup(cfm->rmps_array, cfm->rmps_array_len * sizeof **rmps);
*n_rmps = cfm->rmps_array_len;
ovs_mutex_unlock(&mutex);
}
}
}
+ seq_change(connectivity_seq_get());
unixctl_command_reply(conn, "OK");
out: