X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fcfm.c;h=1b326252902c21b98f644fc247f317ec9b9d23db;hb=5767a79a40599e5e6e70662833b27ffdbdab7e37;hp=4a46c0556b6537f47cf36e95a5130c5ed80b2c34;hpb=95e22d6798a07f01ad445263f44d6743b13bc2ce;p=sliver-openvswitch.git diff --git a/lib/cfm.c b/lib/cfm.c index 4a46c0556..1b3262529 100644 --- a/lib/cfm.c +++ b/lib/cfm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ #include #include "byte-order.h" +#include "connectivity.h" #include "dynamic-string.h" #include "flow.h" #include "hash.h" @@ -31,6 +32,7 @@ #include "packets.h" #include "poll-loop.h" #include "random.h" +#include "seq.h" #include "timer.h" #include "timeval.h" #include "unixctl.h" @@ -128,7 +130,18 @@ struct cfm { 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. */ + + /* True when the variables returned by cfm_get_*() are changed + * since last check. */ + bool status_changed; + + /* When 'cfm->demand' is set, at least one ccm is required to be received + * every 100 * cfm_interval. If ccm is not received within this interval, + * even if data packets are received, the cfm fault will be set. */ + struct timer demand_rx_ccm_t; }; /* Remote MPs represent foreign network entities that are configured to have @@ -232,7 +245,7 @@ static int 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; @@ -240,10 +253,10 @@ ccm_interval_to_ms(uint8_t interval) 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 @@ -255,9 +268,13 @@ cfm_fault_interval(struct cfm *cfm) OVS_REQUIRES(mutex) * 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 @@ -277,7 +294,7 @@ ms_to_ccm_interval(int interval_ms) static uint32_t hash_mpid(uint64_t mpid) { - return hash_bytes(&mpid, sizeof mpid, 0); + return hash_uint64(mpid); } static bool @@ -326,14 +343,16 @@ cfm_create(const struct netdev *netdev) OVS_EXCLUDED(mutex) 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); hmap_insert(all_cfms, &cfm->hmap_node, hash_string(cfm->name, 0)); ovs_mutex_unlock(&mutex); + return cfm; } @@ -341,15 +360,12 @@ void 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; } @@ -365,6 +381,7 @@ cfm_unref(struct cfm *cfm) OVS_EXCLUDED(mutex) hmap_destroy(&cfm->remote_mps); netdev_close(cfm->netdev); free(cfm->rmps_array); + free(cfm); } @@ -373,13 +390,19 @@ cfm_ref(const struct cfm *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; } +/* Records the status change and changes the global connectivity seq. */ +static void +cfm_status_changed(struct cfm *cfm) OVS_REQUIRES(mutex) +{ + seq_change(connectivity_seq_get()); + cfm->status_changed = true; +} + /* Should be run periodically to update fault statistics messages. */ void cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex) @@ -388,7 +411,12 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex) if (timer_expired(&cfm->fault_timer)) { long long int interval = cfm_fault_interval(cfm); struct remote_mp *rmp, *rmp_next; - bool old_cfm_fault = cfm->fault; + enum cfm_fault_reason old_cfm_fault = cfm->fault; + uint64_t old_flap_count = cfm->flap_count; + int old_health = cfm->health; + size_t old_rmps_array_len = cfm->rmps_array_len; + bool old_rmps_deleted = false; + bool old_rmp_opup = cfm->remote_opup; bool demand_override; bool rmp_set_opup = false; bool rmp_set_opdown = false; @@ -436,7 +464,8 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex) if (cfm->demand) { uint64_t rx_packets = cfm_rx_packets(cfm); demand_override = hmap_count(&cfm->remote_mps) == 1 - && rx_packets > cfm->rx_packets; + && rx_packets > cfm->rx_packets + && !timer_expired(&cfm->demand_rx_ccm_t); cfm->rx_packets = rx_packets; } @@ -446,6 +475,7 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex) " %lldms", cfm->name, rmp->mpid, time_msec() - rmp->last_rx); if (!demand_override) { + old_rmps_deleted = true; hmap_remove(&cfm->remote_mps, &rmp->node); free(rmp); } @@ -473,16 +503,33 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex) 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); + } - 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 == 0 || cfm->fault == 0) { + cfm->flap_count++; + } + } + + /* These variables represent the cfm session status, it is desirable + * to update them to database immediately after change. */ + if (old_health != cfm->health + || old_rmp_opup != cfm->remote_opup + || (old_rmps_array_len != cfm->rmps_array_len || old_rmps_deleted) + || old_cfm_fault != cfm->fault + || old_flap_count != cfm->flap_count) { + cfm_status_changed(cfm); } cfm->booted = true; @@ -526,10 +573,10 @@ cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet, 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; + ccm = ofpbuf_l3(packet); ccm->mdlevel_version = 0; ccm->opcode = CCM_OPCODE; ccm->tlv_offset = 70; @@ -576,12 +623,27 @@ cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet, 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) @@ -611,7 +673,6 @@ 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); @@ -676,8 +737,9 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) ovs_mutex_lock(&mutex); - eth = p->l2; - ccm = ofpbuf_at(p, (uint8_t *)p->l3 - (uint8_t *)p->data, CCM_ACCEPT_LEN); + eth = ofpbuf_l2(p); + ccm = ofpbuf_at(p, (uint8_t *)ofpbuf_l3(p) - (uint8_t *)ofpbuf_data(p), + CCM_ACCEPT_LEN); if (!ccm) { VLOG_INFO_RL(&rl, "%s: Received an unparseable 802.1ag CCM heartbeat.", @@ -727,7 +789,6 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) 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); @@ -735,7 +796,6 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) 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); @@ -782,6 +842,10 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) rmp->mpid = ccm_mpid; if (!cfm_fault) { rmp->num_health_ccm++; + if (cfm->demand) { + timer_set_duration(&cfm->demand_rx_ccm_t, + 100 * cfm->ccm_interval_ms); + } } rmp->recv = true; cfm->recv_fault |= cfm_fault; @@ -795,6 +859,20 @@ 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) { @@ -818,6 +896,17 @@ cfm_get_fault(const struct cfm *cfm) OVS_EXCLUDED(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, @@ -977,9 +1066,11 @@ cfm_unixctl_set_fault(struct unixctl_conn *conn, int argc, const char *argv[], goto out; } cfm->fault_override = fault_override; + cfm_status_changed(cfm); } else { HMAP_FOR_EACH (cfm, hmap_node, all_cfms) { cfm->fault_override = fault_override; + cfm_status_changed(cfm); } }