+ if (!cfm_fault) {
+ rmp->num_health_ccm++;
+ }
+ rmp->recv = true;
+ cfm->recv_fault |= cfm_fault;
+ rmp->seq = ccm_seq;
+ rmp->opup = !ccm_opdown;
+ rmp->last_rx = time_msec();
+ }
+ }
+
+out:
+ ovs_mutex_unlock(&mutex);
+}
+
+/* Returns and resets the 'cfm->status_changed'. */
+bool
+cfm_check_status_change(struct cfm *cfm) OVS_EXCLUDED(mutex)
+{
+ bool ret;
+
+ ovs_mutex_lock(&mutex);
+ ret = cfm->status_changed;
+ cfm->status_changed = false;
+ ovs_mutex_unlock(&mutex);
+
+ return ret;
+}
+
+static int
+cfm_get_fault__(const struct cfm *cfm) OVS_REQUIRES(mutex)
+{
+ if (cfm->fault_override >= 0) {
+ return cfm->fault_override ? CFM_FAULT_OVERRIDE : 0;
+ }
+ return cfm->fault;
+}
+
+/* Gets the fault status of 'cfm'. Returns a bit mask of 'cfm_fault_reason's
+ * indicating the cause of the connectivity fault, or zero if there is no
+ * fault. */
+int
+cfm_get_fault(const struct cfm *cfm) OVS_EXCLUDED(mutex)
+{
+ int fault;
+
+ ovs_mutex_lock(&mutex);
+ fault = cfm_get_fault__(cfm);
+ ovs_mutex_unlock(&mutex);
+ 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,
+ * returns 0 if there are no remote_mpids, and returns -1 if there are more
+ * than 1 remote_mpids. */
+int
+cfm_get_health(const struct cfm *cfm) OVS_EXCLUDED(mutex)
+{
+ int health;
+
+ ovs_mutex_lock(&mutex);
+ health = cfm->health;
+ ovs_mutex_unlock(&mutex);
+ return health;
+}
+
+/* Gets the operational state of 'cfm'. 'cfm' is considered operationally down
+ * if it has received a CCM with the operationally down bit set from any of its
+ * remote maintenance points. Returns 1 if 'cfm' is operationally up, 0 if
+ * 'cfm' is operationally down, or -1 if 'cfm' has no operational state
+ * (because it isn't in extended mode). */
+int
+cfm_get_opup(const struct cfm *cfm_) OVS_EXCLUDED(mutex)
+{
+ struct cfm *cfm = CONST_CAST(struct cfm *, cfm_);
+ bool extended;
+ int opup;
+
+ ovs_mutex_lock(&mutex);
+ atomic_read(&cfm->extended, &extended);
+ opup = extended ? cfm->remote_opup : -1;
+ ovs_mutex_unlock(&mutex);
+
+ return opup;
+}
+
+/* Populates 'rmps' with an array of remote maintenance points reachable by
+ * 'cfm'. The number of remote maintenance points is written to 'n_rmps'.
+ * 'cfm' retains ownership of the array written to 'rmps' */
+void
+cfm_get_remote_mpids(const struct cfm *cfm, uint64_t **rmps, size_t *n_rmps)
+ OVS_EXCLUDED(mutex)
+{
+ ovs_mutex_lock(&mutex);
+ *rmps = xmemdup(cfm->rmps_array, cfm->rmps_array_len * sizeof **rmps);
+ *n_rmps = cfm->rmps_array_len;
+ ovs_mutex_unlock(&mutex);
+}
+
+static struct cfm *
+cfm_find(const char *name) OVS_REQUIRES(mutex)
+{
+ struct cfm *cfm;
+
+ HMAP_FOR_EACH_WITH_HASH (cfm, hmap_node, hash_string(name, 0), all_cfms) {
+ if (!strcmp(cfm->name, name)) {
+ return cfm;
+ }
+ }
+ return NULL;
+}
+
+static void
+cfm_print_details(struct ds *ds, struct cfm *cfm) OVS_REQUIRES(mutex)
+{
+ struct remote_mp *rmp;
+ bool extended;
+ int fault;
+
+ atomic_read(&cfm->extended, &extended);
+
+ ds_put_format(ds, "---- %s ----\n", cfm->name);
+ ds_put_format(ds, "MPID %"PRIu64":%s%s\n", cfm->mpid,
+ extended ? " extended" : "",
+ cfm->fault_override >= 0 ? " fault_override" : "");
+
+ fault = cfm_get_fault__(cfm);
+ if (fault) {
+ ds_put_cstr(ds, "\tfault: ");
+ ds_put_cfm_fault(ds, fault);
+ ds_put_cstr(ds, "\n");
+ }
+
+ if (cfm->health == -1) {
+ ds_put_format(ds, "\taverage health: undefined\n");
+ } else {
+ ds_put_format(ds, "\taverage health: %d\n", cfm->health);
+ }
+ ds_put_format(ds, "\topstate: %s\n", cfm->opup ? "up" : "down");
+ ds_put_format(ds, "\tremote_opstate: %s\n",
+ cfm->remote_opup ? "up" : "down");
+ ds_put_format(ds, "\tinterval: %dms\n", cfm->ccm_interval_ms);
+ ds_put_format(ds, "\tnext CCM tx: %lldms\n",
+ timer_msecs_until_expired(&cfm->tx_timer));
+ ds_put_format(ds, "\tnext fault check: %lldms\n",
+ timer_msecs_until_expired(&cfm->fault_timer));
+
+ HMAP_FOR_EACH (rmp, node, &cfm->remote_mps) {
+ ds_put_format(ds, "Remote MPID %"PRIu64"\n", rmp->mpid);
+ ds_put_format(ds, "\trecv since check: %s\n",
+ rmp->recv ? "true" : "false");
+ ds_put_format(ds, "\topstate: %s\n", rmp->opup? "up" : "down");
+ }
+}
+
+static void
+cfm_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
+ void *aux OVS_UNUSED) OVS_EXCLUDED(mutex)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ struct cfm *cfm;
+
+ ovs_mutex_lock(&mutex);
+ if (argc > 1) {
+ cfm = cfm_find(argv[1]);
+ if (!cfm) {
+ unixctl_command_reply_error(conn, "no such CFM object");
+ goto out;
+ }
+ cfm_print_details(&ds, cfm);
+ } else {
+ HMAP_FOR_EACH (cfm, hmap_node, all_cfms) {
+ cfm_print_details(&ds, cfm);