X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Flacp.c;h=49ae5e5a2d41933ef161e49e16eaae48a5c20590;hb=0ef165ecb57943e17a8ee8270df68ffb8d032e29;hp=8fd9d899b3511d18e9a0b0a3cc38e64f8941223b;hpb=cb22974d773942d66da42b700b8bca0db27a0920;p=sliver-openvswitch.git diff --git a/lib/lacp.c b/lib/lacp.c index 8fd9d899b..49ae5e5a2 100644 --- a/lib/lacp.c +++ b/lib/lacp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, 2012 Nicira, Inc. +/* Copyright (c) 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. @@ -18,12 +18,14 @@ #include +#include "connectivity.h" #include "dynamic-string.h" #include "hash.h" #include "hmap.h" #include "ofpbuf.h" #include "packets.h" #include "poll-loop.h" +#include "seq.h" #include "shash.h" #include "timer.h" #include "timeval.h" @@ -47,6 +49,7 @@ VLOG_DEFINE_THIS_MODULE(lacp); #define LACP_RX_MULTIPLIER 3 /* Multiply by TX rate to get RX rate. */ #define LACP_INFO_LEN 15 +OVS_PACKED( struct lacp_info { ovs_be16 sys_priority; /* System priority. */ uint8_t sys_id[ETH_ADDR_LEN]; /* System ID. */ @@ -54,10 +57,11 @@ struct lacp_info { ovs_be16 port_priority; /* Port priority. */ ovs_be16 port_id; /* Port ID. */ uint8_t state; /* State mask. See LACP_STATE macros. */ -} __attribute__((packed)); +}); BUILD_ASSERT_DECL(LACP_INFO_LEN == sizeof(struct lacp_info)); #define LACP_PDU_LEN 110 +OVS_PACKED( struct lacp_pdu { uint8_t subtype; /* Always 1. */ uint8_t version; /* Always 1. */ @@ -76,7 +80,7 @@ struct lacp_pdu { uint8_t collector_len; /* Always 16. */ ovs_be16 collector_delay; /* Maximum collector delay. Set to UINT16_MAX. */ uint8_t z3[64]; /* Combination of several fields. Always 0. */ -} __attribute__((packed)); +}); BUILD_ASSERT_DECL(LACP_PDU_LEN == sizeof(struct lacp_pdu)); /* Implementation. */ @@ -100,6 +104,9 @@ struct lacp { bool fast; /* True if using fast probe interval. */ bool negotiated; /* True if LACP negotiations were successful. */ bool update; /* True if lacp_update() needs to be called. */ + bool fallback_ab; /* True if fallback to active-backup on LACP failure. */ + + struct ovs_refcount ref_cnt; }; struct slave { @@ -120,18 +127,25 @@ struct slave { struct timer rx; /* Expected message receive timer. */ }; -static struct list all_lacps = LIST_INITIALIZER(&all_lacps); - -static void lacp_update_attached(struct lacp *); - -static void slave_destroy(struct slave *); -static void slave_set_defaulted(struct slave *); -static void slave_set_expired(struct slave *); -static void slave_get_actor(struct slave *, struct lacp_info *actor); -static void slave_get_priority(struct slave *, struct lacp_info *priority); -static bool slave_may_tx(const struct slave *); -static struct slave *slave_lookup(const struct lacp *, const void *slave); -static bool info_tx_equal(struct lacp_info *, struct lacp_info *); +static struct ovs_mutex mutex; +static struct list all_lacps__ = LIST_INITIALIZER(&all_lacps__); +static struct list *const all_lacps OVS_GUARDED_BY(mutex) = &all_lacps__; + +static void lacp_update_attached(struct lacp *) OVS_REQUIRES(mutex); + +static void slave_destroy(struct slave *) OVS_REQUIRES(mutex); +static void slave_set_defaulted(struct slave *) OVS_REQUIRES(mutex); +static void slave_set_expired(struct slave *) OVS_REQUIRES(mutex); +static void slave_get_actor(struct slave *, struct lacp_info *actor) + OVS_REQUIRES(mutex); +static void slave_get_priority(struct slave *, struct lacp_info *priority) + OVS_REQUIRES(mutex); +static bool slave_may_tx(const struct slave *) + OVS_REQUIRES(mutex); +static struct slave *slave_lookup(const struct lacp *, const void *slave) + OVS_REQUIRES(mutex); +static bool info_tx_equal(struct lacp_info *, struct lacp_info *) + OVS_REQUIRES(mutex); static unixctl_cb_func lacp_unixctl_show; @@ -167,7 +181,8 @@ parse_lacp_packet(const struct ofpbuf *b) { const struct lacp_pdu *pdu; - pdu = ofpbuf_at(b, (uint8_t *)b->l3 - (uint8_t *)b->data, LACP_PDU_LEN); + pdu = ofpbuf_at(b, (uint8_t *)ofpbuf_l3(b) - (uint8_t *)ofpbuf_data(b), + LACP_PDU_LEN); if (pdu && pdu->subtype == 1 && pdu->actor_type == 1 && pdu->actor_len == 20 @@ -190,23 +205,44 @@ lacp_init(void) /* Creates a LACP object. */ struct lacp * -lacp_create(void) +lacp_create(void) OVS_EXCLUDED(mutex) { + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; struct lacp *lacp; + if (ovsthread_once_start(&once)) { + ovs_mutex_init_recursive(&mutex); + ovsthread_once_done(&once); + } + lacp = xzalloc(sizeof *lacp); hmap_init(&lacp->slaves); - list_push_back(&all_lacps, &lacp->node); + ovs_refcount_init(&lacp->ref_cnt); + + ovs_mutex_lock(&mutex); + list_push_back(all_lacps, &lacp->node); + ovs_mutex_unlock(&mutex); + return lacp; +} + +struct lacp * +lacp_ref(const struct lacp *lacp_) +{ + struct lacp *lacp = CONST_CAST(struct lacp *, lacp_); + if (lacp) { + ovs_refcount_ref(&lacp->ref_cnt); + } return lacp; } /* Destroys 'lacp' and its slaves. Does nothing if 'lacp' is NULL. */ void -lacp_destroy(struct lacp *lacp) +lacp_unref(struct lacp *lacp) OVS_EXCLUDED(mutex) { - if (lacp) { + if (lacp && ovs_refcount_unref(&lacp->ref_cnt) == 1) { struct slave *slave, *next; + ovs_mutex_lock(&mutex); HMAP_FOR_EACH_SAFE (slave, next, node, &lacp->slaves) { slave_destroy(slave); } @@ -215,15 +251,18 @@ lacp_destroy(struct lacp *lacp) list_remove(&lacp->node); free(lacp->name); free(lacp); + ovs_mutex_unlock(&mutex); } } /* Configures 'lacp' with settings from 's'. */ void lacp_configure(struct lacp *lacp, const struct lacp_settings *s) + OVS_EXCLUDED(mutex) { ovs_assert(!eth_addr_is_zero(s->id)); + ovs_mutex_lock(&mutex); if (!lacp->name || strcmp(s->name, lacp->name)) { free(lacp->name); lacp->name = xstrdup(s->name); @@ -238,14 +277,25 @@ lacp_configure(struct lacp *lacp, const struct lacp_settings *s) lacp->active = s->active; lacp->fast = s->fast; + + if (lacp->fallback_ab != s->fallback_ab_cfg) { + lacp->fallback_ab = s->fallback_ab_cfg; + lacp->update = true; + } + + ovs_mutex_unlock(&mutex); } /* Returns true if 'lacp' is configured in active mode, false if 'lacp' is * configured for passive mode. */ bool -lacp_is_active(const struct lacp *lacp) +lacp_is_active(const struct lacp *lacp) OVS_EXCLUDED(mutex) { - return lacp->active; + bool ret; + ovs_mutex_lock(&mutex); + ret = lacp->active; + ovs_mutex_unlock(&mutex); + return ret; } /* Processes 'packet' which was received on 'slave_'. This function should be @@ -254,16 +304,23 @@ lacp_is_active(const struct lacp *lacp) void lacp_process_packet(struct lacp *lacp, const void *slave_, const struct ofpbuf *packet) + OVS_EXCLUDED(mutex) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - struct slave *slave = slave_lookup(lacp, slave_); const struct lacp_pdu *pdu; long long int tx_rate; + struct slave *slave; + + ovs_mutex_lock(&mutex); + slave = slave_lookup(lacp, slave_); + if (!slave) { + goto out; + } pdu = parse_lacp_packet(packet); if (!pdu) { VLOG_WARN_RL(&rl, "%s: received an unparsable LACP PDU.", lacp->name); - return; + goto out; } slave->status = LACP_CURRENT; @@ -279,18 +336,26 @@ lacp_process_packet(struct lacp *lacp, const void *slave_, lacp->update = true; slave->partner = pdu->actor; } + +out: + ovs_mutex_unlock(&mutex); } /* Returns the lacp_status of the given 'lacp' object (which may be NULL). */ enum lacp_status -lacp_status(const struct lacp *lacp) +lacp_status(const struct lacp *lacp) OVS_EXCLUDED(mutex) { - if (!lacp) { - return LACP_DISABLED; - } else if (lacp->negotiated) { - return LACP_NEGOTIATED; + if (lacp) { + enum lacp_status ret; + + ovs_mutex_lock(&mutex); + ret = lacp->negotiated ? LACP_NEGOTIATED : LACP_CONFIGURED; + ovs_mutex_unlock(&mutex); + return ret; } else { - return LACP_CONFIGURED; + /* Don't take 'mutex'. It might not even be initialized, since we + * don't know that any lacp object has been created. */ + return LACP_DISABLED; } } @@ -300,9 +365,12 @@ lacp_status(const struct lacp *lacp) void lacp_slave_register(struct lacp *lacp, void *slave_, const struct lacp_slave_settings *s) + OVS_EXCLUDED(mutex) { - struct slave *slave = slave_lookup(lacp, slave_); + struct slave *slave; + ovs_mutex_lock(&mutex); + slave = slave_lookup(lacp, slave_); if (!slave) { slave = xzalloc(sizeof *slave); slave->lacp = lacp; @@ -333,40 +401,58 @@ lacp_slave_register(struct lacp *lacp, void *slave_, slave_set_expired(slave); } } + ovs_mutex_unlock(&mutex); } /* Unregisters 'slave_' with 'lacp'. */ void lacp_slave_unregister(struct lacp *lacp, const void *slave_) + OVS_EXCLUDED(mutex) { - struct slave *slave = slave_lookup(lacp, slave_); + struct slave *slave; + ovs_mutex_lock(&mutex); + slave = slave_lookup(lacp, slave_); if (slave) { slave_destroy(slave); lacp->update = true; } + ovs_mutex_unlock(&mutex); } /* This function should be called whenever the carrier status of 'slave_' has * changed. If 'lacp' is null, this function has no effect.*/ void lacp_slave_carrier_changed(const struct lacp *lacp, const void *slave_) + OVS_EXCLUDED(mutex) { - if (lacp) { - struct slave *slave = slave_lookup(lacp, slave_); + struct slave *slave; + if (!lacp) { + return; + } - if (slave->status == LACP_CURRENT || slave->lacp->active) { - slave_set_expired(slave); - } + ovs_mutex_lock(&mutex); + slave = slave_lookup(lacp, slave_); + if (!slave) { + goto out; } + + if (slave->status == LACP_CURRENT || slave->lacp->active) { + slave_set_expired(slave); + } + +out: + ovs_mutex_unlock(&mutex); } static bool -slave_may_enable__(struct slave *slave) +slave_may_enable__(struct slave *slave) OVS_REQUIRES(mutex) { /* The slave may be enabled if it's attached to an aggregator and its * partner is synchronized.*/ - return slave->attached && (slave->partner.state & LACP_STATE_SYNC); + return slave->attached && (slave->partner.state & LACP_STATE_SYNC + || (slave->lacp && slave->lacp->fallback_ab + && slave->status == LACP_DEFAULTED)); } /* This function should be called before enabling 'slave_' to send or receive @@ -374,44 +460,58 @@ slave_may_enable__(struct slave *slave) * convenience, returns true if 'lacp' is NULL. */ bool lacp_slave_may_enable(const struct lacp *lacp, const void *slave_) + OVS_EXCLUDED(mutex) { if (lacp) { - return slave_may_enable__(slave_lookup(lacp, slave_)); + struct slave *slave; + bool ret; + + ovs_mutex_lock(&mutex); + slave = slave_lookup(lacp, slave_); + ret = slave ? slave_may_enable__(slave) : false; + ovs_mutex_unlock(&mutex); + return ret; } else { return true; } } -/* Returns the port ID used for 'slave_' in LACP communications. */ -uint16_t -lacp_slave_get_port_id(const struct lacp *lacp, const void *slave_) -{ - struct slave *slave = slave_lookup(lacp, slave_); - return slave->port_id; -} - /* Returns true if partner information on 'slave_' is up to date. 'slave_' * not being current, generally indicates a connectivity problem, or a * misconfigured (or broken) partner. */ bool lacp_slave_is_current(const struct lacp *lacp, const void *slave_) + OVS_EXCLUDED(mutex) { - return slave_lookup(lacp, slave_)->status != LACP_DEFAULTED; + struct slave *slave; + bool ret; + + ovs_mutex_lock(&mutex); + slave = slave_lookup(lacp, slave_); + ret = slave ? slave->status != LACP_DEFAULTED : false; + ovs_mutex_unlock(&mutex); + return ret; } /* This function should be called periodically to update 'lacp'. */ void -lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) +lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) OVS_EXCLUDED(mutex) { struct slave *slave; + ovs_mutex_lock(&mutex); HMAP_FOR_EACH (slave, node, &lacp->slaves) { if (timer_expired(&slave->rx)) { + enum slave_status old_status = slave->status; + if (slave->status == LACP_CURRENT) { slave_set_expired(slave); } else if (slave->status == LACP_EXPIRED) { slave_set_defaulted(slave); } + if (slave->status != old_status) { + seq_change(connectivity_seq_get()); + } } } @@ -442,16 +542,19 @@ lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) : LACP_SLOW_TIME_TX); timer_set_duration(&slave->tx, duration); + seq_change(connectivity_seq_get()); } } + ovs_mutex_unlock(&mutex); } /* Causes poll_block() to wake up when lacp_run() needs to be called again. */ void -lacp_wait(struct lacp *lacp) +lacp_wait(struct lacp *lacp) OVS_EXCLUDED(mutex) { struct slave *slave; + ovs_mutex_lock(&mutex); HMAP_FOR_EACH (slave, node, &lacp->slaves) { if (slave_may_tx(slave)) { timer_wait(&slave->tx); @@ -461,6 +564,7 @@ lacp_wait(struct lacp *lacp) timer_wait(&slave->rx); } } + ovs_mutex_unlock(&mutex); } /* Static Helpers. */ @@ -468,7 +572,7 @@ lacp_wait(struct lacp *lacp) /* Updates the attached status of all slaves controlled by 'lacp' and sets its * negotiated parameter to true if any slaves are attachable. */ static void -lacp_update_attached(struct lacp *lacp) +lacp_update_attached(struct lacp *lacp) OVS_REQUIRES(mutex) { struct slave *lead, *slave; struct lacp_info lead_pri; @@ -491,6 +595,9 @@ lacp_update_attached(struct lacp *lacp) } if (slave->status == LACP_DEFAULTED) { + if (lacp->fallback_ab) { + slave->attached = true; + } continue; } @@ -507,7 +614,8 @@ lacp_update_attached(struct lacp *lacp) if (lead) { HMAP_FOR_EACH (slave, node, &lacp->slaves) { - if (lead->partner.key != slave->partner.key + if ((lacp->fallback_ab && slave->status == LACP_DEFAULTED) + || lead->partner.key != slave->partner.key || !eth_addr_equals(lead->partner.sys_id, slave->partner.sys_id)) { slave->attached = false; @@ -517,7 +625,7 @@ lacp_update_attached(struct lacp *lacp) } static void -slave_destroy(struct slave *slave) +slave_destroy(struct slave *slave) OVS_REQUIRES(mutex) { if (slave) { struct lacp *lacp = slave->lacp; @@ -541,7 +649,7 @@ slave_destroy(struct slave *slave) } static void -slave_set_defaulted(struct slave *slave) +slave_set_defaulted(struct slave *slave) OVS_REQUIRES(mutex) { memset(&slave->partner, 0, sizeof slave->partner); @@ -550,7 +658,7 @@ slave_set_defaulted(struct slave *slave) } static void -slave_set_expired(struct slave *slave) +slave_set_expired(struct slave *slave) OVS_REQUIRES(mutex) { slave->status = LACP_EXPIRED; slave->partner.state |= LACP_STATE_TIME; @@ -561,6 +669,7 @@ slave_set_expired(struct slave *slave) static void slave_get_actor(struct slave *slave, struct lacp_info *actor) + OVS_REQUIRES(mutex) { struct lacp *lacp = slave->lacp; uint16_t key; @@ -613,6 +722,7 @@ slave_get_actor(struct slave *slave, struct lacp_info *actor) * link. */ static void slave_get_priority(struct slave *slave, struct lacp_info *priority) + OVS_REQUIRES(mutex) { uint16_t partner_priority, actor_priority; @@ -637,13 +747,13 @@ slave_get_priority(struct slave *slave, struct lacp_info *priority) } static bool -slave_may_tx(const struct slave *slave) +slave_may_tx(const struct slave *slave) OVS_REQUIRES(mutex) { return slave->lacp->active || slave->status != LACP_DEFAULTED; } static struct slave * -slave_lookup(const struct lacp *lacp, const void *slave_) +slave_lookup(const struct lacp *lacp, const void *slave_) OVS_REQUIRES(mutex) { struct slave *slave; @@ -680,11 +790,11 @@ info_tx_equal(struct lacp_info *a, struct lacp_info *b) } static struct lacp * -lacp_find(const char *name) +lacp_find(const char *name) OVS_REQUIRES(mutex) { struct lacp *lacp; - LIST_FOR_EACH (lacp, node, &all_lacps) { + LIST_FOR_EACH (lacp, node, all_lacps) { if (!strcmp(lacp->name, name)) { return lacp; } @@ -730,7 +840,7 @@ ds_put_lacp_state(struct ds *ds, uint8_t state) } static void -lacp_print_details(struct ds *ds, struct lacp *lacp) +lacp_print_details(struct ds *ds, struct lacp *lacp) OVS_REQUIRES(mutex) { struct shash slave_shash = SHASH_INITIALIZER(&slave_shash); const struct shash_node **sorted_slaves = NULL; @@ -786,7 +896,7 @@ lacp_print_details(struct ds *ds, struct lacp *lacp) status = "defaulted"; break; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } ds_put_format(ds, "\nslave: %s: %s %s\n", slave->name, status, @@ -831,24 +941,28 @@ lacp_print_details(struct ds *ds, struct lacp *lacp) static void lacp_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[], - void *aux OVS_UNUSED) + void *aux OVS_UNUSED) OVS_EXCLUDED(mutex) { struct ds ds = DS_EMPTY_INITIALIZER; struct lacp *lacp; + ovs_mutex_lock(&mutex); if (argc > 1) { lacp = lacp_find(argv[1]); if (!lacp) { unixctl_command_reply_error(conn, "no such lacp object"); - return; + goto out; } lacp_print_details(&ds, lacp); } else { - LIST_FOR_EACH (lacp, node, &all_lacps) { + LIST_FOR_EACH (lacp, node, all_lacps) { lacp_print_details(&ds, lacp); } } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); + +out: + ovs_mutex_unlock(&mutex); }