X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Flacp.c;h=49ae5e5a2d41933ef161e49e16eaae48a5c20590;hb=HEAD;hp=eaf01c3c909c62bae294d14c976636f7f3022239;hpb=b6466afa85a9960f9424089f41c1a6e14009c994;p=sliver-openvswitch.git diff --git a/lib/lacp.c b/lib/lacp.c index eaf01c3c9..49ae5e5a2 100644 --- a/lib/lacp.c +++ b/lib/lacp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011 Nicira Networks +/* 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. @@ -16,15 +16,17 @@ #include #include "lacp.h" -#include #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" #include "unixctl.h" @@ -32,6 +34,57 @@ VLOG_DEFINE_THIS_MODULE(lacp); +/* Masks for lacp_info state member. */ +#define LACP_STATE_ACT 0x01 /* Activity. Active or passive? */ +#define LACP_STATE_TIME 0x02 /* Timeout. Short or long timeout? */ +#define LACP_STATE_AGG 0x04 /* Aggregation. Is the link is bondable? */ +#define LACP_STATE_SYNC 0x08 /* Synchronization. Is the link in up to date? */ +#define LACP_STATE_COL 0x10 /* Collecting. Is the link receiving frames? */ +#define LACP_STATE_DIST 0x20 /* Distributing. Is the link sending frames? */ +#define LACP_STATE_DEF 0x40 /* Defaulted. Using default partner info? */ +#define LACP_STATE_EXP 0x80 /* Expired. Using expired partner info? */ + +#define LACP_FAST_TIME_TX 1000 /* Fast transmission rate. */ +#define LACP_SLOW_TIME_TX 30000 /* Slow transmission rate. */ +#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. */ + ovs_be16 key; /* Operational key. */ + ovs_be16 port_priority; /* Port priority. */ + ovs_be16 port_id; /* Port ID. */ + uint8_t state; /* State mask. See LACP_STATE macros. */ +}); +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. */ + + uint8_t actor_type; /* Always 1. */ + uint8_t actor_len; /* Always 20. */ + struct lacp_info actor; /* LACP actor information. */ + uint8_t z1[3]; /* Reserved. Always 0. */ + + uint8_t partner_type; /* Always 2. */ + uint8_t partner_len; /* Always 20. */ + struct lacp_info partner; /* LACP partner information. */ + uint8_t z2[3]; /* Reserved. Always 0. */ + + uint8_t collector_type; /* Always 3. */ + 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. */ +}); +BUILD_ASSERT_DECL(LACP_PDU_LEN == sizeof(struct lacp_pdu)); + +/* Implementation. */ + enum slave_status { LACP_CURRENT, /* Current State. Partner up to date. */ LACP_EXPIRED, /* Expired State. Partner out of date. */ @@ -48,11 +101,12 @@ struct lacp { struct hmap slaves; /* Slaves this LACP object controls. */ struct slave *key_slave; /* Slave whose ID will be the aggregation key. */ - enum lacp_time lacp_time; /* Fast, Slow or Custom LACP time. */ - long long int custom_time; /* LACP_TIME_CUSTOM transmission rate. */ + 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 heartbeat; /* LACP heartbeat mode. */ + bool fallback_ab; /* True if fallback to active-backup on LACP failure. */ + + struct ovs_refcount ref_cnt; }; struct slave { @@ -73,24 +127,30 @@ struct slave { struct timer rx; /* Expected message receive timer. */ }; -static struct list all_lacps = LIST_INITIALIZER(&all_lacps); +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 *); +static void lacp_update_attached(struct lacp *) OVS_REQUIRES(mutex); -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 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 void lacp_unixctl_show(struct unixctl_conn *, const char *args, - void *aux); +static unixctl_cb_func lacp_unixctl_show; /* Populates 'pdu' with a LACP PDU comprised of 'actor' and 'partner'. */ -void +static void compose_lacp_pdu(const struct lacp_info *actor, const struct lacp_info *partner, struct lacp_pdu *pdu) { @@ -116,12 +176,13 @@ compose_lacp_pdu(const struct lacp_info *actor, * returns NULL if 'b' is malformed, or does not represent a LACP PDU format * supported by OVS. Otherwise, it returns a pointer to the lacp_pdu contained * within 'b'. */ -const struct lacp_pdu * +static const struct lacp_pdu * 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 @@ -138,28 +199,50 @@ parse_lacp_packet(const struct ofpbuf *b) void lacp_init(void) { - unixctl_command_register("lacp/show", lacp_unixctl_show, NULL); + unixctl_command_register("lacp/show", "[port]", 0, 1, + lacp_unixctl_show, NULL); } /* 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); } @@ -168,64 +251,80 @@ 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); } if (!eth_addr_equals(lacp->sys_id, s->id) - || lacp->sys_priority != s->priority - || lacp->heartbeat != s->heartbeat) { + || lacp->sys_priority != s->priority) { memcpy(lacp->sys_id, s->id, ETH_ADDR_LEN); lacp->sys_priority = s->priority; - lacp->heartbeat = s->heartbeat; lacp->update = true; } lacp->active = s->active; - lacp->lacp_time = s->lacp_time; - lacp->custom_time = MAX(TIME_UPDATE_INTERVAL, s->custom_time); + 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 'pdu', a parsed LACP packet received on 'slave_'. This function - * should be called on all packets received on 'slave_' with Ethernet Type - * ETH_TYPE_LACP and parsable by parse_lacp_packet(). */ +/* Processes 'packet' which was received on 'slave_'. This function should be + * called on all packets received on 'slave_' with Ethernet Type ETH_TYPE_LACP. + */ void -lacp_process_pdu(struct lacp *lacp, const void *slave_, - const struct lacp_pdu *pdu) +lacp_process_packet(struct lacp *lacp, const void *slave_, + const struct ofpbuf *packet) + OVS_EXCLUDED(mutex) { - struct slave *slave = slave_lookup(lacp, slave_); + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + 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; + } - switch (lacp->lacp_time) { - case LACP_TIME_FAST: - tx_rate = LACP_FAST_TIME_TX; - break; - case LACP_TIME_SLOW: - tx_rate = LACP_SLOW_TIME_TX; - break; - case LACP_TIME_CUSTOM: - tx_rate = lacp->custom_time; - break; - default: NOT_REACHED(); + pdu = parse_lacp_packet(packet); + if (!pdu) { + VLOG_WARN_RL(&rl, "%s: received an unparsable LACP PDU.", lacp->name); + goto out; } slave->status = LACP_CURRENT; + tx_rate = lacp->fast ? LACP_FAST_TIME_TX : LACP_SLOW_TIME_TX; timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * tx_rate); slave->ntt_actor = pdu->partner; @@ -237,14 +336,27 @@ lacp_process_pdu(struct lacp *lacp, const void *slave_, lacp->update = true; slave->partner = pdu->actor; } + +out: + ovs_mutex_unlock(&mutex); } -/* Returns true if 'lacp' has successfully negotiated with its partner. False - * if 'lacp' is NULL. */ -bool -lacp_negotiated(const struct lacp *lacp) +/* Returns the lacp_status of the given 'lacp' object (which may be NULL). */ +enum lacp_status +lacp_status(const struct lacp *lacp) OVS_EXCLUDED(mutex) { - return lacp ? lacp->negotiated : false; + if (lacp) { + enum lacp_status ret; + + ovs_mutex_lock(&mutex); + ret = lacp->negotiated ? LACP_NEGOTIATED : LACP_CONFIGURED; + ovs_mutex_unlock(&mutex); + return ret; + } else { + /* 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; + } } /* Registers 'slave_' as subordinate to 'lacp'. This should be called at least @@ -253,9 +365,12 @@ lacp_negotiated(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; @@ -286,30 +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. */ + * 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) { - struct slave *slave = slave_lookup(lacp, slave_); + struct slave *slave; + if (!lacp) { + return; + } + + 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) 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 + || (slave->lacp && slave->lacp->fallback_ab + && slave->status == LACP_DEFAULTED)); } /* This function should be called before enabling 'slave_' to send or receive @@ -317,52 +460,58 @@ lacp_slave_carrier_changed(const struct lacp *lacp, const void *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) { - struct slave *slave = slave_lookup(lacp, slave_); - - /* The slave may be enabled if it's attached to an aggregator and its - * partner is synchronized. The only exception is defaulted slaves. - * They are not required to have synchronized partners because they - * have no partners at all. They will only be attached if negotiations - * failed on all slaves in the bond. */ - return slave->attached && (slave->partner.state & LACP_STATE_SYNC - || slave->status == LACP_DEFAULTED); + 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()); + } } } @@ -371,7 +520,6 @@ lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) } HMAP_FOR_EACH (slave, node, &lacp->slaves) { - struct lacp_pdu pdu; struct lacp_info actor; if (!slave_may_tx(slave)) { @@ -383,30 +531,30 @@ lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) if (timer_expired(&slave->tx) || !info_tx_equal(&actor, &slave->ntt_actor)) { long long int duration; + struct lacp_pdu pdu; slave->ntt_actor = actor; compose_lacp_pdu(&actor, &slave->partner, &pdu); - send_pdu(slave->aux, &pdu); + send_pdu(slave->aux, &pdu, sizeof pdu); - if (lacp->lacp_time == LACP_TIME_CUSTOM) { - duration = lacp->custom_time; - } else { - duration = (slave->partner.state & LACP_STATE_TIME - ? LACP_FAST_TIME_TX - : LACP_SLOW_TIME_TX); - } + duration = (slave->partner.state & LACP_STATE_TIME + ? LACP_FAST_TIME_TX + : 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); @@ -416,6 +564,7 @@ lacp_wait(struct lacp *lacp) timer_wait(&slave->rx); } } + ovs_mutex_unlock(&mutex); } /* Static Helpers. */ @@ -423,40 +572,36 @@ 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; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10); - if (lacp->heartbeat) { - HMAP_FOR_EACH (slave, node, &lacp->slaves) { - slave->attached = slave->status != LACP_DEFAULTED; - } - return; - } - lacp->update = false; lead = NULL; HMAP_FOR_EACH (slave, node, &lacp->slaves) { struct lacp_info pri; - slave->attached = true; + slave->attached = false; /* XXX: In the future allow users to configure the expected system ID. * For now just special case loopback. */ if (eth_addr_equals(slave->partner.sys_id, slave->lacp->sys_id)) { VLOG_WARN_RL(&rl, "slave %s: Loopback detected. Slave is " "connected to its own bond", slave->name); - slave->attached = false; continue; } if (slave->status == LACP_DEFAULTED) { + if (lacp->fallback_ab) { + slave->attached = true; + } continue; } + slave->attached = true; slave_get_priority(slave, &pri); if (!lead || memcmp(&pri, &lead_pri, sizeof pri) < 0) { @@ -469,7 +614,7 @@ lacp_update_attached(struct lacp *lacp) if (lead) { HMAP_FOR_EACH (slave, node, &lacp->slaves) { - if (slave->status == LACP_DEFAULTED + 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)) { @@ -480,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; @@ -504,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); @@ -513,24 +658,18 @@ slave_set_defaulted(struct slave *slave) } static void -slave_set_expired(struct slave *slave) +slave_set_expired(struct slave *slave) OVS_REQUIRES(mutex) { - struct lacp *lacp = slave->lacp; - slave->status = LACP_EXPIRED; slave->partner.state |= LACP_STATE_TIME; slave->partner.state &= ~LACP_STATE_SYNC; - /* The spec says we should wait LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX. - * This doesn't make sense when using custom times which can be much - * smaller than LACP_FAST_TIME. */ - timer_set_duration(&slave->rx, (lacp->lacp_time == LACP_TIME_CUSTOM - ? lacp->custom_time - : LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX)); + timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX); } static void slave_get_actor(struct slave *slave, struct lacp_info *actor) + OVS_REQUIRES(mutex) { struct lacp *lacp = slave->lacp; uint16_t key; @@ -540,7 +679,7 @@ slave_get_actor(struct slave *slave, struct lacp_info *actor) state |= LACP_STATE_ACT; } - if (lacp->lacp_time != LACP_TIME_SLOW) { + if (lacp->fast) { state |= LACP_STATE_TIME; } @@ -556,7 +695,7 @@ slave_get_actor(struct slave *slave, struct lacp_info *actor) state |= LACP_STATE_EXP; } - if (lacp->heartbeat || hmap_count(&lacp->slaves) > 1) { + if (hmap_count(&lacp->slaves) > 1) { state |= LACP_STATE_AGG; } @@ -583,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; @@ -607,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; @@ -650,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; } @@ -667,92 +807,83 @@ static void ds_put_lacp_state(struct ds *ds, uint8_t state) { if (state & LACP_STATE_ACT) { - ds_put_cstr(ds, "activity "); + ds_put_cstr(ds, " activity"); } if (state & LACP_STATE_TIME) { - ds_put_cstr(ds, "timeout "); + ds_put_cstr(ds, " timeout"); } if (state & LACP_STATE_AGG) { - ds_put_cstr(ds, "aggregation "); + ds_put_cstr(ds, " aggregation"); } if (state & LACP_STATE_SYNC) { - ds_put_cstr(ds, "synchronized "); + ds_put_cstr(ds, " synchronized"); } if (state & LACP_STATE_COL) { - ds_put_cstr(ds, "collecting "); + ds_put_cstr(ds, " collecting"); } if (state & LACP_STATE_DIST) { - ds_put_cstr(ds, "distributing "); + ds_put_cstr(ds, " distributing"); } if (state & LACP_STATE_DEF) { - ds_put_cstr(ds, "defaulted "); + ds_put_cstr(ds, " defaulted"); } if (state & LACP_STATE_EXP) { - ds_put_cstr(ds, "expired "); + ds_put_cstr(ds, " expired"); } } static void -lacp_unixctl_show(struct unixctl_conn *conn, - const char *args, void *aux OVS_UNUSED) +lacp_print_details(struct ds *ds, struct lacp *lacp) OVS_REQUIRES(mutex) { - struct ds ds = DS_EMPTY_INITIALIZER; - struct lacp *lacp; - struct slave *slave; + struct shash slave_shash = SHASH_INITIALIZER(&slave_shash); + const struct shash_node **sorted_slaves = NULL; - lacp = lacp_find(args); - if (!lacp) { - unixctl_command_reply(conn, 501, "no such lacp object"); - return; - } - - ds_put_format(&ds, "lacp: %s\n", lacp->name); + struct slave *slave; + int i; - ds_put_format(&ds, "\tstatus: %s", lacp->active ? "active" : "passive"); - if (lacp->heartbeat) { - ds_put_cstr(&ds, " heartbeat"); - } + ds_put_format(ds, "---- %s ----\n", lacp->name); + ds_put_format(ds, "\tstatus: %s", lacp->active ? "active" : "passive"); if (lacp->negotiated) { - ds_put_cstr(&ds, " negotiated"); + ds_put_cstr(ds, " negotiated"); } - ds_put_cstr(&ds, "\n"); + ds_put_cstr(ds, "\n"); - ds_put_format(&ds, "\tsys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(lacp->sys_id)); - ds_put_format(&ds, "\tsys_priority: %u\n", lacp->sys_priority); - ds_put_cstr(&ds, "\taggregation key: "); + ds_put_format(ds, "\tsys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(lacp->sys_id)); + ds_put_format(ds, "\tsys_priority: %u\n", lacp->sys_priority); + ds_put_cstr(ds, "\taggregation key: "); if (lacp->key_slave) { - ds_put_format(&ds, "%u", lacp->key_slave->port_id); + ds_put_format(ds, "%u", lacp->key_slave->key + ? lacp->key_slave->key + : lacp->key_slave->port_id); } else { - ds_put_cstr(&ds, "none"); + ds_put_cstr(ds, "none"); } - ds_put_cstr(&ds, "\n"); + ds_put_cstr(ds, "\n"); - ds_put_cstr(&ds, "\tlacp_time: "); - switch (lacp->lacp_time) { - case LACP_TIME_FAST: - ds_put_cstr(&ds, "fast\n"); - break; - case LACP_TIME_SLOW: - ds_put_cstr(&ds, "slow\n"); - break; - case LACP_TIME_CUSTOM: - ds_put_format(&ds, "custom (%lld)\n", lacp->custom_time); - break; - default: - ds_put_cstr(&ds, "unknown\n"); + ds_put_cstr(ds, "\tlacp_time: "); + if (lacp->fast) { + ds_put_cstr(ds, "fast\n"); + } else { + ds_put_cstr(ds, "slow\n"); } HMAP_FOR_EACH (slave, node, &lacp->slaves) { + shash_add(&slave_shash, slave->name, slave); + } + sorted_slaves = shash_sort(&slave_shash); + + for (i = 0; i < shash_count(&slave_shash); i++) { char *status; struct lacp_info actor; + slave = sorted_slaves[i]->data; slave_get_actor(slave, &actor); switch (slave->status) { case LACP_CURRENT: @@ -765,43 +896,73 @@ lacp_unixctl_show(struct unixctl_conn *conn, status = "defaulted"; break; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } - ds_put_format(&ds, "\nslave: %s: %s %s\n", slave->name, status, + ds_put_format(ds, "\nslave: %s: %s %s\n", slave->name, status, slave->attached ? "attached" : "detached"); - ds_put_format(&ds, "\tport_id: %u\n", slave->port_id); - ds_put_format(&ds, "\tport_priority: %u\n", slave->port_priority); + ds_put_format(ds, "\tport_id: %u\n", slave->port_id); + ds_put_format(ds, "\tport_priority: %u\n", slave->port_priority); + ds_put_format(ds, "\tmay_enable: %s\n", (slave_may_enable__(slave) + ? "true" : "false")); - ds_put_format(&ds, "\n\tactor sys_id: " ETH_ADDR_FMT "\n", + ds_put_format(ds, "\n\tactor sys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(actor.sys_id)); - ds_put_format(&ds, "\tactor sys_priority: %u\n", + ds_put_format(ds, "\tactor sys_priority: %u\n", ntohs(actor.sys_priority)); - ds_put_format(&ds, "\tactor port_id: %u\n", + ds_put_format(ds, "\tactor port_id: %u\n", ntohs(actor.port_id)); - ds_put_format(&ds, "\tactor port_priority: %u\n", + ds_put_format(ds, "\tactor port_priority: %u\n", ntohs(actor.port_priority)); - ds_put_format(&ds, "\tactor key: %u\n", + ds_put_format(ds, "\tactor key: %u\n", ntohs(actor.key)); - ds_put_cstr(&ds, "\tactor state: "); - ds_put_lacp_state(&ds, actor.state); - ds_put_cstr(&ds, "\n\n"); + ds_put_cstr(ds, "\tactor state:"); + ds_put_lacp_state(ds, actor.state); + ds_put_cstr(ds, "\n\n"); - ds_put_format(&ds, "\tpartner sys_id: " ETH_ADDR_FMT "\n", + ds_put_format(ds, "\tpartner sys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(slave->partner.sys_id)); - ds_put_format(&ds, "\tpartner sys_priority: %u\n", + ds_put_format(ds, "\tpartner sys_priority: %u\n", ntohs(slave->partner.sys_priority)); - ds_put_format(&ds, "\tpartner port_id: %u\n", + ds_put_format(ds, "\tpartner port_id: %u\n", ntohs(slave->partner.port_id)); - ds_put_format(&ds, "\tpartner port_priority: %u\n", + ds_put_format(ds, "\tpartner port_priority: %u\n", ntohs(slave->partner.port_priority)); - ds_put_format(&ds, "\tpartner key: %u\n", + ds_put_format(ds, "\tpartner key: %u\n", ntohs(slave->partner.key)); - ds_put_cstr(&ds, "\tpartner state: "); - ds_put_lacp_state(&ds, slave->partner.state); - ds_put_cstr(&ds, "\n"); + ds_put_cstr(ds, "\tpartner state:"); + ds_put_lacp_state(ds, slave->partner.state); + ds_put_cstr(ds, "\n"); + } + + shash_destroy(&slave_shash); + free(sorted_slaves); +} + +static void +lacp_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[], + 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"); + goto out; + } + lacp_print_details(&ds, lacp); + } else { + LIST_FOR_EACH (lacp, node, all_lacps) { + lacp_print_details(&ds, lacp); + } } - unixctl_command_reply(conn, 200, ds_cstr(&ds)); + unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); + +out: + ovs_mutex_unlock(&mutex); }