]>
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); | |
539 | } | |
540 | ||
541 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { | |
77fdfa9b | 542 | struct lacp_info actor; |
6aa74308 | 543 | |
77fdfa9b | 544 | if (!slave_may_tx(slave)) { |
6aa74308 EJ |
545 | continue; |
546 | } | |
547 | ||
77fdfa9b EJ |
548 | slave_get_actor(slave, &actor); |
549 | ||
20834809 | 550 | if (timer_expired(&slave->tx) |
77fdfa9b | 551 | || !info_tx_equal(&actor, &slave->ntt_actor)) { |
cdcf42c6 | 552 | long long int duration; |
5f877369 | 553 | struct lacp_pdu pdu; |
77fdfa9b EJ |
554 | |
555 | slave->ntt_actor = actor; | |
556 | compose_lacp_pdu(&actor, &slave->partner, &pdu); | |
5f877369 | 557 | send_pdu(slave->aux, &pdu, sizeof pdu); |
00179c7b | 558 | slave->count_tx_pdus++; |
6aa74308 | 559 | |
bf83f7c8 EJ |
560 | duration = (slave->partner.state & LACP_STATE_TIME |
561 | ? LACP_FAST_TIME_TX | |
562 | : LACP_SLOW_TIME_TX); | |
cdcf42c6 EJ |
563 | |
564 | timer_set_duration(&slave->tx, duration); | |
f23d157c | 565 | seq_change(connectivity_seq_get()); |
77fdfa9b | 566 | } |
6aa74308 | 567 | } |
2339e869 | 568 | lacp_unlock(); |
6aa74308 EJ |
569 | } |
570 | ||
571 | /* Causes poll_block() to wake up when lacp_run() needs to be called again. */ | |
572 | void | |
b468782a | 573 | lacp_wait(struct lacp *lacp) OVS_EXCLUDED(mutex) |
6aa74308 EJ |
574 | { |
575 | struct slave *slave; | |
576 | ||
2339e869 | 577 | lacp_lock(); |
6aa74308 EJ |
578 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { |
579 | if (slave_may_tx(slave)) { | |
20834809 | 580 | timer_wait(&slave->tx); |
6aa74308 EJ |
581 | } |
582 | ||
583 | if (slave->status != LACP_DEFAULTED) { | |
20834809 | 584 | timer_wait(&slave->rx); |
6aa74308 EJ |
585 | } |
586 | } | |
2339e869 | 587 | lacp_unlock(); |
6aa74308 EJ |
588 | } |
589 | \f | |
590 | /* Static Helpers. */ | |
591 | ||
20601813 | 592 | /* Updates the attached status of all slaves controlled by 'lacp' and sets its |
6aa74308 EJ |
593 | * negotiated parameter to true if any slaves are attachable. */ |
594 | static void | |
bd3950dd | 595 | lacp_update_attached(struct lacp *lacp) OVS_REQUIRES(mutex) |
6aa74308 EJ |
596 | { |
597 | struct slave *lead, *slave; | |
598 | struct lacp_info lead_pri; | |
86c8462d | 599 | bool lead_enable; |
6aa74308 EJ |
600 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10); |
601 | ||
602 | lacp->update = false; | |
603 | ||
604 | lead = NULL; | |
86c8462d | 605 | lead_enable = false; |
6aa74308 EJ |
606 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { |
607 | struct lacp_info pri; | |
608 | ||
bdebeece | 609 | slave->attached = false; |
6aa74308 EJ |
610 | |
611 | /* XXX: In the future allow users to configure the expected system ID. | |
612 | * For now just special case loopback. */ | |
77fdfa9b | 613 | if (eth_addr_equals(slave->partner.sys_id, slave->lacp->sys_id)) { |
6aa74308 EJ |
614 | VLOG_WARN_RL(&rl, "slave %s: Loopback detected. Slave is " |
615 | "connected to its own bond", slave->name); | |
6aa74308 EJ |
616 | continue; |
617 | } | |
618 | ||
619 | if (slave->status == LACP_DEFAULTED) { | |
9dd165e0 RK |
620 | if (lacp->fallback_ab) { |
621 | slave->attached = true; | |
622 | } | |
6aa74308 EJ |
623 | continue; |
624 | } | |
625 | ||
bdebeece | 626 | slave->attached = true; |
6aa74308 | 627 | slave_get_priority(slave, &pri); |
86c8462d | 628 | bool enable = slave_may_enable__(slave); |
6aa74308 | 629 | |
86c8462d BP |
630 | if (!lead |
631 | || enable > lead_enable | |
632 | || (enable == lead_enable | |
633 | && memcmp(&pri, &lead_pri, sizeof pri) < 0)) { | |
6aa74308 | 634 | lead = slave; |
86c8462d | 635 | lead_enable = enable; |
6aa74308 EJ |
636 | lead_pri = pri; |
637 | } | |
638 | } | |
639 | ||
640 | lacp->negotiated = lead != NULL; | |
641 | ||
642 | if (lead) { | |
643 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { | |
9dd165e0 RK |
644 | if ((lacp->fallback_ab && slave->status == LACP_DEFAULTED) |
645 | || lead->partner.key != slave->partner.key | |
6aa74308 EJ |
646 | || !eth_addr_equals(lead->partner.sys_id, |
647 | slave->partner.sys_id)) { | |
648 | slave->attached = false; | |
649 | } | |
650 | } | |
651 | } | |
652 | } | |
653 | ||
654 | static void | |
bd3950dd | 655 | slave_destroy(struct slave *slave) OVS_REQUIRES(mutex) |
6aa74308 EJ |
656 | { |
657 | if (slave) { | |
658 | struct lacp *lacp = slave->lacp; | |
659 | ||
660 | lacp->update = true; | |
661 | hmap_remove(&lacp->slaves, &slave->node); | |
662 | ||
663 | if (lacp->key_slave == slave) { | |
664 | struct hmap_node *slave_node = hmap_first(&lacp->slaves); | |
665 | ||
666 | if (slave_node) { | |
667 | lacp->key_slave = CONTAINER_OF(slave_node, struct slave, node); | |
668 | } else { | |
669 | lacp->key_slave = NULL; | |
670 | } | |
671 | } | |
672 | ||
673 | free(slave->name); | |
674 | free(slave); | |
675 | } | |
676 | } | |
677 | ||
678 | static void | |
bd3950dd | 679 | slave_set_defaulted(struct slave *slave) OVS_REQUIRES(mutex) |
6aa74308 EJ |
680 | { |
681 | memset(&slave->partner, 0, sizeof slave->partner); | |
682 | ||
6aa74308 EJ |
683 | slave->lacp->update = true; |
684 | slave->status = LACP_DEFAULTED; | |
685 | } | |
686 | ||
687 | static void | |
bd3950dd | 688 | slave_set_expired(struct slave *slave) OVS_REQUIRES(mutex) |
6aa74308 EJ |
689 | { |
690 | slave->status = LACP_EXPIRED; | |
691 | slave->partner.state |= LACP_STATE_TIME; | |
692 | slave->partner.state &= ~LACP_STATE_SYNC; | |
cdcf42c6 | 693 | |
bf83f7c8 | 694 | timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX); |
6aa74308 EJ |
695 | } |
696 | ||
697 | static void | |
77fdfa9b | 698 | slave_get_actor(struct slave *slave, struct lacp_info *actor) |
bd3950dd | 699 | OVS_REQUIRES(mutex) |
6aa74308 | 700 | { |
da2f7b8f | 701 | struct lacp *lacp = slave->lacp; |
e1ce3f2d | 702 | uint16_t key; |
6aa74308 EJ |
703 | uint8_t state = 0; |
704 | ||
da2f7b8f | 705 | if (lacp->active) { |
6aa74308 EJ |
706 | state |= LACP_STATE_ACT; |
707 | } | |
708 | ||
bf83f7c8 | 709 | if (lacp->fast) { |
269340fa EJ |
710 | state |= LACP_STATE_TIME; |
711 | } | |
712 | ||
6aa74308 EJ |
713 | if (slave->attached) { |
714 | state |= LACP_STATE_SYNC; | |
715 | } | |
716 | ||
717 | if (slave->status == LACP_DEFAULTED) { | |
718 | state |= LACP_STATE_DEF; | |
719 | } | |
720 | ||
721 | if (slave->status == LACP_EXPIRED) { | |
722 | state |= LACP_STATE_EXP; | |
723 | } | |
724 | ||
b20a8f7c | 725 | if (hmap_count(&lacp->slaves) > 1) { |
6aa74308 EJ |
726 | state |= LACP_STATE_AGG; |
727 | } | |
728 | ||
da2f7b8f | 729 | if (slave->attached || !lacp->negotiated) { |
6aa74308 EJ |
730 | state |= LACP_STATE_COL | LACP_STATE_DIST; |
731 | } | |
732 | ||
e1ce3f2d EJ |
733 | key = lacp->key_slave->key; |
734 | if (!key) { | |
735 | key = lacp->key_slave->port_id; | |
736 | } | |
737 | ||
77fdfa9b | 738 | actor->state = state; |
e1ce3f2d | 739 | actor->key = htons(key); |
77fdfa9b EJ |
740 | actor->port_priority = htons(slave->port_priority); |
741 | actor->port_id = htons(slave->port_id); | |
da2f7b8f | 742 | actor->sys_priority = htons(lacp->sys_priority); |
74ff3298 | 743 | actor->sys_id = lacp->sys_id; |
6aa74308 EJ |
744 | } |
745 | ||
746 | /* Given 'slave', populates 'priority' with data representing its LACP link | |
747 | * priority. If two priority objects populated by this function are compared | |
748 | * using memcmp, the higher priority link will be less than the lower priority | |
749 | * link. */ | |
750 | static void | |
751 | slave_get_priority(struct slave *slave, struct lacp_info *priority) | |
bd3950dd | 752 | OVS_REQUIRES(mutex) |
6aa74308 EJ |
753 | { |
754 | uint16_t partner_priority, actor_priority; | |
755 | ||
756 | /* Choose the lacp_info of the higher priority system by comparing their | |
757 | * system priorities and mac addresses. */ | |
77fdfa9b | 758 | actor_priority = slave->lacp->sys_priority; |
6aa74308 EJ |
759 | partner_priority = ntohs(slave->partner.sys_priority); |
760 | if (actor_priority < partner_priority) { | |
77fdfa9b | 761 | slave_get_actor(slave, priority); |
6aa74308 EJ |
762 | } else if (partner_priority < actor_priority) { |
763 | *priority = slave->partner; | |
77fdfa9b | 764 | } else if (eth_addr_compare_3way(slave->lacp->sys_id, |
6aa74308 | 765 | slave->partner.sys_id) < 0) { |
77fdfa9b | 766 | slave_get_actor(slave, priority); |
6aa74308 EJ |
767 | } else { |
768 | *priority = slave->partner; | |
769 | } | |
770 | ||
771 | /* Key and state are not used in priority comparisons. */ | |
772 | priority->key = 0; | |
773 | priority->state = 0; | |
774 | } | |
775 | ||
776 | static bool | |
bd3950dd | 777 | slave_may_tx(const struct slave *slave) OVS_REQUIRES(mutex) |
6aa74308 EJ |
778 | { |
779 | return slave->lacp->active || slave->status != LACP_DEFAULTED; | |
780 | } | |
781 | ||
782 | static struct slave * | |
bd3950dd | 783 | slave_lookup(const struct lacp *lacp, const void *slave_) OVS_REQUIRES(mutex) |
6aa74308 EJ |
784 | { |
785 | struct slave *slave; | |
786 | ||
787 | HMAP_FOR_EACH_IN_BUCKET (slave, node, hash_pointer(slave_, 0), | |
788 | &lacp->slaves) { | |
789 | if (slave->aux == slave_) { | |
790 | return slave; | |
791 | } | |
792 | } | |
793 | ||
794 | return NULL; | |
795 | } | |
77fdfa9b EJ |
796 | |
797 | /* Two lacp_info structures are tx_equal if and only if they do not differ in | |
798 | * ways which would require a lacp_pdu transmission. */ | |
799 | static bool | |
800 | info_tx_equal(struct lacp_info *a, struct lacp_info *b) | |
801 | { | |
802 | ||
803 | /* LACP specification dictates that we transmit whenever the actor and | |
804 | * remote_actor differ in the following fields: Port, Port Priority, | |
805 | * System, System Priority, Aggregation Key, Activity State, Timeout State, | |
806 | * Sync State, and Aggregation State. The state flags are most likely to | |
807 | * change so are checked first. */ | |
808 | return !((a->state ^ b->state) & (LACP_STATE_ACT | |
809 | | LACP_STATE_TIME | |
810 | | LACP_STATE_SYNC | |
811 | | LACP_STATE_AGG)) | |
812 | && a->port_id == b->port_id | |
813 | && a->port_priority == b->port_priority | |
814 | && a->key == b->key | |
815 | && a->sys_priority == b->sys_priority | |
816 | && eth_addr_equals(a->sys_id, b->sys_id); | |
817 | } | |
6aa74308 EJ |
818 | \f |
819 | static struct lacp * | |
344e21d4 | 820 | lacp_find(const char *name) OVS_REQUIRES(mutex) |
6aa74308 EJ |
821 | { |
822 | struct lacp *lacp; | |
823 | ||
b468782a | 824 | LIST_FOR_EACH (lacp, node, all_lacps) { |
6aa74308 EJ |
825 | if (!strcmp(lacp->name, name)) { |
826 | return lacp; | |
827 | } | |
828 | } | |
829 | ||
830 | return NULL; | |
831 | } | |
832 | ||
833 | static void | |
834 | ds_put_lacp_state(struct ds *ds, uint8_t state) | |
835 | { | |
836 | if (state & LACP_STATE_ACT) { | |
1c1df7f1 | 837 | ds_put_cstr(ds, " activity"); |
6aa74308 EJ |
838 | } |
839 | ||
840 | if (state & LACP_STATE_TIME) { | |
1c1df7f1 | 841 | ds_put_cstr(ds, " timeout"); |
6aa74308 EJ |
842 | } |
843 | ||
844 | if (state & LACP_STATE_AGG) { | |
1c1df7f1 | 845 | ds_put_cstr(ds, " aggregation"); |
6aa74308 EJ |
846 | } |
847 | ||
848 | if (state & LACP_STATE_SYNC) { | |
1c1df7f1 | 849 | ds_put_cstr(ds, " synchronized"); |
6aa74308 EJ |
850 | } |
851 | ||
852 | if (state & LACP_STATE_COL) { | |
1c1df7f1 | 853 | ds_put_cstr(ds, " collecting"); |
6aa74308 EJ |
854 | } |
855 | ||
856 | if (state & LACP_STATE_DIST) { | |
1c1df7f1 | 857 | ds_put_cstr(ds, " distributing"); |
6aa74308 EJ |
858 | } |
859 | ||
860 | if (state & LACP_STATE_DEF) { | |
1c1df7f1 | 861 | ds_put_cstr(ds, " defaulted"); |
6aa74308 EJ |
862 | } |
863 | ||
864 | if (state & LACP_STATE_EXP) { | |
1c1df7f1 | 865 | ds_put_cstr(ds, " expired"); |
6aa74308 EJ |
866 | } |
867 | } | |
868 | ||
869 | static void | |
344e21d4 | 870 | lacp_print_details(struct ds *ds, struct lacp *lacp) OVS_REQUIRES(mutex) |
6aa74308 | 871 | { |
431d6a6a EJ |
872 | struct shash slave_shash = SHASH_INITIALIZER(&slave_shash); |
873 | const struct shash_node **sorted_slaves = NULL; | |
874 | ||
6aa74308 | 875 | struct slave *slave; |
431d6a6a | 876 | int i; |
6aa74308 | 877 | |
5dab8ece JP |
878 | ds_put_format(ds, "---- %s ----\n", lacp->name); |
879 | ds_put_format(ds, "\tstatus: %s", lacp->active ? "active" : "passive"); | |
20601813 | 880 | if (lacp->negotiated) { |
5dab8ece | 881 | ds_put_cstr(ds, " negotiated"); |
20601813 | 882 | } |
5dab8ece | 883 | ds_put_cstr(ds, "\n"); |
20601813 | 884 | |
5dab8ece JP |
885 | ds_put_format(ds, "\tsys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(lacp->sys_id)); |
886 | ds_put_format(ds, "\tsys_priority: %u\n", lacp->sys_priority); | |
887 | ds_put_cstr(ds, "\taggregation key: "); | |
6aa74308 | 888 | if (lacp->key_slave) { |
ab3a09c8 AS |
889 | ds_put_format(ds, "%u", lacp->key_slave->key |
890 | ? lacp->key_slave->key | |
891 | : lacp->key_slave->port_id); | |
6aa74308 | 892 | } else { |
5dab8ece | 893 | ds_put_cstr(ds, "none"); |
6aa74308 | 894 | } |
5dab8ece | 895 | ds_put_cstr(ds, "\n"); |
6aa74308 | 896 | |
5dab8ece | 897 | ds_put_cstr(ds, "\tlacp_time: "); |
bf83f7c8 | 898 | if (lacp->fast) { |
5dab8ece | 899 | ds_put_cstr(ds, "fast\n"); |
bf83f7c8 | 900 | } else { |
5dab8ece | 901 | ds_put_cstr(ds, "slow\n"); |
cdcf42c6 EJ |
902 | } |
903 | ||
6aa74308 | 904 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { |
431d6a6a EJ |
905 | shash_add(&slave_shash, slave->name, slave); |
906 | } | |
907 | sorted_slaves = shash_sort(&slave_shash); | |
908 | ||
909 | for (i = 0; i < shash_count(&slave_shash); i++) { | |
6aa74308 | 910 | char *status; |
77fdfa9b | 911 | struct lacp_info actor; |
6aa74308 | 912 | |
431d6a6a | 913 | slave = sorted_slaves[i]->data; |
77fdfa9b | 914 | slave_get_actor(slave, &actor); |
6aa74308 EJ |
915 | switch (slave->status) { |
916 | case LACP_CURRENT: | |
917 | status = "current"; | |
918 | break; | |
919 | case LACP_EXPIRED: | |
920 | status = "expired"; | |
921 | break; | |
922 | case LACP_DEFAULTED: | |
923 | status = "defaulted"; | |
924 | break; | |
925 | default: | |
428b2edd | 926 | OVS_NOT_REACHED(); |
6aa74308 EJ |
927 | } |
928 | ||
5dab8ece | 929 | ds_put_format(ds, "\nslave: %s: %s %s\n", slave->name, status, |
0f1a47f1 | 930 | slave->attached ? "attached" : "detached"); |
5dab8ece JP |
931 | ds_put_format(ds, "\tport_id: %u\n", slave->port_id); |
932 | ds_put_format(ds, "\tport_priority: %u\n", slave->port_priority); | |
29985e75 EJ |
933 | ds_put_format(ds, "\tmay_enable: %s\n", (slave_may_enable__(slave) |
934 | ? "true" : "false")); | |
6aa74308 | 935 | |
5dab8ece | 936 | ds_put_format(ds, "\n\tactor sys_id: " ETH_ADDR_FMT "\n", |
77fdfa9b | 937 | ETH_ADDR_ARGS(actor.sys_id)); |
5dab8ece | 938 | ds_put_format(ds, "\tactor sys_priority: %u\n", |
77fdfa9b | 939 | ntohs(actor.sys_priority)); |
5dab8ece | 940 | ds_put_format(ds, "\tactor port_id: %u\n", |
77fdfa9b | 941 | ntohs(actor.port_id)); |
5dab8ece | 942 | ds_put_format(ds, "\tactor port_priority: %u\n", |
77fdfa9b | 943 | ntohs(actor.port_priority)); |
5dab8ece | 944 | ds_put_format(ds, "\tactor key: %u\n", |
77fdfa9b | 945 | ntohs(actor.key)); |
1c1df7f1 | 946 | ds_put_cstr(ds, "\tactor state:"); |
5dab8ece JP |
947 | ds_put_lacp_state(ds, actor.state); |
948 | ds_put_cstr(ds, "\n\n"); | |
6aa74308 | 949 | |
5dab8ece | 950 | ds_put_format(ds, "\tpartner sys_id: " ETH_ADDR_FMT "\n", |
6aa74308 | 951 | ETH_ADDR_ARGS(slave->partner.sys_id)); |
5dab8ece | 952 | ds_put_format(ds, "\tpartner sys_priority: %u\n", |
6aa74308 | 953 | ntohs(slave->partner.sys_priority)); |
5dab8ece | 954 | ds_put_format(ds, "\tpartner port_id: %u\n", |
6aa74308 | 955 | ntohs(slave->partner.port_id)); |
5dab8ece | 956 | ds_put_format(ds, "\tpartner port_priority: %u\n", |
6aa74308 | 957 | ntohs(slave->partner.port_priority)); |
5dab8ece | 958 | ds_put_format(ds, "\tpartner key: %u\n", |
6aa74308 | 959 | ntohs(slave->partner.key)); |
1c1df7f1 | 960 | ds_put_cstr(ds, "\tpartner state:"); |
5dab8ece JP |
961 | ds_put_lacp_state(ds, slave->partner.state); |
962 | ds_put_cstr(ds, "\n"); | |
963 | } | |
431d6a6a EJ |
964 | |
965 | shash_destroy(&slave_shash); | |
966 | free(sorted_slaves); | |
5dab8ece JP |
967 | } |
968 | ||
969 | static void | |
0e15264f | 970 | lacp_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[], |
b468782a | 971 | void *aux OVS_UNUSED) OVS_EXCLUDED(mutex) |
5dab8ece JP |
972 | { |
973 | struct ds ds = DS_EMPTY_INITIALIZER; | |
974 | struct lacp *lacp; | |
975 | ||
2339e869 | 976 | lacp_lock(); |
0e15264f BP |
977 | if (argc > 1) { |
978 | lacp = lacp_find(argv[1]); | |
5dab8ece | 979 | if (!lacp) { |
bde9f75d | 980 | unixctl_command_reply_error(conn, "no such lacp object"); |
b468782a | 981 | goto out; |
5dab8ece JP |
982 | } |
983 | lacp_print_details(&ds, lacp); | |
984 | } else { | |
b468782a | 985 | LIST_FOR_EACH (lacp, node, all_lacps) { |
5dab8ece JP |
986 | lacp_print_details(&ds, lacp); |
987 | } | |
6aa74308 EJ |
988 | } |
989 | ||
bde9f75d | 990 | unixctl_command_reply(conn, ds_cstr(&ds)); |
6aa74308 | 991 | ds_destroy(&ds); |
b468782a EJ |
992 | |
993 | out: | |
2339e869 | 994 | lacp_unlock(); |
6aa74308 | 995 | } |
50b9699f NM |
996 | |
997 | /* Extract a snapshot of the current state and counters for a slave port. | |
998 | Return false if the slave is not active. */ | |
999 | bool | |
1000 | lacp_get_slave_stats(const struct lacp *lacp, const void *slave_, struct lacp_slave_stats *stats) | |
1001 | OVS_EXCLUDED(mutex) | |
1002 | { | |
1003 | struct slave *slave; | |
1004 | struct lacp_info actor; | |
1005 | bool ret; | |
1006 | ||
1007 | ovs_mutex_lock(&mutex); | |
1008 | ||
1009 | slave = slave_lookup(lacp, slave_); | |
1010 | if (slave) { | |
1011 | ret = true; | |
1012 | slave_get_actor(slave, &actor); | |
74ff3298 JR |
1013 | stats->dot3adAggPortActorSystemID = actor.sys_id; |
1014 | stats->dot3adAggPortPartnerOperSystemID = slave->partner.sys_id; | |
50b9699f NM |
1015 | stats->dot3adAggPortAttachedAggID = (lacp->key_slave->key ? |
1016 | lacp->key_slave->key : | |
1017 | lacp->key_slave->port_id); | |
1018 | ||
1019 | /* Construct my admin-state. Assume aggregation is configured on. */ | |
1020 | stats->dot3adAggPortActorAdminState = LACP_STATE_AGG; | |
1021 | if (lacp->active) { | |
1022 | stats->dot3adAggPortActorAdminState |= LACP_STATE_ACT; | |
1023 | } | |
1024 | if (lacp->fast) { | |
1025 | stats->dot3adAggPortActorAdminState |= LACP_STATE_TIME; | |
1026 | } | |
1027 | /* XXX Not sure how to know the partner admin state. It | |
1028 | * might have to be captured and remembered during the | |
1029 | * negotiation phase. | |
1030 | */ | |
1031 | stats->dot3adAggPortPartnerAdminState = 0; | |
1032 | ||
1033 | stats->dot3adAggPortActorOperState = actor.state; | |
1034 | stats->dot3adAggPortPartnerOperState = slave->partner.state; | |
1035 | ||
1036 | /* Read out the latest counters */ | |
1037 | stats->dot3adAggPortStatsLACPDUsRx = slave->count_rx_pdus; | |
1038 | stats->dot3adAggPortStatsIllegalRx = slave->count_rx_pdus_bad; | |
1039 | stats->dot3adAggPortStatsLACPDUsTx = slave->count_tx_pdus; | |
1040 | } else { | |
1041 | ret = false; | |
1042 | } | |
1043 | ovs_mutex_unlock(&mutex); | |
1044 | return ret; | |
1045 | ||
1046 | } |