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