]> git.proxmox.com Git - mirror_ovs.git/blame - lib/lacp.c
ovsdb-idl: Fix iteration over tracked rows with no actual data.
[mirror_ovs.git] / lib / lacp.c
CommitLineData
5617ae6a 1/* Copyright (c) 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
6aa74308
EJ
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <config.h>
17#include "lacp.h"
18
6aa74308
EJ
19#include <stdlib.h>
20
f23d157c 21#include "connectivity.h"
3e8a2ad1 22#include "openvswitch/dynamic-string.h"
6aa74308 23#include "hash.h"
ee89ea7b 24#include "openvswitch/hmap.h"
cf62fa4c 25#include "dp-packet.h"
5617ae6a 26#include "ovs-atomic.h"
6aa74308 27#include "packets.h"
fd016ae3 28#include "openvswitch/poll-loop.h"
f23d157c 29#include "seq.h"
ee89ea7b 30#include "openvswitch/shash.h"
20834809 31#include "timer.h"
6aa74308
EJ
32#include "timeval.h"
33#include "unixctl.h"
e6211adc 34#include "openvswitch/vlog.h"
ee89ea7b 35#include "util.h"
6aa74308
EJ
36
37VLOG_DEFINE_THIS_MODULE(lacp);
38
5f877369
EJ
39/* Masks for lacp_info state member. */
40#define LACP_STATE_ACT 0x01 /* Activity. Active or passive? */
41#define LACP_STATE_TIME 0x02 /* Timeout. Short or long timeout? */
42#define LACP_STATE_AGG 0x04 /* Aggregation. Is the link is bondable? */
43#define LACP_STATE_SYNC 0x08 /* Synchronization. Is the link in up to date? */
44#define LACP_STATE_COL 0x10 /* Collecting. Is the link receiving frames? */
45#define LACP_STATE_DIST 0x20 /* Distributing. Is the link sending frames? */
46#define LACP_STATE_DEF 0x40 /* Defaulted. Using default partner info? */
47#define LACP_STATE_EXP 0x80 /* Expired. Using expired partner info? */
48
49#define LACP_FAST_TIME_TX 1000 /* Fast transmission rate. */
50#define LACP_SLOW_TIME_TX 30000 /* Slow transmission rate. */
51#define LACP_RX_MULTIPLIER 3 /* Multiply by TX rate to get RX rate. */
52
53#define LACP_INFO_LEN 15
13b6bae6 54OVS_PACKED(
5f877369
EJ
55struct lacp_info {
56 ovs_be16 sys_priority; /* System priority. */
74ff3298 57 struct eth_addr sys_id; /* System ID. */
5f877369
EJ
58 ovs_be16 key; /* Operational key. */
59 ovs_be16 port_priority; /* Port priority. */
60 ovs_be16 port_id; /* Port ID. */
61 uint8_t state; /* State mask. See LACP_STATE macros. */
13b6bae6 62});
5f877369
EJ
63BUILD_ASSERT_DECL(LACP_INFO_LEN == sizeof(struct lacp_info));
64
65#define LACP_PDU_LEN 110
66struct lacp_pdu {
67 uint8_t subtype; /* Always 1. */
68 uint8_t version; /* Always 1. */
69
70 uint8_t actor_type; /* Always 1. */
71 uint8_t actor_len; /* Always 20. */
72 struct lacp_info actor; /* LACP actor information. */
73 uint8_t z1[3]; /* Reserved. Always 0. */
74
75 uint8_t partner_type; /* Always 2. */
76 uint8_t partner_len; /* Always 20. */
77 struct lacp_info partner; /* LACP partner information. */
78 uint8_t z2[3]; /* Reserved. Always 0. */
79
80 uint8_t collector_type; /* Always 3. */
81 uint8_t collector_len; /* Always 16. */
82 ovs_be16 collector_delay; /* Maximum collector delay. Set to UINT16_MAX. */
83 uint8_t z3[64]; /* Combination of several fields. Always 0. */
f303f87e 84};
5f877369
EJ
85BUILD_ASSERT_DECL(LACP_PDU_LEN == sizeof(struct lacp_pdu));
86\f
87/* Implementation. */
88
9248e103
N
89enum pdu_subtype {
90 SUBTYPE_UNUSED = 0,
91 SUBTYPE_LACP, /* Link Aggregation Control Protocol. */
92 SUBTYPE_MARKER, /* Link Aggregation Marker Protocol. */
93};
94
91fc374a 95enum member_status {
6aa74308
EJ
96 LACP_CURRENT, /* Current State. Partner up to date. */
97 LACP_EXPIRED, /* Expired State. Partner out of date. */
98 LACP_DEFAULTED, /* Defaulted State. No partner. */
99};
100
91fc374a 101/* A LACP primary interface. */
6aa74308 102struct lacp {
ca6ba700 103 struct ovs_list node; /* Node in all_lacps list. */
6aa74308 104 char *name; /* Name of this lacp object. */
74ff3298 105 struct eth_addr sys_id; /* System ID. */
6aa74308
EJ
106 uint16_t sys_priority; /* System Priority. */
107 bool active; /* Active or Passive. */
108
91fc374a
BP
109 struct hmap members; /* Members this LACP object controls. */
110 struct member *key_member; /* Member whose ID will be aggregation key. */
6aa74308 111
bf83f7c8 112 bool fast; /* True if using fast probe interval. */
6aa74308
EJ
113 bool negotiated; /* True if LACP negotiations were successful. */
114 bool update; /* True if lacp_update() needs to be called. */
9dd165e0 115 bool fallback_ab; /* True if fallback to active-backup on LACP failure. */
91779071 116
37bec3d3 117 struct ovs_refcount ref_cnt;
6aa74308
EJ
118};
119
91fc374a
BP
120/* A LACP member interface. */
121struct member {
122 void *aux; /* Handle used to identify this member. */
123 struct hmap_node node; /* Node in primary's members map. */
6aa74308 124
91fc374a 125 struct lacp *lacp; /* LACP object containing this member. */
6aa74308
EJ
126 uint16_t port_id; /* Port ID. */
127 uint16_t port_priority; /* Port Priority. */
e1ce3f2d 128 uint16_t key; /* Aggregation Key. 0 if default. */
91fc374a 129 char *name; /* Name of this member. */
6aa74308 130
91fc374a 131 enum member_status status; /* Member status. */
6aa74308 132 bool attached; /* Attached. Traffic may flow. */
b3e8cd6b 133 bool carrier_up; /* Carrier state of link. */
6aa74308 134 struct lacp_info partner; /* Partner information. */
77fdfa9b 135 struct lacp_info ntt_actor; /* Used to decide if we Need To Transmit. */
20834809
EJ
136 struct timer tx; /* Next message transmission timer. */
137 struct timer rx; /* Expected message receive timer. */
50b9699f 138
49b9cad3
NK
139 uint32_t count_rx_pdus; /* dot3adAggPortStatsLACPDUsRx */
140 uint32_t count_rx_pdus_bad; /* dot3adAggPortStatsIllegalRx */
9248e103 141 uint32_t count_rx_pdus_marker; /* dot3adAggPortStatsMarkerPDUsRx */
49b9cad3
NK
142 uint32_t count_tx_pdus; /* dot3adAggPortStatsLACPDUsTx */
143 uint32_t count_link_expired; /* Num of times link expired */
144 uint32_t count_link_defaulted; /* Num of times link defaulted */
145 uint32_t count_carrier_changed; /* Num of times link status changed */
6aa74308
EJ
146};
147
b468782a 148static struct ovs_mutex mutex;
55951e15 149static struct ovs_list all_lacps__ = OVS_LIST_INITIALIZER(&all_lacps__);
ca6ba700 150static struct ovs_list *const all_lacps OVS_GUARDED_BY(mutex) = &all_lacps__;
b468782a 151
bd3950dd 152static void lacp_update_attached(struct lacp *) OVS_REQUIRES(mutex);
b468782a 153
91fc374a
BP
154static void member_destroy(struct member *) OVS_REQUIRES(mutex);
155static void member_set_defaulted(struct member *) OVS_REQUIRES(mutex);
156static void member_set_expired(struct member *) OVS_REQUIRES(mutex);
157static void member_get_actor(struct member *, struct lacp_info *actor)
bd3950dd 158 OVS_REQUIRES(mutex);
91fc374a 159static void member_get_priority(struct member *, struct lacp_info *priority)
bd3950dd 160 OVS_REQUIRES(mutex);
91fc374a 161static bool member_may_tx(const struct member *)
bd3950dd 162 OVS_REQUIRES(mutex);
91fc374a 163static struct member *member_lookup(const struct lacp *, const void *member)
bd3950dd 164 OVS_REQUIRES(mutex);
b468782a 165static bool info_tx_equal(struct lacp_info *, struct lacp_info *)
bd3950dd 166 OVS_REQUIRES(mutex);
91fc374a 167static bool member_may_enable__(struct member *) OVS_REQUIRES(mutex);
6aa74308 168
0e15264f 169static unixctl_cb_func lacp_unixctl_show;
49b9cad3 170static unixctl_cb_func lacp_unixctl_show_stats;
6aa74308 171
81aee5f9 172/* Populates 'pdu' with a LACP PDU comprised of 'actor' and 'partner'. */
5f877369 173static void
81aee5f9
EJ
174compose_lacp_pdu(const struct lacp_info *actor,
175 const struct lacp_info *partner, struct lacp_pdu *pdu)
176{
177 memset(pdu, 0, sizeof *pdu);
178
179 pdu->subtype = 1;
180 pdu->version = 1;
181
182 pdu->actor_type = 1;
183 pdu->actor_len = 20;
184 pdu->actor = *actor;
185
186 pdu->partner_type = 2;
187 pdu->partner_len = 20;
188 pdu->partner = *partner;
189
190 pdu->collector_type = 3;
191 pdu->collector_len = 16;
192 pdu->collector_delay = htons(0);
193}
194
9248e103
N
195/* Parses 'p' which represents a packet containing a LACP PDU. This function
196 * returns NULL if 'p' is malformed, or does not represent a LACP PDU format
81aee5f9 197 * supported by OVS. Otherwise, it returns a pointer to the lacp_pdu contained
9248e103
N
198 * within 'p'. It also returns the subtype of PDU.*/
199
5f877369 200static const struct lacp_pdu *
9248e103 201parse_lacp_packet(const struct dp_packet *p, enum pdu_subtype *subtype)
81aee5f9
EJ
202{
203 const struct lacp_pdu *pdu;
204
cf62fa4c 205 pdu = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p),
437d0d22 206 LACP_PDU_LEN);
81aee5f9
EJ
207
208 if (pdu && pdu->subtype == 1
209 && pdu->actor_type == 1 && pdu->actor_len == 20
210 && pdu->partner_type == 2 && pdu->partner_len == 20) {
9248e103 211 *subtype = SUBTYPE_LACP;
81aee5f9 212 return pdu;
9248e103
N
213 } else if (pdu && pdu->subtype == SUBTYPE_MARKER) {
214 *subtype = SUBTYPE_MARKER;
215 return NULL;
216 } else{
217 *subtype = SUBTYPE_UNUSED;
81aee5f9
EJ
218 return NULL;
219 }
220}
221\f
222/* LACP Protocol Implementation. */
223
6aa74308
EJ
224/* Initializes the lacp module. */
225void
226lacp_init(void)
227{
0e15264f
BP
228 unixctl_command_register("lacp/show", "[port]", 0, 1,
229 lacp_unixctl_show, NULL);
49b9cad3
NK
230 unixctl_command_register("lacp/show-stats", "[port]", 0, 1,
231 lacp_unixctl_show_stats, NULL);
6aa74308
EJ
232}
233
2339e869
BP
234static void
235lacp_lock(void) OVS_ACQUIRES(mutex)
6aa74308 236{
b468782a 237 static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
6aa74308 238
b468782a 239 if (ovsthread_once_start(&once)) {
834d6caf 240 ovs_mutex_init_recursive(&mutex);
b468782a
EJ
241 ovsthread_once_done(&once);
242 }
2339e869
BP
243 ovs_mutex_lock(&mutex);
244}
245
246static void
247lacp_unlock(void) OVS_RELEASES(mutex)
248{
249 ovs_mutex_unlock(&mutex);
250}
251
252/* Creates a LACP object. */
253struct lacp *
254lacp_create(void) OVS_EXCLUDED(mutex)
255{
256 struct lacp *lacp;
b468782a 257
6aa74308 258 lacp = xzalloc(sizeof *lacp);
91fc374a 259 hmap_init(&lacp->members);
37bec3d3 260 ovs_refcount_init(&lacp->ref_cnt);
b468782a 261
2339e869 262 lacp_lock();
417e7e66 263 ovs_list_push_back(all_lacps, &lacp->node);
2339e869 264 lacp_unlock();
91779071
EJ
265 return lacp;
266}
267
268struct lacp *
269lacp_ref(const struct lacp *lacp_)
270{
271 struct lacp *lacp = CONST_CAST(struct lacp *, lacp_);
272 if (lacp) {
37bec3d3 273 ovs_refcount_ref(&lacp->ref_cnt);
91779071 274 }
6aa74308
EJ
275 return lacp;
276}
277
91fc374a 278/* Destroys 'lacp' and its members. Does nothing if 'lacp' is NULL. */
6aa74308 279void
b468782a 280lacp_unref(struct lacp *lacp) OVS_EXCLUDED(mutex)
6aa74308 281{
24f83812 282 if (lacp && ovs_refcount_unref_relaxed(&lacp->ref_cnt) == 1) {
91fc374a 283 struct member *member, *next;
6aa74308 284
2339e869 285 lacp_lock();
91fc374a
BP
286 HMAP_FOR_EACH_SAFE (member, next, node, &lacp->members) {
287 member_destroy(member);
6aa74308
EJ
288 }
289
91fc374a 290 hmap_destroy(&lacp->members);
417e7e66 291 ovs_list_remove(&lacp->node);
6aa74308
EJ
292 free(lacp->name);
293 free(lacp);
2339e869 294 lacp_unlock();
6aa74308
EJ
295 }
296}
297
bb5bc6c0 298/* Configures 'lacp' with settings from 's'. */
6aa74308 299void
bb5bc6c0 300lacp_configure(struct lacp *lacp, const struct lacp_settings *s)
b468782a 301 OVS_EXCLUDED(mutex)
6aa74308 302{
cb22974d 303 ovs_assert(!eth_addr_is_zero(s->id));
69fdadb1 304
2339e869 305 lacp_lock();
bb5bc6c0 306 if (!lacp->name || strcmp(s->name, lacp->name)) {
6aa74308 307 free(lacp->name);
bb5bc6c0 308 lacp->name = xstrdup(s->name);
6aa74308
EJ
309 }
310
eb458893 311 if (!eth_addr_equals(lacp->sys_id, s->id)
b20a8f7c 312 || lacp->sys_priority != s->priority) {
74ff3298 313 lacp->sys_id = s->id;
eb458893
EJ
314 lacp->sys_priority = s->priority;
315 lacp->update = true;
316 }
317
bb5bc6c0 318 lacp->active = s->active;
bf83f7c8 319 lacp->fast = s->fast;
9dd165e0
RK
320
321 if (lacp->fallback_ab != s->fallback_ab_cfg) {
322 lacp->fallback_ab = s->fallback_ab_cfg;
323 lacp->update = true;
324 }
325
2339e869 326 lacp_unlock();
6aa74308
EJ
327}
328
7a673515
BP
329/* Returns true if 'lacp' is configured in active mode, false if 'lacp' is
330 * configured for passive mode. */
331bool
b468782a 332lacp_is_active(const struct lacp *lacp) OVS_EXCLUDED(mutex)
7a673515 333{
b468782a 334 bool ret;
2339e869 335 lacp_lock();
b468782a 336 ret = lacp->active;
2339e869 337 lacp_unlock();
b468782a 338 return ret;
7a673515
BP
339}
340
91fc374a
BP
341/* Processes 'packet' which was received on 'member_'. This function should be
342 * called on all packets received on 'member_' with Ethernet Type
343 * ETH_TYPE_LACP.
5f877369 344 */
a8448cb1 345bool
91fc374a 346lacp_process_packet(struct lacp *lacp, const void *member_,
cf62fa4c 347 const struct dp_packet *packet)
b468782a 348 OVS_EXCLUDED(mutex)
6aa74308 349{
5f877369 350 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
5f877369 351 const struct lacp_pdu *pdu;
cdcf42c6 352 long long int tx_rate;
91fc374a 353 struct member *member;
a8448cb1 354 bool lacp_may_enable = false;
9248e103 355 enum pdu_subtype subtype;
cdcf42c6 356
2339e869 357 lacp_lock();
91fc374a
BP
358 member = member_lookup(lacp, member_);
359 if (!member) {
b468782a 360 goto out;
f1ce3514 361 }
91fc374a 362 member->count_rx_pdus++;
f1ce3514 363
9248e103
N
364 pdu = parse_lacp_packet(packet, &subtype);
365 switch (subtype) {
366 case SUBTYPE_LACP:
367 break;
368 case SUBTYPE_MARKER:
91fc374a 369 member->count_rx_pdus_marker++;
9248e103
N
370 VLOG_DBG("%s: received a LACP marker PDU.", lacp->name);
371 goto out;
372 case SUBTYPE_UNUSED:
373 default:
91fc374a 374 member->count_rx_pdus_bad++;
9248e103
N
375 VLOG_WARN_RL(&rl, "%s: received an unparsable LACP PDU.",
376 lacp->name);
377 goto out;
5f877369
EJ
378 }
379
b3e8cd6b
NK
380 /* On some NICs L1 state reporting is slow. In case LACP packets are
381 * received while carrier (L1) state is still down, drop the LACP PDU and
382 * trigger re-checking of L1 state. */
91fc374a 383 if (!member->carrier_up) {
b3e8cd6b 384 VLOG_INFO_RL(&rl, "%s: carrier state is DOWN,"
91fc374a 385 " dropping received LACP PDU.", member->name);
b3e8cd6b
NK
386 seq_change(connectivity_seq_get());
387 goto out;
388 }
389
91fc374a 390 member->status = LACP_CURRENT;
bf83f7c8 391 tx_rate = lacp->fast ? LACP_FAST_TIME_TX : LACP_SLOW_TIME_TX;
91fc374a 392 timer_set_duration(&member->rx, LACP_RX_MULTIPLIER * tx_rate);
6aa74308 393
91fc374a 394 member->ntt_actor = pdu->partner;
6aa74308 395
9248e103 396 /* Update our information about our partner if it's out of date. This may
6aa74308 397 * cause priorities to change so re-calculate attached status of all
91fc374a
BP
398 * members. */
399 if (memcmp(&member->partner, &pdu->actor, sizeof pdu->actor)) {
6aa74308 400 lacp->update = true;
91fc374a 401 member->partner = pdu->actor;
6aa74308 402 }
b468782a 403
a8448cb1
NK
404 /* Evaluate may_enable here to avoid dropping of packets till main thread
405 * sets may_enable to true. */
91fc374a 406 lacp_may_enable = member_may_enable__(member);
a8448cb1 407
b468782a 408out:
2339e869 409 lacp_unlock();
a8448cb1
NK
410
411 return lacp_may_enable;
6aa74308
EJ
412}
413
bdebeece
EJ
414/* Returns the lacp_status of the given 'lacp' object (which may be NULL). */
415enum lacp_status
b468782a 416lacp_status(const struct lacp *lacp) OVS_EXCLUDED(mutex)
6aa74308 417{
2a3fb0aa
BP
418 if (lacp) {
419 enum lacp_status ret;
b468782a 420
2339e869 421 lacp_lock();
2a3fb0aa 422 ret = lacp->negotiated ? LACP_NEGOTIATED : LACP_CONFIGURED;
2339e869 423 lacp_unlock();
2a3fb0aa 424 return ret;
bdebeece 425 } else {
2a3fb0aa
BP
426 /* Don't take 'mutex'. It might not even be initialized, since we
427 * don't know that any lacp object has been created. */
428 return LACP_DISABLED;
bdebeece 429 }
6aa74308
EJ
430}
431
91fc374a
BP
432/* Registers 'member_' as subordinate to 'lacp'. This should be called at
433 * least once per member in a LACP managed bond. Should also be called
434 * whenever a member's settings change. */
6aa74308 435void
91fc374a
BP
436lacp_member_register(struct lacp *lacp, void *member_,
437 const struct lacp_member_settings *s)
b468782a 438 OVS_EXCLUDED(mutex)
6aa74308 439{
91fc374a 440 struct member *member;
6aa74308 441
2339e869 442 lacp_lock();
91fc374a
BP
443 member = member_lookup(lacp, member_);
444 if (!member) {
445 member = xzalloc(sizeof *member);
446 member->lacp = lacp;
447 member->aux = member_;
448 hmap_insert(&lacp->members, &member->node, hash_pointer(member_, 0));
449 member_set_defaulted(member);
450
451 if (!lacp->key_member) {
452 lacp->key_member = member;
6aa74308
EJ
453 }
454 }
455
91fc374a
BP
456 if (!member->name || strcmp(s->name, member->name)) {
457 free(member->name);
458 member->name = xstrdup(s->name);
6aa74308
EJ
459 }
460
91fc374a
BP
461 if (member->port_id != s->id
462 || member->port_priority != s->priority
463 || member->key != s->key) {
464 member->port_id = s->id;
465 member->port_priority = s->priority;
466 member->key = s->key;
6aa74308 467
6aa74308
EJ
468 lacp->update = true;
469
470 if (lacp->active || lacp->negotiated) {
91fc374a 471 member_set_expired(member);
6aa74308
EJ
472 }
473 }
2339e869 474 lacp_unlock();
6aa74308
EJ
475}
476
91fc374a 477/* Unregisters 'member_' with 'lacp'. */
6aa74308 478void
91fc374a 479lacp_member_unregister(struct lacp *lacp, const void *member_)
b468782a 480 OVS_EXCLUDED(mutex)
6aa74308 481{
91fc374a 482 struct member *member;
6aa74308 483
2339e869 484 lacp_lock();
91fc374a
BP
485 member = member_lookup(lacp, member_);
486 if (member) {
487 member_destroy(member);
eb458893 488 lacp->update = true;
6aa74308 489 }
2339e869 490 lacp_unlock();
6aa74308
EJ
491}
492
91fc374a 493/* This function should be called whenever the carrier status of 'member_' has
3e5b3fdb 494 * changed. If 'lacp' is null, this function has no effect.*/
6aa74308 495void
91fc374a
BP
496lacp_member_carrier_changed(const struct lacp *lacp, const void *member_,
497 bool carrier_up)
b468782a 498 OVS_EXCLUDED(mutex)
6aa74308 499{
91fc374a 500 struct member *member;
b468782a
EJ
501 if (!lacp) {
502 return;
503 }
6aa74308 504
2339e869 505 lacp_lock();
91fc374a
BP
506 member = member_lookup(lacp, member_);
507 if (!member) {
b468782a
EJ
508 goto out;
509 }
f1ce3514 510
91fc374a
BP
511 if (member->status == LACP_CURRENT || member->lacp->active) {
512 member_set_expired(member);
6aa74308 513 }
b3e8cd6b 514
91fc374a
BP
515 if (member->carrier_up != carrier_up) {
516 member->carrier_up = carrier_up;
517 member->count_carrier_changed++;
b3e8cd6b 518 }
b468782a
EJ
519
520out:
2339e869 521 lacp_unlock();
6aa74308
EJ
522}
523
29985e75 524static bool
91fc374a 525member_may_enable__(struct member *member) OVS_REQUIRES(mutex)
29985e75 526{
91fc374a 527 /* The member may be enabled if it's attached to an aggregator and its
29985e75 528 * partner is synchronized.*/
91fc374a
BP
529 return member->attached && (member->partner.state & LACP_STATE_SYNC
530 || (member->lacp && member->lacp->fallback_ab
531 && member->status == LACP_DEFAULTED));
29985e75
EJ
532}
533
91fc374a
BP
534/* This function should be called before enabling 'member_' to send or receive
535 * traffic. If it returns false, 'member_' should not enabled. As a
6aa74308
EJ
536 * convenience, returns true if 'lacp' is NULL. */
537bool
91fc374a 538lacp_member_may_enable(const struct lacp *lacp, const void *member_)
b468782a 539 OVS_EXCLUDED(mutex)
6aa74308
EJ
540{
541 if (lacp) {
91fc374a 542 struct member *member;
b3e8cd6b 543 bool ret = false;
b468782a 544
2339e869 545 lacp_lock();
91fc374a
BP
546 member = member_lookup(lacp, member_);
547 if (member) {
548 /* It is only called when carrier is up. So, enable member's
b3e8cd6b 549 * carrier state if it is currently down. */
91fc374a
BP
550 if (!member->carrier_up) {
551 member->carrier_up = true;
b3e8cd6b 552 }
91fc374a 553 ret = member_may_enable__(member);
b3e8cd6b 554 }
2339e869 555 lacp_unlock();
b468782a 556 return ret;
6aa74308
EJ
557 } else {
558 return true;
559 }
560}
561
91fc374a 562/* Returns true if partner information on 'member_' is up to date. 'member_'
44cb163f
EJ
563 * not being current, generally indicates a connectivity problem, or a
564 * misconfigured (or broken) partner. */
565bool
91fc374a 566lacp_member_is_current(const struct lacp *lacp, const void *member_)
b468782a 567 OVS_EXCLUDED(mutex)
44cb163f 568{
91fc374a 569 struct member *member;
b468782a
EJ
570 bool ret;
571
2339e869 572 lacp_lock();
91fc374a
BP
573 member = member_lookup(lacp, member_);
574 ret = member ? member->status != LACP_DEFAULTED : false;
2339e869 575 lacp_unlock();
b468782a 576 return ret;
44cb163f
EJ
577}
578
6aa74308
EJ
579/* This function should be called periodically to update 'lacp'. */
580void
b468782a 581lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) OVS_EXCLUDED(mutex)
6aa74308 582{
91fc374a 583 struct member *member;
6aa74308 584
2339e869 585 lacp_lock();
91fc374a
BP
586 HMAP_FOR_EACH (member, node, &lacp->members) {
587 if (timer_expired(&member->rx)) {
588 enum member_status old_status = member->status;
589
590 if (member->status == LACP_CURRENT) {
591 member_set_expired(member);
592 member->count_link_expired++;
593 } else if (member->status == LACP_EXPIRED) {
594 member_set_defaulted(member);
595 member->count_link_defaulted++;
6aa74308 596 }
91fc374a 597 if (member->status != old_status) {
f23d157c
JS
598 seq_change(connectivity_seq_get());
599 }
6aa74308 600 }
6aa74308
EJ
601 }
602
603 if (lacp->update) {
604 lacp_update_attached(lacp);
8b0c3c38 605 seq_change(connectivity_seq_get());
6aa74308
EJ
606 }
607
91fc374a 608 HMAP_FOR_EACH (member, node, &lacp->members) {
77fdfa9b 609 struct lacp_info actor;
6aa74308 610
91fc374a 611 if (!member_may_tx(member)) {
6aa74308
EJ
612 continue;
613 }
614
91fc374a 615 member_get_actor(member, &actor);
77fdfa9b 616
91fc374a
BP
617 if (timer_expired(&member->tx)
618 || !info_tx_equal(&actor, &member->ntt_actor)) {
cdcf42c6 619 long long int duration;
5f877369 620 struct lacp_pdu pdu;
77fdfa9b 621
91fc374a
BP
622 member->ntt_actor = actor;
623 compose_lacp_pdu(&actor, &member->partner, &pdu);
624 send_pdu(member->aux, &pdu, sizeof pdu);
625 member->count_tx_pdus++;
6aa74308 626
91fc374a 627 duration = (member->partner.state & LACP_STATE_TIME
bf83f7c8
EJ
628 ? LACP_FAST_TIME_TX
629 : LACP_SLOW_TIME_TX);
cdcf42c6 630
91fc374a 631 timer_set_duration(&member->tx, duration);
f23d157c 632 seq_change(connectivity_seq_get());
77fdfa9b 633 }
6aa74308 634 }
2339e869 635 lacp_unlock();
6aa74308
EJ
636}
637
638/* Causes poll_block() to wake up when lacp_run() needs to be called again. */
639void
b468782a 640lacp_wait(struct lacp *lacp) OVS_EXCLUDED(mutex)
6aa74308 641{
91fc374a 642 struct member *member;
6aa74308 643
2339e869 644 lacp_lock();
91fc374a
BP
645 HMAP_FOR_EACH (member, node, &lacp->members) {
646 if (member_may_tx(member)) {
647 timer_wait(&member->tx);
6aa74308
EJ
648 }
649
91fc374a
BP
650 if (member->status != LACP_DEFAULTED) {
651 timer_wait(&member->rx);
6aa74308
EJ
652 }
653 }
2339e869 654 lacp_unlock();
6aa74308
EJ
655}
656\f
657/* Static Helpers. */
658
91fc374a
BP
659/* Updates the attached status of all members controlled by 'lacp' and sets its
660 * negotiated parameter to true if any members are attachable. */
6aa74308 661static void
bd3950dd 662lacp_update_attached(struct lacp *lacp) OVS_REQUIRES(mutex)
6aa74308 663{
91fc374a 664 struct member *lead, *lead_current, *member;
6aa74308 665 struct lacp_info lead_pri;
86c8462d 666 bool lead_enable;
6aa74308
EJ
667 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
668
669 lacp->update = false;
670
671 lead = NULL;
6f50056c 672 lead_current = NULL;
86c8462d 673 lead_enable = false;
6f50056c
RM
674
675 /* Check if there is a working interface.
676 * Store as lead_current, if there is one. */
91fc374a
BP
677 HMAP_FOR_EACH (member, node, &lacp->members) {
678 if (member->status == LACP_CURRENT && member->attached) {
6f50056c 679 struct lacp_info pri;
91fc374a 680 member_get_priority(member, &pri);
6f50056c 681 if (!lead_current || memcmp(&pri, &lead_pri, sizeof pri) < 0) {
91fc374a 682 lead_current = member;
6f50056c
RM
683 lead = lead_current;
684 lead_pri = pri;
685 lead_enable = true;
686 }
687 }
688 }
689
690 /* Find interface with highest priority. */
91fc374a 691 HMAP_FOR_EACH (member, node, &lacp->members) {
6aa74308
EJ
692 struct lacp_info pri;
693
91fc374a 694 member->attached = false;
6aa74308
EJ
695
696 /* XXX: In the future allow users to configure the expected system ID.
697 * For now just special case loopback. */
91fc374a
BP
698 if (eth_addr_equals(member->partner.sys_id, member->lacp->sys_id)) {
699 VLOG_WARN_RL(&rl, "member %s: Loopback detected. Interface is "
700 "connected to its own bond", member->name);
6aa74308
EJ
701 continue;
702 }
703
91fc374a 704 if (member->status == LACP_DEFAULTED) {
9dd165e0 705 if (lacp->fallback_ab) {
91fc374a 706 member->attached = true;
9dd165e0 707 }
6aa74308
EJ
708 continue;
709 }
710
91fc374a
BP
711 member_get_priority(member, &pri);
712 bool enable = member_may_enable__(member);
6aa74308 713
6f50056c 714 /* Check if partner MAC address is the same as on the working
91fc374a 715 * interface. Activate member only if the MAC is the same, or
6f50056c
RM
716 * there is no working interface. */
717 if (!lead_current || (lead_current
91fc374a 718 && eth_addr_equals(member->partner.sys_id,
6f50056c 719 lead_current->partner.sys_id))) {
91fc374a 720 member->attached = true;
6f50056c 721 }
91fc374a 722 if (member->attached &&
6f50056c
RM
723 (!lead
724 || enable > lead_enable
725 || (enable == lead_enable
726 && memcmp(&pri, &lead_pri, sizeof pri) < 0))) {
91fc374a 727 lead = member;
86c8462d 728 lead_enable = enable;
6aa74308
EJ
729 lead_pri = pri;
730 }
731 }
732
733 lacp->negotiated = lead != NULL;
734
735 if (lead) {
91fc374a
BP
736 HMAP_FOR_EACH (member, node, &lacp->members) {
737 if ((lacp->fallback_ab && member->status == LACP_DEFAULTED)
738 || lead->partner.key != member->partner.key
6aa74308 739 || !eth_addr_equals(lead->partner.sys_id,
91fc374a
BP
740 member->partner.sys_id)) {
741 member->attached = false;
6aa74308
EJ
742 }
743 }
744 }
745}
746
747static void
91fc374a 748member_destroy(struct member *member) OVS_REQUIRES(mutex)
6aa74308 749{
91fc374a
BP
750 if (member) {
751 struct lacp *lacp = member->lacp;
6aa74308
EJ
752
753 lacp->update = true;
91fc374a 754 hmap_remove(&lacp->members, &member->node);
6aa74308 755
91fc374a
BP
756 if (lacp->key_member == member) {
757 struct hmap_node *member_node = hmap_first(&lacp->members);
6aa74308 758
91fc374a
BP
759 if (member_node) {
760 lacp->key_member = CONTAINER_OF(member_node, struct member,
761 node);
6aa74308 762 } else {
91fc374a 763 lacp->key_member = NULL;
6aa74308
EJ
764 }
765 }
766
91fc374a
BP
767 free(member->name);
768 free(member);
6aa74308
EJ
769 }
770}
771
772static void
91fc374a 773member_set_defaulted(struct member *member) OVS_REQUIRES(mutex)
6aa74308 774{
91fc374a 775 memset(&member->partner, 0, sizeof member->partner);
6aa74308 776
91fc374a
BP
777 member->lacp->update = true;
778 member->status = LACP_DEFAULTED;
6aa74308
EJ
779}
780
781static void
91fc374a 782member_set_expired(struct member *member) OVS_REQUIRES(mutex)
6aa74308 783{
91fc374a
BP
784 member->status = LACP_EXPIRED;
785 member->partner.state |= LACP_STATE_TIME;
786 member->partner.state &= ~LACP_STATE_SYNC;
cdcf42c6 787
91fc374a 788 timer_set_duration(&member->rx, LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX);
6aa74308
EJ
789}
790
791static void
91fc374a 792member_get_actor(struct member *member, struct lacp_info *actor)
bd3950dd 793 OVS_REQUIRES(mutex)
6aa74308 794{
91fc374a 795 struct lacp *lacp = member->lacp;
e1ce3f2d 796 uint16_t key;
6aa74308
EJ
797 uint8_t state = 0;
798
da2f7b8f 799 if (lacp->active) {
6aa74308
EJ
800 state |= LACP_STATE_ACT;
801 }
802
bf83f7c8 803 if (lacp->fast) {
269340fa
EJ
804 state |= LACP_STATE_TIME;
805 }
806
91fc374a 807 if (member->attached) {
6aa74308
EJ
808 state |= LACP_STATE_SYNC;
809 }
810
91fc374a 811 if (member->status == LACP_DEFAULTED) {
6aa74308
EJ
812 state |= LACP_STATE_DEF;
813 }
814
91fc374a 815 if (member->status == LACP_EXPIRED) {
6aa74308
EJ
816 state |= LACP_STATE_EXP;
817 }
818
91fc374a 819 if (hmap_count(&lacp->members) > 1) {
6aa74308
EJ
820 state |= LACP_STATE_AGG;
821 }
822
91fc374a 823 if (member->attached || !lacp->negotiated) {
6aa74308
EJ
824 state |= LACP_STATE_COL | LACP_STATE_DIST;
825 }
826
91fc374a 827 key = lacp->key_member->key;
e1ce3f2d 828 if (!key) {
91fc374a 829 key = lacp->key_member->port_id;
e1ce3f2d
EJ
830 }
831
77fdfa9b 832 actor->state = state;
e1ce3f2d 833 actor->key = htons(key);
91fc374a
BP
834 actor->port_priority = htons(member->port_priority);
835 actor->port_id = htons(member->port_id);
da2f7b8f 836 actor->sys_priority = htons(lacp->sys_priority);
74ff3298 837 actor->sys_id = lacp->sys_id;
6aa74308
EJ
838}
839
91fc374a 840/* Given 'member', populates 'priority' with data representing its LACP link
6aa74308
EJ
841 * priority. If two priority objects populated by this function are compared
842 * using memcmp, the higher priority link will be less than the lower priority
843 * link. */
844static void
91fc374a 845member_get_priority(struct member *member, struct lacp_info *priority)
bd3950dd 846 OVS_REQUIRES(mutex)
6aa74308
EJ
847{
848 uint16_t partner_priority, actor_priority;
849
850 /* Choose the lacp_info of the higher priority system by comparing their
851 * system priorities and mac addresses. */
91fc374a
BP
852 actor_priority = member->lacp->sys_priority;
853 partner_priority = ntohs(member->partner.sys_priority);
6aa74308 854 if (actor_priority < partner_priority) {
91fc374a 855 member_get_actor(member, priority);
6aa74308 856 } else if (partner_priority < actor_priority) {
91fc374a
BP
857 *priority = member->partner;
858 } else if (eth_addr_compare_3way(member->lacp->sys_id,
859 member->partner.sys_id) < 0) {
860 member_get_actor(member, priority);
6aa74308 861 } else {
91fc374a 862 *priority = member->partner;
6aa74308
EJ
863 }
864
865 /* Key and state are not used in priority comparisons. */
866 priority->key = 0;
867 priority->state = 0;
868}
869
870static bool
91fc374a 871member_may_tx(const struct member *member) OVS_REQUIRES(mutex)
6aa74308 872{
b3e8cd6b 873 /* Check for L1 state as well as LACP state. */
91fc374a
BP
874 return (member->carrier_up) && ((member->lacp->active) ||
875 (member->status != LACP_DEFAULTED));
6aa74308
EJ
876}
877
91fc374a
BP
878static struct member *
879member_lookup(const struct lacp *lacp, const void *member_) OVS_REQUIRES(mutex)
6aa74308 880{
91fc374a 881 struct member *member;
6aa74308 882
91fc374a
BP
883 HMAP_FOR_EACH_IN_BUCKET (member, node, hash_pointer(member_, 0),
884 &lacp->members) {
885 if (member->aux == member_) {
886 return member;
6aa74308
EJ
887 }
888 }
889
890 return NULL;
891}
77fdfa9b
EJ
892
893/* Two lacp_info structures are tx_equal if and only if they do not differ in
894 * ways which would require a lacp_pdu transmission. */
895static bool
896info_tx_equal(struct lacp_info *a, struct lacp_info *b)
897{
898
899 /* LACP specification dictates that we transmit whenever the actor and
900 * remote_actor differ in the following fields: Port, Port Priority,
901 * System, System Priority, Aggregation Key, Activity State, Timeout State,
902 * Sync State, and Aggregation State. The state flags are most likely to
903 * change so are checked first. */
904 return !((a->state ^ b->state) & (LACP_STATE_ACT
905 | LACP_STATE_TIME
906 | LACP_STATE_SYNC
907 | LACP_STATE_AGG))
908 && a->port_id == b->port_id
909 && a->port_priority == b->port_priority
910 && a->key == b->key
911 && a->sys_priority == b->sys_priority
912 && eth_addr_equals(a->sys_id, b->sys_id);
913}
6aa74308
EJ
914\f
915static struct lacp *
344e21d4 916lacp_find(const char *name) OVS_REQUIRES(mutex)
6aa74308
EJ
917{
918 struct lacp *lacp;
919
b468782a 920 LIST_FOR_EACH (lacp, node, all_lacps) {
6aa74308
EJ
921 if (!strcmp(lacp->name, name)) {
922 return lacp;
923 }
924 }
925
926 return NULL;
927}
928
929static void
930ds_put_lacp_state(struct ds *ds, uint8_t state)
931{
932 if (state & LACP_STATE_ACT) {
1c1df7f1 933 ds_put_cstr(ds, " activity");
6aa74308
EJ
934 }
935
936 if (state & LACP_STATE_TIME) {
1c1df7f1 937 ds_put_cstr(ds, " timeout");
6aa74308
EJ
938 }
939
940 if (state & LACP_STATE_AGG) {
1c1df7f1 941 ds_put_cstr(ds, " aggregation");
6aa74308
EJ
942 }
943
944 if (state & LACP_STATE_SYNC) {
1c1df7f1 945 ds_put_cstr(ds, " synchronized");
6aa74308
EJ
946 }
947
948 if (state & LACP_STATE_COL) {
1c1df7f1 949 ds_put_cstr(ds, " collecting");
6aa74308
EJ
950 }
951
952 if (state & LACP_STATE_DIST) {
1c1df7f1 953 ds_put_cstr(ds, " distributing");
6aa74308
EJ
954 }
955
956 if (state & LACP_STATE_DEF) {
1c1df7f1 957 ds_put_cstr(ds, " defaulted");
6aa74308
EJ
958 }
959
960 if (state & LACP_STATE_EXP) {
1c1df7f1 961 ds_put_cstr(ds, " expired");
6aa74308
EJ
962 }
963}
964
965static void
344e21d4 966lacp_print_details(struct ds *ds, struct lacp *lacp) OVS_REQUIRES(mutex)
6aa74308 967{
91fc374a
BP
968 struct shash member_shash = SHASH_INITIALIZER(&member_shash);
969 const struct shash_node **sorted_members = NULL;
431d6a6a 970
91fc374a 971 struct member *member;
431d6a6a 972 int i;
6aa74308 973
5dab8ece 974 ds_put_format(ds, "---- %s ----\n", lacp->name);
6d7d467e 975 ds_put_format(ds, " status: %s", lacp->active ? "active" : "passive");
20601813 976 if (lacp->negotiated) {
5dab8ece 977 ds_put_cstr(ds, " negotiated");
20601813 978 }
5dab8ece 979 ds_put_cstr(ds, "\n");
20601813 980
6d7d467e
BP
981 ds_put_format(ds, " sys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(lacp->sys_id));
982 ds_put_format(ds, " sys_priority: %u\n", lacp->sys_priority);
983 ds_put_cstr(ds, " aggregation key: ");
91fc374a
BP
984 if (lacp->key_member) {
985 ds_put_format(ds, "%u", lacp->key_member->key
986 ? lacp->key_member->key
987 : lacp->key_member->port_id);
6aa74308 988 } else {
5dab8ece 989 ds_put_cstr(ds, "none");
6aa74308 990 }
5dab8ece 991 ds_put_cstr(ds, "\n");
6aa74308 992
6d7d467e 993 ds_put_cstr(ds, " lacp_time: ");
bf83f7c8 994 if (lacp->fast) {
5dab8ece 995 ds_put_cstr(ds, "fast\n");
bf83f7c8 996 } else {
5dab8ece 997 ds_put_cstr(ds, "slow\n");
cdcf42c6
EJ
998 }
999
91fc374a
BP
1000 HMAP_FOR_EACH (member, node, &lacp->members) {
1001 shash_add(&member_shash, member->name, member);
431d6a6a 1002 }
91fc374a 1003 sorted_members = shash_sort(&member_shash);
431d6a6a 1004
91fc374a 1005 for (i = 0; i < shash_count(&member_shash); i++) {
6aa74308 1006 char *status;
77fdfa9b 1007 struct lacp_info actor;
6aa74308 1008
91fc374a
BP
1009 member = sorted_members[i]->data;
1010 member_get_actor(member, &actor);
1011 switch (member->status) {
6aa74308
EJ
1012 case LACP_CURRENT:
1013 status = "current";
1014 break;
1015 case LACP_EXPIRED:
1016 status = "expired";
1017 break;
1018 case LACP_DEFAULTED:
1019 status = "defaulted";
1020 break;
1021 default:
428b2edd 1022 OVS_NOT_REACHED();
6aa74308
EJ
1023 }
1024
91fc374a
BP
1025 ds_put_format(ds, "\nmember: %s: %s %s\n", member->name, status,
1026 member->attached ? "attached" : "detached");
1027 ds_put_format(ds, " port_id: %u\n", member->port_id);
1028 ds_put_format(ds, " port_priority: %u\n", member->port_priority);
1029 ds_put_format(ds, " may_enable: %s\n", (member_may_enable__(member)
29985e75 1030 ? "true" : "false"));
6aa74308 1031
6d7d467e 1032 ds_put_format(ds, "\n actor sys_id: " ETH_ADDR_FMT "\n",
77fdfa9b 1033 ETH_ADDR_ARGS(actor.sys_id));
6d7d467e 1034 ds_put_format(ds, " actor sys_priority: %u\n",
77fdfa9b 1035 ntohs(actor.sys_priority));
6d7d467e 1036 ds_put_format(ds, " actor port_id: %u\n",
77fdfa9b 1037 ntohs(actor.port_id));
6d7d467e 1038 ds_put_format(ds, " actor port_priority: %u\n",
77fdfa9b 1039 ntohs(actor.port_priority));
6d7d467e 1040 ds_put_format(ds, " actor key: %u\n",
77fdfa9b 1041 ntohs(actor.key));
6d7d467e 1042 ds_put_cstr(ds, " actor state:");
5dab8ece
JP
1043 ds_put_lacp_state(ds, actor.state);
1044 ds_put_cstr(ds, "\n\n");
6aa74308 1045
6d7d467e 1046 ds_put_format(ds, " partner sys_id: " ETH_ADDR_FMT "\n",
91fc374a 1047 ETH_ADDR_ARGS(member->partner.sys_id));
6d7d467e 1048 ds_put_format(ds, " partner sys_priority: %u\n",
91fc374a 1049 ntohs(member->partner.sys_priority));
6d7d467e 1050 ds_put_format(ds, " partner port_id: %u\n",
91fc374a 1051 ntohs(member->partner.port_id));
6d7d467e 1052 ds_put_format(ds, " partner port_priority: %u\n",
91fc374a 1053 ntohs(member->partner.port_priority));
6d7d467e 1054 ds_put_format(ds, " partner key: %u\n",
91fc374a 1055 ntohs(member->partner.key));
6d7d467e 1056 ds_put_cstr(ds, " partner state:");
91fc374a 1057 ds_put_lacp_state(ds, member->partner.state);
5dab8ece
JP
1058 ds_put_cstr(ds, "\n");
1059 }
431d6a6a 1060
91fc374a
BP
1061 shash_destroy(&member_shash);
1062 free(sorted_members);
5dab8ece
JP
1063}
1064
49b9cad3
NK
1065static void
1066lacp_print_stats(struct ds *ds, struct lacp *lacp) OVS_REQUIRES(mutex)
1067{
91fc374a
BP
1068 struct shash member_shash = SHASH_INITIALIZER(&member_shash);
1069 const struct shash_node **sorted_members = NULL;
49b9cad3 1070
91fc374a 1071 struct member *member;
49b9cad3
NK
1072 int i;
1073
1074 ds_put_format(ds, "---- %s statistics ----\n", lacp->name);
1075
91fc374a
BP
1076 HMAP_FOR_EACH (member, node, &lacp->members) {
1077 shash_add(&member_shash, member->name, member);
49b9cad3 1078 }
91fc374a
BP
1079 sorted_members = shash_sort(&member_shash);
1080
1081 for (i = 0; i < shash_count(&member_shash); i++) {
1082 member = sorted_members[i]->data;
1083 ds_put_format(ds, "\nmember: %s:\n", member->name);
1084 ds_put_format(ds, " TX PDUs: %u\n", member->count_tx_pdus);
1085 ds_put_format(ds, " RX PDUs: %u\n", member->count_rx_pdus);
1086 ds_put_format(ds, " RX Bad PDUs: %u\n", member->count_rx_pdus_bad);
9248e103 1087 ds_put_format(ds, " RX Marker Request PDUs: %u\n",
91fc374a 1088 member->count_rx_pdus_marker);
6d7d467e 1089 ds_put_format(ds, " Link Expired: %u\n",
91fc374a 1090 member->count_link_expired);
6d7d467e 1091 ds_put_format(ds, " Link Defaulted: %u\n",
91fc374a 1092 member->count_link_defaulted);
6d7d467e 1093 ds_put_format(ds, " Carrier Status Changed: %u\n",
91fc374a 1094 member->count_carrier_changed);
49b9cad3
NK
1095 }
1096
91fc374a
BP
1097 shash_destroy(&member_shash);
1098 free(sorted_members);
49b9cad3
NK
1099}
1100
5dab8ece 1101static void
0e15264f 1102lacp_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
b468782a 1103 void *aux OVS_UNUSED) OVS_EXCLUDED(mutex)
5dab8ece
JP
1104{
1105 struct ds ds = DS_EMPTY_INITIALIZER;
1106 struct lacp *lacp;
1107
2339e869 1108 lacp_lock();
0e15264f
BP
1109 if (argc > 1) {
1110 lacp = lacp_find(argv[1]);
5dab8ece 1111 if (!lacp) {
bde9f75d 1112 unixctl_command_reply_error(conn, "no such lacp object");
b468782a 1113 goto out;
5dab8ece
JP
1114 }
1115 lacp_print_details(&ds, lacp);
1116 } else {
b468782a 1117 LIST_FOR_EACH (lacp, node, all_lacps) {
5dab8ece
JP
1118 lacp_print_details(&ds, lacp);
1119 }
6aa74308
EJ
1120 }
1121
bde9f75d 1122 unixctl_command_reply(conn, ds_cstr(&ds));
6aa74308 1123 ds_destroy(&ds);
b468782a
EJ
1124
1125out:
2339e869 1126 lacp_unlock();
6aa74308 1127}
50b9699f 1128
49b9cad3
NK
1129static void
1130lacp_unixctl_show_stats(struct unixctl_conn *conn,
1131 int argc,
1132 const char *argv[],
1133 void *aux OVS_UNUSED) OVS_EXCLUDED(mutex)
1134{
1135 struct ds ds = DS_EMPTY_INITIALIZER;
1136 struct lacp *lacp;
1137
1138 lacp_lock();
1139 if (argc > 1) {
1140 lacp = lacp_find(argv[1]);
1141 if (!lacp) {
1142 unixctl_command_reply_error(conn, "no such lacp object");
1143 goto out;
1144 }
1145 lacp_print_stats(&ds, lacp);
1146 } else {
1147 LIST_FOR_EACH (lacp, node, all_lacps) {
1148 lacp_print_stats(&ds, lacp);
1149 }
1150 }
1151
1152 unixctl_command_reply(conn, ds_cstr(&ds));
1153 ds_destroy(&ds);
1154
1155out:
1156 lacp_unlock();
1157}
1158
91fc374a
BP
1159/* Extract a snapshot of the current state and counters for a member port.
1160 Return false if the member is not active. */
50b9699f 1161bool
91fc374a
BP
1162lacp_get_member_stats(const struct lacp *lacp, const void *member_,
1163 struct lacp_member_stats *stats)
50b9699f
NM
1164 OVS_EXCLUDED(mutex)
1165{
91fc374a 1166 struct member *member;
50b9699f
NM
1167 struct lacp_info actor;
1168 bool ret;
1169
1170 ovs_mutex_lock(&mutex);
1171
91fc374a
BP
1172 member = member_lookup(lacp, member_);
1173 if (member) {
6d7d467e 1174 ret = true;
91fc374a 1175 member_get_actor(member, &actor);
6d7d467e 1176 stats->dot3adAggPortActorSystemID = actor.sys_id;
91fc374a
BP
1177 stats->dot3adAggPortPartnerOperSystemID = member->partner.sys_id;
1178 stats->dot3adAggPortAttachedAggID = (lacp->key_member->key ?
1179 lacp->key_member->key :
1180 lacp->key_member->port_id);
6d7d467e
BP
1181
1182 /* Construct my admin-state. Assume aggregation is configured on. */
1183 stats->dot3adAggPortActorAdminState = LACP_STATE_AGG;
1184 if (lacp->active) {
1185 stats->dot3adAggPortActorAdminState |= LACP_STATE_ACT;
1186 }
1187 if (lacp->fast) {
1188 stats->dot3adAggPortActorAdminState |= LACP_STATE_TIME;
1189 }
1190 /* XXX Not sure how to know the partner admin state. It
1191 * might have to be captured and remembered during the
1192 * negotiation phase.
1193 */
1194 stats->dot3adAggPortPartnerAdminState = 0;
1195
1196 stats->dot3adAggPortActorOperState = actor.state;
91fc374a 1197 stats->dot3adAggPortPartnerOperState = member->partner.state;
6d7d467e
BP
1198
1199 /* Read out the latest counters */
91fc374a
BP
1200 stats->dot3adAggPortStatsLACPDUsRx = member->count_rx_pdus;
1201 stats->dot3adAggPortStatsIllegalRx = member->count_rx_pdus_bad;
1202 stats->dot3adAggPortStatsLACPDUsTx = member->count_tx_pdus;
50b9699f
NM
1203 } else {
1204 ret = false;
1205 }
1206 ovs_mutex_unlock(&mutex);
1207 return ret;
1208
1209}