This commit adds call-back function to the tunnel monitoring protocal bfd/cfm to allow the session to notify the ofproto-dpif whenever its status changes. This helps eliminate the overhead of updating unchanged session status to the database.
Signed-off-by: Alex Wang <al...@nicira.com> --- lib/bfd.c | 24 ++++++++++---- lib/bfd.h | 5 ++- lib/cfm.c | 22 +++++++++--- lib/cfm.h | 4 ++- ofproto/ofproto-dpif.c | 79 ++++++++++++++++++++++++++++++++++++-------- ofproto/ofproto-provider.h | 19 ++++++----- ofproto/ofproto.c | 18 +++++----- ofproto/ofproto.h | 6 ++-- vswitchd/bridge.c | 16 ++++++--- 9 files changed, 143 insertions(+), 50 deletions(-) diff --git a/lib/bfd.c b/lib/bfd.c index 8bfe385..e7c09f7 100644 --- a/lib/bfd.c +++ b/lib/bfd.c @@ -21,7 +21,6 @@ #include <netinet/ip.h> #include "byte-order.h" -#include "connectivity.h" #include "csum.h" #include "dpif.h" #include "dynamic-string.h" @@ -37,7 +36,6 @@ #include "packets.h" #include "poll-loop.h" #include "random.h" -#include "seq.h" #include "smap.h" #include "timeval.h" #include "unaligned.h" @@ -213,6 +211,13 @@ struct bfd { long long int decay_detect_time; /* Decay detection time. */ uint64_t flap_count; /* Counts bfd forwarding flaps. */ + + /* When the variables returned by bfd_get_status() are changed, + * the status_chang_cb() should be called to signal the upper + * level owner of the session. 'aux' serves as an identifier + * to the upper level. */ + void (*status_change_cb)(void *aux); + void *aux; }; static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; @@ -307,7 +312,8 @@ bfd_get_status(const struct bfd *bfd, struct smap *smap) * Also returns NULL if cfg is NULL. */ struct bfd * bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg, - struct netdev *netdev) OVS_EXCLUDED(mutex) + struct netdev *netdev, void (*status_change_cb)(void *aux), + void *aux) OVS_EXCLUDED(mutex) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static atomic_uint16_t udp_src = ATOMIC_VAR_INIT(0); @@ -351,6 +357,8 @@ bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg, bfd->rx_packets = bfd_rx_packets(bfd); bfd->in_decay = false; bfd->flap_count = 0; + bfd->status_change_cb = status_change_cb; + bfd->aux = aux; /* RFC 5881 section 4 * The source port MUST be in the range 49152 through 65535. The same @@ -363,6 +371,7 @@ bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg, bfd_set_state(bfd, STATE_DOWN, DIAG_NONE); memcpy(bfd->eth_dst, eth_addr_bfd, ETH_ADDR_LEN); + bfd->status_change_cb(bfd->aux); } atomic_store(&bfd->check_tnl_key, @@ -467,6 +476,7 @@ bfd_unref(struct bfd *bfd) OVS_EXCLUDED(mutex) { if (bfd && ovs_refcount_unref(&bfd->ref_cnt) == 1) { ovs_mutex_lock(&mutex); + bfd->status_change_cb(bfd->aux); hmap_remove(all_bfds, &bfd->node); netdev_close(bfd->netdev); free(bfd->name); @@ -749,7 +759,7 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow, } if (bfd->rmt_state != rmt_state) { - seq_change(connectivity_seq_get()); + bfd->status_change_cb(bfd->aux); } bfd->rmt_disc = ntohl(msg->my_disc); @@ -872,7 +882,7 @@ bfd_forwarding__(struct bfd *bfd) OVS_REQUIRES(mutex) && bfd->rmt_diag != DIAG_RCPATH_DOWN; if (bfd->last_forwarding != last_forwarding) { bfd->flap_count++; - seq_change(connectivity_seq_get()); + bfd->status_change_cb(bfd->aux); } return bfd->last_forwarding; } @@ -1085,7 +1095,7 @@ bfd_set_state(struct bfd *bfd, enum state state, enum diag diag) bfd_decay_update(bfd); } - seq_change(connectivity_seq_get()); + bfd->status_change_cb(bfd->aux); } } @@ -1279,9 +1289,11 @@ bfd_unixctl_set_forwarding_override(struct unixctl_conn *conn, int argc, goto out; } bfd->forwarding_override = forwarding_override; + bfd->status_change_cb(bfd->aux); } else { HMAP_FOR_EACH (bfd, node, all_bfds) { bfd->forwarding_override = forwarding_override; + bfd->status_change_cb(bfd->aux); } } diff --git a/lib/bfd.h b/lib/bfd.h index 4e7d4cb..a6c4d36 100644 --- a/lib/bfd.h +++ b/lib/bfd.h @@ -43,7 +43,10 @@ void bfd_process_packet(struct bfd *, const struct flow *, struct bfd *bfd_configure(struct bfd *, const char *name, const struct smap *smap, - struct netdev *netdev); + struct netdev *netdev, + void (*status_change_cb)(void *aux), + void *aux); + struct bfd *bfd_ref(const struct bfd *); void bfd_unref(struct bfd *); diff --git a/lib/cfm.c b/lib/cfm.c index ce0c471..f3bbc77 100644 --- a/lib/cfm.c +++ b/lib/cfm.c @@ -22,7 +22,6 @@ #include <string.h> #include "byte-order.h" -#include "connectivity.h" #include "dynamic-string.h" #include "flow.h" #include "hash.h" @@ -32,7 +31,6 @@ #include "packets.h" #include "poll-loop.h" #include "random.h" -#include "seq.h" #include "timer.h" #include "timeval.h" #include "unixctl.h" @@ -133,6 +131,13 @@ struct cfm { struct ovs_refcount ref_cnt; uint64_t flap_count; /* Count the flaps since boot. */ + + /* When the variables returned by cfm_get_*() are changed, + * the status_chang_cb() should be called to signal the upper + * level owner of the session. 'aux' serves as an identifier + * to the upper level. */ + void (*status_change_cb)(void *aux); + void *aux; }; /* Remote MPs represent foreign network entities that are configured to have @@ -322,7 +327,8 @@ cfm_init(void) /* Allocates a 'cfm' object called 'name'. 'cfm' should be initialized by * cfm_configure() before use. */ struct cfm * -cfm_create(const struct netdev *netdev) OVS_EXCLUDED(mutex) +cfm_create(const struct netdev *netdev, void (*status_change_cb)(void *aux), + void *aux) OVS_EXCLUDED(mutex) { struct cfm *cfm; @@ -338,11 +344,15 @@ cfm_create(const struct netdev *netdev) OVS_EXCLUDED(mutex) atomic_init(&cfm->extended, false); atomic_init(&cfm->check_tnl_key, false); ovs_refcount_init(&cfm->ref_cnt); + cfm->status_change_cb = status_change_cb; + cfm->aux = aux; ovs_mutex_lock(&mutex); cfm_generate_maid(cfm); hmap_insert(all_cfms, &cfm->hmap_node, hash_string(cfm->name, 0)); ovs_mutex_unlock(&mutex); + + cfm->status_change_cb(cfm->aux); return cfm; } @@ -368,6 +378,7 @@ cfm_unref(struct cfm *cfm) OVS_EXCLUDED(mutex) free(rmp); } + cfm->status_change_cb(cfm->aux); hmap_destroy(&cfm->remote_mps); netdev_close(cfm->netdev); free(cfm->rmps_array); @@ -510,7 +521,7 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex) || (old_rmps_array_len != cfm->rmps_array_len || old_rmps_deleted) || old_cfm_fault != cfm->fault || old_flap_count != cfm->flap_count) { - seq_change(connectivity_seq_get()); + cfm->status_change_cb(cfm->aux); } cfm->booted = true; @@ -1029,13 +1040,14 @@ cfm_unixctl_set_fault(struct unixctl_conn *conn, int argc, const char *argv[], goto out; } cfm->fault_override = fault_override; + cfm->status_change_cb(cfm->aux); } else { HMAP_FOR_EACH (cfm, hmap_node, all_cfms) { cfm->fault_override = fault_override; + cfm->status_change_cb(cfm->aux); } } - seq_change(connectivity_seq_get()); unixctl_command_reply(conn, "OK"); out: diff --git a/lib/cfm.h b/lib/cfm.h index 4213eb5..3fda809 100644 --- a/lib/cfm.h +++ b/lib/cfm.h @@ -64,7 +64,9 @@ struct cfm_settings { }; void cfm_init(void); -struct cfm *cfm_create(const struct netdev *); +struct cfm *cfm_create(const struct netdev *, + void (*status_change_cb)(void *aux), + void *aux); struct cfm *cfm_ref(const struct cfm *); void cfm_unref(struct cfm *); void cfm_run(struct cfm *); diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index f72d53e..f48bd58 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -145,11 +145,14 @@ struct ofport_dpif { struct hmap_node odp_port_node; /* In dpif_backer's "odp_to_ofport_map". */ struct ofport up; + struct ovs_mutex status_mutex; /* Just protects the *_status_changed. */ odp_port_t odp_port; struct ofbundle *bundle; /* Bundle that contains this port, if any. */ struct list bundle_node; /* In struct ofbundle's "ports" list. */ struct cfm *cfm; /* Connectivity Fault Management, if any. */ struct bfd *bfd; /* BFD, if any. */ + bool cfm_status_changed; /* Set when cfm status needs to be updated. */ + bool bfd_status_changed; /* Set when bfd status needs to be updated. */ bool may_enable; /* May be enabled in bonds. */ bool is_tunnel; /* This port is a tunnel. */ bool is_layer3; /* This is a layer 3 port. */ @@ -205,6 +208,8 @@ ofport_dpif_cast(const struct ofport *ofport) } static void port_run(struct ofport_dpif *); +static void cfm_status_changed_cb(void *); +static void bfd_status_changed_cb(void *); static int set_bfd(struct ofport *, const struct smap *); static int set_cfm(struct ofport *, const struct cfm_settings *); static void ofport_update_peer(struct ofport_dpif *); @@ -1465,9 +1470,12 @@ port_construct(struct ofport *port_) int error; ofproto->backer->need_revalidate = REV_RECONFIGURE; + ovs_mutex_init(&port->status_mutex); port->bundle = NULL; port->cfm = NULL; port->bfd = NULL; + port->cfm_status_changed = false; + port->bfd_status_changed = false; port->may_enable = true; port->stp_port = NULL; port->stp_state = STP_DISABLED; @@ -1571,6 +1579,7 @@ port_destruct(struct ofport *port_) bundle_remove(port_); set_cfm(port_, NULL); set_bfd(port_, NULL); + ovs_mutex_destroy(&port->status_mutex); if (port->stp_port) { stp_port_disable(port->stp_port); } @@ -1699,7 +1708,8 @@ set_cfm(struct ofport *ofport_, const struct cfm_settings *s) ofproto = ofproto_dpif_cast(ofport->up.ofproto); ofproto->backer->need_revalidate = REV_RECONFIGURE; - ofport->cfm = cfm_create(ofport->up.netdev); + ofport->cfm = cfm_create(ofport->up.netdev, cfm_status_changed_cb, + ofport); } if (cfm_configure(ofport->cfm, s)) { @@ -1717,22 +1727,43 @@ out: return error; } -static bool +static void +cfm_status_changed_cb(void *aux) +{ + struct ofport_dpif *port = aux; + + ovs_mutex_lock(&port->status_mutex); + port->cfm_status_changed = true; + seq_change(connectivity_seq_get()); + ovs_mutex_unlock(&port->status_mutex); +} + +static int get_cfm_status(const struct ofport *ofport_, struct ofproto_cfm_status *status) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + int ret; + ovs_mutex_lock(&ofport->status_mutex); if (ofport->cfm) { - status->faults = cfm_get_fault(ofport->cfm); - status->flap_count = cfm_get_flap_count(ofport->cfm); - status->remote_opstate = cfm_get_opup(ofport->cfm); - status->health = cfm_get_health(ofport->cfm); - cfm_get_remote_mpids(ofport->cfm, &status->rmps, &status->n_rmps); - return true; + if (ofport->cfm_status_changed) { + status->faults = cfm_get_fault(ofport->cfm); + status->flap_count = cfm_get_flap_count(ofport->cfm); + status->remote_opstate = cfm_get_opup(ofport->cfm); + status->health = cfm_get_health(ofport->cfm); + cfm_get_remote_mpids(ofport->cfm, &status->rmps, &status->n_rmps); + ofport->cfm_status_changed = false; + ret = 0; + } else { + ret = -1; + } } else { - return false; + ret = 1; } + ovs_mutex_unlock(&ofport->status_mutex); + + return ret; } static int @@ -1744,7 +1775,8 @@ set_bfd(struct ofport *ofport_, const struct smap *cfg) old = ofport->bfd; ofport->bfd = bfd_configure(old, netdev_get_name(ofport->up.netdev), - cfg, ofport->up.netdev); + cfg, ofport->up.netdev, bfd_status_changed_cb, + ofport); if (ofport->bfd != old) { ofproto->backer->need_revalidate = REV_RECONFIGURE; } @@ -1753,17 +1785,38 @@ set_bfd(struct ofport *ofport_, const struct smap *cfg) return 0; } +static void +bfd_status_changed_cb(void *aux) +{ + struct ofport_dpif *port = aux; + + ovs_mutex_lock(&port->status_mutex); + port->bfd_status_changed = true; + seq_change(connectivity_seq_get()); + ovs_mutex_unlock(&port->status_mutex); +} + static int get_bfd_status(struct ofport *ofport_, struct smap *smap) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + int ret; + ovs_mutex_lock(&ofport->status_mutex); if (ofport->bfd) { - bfd_get_status(ofport->bfd, smap); - return 0; + if (ofport->bfd_status_changed) { + bfd_get_status(ofport->bfd, smap); + ofport->bfd_status_changed = false; + ret = 0; + } else { + ret = -1; + } } else { - return ENOENT; + ret = ENOENT; } + ovs_mutex_unlock(&ofport->status_mutex); + + return ret; } /* Spanning Tree. */ diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index adf080f..a71c083 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -1459,15 +1459,16 @@ struct ofproto_class { * support CFM, as does a null pointer. */ int (*set_cfm)(struct ofport *ofport, const struct cfm_settings *s); - /* Checks the status of CFM configured on 'ofport'. Returns true if the - * port's CFM status was successfully stored into '*status'. Returns false - * if the port did not have CFM configured, in which case '*status' is - * indeterminate. + /* Checks the status of CFM configured on 'ofport'. Returns 0 if the + * port's CFM status was successfully stored into '*status'. Returns + * positive number if the port did not have CFM configured, in which + * case '*status' is indeterminate. And returns negative number if there + * is no status change since last update. * * The caller must provide and owns '*status', but it does not own and must * not modify or free the array returned in 'status->rmps'. */ - bool (*get_cfm_status)(const struct ofport *ofport, - struct ofproto_cfm_status *status); + int (*get_cfm_status)(const struct ofport *ofport, + struct ofproto_cfm_status *status); /* Configures BFD on 'ofport'. * @@ -1480,8 +1481,10 @@ struct ofproto_class { int (*set_bfd)(struct ofport *ofport, const struct smap *cfg); /* Populates 'smap' with the status of BFD on 'ofport'. Returns 0 on - * success, or a positive errno. EOPNOTSUPP as a return value indicates - * that this ofproto_class does not support BFD, as does a null pointer. */ + * success. Returns a negative errno if there is no status change since + * last update. Returns a positive errno otherwise. EOPNOTSUPP as a + * return value indicates that this ofproto_class does not support BFD, + * as does a null pointer. */ int (*get_bfd_status)(struct ofport *ofport, struct smap *smap); /* Configures spanning tree protocol (STP) on 'ofproto' using the diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index cd1ec27..ccd2f07 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -1017,7 +1017,7 @@ ofproto_port_set_bfd(struct ofproto *ofproto, ofp_port_t ofp_port, /* Populates 'status' with key value pairs indicating the status of the BFD * session on 'ofp_port'. This information is intended to be populated in the - * OVS database. Has no effect if 'ofp_port' is not na OpenFlow port in + * OVS database. Has no effect if 'ofp_port' is not an OpenFlow port in * 'ofproto'. */ int ofproto_port_get_bfd_status(struct ofproto *ofproto, ofp_port_t ofp_port, @@ -3684,20 +3684,20 @@ ofproto_get_netflow_ids(const struct ofproto *ofproto, ofproto->ofproto_class->get_netflow_ids(ofproto, engine_type, engine_id); } -/* Checks the status of CFM configured on 'ofp_port' within 'ofproto'. Returns - * true if the port's CFM status was successfully stored into '*status'. - * Returns false if the port did not have CFM configured, in which case - * '*status' is indeterminate. +/* Checks the status of CFM configured on 'ofp_port' within 'ofproto'. + * Returns 0 if the port's CFM status was successfully stored into + * '*status'. Returns positive number if the port did not have CFM + * configured, in which case '*status' is indeterminate. Returns + * negative number if there is no status change since last update. * * The caller must provide and owns '*status', and must free 'status->rmps'. */ -bool +int ofproto_port_get_cfm_status(const struct ofproto *ofproto, ofp_port_t ofp_port, struct ofproto_cfm_status *status) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); - return (ofport - && ofproto->ofproto_class->get_cfm_status - && ofproto->ofproto_class->get_cfm_status(ofport, status)); + return (ofport && ofproto->ofproto_class->get_cfm_status ? + ofproto->ofproto_class->get_cfm_status(ofport, status) : 1); } static enum ofperr diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h index 3f3557c..fafb128 100644 --- a/ofproto/ofproto.h +++ b/ofproto/ofproto.h @@ -418,9 +418,9 @@ struct ofproto_cfm_status { size_t n_rmps; }; -bool ofproto_port_get_cfm_status(const struct ofproto *, - ofp_port_t ofp_port, - struct ofproto_cfm_status *); +int ofproto_port_get_cfm_status(const struct ofproto *, + ofp_port_t ofp_port, + struct ofproto_cfm_status *); /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 24b3602..1842e49 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -1835,9 +1835,14 @@ iface_refresh_cfm_stats(struct iface *iface) { const struct ovsrec_interface *cfg = iface->cfg; struct ofproto_cfm_status status; + int error; - if (!ofproto_port_get_cfm_status(iface->port->bridge->ofproto, - iface->ofp_port, &status)) { + error = ofproto_port_get_cfm_status(iface->port->bridge->ofproto, + iface->ofp_port, &status); + /* Do nothing if there is no status change since last update. */ + if (error < 0) { + // PASS. + } else if (error > 0) { ovsrec_interface_set_cfm_fault(cfg, NULL, 0); ovsrec_interface_set_cfm_fault_status(cfg, NULL, 0); ovsrec_interface_set_cfm_remote_opstate(cfg, NULL); @@ -2245,8 +2250,11 @@ instant_stats_run(void) iface_refresh_cfm_stats(iface); smap_init(&smap); - ofproto_port_get_bfd_status(br->ofproto, iface->ofp_port, - &smap); + error = ofproto_port_get_bfd_status(br->ofproto, iface->ofp_port, + &smap); + if (error < 0) { + continue; + } ovsrec_interface_set_bfd_status(iface->cfg, &smap); smap_destroy(&smap); } -- 1.7.9.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev