X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=lib%2Fcfm.c;h=a76a3ecccda2395dc51129c5744144e19cb6e8e4;hb=d017eeb9f9ebcb46c24a67fd301b3e36cd26a04e;hp=b71c242a54763eb4a182b7df98af1e591f2d328f;hpb=7a97619ae4484604c15b846ce7268094e048729a;p=sliver-openvswitch.git diff --git a/lib/cfm.c b/lib/cfm.c index b71c242a5..a76a3eccc 100644 --- a/lib/cfm.c +++ b/lib/cfm.c @@ -17,7 +17,6 @@ #include #include "cfm.h" -#include #include #include #include @@ -27,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" @@ -61,6 +61,8 @@ static const uint8_t eth_addr_ccm_x[6] = { #define CCM_OPCODE 1 /* CFM message opcode meaning CCM. */ #define CCM_RDI_MASK 0x80 #define CFM_HEALTH_INTERVAL 6 + +OVS_PACKED( struct ccm { uint8_t mdlevel_version; /* MD Level and Version */ uint8_t opcode; @@ -78,19 +80,23 @@ struct ccm { /* TLV space. */ uint8_t end_tlv; -} __attribute__((packed)); +}); 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 booted; /* A full fault interval has occured. */ + bool demand; /* Demand mode. */ + bool booted; /* A full fault interval has occurred. */ enum cfm_fault_reason fault; /* Connectivity fault status. */ - enum cfm_fault_reason recv_fault; /* Bit mask of faults occuring on + enum cfm_fault_reason recv_fault; /* Bit mask of faults occurring on receive. */ bool opup; /* Operational State. */ bool remote_opup; /* Remote Operational State. */ @@ -121,6 +127,8 @@ struct cfm { int health_interval; /* Number of fault_intervals since health was recomputed. */ long long int last_tx; /* Last CCM transmission time. */ + + int ref_cnt; }; /* Remote MPs represent foreign network entities that are configured to have @@ -144,6 +152,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) { @@ -190,7 +210,7 @@ cfm_generate_maid(struct cfm *cfm) md_len = strlen(ovs_md_name); ma_len = strlen(ovs_ma_name); - assert(md_len && ma_len && md_len + ma_len + 4 <= CCM_MAID_LEN); + ovs_assert(md_len && ma_len && md_len + ma_len + 4 <= CCM_MAID_LEN); cfm->maid[0] = 4; /* MD name string format. */ cfm->maid[1] = md_len; /* MD name size. */ @@ -288,12 +308,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)); @@ -301,11 +322,12 @@ cfm_create(const char *name) cfm->fault_override = -1; cfm->health = -1; cfm->last_tx = 0; + cfm->ref_cnt = 1; return cfm; } void -cfm_destroy(struct cfm *cfm) +cfm_unref(struct cfm *cfm) { struct remote_mp *rmp, *rmp_next; @@ -313,6 +335,11 @@ cfm_destroy(struct cfm *cfm) return; } + ovs_assert(cfm->ref_cnt); + if (--cfm->ref_cnt) { + return; + } + HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) { hmap_remove(&cfm->remote_mps, &rmp->node); free(rmp); @@ -320,11 +347,22 @@ 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); } +struct cfm * +cfm_ref(const struct cfm *cfm_) +{ + struct cfm *cfm = CONST_CAST(struct cfm *, cfm_); + if (cfm) { + ovs_assert(cfm->ref_cnt > 0); + cfm->ref_cnt++; + } + return cfm; +} + /* Should be run periodically to update fault statistics messages. */ void cfm_run(struct cfm *cfm) @@ -333,6 +371,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; @@ -368,20 +407,29 @@ cfm_run(struct cfm *cfm) cfm->health = (rmp->num_health_ccm * 100) / exp_ccm_recvd; cfm->health = MIN(cfm->health, 100); rmp->num_health_ccm = 0; - assert(cfm->health >= 0 && cfm->health <= 100); + ovs_assert(cfm->health >= 0 && cfm->health <= 100); } cfm->health_interval = 0; } 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; @@ -466,7 +514,7 @@ cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet, } if (cfm->ccm_interval == 0) { - assert(cfm->extended); + ovs_assert(cfm->extended); ccm->interval_ms_x = htons(cfm->ccm_interval_ms); } else { ccm->interval_ms_x = htons(0); @@ -519,6 +567,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; @@ -530,10 +588,26 @@ cfm_configure(struct cfm *cfm, const struct cfm_settings *s) return true; } -/* Returns true if 'cfm' should process packets from 'flow'. */ +/* Must be called when the netdev owned by 'cfm' should change. */ +void +cfm_set_netdev(struct cfm *cfm, const struct netdev *netdev) +{ + if (cfm->netdev != netdev) { + netdev_close(cfm->netdev); + cfm->netdev = netdev_ref(netdev); + } +} + +/* Returns true if 'cfm' should process packets from 'flow'. Sets + * fields in 'wc' that were used to make the determination. */ bool -cfm_should_process_flow(const struct cfm *cfm, const struct flow *flow) +cfm_should_process_flow(const struct cfm *cfm, const struct flow *flow, + struct flow_wildcards *wc) { + memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); + if (cfm->check_tnl_key) { + memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id); + } return (ntohs(flow->dl_type) == ETH_TYPE_CFM && eth_addr_equals(flow->dl_dst, cfm_ccm_addr(cfm)) && (!cfm->check_tnl_key || flow->tunnel.tun_id == htonll(0)));