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