X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=lib%2Fcfm.c;h=587454a750adf9042b2d1aa84e0ebd6dbff113ce;hb=ca7e7bee86b4ee821d61b58bf15c89a9d8a3cb30;hp=fc0ef785912f6ac79bf588bc5325ce4265f8853d;hpb=c5f81b20da9bbf0ac406a88718597a4e84729a98;p=cascardo%2Fovs.git diff --git a/lib/cfm.c b/lib/cfm.c index fc0ef7859..587454a75 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, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,12 +23,13 @@ #include "byte-order.h" #include "connectivity.h" +#include "dp-packet.h" #include "dynamic-string.h" #include "flow.h" #include "hash.h" #include "hmap.h" #include "netdev.h" -#include "ofpbuf.h" +#include "ovs-atomic.h" #include "packets.h" #include "poll-loop.h" #include "random.h" @@ -36,17 +37,17 @@ #include "timer.h" #include "timeval.h" #include "unixctl.h" -#include "vlog.h" +#include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(cfm); #define CFM_MAX_RMPS 256 /* Ethernet destination address of CCM packets. */ -static const uint8_t eth_addr_ccm[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x30 }; -static const uint8_t eth_addr_ccm_x[6] = { - 0x01, 0x23, 0x20, 0x00, 0x00, 0x30 -}; +static const struct eth_addr eth_addr_ccm = { + { { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x30 } } }; +static const struct eth_addr eth_addr_ccm_x = { + { { 0x01, 0x23, 0x20, 0x00, 0x00, 0x30 } } }; #define ETH_TYPE_CFM 0x8902 @@ -128,11 +129,23 @@ struct cfm { recomputed. */ long long int last_tx; /* Last CCM transmission time. */ + /* These bools are atomic to allow readers to check their values + * without taking 'mutex'. Such readers do not assume the values they + * read are synchronized with any other members. */ 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 @@ -171,11 +184,13 @@ cfm_rx_packets(const struct cfm *cfm) OVS_REQUIRES(mutex) } } -static const uint8_t * +static struct eth_addr cfm_ccm_addr(struct cfm *cfm) { bool extended; - atomic_read(&cfm->extended, &extended); + + atomic_read_relaxed(&cfm->extended, &extended); + return extended ? eth_addr_ccm_x : eth_addr_ccm; } @@ -285,7 +300,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 @@ -319,6 +334,14 @@ cfm_init(void) 1, 2, cfm_unixctl_set_fault, NULL); } +/* 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; +} + /* Allocates a 'cfm' object called 'name'. 'cfm' should be initialized by * cfm_configure() before use. */ struct cfm * @@ -337,12 +360,14 @@ cfm_create(const struct netdev *netdev) OVS_EXCLUDED(mutex) 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_status_changed(cfm); cfm_generate_maid(cfm); hmap_insert(all_cfms, &cfm->hmap_node, hash_string(cfm->name, 0)); ovs_mutex_unlock(&mutex); + return cfm; } @@ -350,19 +375,17 @@ 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_relaxed(&cfm->ref_cnt) != 1) { return; } ovs_mutex_lock(&mutex); + cfm_status_changed(cfm); hmap_remove(all_cfms, &cfm->hmap_node); ovs_mutex_unlock(&mutex); @@ -375,10 +398,6 @@ cfm_unref(struct cfm *cfm) OVS_EXCLUDED(mutex) netdev_close(cfm->netdev); free(cfm->rmps_array); - atomic_destroy(&cfm->extended); - atomic_destroy(&cfm->check_tnl_key); - atomic_destroy(&cfm->ref_cnt); - free(cfm); } @@ -387,9 +406,7 @@ 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; } @@ -402,7 +419,11 @@ 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; @@ -428,7 +449,6 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex) 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); @@ -443,10 +463,6 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex) 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; } @@ -456,7 +472,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; } @@ -466,6 +483,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); } @@ -489,10 +507,6 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex) 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; } @@ -511,11 +525,19 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex) } /* If there is a flap, increments the counter. */ - if (old_cfm_fault == false || cfm->fault == false) { + if (old_cfm_fault == 0 || cfm->fault == 0) { cfm->flap_count++; } + } - seq_change(connectivity_seq_get()); + /* 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; @@ -541,8 +563,8 @@ cfm_should_send_ccm(struct cfm *cfm) OVS_EXCLUDED(mutex) /* Composes a CCM message into 'packet'. Messages generated with this function * should be sent whenever cfm_should_send_ccm() indicates. */ void -cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet, - uint8_t eth_src[ETH_ADDR_LEN]) OVS_EXCLUDED(mutex) +cfm_compose_ccm(struct cfm *cfm, struct dp_packet *packet, + const struct eth_addr eth_src) OVS_EXCLUDED(mutex) { uint16_t ccm_vlan; struct ccm *ccm; @@ -559,10 +581,12 @@ 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; + atomic_read_relaxed(&cfm->extended, &extended); + + ccm = dp_packet_l3(packet); ccm->mdlevel_version = 0; ccm->opcode = CCM_OPCODE; ccm->tlv_offset = 70; @@ -572,7 +596,6 @@ cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet, memset(ccm->zero, 0, sizeof ccm->zero); ccm->end_tlv = 0; - atomic_read(&cfm->extended, &extended); if (extended) { ccm->mpid = htons(hash_mpid(cfm->mpid)); ccm->mpid64 = htonll(cfm->mpid); @@ -597,7 +620,7 @@ cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet, if (cfm->last_tx) { long long int delay = time_msec() - cfm->last_tx; if (delay > (cfm->ccm_interval_ms * 3 / 2)) { - VLOG_WARN("%s: long delay of %lldms (expected %dms) sending CCM" + VLOG_INFO("%s: long delay of %lldms (expected %dms) sending CCM" " seq %"PRIu32, cfm->name, delay, cfm->ccm_interval_ms, cfm->seq); } @@ -606,10 +629,12 @@ cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet, ovs_mutex_unlock(&mutex); } -void +long long int cfm_wait(struct cfm *cfm) OVS_EXCLUDED(mutex) { - poll_timer_wait_until(cfm_wake_time(cfm)); + long long int wake_time = cfm_wake_time(cfm); + poll_timer_wait_until(wake_time); + return wake_time; } @@ -648,8 +673,8 @@ cfm_configure(struct cfm *cfm, const struct cfm_settings *s) interval = ms_to_ccm_interval(s->interval); interval_ms = ccm_interval_to_ms(interval); - atomic_store(&cfm->check_tnl_key, s->check_tnl_key); - atomic_store(&cfm->extended, s->extended); + atomic_store_relaxed(&cfm->check_tnl_key, s->check_tnl_key); + atomic_store_relaxed(&cfm->extended, s->extended); cfm->ccm_vlan = s->ccm_vlan; cfm->ccm_pcp = s->ccm_pcp & (VLAN_PCP_MASK >> VLAN_PCP_SHIFT); @@ -701,30 +726,43 @@ cfm_should_process_flow(const struct cfm *cfm_, const struct flow *flow, struct cfm *cfm = CONST_CAST(struct cfm *, cfm_); bool check_tnl_key; - atomic_read(&cfm->check_tnl_key, &check_tnl_key); + /* Most packets are not CFM. */ + if (OVS_LIKELY(flow->dl_type != htons(ETH_TYPE_CFM))) { + return false; + } + memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); + if (OVS_UNLIKELY(!eth_addr_equals(flow->dl_dst, cfm_ccm_addr(cfm)))) { + return false; + } + + atomic_read_relaxed(&cfm->check_tnl_key, &check_tnl_key); + if (check_tnl_key) { memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id); + return flow->tunnel.tun_id == htonll(0); } - return (ntohs(flow->dl_type) == ETH_TYPE_CFM - && eth_addr_equals(flow->dl_dst, cfm_ccm_addr(cfm)) - && (!check_tnl_key || flow->tunnel.tun_id == htonll(0))); + return true; } /* Updates internal statistics relevant to packet 'p'. Should be called on * every packet whose flow returned true when passed to * cfm_should_process_flow. */ void -cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) +cfm_process_heartbeat(struct cfm *cfm, const struct dp_packet *p) OVS_EXCLUDED(mutex) { struct ccm *ccm; struct eth_header *eth; + bool extended; ovs_mutex_lock(&mutex); - eth = p->l2; - ccm = ofpbuf_at(p, (uint8_t *)p->l3 - (uint8_t *)p->data, CCM_ACCEPT_LEN); + atomic_read_relaxed(&cfm->extended, &extended); + + eth = dp_packet_l2(p); + ccm = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p), + CCM_ACCEPT_LEN); if (!ccm) { VLOG_INFO_RL(&rl, "%s: Received an unparseable 802.1ag CCM heartbeat.", @@ -760,10 +798,8 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) uint64_t ccm_mpid; uint32_t ccm_seq; bool ccm_opdown; - bool extended; enum cfm_fault_reason cfm_fault = 0; - atomic_read(&cfm->extended, &extended); if (extended) { ccm_mpid = ntohll(ccm->mpid64); ccm_opdown = ccm->opdown; @@ -827,6 +863,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; @@ -840,6 +880,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) { @@ -890,26 +944,42 @@ cfm_get_health(const struct cfm *cfm) OVS_EXCLUDED(mutex) return health; } +static int +cfm_get_opup__(const struct cfm *cfm_) OVS_REQUIRES(mutex) +{ + struct cfm *cfm = CONST_CAST(struct cfm *, cfm_); + bool extended; + + atomic_read_relaxed(&cfm->extended, &extended); + + return extended ? cfm->remote_opup : -1; +} + /* 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) +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; + opup = cfm_get_opup__(cfm); ovs_mutex_unlock(&mutex); return opup; } +static void +cfm_get_remote_mpids__(const struct cfm *cfm, uint64_t **rmps, size_t *n_rmps) + OVS_REQUIRES(mutex) +{ + *rmps = xmemdup(cfm->rmps_array, cfm->rmps_array_len * sizeof **rmps); + *n_rmps = cfm->rmps_array_len; +} + /* 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' */ @@ -918,8 +988,20 @@ 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; + cfm_get_remote_mpids__(cfm, rmps, n_rmps); + ovs_mutex_unlock(&mutex); +} + +/* Extracts the status of 'cfm' and fills in the 's'. */ +void +cfm_get_status(const struct cfm *cfm, struct cfm_status *s) OVS_EXCLUDED(mutex) +{ + ovs_mutex_lock(&mutex); + s->faults = cfm_get_fault__(cfm); + s->remote_opstate = cfm_get_opup__(cfm); + s->flap_count = cfm->flap_count; + s->health = cfm->health; + cfm_get_remote_mpids__(cfm, &s->rmps, &s->n_rmps); ovs_mutex_unlock(&mutex); } @@ -943,7 +1025,7 @@ cfm_print_details(struct ds *ds, struct cfm *cfm) OVS_REQUIRES(mutex) bool extended; int fault; - atomic_read(&cfm->extended, &extended); + atomic_read_relaxed(&cfm->extended, &extended); ds_put_format(ds, "---- %s ----\n", cfm->name); ds_put_format(ds, "MPID %"PRIu64":%s%s\n", cfm->mpid, @@ -1033,13 +1115,14 @@ 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); } } - seq_change(connectivity_seq_get()); unixctl_command_reply(conn, "OK"); out: