]>
Commit | Line | Data |
---|---|---|
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 EJ |
27 | #include "packets.h" |
28 | #include "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 | |
37 | VLOG_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 | 54 | OVS_PACKED( |
5f877369 EJ |
55 | struct 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 |
63 | BUILD_ASSERT_DECL(LACP_INFO_LEN == sizeof(struct lacp_info)); |
64 | ||
65 | #define LACP_PDU_LEN 110 | |
66 | struct 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 |
85 | BUILD_ASSERT_DECL(LACP_PDU_LEN == sizeof(struct lacp_pdu)); |
86 | \f | |
87 | /* Implementation. */ | |
88 | ||
6aa74308 EJ |
89 | enum slave_status { |
90 | LACP_CURRENT, /* Current State. Partner up to date. */ | |
91 | LACP_EXPIRED, /* Expired State. Partner out of date. */ | |
92 | LACP_DEFAULTED, /* Defaulted State. No partner. */ | |
93 | }; | |
94 | ||
95 | struct lacp { | |
ca6ba700 | 96 | struct ovs_list node; /* Node in all_lacps list. */ |
6aa74308 | 97 | char *name; /* Name of this lacp object. */ |
74ff3298 | 98 | struct eth_addr sys_id; /* System ID. */ |
6aa74308 EJ |
99 | uint16_t sys_priority; /* System Priority. */ |
100 | bool active; /* Active or Passive. */ | |
101 | ||
102 | struct hmap slaves; /* Slaves this LACP object controls. */ | |
103 | struct slave *key_slave; /* Slave whose ID will be the aggregation key. */ | |
104 | ||
bf83f7c8 | 105 | bool fast; /* True if using fast probe interval. */ |
6aa74308 EJ |
106 | bool negotiated; /* True if LACP negotiations were successful. */ |
107 | bool update; /* True if lacp_update() needs to be called. */ | |
9dd165e0 | 108 | bool fallback_ab; /* True if fallback to active-backup on LACP failure. */ |
91779071 | 109 | |
37bec3d3 | 110 | struct ovs_refcount ref_cnt; |
6aa74308 EJ |
111 | }; |
112 | ||
113 | struct slave { | |
114 | void *aux; /* Handle used to identify this slave. */ | |
115 | struct hmap_node node; /* Node in master's slaves map. */ | |
116 | ||
117 | struct lacp *lacp; /* LACP object containing this slave. */ | |
118 | uint16_t port_id; /* Port ID. */ | |
119 | uint16_t port_priority; /* Port Priority. */ | |
e1ce3f2d | 120 | uint16_t key; /* Aggregation Key. 0 if default. */ |
6aa74308 EJ |
121 | char *name; /* Name of this slave. */ |
122 | ||
123 | enum slave_status status; /* Slave status. */ | |
124 | bool attached; /* Attached. Traffic may flow. */ | |
6aa74308 | 125 | struct lacp_info partner; /* Partner information. */ |
77fdfa9b | 126 | struct lacp_info ntt_actor; /* Used to decide if we Need To Transmit. */ |
20834809 EJ |
127 | struct timer tx; /* Next message transmission timer. */ |
128 | struct timer rx; /* Expected message receive timer. */ | |
50b9699f NM |
129 | |
130 | uint32_t count_rx_pdus; /* dot3adAggPortStatsLACPDUsRx */ | |
131 | uint32_t count_rx_pdus_bad; /* dot3adAggPortStatsIllegalRx */ | |
132 | uint32_t count_tx_pdus; /* dot3adAggPortStatsLACPDUsTx */ | |
6aa74308 EJ |
133 | }; |
134 | ||
b468782a | 135 | static struct ovs_mutex mutex; |
55951e15 | 136 | static struct ovs_list all_lacps__ = OVS_LIST_INITIALIZER(&all_lacps__); |
ca6ba700 | 137 | static struct ovs_list *const all_lacps OVS_GUARDED_BY(mutex) = &all_lacps__; |
b468782a | 138 | |
bd3950dd | 139 | static void lacp_update_attached(struct lacp *) OVS_REQUIRES(mutex); |
b468782a | 140 | |
bd3950dd AW |
141 | static void slave_destroy(struct slave *) OVS_REQUIRES(mutex); |
142 | static void slave_set_defaulted(struct slave *) OVS_REQUIRES(mutex); | |
143 | static void slave_set_expired(struct slave *) OVS_REQUIRES(mutex); | |
b468782a | 144 | static void slave_get_actor(struct slave *, struct lacp_info *actor) |
bd3950dd | 145 | OVS_REQUIRES(mutex); |
b468782a | 146 | static void slave_get_priority(struct slave *, struct lacp_info *priority) |
bd3950dd | 147 | OVS_REQUIRES(mutex); |
b468782a | 148 | static bool slave_may_tx(const struct slave *) |
bd3950dd | 149 | OVS_REQUIRES(mutex); |
b468782a | 150 | static struct slave *slave_lookup(const struct lacp *, const void *slave) |
bd3950dd | 151 | OVS_REQUIRES(mutex); |
b468782a | 152 | static bool info_tx_equal(struct lacp_info *, struct lacp_info *) |
bd3950dd | 153 | OVS_REQUIRES(mutex); |
6aa74308 | 154 | |
0e15264f | 155 | static unixctl_cb_func lacp_unixctl_show; |
6aa74308 | 156 | |
81aee5f9 | 157 | /* Populates 'pdu' with a LACP PDU comprised of 'actor' and 'partner'. */ |
5f877369 | 158 | static void |
81aee5f9 EJ |
159 | compose_lacp_pdu(const struct lacp_info *actor, |
160 | const struct lacp_info *partner, struct lacp_pdu *pdu) | |
161 | { | |
162 | memset(pdu, 0, sizeof *pdu); | |
163 | ||
164 | pdu->subtype = 1; | |
165 | pdu->version = 1; | |
166 | ||
167 | pdu->actor_type = 1; | |
168 | pdu->actor_len = 20; | |
169 | pdu->actor = *actor; | |
170 | ||
171 | pdu->partner_type = 2; | |
172 | pdu->partner_len = 20; | |
173 | pdu->partner = *partner; | |
174 | ||
175 | pdu->collector_type = 3; | |
176 | pdu->collector_len = 16; | |
177 | pdu->collector_delay = htons(0); | |
178 | } | |
179 | ||
180 | /* Parses 'b' which represents a packet containing a LACP PDU. This function | |
181 | * returns NULL if 'b' is malformed, or does not represent a LACP PDU format | |
182 | * supported by OVS. Otherwise, it returns a pointer to the lacp_pdu contained | |
183 | * within 'b'. */ | |
5f877369 | 184 | static const struct lacp_pdu * |
cf62fa4c | 185 | parse_lacp_packet(const struct dp_packet *p) |
81aee5f9 EJ |
186 | { |
187 | const struct lacp_pdu *pdu; | |
188 | ||
cf62fa4c | 189 | pdu = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p), |
437d0d22 | 190 | LACP_PDU_LEN); |
81aee5f9 EJ |
191 | |
192 | if (pdu && pdu->subtype == 1 | |
193 | && pdu->actor_type == 1 && pdu->actor_len == 20 | |
194 | && pdu->partner_type == 2 && pdu->partner_len == 20) { | |
195 | return pdu; | |
196 | } else { | |
197 | return NULL; | |
198 | } | |
199 | } | |
200 | \f | |
201 | /* LACP Protocol Implementation. */ | |
202 | ||
6aa74308 EJ |
203 | /* Initializes the lacp module. */ |
204 | void | |
205 | lacp_init(void) | |
206 | { | |
0e15264f BP |
207 | unixctl_command_register("lacp/show", "[port]", 0, 1, |
208 | lacp_unixctl_show, NULL); | |
6aa74308 EJ |
209 | } |
210 | ||
2339e869 BP |
211 | static void |
212 | lacp_lock(void) OVS_ACQUIRES(mutex) | |
6aa74308 | 213 | { |
b468782a | 214 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
6aa74308 | 215 | |
b468782a | 216 | if (ovsthread_once_start(&once)) { |
834d6caf | 217 | ovs_mutex_init_recursive(&mutex); |
b468782a EJ |
218 | ovsthread_once_done(&once); |
219 | } | |
2339e869 BP |
220 | ovs_mutex_lock(&mutex); |
221 | } | |
222 | ||
223 | static void | |
224 | lacp_unlock(void) OVS_RELEASES(mutex) | |
225 | { | |
226 | ovs_mutex_unlock(&mutex); | |
227 | } | |
228 | ||
229 | /* Creates a LACP object. */ | |
230 | struct lacp * | |
231 | lacp_create(void) OVS_EXCLUDED(mutex) | |
232 | { | |
233 | struct lacp *lacp; | |
b468782a | 234 | |
6aa74308 EJ |
235 | lacp = xzalloc(sizeof *lacp); |
236 | hmap_init(&lacp->slaves); | |
37bec3d3 | 237 | ovs_refcount_init(&lacp->ref_cnt); |
b468782a | 238 | |
2339e869 | 239 | lacp_lock(); |
417e7e66 | 240 | ovs_list_push_back(all_lacps, &lacp->node); |
2339e869 | 241 | lacp_unlock(); |
91779071 EJ |
242 | return lacp; |
243 | } | |
244 | ||
245 | struct lacp * | |
246 | lacp_ref(const struct lacp *lacp_) | |
247 | { | |
248 | struct lacp *lacp = CONST_CAST(struct lacp *, lacp_); | |
249 | if (lacp) { | |
37bec3d3 | 250 | ovs_refcount_ref(&lacp->ref_cnt); |
91779071 | 251 | } |
6aa74308 EJ |
252 | return lacp; |
253 | } | |
254 | ||
255 | /* Destroys 'lacp' and its slaves. Does nothing if 'lacp' is NULL. */ | |
256 | void | |
b468782a | 257 | lacp_unref(struct lacp *lacp) OVS_EXCLUDED(mutex) |
6aa74308 | 258 | { |
24f83812 | 259 | if (lacp && ovs_refcount_unref_relaxed(&lacp->ref_cnt) == 1) { |
6aa74308 EJ |
260 | struct slave *slave, *next; |
261 | ||
2339e869 | 262 | lacp_lock(); |
6aa74308 EJ |
263 | HMAP_FOR_EACH_SAFE (slave, next, node, &lacp->slaves) { |
264 | slave_destroy(slave); | |
265 | } | |
266 | ||
267 | hmap_destroy(&lacp->slaves); | |
417e7e66 | 268 | ovs_list_remove(&lacp->node); |
6aa74308 EJ |
269 | free(lacp->name); |
270 | free(lacp); | |
2339e869 | 271 | lacp_unlock(); |
6aa74308 EJ |
272 | } |
273 | } | |
274 | ||
bb5bc6c0 | 275 | /* Configures 'lacp' with settings from 's'. */ |
6aa74308 | 276 | void |
bb5bc6c0 | 277 | lacp_configure(struct lacp *lacp, const struct lacp_settings *s) |
b468782a | 278 | OVS_EXCLUDED(mutex) |
6aa74308 | 279 | { |
cb22974d | 280 | ovs_assert(!eth_addr_is_zero(s->id)); |
69fdadb1 | 281 | |
2339e869 | 282 | lacp_lock(); |
bb5bc6c0 | 283 | if (!lacp->name || strcmp(s->name, lacp->name)) { |
6aa74308 | 284 | free(lacp->name); |
bb5bc6c0 | 285 | lacp->name = xstrdup(s->name); |
6aa74308 EJ |
286 | } |
287 | ||
eb458893 | 288 | if (!eth_addr_equals(lacp->sys_id, s->id) |
b20a8f7c | 289 | || lacp->sys_priority != s->priority) { |
74ff3298 | 290 | lacp->sys_id = s->id; |
eb458893 EJ |
291 | lacp->sys_priority = s->priority; |
292 | lacp->update = true; | |
293 | } | |
294 | ||
bb5bc6c0 | 295 | lacp->active = s->active; |
bf83f7c8 | 296 | lacp->fast = s->fast; |
9dd165e0 RK |
297 | |
298 | if (lacp->fallback_ab != s->fallback_ab_cfg) { | |
299 | lacp->fallback_ab = s->fallback_ab_cfg; | |
300 | lacp->update = true; | |
301 | } | |
302 | ||
2339e869 | 303 | lacp_unlock(); |
6aa74308 EJ |
304 | } |
305 | ||
7a673515 BP |
306 | /* Returns true if 'lacp' is configured in active mode, false if 'lacp' is |
307 | * configured for passive mode. */ | |
308 | bool | |
b468782a | 309 | lacp_is_active(const struct lacp *lacp) OVS_EXCLUDED(mutex) |
7a673515 | 310 | { |
b468782a | 311 | bool ret; |
2339e869 | 312 | lacp_lock(); |
b468782a | 313 | ret = lacp->active; |
2339e869 | 314 | lacp_unlock(); |
b468782a | 315 | return ret; |
7a673515 BP |
316 | } |
317 | ||
5f877369 EJ |
318 | /* Processes 'packet' which was received on 'slave_'. This function should be |
319 | * called on all packets received on 'slave_' with Ethernet Type ETH_TYPE_LACP. | |
320 | */ | |
6aa74308 | 321 | void |
5f877369 | 322 | lacp_process_packet(struct lacp *lacp, const void *slave_, |
cf62fa4c | 323 | const struct dp_packet *packet) |
b468782a | 324 | OVS_EXCLUDED(mutex) |
6aa74308 | 325 | { |
5f877369 | 326 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
5f877369 | 327 | const struct lacp_pdu *pdu; |
cdcf42c6 | 328 | long long int tx_rate; |
b468782a | 329 | struct slave *slave; |
cdcf42c6 | 330 | |
2339e869 | 331 | lacp_lock(); |
b468782a | 332 | slave = slave_lookup(lacp, slave_); |
f1ce3514 | 333 | if (!slave) { |
b468782a | 334 | goto out; |
f1ce3514 | 335 | } |
50b9699f | 336 | slave->count_rx_pdus++; |
f1ce3514 | 337 | |
5f877369 EJ |
338 | pdu = parse_lacp_packet(packet); |
339 | if (!pdu) { | |
00179c7b | 340 | slave->count_rx_pdus_bad++; |
5f877369 | 341 | VLOG_WARN_RL(&rl, "%s: received an unparsable LACP PDU.", lacp->name); |
b468782a | 342 | goto out; |
5f877369 EJ |
343 | } |
344 | ||
6aa74308 | 345 | slave->status = LACP_CURRENT; |
bf83f7c8 | 346 | tx_rate = lacp->fast ? LACP_FAST_TIME_TX : LACP_SLOW_TIME_TX; |
cdcf42c6 | 347 | timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * tx_rate); |
6aa74308 | 348 | |
77fdfa9b | 349 | slave->ntt_actor = pdu->partner; |
6aa74308 EJ |
350 | |
351 | /* Update our information about our partner if it's out of date. This may | |
352 | * cause priorities to change so re-calculate attached status of all | |
353 | * slaves. */ | |
354 | if (memcmp(&slave->partner, &pdu->actor, sizeof pdu->actor)) { | |
355 | lacp->update = true; | |
356 | slave->partner = pdu->actor; | |
357 | } | |
b468782a EJ |
358 | |
359 | out: | |
2339e869 | 360 | lacp_unlock(); |
6aa74308 EJ |
361 | } |
362 | ||
bdebeece EJ |
363 | /* Returns the lacp_status of the given 'lacp' object (which may be NULL). */ |
364 | enum lacp_status | |
b468782a | 365 | lacp_status(const struct lacp *lacp) OVS_EXCLUDED(mutex) |
6aa74308 | 366 | { |
2a3fb0aa BP |
367 | if (lacp) { |
368 | enum lacp_status ret; | |
b468782a | 369 | |
2339e869 | 370 | lacp_lock(); |
2a3fb0aa | 371 | ret = lacp->negotiated ? LACP_NEGOTIATED : LACP_CONFIGURED; |
2339e869 | 372 | lacp_unlock(); |
2a3fb0aa | 373 | return ret; |
bdebeece | 374 | } else { |
2a3fb0aa BP |
375 | /* Don't take 'mutex'. It might not even be initialized, since we |
376 | * don't know that any lacp object has been created. */ | |
377 | return LACP_DISABLED; | |
bdebeece | 378 | } |
6aa74308 EJ |
379 | } |
380 | ||
381 | /* Registers 'slave_' as subordinate to 'lacp'. This should be called at least | |
382 | * once per slave in a LACP managed bond. Should also be called whenever a | |
bb5bc6c0 | 383 | * slave's settings change. */ |
6aa74308 | 384 | void |
bb5bc6c0 BP |
385 | lacp_slave_register(struct lacp *lacp, void *slave_, |
386 | const struct lacp_slave_settings *s) | |
b468782a | 387 | OVS_EXCLUDED(mutex) |
6aa74308 | 388 | { |
b468782a | 389 | struct slave *slave; |
6aa74308 | 390 | |
2339e869 | 391 | lacp_lock(); |
b468782a | 392 | slave = slave_lookup(lacp, slave_); |
6aa74308 EJ |
393 | if (!slave) { |
394 | slave = xzalloc(sizeof *slave); | |
395 | slave->lacp = lacp; | |
396 | slave->aux = slave_; | |
397 | hmap_insert(&lacp->slaves, &slave->node, hash_pointer(slave_, 0)); | |
398 | slave_set_defaulted(slave); | |
399 | ||
400 | if (!lacp->key_slave) { | |
401 | lacp->key_slave = slave; | |
402 | } | |
403 | } | |
404 | ||
bb5bc6c0 | 405 | if (!slave->name || strcmp(s->name, slave->name)) { |
6aa74308 | 406 | free(slave->name); |
bb5bc6c0 | 407 | slave->name = xstrdup(s->name); |
6aa74308 EJ |
408 | } |
409 | ||
e1ce3f2d EJ |
410 | if (slave->port_id != s->id |
411 | || slave->port_priority != s->priority | |
412 | || slave->key != s->key) { | |
bb5bc6c0 BP |
413 | slave->port_id = s->id; |
414 | slave->port_priority = s->priority; | |
e1ce3f2d | 415 | slave->key = s->key; |
6aa74308 | 416 | |
6aa74308 EJ |
417 | lacp->update = true; |
418 | ||
419 | if (lacp->active || lacp->negotiated) { | |
420 | slave_set_expired(slave); | |
421 | } | |
422 | } | |
2339e869 | 423 | lacp_unlock(); |
6aa74308 EJ |
424 | } |
425 | ||
426 | /* Unregisters 'slave_' with 'lacp'. */ | |
427 | void | |
428 | lacp_slave_unregister(struct lacp *lacp, const void *slave_) | |
b468782a | 429 | OVS_EXCLUDED(mutex) |
6aa74308 | 430 | { |
b468782a | 431 | struct slave *slave; |
6aa74308 | 432 | |
2339e869 | 433 | lacp_lock(); |
b468782a | 434 | slave = slave_lookup(lacp, slave_); |
6aa74308 EJ |
435 | if (slave) { |
436 | slave_destroy(slave); | |
eb458893 | 437 | lacp->update = true; |
6aa74308 | 438 | } |
2339e869 | 439 | lacp_unlock(); |
6aa74308 EJ |
440 | } |
441 | ||
6aa74308 | 442 | /* This function should be called whenever the carrier status of 'slave_' has |
3e5b3fdb | 443 | * changed. If 'lacp' is null, this function has no effect.*/ |
6aa74308 EJ |
444 | void |
445 | lacp_slave_carrier_changed(const struct lacp *lacp, const void *slave_) | |
b468782a | 446 | OVS_EXCLUDED(mutex) |
6aa74308 | 447 | { |
b468782a EJ |
448 | struct slave *slave; |
449 | if (!lacp) { | |
450 | return; | |
451 | } | |
6aa74308 | 452 | |
2339e869 | 453 | lacp_lock(); |
b468782a EJ |
454 | slave = slave_lookup(lacp, slave_); |
455 | if (!slave) { | |
456 | goto out; | |
457 | } | |
f1ce3514 | 458 | |
b468782a EJ |
459 | if (slave->status == LACP_CURRENT || slave->lacp->active) { |
460 | slave_set_expired(slave); | |
6aa74308 | 461 | } |
b468782a EJ |
462 | |
463 | out: | |
2339e869 | 464 | lacp_unlock(); |
6aa74308 EJ |
465 | } |
466 | ||
29985e75 | 467 | static bool |
bd3950dd | 468 | slave_may_enable__(struct slave *slave) OVS_REQUIRES(mutex) |
29985e75 EJ |
469 | { |
470 | /* The slave may be enabled if it's attached to an aggregator and its | |
471 | * partner is synchronized.*/ | |
9dd165e0 RK |
472 | return slave->attached && (slave->partner.state & LACP_STATE_SYNC |
473 | || (slave->lacp && slave->lacp->fallback_ab | |
474 | && slave->status == LACP_DEFAULTED)); | |
29985e75 EJ |
475 | } |
476 | ||
6aa74308 EJ |
477 | /* This function should be called before enabling 'slave_' to send or receive |
478 | * traffic. If it returns false, 'slave_' should not enabled. As a | |
479 | * convenience, returns true if 'lacp' is NULL. */ | |
480 | bool | |
481 | lacp_slave_may_enable(const struct lacp *lacp, const void *slave_) | |
b468782a | 482 | OVS_EXCLUDED(mutex) |
6aa74308 EJ |
483 | { |
484 | if (lacp) { | |
b468782a EJ |
485 | struct slave *slave; |
486 | bool ret; | |
487 | ||
2339e869 | 488 | lacp_lock(); |
b468782a EJ |
489 | slave = slave_lookup(lacp, slave_); |
490 | ret = slave ? slave_may_enable__(slave) : false; | |
2339e869 | 491 | lacp_unlock(); |
b468782a | 492 | return ret; |
6aa74308 EJ |
493 | } else { |
494 | return true; | |
495 | } | |
496 | } | |
497 | ||
44cb163f EJ |
498 | /* Returns true if partner information on 'slave_' is up to date. 'slave_' |
499 | * not being current, generally indicates a connectivity problem, or a | |
500 | * misconfigured (or broken) partner. */ | |
501 | bool | |
502 | lacp_slave_is_current(const struct lacp *lacp, const void *slave_) | |
b468782a | 503 | OVS_EXCLUDED(mutex) |
44cb163f | 504 | { |
b468782a EJ |
505 | struct slave *slave; |
506 | bool ret; | |
507 | ||
2339e869 | 508 | lacp_lock(); |
b468782a EJ |
509 | slave = slave_lookup(lacp, slave_); |
510 | ret = slave ? slave->status != LACP_DEFAULTED : false; | |
2339e869 | 511 | lacp_unlock(); |
b468782a | 512 | return ret; |
44cb163f EJ |
513 | } |
514 | ||
6aa74308 EJ |
515 | /* This function should be called periodically to update 'lacp'. */ |
516 | void | |
b468782a | 517 | lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) OVS_EXCLUDED(mutex) |
6aa74308 EJ |
518 | { |
519 | struct slave *slave; | |
520 | ||
2339e869 | 521 | lacp_lock(); |
6aa74308 | 522 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { |
20834809 | 523 | if (timer_expired(&slave->rx)) { |
f23d157c JS |
524 | enum slave_status old_status = slave->status; |
525 | ||
6aa74308 EJ |
526 | if (slave->status == LACP_CURRENT) { |
527 | slave_set_expired(slave); | |
528 | } else if (slave->status == LACP_EXPIRED) { | |
529 | slave_set_defaulted(slave); | |
530 | } | |
f23d157c JS |
531 | if (slave->status != old_status) { |
532 | seq_change(connectivity_seq_get()); | |
533 | } | |
6aa74308 | 534 | } |
6aa74308 EJ |
535 | } |
536 | ||
537 | if (lacp->update) { | |
538 | lacp_update_attached(lacp); | |
8b0c3c38 | 539 | seq_change(connectivity_seq_get()); |
6aa74308 EJ |
540 | } |
541 | ||
542 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { | |
77fdfa9b | 543 | struct lacp_info actor; |
6aa74308 | 544 | |
77fdfa9b | 545 | if (!slave_may_tx(slave)) { |
6aa74308 EJ |
546 | continue; |
547 | } | |
548 | ||
77fdfa9b EJ |
549 | slave_get_actor(slave, &actor); |
550 | ||
20834809 | 551 | if (timer_expired(&slave->tx) |
77fdfa9b | 552 | || !info_tx_equal(&actor, &slave->ntt_actor)) { |
cdcf42c6 | 553 | long long int duration; |
5f877369 | 554 | struct lacp_pdu pdu; |
77fdfa9b EJ |
555 | |
556 | slave->ntt_actor = actor; | |
557 | compose_lacp_pdu(&actor, &slave->partner, &pdu); | |
5f877369 | 558 | send_pdu(slave->aux, &pdu, sizeof pdu); |
00179c7b | 559 | slave->count_tx_pdus++; |
6aa74308 | 560 | |
bf83f7c8 EJ |
561 | duration = (slave->partner.state & LACP_STATE_TIME |
562 | ? LACP_FAST_TIME_TX | |
563 | : LACP_SLOW_TIME_TX); | |
cdcf42c6 EJ |
564 | |
565 | timer_set_duration(&slave->tx, duration); | |
f23d157c | 566 | seq_change(connectivity_seq_get()); |
77fdfa9b | 567 | } |
6aa74308 | 568 | } |
2339e869 | 569 | lacp_unlock(); |
6aa74308 EJ |
570 | } |
571 | ||
572 | /* Causes poll_block() to wake up when lacp_run() needs to be called again. */ | |
573 | void | |
b468782a | 574 | lacp_wait(struct lacp *lacp) OVS_EXCLUDED(mutex) |
6aa74308 EJ |
575 | { |
576 | struct slave *slave; | |
577 | ||
2339e869 | 578 | lacp_lock(); |
6aa74308 EJ |
579 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { |
580 | if (slave_may_tx(slave)) { | |
20834809 | 581 | timer_wait(&slave->tx); |
6aa74308 EJ |
582 | } |
583 | ||
584 | if (slave->status != LACP_DEFAULTED) { | |
20834809 | 585 | timer_wait(&slave->rx); |
6aa74308 EJ |
586 | } |
587 | } | |
2339e869 | 588 | lacp_unlock(); |
6aa74308 EJ |
589 | } |
590 | \f | |
591 | /* Static Helpers. */ | |
592 | ||
20601813 | 593 | /* Updates the attached status of all slaves controlled by 'lacp' and sets its |
6aa74308 EJ |
594 | * negotiated parameter to true if any slaves are attachable. */ |
595 | static void | |
bd3950dd | 596 | lacp_update_attached(struct lacp *lacp) OVS_REQUIRES(mutex) |
6aa74308 EJ |
597 | { |
598 | struct slave *lead, *slave; | |
599 | struct lacp_info lead_pri; | |
86c8462d | 600 | bool lead_enable; |
6aa74308 EJ |
601 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10); |
602 | ||
603 | lacp->update = false; | |
604 | ||
605 | lead = NULL; | |
86c8462d | 606 | lead_enable = false; |
6aa74308 EJ |
607 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { |
608 | struct lacp_info pri; | |
609 | ||
bdebeece | 610 | slave->attached = false; |
6aa74308 EJ |
611 | |
612 | /* XXX: In the future allow users to configure the expected system ID. | |
613 | * For now just special case loopback. */ | |
77fdfa9b | 614 | if (eth_addr_equals(slave->partner.sys_id, slave->lacp->sys_id)) { |
6aa74308 EJ |
615 | VLOG_WARN_RL(&rl, "slave %s: Loopback detected. Slave is " |
616 | "connected to its own bond", slave->name); | |
6aa74308 EJ |
617 | continue; |
618 | } | |
619 | ||
620 | if (slave->status == LACP_DEFAULTED) { | |
9dd165e0 RK |
621 | if (lacp->fallback_ab) { |
622 | slave->attached = true; | |
623 | } | |
6aa74308 EJ |
624 | continue; |
625 | } | |
626 | ||
bdebeece | 627 | slave->attached = true; |
6aa74308 | 628 | slave_get_priority(slave, &pri); |
86c8462d | 629 | bool enable = slave_may_enable__(slave); |
6aa74308 | 630 | |
86c8462d BP |
631 | if (!lead |
632 | || enable > lead_enable | |
633 | || (enable == lead_enable | |
634 | && memcmp(&pri, &lead_pri, sizeof pri) < 0)) { | |
6aa74308 | 635 | lead = slave; |
86c8462d | 636 | lead_enable = enable; |
6aa74308 EJ |
637 | lead_pri = pri; |
638 | } | |
639 | } | |
640 | ||
641 | lacp->negotiated = lead != NULL; | |
642 | ||
643 | if (lead) { | |
644 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { | |
9dd165e0 RK |
645 | if ((lacp->fallback_ab && slave->status == LACP_DEFAULTED) |
646 | || lead->partner.key != slave->partner.key | |
6aa74308 EJ |
647 | || !eth_addr_equals(lead->partner.sys_id, |
648 | slave->partner.sys_id)) { | |
649 | slave->attached = false; | |
650 | } | |
651 | } | |
652 | } | |
653 | } | |
654 | ||
655 | static void | |
bd3950dd | 656 | slave_destroy(struct slave *slave) OVS_REQUIRES(mutex) |
6aa74308 EJ |
657 | { |
658 | if (slave) { | |
659 | struct lacp *lacp = slave->lacp; | |
660 | ||
661 | lacp->update = true; | |
662 | hmap_remove(&lacp->slaves, &slave->node); | |
663 | ||
664 | if (lacp->key_slave == slave) { | |
665 | struct hmap_node *slave_node = hmap_first(&lacp->slaves); | |
666 | ||
667 | if (slave_node) { | |
668 | lacp->key_slave = CONTAINER_OF(slave_node, struct slave, node); | |
669 | } else { | |
670 | lacp->key_slave = NULL; | |
671 | } | |
672 | } | |
673 | ||
674 | free(slave->name); | |
675 | free(slave); | |
676 | } | |
677 | } | |
678 | ||
679 | static void | |
bd3950dd | 680 | slave_set_defaulted(struct slave *slave) OVS_REQUIRES(mutex) |
6aa74308 EJ |
681 | { |
682 | memset(&slave->partner, 0, sizeof slave->partner); | |
683 | ||
6aa74308 EJ |
684 | slave->lacp->update = true; |
685 | slave->status = LACP_DEFAULTED; | |
686 | } | |
687 | ||
688 | static void | |
bd3950dd | 689 | slave_set_expired(struct slave *slave) OVS_REQUIRES(mutex) |
6aa74308 EJ |
690 | { |
691 | slave->status = LACP_EXPIRED; | |
692 | slave->partner.state |= LACP_STATE_TIME; | |
693 | slave->partner.state &= ~LACP_STATE_SYNC; | |
cdcf42c6 | 694 | |
bf83f7c8 | 695 | timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX); |
6aa74308 EJ |
696 | } |
697 | ||
698 | static void | |
77fdfa9b | 699 | slave_get_actor(struct slave *slave, struct lacp_info *actor) |
bd3950dd | 700 | OVS_REQUIRES(mutex) |
6aa74308 | 701 | { |
da2f7b8f | 702 | struct lacp *lacp = slave->lacp; |
e1ce3f2d | 703 | uint16_t key; |
6aa74308 EJ |
704 | uint8_t state = 0; |
705 | ||
da2f7b8f | 706 | if (lacp->active) { |
6aa74308 EJ |
707 | state |= LACP_STATE_ACT; |
708 | } | |
709 | ||
bf83f7c8 | 710 | if (lacp->fast) { |
269340fa EJ |
711 | state |= LACP_STATE_TIME; |
712 | } | |
713 | ||
6aa74308 EJ |
714 | if (slave->attached) { |
715 | state |= LACP_STATE_SYNC; | |
716 | } | |
717 | ||
718 | if (slave->status == LACP_DEFAULTED) { | |
719 | state |= LACP_STATE_DEF; | |
720 | } | |
721 | ||
722 | if (slave->status == LACP_EXPIRED) { | |
723 | state |= LACP_STATE_EXP; | |
724 | } | |
725 | ||
b20a8f7c | 726 | if (hmap_count(&lacp->slaves) > 1) { |
6aa74308 EJ |
727 | state |= LACP_STATE_AGG; |
728 | } | |
729 | ||
da2f7b8f | 730 | if (slave->attached || !lacp->negotiated) { |
6aa74308 EJ |
731 | state |= LACP_STATE_COL | LACP_STATE_DIST; |
732 | } | |
733 | ||
e1ce3f2d EJ |
734 | key = lacp->key_slave->key; |
735 | if (!key) { | |
736 | key = lacp->key_slave->port_id; | |
737 | } | |
738 | ||
77fdfa9b | 739 | actor->state = state; |
e1ce3f2d | 740 | actor->key = htons(key); |
77fdfa9b EJ |
741 | actor->port_priority = htons(slave->port_priority); |
742 | actor->port_id = htons(slave->port_id); | |
da2f7b8f | 743 | actor->sys_priority = htons(lacp->sys_priority); |
74ff3298 | 744 | actor->sys_id = lacp->sys_id; |
6aa74308 EJ |
745 | } |
746 | ||
747 | /* Given 'slave', populates 'priority' with data representing its LACP link | |
748 | * priority. If two priority objects populated by this function are compared | |
749 | * using memcmp, the higher priority link will be less than the lower priority | |
750 | * link. */ | |
751 | static void | |
752 | slave_get_priority(struct slave *slave, struct lacp_info *priority) | |
bd3950dd | 753 | OVS_REQUIRES(mutex) |
6aa74308 EJ |
754 | { |
755 | uint16_t partner_priority, actor_priority; | |
756 | ||
757 | /* Choose the lacp_info of the higher priority system by comparing their | |
758 | * system priorities and mac addresses. */ | |
77fdfa9b | 759 | actor_priority = slave->lacp->sys_priority; |
6aa74308 EJ |
760 | partner_priority = ntohs(slave->partner.sys_priority); |
761 | if (actor_priority < partner_priority) { | |
77fdfa9b | 762 | slave_get_actor(slave, priority); |
6aa74308 EJ |
763 | } else if (partner_priority < actor_priority) { |
764 | *priority = slave->partner; | |
77fdfa9b | 765 | } else if (eth_addr_compare_3way(slave->lacp->sys_id, |
6aa74308 | 766 | slave->partner.sys_id) < 0) { |
77fdfa9b | 767 | slave_get_actor(slave, priority); |
6aa74308 EJ |
768 | } else { |
769 | *priority = slave->partner; | |
770 | } | |
771 | ||
772 | /* Key and state are not used in priority comparisons. */ | |
773 | priority->key = 0; | |
774 | priority->state = 0; | |
775 | } | |
776 | ||
777 | static bool | |
bd3950dd | 778 | slave_may_tx(const struct slave *slave) OVS_REQUIRES(mutex) |
6aa74308 EJ |
779 | { |
780 | return slave->lacp->active || slave->status != LACP_DEFAULTED; | |
781 | } | |
782 | ||
783 | static struct slave * | |
bd3950dd | 784 | slave_lookup(const struct lacp *lacp, const void *slave_) OVS_REQUIRES(mutex) |
6aa74308 EJ |
785 | { |
786 | struct slave *slave; | |
787 | ||
788 | HMAP_FOR_EACH_IN_BUCKET (slave, node, hash_pointer(slave_, 0), | |
789 | &lacp->slaves) { | |
790 | if (slave->aux == slave_) { | |
791 | return slave; | |
792 | } | |
793 | } | |
794 | ||
795 | return NULL; | |
796 | } | |
77fdfa9b EJ |
797 | |
798 | /* Two lacp_info structures are tx_equal if and only if they do not differ in | |
799 | * ways which would require a lacp_pdu transmission. */ | |
800 | static bool | |
801 | info_tx_equal(struct lacp_info *a, struct lacp_info *b) | |
802 | { | |
803 | ||
804 | /* LACP specification dictates that we transmit whenever the actor and | |
805 | * remote_actor differ in the following fields: Port, Port Priority, | |
806 | * System, System Priority, Aggregation Key, Activity State, Timeout State, | |
807 | * Sync State, and Aggregation State. The state flags are most likely to | |
808 | * change so are checked first. */ | |
809 | return !((a->state ^ b->state) & (LACP_STATE_ACT | |
810 | | LACP_STATE_TIME | |
811 | | LACP_STATE_SYNC | |
812 | | LACP_STATE_AGG)) | |
813 | && a->port_id == b->port_id | |
814 | && a->port_priority == b->port_priority | |
815 | && a->key == b->key | |
816 | && a->sys_priority == b->sys_priority | |
817 | && eth_addr_equals(a->sys_id, b->sys_id); | |
818 | } | |
6aa74308 EJ |
819 | \f |
820 | static struct lacp * | |
344e21d4 | 821 | lacp_find(const char *name) OVS_REQUIRES(mutex) |
6aa74308 EJ |
822 | { |
823 | struct lacp *lacp; | |
824 | ||
b468782a | 825 | LIST_FOR_EACH (lacp, node, all_lacps) { |
6aa74308 EJ |
826 | if (!strcmp(lacp->name, name)) { |
827 | return lacp; | |
828 | } | |
829 | } | |
830 | ||
831 | return NULL; | |
832 | } | |
833 | ||
834 | static void | |
835 | ds_put_lacp_state(struct ds *ds, uint8_t state) | |
836 | { | |
837 | if (state & LACP_STATE_ACT) { | |
1c1df7f1 | 838 | ds_put_cstr(ds, " activity"); |
6aa74308 EJ |
839 | } |
840 | ||
841 | if (state & LACP_STATE_TIME) { | |
1c1df7f1 | 842 | ds_put_cstr(ds, " timeout"); |
6aa74308 EJ |
843 | } |
844 | ||
845 | if (state & LACP_STATE_AGG) { | |
1c1df7f1 | 846 | ds_put_cstr(ds, " aggregation"); |
6aa74308 EJ |
847 | } |
848 | ||
849 | if (state & LACP_STATE_SYNC) { | |
1c1df7f1 | 850 | ds_put_cstr(ds, " synchronized"); |
6aa74308 EJ |
851 | } |
852 | ||
853 | if (state & LACP_STATE_COL) { | |
1c1df7f1 | 854 | ds_put_cstr(ds, " collecting"); |
6aa74308 EJ |
855 | } |
856 | ||
857 | if (state & LACP_STATE_DIST) { | |
1c1df7f1 | 858 | ds_put_cstr(ds, " distributing"); |
6aa74308 EJ |
859 | } |
860 | ||
861 | if (state & LACP_STATE_DEF) { | |
1c1df7f1 | 862 | ds_put_cstr(ds, " defaulted"); |
6aa74308 EJ |
863 | } |
864 | ||
865 | if (state & LACP_STATE_EXP) { | |
1c1df7f1 | 866 | ds_put_cstr(ds, " expired"); |
6aa74308 EJ |
867 | } |
868 | } | |
869 | ||
870 | static void | |
344e21d4 | 871 | lacp_print_details(struct ds *ds, struct lacp *lacp) OVS_REQUIRES(mutex) |
6aa74308 | 872 | { |
431d6a6a EJ |
873 | struct shash slave_shash = SHASH_INITIALIZER(&slave_shash); |
874 | const struct shash_node **sorted_slaves = NULL; | |
875 | ||
6aa74308 | 876 | struct slave *slave; |
431d6a6a | 877 | int i; |
6aa74308 | 878 | |
5dab8ece JP |
879 | ds_put_format(ds, "---- %s ----\n", lacp->name); |
880 | ds_put_format(ds, "\tstatus: %s", lacp->active ? "active" : "passive"); | |
20601813 | 881 | if (lacp->negotiated) { |
5dab8ece | 882 | ds_put_cstr(ds, " negotiated"); |
20601813 | 883 | } |
5dab8ece | 884 | ds_put_cstr(ds, "\n"); |
20601813 | 885 | |
5dab8ece JP |
886 | ds_put_format(ds, "\tsys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(lacp->sys_id)); |
887 | ds_put_format(ds, "\tsys_priority: %u\n", lacp->sys_priority); | |
888 | ds_put_cstr(ds, "\taggregation key: "); | |
6aa74308 | 889 | if (lacp->key_slave) { |
ab3a09c8 AS |
890 | ds_put_format(ds, "%u", lacp->key_slave->key |
891 | ? lacp->key_slave->key | |
892 | : lacp->key_slave->port_id); | |
6aa74308 | 893 | } else { |
5dab8ece | 894 | ds_put_cstr(ds, "none"); |
6aa74308 | 895 | } |
5dab8ece | 896 | ds_put_cstr(ds, "\n"); |
6aa74308 | 897 | |
5dab8ece | 898 | ds_put_cstr(ds, "\tlacp_time: "); |
bf83f7c8 | 899 | if (lacp->fast) { |
5dab8ece | 900 | ds_put_cstr(ds, "fast\n"); |
bf83f7c8 | 901 | } else { |
5dab8ece | 902 | ds_put_cstr(ds, "slow\n"); |
cdcf42c6 EJ |
903 | } |
904 | ||
6aa74308 | 905 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { |
431d6a6a EJ |
906 | shash_add(&slave_shash, slave->name, slave); |
907 | } | |
908 | sorted_slaves = shash_sort(&slave_shash); | |
909 | ||
910 | for (i = 0; i < shash_count(&slave_shash); i++) { | |
6aa74308 | 911 | char *status; |
77fdfa9b | 912 | struct lacp_info actor; |
6aa74308 | 913 | |
431d6a6a | 914 | slave = sorted_slaves[i]->data; |
77fdfa9b | 915 | slave_get_actor(slave, &actor); |
6aa74308 EJ |
916 | switch (slave->status) { |
917 | case LACP_CURRENT: | |
918 | status = "current"; | |
919 | break; | |
920 | case LACP_EXPIRED: | |
921 | status = "expired"; | |
922 | break; | |
923 | case LACP_DEFAULTED: | |
924 | status = "defaulted"; | |
925 | break; | |
926 | default: | |
428b2edd | 927 | OVS_NOT_REACHED(); |
6aa74308 EJ |
928 | } |
929 | ||
5dab8ece | 930 | ds_put_format(ds, "\nslave: %s: %s %s\n", slave->name, status, |
0f1a47f1 | 931 | slave->attached ? "attached" : "detached"); |
5dab8ece JP |
932 | ds_put_format(ds, "\tport_id: %u\n", slave->port_id); |
933 | ds_put_format(ds, "\tport_priority: %u\n", slave->port_priority); | |
29985e75 EJ |
934 | ds_put_format(ds, "\tmay_enable: %s\n", (slave_may_enable__(slave) |
935 | ? "true" : "false")); | |
6aa74308 | 936 | |
5dab8ece | 937 | ds_put_format(ds, "\n\tactor sys_id: " ETH_ADDR_FMT "\n", |
77fdfa9b | 938 | ETH_ADDR_ARGS(actor.sys_id)); |
5dab8ece | 939 | ds_put_format(ds, "\tactor sys_priority: %u\n", |
77fdfa9b | 940 | ntohs(actor.sys_priority)); |
5dab8ece | 941 | ds_put_format(ds, "\tactor port_id: %u\n", |
77fdfa9b | 942 | ntohs(actor.port_id)); |
5dab8ece | 943 | ds_put_format(ds, "\tactor port_priority: %u\n", |
77fdfa9b | 944 | ntohs(actor.port_priority)); |
5dab8ece | 945 | ds_put_format(ds, "\tactor key: %u\n", |
77fdfa9b | 946 | ntohs(actor.key)); |
1c1df7f1 | 947 | ds_put_cstr(ds, "\tactor state:"); |
5dab8ece JP |
948 | ds_put_lacp_state(ds, actor.state); |
949 | ds_put_cstr(ds, "\n\n"); | |
6aa74308 | 950 | |
5dab8ece | 951 | ds_put_format(ds, "\tpartner sys_id: " ETH_ADDR_FMT "\n", |
6aa74308 | 952 | ETH_ADDR_ARGS(slave->partner.sys_id)); |
5dab8ece | 953 | ds_put_format(ds, "\tpartner sys_priority: %u\n", |
6aa74308 | 954 | ntohs(slave->partner.sys_priority)); |
5dab8ece | 955 | ds_put_format(ds, "\tpartner port_id: %u\n", |
6aa74308 | 956 | ntohs(slave->partner.port_id)); |
5dab8ece | 957 | ds_put_format(ds, "\tpartner port_priority: %u\n", |
6aa74308 | 958 | ntohs(slave->partner.port_priority)); |
5dab8ece | 959 | ds_put_format(ds, "\tpartner key: %u\n", |
6aa74308 | 960 | ntohs(slave->partner.key)); |
1c1df7f1 | 961 | ds_put_cstr(ds, "\tpartner state:"); |
5dab8ece JP |
962 | ds_put_lacp_state(ds, slave->partner.state); |
963 | ds_put_cstr(ds, "\n"); | |
964 | } | |
431d6a6a EJ |
965 | |
966 | shash_destroy(&slave_shash); | |
967 | free(sorted_slaves); | |
5dab8ece JP |
968 | } |
969 | ||
970 | static void | |
0e15264f | 971 | lacp_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[], |
b468782a | 972 | void *aux OVS_UNUSED) OVS_EXCLUDED(mutex) |
5dab8ece JP |
973 | { |
974 | struct ds ds = DS_EMPTY_INITIALIZER; | |
975 | struct lacp *lacp; | |
976 | ||
2339e869 | 977 | lacp_lock(); |
0e15264f BP |
978 | if (argc > 1) { |
979 | lacp = lacp_find(argv[1]); | |
5dab8ece | 980 | if (!lacp) { |
bde9f75d | 981 | unixctl_command_reply_error(conn, "no such lacp object"); |
b468782a | 982 | goto out; |
5dab8ece JP |
983 | } |
984 | lacp_print_details(&ds, lacp); | |
985 | } else { | |
b468782a | 986 | LIST_FOR_EACH (lacp, node, all_lacps) { |
5dab8ece JP |
987 | lacp_print_details(&ds, lacp); |
988 | } | |
6aa74308 EJ |
989 | } |
990 | ||
bde9f75d | 991 | unixctl_command_reply(conn, ds_cstr(&ds)); |
6aa74308 | 992 | ds_destroy(&ds); |
b468782a EJ |
993 | |
994 | out: | |
2339e869 | 995 | lacp_unlock(); |
6aa74308 | 996 | } |
50b9699f NM |
997 | |
998 | /* Extract a snapshot of the current state and counters for a slave port. | |
999 | Return false if the slave is not active. */ | |
1000 | bool | |
1001 | lacp_get_slave_stats(const struct lacp *lacp, const void *slave_, struct lacp_slave_stats *stats) | |
1002 | OVS_EXCLUDED(mutex) | |
1003 | { | |
1004 | struct slave *slave; | |
1005 | struct lacp_info actor; | |
1006 | bool ret; | |
1007 | ||
1008 | ovs_mutex_lock(&mutex); | |
1009 | ||
1010 | slave = slave_lookup(lacp, slave_); | |
1011 | if (slave) { | |
1012 | ret = true; | |
1013 | slave_get_actor(slave, &actor); | |
74ff3298 JR |
1014 | stats->dot3adAggPortActorSystemID = actor.sys_id; |
1015 | stats->dot3adAggPortPartnerOperSystemID = slave->partner.sys_id; | |
50b9699f NM |
1016 | stats->dot3adAggPortAttachedAggID = (lacp->key_slave->key ? |
1017 | lacp->key_slave->key : | |
1018 | lacp->key_slave->port_id); | |
1019 | ||
1020 | /* Construct my admin-state. Assume aggregation is configured on. */ | |
1021 | stats->dot3adAggPortActorAdminState = LACP_STATE_AGG; | |
1022 | if (lacp->active) { | |
1023 | stats->dot3adAggPortActorAdminState |= LACP_STATE_ACT; | |
1024 | } | |
1025 | if (lacp->fast) { | |
1026 | stats->dot3adAggPortActorAdminState |= LACP_STATE_TIME; | |
1027 | } | |
1028 | /* XXX Not sure how to know the partner admin state. It | |
1029 | * might have to be captured and remembered during the | |
1030 | * negotiation phase. | |
1031 | */ | |
1032 | stats->dot3adAggPortPartnerAdminState = 0; | |
1033 | ||
1034 | stats->dot3adAggPortActorOperState = actor.state; | |
1035 | stats->dot3adAggPortPartnerOperState = slave->partner.state; | |
1036 | ||
1037 | /* Read out the latest counters */ | |
1038 | stats->dot3adAggPortStatsLACPDUsRx = slave->count_rx_pdus; | |
1039 | stats->dot3adAggPortStatsIllegalRx = slave->count_rx_pdus_bad; | |
1040 | stats->dot3adAggPortStatsLACPDUsTx = slave->count_tx_pdus; | |
1041 | } else { | |
1042 | ret = false; | |
1043 | } | |
1044 | ovs_mutex_unlock(&mutex); | |
1045 | return ret; | |
1046 | ||
1047 | } |